aws-amplify / amplify-cli

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

GraphQL ID field name not present on Update** input #9136

Closed sansavision closed 2 years ago

sansavision commented 2 years ago

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

Authentication, DataStore, Storage

Amplify Categories

auth, storage, api

Environment information

``` System: OS: macOS 12.0.1 CPU: (8) x64 Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz Memory: 52.16 MB / 16.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 14.15.4 - ~/.nvm/versions/node/v14.15.4/bin/node Yarn: 1.22.15 - ~/.yarn/bin/yarn npm: 7.21.0 - ~/.nvm/versions/node/v14.15.4/bin/npm Browsers: Chrome: 96.0.4664.55 Safari: 15.1 npmPackages: @ant-design/pro-card: ^1.18.5 => 1.18.5 @ant-design/pro-form: ^1.49.3 => 1.49.3 @aws-amplify/ui-react: ^2.1.0 => 2.1.1 @aws-amplify/ui-react-internal: undefined () @aws-amplify/ui-react-legacy: undefined () @craco/craco: ^6.4.0 => 6.4.2 @reduxjs/toolkit: ^1.6.2 => 1.6.2 @reduxjs/toolkit-query: 1.0.0 @reduxjs/toolkit-query-react: 1.0.0 @testing-library/jest-dom: ^5.11.4 => 5.15.1 @testing-library/react: ^11.1.0 => 11.2.7 @testing-library/user-event: ^12.1.10 => 12.8.3 @types/chroma-js: ^2.1.3 => 2.1.3 @types/faker: ^5.5.9 => 5.5.9 @types/jest: ^26.0.15 => 26.0.24 (27.0.3) @types/lodash: ^4.14.176 => 4.14.177 @types/node: ^12.0.0 => 12.20.37 (16.11.10) @types/react: ^17.0.0 => 17.0.37 @types/react-dom: ^17.0.0 => 17.0.11 @types/react-router-dom: ^5.3.2 => 5.3.2 @types/styled-components: ^5.1.15 => 5.1.15 antd: ^4.17.1 => 4.17.2 aws-amplify: ^4.3.8 => 4.3.8 aws-amplify-react: 5.1.9 => 5.1.9 chroma-js: ^2.1.2 => 2.1.2 craco-antd: ^1.19.0 => 1.19.0 email-validator: ^2.0.4 => 2.0.4 faker: ^5.5.3 => 5.5.3 firebase: ^9.3.0 => 9.5.0 firebase/analytics: undefined () firebase/app: undefined () firebase/app-check: undefined () firebase/auth: undefined () firebase/auth/cordova: undefined () firebase/auth/react-native: undefined () firebase/compat: undefined () firebase/compat/analytics: undefined () firebase/compat/app: undefined () firebase/compat/app-check: undefined () firebase/compat/auth: undefined () firebase/compat/database: undefined () firebase/compat/firestore: undefined () firebase/compat/functions: undefined () firebase/compat/messaging: undefined () firebase/compat/performance: undefined () firebase/compat/remote-config: undefined () firebase/compat/storage: undefined () firebase/database: undefined () firebase/firestore: undefined () firebase/firestore/lite: undefined () firebase/functions: undefined () firebase/messaging: undefined () firebase/messaging/sw: undefined () firebase/performance: undefined () firebase/remote-config: undefined () firebase/storage: undefined () framer-motion: ^5.3.3 => 5.3.3 immutability-helper: ^3.1.1 => 3.1.1 ini: ^1.3.5 => 1.3.8 inquirer: ^6.5.1 => 6.5.2 lodash: ^4.17.21 => 4.17.21 nanoid: ^3.1.30 => 3.1.30 react: ^17.0.2 => 17.0.2 react-dnd: ^14.0.4 => 14.0.4 react-dnd-html5-backend: ^14.0.2 => 14.0.2 react-dom: ^17.0.2 => 17.0.2 react-icons: ^4.3.1 => 4.3.1 react-phone-number-input: ^3.1.38 => 3.1.41 react-phone-number-input-core: 1.0.0 react-phone-number-input-flags: 1.0.0 react-phone-number-input-input-core: 1.0.0 react-phone-number-input-input-max: 1.0.0 react-phone-number-input-input-min: 1.0.0 react-phone-number-input-input-mobile: 1.0.0 react-phone-number-input-max: 1.0.0 react-phone-number-input-min: 1.0.0 react-phone-number-input-mobile: 1.0.0 react-phone-number-input/react-hook-form: 1.0.0 react-phone-number-input/react-hook-form-core: 1.0.0 react-phone-number-input/react-hook-form-input: 1.0.0 react-phone-number-input/react-hook-form-input-core: 1.0.0 react-phone-number-input/react-native-input: 1.0.0 react-redux: ^7.2.6 => 7.2.6 react-router-dom: 6.0.2 => 6.0.2 react-scripts: 4.0.3 => 4.0.3 redux-persist: ^6.0.0 => 6.0.0 redux-persist/integration/react: undefined () styled-components: ^5.3.3 => 5.3.3 styled-components/macro: undefined () styled-components/native: undefined () styled-components/primitives: undefined () typescript: ^4.5.2 => 4.5.2 web-vitals: ^1.0.1 => 1.1.2 webpack-bundle-analyzer: ^4.5.0 => 4.5.0 webpackbar: ^5.0.0-3 => 5.0.2 npmGlobalPackages: @aws-amplify/cli: 7.5.2 firebase-tools: 9.18.0 ios-deploy: 1.11.4 npm: 7.21.0 yarn: 1.22.10 ```

Describe the bug

We are a small dev team that primarily worked with firebase. Recently we decided to implement a grapql based api and landed on giving amplify a shot.

The problem we faced is one that we have encountered many times in our search through the posted issues, seemingly having many faces but they boil down to local data not being persisted in DynamoDB. likely relating to app sync issues.

In our case what happens is the following, we have a Todo model that we create a new instance of using DataStore.save( new Todo ). This actually gets persisted to DynamoDB. However on further updates using DataStore.save and copyOf, the new data only persist locally which is expected but it never makes it to the DynamoDB, outboxMutationProcessed is never called. So naturally on sign out when we do DataStore.clear and then sign in again we get the data that was first persisted with DataStore.save( new Todo ) and not the updated version.

We have followed the relevant guides and setup the environment according to the docs, all packages are updated. Again the sync element works but only with the DataStore.save( new Todo ), but not when updating.

We have conflict resolution enabled and have tried both Auto Merge and Optimistic Concurrency.

It seems like the _.version, lastChangedAt, .updatedAt never changes and so it never syncs.

We hope you can help us resolve this issue as amplify seems like a wonderful product.

Expected behavior

We expect the data to persist to DynamoDB at some point whenever we make an update to one of the fields using copyOf.

Reproduction steps

  1. create react app.
  2. install amplify cli globally.
  3. install aws-amplify latest
  4. Amplify configure
  5. Amplify init
  6. Amplify add api / select conflict resolution (we tried both, auto merge and optimistic concurrency.)
  7. Create schema - see below
  8. Amplify add auth / configured to use cognito pool
  9. Amplify codgen models , as well as amplify codgen.
  10. Amplify push

Code Snippet


// in app root  we have imported the config
Amplify.configure(config);
Amplify.Logger.LOG_LEVEL = "DEBUG"; 

// A test model schema 
type Todo @model @auth(rules: [{ allow: owner }]) {
  uid: String!
  username: String
}

// To create a new model instance - This persists data to Dynamo DB
await DataStore.save(
      new Todo({
        uid: 'some-internal-id',
        username: 'JohnDoe',
      })
    );

// To Update - This never persists to DynamoDB.
const original = (await DataStore.query(Todo, (c) => c.uid("eq", 'some-internal-id')))[0];
// if we console.log original we get the original data that has username:'JohnDoe'
DataStore.save(
      Todo.copyOf(original, (updated) => {
        updated.username = 'JaneSmith';
      })

// in our indexdb we now have the newly updated Todo but this is not reflected in DynamoDB.
// upon sign out with DataStore.clear this data is lost since it never was persisted to DynamoDB.

Log output

``` // Put your logs below this line // On Create ConsoleLogger.ts:115 [DEBUG] 51:22.21 Util - attempt aws-amplify/amplify-js#1 with this vars: ["Todo","Create","{\"uid\":\"us-west-1:*****-*****-*****-*****-**********\",\"username\":\"test\",\"id\":\"35c537cc-4272-4513-8ec0-f76604b58baa\"}","{}",null,null,{"data":"{\"uid\":\"us-west-1:*****-*****-*****-*****-**********\",\"username\":\"test\",\"id\":\"35c537cc-4272-4513-8ec0-f76604b58baa\"}","modelId":"35c537cc-4272-4513-8ec0-f76604b58baa","model":"Todo","operation":"Create","condition":"{}","id":"01FNGY1GJ1W341VYR8RA5PEZ01"}] ConsoleLogger.ts:115 [DEBUG] 51:22.23 AuthClass - Getting current session ConsoleLogger.ts:125 [DEBUG] 51:22.24 AuthClass - Getting the session from this user: CognitoUser {username: '*****-*****-*****-*****-**********', pool: CognitoUserPool, Session: null, client: Client, signInUserSession: CognitoUserSession, …} ConsoleLogger.ts:125 [DEBUG] 51:22.24 AuthClass - Succeed to get the user session CognitoUserSession {idToken: CognitoIdToken, refreshToken: CognitoRefreshToken, accessToken: CognitoAccessToken, clockDrift: -1} ConsoleLogger.ts:125 [DEBUG] 51:22.25 RestClient - POST https://***************.appsync-api.us-west-1.amazonaws.com/graphql ConsoleLogger.ts:115 [DEBUG] 51:22.656 DataStore - Mutation sent successfully with authMode: AMAZON_COGNITO_USER_POOLS ConsoleLogger.ts:125 [DEBUG] 51:22.671 Hub - Dispatching to datastore with {event: 'outboxMutationProcessed', data: {…}} ConsoleLogger.ts:125 [DEBUG] 51:22.673 Hub - Dispatching to datastore with {event: 'outboxStatus', data: {…}} ConsoleLogger.ts:115 [DEBUG] 51:23.20 AWSAppSyncRealTimeProvider - subscription message from AWS AppSync RealTime: {"id":"58661be1-5217-4722-a6b2-b272c9d31221","type":"data","payload":{"data":{"onCreateTodo":{"id":"35c537cc-4272-4513-8ec0-f76604b58baa","uid":"us-west-1:*****-*****-*****-*****-**********","username":"test","createdAt":"2021-11-27T14:51:22.689Z","updatedAt":"2021-11-27T14:51:22.689Z","owner":"*****-*****-*****-*****-**********","\_version":1,"\_lastChangedAt":1638024682719,"\_deleted":null}}}} ConsoleLogger.ts:118 [DEBUG] 51:23.21 AWSAppSyncRealTimeProvider {id: '58661be1-5217-4722-a6b2-b272c9d31221', observer: SubscriptionObserver, query: 'subscription operation($owner: String!) {\n onCrea… \_version\n \_lastChangedAt\n \_deleted\n }\n}\n', variables: {…}} ​ // On Update UserDetails.tsx:105 original------> Model {id: '35c537cc-4272-4513-8ec0-f76604b58baa', uid: 'us-west-1:*****-*****-*****-*****-**********', username: 'test', createdAt: '2021-11-27T14:51:22.689Z', updatedAt: '2021-11-27T14:51:22.689Z', …} ConsoleLogger.ts:125 [DEBUG] 55:00.765 Hub - Dispatching to datastore with {event: 'outboxMutationEnqueued', data: {…}} ConsoleLogger.ts:125 [DEBUG] 55:00.767 Hub - Dispatching to datastore with {event: 'outboxStatus', data: {…}} UserDetails.tsx:112 res Model {id: '35c537cc-4272-4513-8ec0-f76604b58baa', uid: 'us-west-1:*****-*****-*****-*****-**********', username: 'Sansa20k', createdAt: '2021-11-27T14:51:22.689Z', updatedAt: '2021-11-27T14:51:22.689Z', …} ConsoleLogger.ts:115 [DEBUG] 55:00.836 DataStore - Attempting mutation with authMode: AMAZON_COGNITO_USER_POOLS ConsoleLogger.ts:115 [DEBUG] 55:00.836 Util - attempt aws-amplify/amplify-js#1 with this vars: ["Todo","Update","{\"username\":\"Sansa20k\",\"id\":\"35c537cc-4272-4513-8ec0-f76604b58baa\",\"_version\":1,\"_lastChangedAt\":1638024682719,\"_deleted\":null}","{}",null,null,{"data":"{\"username\":\"Sansa20k\",\"id\":\"35c537cc-4272-4513-8ec0-f76604b58baa\",\"_version\":1,\"_lastChangedAt\":1638024682719,\"_deleted\":null}","modelId":"35c537cc-4272-4513-8ec0-f76604b58baa","model":"Todo","operation":"Update","condition":"{}","id":"01FNGY85FQS9HZC7F0TYVAVCF4"}] ConsoleLogger.ts:115 [DEBUG] 55:00.839 AuthClass - Getting current session ConsoleLogger.ts:125 [DEBUG] 55:00.840 AuthClass - Getting the session from this user: CognitoUser {username: '*****-*****-*****-*****-**********', pool: CognitoUserPool, Session: null, client: Client, signInUserSession: CognitoUserSession, …} ConsoleLogger.ts:125 [DEBUG] 55:00.841 AuthClass - Succeed to get the user session CognitoUserSession {idToken: CognitoIdToken, refreshToken: CognitoRefreshToken, accessToken: CognitoAccessToken, clockDrift: 0} ConsoleLogger.ts:125 [DEBUG] 55:00.843 RestClient - POST https://***************.appsync-api.us-west-1.amazonaws.com/graphql ConsoleLogger.ts:115 [DEBUG] 55:01.507 DataStore - Mutation sent successfully with authMode: AMAZON_COGNITO_USER_POOLS ConsoleLogger.ts:115 [DEBUG] 55:01.507 DataStore - done retrying ```

aws-exports.js

const awsmobile = {
    "aws_project_region": "us-west-1",
    "aws_appsync_graphqlEndpoint": "https://***************.appsync-api.us-west-1.amazonaws.com/graphql",
    "aws_appsync_region": "us-west-1",
    "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
    "aws_cognito_identity_pool_id": "us-west-1:*****-*****-*****-*****-**********",
    "aws_cognito_region": "us-west-1",
    "aws_user_pools_id": "us-west-1_*****",
    "aws_user_pools_web_client_id": "********************",
    "oauth": {},
    "aws_cognito_username_attributes": [
        "EMAIL"
    ],
    "aws_cognito_social_providers": [],
    "aws_cognito_signup_attributes": [
        "EMAIL"
    ],
    "aws_cognito_mfa_configuration": "OFF",
    "aws_cognito_mfa_types": [
        "SMS"
    ],
    "aws_cognito_password_protection_settings": {
        "passwordPolicyMinLength": 8,
        "passwordPolicyCharacters": []
    },
    "aws_cognito_verification_mechanisms": [
        "EMAIL"
    ],
    "aws_user_files_s3_bucket": "sansa-user-storage154622-dev",
    "aws_user_files_s3_bucket_region": "us-west-1"
};

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

josefaidt commented 2 years ago

Hey @sansavision :wave: thanks for raising this! Using the steps and sample code snippets provided I was able to reproduce this issue and noticed the built UpdateTodoInput does not include an id property, leading to a warning from DataStore

# build/schema.graphql
input UpdateTodoInput {
    uid: String
    username: String
    _version: Int
}

without it we receive the following warning when attempting to update the resource. Note we will see the update locally but will receive an error from the GraphQL POST call image

As a workaround, by manually adding an ID property to the model we can then push the schema and successfully update records which reflect in the AppSync console:

  1. update the GraphQL schema

    type Todo @model @auth(rules: [{ allow: owner }]) {
    + id: ID!
      uid: String!
      username: String
    }
  2. push the changes with amplify push -y

  3. start the frontend dev server (Next.js in this example): yarn next

  4. save record with JohnDoe as the username image image

  5. update record's username to JaneSmith image image

sample Next.js home page ```js // src/pages/index.js import { useState, useEffect } from 'react' import { DataStore } from 'aws-amplify' import { Todo } from '../models/index' export default function HomePage(props) { const [todos, setTodos] = useState([]) async function list() { try { const todos = await DataStore.query(Todo) console.log({ todos }) setTodos(todos) } catch (error) { console.error('Unable to get todos') } } async function reload() { return await list() } async function save() { await DataStore.save( new Todo({ uid: 'some-internal-id', username: 'JohnDoe', }) ) await reload() } async function update() { // To Update - This never persists to DynamoDB. const original = ( await DataStore.query(Todo, c => c.username('eq', 'JohnDoe')) )[0] try { await DataStore.save( Todo.copyOf(original, updated => { updated.username = 'JaneSmith' }) ) } catch (error) { console.warn('Unable to save copy of Todo') } await reload() } useEffect(() => { ;(async () => { await DataStore.clear() await reload() })() }, []) return (

hello from the homepage

{todos?.length ? todos.map(todo => (
              {JSON.stringify(todo, null, 2)}
            
)) : null}
) } ```

Marking as a bug 🙂

sansavision commented 2 years ago

@josefaidt thank you

github-actions[bot] commented 2 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.