amplify-js
amplify-js copied to clipboard
[Datastore] saving with undefined relationships throws `Field: id is required` error
Before opening, please confirm:
- [X] I have searched for duplicate or closed issues and discussions.
- [X] I have read the guide for submitting bug reports.
- [X] I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
JavaScript Framework
React
Amplify APIs
DataStore
Amplify Categories
storage, api
Environment information
System:
OS: macOS 12.6.2
CPU: (4) x64 Intel(R) Core(TM) i5-6267U CPU @ 2.90GHz
Memory: 210.45 MB / 8.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 18.16.0 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 9.6.7 - ~/Documents/Github/Agroview2.0/node_modules/.bin/npm
Browsers:
Chrome: 114.0.5735.106
Safari: 16.2
npmPackages:
@fortawesome/fontawesome-svg-core: ^6.4.0 => 6.4.0
@fortawesome/free-brands-svg-icons: ^6.4.0 => 6.4.0
@fortawesome/free-regular-svg-icons: ^6.3.0 => 6.3.0
@fortawesome/free-solid-svg-icons: ^6.4.0 => 6.4.0
@fortawesome/react-fontawesome: ^0.2.0 => 0.2.0
@reduxjs/toolkit: ^1.9.5 => 1.9.5
@reduxjs/toolkit-query: 1.0.0
@reduxjs/toolkit-query-react: 1.0.0
@types/compression: ^1.7.2 => 1.7.2
@types/express: ^4.17.14 => 4.17.17
@types/leaflet: ^1.9.3 => 1.9.3
@types/node: ^20.2.5 => 20.2.5
@types/react: ^18.0.28 => 18.2.8
@types/react-dom: ^18.0.3 => 18.0.11
@types/react-slider: ^1.3.1 => 1.3.1
@typescript-eslint/eslint-plugin: ^5.59.9 => 5.59.9
@typescript-eslint/parser: ^5.59.9 => 5.59.9
@vitejs/plugin-react: ^3.0.0 => 3.1.0
@vitejs/plugin-react-swc: ^3.0.0 => 3.3.2
amazon-cognito-identity-js: ^6.2.0 => 6.2.0
autoprefixer: ^10.4.14 => 10.4.14
aws-amplify: ^5.0.23 => 5.0.23
compression: ^1.7.4 => 1.7.4
concurrently: ^7.6.0 => 7.6.0
cross-env: ^7.0.3 => 7.0.3
debounced-drag: 1.0.0
eslint: ^8.38.0 => 8.41.0
eslint-plugin-react-hooks: ^4.6.0 => 4.6.0
eslint-plugin-react-refresh: ^0.3.4 => 0.3.5
express: ^4.18.1 => 4.18.2
i: ^0.3.7 => 0.3.7
leaflet: ^1.9.4 => 1.9.4
leaflet-draw: ^1.0.4 => 1.0.4
npm: ^9.6.7 => 9.6.7
postcss: ^8.4.21 => 8.4.23
react: ^18.2.0 => 18.2.0
react-dom: ^18.2.0 => 18.2.0
react-leaflet: ^4.2.1 => 4.2.1
react-leaflet-draw: ^0.20.4 => 0.20.4
react-redux: ^8.0.7 => 8.0.7
react-slider: ^2.0.4 => 2.0.4
react-tabs: ^6.0.1 => 6.0.1
recharts: ^2.5.0 => 2.6.2
redux: ^4.2.1 => 4.2.1
redux-todos-with-undo-example: 0.0.0
redux-undo: ^1.0.1 => 1.0.1
sirv: ^2.0.2 => 2.0.2
tailwindcss: ^3.3.2 => 3.3.2
ts-node: ^10.9.1 => 10.9.1
typescript: ^5.1.3 => 5.1.3
vite: ^4.0.3 => 4.3.9
npmGlobalPackages:
@aws-amplify/cli: 10.2.1
corepack: 0.17.0
gh-pages: 3.2.3
n: 9.1.0
nodemon: 2.0.13
npm: 9.5.1
stable: 0.1.8
tls-test: 1.0.0
ts-node: 10.9.1
Describe the bug
Related to #11271 (comment) Using DataStore.save() on a newly instantiated model throws:
Error: Field id is required
at datastore.ts:580:11
at datastore.ts:891:7
at Array.forEach (<anonymous>)
at datastore.ts:888:28
at produce (immerClass.ts:94:14)
at Model.copyOf (datastore.ts:870:18)
at traverseModel (util.ts:220:39)
at StorageAdapterBase2.saveMetadata (StorageAdapterBase.ts:186:27)
at IndexedDBAdapter2.<anonymous> (IndexedDBAdapter.ts:218:9)
at step (tslib.es6.js:147:23)
Expected behavior
I expect the model to save error-free similarly to how all the other models save. Unsure why this model is now throwing problems while the others are not.
Reproduction steps
- Install Amplify Libraries
- Create Upload type (see below)
- Save this to the data model.
Code Snippet
With this model
type Upload @model @auth(rules: [{ allow: public }]) {
id: ID!
datetime: AWSDateTime!
name: String!
status: Int!
type: [Int!]!
uploadCount: Int!
processCount: Int!
Collection: [Collection] @hasMany(indexName: "byUpload", fields: ["id"])
userID: ID! @index(name: "byUser")
User: User! @belongsTo(fields: ["userID"])
RawImages: [RawImage] @hasMany(indexName: "byUpload", fields: ["id"])
}
& this utility function
async function createUpload({
name,
status,
type,
uploadCount,
userID,
User,
RawImages,
Collection,
}: {
name: string;
status: number;
type: number[];
uploadCount: number;
userID: string;
User: User;
RawImages?: RawImage[];
Collection?: Collection[];
}) {
try {
const newUpload = new Upload({
datetime: new Date().toISOString(),
name: name,
status: status,
type: type,
uploadCount: uploadCount,
processCount: 0,
userID: userID,
User: User,
RawImages: RawImages,
Collection: Collection,
});
return await saveModelAPI(newUpload);
} catch (error) {
throw error as Error;
}
}
& this implementation
const name = "unnamed";
const files.length = 0;
upload = await createUpload({
name: name,
status: 0,
type: [0],
uploadCount: files.length,
userID: user.id,
User: user,
});
Log output
Console logged the model type prior to saving it:
{
"datetime": "2023-06-15T18:42:41.515Z",
"name": "asdfg",
"status": 0,
"type": [
0
],
"uploadCount": 12,
"processCount": 0,
"userID": "f70dad58-14ce-4485-9d0e-c790ed72fda8",
"createdAt": null,
"updatedAt": null
}
I have verified that the user exists by searching the userID in Amplify Studio.
aws-exports.js
// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.
const awsmobile = {
"aws_project_region": "us-east-2",
"aws_appsync_graphqlEndpoint": "https://whw7hqknjzd6hhqdf56nex3lv4.appsync-api.us-east-2.amazonaws.com/graphql",
"aws_appsync_region": "us-east-2",
"aws_appsync_authenticationType": "API_KEY",
"aws_appsync_apiKey": "<KEY>",
"aws_cognito_identity_pool_id": "<ID>",
"aws_cognito_region": "us-east-2",
"aws_user_pools_id": "<ID>",
"aws_user_pools_web_client_id": "<ID>",
"oauth": {
"domain": "auth-agroview-staging.auth.us-east-2.amazoncognito.com",
"scope": [
"phone",
"email",
"openid",
"profile",
"aws.cognito.signin.user.admin"
],
"redirectSignIn": "https://test.agroview.farm/,https://www.test.agroview.farm/,http://localhost:3000/",
"redirectSignOut": "https://test.agroview.farm/,https://www.test.agroview.farm/,http://localhost:3000/",
"responseType": "code"
},
"federationTarget": "COGNITO_USER_POOLS",
"aws_cognito_username_attributes": [
"EMAIL"
],
"aws_cognito_social_providers": [
"GOOGLE",
"APPLE"
],
"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": "agroview320d41778bdb4fd3a73392917fe8f514195551-staging",
"aws_user_files_s3_bucket_region": "us-east-2"
};
export default awsmobile;
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
@charlieforward9 - I have a few follow-up questions:
- What version of the CLI are you using?
- What conflict resolution strategy are you using?
- Can you share your full schema?
- Can you share the full code snippet for
saveModelAPI? - Can you confirm that you are using DataStore and not the API category? You mention DataStore in the ticket title, but API under
Amplify Categories.
Thank you!
@charlieforward9 - to follow up on this, I attempted to reproduce the issue with the following schema + sample code, and did not encounter the error:
schema.graphql
input AMPLIFY {
globalAuthRule: AuthRule = { allow: public }
}
type Upload @model {
id: ID!
datetime: AWSDateTime!
name: String!
status: Int!
type: [Int!]!
uploadCount: Int!
processCount: Int!
userID: ID! @index(name: "byUser")
}
App.tsx
import { useState } from "react";
import { DataStore, Predicates } from "@aws-amplify/datastore";
import { Upload } from "./models";
function App() {
const [upload, setUpload] = useState<Upload | null>(null);
async function saveModelAPI(model: Upload) {
try {
const savedModel = await DataStore.save(model);
console.log("savedModel: ", savedModel);
setUpload(savedModel);
return savedModel;
} catch (error) {
console.error("saveModelAPI error: ", error);
}
}
async function createUpload({
name,
status,
type,
uploadCount,
userID,
}: {
name: string;
status: number;
type: number[];
uploadCount: number;
userID: string;
}) {
try {
const newUpload = new Upload({
datetime: new Date().toISOString(),
name: name,
status: status,
type: type,
uploadCount: uploadCount,
processCount: 0,
userID: userID,
});
console.log("newUpload: ", newUpload);
return await saveModelAPI(newUpload);
} catch (error) {
throw error as Error;
}
}
return (
<div className="App">
<header className="App-header">
<button
onClick={() =>
createUpload({
name: `Upload ${new Date().toISOString()}`,
status: 1,
type: [1, 2],
uploadCount: 1,
userID: `${Date.now()}`,
})
}
>
Create Upload
</button>
<pre>Current Upload: {JSON.stringify(upload, null, 2)}</pre>
</header>
</div>
);
}
export default App;
@david-mcafee Thank you for the elaborate set of questions and attempting to reproduce my issue. I have been messing around with the problem and seem to have narrowed the issue at hand. It stems from the relationships...
When I call createUpload(...) without the // <-- Problematic lines, the model is successfully saved. However, when I add in the optional relationships, the error is thrown:
async function createUpload({
name,
status,
type,
uploadCount,
userID,
User,
RawImages,
Collection,
}: {
name: string;
status: number;
type: number[];
uploadCount: number;
userID: string;
User: User;
RawImages?: RawImage[];
Collection?: Collection[];
}): Promise<Upload> {
const upload = new Upload({
datetime: new Date().toISOString(),
name: name,
status: status,
type: type,
uploadCount: uploadCount,
processCount: 0,
userID: userID,
User: User,
RawImages: RawImages ? RawImages : undefined, // <--Problematic
Collection: Collection ? Collection : undefined, // <--Problematic
});
return await dataStoreService.saveModelAPI(upload);
}
Even if I explicitly set RawImages: undefined & Collection: undefined, it throws. This seems to be a bug, since the data model allows the relationship to be undefined, so it should not matter how I am setting the relationships, so long as the type conforms...
With this now known, I do not think it is necessary to provide the ALL requested info. I'll answer the ones that seem important to move forward:
What version of the CLI are you using?
amplify -v: 11.0.3
What conflict resolution strategy are you using?
Auto Merge
Can you confirm that you are using DataStore and not the API category?
Yes, saving the models with DataStore.save(...)
Just unassigned this from me so that this can be picked up for reproduction now that we have additional repro steps (cc @chrisbonifacio)