Search in sources :

Example 21 with GraphQLResponse

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;
}
Also used : ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) Action(com.amplifyframework.core.Action) PaginatedResult(com.amplifyframework.api.graphql.PaginatedResult) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) RandomString(com.amplifyframework.testutils.random.RandomString) GraphQLRequest(com.amplifyframework.api.graphql.GraphQLRequest) Consumer(com.amplifyframework.core.Consumer) ApiEndpointStatusChangeEvent(com.amplifyframework.api.events.ApiEndpointStatusChangeEvent) ApiCategory(com.amplifyframework.api.ApiCategory) ApiCategoryConfiguration(com.amplifyframework.api.ApiCategoryConfiguration)

Example 22 with GraphQLResponse

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;
}
Also used : ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) PaginatedResult(com.amplifyframework.api.graphql.PaginatedResult) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) GraphQLRequest(com.amplifyframework.api.graphql.GraphQLRequest) Consumer(com.amplifyframework.core.Consumer) ApiEndpointStatusChangeEvent(com.amplifyframework.api.events.ApiEndpointStatusChangeEvent) ApiCategory(com.amplifyframework.api.ApiCategory) ApiCategoryConfiguration(com.amplifyframework.api.ApiCategoryConfiguration)

Example 23 with GraphQLResponse

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());
}
Also used : ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) Post(com.amplifyframework.testmodels.commentsblog.Post) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) Date(java.util.Date) ModelSchema(com.amplifyframework.core.model.ModelSchema) Temporal(com.amplifyframework.core.model.temporal.Temporal) BlogOwner(com.amplifyframework.testmodels.commentsblog.BlogOwner) Blog(com.amplifyframework.testmodels.commentsblog.Blog) Test(org.junit.Test)

Example 24 with GraphQLResponse

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());
}
Also used : Arrays(java.util.Arrays) AmplifyException(com.amplifyframework.AmplifyException) NonNull(androidx.annotation.NonNull) ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) ModelProvider(com.amplifyframework.core.model.ModelProvider) DataStoreChannelEventName(com.amplifyframework.datastore.DataStoreChannelEventName) AppSync(com.amplifyframework.datastore.appsync.AppSync) AtomicReference(java.util.concurrent.atomic.AtomicReference) Empty(com.amplifyframework.util.Empty) SchemaRegistry(com.amplifyframework.core.model.SchemaRegistry) HashSet(java.util.HashSet) Schedulers(io.reactivex.rxjava3.schedulers.Schedulers) AppSyncExtensions(com.amplifyframework.datastore.appsync.AppSyncExtensions) Consumer(com.amplifyframework.core.Consumer) CompositeDisposable(io.reactivex.rxjava3.disposables.CompositeDisposable) SubscriptionType(com.amplifyframework.api.graphql.SubscriptionType) Locale(java.util.Locale) Observable(io.reactivex.rxjava3.core.Observable) ModelSchema(com.amplifyframework.core.model.ModelSchema) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) QueryPredicate(com.amplifyframework.core.model.query.predicate.QueryPredicate) HubEvent(com.amplifyframework.hub.HubEvent) Amplify(com.amplifyframework.core.Amplify) HubChannel(com.amplifyframework.hub.HubChannel) SerializedModel(com.amplifyframework.core.model.SerializedModel) AppSyncErrorType(com.amplifyframework.datastore.appsync.AppSyncExtensions.AppSyncErrorType) Model(com.amplifyframework.core.model.Model) Set(java.util.Set) Completable(io.reactivex.rxjava3.core.Completable) Action(com.amplifyframework.core.Action) Logger(com.amplifyframework.logging.Logger) Objects(java.util.Objects) TimeUnit(java.util.concurrent.TimeUnit) DataStoreException(com.amplifyframework.datastore.DataStoreException) List(java.util.List) Cancelable(com.amplifyframework.core.async.Cancelable) ReplaySubject(io.reactivex.rxjava3.subjects.ReplaySubject) AmplifyDisposables(com.amplifyframework.datastore.AmplifyDisposables) GraphQLResponseException(com.amplifyframework.datastore.DataStoreException.GraphQLResponseException) VisibleForTesting(androidx.annotation.VisibleForTesting) QueryPredicate(com.amplifyframework.core.model.query.predicate.QueryPredicate) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) AtomicReference(java.util.concurrent.atomic.AtomicReference) Cancelable(com.amplifyframework.core.async.Cancelable)

Example 25 with GraphQLResponse

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();
}
Also used : Amplify(com.amplifyframework.core.Amplify) AmplifyException(com.amplifyframework.AmplifyException) NonNull(androidx.annotation.NonNull) QueryPredicates(com.amplifyframework.core.model.query.predicate.QueryPredicates) GraphQLRequest(com.amplifyframework.api.graphql.GraphQLRequest) Model(com.amplifyframework.core.model.Model) Action(com.amplifyframework.core.Action) ApiException(com.amplifyframework.api.ApiException) Logger(com.amplifyframework.logging.Logger) GraphQLBehavior(com.amplifyframework.api.graphql.GraphQLBehavior) DataStoreException(com.amplifyframework.datastore.DataStoreException) Consumer(com.amplifyframework.core.Consumer) Nullable(androidx.annotation.Nullable) Cancelable(com.amplifyframework.core.async.Cancelable) SubscriptionType(com.amplifyframework.api.graphql.SubscriptionType) AuthModeStrategyType(com.amplifyframework.api.aws.AuthModeStrategyType) NoOpCancelable(com.amplifyframework.core.async.NoOpCancelable) ModelSchema(com.amplifyframework.core.model.ModelSchema) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) ApiCategoryBehavior(com.amplifyframework.api.ApiCategoryBehavior) PaginatedResult(com.amplifyframework.api.graphql.PaginatedResult) QueryPredicate(com.amplifyframework.core.model.query.predicate.QueryPredicate) DataStoreException(com.amplifyframework.datastore.DataStoreException) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) NoOpCancelable(com.amplifyframework.core.async.NoOpCancelable) Cancelable(com.amplifyframework.core.async.Cancelable) NoOpCancelable(com.amplifyframework.core.async.NoOpCancelable) ApiException(com.amplifyframework.api.ApiException)

Aggregations

GraphQLResponse (com.amplifyframework.api.graphql.GraphQLResponse)30 Test (org.junit.Test)23 PaginatedResult (com.amplifyframework.api.graphql.PaginatedResult)14 Model (com.amplifyframework.core.model.Model)11 ApiException (com.amplifyframework.api.ApiException)10 Action (com.amplifyframework.core.Action)9 Consumer (com.amplifyframework.core.Consumer)9 ModelWithMetadata (com.amplifyframework.datastore.appsync.ModelWithMetadata)9 JSONObject (org.json.JSONObject)9 BlogOwner (com.amplifyframework.testmodels.commentsblog.BlogOwner)8 GraphQLRequest (com.amplifyframework.api.graphql.GraphQLRequest)7 RandomString (com.amplifyframework.testutils.random.RandomString)7 AmplifyException (com.amplifyframework.AmplifyException)6 ModelSchema (com.amplifyframework.core.model.ModelSchema)6 ModelMetadata (com.amplifyframework.datastore.appsync.ModelMetadata)6 RandomModel (com.amplifyframework.testutils.random.RandomModel)6 HashMap (java.util.HashMap)6 QueryType (com.amplifyframework.api.graphql.QueryType)5 SubscriptionType (com.amplifyframework.api.graphql.SubscriptionType)5 Cancelable (com.amplifyframework.core.async.Cancelable)5