Search in sources :

Example 51 with Model

use of com.amplifyframework.core.model.Model in project amplify-android by aws-amplify.

the class ConflictResolver method resolve.

@NonNull
<T extends Model> Single<ModelWithMetadata<T>> resolve(@NonNull PendingMutation<T> pendingMutation, @NonNull AppSyncConflictUnhandledError<T> conflictUnhandledError) {
    final DataStoreConflictHandler conflictHandler;
    try {
        conflictHandler = configurationProvider.getConfiguration().getConflictHandler();
    } catch (DataStoreException badConfigurationProvider) {
        return Single.error(badConfigurationProvider);
    }
    ModelWithMetadata<T> serverData = conflictUnhandledError.getServerVersion();
    ModelMetadata metadata = serverData.getSyncMetadata();
    T local = getMutatedModelFromSerializedModel(pendingMutation);
    T remote = getServerModel(serverData, pendingMutation.getMutatedItem());
    ConflictData<T> conflictData = ConflictData.create(local, remote);
    return Single.<ConflictResolutionDecision<? extends Model>>create(emitter -> conflictHandler.onConflictDetected(conflictData, emitter::onSuccess)).flatMap(decision -> {
        @SuppressWarnings("unchecked") ConflictResolutionDecision<T> typedDecision = (ConflictResolutionDecision<T>) decision;
        return resolveModelAndMetadata(conflictData, metadata, typedDecision);
    });
}
Also used : DataStoreConfigurationProvider(com.amplifyframework.datastore.DataStoreConfigurationProvider) Single(io.reactivex.rxjava3.core.Single) DataStoreConflictHandler(com.amplifyframework.datastore.DataStoreConflictHandler) SerializedModel(com.amplifyframework.core.model.SerializedModel) NonNull(androidx.annotation.NonNull) ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) Model(com.amplifyframework.core.model.Model) AppSync(com.amplifyframework.datastore.appsync.AppSync) AppSyncConflictUnhandledError(com.amplifyframework.datastore.appsync.AppSyncConflictUnhandledError) SchemaRegistry(com.amplifyframework.core.model.SchemaRegistry) ConflictData(com.amplifyframework.datastore.DataStoreConflictHandler.ConflictData) GsonFactory(com.amplifyframework.util.GsonFactory) Objects(java.util.Objects) DataStoreException(com.amplifyframework.datastore.DataStoreException) Type(java.lang.reflect.Type) Gson(com.google.gson.Gson) ModelSchema(com.amplifyframework.core.model.ModelSchema) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) ConflictResolutionDecision(com.amplifyframework.datastore.DataStoreConflictHandler.ConflictResolutionDecision) DataStoreException(com.amplifyframework.datastore.DataStoreException) ConflictResolutionDecision(com.amplifyframework.datastore.DataStoreConflictHandler.ConflictResolutionDecision) DataStoreConflictHandler(com.amplifyframework.datastore.DataStoreConflictHandler) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) NonNull(androidx.annotation.NonNull)

Example 52 with Model

use of com.amplifyframework.core.model.Model in project amplify-android by aws-amplify.

the class SQLiteModelTree method descendantsOf.

/**
 * Returns a map of descendants of a set of models (of same type).
 * A model is a child of its parent if it uses its parent's ID as foreign key.
 * @param root Collection of models to query its descendants of.
 * @return List of models that are descendants of given models. These models will
 *          have the correct model type and ID, but no other field will be populated.
 */
<T extends Model> List<Model> descendantsOf(Collection<T> root) {
    if (Empty.check(root)) {
        return new ArrayList<>();
    }
    Map<ModelSchema, Set<String>> modelMap = new LinkedHashMap<>();
    Model rootModel = root.iterator().next();
    ModelSchema rootSchema = registry.getModelSchemaForModelClass(getModelName(rootModel));
    Set<String> rootIds = new HashSet<>();
    for (T model : root) {
        rootIds.add(model.getId());
    }
    recurseTree(modelMap, rootSchema, rootIds);
    List<Model> descendants = new ArrayList<>();
    for (Map.Entry<ModelSchema, Set<String>> entry : modelMap.entrySet()) {
        ModelSchema schema = entry.getKey();
        for (String id : entry.getValue()) {
            if (rootModel.getClass() == SerializedModel.class) {
                SerializedModel dummyItem = SerializedModel.builder().serializedData(Collections.singletonMap("id", id)).modelSchema(schema).build();
                descendants.add(dummyItem);
            } else {
                // Create dummy model instance using just the ID and model type
                String dummyJson = gson.toJson(Collections.singletonMap("id", id));
                Model dummyItem = gson.fromJson(dummyJson, schema.getModelClass());
                descendants.add(dummyItem);
            }
        }
    }
    return descendants;
}
Also used : HashSet(java.util.HashSet) Set(java.util.Set) ArrayList(java.util.ArrayList) SerializedModel(com.amplifyframework.core.model.SerializedModel) LinkedHashMap(java.util.LinkedHashMap) ModelSchema(com.amplifyframework.core.model.ModelSchema) SerializedModel(com.amplifyframework.core.model.SerializedModel) Model(com.amplifyframework.core.model.Model) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) HashSet(java.util.HashSet)

Example 53 with Model

use of com.amplifyframework.core.model.Model in project amplify-android by aws-amplify.

the class SQLiteStorageAdapter method initialize.

/**
 * {@inheritDoc}
 */
@Override
public synchronized void initialize(@NonNull Context context, @NonNull Consumer<List<ModelSchema>> onSuccess, @NonNull Consumer<DataStoreException> onError, @NonNull DataStoreConfiguration dataStoreConfiguration) {
    Objects.requireNonNull(context);
    Objects.requireNonNull(onSuccess);
    Objects.requireNonNull(onError);
    // Create a thread pool large enough to take advantage of parallelization, but small enough to avoid
    // OutOfMemoryError and CursorWindowAllocationException issues.
    this.threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * THREAD_POOL_SIZE_MULTIPLIER);
    this.context = context;
    this.dataStoreConfiguration = dataStoreConfiguration;
    threadPool.submit(() -> {
        try {
            /*
                 * Start with a fresh registry.
                 */
            schemaRegistry.clear();
            /*
                 * Create {@link ModelSchema} objects for the corresponding {@link Model}.
                 * Any exception raised during this when inspecting the Model classes
                 * through reflection will be notified via the `onError` callback.
                 */
            schemaRegistry.register(modelsProvider.modelSchemas(), modelsProvider.customTypeSchemas());
            /*
                 * Create the CREATE TABLE and CREATE INDEX commands for each of the
                 * Models. Instantiate {@link SQLiteStorageHelper} to execute those
                 * create commands.
                 */
            this.sqlCommandFactory = new SQLiteCommandFactory(schemaRegistry, gson);
            CreateSqlCommands createSqlCommands = getCreateCommands(modelsProvider.modelNames());
            sqliteStorageHelper = SQLiteStorageHelper.getInstance(context, databaseName, DATABASE_VERSION, createSqlCommands);
            /*
                 * Create and/or open a database. This also invokes
                 * {@link SQLiteStorageHelper#onCreate(SQLiteDatabase)} which executes the tasks
                 * to create tables and indexes. When the function returns without any exception
                 * being thrown, invoke the `onError` callback.
                 *
                 * Errors are thrown when there is no write permission to the database, no space
                 * left in the database for any write operation and other errors thrown while
                 * creating and opening a database. All errors are passed through the
                 * `onError` callback.
                 *
                 * databaseConnectionHandle represents a connection handle to the database.
                 * All database operations will happen through this handle.
                 */
            databaseConnectionHandle = sqliteStorageHelper.getWritableDatabase();
            /*
                 * Create helper instance that can traverse through model relations.
                 */
            this.sqliteModelTree = new SQLiteModelTree(schemaRegistry, databaseConnectionHandle);
            /*
                 * Create a command processor which runs the actual SQL transactions.
                 */
            this.sqlCommandProcessor = new SQLCommandProcessor(databaseConnectionHandle);
            sqlQueryProcessor = new SqlQueryProcessor(sqlCommandProcessor, sqlCommandFactory, schemaRegistry);
            syncStatus = new SyncStatus(sqlQueryProcessor, dataStoreConfiguration);
            /*
                 * Detect if the version of the models stored in SQLite is different
                 * from the version passed in through {@link ModelProvider#version()}.
                 * Delete the database if there is a version change.
                 */
            toBeDisposed.add(updateModels().subscribe(() -> onSuccess.accept(Immutable.of(new ArrayList<>(schemaRegistry.getModelSchemaMap().values()))), throwable -> onError.accept(new DataStoreException("Error in initializing the SQLiteStorageAdapter", throwable, AmplifyException.TODO_RECOVERY_SUGGESTION))));
        } catch (Exception exception) {
            onError.accept(new DataStoreException("Error in initializing the SQLiteStorageAdapter", exception, "See attached exception"));
        }
    });
}
Also used : ObjectsCompat(androidx.core.util.ObjectsCompat) AmplifyException(com.amplifyframework.AmplifyException) NonNull(androidx.annotation.NonNull) QueryPredicates(com.amplifyframework.core.model.query.predicate.QueryPredicates) ModelProvider(com.amplifyframework.core.model.ModelProvider) StorageItemChange(com.amplifyframework.datastore.storage.StorageItemChange) SQLiteDatabase(android.database.sqlite.SQLiteDatabase) Gson(com.google.gson.Gson) Map(java.util.Map) PublishSubject(io.reactivex.rxjava3.subjects.PublishSubject) QueryOptions(com.amplifyframework.core.model.query.QueryOptions) CustomTypeSchema(com.amplifyframework.core.model.CustomTypeSchema) SerializedModel(com.amplifyframework.core.model.SerializedModel) Immutable(com.amplifyframework.util.Immutable) Set(java.util.Set) Executors(java.util.concurrent.Executors) Logger(com.amplifyframework.logging.Logger) Objects(java.util.Objects) DataStoreException(com.amplifyframework.datastore.DataStoreException) LocalStorageAdapter(com.amplifyframework.datastore.storage.LocalStorageAdapter) List(java.util.List) Cancelable(com.amplifyframework.core.async.Cancelable) SQLiteTable(com.amplifyframework.datastore.storage.sqlite.adapter.SQLiteTable) Disposable(io.reactivex.rxjava3.disposables.Disposable) Context(android.content.Context) SerializedCustomType(com.amplifyframework.core.model.SerializedCustomType) QueryField(com.amplifyframework.core.model.query.predicate.QueryField) Single(io.reactivex.rxjava3.core.Single) ObserveQueryOptions(com.amplifyframework.core.model.query.ObserveQueryOptions) SystemModelsProviderFactory(com.amplifyframework.datastore.model.SystemModelsProviderFactory) HashMap(java.util.HashMap) CustomTypeField(com.amplifyframework.core.model.CustomTypeField) ModelField(com.amplifyframework.core.model.ModelField) ArrayList(java.util.ArrayList) SchemaRegistry(com.amplifyframework.core.model.SchemaRegistry) GsonFactory(com.amplifyframework.util.GsonFactory) HashSet(java.util.HashSet) Consumer(com.amplifyframework.core.Consumer) CompositeDisposable(io.reactivex.rxjava3.disposables.CompositeDisposable) ModelSchema(com.amplifyframework.core.model.ModelSchema) Subject(io.reactivex.rxjava3.subjects.Subject) QueryPredicate(com.amplifyframework.core.model.query.predicate.QueryPredicate) ExecutorService(java.util.concurrent.ExecutorService) Cursor(android.database.Cursor) DataStoreQuerySnapshot(com.amplifyframework.datastore.DataStoreQuerySnapshot) DataStoreConfiguration(com.amplifyframework.datastore.DataStoreConfiguration) Amplify(com.amplifyframework.core.Amplify) CompoundModelProvider(com.amplifyframework.datastore.model.CompoundModelProvider) Iterator(java.util.Iterator) Model(com.amplifyframework.core.model.Model) Completable(io.reactivex.rxjava3.core.Completable) Action(com.amplifyframework.core.Action) Where(com.amplifyframework.core.model.query.Where) ModelAssociation(com.amplifyframework.core.model.ModelAssociation) TimeUnit(java.util.concurrent.TimeUnit) ModelMigrations(com.amplifyframework.datastore.storage.sqlite.migrations.ModelMigrations) VisibleForTesting(androidx.annotation.VisibleForTesting) Collections(java.util.Collections) DataStoreException(com.amplifyframework.datastore.DataStoreException) ArrayList(java.util.ArrayList) AmplifyException(com.amplifyframework.AmplifyException) DataStoreException(com.amplifyframework.datastore.DataStoreException)

Example 54 with Model

use of com.amplifyframework.core.model.Model in project amplify-android by aws-amplify.

the class RxApiBindingTest method subscribeStartsAndGetsCancelled.

/**
 * Verify that the subscription starts and is cancelled gracefully.
 * @throws InterruptedException Not expected.
 */
@SuppressWarnings("rawtypes")
@Test
public void subscribeStartsAndGetsCancelled() throws InterruptedException {
    // Arrange a category behavior which emits an expected sequence of callback events
    String token = RandomString.string();
    GraphQLRequest<Model> request = createMockSubscriptionRequest(Model.class);
    ConnectionStateEvent expectedConnectionStateEvent = new ConnectionStateEvent(ConnectionState.CONNECTED, token);
    doAnswer(invocation -> {
        final int onStartPosition = 1;
        final int onCompletePosition = 4;
        Consumer<String> onStart = invocation.getArgument(onStartPosition);
        Action onComplete = invocation.getArgument(onCompletePosition);
        onStart.accept(token);
        GraphQLOperation mockApiOperation = mock(GraphQLOperation.class);
        doAnswer(apiCancelInvocation -> {
            onComplete.call();
            return null;
        }).when(mockApiOperation).cancel();
        return mockApiOperation;
    }).when(delegate).subscribe(eq(request), anyConsumer(), anyConsumer(), anyConsumer(), anyAction());
    // Act: subscribe via binding
    RxSubscriptionOperation<GraphQLResponse<Model>> rxOperation = rxApi.subscribe(request);
    // Act: subscribe via binding
    TestObserver<GraphQLResponse<Model>> dataObserver = rxOperation.observeSubscriptionData().test();
    TestObserver<ConnectionStateEvent> startObserver = rxOperation.observeConnectionState().test();
    startObserver.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
    startObserver.assertValue(expectedConnectionStateEvent);
    startObserver.assertNoErrors();
    // Act: cancel the subscription
    Completable.timer(1, TimeUnit.SECONDS).andThen(Completable.fromAction(rxOperation::cancel)).subscribe();
    dataObserver.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
    dataObserver.assertNoValues();
    dataObserver.assertNoErrors();
    dataObserver.assertComplete();
    startObserver.assertComplete();
}
Also used : Matchers.anyAction(com.amplifyframework.rx.Matchers.anyAction) Action(com.amplifyframework.core.Action) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) RandomString(com.amplifyframework.testutils.random.RandomString) ConnectionStateEvent(com.amplifyframework.rx.RxOperations.RxSubscriptionOperation.ConnectionStateEvent) GraphQLOperation(com.amplifyframework.api.graphql.GraphQLOperation) RandomModel(com.amplifyframework.testutils.random.RandomModel) Model(com.amplifyframework.core.model.Model) Test(org.junit.Test)

Example 55 with Model

use of com.amplifyframework.core.model.Model in project amplify-android by aws-amplify.

the class RxApiBindingTest method queryEmitsFailure.

/**
 * When the API behavior emits a failure for a query, so too should the Rx binding.
 * @throws InterruptedException If interrupted while test observer is awaiting terminal event
 */
@Test
public void queryEmitsFailure() throws InterruptedException {
    // Arrange: category behavior emits a failure
    ApiException expectedFailure = new ApiException("Expected", "Failure");
    GraphQLRequest<Iterable<Model>> listRequest = createMockListRequest(Model.class);
    doAnswer(invocation -> {
        // 0 = clazz, 1 = onResponse, 2 = onFailure
        final int positionOfOnFailure = 2;
        Consumer<ApiException> onFailure = invocation.getArgument(positionOfOnFailure);
        onFailure.accept(expectedFailure);
        return null;
    }).when(delegate).query(eq(listRequest), anyConsumer(), anyConsumer());
    // Act: access query() method via Rx binding
    TestObserver<GraphQLResponse<Iterable<Model>>> observer = rxApi.query(listRequest).test();
    // Assert: failure bubbles up to Rx
    observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
    observer.assertError(expectedFailure);
    verify(delegate).query(eq(listRequest), anyConsumer(), anyConsumer());
}
Also used : RandomModel(com.amplifyframework.testutils.random.RandomModel) Model(com.amplifyframework.core.model.Model) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) ApiException(com.amplifyframework.api.ApiException) Test(org.junit.Test)

Aggregations

Model (com.amplifyframework.core.model.Model)62 Test (org.junit.Test)37 DataStoreException (com.amplifyframework.datastore.DataStoreException)34 ModelSchema (com.amplifyframework.core.model.ModelSchema)32 SerializedModel (com.amplifyframework.core.model.SerializedModel)30 AmplifyException (com.amplifyframework.AmplifyException)23 Cancelable (com.amplifyframework.core.async.Cancelable)22 Action (com.amplifyframework.core.Action)19 Consumer (com.amplifyframework.core.Consumer)19 StorageItemChange (com.amplifyframework.datastore.storage.StorageItemChange)19 BlogOwner (com.amplifyframework.testmodels.commentsblog.BlogOwner)19 ArrayList (java.util.ArrayList)19 List (java.util.List)19 SchemaRegistry (com.amplifyframework.core.model.SchemaRegistry)18 RandomModel (com.amplifyframework.testutils.random.RandomModel)17 GraphQLResponse (com.amplifyframework.api.graphql.GraphQLResponse)16 QueryPredicates (com.amplifyframework.core.model.query.predicate.QueryPredicates)16 TimeUnit (java.util.concurrent.TimeUnit)16 Collections (java.util.Collections)15 ObserveQueryOptions (com.amplifyframework.core.model.query.ObserveQueryOptions)13