aws-amplify / amplify-flutter

A declarative library with an easy-to-use interface for building Flutter applications on AWS.
https://docs.amplify.aws
Apache License 2.0
1.31k stars 242 forks source link

Better developer experience when using API plugin to operate data managed by DataStore #1945

Open HuiSF opened 2 years ago

HuiSF commented 2 years ago

Description

This is an aggregated feature request tracking issue for use cases of using API plugin to operate DataStore.

Please refer to the linked issues below to see the original feature requests.

Thanks everyone for providing your feedback and requests.

  1. DataStore codegen models should support conflict detection meta fields _deleted, _lastChangedAt and _version DataStore requires enabling conflict detection while configuring API category. Conflict detection adds three aforementioned meta fields into GraphQL schema. These meta fields are currently hidden from codegen generated model, hence, the API plugin GraphQL helper cannot generate a selection set including these mandatory fields, which requires developers to manually create GraphQL document.
  2. API plugin and GraphQL helper should support use cases when enabling conflict detection even when DataStore is not used
  3. DataStore should support "partial" selection set with subscription, so when using API plugin to mutate data with a subset of model fields listed in the selection set, DataStore can still receive a subscription event correctly
  4. Configuring conflict detection and resolution strategies per model not per API
  5. Amplify API plugin supports use case that operating on multiple REST or GraphQL APIs, however, DataStore throws exception and reject to sync when multiple GraphQL APIs are listed in amplifyconfiguration.dart. In this case, DataStore should be able to pick appropriate GraphQL endpoint for its sync engine.
  6. AppSync supports dynamic group authorization with limitations on real-time subscriptions. As a developer I'd like to use dynamic group auth for some use cases, but it somehow breaks DataStore functionality. I want the DataStore can still function when I'm applying dynamic group auth to some models. (related to https://github.com/aws-amplify/amplify-android/issues/1574)

Categories

Steps to Reproduce

No response

Screenshots

No response

Platforms

Android Device/Emulator API Level

No response

Environment

N/A

Dependencies

N/A

Device

N/A

OS

N/A

CLI Version

N/A

Additional Context

No response

adplant commented 10 months ago

This ticket still being worked on?

thomasklaush commented 7 months ago

Are there any updates? Can the _version field be added manually, or will this cause other issues?

sampaio96 commented 2 months ago

Hi. So I think I came up with a pretty good solution for this. It's as simple as I could, and I'll explain after why it is working for me.

  1. Add the field version: Int to every one of your models on your schema.graphql. Run amplify codegen models.
  2. Do Shift-Cmd-R ("Replace in Files…"), select the directory lib/models and replace every instance of 'version' with '_version'. You need to do this every time you run amplify codegen models, sadly.
  3. Change the instances of your function Amplify.API.query(request: request) to Amplify.API.query(request: request.copyWith(document: request.document.replaceFirst('version','_version'))).

Your GraphQL updates and deletes should be working now, even if you have conflict resolution turned on. This means you can use GraphQL and DataStore on the same Flutter app!

sampaio96 commented 2 months ago

So, what does this do?

Create and Read methods work on GraphQL because they do not require you to provide the _version field. But if you do update/delete requests, and you have conflict resolution set up, these methods fail, because you need to provide the field _version. The problem is that there is no way to get that field (and no way to store it).

The first thing we do is create a fake Int? version field on the schema. We will use this to trick the Amplify API to think this is the real _version. This way we can 1) get the current _version field from the DynamoDB, and 2) send the updated _version field on the update/delete requests. All of this without writing any GraphQL.

Now that our model has a version field, we can edit the code for the queries. Remember that a query is what retrieves all the information on the DB about our objects. Our goal is to get the '_version' of our objects in addition to all their other properties, so that we can later use it for update/delete requests.

The API.query function is GraphQL code to get each and every one of the fields in your model. Since you defined version in your schema, if you run API.query, you will get the version on the DB. But if you do a simple string-replace on the GraphQL request, you fool the API library into getting you the _version number instead. (That is step 3.)

This is almost done. The Amplify API is getting the _version field, but it doesn't know where to save it to afterwards. It uses JSON to convert the API response into a Model Object. But the Model Class does not have a _version field, so even if the API retrieves this field, it will not get saved. Your goal is to edit the amplify code that converts the response JSON into a Model Object. That code is in the lib/models folder. If you replace every instance of 'version' with '_version', you will tell Amplify to take the '_version' that you get on JSON responses to API requests, and save it on the version field of your Model.

This is it! Since the version/_version field is kept in sync between your Model and the DB, when you run your update or delete request, the up-to-date version is automatically sent. In this request, the Model code you edited earlier will convert the version field on the Model Object into the JSON '_version', which will get correctly interpreted by AppSync.