aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.42k stars 2.12k forks source link

DataStore.save only registering the first call #13686

Closed Gaztoof closed 1 month ago

Gaztoof commented 1 month ago

Before opening, please confirm:

JavaScript Framework

React Native

Amplify APIs

DataStore

Amplify Version

v6

Amplify Categories

api

Backend

Amplify CLI

Environment information

``` # Put output below this line System: OS: Windows 10 10.0.19045 CPU: (8) x64 Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz Memory: 26.28 GB / 47.94 GB Binaries: Node: 20.15.1 - C:\Program Files\nodejs\node.EXE Yarn: 1.22.22 - ~\AppData\Roaming\npm\yarn.CMD npm: 10.7.0 - C:\Program Files\nodejs\npm.CMD Browsers: Edge: Spartan (44.19041.3636.0), Chromium (127.0.2651.74) Internet Explorer: 11.0.19041.3636 npmPackages: %name%: 0.1.0 @aws-amplify/backend: ^1.0.4 => 1.0.4 @aws-amplify/backend-cli: ^1.2.1 => 1.2.1 @aws-amplify/react-native: ^1.1.4 => 1.1.4 @aws-amplify/ui-react: ^6.1.14 => 6.1.14 @aws-amplify/ui-react-internal: undefined () @aws-amplify/ui-react-native: ^2.2.3 => 2.2.3 @babel/core: ^7.20.0 => 7.24.9 @expo/vector-icons: ^14.0.2 => 14.0.2 @react-native-async-storage/async-storage: ^1.24.0 => 1.24.0 @react-native-community/datetimepicker: git+https://github.com/react-native-datetimepicker/datetimepicker.git => 8.2.0 @react-native-community/netinfo: ^11.3.2 => 11.3.2 @react-navigation/material-top-tabs: ^6.6.14 => 6.6.14 @react-navigation/native: ^6.1.18 => 6.1.18 @react-navigation/stack: ^6.4.1 => 6.4.1 @types/jest: ^29.5.12 => 29.5.12 @types/react: ~18.2.45 => 18.2.79 @types/react-test-renderer: ^18.0.7 => 18.3.0 HelloWorld: 0.0.1 aws-amplify: ^6.4.3 => 6.4.3 aws-amplify-react-native: ^7.0.8 => 7.0.8 aws-amplify/adapter-core: undefined () aws-amplify/analytics: undefined () aws-amplify/analytics/kinesis: undefined () aws-amplify/analytics/kinesis-firehose: undefined () aws-amplify/analytics/personalize: undefined () aws-amplify/analytics/pinpoint: undefined () aws-amplify/api: undefined () aws-amplify/api/server: undefined () aws-amplify/auth: undefined () aws-amplify/auth/cognito: undefined () aws-amplify/auth/cognito/server: undefined () aws-amplify/auth/enable-oauth-listener: undefined () aws-amplify/auth/server: undefined () aws-amplify/data: undefined () aws-amplify/data/server: undefined () aws-amplify/datastore: undefined () aws-amplify/in-app-messaging: undefined () aws-amplify/in-app-messaging/pinpoint: undefined () aws-amplify/push-notifications: undefined () aws-amplify/push-notifications/pinpoint: undefined () aws-amplify/storage: undefined () aws-amplify/storage/s3: undefined () aws-amplify/storage/s3/server: undefined () aws-amplify/storage/server: undefined () aws-amplify/utils: undefined () aws-cdk: ^2.150.0 => 2.150.0 aws-cdk-lib: ^2.150.0 => 2.150.0 constructs: ^10.3.0 => 10.3.0 esbuild: ^0.23.0 => 0.23.0 (0.21.5) expo: ^51.0.21 => 51.0.21 expo-constants: ~16.0.2 => 16.0.2 expo-font: ~12.0.9 => 12.0.9 expo-image-picker: ~15.0.7 => 15.0.7 expo-linear-gradient: ~13.0.2 => 13.0.2 expo-linking: ~6.3.1 => 6.3.1 expo-router: ~3.5.18 => 3.5.18 expo-splash-screen: ~0.27.5 => 0.27.5 expo-status-bar: ~1.12.1 => 1.12.1 expo-system-ui: ~3.0.7 => 3.0.7 expo-web-browser: ~13.0.3 => 13.0.3 jest: ^29.2.1 => 29.7.0 jest-expo: ~51.0.3 => 51.0.3 react: 18.2.0 => 18.2.0 react-dom: 18.2.0 => 18.2.0 react-native: 0.74.3 => 0.74.3 react-native-country-picker-modal: ^2.0.0 => 2.0.0 react-native-elements: ^3.4.3 => 3.4.3 react-native-gesture-handler: ~2.16.1 => 2.16.2 react-native-get-random-values: ^1.11.0 => 1.11.0 react-native-google-address-validation: ^1.2.7 => 1.2.7 react-native-iconly: ^1.0.12 => 1.0.12 react-native-linear-gradient: ^2.8.3 => 2.8.3 react-native-modal-datetime-picker: ^17.1.0 => 17.1.0 react-native-pager-view: 6.3.0 => 6.3.0 react-native-reanimated: ~3.10.1 => 3.10.1 react-native-safe-area-context: ^4.10.8 => 4.10.8 react-native-screens: 3.31.1 => 3.31.1 react-native-svg: ^15.4.0 => 15.4.0 (12.5.1) react-native-tab-view: ^3.5.2 => 3.5.2 react-native-tag-input: ^0.0.21 => 0.0.21 react-native-tags-input: ^1.0.10 => 1.0.10 react-native-url-polyfill: ^2.0.0 => 2.0.0 react-native-vector-icons: ^10.1.0 => 10.1.0 react-native-web: ~0.19.10 => 0.19.12 react-navigation-stack: ^2.10.4 => 2.10.4 react-phone-number-input: ^3.4.5 => 3.4.5 react-phone-number-input/commonjs: undefined () react-phone-number-input/core: undefined () react-phone-number-input/flags: undefined () react-phone-number-input/input-core: undefined () react-phone-number-input/input-max: undefined () react-phone-number-input/input-min: undefined () react-phone-number-input/input-mobile: undefined () react-phone-number-input/max: undefined () react-phone-number-input/min: undefined () react-phone-number-input/mobile: undefined () react-phone-number-input/react-hook-form: undefined () react-phone-number-input/react-hook-form-core: undefined () react-phone-number-input/react-hook-form-input: undefined () react-phone-number-input/react-hook-form-input-core: undefined () react-phone-number-input/react-native-input: undefined () react-phone-number-input/react-styleguidist: undefined () react-test-renderer: 18.2.0 => 18.2.0 tsx: ^4.16.3 => 4.16.3 typescript: ^5.5.4 => 5.5.4 (4.4.4, 4.9.5) npmGlobalPackages: @aws-amplify/cli: 12.12.4 @aws-amplify/ui-react-native: 2.2.3 aws-amplify: 6.4.3 eas-cli: 10.2.2 react-native-cli: 2.0.1 yarn: 1.22.22 ```

Describe the bug

When calling DataStore.Save more than once in a single function, only the first DataStore.Save actually works. If you repeat the a DataStore.Save call 3 times, only the first will be registered.

Expected behavior

I expect all of the DataStore.Save calls to work

Reproduction steps

  1. Call the createProfile function
  2. Go to DynamoDB Console
  3. Only a ConsultantProfile is created
  4. Modify the createProfile function and comment out the ConsultantProfile DataStore.save part
  5. Go to DynamoDB Console
  6. Only a UserProfile is created
  7. Modify the code and just repeat the line of the first DataStore.save 3 times and remove the UserProfile DataStore.save
  8. Only a single ConsultantProfile is created

Code Snippet

// Put your code below this line.

import { DataStore } from 'aws-amplify/datastore';
import { UserProfile, ConsultantProfile, ProfileType } from '@/src/models';

const createProfile = async (profile) => {
      const newProfile = {
        firstName: profile.firstName,
        lastName: profile.lastName,
        profilePicture: profile.profilePicture,
        birthdate: profile.birthdate,
        phoneNumber: profile.phoneNumber,
        streetAddress: profile.address,
        city: profile.city,
        state: profile.state,
        postCode: profile.zipCode,
        country: profile.country,
        profileType: ProfileType[profile.profileType as keyof typeof ProfileType],
      };
      if (profile.profileType === 'CONSULTANT') {
              const newConsultantProfile = await DataStore.save(new ConsultantProfile({ streetAddress: profile.address, city: profile.city, state: profile.state, postCode: profile.zipCode, country: profile.country, jobTitle: profile.jobTitle }));
        console.log(newConsultantProfile);
        newProfile.consultantProfile = newConsultantProfile;
      }

      console.log('newProfile: ', await DataStore.save(new UserProfile(newProfile)));
      // Ive removed parts of the code that are non useful in our context
}
enum ProfileType {
  CONSULTANT
  CONSULTANTCOMPANY
  HR
}

type UserProfile @model @auth(rules: [{ allow: private, operations: [read] }, { allow: owner, ownerField: "owner" }]) {
  id: ID!
  owner: String @auth(rules: [{ allow: owner, operations: [read, delete] }])

  firstName: String!
  lastName: String!
  profilePicture: String
  birthdate: AWSDate!
  phoneNumber: String
  streetAddress: String
  city: String
  state: String
  postCode: String
  country: String
  profileType: ProfileType!
  consultantProfile: ConsultantProfile @hasOne
}

type ConsultantProfile @model @auth(rules: [{ allow: private, operations: [read] }, { allow: owner, ownerField: "owner" }]) {
  id: ID!
  owner: String @auth(rules: [{ allow: owner, operations: [read, delete] }])
  tvaNumber: String
  streetAddress: String
  city: String
  state: String
  postCode: String
  country: String

  bio: String

  tags: [String]
  pictures: [String]
  price: String
  jobTitle: String
  spokenLanguages: [String]
}

Log output

``` // Put your logs below this line LOG {"_deleted": undefined, "_lastChangedAt": undefined, "_version": undefined, "bio": null, "city": "MyCity", "consultantCompanyProfileConsultantsId": null, "country": "AG", "createdAt": null, "education": null, "experiences": null, "id": "16bebfc9-d135-4c83-975a-09068b053246", "jobTitle": "UnemplolyedEED", "licensesCertifications": null, "owner": null, "pictures": null, "postCode": "100000", "preferWorkFrom": null, "price": null, "spokenLanguages": null, "state": "MySTATE", "streetAddress": "MyADDR", "tags": null, "tvaNumber": null, "updatedAt": null} LOG newProfile: {"_deleted": undefined, "_lastChangedAt": undefined, "_version": undefined, "birthdate": "1950-08-07", "city": "MyCity", "country": "AG", "createdAt": null, "firstName": "TEST", "id": "8103cfc6-e50d-4083-9baf-ac28c496cc86", "lastName": "TEST", "owner": null, "phoneNumber": "+32485000000", "postCode": "100000", "profilePicture": "URL TO PROFILE PICTURE", "profileType": "CONSULTANT", "state": "MySTATE", "streetAddress": "MyADDR", "updatedAt": null, "userProfileConsultantCompanyProfileId": null, "userProfileConsultantProfileId": "16bebfc9-d135-4c83-975a-09068b053246", "userProfileHrProfileId": null} ```

aws-exports.js

const awsmobile = {
    "aws_project_region": "eu-north-1",
    "aws_appsync_graphqlEndpoint": "https://xxxxxxx.appsync-api.eu-north-1.amazonaws.com/graphql",
    "aws_appsync_region": "eu-north-1",
    "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
    "aws_cognito_identity_pool_id": "eu-north-1:xxxxxxx-xxxxxxx-xxxxxxx-xxxxxxx-xxxxxxx",
    "aws_cognito_region": "eu-north-1",
    "aws_user_pools_id": "eu-north-1_xxxxxxx",
    "aws_user_pools_web_client_id": "xxxxxxxxxxx",
    "oauth": {},
    "aws_cognito_username_attributes": [
        "EMAIL",
        "PHONE_NUMBER"
    ],
    "aws_cognito_social_providers": [],
    "aws_cognito_signup_attributes": [
        "EMAIL",
        "FAMILY_NAME",
        "GIVEN_NAME",
        "PHONE_NUMBER"
    ],
    "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": "xxxxxxx-dev",
    "aws_user_files_s3_bucket_region": "eu-north-1"
};

export default awsmobile;

Manual configuration

No response

Additional configuration

No response

Mobile Device

Pixel 8

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

{ "name": "consultu", "main": "expo-router/entry", "version": "1.0.0", "scripts": { "start": "expo start", "reset-project": "node ./scripts/reset-project.js", "android": "expo run:android", "ios": "expo run:ios", "web": "expo start --web", "test": "jest --watchAll", "lint": "expo lint" }, "jest": { "preset": "jest-expo" }, "dependencies": { "@aws-amplify/react-native": "^1.1.4", "@aws-amplify/ui-react": "^6.1.14", "@aws-amplify/ui-react-native": "^2.2.3", "@expo/vector-icons": "^14.0.2", "@react-native-async-storage/async-storage": "^1.24.0", "@react-native-community/datetimepicker": "git+https://github.com/react-native-datetimepicker/datetimepicker.git", "@react-native-community/netinfo": "^11.3.2", "@react-navigation/material-top-tabs": "^6.6.14", "@react-navigation/native": "^6.1.18", "@react-navigation/stack": "^6.4.1", "aws-amplify": "^6.4.3", "aws-amplify-react-native": "^7.0.8", "expo": "^51.0.21", "expo-constants": "~16.0.2", "expo-font": "~12.0.9", "expo-image-picker": "~15.0.7", "expo-linear-gradient": "~13.0.2", "expo-linking": "~6.3.1", "expo-router": "~3.5.18", "expo-splash-screen": "~0.27.5", "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.7", "expo-web-browser": "~13.0.3", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.3", "react-native-country-picker-modal": "^2.0.0", "react-native-elements": "^3.4.3", "react-native-gesture-handler": "~2.16.1", "react-native-get-random-values": "^1.11.0", "react-native-google-address-validation": "^1.2.7", "react-native-iconly": "^1.0.12", "react-native-linear-gradient": "^2.8.3", "react-native-modal-datetime-picker": "^17.1.0", "react-native-pager-view": "6.3.0", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "^4.10.8", "react-native-screens": "3.31.1", "react-native-svg": "^15.4.0", "react-native-tab-view": "^3.5.2", "react-native-tag-input": "^0.0.21", "react-native-tags-input": "^1.0.10", "react-native-url-polyfill": "^2.0.0", "react-native-vector-icons": "^10.1.0", "react-native-web": "~0.19.10", "react-navigation-stack": "^2.10.4", "react-phone-number-input": "^3.4.5" }, "devDependencies": { "@aws-amplify/backend": "^1.0.4", "@aws-amplify/backend-cli": "^1.2.1", "@babel/core": "^7.20.0", "@types/jest": "^29.5.12", "@types/react": "~18.2.45", "@types/react-test-renderer": "^18.0.7", "aws-cdk": "^2.150.0", "aws-cdk-lib": "^2.150.0", "constructs": "^10.3.0", "esbuild": "^0.23.0", "jest": "^29.2.1", "jest-expo": "~51.0.3", "react-test-renderer": "18.2.0", "tsx": "^4.16.3", "typescript": "^5.5.4" }, "private": true }

Gaztoof commented 1 month ago

EDIT: It turns out Amplify isn't actually simply registering the first call. But I noticed the actual problem is that DataStore is so slow that it appears like that... After a few minutes and restarting the app, everything syncs... But, when calling functions like DataStore.save, it literally takes a whole minute to update a very simple element...

Gaztoof commented 1 month ago

EDIT: Problem solved, turns out DataStore.query was being called repeatedly in a useEffect that was wrongly configurated. So it was getting spammed / overloaded and that's why it was so unresponsive! Here's the problematic code snippet: ` useEffect(() => { const checkUser = async () => { try { const currUser = await getCurrentUser(); const profiles = await DataStore.query(UserProfile, (profile) => profile.owner.eq(currUser.userId));

            if(profiles.length > 0)
            {
                switch(profiles[0].profileType)
                {
                    case 'CONSULTANT':
                        const consultantProfiles = await DataStore.query(ConsultantProfile, (profile) => profile.owner.eq(currUser.userId));
                        setConsultantProfile(consultantProfiles.length > 0 ? consultantProfiles[0] : null);
                        break;
                    case 'CONSULTANTCOMPANY':
                        const companyProfiles = await DataStore.query(ConsultantCompanyProfile, (profile) => profile.owner.eq(currUser.userId));
                        setCompanyProfile(companyProfiles.length > 0 ? companyProfiles[0] : null);    
                        break;
                    case 'HR':
                        const hrProfiles = await DataStore.query(HRProfile, (profile) => profile.owner.eq(currUser.userId));
                        setCompanyProfile(hrProfiles.length > 0 ? hrProfiles[0] : null);    
                        break;
                }
            }
            setProfile(profiles.length > 0 ? profiles[0] : null);

        } catch (e) {
            console.log(e);
            setProfile(null);
        }
    }
    checkUser();
});`

The fix was simply to add a ", []" as a parameter in the useEffect, so, going from: useEffect(() => { ...}); To useEffect(() => { ...}, []);

chrisbonifacio commented 1 month ago

Classic React 😅 Thanks for the updates, @Gaztoof.

Glad you're unblocked! 🎉