use of com.amplifyframework.api.graphql.GraphQLResponse in project amplify-android by aws-amplify.
the class AWSDataStorePluginTest method mockApiCategoryWithGraphQlApi.
@SuppressWarnings("unchecked")
private ApiCategory mockApiCategoryWithGraphQlApi() throws AmplifyException {
ApiCategory mockApiCategory = spy(ApiCategory.class);
ApiPlugin<?> mockApiPlugin = mock(ApiPlugin.class);
when(mockApiPlugin.getPluginKey()).thenReturn(MOCK_API_PLUGIN_NAME);
when(mockApiPlugin.getCategoryType()).thenReturn(CategoryType.API);
ApiEndpointStatusChangeEvent eventData = new ApiEndpointStatusChangeEvent(ApiEndpointStatusChangeEvent.ApiEndpointStatus.REACHABLE, ApiEndpointStatusChangeEvent.ApiEndpointStatus.UNKOWN);
HubEvent<ApiEndpointStatusChangeEvent> hubEvent = HubEvent.create(ApiChannelEventName.API_ENDPOINT_STATUS_CHANGED, eventData);
// Make believe that queries return response immediately
doAnswer(invocation -> {
// Mock the API emitting an ApiEndpointStatusChangeEvent event.
Amplify.Hub.publish(HubChannel.API, hubEvent);
int indexOfResponseConsumer = 1;
Consumer<GraphQLResponse<PaginatedResult<ModelWithMetadata<Person>>>> onResponse = invocation.getArgument(indexOfResponseConsumer);
PaginatedResult<ModelWithMetadata<Person>> data = new PaginatedResult<>(Collections.emptyList(), null);
onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList()));
return null;
}).when(mockApiPlugin).query(any(GraphQLRequest.class), any(Consumer.class), any(Consumer.class));
// Make believe that subscriptions return response immediately
doAnswer(invocation -> {
int indexOfStartConsumer = 1;
Consumer<String> onStart = invocation.getArgument(indexOfStartConsumer);
GraphQLOperation<?> mockOperation = mock(GraphQLOperation.class);
doAnswer(opAnswer -> {
this.subscriptionCancelledCounter.incrementAndGet();
return null;
}).when(mockOperation).cancel();
this.subscriptionStartedCounter.incrementAndGet();
// Trigger the subscription start event.
onStart.accept(RandomString.string());
return mockOperation;
}).when(mockApiPlugin).subscribe(any(GraphQLRequest.class), any(Consumer.class), any(Consumer.class), any(Consumer.class), any(Action.class));
mockApiCategory.addPlugin(mockApiPlugin);
mockApiCategory.configure(new ApiCategoryConfiguration(), getApplicationContext());
mockApiCategory.initialize(getApplicationContext());
return mockApiCategory;
}
use of com.amplifyframework.api.graphql.GraphQLResponse in project amplify-android by aws-amplify.
the class ConflictResolverIntegrationTest method mockApiCategoryWithGraphQlApi.
@SuppressWarnings("unchecked")
private ApiCategory mockApiCategoryWithGraphQlApi() throws AmplifyException {
ApiCategory mockApiCategory = spy(ApiCategory.class);
ApiPlugin<?> mockApiPlugin = mock(ApiPlugin.class);
when(mockApiPlugin.getPluginKey()).thenReturn(MOCK_API_PLUGIN_NAME);
when(mockApiPlugin.getCategoryType()).thenReturn(CategoryType.API);
ApiEndpointStatusChangeEvent eventData = new ApiEndpointStatusChangeEvent(ApiEndpointStatusChangeEvent.ApiEndpointStatus.REACHABLE, ApiEndpointStatusChangeEvent.ApiEndpointStatus.UNKOWN);
HubEvent<ApiEndpointStatusChangeEvent> hubEvent = HubEvent.create(ApiChannelEventName.API_ENDPOINT_STATUS_CHANGED, eventData);
// Make believe that queries return response immediately
doAnswer(invocation -> {
// Mock the API emitting an ApiEndpointStatusChangeEvent event.
Amplify.Hub.publish(HubChannel.API, hubEvent);
int indexOfResponseConsumer = 1;
Consumer<GraphQLResponse<PaginatedResult<ModelWithMetadata<Person>>>> onResponse = invocation.getArgument(indexOfResponseConsumer);
PaginatedResult<ModelWithMetadata<Person>> data = new PaginatedResult<>(Collections.emptyList(), null);
onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList()));
return null;
}).when(mockApiPlugin).query(any(GraphQLRequest.class), any(Consumer.class), any(Consumer.class));
mockApiCategory.addPlugin(mockApiPlugin);
mockApiCategory.configure(new ApiCategoryConfiguration(), getApplicationContext());
mockApiCategory.initialize(getApplicationContext());
return mockApiCategory;
}
use of com.amplifyframework.api.graphql.GraphQLResponse in project amplify-android by aws-amplify.
the class AppSyncClientInstrumentationTest method testAllOperations.
/**
* Tests the operations in AppSyncClient.
* @throws DataStoreException If any call to AppSync endpoint fails to return a response
* @throws AmplifyException On failure to obtain ModelSchema
*/
@Test
@SuppressWarnings("MethodLength")
public void testAllOperations() throws AmplifyException {
ModelSchema blogOwnerSchema = ModelSchema.fromModelClass(BlogOwner.class);
ModelSchema postSchema = ModelSchema.fromModelClass(Post.class);
ModelSchema blogSchema = ModelSchema.fromModelClass(Blog.class);
long startTimeSeconds = TimeUnit.MILLISECONDS.toSeconds(new Date().getTime());
// Create simple model with no relationship
BlogOwner owner = BlogOwner.builder().name("David").build();
ModelWithMetadata<BlogOwner> blogOwnerCreateResult = create(owner, blogOwnerSchema);
BlogOwner actual = blogOwnerCreateResult.getModel();
ModelAssert.assertEqualsIgnoringTimestamps(owner, actual);
assertEquals(new Integer(1), blogOwnerCreateResult.getSyncMetadata().getVersion());
// TODO: BE AWARE THAT THE DELETED PROPERTY RETURNS NULL INSTEAD OF FALSE
assertNull(blogOwnerCreateResult.getSyncMetadata().isDeleted());
assertTrue(blogOwnerCreateResult.getSyncMetadata().getId().endsWith(owner.getId()));
// Subscribe to Blog creations
Observable<GraphQLResponse<ModelWithMetadata<Blog>>> blogCreations = onCreate(blogSchema);
// Now, actually create a Blog
Blog blog = Blog.builder().name("Create test").owner(owner).build();
ModelWithMetadata<Blog> blogCreateResult = create(blog, blogSchema);
// Currently cannot do BlogOwner.justId because it will assign the id to the name field.
// This is being fixed
assertEquals(blog.getId(), blogCreateResult.getModel().getId());
assertEquals(blog.getName(), blogCreateResult.getModel().getName());
assertEquals(blog.getOwner().getId(), blogCreateResult.getModel().getOwner().getId());
assertEquals(new Integer(1), blogCreateResult.getSyncMetadata().getVersion());
assertNull(blogCreateResult.getSyncMetadata().isDeleted());
Temporal.Timestamp createdBlogLastChangedAt = blogCreateResult.getSyncMetadata().getLastChangedAt();
assertNotNull(createdBlogLastChangedAt);
assertTrue(createdBlogLastChangedAt.getSecondsSinceEpoch() > startTimeSeconds);
assertTrue(blogCreateResult.getSyncMetadata().getId().endsWith(blog.getId()));
// TODO: Subscriptions are currently failing. More investigation required to fix this part of the test.
// Validate that subscription picked up the mutation and end the subscription since we're done with.
// TestObserver<ModelWithMetadata<Blog>> blogCreationSubscriber = TestObserver.create();
// blogCreations
// .map(GraphQLResponse::getData)
// .subscribe(blogCreationSubscriber);
// blogCreationSubscriber.assertValue(blogCreateResult);
// Create Posts which Blog hasMany of
Post post1 = Post.builder().title("Post 1").status(PostStatus.ACTIVE).rating(4).blog(blog).build();
Post post2 = Post.builder().title("Post 2").status(PostStatus.INACTIVE).rating(-1).blog(blog).build();
Post post1ModelResult = create(post1, postSchema).getModel();
Post post2ModelResult = create(post2, postSchema).getModel();
// Results only have blog ID so strip out other information from the original post blog
ModelAssert.assertEqualsIgnoringTimestamps(post1.copyOfBuilder().blog(Blog.justId(blog.getId())).build(), post1ModelResult);
ModelAssert.assertEqualsIgnoringTimestamps(post2.copyOfBuilder().blog(Blog.justId(blog.getId())).build(), post2ModelResult);
// Update model
Blog updatedBlog = blog.copyOfBuilder().name("Updated blog").build();
long updateBlogStartTimeSeconds = TimeUnit.MILLISECONDS.toSeconds(new Date().getTime());
ModelWithMetadata<Blog> blogUpdateResult = update(updatedBlog, blogSchema, 1);
assertEquals(updatedBlog.getName(), blogUpdateResult.getModel().getName());
assertEquals(updatedBlog.getOwner().getId(), blogUpdateResult.getModel().getOwner().getId());
assertEquals(updatedBlog.getId(), blogUpdateResult.getModel().getId());
assertEquals(2, blogUpdateResult.getModel().getPosts().size());
assertEquals(new Integer(2), blogUpdateResult.getSyncMetadata().getVersion());
assertNull(blogUpdateResult.getSyncMetadata().isDeleted());
Temporal.Timestamp updatedBlogLastChangedAt = blogUpdateResult.getSyncMetadata().getLastChangedAt();
assertNotNull(updatedBlogLastChangedAt);
assertTrue(updatedBlogLastChangedAt.getSecondsSinceEpoch() > updateBlogStartTimeSeconds);
// Delete one of the posts
ModelWithMetadata<Post> post1DeleteResult = delete(post1, postSchema, 1);
ModelAssert.assertEqualsIgnoringTimestamps(post1.copyOfBuilder().blog(Blog.justId(blog.getId())).build(), post1DeleteResult.getModel());
Boolean isDeleted = post1DeleteResult.getSyncMetadata().isDeleted();
assertEquals(Boolean.TRUE, isDeleted);
// Try to delete a post with a bad version number
List<GraphQLResponse.Error> post2DeleteErrors = deleteExpectingResponseErrors(post2, postSchema, 0);
assertEquals("Conflict resolver rejects mutation.", post2DeleteErrors.get(0).getMessage());
// Run sync on Blogs
// TODO: This is currently a pretty worthless test - mainly for setting a debug point and manually inspecting
// When you call sync with a null lastSync it gives only one entry per object (the latest state)
Iterable<ModelWithMetadata<Blog>> blogSyncResult = sync(api.buildSyncRequest(blogSchema, null, 1000, QueryPredicates.all()));
assertTrue(blogSyncResult.iterator().hasNext());
// Run sync on Posts
// TODO: This is currently a pretty worthless test - mainly for setting a debug point and manually inspecting
// When you call sync with a lastSyncTime it gives you one entry per version of that object which was created
// since that time.
Iterable<ModelWithMetadata<Post>> postSyncResult = sync(api.buildSyncRequest(postSchema, startTimeSeconds, 1000, QueryPredicates.all()));
assertTrue(postSyncResult.iterator().hasNext());
}
use of com.amplifyframework.api.graphql.GraphQLResponse in project amplify-android by aws-amplify.
the class SubscriptionProcessor method subscriptionObservable.
private <T extends Model> Observable<SubscriptionEvent<? extends Model>> subscriptionObservable(AppSync appSync, SubscriptionType subscriptionType, AbortableCountDownLatch<DataStoreException> latch, ModelSchema modelSchema) {
return Observable.<GraphQLResponse<ModelWithMetadata<T>>>create(emitter -> {
SubscriptionMethod method = subscriptionMethodFor(appSync, subscriptionType);
AtomicReference<String> subscriptionId = new AtomicReference<>();
Cancelable cancelable = method.subscribe(modelSchema, token -> {
LOG.debug("Subscription started for " + subscriptionType.name() + " " + modelSchema.getName() + " subscriptionId: " + token);
subscriptionId.set(token);
latch.countDown();
}, emitter::onNext, dataStoreException -> {
if (isExceptionType(dataStoreException, AppSyncErrorType.UNAUTHORIZED)) {
// Ignore Unauthorized errors, so that DataStore can still be used even if the user is only
// authorized to read a subset of the models.
latch.countDown();
LOG.warn("Unauthorized failure:" + subscriptionType.name() + " " + modelSchema.getName());
} else if (isExceptionType(dataStoreException, AppSyncErrorType.OPERATION_DISABLED)) {
// Ignore OperationDisabled errors, so that DataStore can be used even without subscriptions.
// This logic is only in place to address a specific use case, and should not be used without
// unless you have consulted with AWS. It is subject to be deprecated/removed in the future.
latch.countDown();
LOG.warn("Operation disabled:" + subscriptionType.name() + " " + modelSchema.getName());
} else {
if (latch.getCount() > 0) {
// An error occurred during startup. Abort and notify the Orchestrator by throwing the
// exception from startSubscriptions.
latch.abort(dataStoreException);
} else {
// An error occurred after startup. Notify the Orchestrator via the onFailure action.
onFailure.accept(dataStoreException);
}
}
}, () -> {
LOG.debug("Subscription completed:" + subscriptionId.get());
emitter.onComplete();
});
// When the observable is disposed, we need to call cancel() on the subscription
// so it can properly dispose of resources if necessary. For the AWS API plugin,
// this means closing the underlying network connection.
emitter.setDisposable(AmplifyDisposables.fromCancelable(cancelable));
}).doOnError(subscriptionError -> LOG.warn("An error occurred on the remote " + subscriptionType.name() + " subscription for model " + modelSchema.getName(), subscriptionError)).subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).map(SubscriptionProcessor::unwrapResponse).filter(modelWithMetadata -> {
QueryPredicate predicate = queryPredicateProvider.getPredicate(modelSchema.getName());
return predicate.evaluate(modelWithMetadata.getModel());
}).map(modelWithMetadata -> SubscriptionEvent.<T>builder().type(fromSubscriptionType(subscriptionType)).modelWithMetadata(modelWithMetadata).modelSchema(modelSchema).build());
}
use of com.amplifyframework.api.graphql.GraphQLResponse in project amplify-android by aws-amplify.
the class AppSyncClient method mutation.
private <T extends Model> Cancelable mutation(final GraphQLRequest<ModelWithMetadata<T>> request, final Consumer<GraphQLResponse<ModelWithMetadata<T>>> onResponse, final Consumer<DataStoreException> onFailure) {
final Consumer<GraphQLResponse<ModelWithMetadata<T>>> responseConsumer = response -> {
if (response.hasErrors()) {
onResponse.accept(new GraphQLResponse<>(null, response.getErrors()));
} else {
onResponse.accept(response);
}
};
final Consumer<ApiException> failureConsumer = failure -> onFailure.accept(new DataStoreException("Failure during mutation.", failure, "Check details."));
final Cancelable cancelable = api.mutate(request, responseConsumer, failureConsumer);
if (cancelable != null) {
return cancelable;
}
return new NoOpCancelable();
}
Aggregations