Search in sources :

Example 31 with ModelWithMetadata

use of com.amplifyframework.datastore.appsync.ModelWithMetadata 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 32 with ModelWithMetadata

use of com.amplifyframework.datastore.appsync.ModelWithMetadata in project amplify-android by aws-amplify.

the class SyncProcessor method hydrateSchemaIfNeeded.

// Cast to T
@SuppressWarnings("unchecked")
private <T extends Model> ModelWithMetadata<T> hydrateSchemaIfNeeded(ModelWithMetadata<T> original, ModelSchema schema) {
    if (original.getModel() instanceof SerializedModel) {
        SerializedModel originalModel = (SerializedModel) original.getModel();
        SerializedModel newModel = SerializedModel.builder().serializedData(SerializedModel.parseSerializedData(originalModel.getSerializedData(), schema.getName(), schemaRegistry)).modelSchema(schema).build();
        return new ModelWithMetadata<>((T) newModel, original.getSyncMetadata());
    } else {
        return original;
    }
}
Also used : ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) SerializedModel(com.amplifyframework.core.model.SerializedModel)

Example 33 with ModelWithMetadata

use of com.amplifyframework.datastore.appsync.ModelWithMetadata in project amplify-android by aws-amplify.

the class Merger method merge.

/**
 * Merge an item back into the local store, using a default strategy.
 * TODO: Change this method to return a Maybe, and remove the Consumer argument.
 * @param modelWithMetadata A model, combined with metadata about it
 * @param changeTypeConsumer A callback invoked when the merge method saves or deletes the model.
 * @param <T> Type of model
 * @return A completable operation to merge the model
 */
<T extends Model> Completable merge(ModelWithMetadata<T> modelWithMetadata, Consumer<StorageItemChange.Type> changeTypeConsumer) {
    AtomicReference<Long> startTime = new AtomicReference<>();
    return Completable.defer(() -> {
        ModelMetadata metadata = modelWithMetadata.getSyncMetadata();
        boolean isDelete = Boolean.TRUE.equals(metadata.isDeleted());
        int incomingVersion = metadata.getVersion() == null ? -1 : metadata.getVersion();
        T model = modelWithMetadata.getModel();
        return versionRepository.findModelVersion(model).onErrorReturnItem(-1).filter(currentVersion -> currentVersion == -1 || incomingVersion > currentVersion).flatMapCompletable(shouldMerge -> {
            Completable firstStep;
            if (mutationOutbox.hasPendingMutation(model.getId())) {
                LOG.info("Mutation outbox has pending mutation for " + model.getId() + ". Saving the metadata, but not model itself.");
                firstStep = Completable.complete();
            } else {
                firstStep = (isDelete ? delete(model, changeTypeConsumer) : save(model, changeTypeConsumer));
            }
            return firstStep.andThen(save(metadata, NoOpConsumer.create()));
        }).doOnComplete(() -> {
            announceSuccessfulMerge(modelWithMetadata);
            LOG.debug("Remote model update was sync'd down into local storage: " + modelWithMetadata);
        }).onErrorComplete(failure -> {
            if (!ErrorInspector.contains(failure, SQLiteConstraintException.class)) {
                return false;
            }
            LOG.warn("Sync failed: foreign key constraint violation: " + modelWithMetadata, failure);
            return true;
        }).doOnError(failure -> LOG.warn("Failed to sync remote model into local storage: " + modelWithMetadata, failure));
    }).doOnSubscribe(disposable -> startTime.set(System.currentTimeMillis())).doOnTerminate(() -> {
        long duration = System.currentTimeMillis() - startTime.get();
        LOG.verbose("Merged a single item in " + duration + " ms.");
    });
}
Also used : Amplify(com.amplifyframework.core.Amplify) HubChannel(com.amplifyframework.hub.HubChannel) NonNull(androidx.annotation.NonNull) QueryPredicates(com.amplifyframework.core.model.query.predicate.QueryPredicates) ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) StorageItemChange(com.amplifyframework.datastore.storage.StorageItemChange) DataStoreChannelEventName(com.amplifyframework.datastore.DataStoreChannelEventName) Model(com.amplifyframework.core.model.Model) Completable(io.reactivex.rxjava3.core.Completable) AtomicReference(java.util.concurrent.atomic.AtomicReference) ErrorInspector(com.amplifyframework.datastore.utils.ErrorInspector) Logger(com.amplifyframework.logging.Logger) Objects(java.util.Objects) Consumer(com.amplifyframework.core.Consumer) LocalStorageAdapter(com.amplifyframework.datastore.storage.LocalStorageAdapter) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) SQLiteConstraintException(android.database.sqlite.SQLiteConstraintException) NoOpConsumer(com.amplifyframework.core.NoOpConsumer) HubEvent(com.amplifyframework.hub.HubEvent) Completable(io.reactivex.rxjava3.core.Completable) AtomicReference(java.util.concurrent.atomic.AtomicReference) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata)

Example 34 with ModelWithMetadata

use of com.amplifyframework.datastore.appsync.ModelWithMetadata in project amplify-android by aws-amplify.

the class MutationProcessor method modelWithSchemaAdded.

private <T extends Model> ModelWithMetadata<? extends Model> modelWithSchemaAdded(ModelWithMetadata<T> modelWithMetadata, ModelSchema modelSchema) {
    final SerializedModel originalModel = (SerializedModel) modelWithMetadata.getModel();
    final SerializedModel newModel = SerializedModel.builder().serializedData(SerializedModel.parseSerializedData(originalModel.getSerializedData(), modelSchema.getName(), schemaRegistry)).modelSchema(modelSchema).build();
    return new ModelWithMetadata<>(newModel, modelWithMetadata.getSyncMetadata());
}
Also used : ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) SerializedModel(com.amplifyframework.core.model.SerializedModel)

Example 35 with ModelWithMetadata

use of com.amplifyframework.datastore.appsync.ModelWithMetadata 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)

Aggregations

ModelWithMetadata (com.amplifyframework.datastore.appsync.ModelWithMetadata)35 ModelMetadata (com.amplifyframework.datastore.appsync.ModelMetadata)25 Test (org.junit.Test)22 BlogOwner (com.amplifyframework.testmodels.commentsblog.BlogOwner)21 GraphQLResponse (com.amplifyframework.api.graphql.GraphQLResponse)13 ModelSchema (com.amplifyframework.core.model.ModelSchema)10 Temporal (com.amplifyframework.core.model.temporal.Temporal)10 Consumer (com.amplifyframework.core.Consumer)9 Model (com.amplifyframework.core.model.Model)9 SerializedModel (com.amplifyframework.core.model.SerializedModel)9 SchemaRegistry (com.amplifyframework.core.model.SchemaRegistry)8 HubEvent (com.amplifyframework.hub.HubEvent)8 Completable (io.reactivex.rxjava3.core.Completable)8 DataStoreException (com.amplifyframework.datastore.DataStoreException)7 AppSync (com.amplifyframework.datastore.appsync.AppSync)7 HubChannel (com.amplifyframework.hub.HubChannel)7 List (java.util.List)7 NonNull (androidx.annotation.NonNull)6 Amplify (com.amplifyframework.core.Amplify)6 ModelProvider (com.amplifyframework.core.model.ModelProvider)6