/** * Converts a {@code ParseObject.State} to REST JSON for saving. * <p> * Only dirty keys from {@code operations} are represented in the data. Non-dirty keys such as * {@code updatedAt}, {@code createdAt}, etc. are not included. * * @param state {@link ParseObject.State} of the type of {@link ParseObject} that will be returned. * Properties are completely ignored. * @param operations Dirty operations that are to be saved. * @param encoder Encoder instance that will be used to encode the request. * @return A REST formatted {@link JSONObject} that will be used for saving. */ public <T extends ParseObject.State> JSONObject encode( T state, ParseOperationSet operations, ParseEncoder encoder) { JSONObject objectJSON = new JSONObject(); try { // Serialize the data for (String key : operations.keySet()) { ParseFieldOperation operation = operations.get(key); objectJSON.put(key, encoder.encode(operation)); // TODO(grantland): Use cached value from hashedObjects if it's a set operation. } if (state.objectId() != null) { objectJSON.put(KEY_OBJECT_ID, state.objectId()); } } catch (JSONException e) { throw new RuntimeException("could not serialize object to JSON"); } return objectJSON; }
Init(State state) { className = state.className(); objectId = state.objectId(); createdAt = state.createdAt(); updatedAt = state.updatedAt(); availableKeys = Collections.synchronizedSet(state.availableKeys()); for (String key : state.keySet()) { serverData.put(key, state.get(key)); availableKeys.add(key); } isComplete = state.isComplete(); }
public static ParseRESTObjectCommand deleteObjectCommand( ParseObject.State state, String sessionToken) { String httpPath = String.format("classes/%s", Uri.encode(state.className())); String objectId = state.objectId(); if (objectId != null) { httpPath += String.format("/%s", Uri.encode(objectId)); } return new ParseRESTObjectCommand(httpPath, ParseHttpRequest.Method.DELETE, null, sessionToken); } }
@Test public void testSaveAsync() throws Exception { // Make mock response and client JSONObject mockResponse = new JSONObject(); String createAtStr = "2015-08-09T22:15:13.460Z"; long createAtLong = ParseDateFormat.getInstance().parse(createAtStr).getTime(); String updateAtStr = "2015-08-09T22:15:13.497Z"; long updateAtLong = ParseDateFormat.getInstance().parse(updateAtStr).getTime(); mockResponse.put("createdAt", createAtStr); mockResponse.put("objectId", "testObjectId"); mockResponse.put("updatedAt", updateAtStr); ParseHttpClient restClient = ParseTestUtils.mockParseHttpClientWithResponse(mockResponse, 200, "OK"); // Make test state ParseObject object = new ParseObject("Test"); object.put("key", "value"); NetworkObjectController controller = new NetworkObjectController(restClient); ParseObject.State newState = ParseTaskUtils.wait(controller.saveAsync( object.getState(), object.startSave(), "sessionToken", ParseDecoder.get())); assertEquals(createAtLong, newState.createdAt()); assertEquals(updateAtLong, newState.updatedAt()); assertEquals("testObjectId", newState.objectId()); assertFalse(newState.isComplete()); }
@Test public void testFetchAsync() throws Exception { // Make mock response and client JSONObject mockResponse = new JSONObject(); String createAtStr = "2015-08-09T22:15:13.460Z"; long createAtLong = ParseDateFormat.getInstance().parse(createAtStr).getTime(); String updateAtStr = "2015-08-09T22:15:13.497Z"; long updateAtLong = ParseDateFormat.getInstance().parse(updateAtStr).getTime(); mockResponse.put("createdAt", createAtStr); mockResponse.put("objectId", "testObjectId"); mockResponse.put("key", "value"); mockResponse.put("updatedAt", updateAtStr); ParseHttpClient restClient = ParseTestUtils.mockParseHttpClientWithResponse(mockResponse, 200, "OK"); // Make test state ParseObject.State state = new ParseObject.State.Builder("Test") .objectId("testObjectId") .build(); NetworkObjectController controller = new NetworkObjectController(restClient); ParseObject.State newState = ParseTaskUtils.wait(controller.fetchAsync(state, "sessionToken", ParseDecoder.get())); assertEquals(createAtLong, newState.createdAt()); assertEquals(updateAtLong, newState.updatedAt()); assertEquals("value", newState.get("key")); assertEquals("testObjectId", newState.objectId()); assertTrue(newState.isComplete()); }
@Test public void testCopy() { long updatedAt = System.currentTimeMillis(); long createdAt = updatedAt + 10; ParseObject.State state = new ParseObject.State.Builder("TestObject") .objectId("fake") .createdAt(new Date(createdAt)) .updatedAt(new Date(updatedAt)) .isComplete(true) .put("foo", "bar") .put("baz", "qux") .availableKeys(Arrays.asList("safe", "keys")) .build(); ParseObject.State copy = new ParseObject.State.Builder(state).build(); assertEquals(state.className(), copy.className()); assertEquals(state.objectId(), copy.objectId()); assertEquals(state.createdAt(), copy.createdAt()); assertEquals(state.updatedAt(), copy.updatedAt()); assertEquals(state.isComplete(), copy.isComplete()); assertEquals(state.keySet().size(), copy.keySet().size()); assertEquals(state.get("foo"), copy.get("foo")); assertEquals(state.get("baz"), copy.get("baz")); assertEquals(state.availableKeys().size(), copy.availableKeys().size()); assertTrue(state.availableKeys().containsAll(copy.availableKeys())); assertTrue(copy.availableKeys().containsAll(state.availableKeys())); }
@Test public void testFailingDelete() throws Exception { ParseRESTCommand.server = new URL("https://api.parse.com/1"); Parse.Configuration configuration = new Parse.Configuration.Builder(RuntimeEnvironment.application) .build(); ParsePlugins plugins = mock(ParsePlugins.class); when(plugins.configuration()).thenReturn(configuration); when(plugins.applicationContext()).thenReturn(RuntimeEnvironment.application); ParsePlugins.set(plugins); JSONObject mockResponse = new JSONObject(); mockResponse.put("code", 141); mockResponse.put("error", "Delete is not allowed"); ParseHttpClient restClient = ParseTestUtils.mockParseHttpClientWithResponse(mockResponse, 400, "Bad Request"); when(plugins.restClient()).thenReturn(restClient); ParseObject.State state = mock(ParseObject.State.class); when(state.className()).thenReturn("TestObject"); when(state.objectId()).thenReturn("test_id"); when(state.keySet()).thenReturn(Collections.singleton("key")); when(state.get("key")).thenReturn("data"); ParseObject object = ParseObject.from(state); thrown.expect(ParseException.class); thrown.expectMessage("Delete is not allowed"); object.delete(); }
/** * Applies a {@code State} on top of this {@code Builder} instance. * * @param other The {@code State} to apply over this instance. * @return A new {@code Builder} instance. */ public T apply(State other) { if (other.objectId() != null) { objectId(other.objectId()); } if (other.createdAt() > 0) { createdAt(other.createdAt()); } if (other.updatedAt() > 0) { updatedAt(other.updatedAt()); } isComplete(isComplete || other.isComplete()); for (String key : other.keySet()) { put(key, other.get(key)); } availableKeys(other.availableKeys()); return self(); }
@Test public void testDecodeSuccessWithoutOldFormatJson() throws Exception { Date createAt = new Date(1000); Date updateAt = new Date(2000); String createAtStr = ParseDateFormat.getInstance().format(createAt); String updateAtStr = ParseDateFormat.getInstance().format(updateAt); JSONObject dataJson = new JSONObject() .put(KEY_OBJECT_ID, "objectId") .put(KEY_CREATED_AT, createAtStr) .put(KEY_UPDATED_AT, updateAtStr) .put("key", "value"); JSONObject objectJson = new JSONObject(); objectJson.put(KEY_DATA, dataJson); ParseObjectCurrentCoder coder = ParseObjectCurrentCoder.get(); ParseObject.State.Builder builder = coder.decode(new ParseObject.State.Builder("Test"), objectJson, ParseDecoder.get()); // We use the builder to build a state to verify the content in the builder ParseObject.State state = builder.build(); assertEquals(createAt.getTime(), state.createdAt()); assertEquals(updateAt.getTime(), state.updatedAt()); assertEquals("objectId", state.objectId()); assertEquals("value", state.get("key")); }
@Override public Task<ParseObject.State> fetchAsync( final ParseObject.State state, String sessionToken, final ParseDecoder decoder) { final ParseRESTCommand command = ParseRESTObjectCommand.getObjectCommand( state.objectId(), state.className(), sessionToken); return command.executeAsync(client).onSuccess(new Continuation<JSONObject, ParseObject.State>() { @Override public ParseObject.State then(Task<JSONObject> task) { JSONObject result = task.getResult(); // Copy and clear to create an new empty instance of the same type as `state` ParseObject.State.Init<?> builder = state.newBuilder().clear(); return coder.decode(builder, result, decoder) .isComplete(true) .build(); } }); }
/** * Converts a {@code ParseObject.State} to a {@code ParseObject}. * * @param state The {@code ParseObject.State} to convert from. * @return A {@code ParseObject} instance. */ static <T extends ParseObject> T from(ParseObject.State state) { @SuppressWarnings("unchecked") T object = (T) ParseObject.createWithoutData(state.className(), state.objectId()); synchronized (object.mutex) { State newState; if (state.isComplete()) { newState = state; } else { newState = object.getState().newBuilder().apply(state).build(); } object.setState(newState); } return object; }
/** * Returns the localId, which is used internally for serializing relations to objects that don't * yet have an objectId. */ String getOrCreateLocalId() { synchronized (mutex) { if (localId == null) { if (state.objectId() != null) { throw new IllegalStateException( "Attempted to get a localId for an object with an objectId."); } localId = getLocalIdManager().createLocalId(); } return localId; } }
@Test public void testProperties() { long updatedAt = System.currentTimeMillis(); long createdAt = updatedAt + 10; ParseObject.State state = new ParseObject.State.Builder("TestObject") .objectId("fake") .createdAt(new Date(createdAt)) .updatedAt(new Date(updatedAt)) .isComplete(true) .build(); assertEquals("TestObject", state.className()); assertEquals("fake", state.objectId()); assertEquals(createdAt, state.createdAt()); assertEquals(updatedAt, state.updatedAt()); assertTrue(state.isComplete()); }
public static ParseRESTObjectCommand saveObjectCommand( ParseObject.State state, JSONObject operations, String sessionToken) { if (state.objectId() == null) { return ParseRESTObjectCommand.createObjectCommand( state.className(), operations, sessionToken); } else { return ParseRESTObjectCommand.updateObjectCommand( state.objectId(), state.className(), operations, sessionToken); } }
/** * Setter for the object id. In general you do not need to use this. However, in some cases this * can be convenient. For example, if you are serializing a {@code ParseObject} yourself and wish * to recreate it, you can use this to recreate the {@code ParseObject} exactly. */ public void setObjectId(String newObjectId) { synchronized (mutex) { String oldObjectId = state.objectId(); if (ParseTextUtils.equals(oldObjectId, newObjectId)) { return; } // We don't need to use setState since it doesn't affect our cached state. state = state.newBuilder().objectId(newObjectId).build(); notifyObjectIdChanged(oldObjectId, newObjectId); } }
@Test public void testDefaults() { ParseObject.State state = new ParseObject.State.Builder("TestObject").build(); assertEquals("TestObject", state.className()); assertNull(state.objectId()); assertEquals(-1, state.createdAt()); assertEquals(-1, state.updatedAt()); assertFalse(state.isComplete()); assertTrue(state.keySet().isEmpty()); assertTrue(state.availableKeys().isEmpty()); }
private void setState(State newState, boolean notifyIfObjectIdChanges) { synchronized (mutex) { String oldObjectId = state.objectId(); String newObjectId = newState.objectId(); state = newState; if (notifyIfObjectIdChanges && !ParseTextUtils.equals(oldObjectId, newObjectId)) { notifyObjectIdChanged(oldObjectId, newObjectId); } rebuildEstimatedData(); } }
private static void verifyBasicSessionState(JSONObject mockResponse, ParseSession.State state) throws JSONException { assertEquals("_Session", state.className()); long createAtLong = ParseDateFormat.getInstance().parse(mockResponse.getString("createdAt")).getTime(); assertEquals(createAtLong, state.createdAt()); assertEquals(mockResponse.getString("objectId"), state.objectId()); assertEquals(mockResponse.getString("sessionToken"), state.get("sessionToken")); assertEquals(mockResponse.getString("restricted"), state.get("restricted")); assertEquals( mockResponse.getJSONObject("createdWith").getString("action"), ((Map<String, String>) state.get("createdWith")).get("action")); }
/** * Accessor to the object id. An object id is assigned as soon as an object is saved to the * server. The combination of a className and an objectId uniquely identifies an object in your * application. * * @return The object id. */ public String getObjectId() { synchronized (mutex) { return state.objectId(); } }