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 241 forks source link

Query with updatedAt / createdAt fields with DataStore #1524

Open Rayv1 opened 2 years ago

Rayv1 commented 2 years ago

Hi,

i try to query the timestamps updatedAt / createdAt with DataStore, so i added them as Fields, so that i can query them with Datastore, as otherwise they were not present.

Schema:

type UserTargets @model @auth(rules: [ {allow: owner}]) {
  id: ID!
  date: AWSDateTime!
  owner: String @index(name: "byOwner")
  createdAt: AWSDateTime
  updatedAt: AWSDateTime
}

Query:

var targets = await Amplify.DataStore.query(UserTargets.classType,
    where: UserTargets.UPDATEDAT.le(date),
    sortBy: [UserTargets.DATE.descending()],
    pagination: QueryPagination.firstResult());

I dont want to manage them by myself and dont want to mutate or delete them, but rather query them. From my understanding they are set by the backend (appsync) automatically, right?

My Issue is, when saving them without updatedAt / createdAt, the fields are null inside dynamodb item. Code to save:

final u = UserTargets(
    date: TemporalDateTime.fromString('1930-01-01T16:00:00-07:00'));
Amplify.DataStore.save(u);

When i now create a new item via the appsync gui and just pass the date, without updatedAt / createdAt, they are created automatically:

mutation MyMutation {
  createUserTargets(input: {date: "1935-01-01T16:00:00-07:00",}) {
    carbs
  }
}

And when passing updatedAt / createdAt with null:

mutation MyMutation {
  createUserTargets(input: {date: "1935-01-01T16:00:00-07:00", updatedAt: null, createdAt: null}) {
    carbs
  }
}

Logs from Cloudwatch showing the mutation which has createdAt & updatedAt with null values

Query: mutation CreateUserTargets($input: CreateUserTargetsInput!) {
  createUserTargets(input: $input) {
    _deleted
    _lastChangedAt
    _version
    createdAt
    date
    updatedAt

  }
}
, Operation: null, Variables: 
{
    "input": {
        "date": "1930-01-01T16:00:00.000000000-07:00",
        "id": "31134543-a4cc-46be-82c3-3a3f5c5a7618"
    }
}

Again when using the gui with:

mutation CreateUserTargets{
  createUserTargets(input: {
        date: "1930-01-01T16:00:00.000000000-07:00",
        id: "31134543-a4cc-46be-82c3-asadasdasd"

  }) {
    _deleted
    _lastChangedAt
    _version
    createdAt
    date
    updatedAt
  }
}

its working and i have not clue why. Is this issue amplify-flutter related and if not, where does this issue belong?

Thanks! Ray

HuiSF commented 2 years ago

Hello @Rayv1 thanks for inquiring and providing detailed example of your use case.

About the db managed createdAt and updatedAt fields

Currently amplify-flutter models should contain createdAt and updatedAt already (automatically added by @model), which are readonly fields and you don't need to add in schema manually. However, looking at the generated models, these two fields are not supporting create query predicates with them, e.g.

// schema 
type UserTargets @model @auth(rules: [ {allow: owner}]) {
  id: ID!
  date: AWSDateTime!
  owner: String @index(name: "byOwner")
}

// You can get the value of createdAt and updatedAt
var userTarget = (await Amplify.DataStore.query(UserTargets.classType)).first;
print(userTarget.createdAt);
print(userTarget.updatedAt);

// But you can't create query predicates with these fields
❌ Amplify.DataStore.query(UserTargets.classType, where: UserTargets.CREATEDAT.le())

This behavior is the same as amplify-android, amplify-ios however, provides a code path to create query predicates with the timestamp fields. I will mark this part of issue as a feature request.

Regarding the issue:

My Issue is, when saving them without updatedAt / createdAt, the fields are null inside dynamodb item.

This behavior occurs when you manually inserting createdAt and updatedAt in the schema, which instructs DataStore client logic to attach createdAt and updatedAt fields in the underlying AppSync GraphQL API calls.

Possible workaround

Unfortunately, there is no effective workaround creating query predicates with the built-in DB managed timestamp fields until the feature request to be fulfilled.

A recommendation is to add a self-managed timestamp fields

type UserTargets @model @auth(rules: [ {allow: owner}]) {
  id: ID!
  date: AWSDateTime!
  owner: String @index(name: "byOwner")
  // different field names from the db managed timestamp fields
  createdOn: AWSDateTime
  updatedOn: AWSDateTime
}

This requires manually setting the values for these two fields when creating/updating the model.

Rayv1 commented 2 years ago

Thanks for your reply and clarification that it is indeed not currently implemented and I wasn't just being silly.

HuiSF commented 2 years ago

Hi @Rayv1 we will keep this issue open for the purpose of tracking this feature request. Thanks.

PeblTech commented 1 year ago

Hi @HuiSF

Without the default createdAt and updatedAt fields being added to the Model, the synchronisation process fails between Flutter and other DataStores (e.g. React).

The reason being that the Flutter DataStore triggers an update of the following format (note the lack of createdAt and updatedAt in the response request:-

updateThing(input: $input) {
    __typename
    _deleted
    _lastChangedAt
    _version
    title
    id
  }
}
, Operation: null, Variables: 
{
    "input": {
        "id": "6ac0f902-0b72-4a17-a327-04ab3b12e023",
        "title": "Wasdas",
        "_version": 18
    }
}

React DataStores receive the subscription to the onUpdate but it fails with the following GQL error:-

Cannot return null for non-nullable type: 'AWSDateTime' within parent 'Thing' (/onUpdateThing/updatedAt)

The only workaround for this is to remove the concept of createdAt and updatedAt from the React Datastore (which is less than ideal)

Thanks mate !!!

HuiSF commented 1 year ago

Hi @PeblTech I'm a bit lost with your comment

Without the default createdAt and updatedAt fields being added to the Model

As I mentioned above, createdAt and updatedAt are default fields (as long as you use @model) for a model in AppSync, the problem of this issue is that you can't directly create query predicates with these two fields, but not that these fields are missing.

For your issue:

the Flutter DataStore triggers an update of the following format

kingcali3 commented 1 year ago

Has there been any update to the createdAt and updatedAt fields having the ability to be queried using datastore?

qwertylolman commented 4 months ago

any updates? it would be nice to have a read-only access to the owner, createdAt, updatedAt fields