From 04a0e21778e3e0a104ef3be70a141a6a6bf17e14 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 19 May 2023 09:57:19 -0700 Subject: [PATCH] Update GraphQL API import and refactor GraphQL call expressions (#1012) * chore: update graphql api import * chore: refactor graphql call expressions to common function * chore: use pluralize for graphql list queries --- ...studio-ui-codegen-react-forms.test.ts.snap | 164 +++++++++++++----- .../studio-ui-codegen-react.test.ts.snap | 34 ++-- .../studio-ui-codegen-react-forms.test.ts | 12 +- .../bidirectional-relationship.ts | 107 ++---------- .../forms/form-renderer-helper/cta-props.ts | 31 +--- .../form-renderer-helper/relationship.ts | 122 +++---------- .../lib/imports/import-mapping.ts | 4 +- .../codegen-ui-react/lib/utils/graphql.ts | 102 +++++++++++ .../codegen-ui-react/lib/workflow/action.ts | 74 ++------ packages/codegen-ui-react/package-lock.json | 20 +-- packages/codegen-ui-react/package.json | 2 + 11 files changed, 316 insertions(+), 356 deletions(-) create mode 100644 packages/codegen-ui-react/lib/utils/graphql.ts diff --git a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap index 92e7b5c0..bbcc9a54 100644 --- a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap +++ b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap @@ -25,8 +25,8 @@ import { } from \\"@aws-amplify/ui-react/internal\\"; import { Post } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { createPost } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -373,7 +373,11 @@ export default function MyPostForm(props: MyPostFormProps): React.ReactElement { }; await API.graphql({ query: createPost, - variables: { input: modelFieldsToSave }, + variables: { + input: { + ...modelFieldsToSave, + }, + }, }); if (onSuccess) { onSuccess(modelFields); @@ -686,9 +690,9 @@ import { } from \\"@aws-amplify/ui-react/internal\\"; import { Member, Team } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { listTeams } from \\"../graphql/queries\\"; import { createMember } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -936,7 +940,9 @@ export default function MyMemberForm( ? Team.map((r) => getIDValue.Team?.(r)) : getIDValue.Team?.(Team) ); - const teamRecords = await API.graphql({ query: listTeams }).data.listTeams; + const teamRecords = await API.graphql({ + query: listTeams, + }).data.listTeams.items; const getDisplayValue = { teamID: (r) => \`\${r?.name ? r?.name + \\" - \\" : \\"\\"}\${r?.id}\`, Team: (r) => r?.name, @@ -1015,7 +1021,11 @@ export default function MyMemberForm( }); await API.graphql({ query: createMember, - variables: { input: modelFields }, + variables: { + input: { + ...modelFields, + }, + }, }); if (onSuccess) { onSuccess(modelFields); @@ -1271,9 +1281,9 @@ import { } from \\"@aws-amplify/ui-react/internal\\"; import { School, Student } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { listStudents } from \\"../graphql/queries\\"; import { createSchool, updateSchool } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -1517,8 +1527,9 @@ export default function SchoolCreateForm( ? Students.map((r) => getIDValue.Students?.(r)) : getIDValue.Students?.(Students) ); - const studentRecords = await API.graphql({ query: listStudents }).data - .listStudents; + const studentRecords = await API.graphql({ + query: listStudents, + }).data.listStudents.items; const getDisplayValue = { Students: (r) => r?.name, }; @@ -1597,7 +1608,11 @@ export default function SchoolCreateForm( }; const school = await API.graphql({ query: createSchool, - variables: { input: modelFieldsToSave }, + variables: { + input: { + ...modelFieldsToSave, + }, + }, }); const promises = []; promises.push( @@ -1605,7 +1620,12 @@ export default function SchoolCreateForm( promises.push( API.graphql({ query: updateSchool, - variables: { input: { ...original, schoolID: school.id } }, + variables: { + input: { + ...original, + schoolID: school.id, + }, + }, }) ); return promises; @@ -1791,9 +1811,9 @@ import { } from \\"@aws-amplify/ui-react/internal\\"; import { Author, Book } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { listAuthors } from \\"../graphql/queries\\"; import { createBook } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -2034,8 +2054,9 @@ export default function BookCreateForm( ? primaryAuthor.map((r) => getIDValue.primaryAuthor?.(r)) : getIDValue.primaryAuthor?.(primaryAuthor) ); - const authorRecords = await API.graphql({ query: listAuthors }).data - .listAuthors; + const authorRecords = await API.graphql({ + query: listAuthors, + }).data.listAuthors.items; const getDisplayValue = { primaryAuthor: (r) => \`\${r?.name ? r?.name + \\" - \\" : \\"\\"}\${r?.id}\`, }; @@ -2111,7 +2132,11 @@ export default function BookCreateForm( }); await API.graphql({ query: createBook, - variables: { input: modelFields }, + variables: { + input: { + ...modelFields, + }, + }, }); if (onSuccess) { onSuccess(modelFields); @@ -2301,9 +2326,9 @@ import { } from \\"@aws-amplify/ui-react/internal\\"; import { Post, Tag, TagPost } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { listPosts } from \\"../graphql/queries\\"; import { createTag, createTagPost } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -2548,7 +2573,9 @@ export default function TagCreateForm( ? Posts.map((r) => getIDValue.Posts?.(r)) : getIDValue.Posts?.(Posts) ); - const postRecords = await API.graphql({ query: listPosts }).data.listPosts; + const postRecords = await API.graphql({ + query: listPosts, + }).data.listPosts.items; const getDisplayValue = { Posts: (r) => r?.title, statuses: (r) => { @@ -2638,7 +2665,11 @@ export default function TagCreateForm( }; const tag = await API.graphql({ query: createTag, - variables: { input: modelFieldsToSave }, + variables: { + input: { + ...modelFieldsToSave, + }, + }, }); const promises = []; promises.push( @@ -2898,9 +2929,9 @@ import { } from \\"@aws-amplify/ui-react/internal\\"; import { Author, Book, Title } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { listAuthors, listTitles } from \\"../graphql/queries\\"; import { createBook } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -3162,9 +3193,12 @@ export default function BookCreateForm( ? primaryTitle.map((r) => getIDValue.primaryTitle?.(r)) : getIDValue.primaryTitle?.(primaryTitle) ); - const authorRecords = await API.graphql({ query: listAuthors }).data - .listAuthors; - const titleRecords = await API.graphql({ query: listTitles }).data.listTitles; + const authorRecords = await API.graphql({ + query: listAuthors, + }).data.listAuthors.items; + const titleRecords = await API.graphql({ + query: listTitles, + }).data.listTitles.items; const getDisplayValue = { primaryAuthor: (r) => r?.name, primaryTitle: (r) => r?.name, @@ -3243,7 +3277,11 @@ export default function BookCreateForm( }); await API.graphql({ query: createBook, - variables: { input: modelFields }, + variables: { + input: { + ...modelFields, + }, + }, }); if (onSuccess) { onSuccess(modelFields); @@ -3508,9 +3546,9 @@ import { } from \\"@aws-amplify/ui-react/internal\\"; import { CompositeDog, CompositeToy } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { listCompositeDogs } from \\"../graphql/queries\\"; import { createCompositeToy } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -3779,8 +3817,9 @@ export default function CreateCompositeToyForm( setCurrentCompositeDogCompositeToysDescriptionValue, ] = React.useState(undefined); const compositeDogCompositeToysDescriptionRef = React.createRef(); - const compositeDogRecords = await API.graphql({ query: listCompositeDogs }) - .data.listCompositeDogs; + const compositeDogRecords = await API.graphql({ + query: listCompositeDogs, + }).data.listCompositeDogs.items; const getDisplayValue = { compositeDogCompositeToysName: (r) => r?.name, compositeDogCompositeToysDescription: (r) => r?.description, @@ -3853,7 +3892,11 @@ export default function CreateCompositeToyForm( }); await API.graphql({ query: createCompositeToy, - variables: { input: modelFields }, + variables: { + input: { + ...modelFields, + }, + }, }); if (onSuccess) { onSuccess(modelFields); @@ -4168,9 +4211,9 @@ import { } from \\"@aws-amplify/ui-react/internal\\"; import { Comment, Org, Post, User } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { listOrgs, listPosts, listUsers } from \\"../graphql/queries\\"; import { createComment } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -4464,9 +4507,15 @@ export default function CreateCommentForm( ? Org.map((r) => getIDValue.Org?.(r)) : getIDValue.Org?.(Org) ); - const postRecords = await API.graphql({ query: listPosts }).data.listPosts; - const userRecords = await API.graphql({ query: listUsers }).data.listUsers; - const orgRecords = await API.graphql({ query: listOrgs }).data.listOrgs; + const postRecords = await API.graphql({ + query: listPosts, + }).data.listPosts.items; + const userRecords = await API.graphql({ + query: listUsers, + }).data.listUsers.items; + const orgRecords = await API.graphql({ + query: listOrgs, + }).data.listOrgs.items; const getDisplayValue = { post: (r) => \`\${r?.name ? r?.name + \\" - \\" : \\"\\"}\${r?.id}\`, User: (r) => \`\${r?.name ? r?.name + \\" - \\" : \\"\\"}\${r?.id}\`, @@ -4551,7 +4600,11 @@ export default function CreateCommentForm( }); await API.graphql({ query: createComment, - variables: { input: modelFields }, + variables: { + input: { + ...modelFields, + }, + }, }); if (onSuccess) { onSuccess(modelFields); @@ -4966,6 +5019,7 @@ import { CompositeVet, } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { listCompositeBowls, listCompositeOwners, @@ -4978,7 +5032,6 @@ import { updateCompositeDog, updateCompositeOwner, } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -5303,15 +5356,18 @@ export default function CreateCompositeDogForm( ? CompositeVets.map((r) => getIDValue.CompositeVets?.(r)) : getIDValue.CompositeVets?.(CompositeVets) ); - const compositeBowlRecords = await API.graphql({ query: listCompositeBowls }) - .data.listCompositeBowls; + const compositeBowlRecords = await API.graphql({ + query: listCompositeBowls, + }).data.listCompositeBowls.items; const compositeOwnerRecords = await API.graphql({ query: listCompositeOwners, - }).data.listCompositeOwners; - const compositeToyRecords = await API.graphql({ query: listCompositeToys }) - .data.listCompositeToys; - const compositeVetRecords = await API.graphql({ query: listCompositeVets }) - .data.listCompositeVets; + }).data.listCompositeOwners.items; + const compositeToyRecords = await API.graphql({ + query: listCompositeToys, + }).data.listCompositeToys.items; + const compositeVetRecords = await API.graphql({ + query: listCompositeVets, + }).data.listCompositeVets.items; const getDisplayValue = { CompositeBowl: (r) => \`\${r?.shape}\${\\"-\\"}\${r?.size}\`, CompositeOwner: (r) => \`\${r?.lastName}\${\\"-\\"}\${r?.firstName}\`, @@ -5404,7 +5460,11 @@ export default function CreateCompositeDogForm( }; const compositeDog = await API.graphql({ query: createCompositeDog, - variables: { input: modelFieldsToSave }, + variables: { + input: { + ...modelFieldsToSave, + }, + }, }); const promises = []; const compositeOwnerToLink = modelFields.CompositeOwner; @@ -5937,9 +5997,9 @@ import { } from \\"@aws-amplify/ui-react/internal\\"; import { Dog, Owner } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { listOwners } from \\"../graphql/queries\\"; import { createDog, updateOwner } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -6173,7 +6233,9 @@ export default function CreateDogForm( ? Owner.map((r) => getIDValue.Owner?.(r)) : getIDValue.Owner?.(Owner) ); - const ownerRecords = await API.graphql({ query: listOwners }).data.listOwners; + const ownerRecords = await API.graphql({ + query: listOwners, + }).data.listOwners.items; const getDisplayValue = { Owner: (r) => \`\${r?.name ? r?.name + \\" - \\" : \\"\\"}\${r?.id}\`, }; @@ -6249,7 +6311,11 @@ export default function CreateDogForm( }); const dog = await API.graphql({ query: createDog, - variables: { input: modelFields }, + variables: { + input: { + ...modelFields, + }, + }, }); const promises = []; const ownerToLink = modelFields.Owner; @@ -6445,9 +6511,9 @@ import { } from \\"@aws-amplify/ui-react/internal\\"; import { Dog, Owner } from \\"../API\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; import { listDogs } from \\"../graphql/queries\\"; import { createOwner, updateDog, updateOwner } from \\"../graphql/mutations\\"; -import { API } from \\"@aws-amplify/api\\"; export declare type ValidationResponse = { hasError: boolean; @@ -6688,7 +6754,9 @@ export default function CreateOwnerForm( ? Dog.map((r) => getIDValue.Dog?.(r)) : getIDValue.Dog?.(Dog) ); - const dogRecords = await API.graphql({ query: listDogs }).data.listDogs; + const dogRecords = await API.graphql({ + query: listDogs, + }).data.listDogs.items; const getDisplayValue = { Dog: (r) => \`\${r?.name ? r?.name + \\" - \\" : \\"\\"}\${r?.id}\`, }; @@ -6764,7 +6832,11 @@ export default function CreateOwnerForm( }); const owner = await API.graphql({ query: createOwner, - variables: { input: modelFields }, + variables: { + input: { + ...modelFields, + }, + }, }); const promises = []; const dogToLink = modelFields.Dog; diff --git a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react.test.ts.snap b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react.test.ts.snap index 558666a0..aa2d346c 100644 --- a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react.test.ts.snap +++ b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react.test.ts.snap @@ -155,7 +155,7 @@ exports[`amplify render tests actions GraphQL DataStoreCreateItem 1`] = ` Object { "componentText": "/* eslint-disable */ import * as React from \\"react\\"; -import { API } from \\"@aws-amplify/api\\"; +import { API } from \\"aws-amplify\\"; import { createCustomer } from \\"../graphql/mutations\\"; import { Customer } from \\"../API\\"; import { @@ -179,10 +179,14 @@ export default function CreateCustomerButton( ): React.ReactElement { const { overrides, ...rest } = props; const createCustomerButtonOnClick = async () => { - const input = { firstName: \\"Din\\", lastName: \\"Djarin\\" }; await API.graphql({ query: createCustomer, - variables: { input }, + variables: { + input: { + firstName: \\"Din\\", + lastName: \\"Djarin\\", + }, + }, }); }; return ( @@ -207,7 +211,7 @@ exports[`amplify render tests actions GraphQL DataStoreDeleteItem 1`] = ` Object { "componentText": "/* eslint-disable */ import * as React from \\"react\\"; -import { API } from \\"@aws-amplify/api\\"; +import { API } from \\"aws-amplify\\"; import { deleteCustomer } from \\"../graphql/mutations\\"; import { Customer } from \\"../API\\"; import { @@ -231,10 +235,13 @@ export default function DeleteCustomerButton( ): React.ReactElement { const { overrides, ...rest } = props; const deleteCustomerButtonOnClick = async () => { - const input = { id: \\"d9887268-47dd-4899-9568-db5809218751\\" }; await API.graphql({ query: deleteCustomer, - variables: { input }, + variables: { + input: { + id: \\"d9887268-47dd-4899-9568-db5809218751\\", + }, + }, }); }; return ( @@ -259,7 +266,7 @@ exports[`amplify render tests actions GraphQL DataStoreUpdateItem 1`] = ` Object { "componentText": "/* eslint-disable */ import * as React from \\"react\\"; -import { API } from \\"@aws-amplify/api\\"; +import { API } from \\"aws-amplify\\"; import { updateCustomer } from \\"../graphql/mutations\\"; import { Customer } from \\"../API\\"; import { @@ -283,14 +290,15 @@ export default function UpdateCustomerButton( ): React.ReactElement { const { overrides, ...rest } = props; const updateCustomerButtonOnClick = async () => { - const input = { - firstName: \\"Din\\", - lastName: \\"Djarin\\", - id: \\"d9887268-47dd-4899-9568-db5809218751\\", - }; await API.graphql({ query: updateCustomer, - variables: { input }, + variables: { + input: { + firstName: \\"Din\\", + lastName: \\"Djarin\\", + id: \\"d9887268-47dd-4899-9568-db5809218751\\", + }, + }, }); }; return ( diff --git a/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts b/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts index d6adc97a..ba10a39f 100644 --- a/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts +++ b/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts @@ -729,7 +729,7 @@ describe('amplify form renderer tests', () => { expect(componentText).toContain('import { Author, Book } from "../API";'); // check binding call is generated - expect(componentText).toContain('const authorRecords = await API.graphql({ query: listAuthors'); + expect(componentText).toContain('const authorRecords = await API.graphql({'); expect(componentText).toMatchSnapshot(); }); @@ -745,8 +745,8 @@ describe('amplify form renderer tests', () => { expect(componentText).toContain('import { Author, Book, Title } from "../API";'); // check binding calls are generated - expect(componentText).toContain('const authorRecords = await API.graphql({ query: listAuthors'); - expect(componentText).toContain('const titleRecords = await API.graphql({ query: listTitles'); + expect(componentText).toContain('const authorRecords = await API.graphql({'); + expect(componentText).toContain('const titleRecords = await API.graphql({'); expect(componentText).toMatchSnapshot(); }); @@ -762,7 +762,7 @@ describe('amplify form renderer tests', () => { expect(componentText).toContain('import { Member, Team } from "../API";'); // check binding call is generated - expect(componentText).toContain('const teamRecords = await API.graphql({ query: listTeams'); + expect(componentText).toContain('const teamRecords = await API.graphql({'); expect(componentText).toMatchSnapshot(); }); @@ -778,7 +778,7 @@ describe('amplify form renderer tests', () => { expect(componentText).toContain('import { Post, Tag, TagPost } from "../API";'); // check binding call is generated - expect(componentText).toContain('const postRecords = await API.graphql({ query: listPosts'); + expect(componentText).toContain('const postRecords = await API.graphql({'); // check custom display value is set expect(componentText).toContain('Posts: (r) => r?.title'); @@ -797,7 +797,7 @@ describe('amplify form renderer tests', () => { expect(componentText).toContain('import { School, Student } from "../API";'); // check binding call is generated - expect(componentText).toContain('const studentRecords = await API.graphql({ query: listStudents'); + expect(componentText).toContain('const studentRecords = await API.graphql({'); // check custom display value is set expect(componentText).toContain('Students: (r) => r?.name'); diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/bidirectional-relationship.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/bidirectional-relationship.ts index bb4bb41f..ad7250b7 100644 --- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/bidirectional-relationship.ts +++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/bidirectional-relationship.ts @@ -21,6 +21,7 @@ import { ImportCollection } from '../../imports'; import { isModelDataType } from './render-checkers'; import { getRecordName } from './form-state'; import { DataApiKind } from '../../react-render-config'; +import { ActionType, getGraphqlCallExpression } from '../../utils/graphql'; function getFieldBiDirectionalWith({ modelName, @@ -98,59 +99,19 @@ function unlinkModelRecordExpression({ dataApi?: DataApiKind; }) { if (dataApi === 'GraphQL') { - const updateMutation = `update${modelName}`; + const inputs = [ + factory.createSpreadAssignment(factory.createIdentifier(recordNameToUnlink)), + factory.createPropertyAssignment(factory.createIdentifier(fieldName), factory.createIdentifier('undefined')), + ...associatedFields.map((field) => + factory.createPropertyAssignment(factory.createIdentifier(field), factory.createIdentifier('undefined')), + ), + ]; return factory.createExpressionStatement( factory.createCallExpression( factory.createPropertyAccessExpression(factory.createIdentifier('promises'), factory.createIdentifier('push')), undefined, - [ - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('API'), - factory.createIdentifier('graphql'), - ), - undefined, - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('query'), - factory.createIdentifier(importCollection.addGraphqlMutationImport(updateMutation)), - ), - factory.createPropertyAssignment( - factory.createIdentifier('variables'), - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('input'), - factory.createObjectLiteralExpression( - [ - factory.createSpreadAssignment(factory.createIdentifier(recordNameToUnlink)), - factory.createPropertyAssignment( - factory.createIdentifier(fieldName), - factory.createIdentifier('undefined'), - ), - ...associatedFields.map((field) => - factory.createPropertyAssignment( - factory.createIdentifier(field), - factory.createIdentifier('undefined'), - ), - ), - ], - true, - ), - ), - ], - true, - ), - ), - ], - true, - ), - ], - ), - ], + [getGraphqlCallExpression(ActionType.UPDATE, modelName, importCollection, inputs)], ), ); } @@ -305,53 +266,19 @@ function linkModelRecordExpression({ dataApi?: DataApiKind; }) { if (dataApi === 'GraphQL') { - const updateMutation = `update${importedRelatedModelName}`; + const inputs = [ + factory.createSpreadAssignment(factory.createIdentifier(importedRelatedModelName)), + factory.createPropertyAssignment( + factory.createIdentifier(fieldBiDirectionalWithName), + factory.createIdentifier(currentRecord), + ), + ]; return factory.createExpressionStatement( factory.createCallExpression( factory.createPropertyAccessExpression(factory.createIdentifier('promises'), factory.createIdentifier('push')), undefined, - [ - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('API'), - factory.createIdentifier('graphql'), - ), - undefined, - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('query'), - factory.createIdentifier(importCollection.addGraphqlMutationImport(updateMutation)), - ), - factory.createPropertyAssignment( - factory.createIdentifier('variables'), - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('input'), - factory.createObjectLiteralExpression( - [ - factory.createSpreadAssignment(factory.createIdentifier(importedRelatedModelName)), - factory.createPropertyAssignment( - factory.createIdentifier(fieldBiDirectionalWithName), - factory.createIdentifier(currentRecord), - ), - ], - true, - ), - ), - ], - true, - ), - ), - ], - true, - ), - ], - ), - ], + [getGraphqlCallExpression(ActionType.UPDATE, importedRelatedModelName, importCollection, inputs)], ), ); } diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/cta-props.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/cta-props.ts index 0d8708f2..58ab4a81 100644 --- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/cta-props.ts +++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/cta-props.ts @@ -36,6 +36,7 @@ import { ImportCollection } from '../../imports'; import { getBiDirectionalRelationshipStatements } from './bidirectional-relationship'; import { generateModelObjectToSave } from './parse-fields'; import { DataApiKind } from '../../react-render-config'; +import { ActionType, getGraphqlCallExpression } from '../../utils/graphql'; const getRecordCreateCallExpression = ({ savedObjectName, @@ -49,35 +50,9 @@ const getRecordCreateCallExpression = ({ dataApi?: DataApiKind; }) => { if (dataApi === 'GraphQL') { - const createMutation = `create${importedModelName}`; + const inputs = [factory.createSpreadAssignment(factory.createIdentifier(savedObjectName))]; - return factory.createCallExpression( - factory.createPropertyAccessExpression(factory.createIdentifier('API'), factory.createIdentifier('graphql')), - undefined, - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('query'), - factory.createIdentifier(importCollection.addGraphqlMutationImport(createMutation)), - ), - factory.createPropertyAssignment( - factory.createIdentifier('variables'), - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('input'), - factory.createIdentifier(savedObjectName), - ), - ], - false, - ), - ), - ], - true, - ), - ], - ); + return getGraphqlCallExpression(ActionType.CREATE, importedModelName, importCollection, inputs); } return factory.createCallExpression( diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts index 787bf73b..b173321a 100644 --- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts +++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts @@ -29,6 +29,7 @@ import { isManyToManyRelationship } from './map-from-fieldConfigs'; import { extractModelAndKeys, getIDValueCallChain, getMatchEveryModelFieldCallExpression } from './model-values'; import { isModelDataType } from './render-checkers'; import { DataApiKind } from '../../react-render-config'; +import { ActionType, getGraphqlCallExpression, getGraphqlQueryForModel } from '../../utils/graphql'; export const buildRelationshipQuery = ( relatedModelName: string, @@ -38,7 +39,6 @@ export const buildRelationshipQuery = ( const itemsName = getRecordsName(relatedModelName); if (dataApi === 'GraphQL') { - const query = `list${importCollection.addModelImport(relatedModelName)}s`; return factory.createVariableStatement( undefined, factory.createVariableDeclarationList( @@ -50,27 +50,13 @@ export const buildRelationshipQuery = ( factory.createAwaitExpression( factory.createPropertyAccessExpression( factory.createPropertyAccessExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('API'), - factory.createIdentifier('graphql'), - ), - undefined, - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('query'), - factory.createIdentifier(importCollection.addGraphqlQueryImport(query)), - ), - ], - false, - ), - ], + factory.createPropertyAccessExpression( + getGraphqlCallExpression(ActionType.LIST, relatedModelName, importCollection), + factory.createIdentifier('data'), ), - factory.createIdentifier('data'), + factory.createIdentifier(getGraphqlQueryForModel(ActionType.LIST, relatedModelName)), ), - factory.createIdentifier(query), + factory.createIdentifier('items'), ), ), ), @@ -1917,7 +1903,6 @@ const getUpdateRelatedModelExpression = ( setToNull?: boolean, ) => { if (dataApi === 'GraphQL') { - const updateMutation = `update${capitalizeFirstLetter(savedModelName)}`; const statements: PropertyAssignment[] = relatedModelFields.map((relatedModelField, index) => { const correspondingPrimaryKey = thisModelPrimaryKeys[index]; @@ -1948,39 +1933,17 @@ const getUpdateRelatedModelExpression = ( /** * API.graphql({ * query: updateStudent, - * variables: { input: { ...original, schoolID: school.id }} + * variables: { + * input: { + * ...original, + * schoolID: school.id + * } + * } * }) */ - return factory.createCallExpression( - factory.createPropertyAccessExpression(factory.createIdentifier('API'), factory.createIdentifier('graphql')), - undefined, - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('query'), - factory.createIdentifier(importCollection.addGraphqlMutationImport(updateMutation)), - ), - factory.createPropertyAssignment( - factory.createIdentifier('variables'), - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('input'), - factory.createObjectLiteralExpression( - [factory.createSpreadAssignment(factory.createIdentifier('original')), ...statements], - false, - ), - ), - ], - false, - ), - ), - ], - true, - ), - ], - ); + const inputs = [factory.createSpreadAssignment(factory.createIdentifier('original')), ...statements]; + + return getGraphqlCallExpression(ActionType.UPDATE, capitalizeFirstLetter(savedModelName), importCollection, inputs); } /** @@ -2041,52 +2004,17 @@ const getCreateJoinTableExpression = ( dataApi?: DataApiKind, ): CallExpression => { if (dataApi === 'GraphQL') { - const createMutation = `create${relatedJoinTableName}`; + const inputs = [ + savedModelName === joinTableThisModelName + ? factory.createShorthandPropertyAssignment(factory.createIdentifier(joinTableThisModelName), undefined) + : factory.createPropertyAssignment( + factory.createIdentifier(joinTableThisModelName), + factory.createIdentifier(savedModelName), + ), + factory.createShorthandPropertyAssignment(factory.createIdentifier(joinTableRelatedModelName), undefined), + ]; - return factory.createCallExpression( - factory.createPropertyAccessExpression(factory.createIdentifier('API'), factory.createIdentifier('graphql')), - undefined, - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('query'), - factory.createIdentifier(importCollection.addGraphqlMutationImport(createMutation)), - ), - factory.createPropertyAssignment( - factory.createIdentifier('variables'), - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('input'), - factory.createObjectLiteralExpression( - [ - savedModelName === joinTableThisModelName - ? factory.createShorthandPropertyAssignment( - factory.createIdentifier(joinTableThisModelName), - undefined, - ) - : factory.createPropertyAssignment( - factory.createIdentifier(joinTableThisModelName), - factory.createIdentifier(savedModelName), - ), - factory.createShorthandPropertyAssignment( - factory.createIdentifier(joinTableRelatedModelName), - undefined, - ), - ], - true, - ), - ), - ], - true, - ), - ), - ], - true, - ), - ], - ); + return getGraphqlCallExpression(ActionType.CREATE, relatedJoinTableName, importCollection, inputs); } return factory.createCallExpression( diff --git a/packages/codegen-ui-react/lib/imports/import-mapping.ts b/packages/codegen-ui-react/lib/imports/import-mapping.ts index 9990c763..acdb9ecb 100644 --- a/packages/codegen-ui-react/lib/imports/import-mapping.ts +++ b/packages/codegen-ui-react/lib/imports/import-mapping.ts @@ -22,7 +22,7 @@ export enum ImportSource { LOCAL_MODELS = '../models', LOCAL_SCHEMA = '../models/schema', UTILS = './utils', - AMPLIFY_API = '@aws-amplify/api', + AMPLIFY = 'aws-amplify', } export enum ImportValue { @@ -81,5 +81,5 @@ export const ImportMapping: Record = { [ImportValue.FETCH_BY_PATH]: ImportSource.UTILS, [ImportValue.PROCESS_FILE]: ImportSource.UTILS, [ImportValue.USE_STATE]: ImportSource.REACT, - [ImportValue.API]: ImportSource.AMPLIFY_API, + [ImportValue.API]: ImportSource.AMPLIFY, }; diff --git a/packages/codegen-ui-react/lib/utils/graphql.ts b/packages/codegen-ui-react/lib/utils/graphql.ts new file mode 100644 index 00000000..8b7d79ff --- /dev/null +++ b/packages/codegen-ui-react/lib/utils/graphql.ts @@ -0,0 +1,102 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { plural } from 'pluralize'; +import { InvalidInputError } from '@aws-amplify/codegen-ui'; +import { CallExpression, factory } from 'typescript'; +import { ImportCollection, ImportValue } from '../imports'; + +export enum ActionType { + CREATE = 'create', + UPDATE = 'update', + DELETE = 'delete', + LIST = 'list', +} + +export const getGraphqlQueryForModel = (action: ActionType, model: string): string => { + switch (action) { + case ActionType.CREATE: + return `create${model}`; + case ActionType.UPDATE: + return `update${model}`; + case ActionType.DELETE: + return `delete${model}`; + case ActionType.LIST: + return `list${plural(model)}`; + default: + throw new InvalidInputError(`Action ${action} has no corresponding GraphQL operation`); + } +}; + +/** + * Returns a GraphQL call expression and adds to importCollection. + * + * @example + * ``` + * API.graphql({ + * query: createTodo, + * variables: { + * input: { + * inputs + * }, + * }, + * }); + * ``` + */ +export const getGraphqlCallExpression = ( + action: ActionType, + model: string, + importCollection: ImportCollection, + inputs?: any[], +): CallExpression => { + const query = getGraphqlQueryForModel(action, model); + + importCollection.addMappedImport(ImportValue.API); + + if (action === ActionType.LIST) { + importCollection.addGraphqlQueryImport(query); + } else { + importCollection.addGraphqlMutationImport(query); + } + importCollection.addModelImport(model); + + return factory.createCallExpression( + factory.createPropertyAccessExpression(factory.createIdentifier('API'), factory.createIdentifier('graphql')), + undefined, + [ + factory.createObjectLiteralExpression( + inputs + ? [ + factory.createPropertyAssignment(factory.createIdentifier('query'), factory.createIdentifier(query)), + factory.createPropertyAssignment( + factory.createIdentifier('variables'), + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier('input'), + factory.createObjectLiteralExpression(inputs, true), + ), + ], + true, + ), + ), + ] + : [factory.createPropertyAssignment(factory.createIdentifier('query'), factory.createIdentifier(query))], + true, + ), + ], + ); +}; diff --git a/packages/codegen-ui-react/lib/workflow/action.ts b/packages/codegen-ui-react/lib/workflow/action.ts index b3a2562b..773207cd 100644 --- a/packages/codegen-ui-react/lib/workflow/action.ts +++ b/packages/codegen-ui-react/lib/workflow/action.ts @@ -31,6 +31,7 @@ import { isActionEvent, propertyToExpression, getSetStateName, sanitizeName } fr import { ImportCollection, ImportSource, ImportValue } from '../imports'; import { getChildPropMappingForComponentName } from './utils'; import { DataApiKind } from '../react-render-config'; +import { ActionType, getGraphqlCallExpression } from '../utils/graphql'; enum Action { 'Amplify.Navigation' = 'Amplify.Navigation', @@ -76,24 +77,17 @@ export function getActionHookImportValue(action: string): ImportValue { return actionName; } -export function getGraphqlMutationForModel(action: Action, model: string): string { - let prefix: string; - +function getGraphqlMutationFromAction(action: Action): ActionType { switch (action) { case Action['Amplify.DataStoreCreateItemAction']: - prefix = 'create'; - break; + return ActionType.CREATE; case Action['Amplify.DataStoreUpdateItemAction']: - prefix = 'update'; - break; + return ActionType.UPDATE; case Action['Amplify.DataStoreDeleteItemAction']: - prefix = 'delete'; - break; + return ActionType.DELETE; default: throw new InvalidInputError(`Action ${action} has no corresponding GraphQL operation`); } - - return prefix + model; } export function getComponentActions(component: StudioComponent | StudioComponentChild): { @@ -163,12 +157,6 @@ export function buildGraphqlCallback( identifier: string, importCollection: ImportCollection, ) { - // Import API client - const actionMutation = getGraphqlMutationForModel(action.action as Action, action.parameters.model); - importCollection.addMappedImport(ImportValue.API); - importCollection.addGraphqlMutationImport(actionMutation); - importCollection.addModelImport(action.parameters.model); - const inputKeys = []; if ('id' in action.parameters && action.parameters.id) { @@ -199,55 +187,13 @@ export function buildGraphqlCallback( factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), factory.createBlock( [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier('input'), - undefined, - undefined, - factory.createObjectLiteralExpression(inputKeys), - ), - ], - // eslint-disable-next-line no-bitwise - ts.NodeFlags.Const | - ts.NodeFlags.AwaitContext | - ts.NodeFlags.ContextFlags | - ts.NodeFlags.TypeExcludesFlags, - ), - ), factory.createExpressionStatement( factory.createAwaitExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('API'), - factory.createIdentifier('graphql'), - ), - undefined, - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier('query'), - factory.createIdentifier(actionMutation), - ), - factory.createPropertyAssignment( - factory.createIdentifier('variables'), - factory.createObjectLiteralExpression( - [ - factory.createShorthandPropertyAssignment( - factory.createIdentifier('input'), - undefined, - ), - ], - false, - ), - ), - ], - true, - ), - ], + getGraphqlCallExpression( + getGraphqlMutationFromAction(action.action as Action), + action.parameters.model, + importCollection, + inputKeys, ), ), ), diff --git a/packages/codegen-ui-react/package-lock.json b/packages/codegen-ui-react/package-lock.json index 0555a081..da02a68e 100644 --- a/packages/codegen-ui-react/package-lock.json +++ b/packages/codegen-ui-react/package-lock.json @@ -9,8 +9,9 @@ "version": "2.14.2", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/codegen-ui": "2.12.2", + "@aws-amplify/codegen-ui": "2.13.1", "@typescript/vfs": "~1.3.5", + "pluralize": "^8.0.0", "typescript": "<=4.5.0" }, "devDependencies": { @@ -19,6 +20,7 @@ "@aws-amplify/ui-react": "^4.6.0", "@aws-amplify/ui-react-storage": "^1.1.0", "@types/node": "^16.3.3", + "@types/pluralize": "^0.0.29", "@types/react": "^17.0.4", "@types/semver": "^7.3.9", "pascalcase": "1.0.0", @@ -229,9 +231,9 @@ } }, "node_modules/@aws-amplify/codegen-ui": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@aws-amplify/codegen-ui/-/codegen-ui-2.12.2.tgz", - "integrity": "sha512-Km1EY3yJKTPlx2prAktZv5NktIBO98ydLygBQ+FA3eNgJdpw7N55+FEt/eZyoeUPthA9KnzzENuMN8RV+v5Ppw==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/codegen-ui/-/codegen-ui-2.13.1.tgz", + "integrity": "sha512-E348akzfNse6vtYMWW1ZFJwmI5km4TUzr7zW+mhw3/7gqqCE5ARG2gy6voL9+w8oC+XAg0g7H3GstpxdLhCVKA==", "dependencies": { "change-case": "^4.1.2", "yup": "^0.32.11" @@ -17921,7 +17923,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, "engines": { "node": ">=4" } @@ -21147,9 +21148,9 @@ } }, "@aws-amplify/codegen-ui": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@aws-amplify/codegen-ui/-/codegen-ui-2.12.2.tgz", - "integrity": "sha512-Km1EY3yJKTPlx2prAktZv5NktIBO98ydLygBQ+FA3eNgJdpw7N55+FEt/eZyoeUPthA9KnzzENuMN8RV+v5Ppw==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/codegen-ui/-/codegen-ui-2.13.1.tgz", + "integrity": "sha512-E348akzfNse6vtYMWW1ZFJwmI5km4TUzr7zW+mhw3/7gqqCE5ARG2gy6voL9+w8oC+XAg0g7H3GstpxdLhCVKA==", "requires": { "change-case": "^4.1.2", "yup": "^0.32.11" @@ -35892,8 +35893,7 @@ "pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==" }, "pngjs": { "version": "5.0.0", diff --git a/packages/codegen-ui-react/package.json b/packages/codegen-ui-react/package.json index 9938aa8d..f0a90cb2 100644 --- a/packages/codegen-ui-react/package.json +++ b/packages/codegen-ui-react/package.json @@ -25,6 +25,7 @@ "@aws-amplify/ui-react": "^4.6.0", "@aws-amplify/ui-react-storage": "^1.1.0", "@types/node": "^16.3.3", + "@types/pluralize": "^0.0.29", "@types/react": "^17.0.4", "@types/semver": "^7.3.9", "pascalcase": "1.0.0", @@ -33,6 +34,7 @@ "dependencies": { "@aws-amplify/codegen-ui": "2.14.2", "@typescript/vfs": "~1.3.5", + "pluralize": "^8.0.0", "typescript": "<=4.5.0" }, "peerDependencies": {