/*
* Copyright © 2013. Palomino Labs (http://palominolabs.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.palominolabs.crm.sf.rest;
import com.palominolabs.crm.sf.core.Id;
import com.palominolabs.crm.sf.core.SObject;
import com.palominolabs.crm.sf.soap.PartnerSObjectImpl;
import com.palominolabs.crm.sf.testutil.SObjectUtil;
import com.palominolabs.crm.sf.testutil.TestFixtureUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static com.palominolabs.crm.sf.rest.HttpApiClient.API_VERSION;
import static com.palominolabs.crm.sf.rest.HttpApiClientTest.PASSWORD;
import static com.palominolabs.crm.sf.rest.HttpApiClientTest.USER;
import static com.palominolabs.crm.sf.rest.TestConnections.getRestConnection;
import static com.palominolabs.testutil.JsonAssert.assertJsonStringEquals;
import static com.palominolabs.testutil.ResourceUtil.readResource;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class RestConnectionImplTest {
private static RestConnection conn = null;
@BeforeClass
public static void setUpClass() throws com.palominolabs.crm.sf.soap.ApiException, MalformedURLException {
conn = getRestConnection(USER, PASSWORD);
}
@Test
public void testBasicSObjectMetadata() throws IOException {
BasicSObjectMetadataResult actual = conn.getBasicObjectInfo("Account");
assertEquals(readResource("/apiResponses/basicSObjectMetadata.xml"),
TestFixtureUtils.dumpFixture(actual));
}
@Test
public void testCreateWithNoFields() throws IOException {
SObject sObj = PartnerSObjectImpl.getNew("Contact");
try {
conn.create(sObj);
fail();
} catch (ApiException e) {
assertEquals("https://na3.salesforce.com:443/services/data/v" + API_VERSION + "/sobjects/Contact/",
e.getUrl());
assertEquals("Bad Request", e.getHttpReason());
assertJsonStringEquals(
"[{\"fields\":[\"LastName\"],\"message\":\"Required fields are missing: " +
"[LastName]\",\"errorCode\":\"REQUIRED_FIELD_MISSING\"}]",
e.getHttpResponseBody());
assertEquals(400, e.getHttpResponseCode());
List<ApiError> errors = e.getErrors();
assertEquals(1, errors.size());
ApiError error = errors.get(0);
assertEquals("REQUIRED_FIELD_MISSING", error.getErrorCode());
assertEquals("Required fields are missing: [LastName]", error.getMessage());
assertEquals(1, error.getFields().size());
assertEquals("LastName", error.getFields().get(0));
}
}
@Test
public void testCreateThenDelete() throws IOException {
SaveResult result = createTask();
conn.delete("Task", result.getId());
assertNotNull(result.getId());
assertTrue(result.isSuccess());
}
@Test
public void testDescribeGlobal() throws IOException {
DescribeGlobalResult actual = conn.describeGlobal();
assertEquals(readResource("/apiResponses/describeGlobal.xml"), TestFixtureUtils.dumpFixture(actual));
}
@Test
public void testDescribeSObject() throws IOException {
SObjectDescription actual = conn.describeSObject("Account");
assertEquals(readResource("/apiResponses/describeSObject.xml"), TestFixtureUtils.dumpFixture(actual));
assertNull(actual.getsObjectUrls().getPasswordUtilities());
}
@Test
public void testDescribeUser() throws IOException {
// user has a "passwordUtilities" field that other objects do not
SObjectDescription userDesc = conn.describeSObject("User");
SObjectUrls sObjectUrls = userDesc.getsObjectUrls();
assertEquals("/services/data/v" + API_VERSION + "/sobjects/User/{ID}/password", sObjectUrls.getPasswordUtilities());
}
@Test
public void testDescribeAllSObjects() throws IOException {
// make sure none of them have issues
for (GlobalSObjectDescription globalSObjectDescription : conn.describeGlobal().getBasicSObjectMetadatas()) {
conn.describeSObject(globalSObjectDescription.getName());
}
}
@Test
public void testQuery() throws IOException {
RestQueryResult result =
conn.query("SELECT Id,Name,Description FROM Product2 WHERE Id = '01t50000001L5cT'");
assertNull(result.getQueryLocator());
assertTrue(result.isDone());
assertEquals(1, result.getSObjects().size());
SObject object = result.getSObjects().get(0);
//noinspection ConstantConditions
assertEquals("01t50000001L5cT", object.getId().toString());
assertEquals("GenWatt Diesel 200kW", object.getField("Name"));
assertNull(object.getField("Description"));
assertEquals(2, object.getAllFields().size());
assertEquals("Product2", object.getType());
assertTrue(object.isFieldSet("Description"));
assertFalse(object.isFieldSet("Foo"));
}
@SuppressWarnings("unchecked")
@Test
public void testQueryWithSubquery() throws com.palominolabs.crm.sf.soap.ApiException, IOException {
RestQueryResult relQR = conn.query(
"SELECT Id, Name, AnnualRevenue, (SELECT Id, FirstName, Email FROM Contacts), " +
" (Select Id, Subject from Tasks) FROM Account WHERE Id='0015000000WWD7b'");
assertTrue(relQR.isDone());
assertNull(relQR.getQueryLocator());
assertEquals(1, relQR.getTotalSize());
RestSObject account = relQR.getSObjects().get(0);
RestQueryResult subqueryResult = account.getRelationshipQueryResults().get("Contacts");
assertTrue(subqueryResult.isDone());
assertNull(subqueryResult.getQueryLocator());
assertEquals(2, subqueryResult.getTotalSize());
assertEquals(1, account.getRelationshipQueryResults().size());
List<RestSObject> expectedContacts =
(List<RestSObject>) TestFixtureUtils
.loadFixtures("/sObjectFixtures/subqueryRecords.xml");
List<RestSObject> actualContacts = subqueryResult.getSObjects();
assertEquals(expectedContacts.size(), actualContacts.size());
Map<Id, RestSObject> actualMap = SObjectUtil.mapifySObjects(actualContacts);
Map<Id, RestSObject> expectedMap = SObjectUtil.mapifySObjects(expectedContacts);
for (Map.Entry<Id, RestSObject> idSObjectEntry : expectedMap.entrySet()) {
SObject actual = actualMap.get(idSObjectEntry.getKey());
SObject expected = idSObjectEntry.getValue();
assertNotNull("Object for id " + idSObjectEntry.getKey(), actual);
assertEquals(expected.getAllFields(), actual.getAllFields());
assertEquals(expected.getType(), actual.getType());
}
}
@Test
public void testQueryWithRelationship() throws IOException {
RestQueryResult result = conn.query("SELECT Id, Name, Owner.Name, Owner.Id FROM Account WHERE Id='0015000000WWD7b'");
RestSObject account = result.getSObjects().get(0);
assertEquals(1, account.getRelationshipSubObjects().size());
assertEquals("United Oil & Gas, Singapore", account.getField("Name"));
assertEquals(new Id("0015000000WWD7b"), account.getId());
RestSObject owner = account.getRelationshipSubObjects().get("Owner");
assertNotNull(owner);
assertEquals("User", owner.getType());
assertEquals("sftestorg3 mpierce", owner.getField("Name"));
assertEquals(new Id("00550000001gvBO"), owner.getId());
}
@Test
public void testRetrieve() throws IOException {
SObject contact = conn.retrieve("Contact", new Id("0035000000km1oh"), Arrays.asList("FirstName", "LastName"));
assertNotNull(contact.getId());
assertEquals("0035000000km1oh", contact.getId().toString());
assertEquals("Rose", contact.getField("FirstName"));
assertEquals("Gonzalez", contact.getField("LastName"));
assertEquals(2, contact.getAllFields().size());
assertEquals("Contact", contact.getType());
}
@Test
public void testRetrieveBadId() throws IOException {
// s/...$/zzz/
try {
conn.retrieve("Contact", new Id("0035000000kmzzz"), Arrays.asList("FirstName", "LastName"));
fail();
} catch (ApiException e) {
assertEquals(
"https://na3.salesforce.com:443/services/data/v" + API_VERSION + "/sobjects/Contact/0035000000kmzzz?fields=FirstName%2CLastName",
e.getUrl());
assertEquals("Not Found", e.getHttpReason());
assertJsonStringEquals(
"[{\"message\":\"The requested resource does not exist\",\"errorCode\":\"NOT_FOUND\"}]",
e.getHttpResponseBody());
assertEquals(404, e.getHttpResponseCode());
List<ApiError> errors = e.getErrors();
assertEquals(1, errors.size());
ApiError error = errors.get(0);
assertEquals("NOT_FOUND", error.getErrorCode());
assertEquals("The requested resource does not exist", error.getMessage());
assertEquals(0, error.getFields().size());
}
}
@Test
public void testSearch() throws IOException {
List<SObject> actual =
conn.search("FIND {dickenson.com} returning contact(id, phone, firstname, lastname, email)");
assertEquals(readResource("/apiResponses/search.xml"), TestFixtureUtils.dumpFixture(actual));
}
@Test
public void testUpdate() throws IOException {
List<RestSObject> initialQueryResults = conn.query("Select Name, Id from Opportunity").getSObjects();
assertTrue(initialQueryResults.size() > 1);
SObject sObj = initialQueryResults.get(0);
Id id = sObj.getId();
assertNotNull(id);
// get the LastName of the first id, add xyz to it, then remove it
String origName = sObj.getField("Name");
sObj.setField("Name", origName + "xyz");
conn.update(sObj);
List<RestSObject> postUpdateQueryResults =
conn.query("Select Name, Id from Opportunity WHERE Id = '" + id.toString() + "'").getSObjects();
assertEquals(1, postUpdateQueryResults.size());
SObject postUpdateSObj = postUpdateQueryResults.get(0);
assertEquals(origName + "xyz", postUpdateSObj.getField("Name"));
postUpdateSObj.setField("Name", origName);
conn.update(postUpdateSObj);
List<RestSObject> post2ndUpdateQueryResults =
conn.query("Select Name, Id from Opportunity WHERE Id = '" + id.toString() + "'").getSObjects();
assertEquals(1, post2ndUpdateQueryResults.size());
SObject post2ndUpdateSObj = post2ndUpdateQueryResults.get(0);
assertEquals(origName, post2ndUpdateSObj.getField("Name"));
}
@Test
public void testUpdateFailsValidation() throws IOException {
List<RestSObject> initialQueryResults =
conn.query("Select Name, Id from Opportunity LIMIT 1").getSObjects();
assertEquals(1, initialQueryResults.size());
SObject sObj = initialQueryResults.get(0);
// get the Name of the first id, add xyz to it, then remove it
String origName = sObj.getField("Name");
sObj.setField("Name", "Invalid-" + origName);
try {
conn.update(sObj);
fail();
} catch (ApiException e) {
assertEquals("https://na3.salesforce.com:443/services/data/v" + API_VERSION + "/sobjects/Opportunity/0065000000FgGSn",
e.getUrl());
assertEquals("Bad Request", e.getHttpReason());
assertJsonStringEquals(
"[{\"fields\":[],\"message\":\"Name can't start with Invalid-\"," +
"\"errorCode\":\"FIELD_CUSTOM_VALIDATION_EXCEPTION\"}]",
e.getHttpResponseBody());
assertEquals(400, e.getHttpResponseCode());
List<ApiError> errors = e.getErrors();
assertEquals(1, errors.size());
ApiError error = errors.get(0);
assertEquals("FIELD_CUSTOM_VALIDATION_EXCEPTION", error.getErrorCode());
assertEquals("Name can't start with Invalid-", error.getMessage());
assertEquals(0, error.getFields().size());
}
}
@Test
public void testUpdateWithFieldsToNull() throws IOException {
Id id = new Id("0065000000FgGSp");
SObject sObj = conn.retrieve("Opportunity", id, Arrays.asList("Amount", "Id"));
String fname = "Amount";
String origValue = "350000.0";
assertEquals(origValue, sObj.getField(fname));
assertNotNull(sObj.getId());
try {
// null it
SObject setNullField = RestSObjectImpl.getNewWithId("Opportunity", sObj.getId());
setNullField.setField(fname, null);
conn.update(setNullField);
SObject sObjWithNullField =
conn.retrieve("Opportunity", id, Arrays.asList("Amount", "Id"));
assertTrue(sObjWithNullField.isFieldSet(fname));
assertNull(sObjWithNullField.getField(fname));
} finally {
sObj.setField(fname, origValue);
conn.update(sObj);
SObject sObjWithFieldAgain = conn.retrieve("Opportunity", id, Arrays.asList("Amount", "Id"));
assertEquals(origValue, sObjWithFieldAgain.getField(fname));
}
}
private static SaveResult createTask() throws IOException {
SObject sObj = PartnerSObjectImpl.getNew("Task");
sObj.setField("Priority", "High");
sObj.setField("Status", "In Progress");
return conn.create(sObj);
}
}