aws-amplify / amplify-cli

The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development.
Apache License 2.0
2.81k stars 821 forks source link

Is it possible to access createdAt and updatedAt? #401

Closed michaelcaterisano closed 4 years ago

michaelcaterisano commented 5 years ago

Which Category is your question related to? amplify schema and DynamoDB

What AWS Services are you utilizing? amplify-js, amplify-cli, AppSync, DynamoDb

Provide additional details e.g. code snippets

I've noticed that the Dynamo DB fields createdAt and updatedAt are automatically generated when I amplify push my graphql schema, but I can't figure out a way to query these fields. Is it possible, or should I add my own custom createdAt and updatedAt fields? Perhaps with different names? Just wondering what the best way to approach this is.

Without date (createdAt and updatedAt automatically generated in DynamoDB):

type Category @model @auth(rules: [{allow: owner}]) {
  id: ID!
  name: String!
  products: [Product] @connection(name: "CategoryProducts")
}

Solution 1 (add createdOn field, so not to override auto createdAt field):

type Category @model @auth(rules: [{allow: owner}]) {
  id: ID!
  createdOn: AWSDateTime!
  name: String!
  products: [Product] @connection(name: "CategoryProducts")
}

Solution 2 (override createdAt with custom field of same name):

type Category @model @auth(rules: [{allow: owner}]) {
  id: ID!
  createdAt: AWSDateTime!
  name: String!
  products: [Product] @connection(name: "CategoryProducts")
}
mikeparisstuff commented 5 years ago

Solution 2 will work but you will need to make the fields nullable or else all Create/Update inputs will require them even though they are being set on the server.

michaelcaterisano commented 5 years ago

Meaning not required, like this?

type Category @model @auth(rules: [{allow: owner}]) {
  id: ID!
  createdAt: AWSDateTime
  name: String!
  products: [Product] @connection(name: "CategoryProducts")
}

This works for me!

I tried it the other way, createdAt: AWSDateTime!, and was able to Create, but the date I entered was ignored, and the current DateTime was set on the server.

hisham commented 5 years ago

One minor thing is as soon as you add createdAt to the model in the schema, then the transformer will add this to the CreateCategoryInput and UpdateCategoryInput, potentially allowing the client to pass a new createdAt and modify the createdAt date on an update call.

mikeparisstuff commented 5 years ago

These are good points. I think we should revisit this topic. This feature was added to make keeping track of timestamps easy but I agree that we should have the ability to tweak timestamp behavior. We are trying to make as few backwards incompatible changes as possible so what if we added a "timestamps" argument to @model.

# The default timestamps value would be
type Post @model(timestamps: {create: "createdAt", update: "updatedAt" }) {
  id: ID!
}

We would then remove those timestamp fields from the CreatePostInput and UpdatePostInput input objects.

You could also turn timestamps off altogether

type Post @model(timestamps: null) {
  id: ID!
}

And we would do nothing concerning timestamps. No input augmentation or otherwise?

Would love to hear your thoughts.

michaelcaterisano commented 5 years ago

@mikeparisstuff Your solution would work for me as long as it would then be possible to query/filter by different timestamps. I think that's what you're proposing would be possible.

robert-moore commented 5 years ago

Any update on a timeline for this?

tedkimzikto commented 5 years ago

anyone?

kareemsuhail commented 5 years ago

take a look at this issue issue#355@aws amplify docs @tedkimzikto @robert-moore

janhesters commented 5 years ago

@mikeparisstuff Is this implemented?

ohsik commented 5 years ago

Any updates on this? will it be possible to query createdAt?

acrabb commented 5 years ago

bump :)

austinamorusocfc commented 5 years ago

@jordanranz @mikeparisstuff Is there any head way on this because the thought of having to manage this from the client seems problematic. Also tangental but similar is the idea of an audit log for objects?

yareyaredesuyo commented 5 years ago

This is very confusing.

In amplify docs, createdAt and updatedAt uses String 🤔🤔🤔🤔🤔

https://aws-amplify.github.io/docs/cli/graphql

maybe use DateType(in appsync, AWSDateTime? ) related type shoud be better.

https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html

oreillyross commented 5 years ago

Would love to see a directive type approach @createdAt or @updatedAt like Prisma introduced in April this year, seems a nice tradeoff but still easy to implement for what is a pretty common need for apps. https://www.prisma.io/blog/datamodel-v11-lrzqy1f56c90

phishy commented 5 years ago

Where did this land?

gitzhouxinyu1 commented 5 years ago

Any updates?

mtliendo commented 5 years ago

One minor thing is as soon as you add createdAt to the model in the schema, then the transformer will add this to the CreateCategoryInput and UpdateCategoryInput, potentially allowing the client to pass a new createdAt and modify the createdAt date on an update call.

@hisham Would this be solved by adding a field-level auth directive so that the client wouldn't have the ability to modify?

createdAt: AWSDateTime @auth(rules: [{ allow: owner, operations: [read] }])

hisham commented 5 years ago

One minor thing is as soon as you add createdAt to the model in the schema, then the transformer will add this to the CreateCategoryInput and UpdateCategoryInput, potentially allowing the client to pass a new createdAt and modify the createdAt date on an update call.

@hisham Would this be solved by adding a field-level auth directive so that the client wouldn't have the ability to modify?

createdAt: AWSDateTime @auth(rules: [{ allow: owner, operations: [read] }])

You'd have to try it and look at the vtl resolver code to see how the auth logic is handled. Worth a try for sure.

jkeys-ecg-nmsu commented 4 years ago

https://github.com/aws-amplify/amplify-cli/issues/401#issuecomment-444694535

My question is why not just assume these things on behalf of us developers and build it in as the default case? Even with ledgers and other timestream databases it is still a common use case to query for the n most recent records in a given partition. The CLI should just detect that DynamoDB is the backing data source -- should be easy at the moment ;) -- and generate queries with createdAt and updatedAt fields by default if that's the case. The CLI should also prompt to generate local indexes (with warning about constraints on size) on createdAt or updatedAt.

I'd much rather the core Amplify developers focus on the default developer case (this may or may not be considered opinionated) and take edge cases as feature requests, so that requests like OP don't drop through the cracks. Workarounds should not be required, they should be made the default case.

IMO, the Amplify CLI should be a tool for enforcing best practices with AWS services anyway. Encouraging developers to conform to a sane default while providing escape hatches should be the default.

xitanggg commented 4 years ago

1.5 years from post, a very important feature is still left hanging. It still strikes me as how createdAt and updatedAt are created by default but not accessible by default. What is the point of creating them in the first place then?

michaelcaterisano commented 4 years ago

Agree. Any update on this feature?

On Thu, Mar 26, 2020 at 1:54 PM Xitang notifications@github.com wrote:

1.5 years from post, a very important feature is still left hanging

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/aws-amplify/amplify-cli/issues/401#issuecomment-604582202, or unsubscribe https://github.com/notifications/unsubscribe-auth/AELFEZQQ4OLDPRBEFBXCWPLRJOJHFANCNFSM4GCDNTCQ .

brandonBuster commented 4 years ago

Glad to see there's ongoing interest here. Auto-generated timestamps provided by Appsync aren't of any help if we can't query them.

Additionally, unless these timestamps are explicitly declared in our schemas, there's no way to use timestamps as our Range Keys. Every production-ready app needs ability to index timestamps

One of the main benefits advertised by Amplify/Appsync are their auto-generated queries. But there's really no benefit to this feature if we have to rewrite all of our resolvers.

At this point, I'm starting to believe a more developer friendly path would be to use Amplify's API Gateway / Aurora vs the Appsync / Dynamo stack emphasised by Amplify documentation

krunde commented 4 years ago

I just hit this issue myself. I agree with what others have said. Do we have any update on a possible fix timeline?

murray-minito commented 4 years ago

This is one of those issues that makes me worry about using Amplify. I am commenting to keep the heat on.

This is a really basic feature that should have been in from the start.

contractorwolf commented 4 years ago

where are we on this issue? I want this feature as well (to add my 2 cents)

domezi commented 4 years ago

I agree with @murray-minito, please keep it up!

undefobj commented 4 years ago

Hi we've been investigating this issue the past couple weeks and are working on it. No ETA but we're actively looking into a fix.

yuth commented 4 years ago

This issue has been fixed and available in Amplify CLI v 4.20.0. For more details refer to @model directive usage docs

emmafass commented 4 years ago

@yuth Thank you for this fix! I came across it today and I am trying it out. I have a type called Suggestion that previously did not set createdAt or updatedAt (it just used the defaults).

I am now using @model(timestamps:{createdAt: "createdOn", updatedAt: "updatedOn"}) on this Suggestion type. Adding a new suggestion is working great, and I am able to view createdOn when I query the new Suggestion.

However, when I try to list all Suggestions, I get an error because the old Suggestions in my DB do not have createdOn set.

Cannot return null for non-nullable type: 'AWSDateTime' within parent 'Suggestion' (/listSuggestions/items[1]/createdOn)

When I try to update an old Suggestion, I get the error:

ObjectField{name='createdOn', value=StringValue{value='2020-04-29T00:04:47.073Z'}}]}' contains a field not in 'UpdateSuggestionInput': 'createdOn' @ 'updateSuggestion'"

which makes sense because the documentation makes it seem like the newly named values still mostly function like createdAt and updatedAt.

My question is how can I efficiently update the data I already have in production so that I can make use of the new @model directive?

stayingcool commented 4 years ago

How about this functionality for RDS/Aurora? As of now, I'm creating the table fields manually. It'll be great to have this automatically taken care of by amplify.

DevTGhosh commented 3 years ago

@yuth after updating the model with @model(timestamps: { createdAt: "createdOn", updatedAt: "updatedOn" }) and running amplify codegen models I still can't see the new fields when using it with datastore. I am however able to view the createdOn and updatedOn on app sync console. Any idea on how to solve this?

Backend api Schema

type Clients
  @model(timestamps: { createdAt: "createdOn", updatedAt: "updatedOn" })
  @auth(
    rules: [{ allow: groups, groups: ["companyAdmin"], operations: [create, read, update, delete] }]
  ) {
  id: ID!
  name: String!
  email: String
  tenantId: [ID!]
}

This is what the datastore schema looks after running amplify codegen models

export const schema = {
    "models": {
        "Clients": {
            "name": "Clients",
            "fields": {
                "id": {
                    "name": "id",
                    "isArray": false,
                    "type": "ID",
                    "isRequired": true,
                    "attributes": []
                },
                "name": {
                    "name": "name",
                    "isArray": false,
                    "type": "String",
                    "isRequired": true,
                    "attributes": []
                },
                "email": {
                    "name": "email",
                    "isArray": false,
                    "type": "String",
                    "isRequired": false,
                    "attributes": []
                },
                "tenantId": {
                    "name": "tenantId",
                    "isArray": true,
                    "type": "ID",
                    "isRequired": true,
                    "attributes": [],
                    "isArrayNullable": true
                }
            },
            "syncable": true,
            "pluralName": "Clients",
            "attributes": [
                {
                    "type": "model",
                    "properties": {
                        "timestamps": {
                            "createdAt": "createdOn",
                            "updatedAt": "updatedOn"
                        }
                    }
                },
                {
                    "type": "auth",
                    "properties": {
                        "rules": [
                            {
                                "groupClaim": "cognito:groups",
                                "provider": "userPools",
                                "allow": "groups",
                                "groups": [
                                    "companyAdmin"
                                ],
                                "operations": [
                                    "create",
                                    "read",
                                    "update",
                                    "delete"
                                ]
                            }
                        ]
                    }
                }
            ]
        }
    },
    "enums": {},
    "nonModels": {},
    "version": "af6a903d3f7a75d2403d0fc2dac75a7a"
};
patrickcze commented 3 years ago

@DevTGhosh I am having the exact same issue as well.

yuth commented 3 years ago

A quick workaround is to add these fields to your type and you should have them accessible in data store schema.

DevTGhosh commented 3 years ago

@patrickcze yeah as mentioned above just add the createdOn field to the model

type Post
  @model(timestamps: { createdAt: "createdOn", updatedAt: "updatedOn" }) {
  id: ID!
  title: String!
  tags: [String!]!
  createdOn: AWSDateTime!
  updatedOn: AWSDateTime!
}

@yuth Thanks for helping.

rraczae commented 3 years ago

This does not work with DataStore. Adding the field "manually" gives you access but updatedOn does not change automatically.

patrickcze commented 3 years ago

@yuth @DevTGhosh thanks for the reply, after making the same changes on your side have you seen errors like this?

DataStore - Skipping incoming subscription. Messages: Cannot return null for non-nullable type: 'AWSTimestamp' within parent 'ModelV1' (/onUpdateModelV1/_lastChangedAt)

sherl0cks commented 3 years ago

This does not work with DataStore. Adding the field "manually" gives you access but updatedOn does not change automatically.

@yuth I think this is important feedback. There are a lot of use cases where developers want to be able to query / display createdAt / updatedAt but do not want the responsibility of managing the timestamps themselves. This does not appear possible with the current timestamps field on the @model directive.

kneekey23 commented 3 years ago

FYI @sherl0cks the codegen team is tracking an issue for this bug here as we agree it should be something you can read from on the front end in your generated models.

sherl0cks commented 3 years ago

@kneekey23 much appreciated!

github-actions[bot] commented 3 years ago

This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.

Looking for a help forum? We recommend joining the Amplify Community Discord server *-help channels for those types of questions.