Skip to content

Commit

Permalink
feat: support type casting for DataStore hooks (#460)
Browse files Browse the repository at this point in the history
Co-authored-by: Hein Jeong <heinje@amazon.com>
  • Loading branch information
hein-j and Hein Jeong authored Apr 13, 2022
1 parent e4062c8 commit 79953c5
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
useDataStoreCreateAction,
} from \\"@aws-amplify/ui-react/internal\\";
import { Customer } from \\"../models\\";
import { schema } from \\"../models/schema\\";
import { Button, ButtonProps } from \\"@aws-amplify/ui-react\\";

export type CreateCustomerButtonProps = React.PropsWithChildren<
Expand All @@ -24,6 +25,7 @@ export default function CreateCustomerButton(
const createCustomerButtonOnClick = useDataStoreCreateAction({
model: Customer,
fields: { firstName: \\"Din\\", lastName: \\"Djarin\\" },
schema: schema,
});
return (
/* @ts-ignore: TS2322 */
Expand Down Expand Up @@ -53,6 +55,7 @@ import {
useDataStoreDeleteAction,
} from \\"@aws-amplify/ui-react/internal\\";
import { Customer } from \\"../models\\";
import { schema } from \\"../models/schema\\";
import { Button, ButtonProps } from \\"@aws-amplify/ui-react\\";

export type DeleteCustomerButtonProps = React.PropsWithChildren<
Expand All @@ -67,6 +70,7 @@ export default function DeleteCustomerButton(
const deleteCustomerButtonOnClick = useDataStoreDeleteAction({
model: Customer,
id: \\"d9887268-47dd-4899-9568-db5809218751\\",
schema: schema,
});
return (
/* @ts-ignore: TS2322 */
Expand Down Expand Up @@ -96,6 +100,7 @@ import {
useDataStoreUpdateAction,
} from \\"@aws-amplify/ui-react/internal\\";
import { Customer } from \\"../models\\";
import { schema } from \\"../models/schema\\";
import { Button, ButtonProps } from \\"@aws-amplify/ui-react\\";

export type UpdateCustomerButtonProps = React.PropsWithChildren<
Expand All @@ -111,6 +116,7 @@ export default function UpdateCustomerButton(
model: Customer,
id: \\"d9887268-47dd-4899-9568-db5809218751\\",
fields: { firstName: \\"Din\\", lastName: \\"Djarin\\" },
schema: schema,
});
return (
/* @ts-ignore: TS2322 */
Expand Down Expand Up @@ -503,6 +509,7 @@ import {
useDataStoreCreateAction,
} from \\"@aws-amplify/ui-react/internal\\";
import { Customer } from \\"../models\\";
import { schema } from \\"../models/schema\\";
import { Button, ButtonProps } from \\"@aws-amplify/ui-react\\";

export type ComponentWithAuthEventBindingProps = React.PropsWithChildren<
Expand All @@ -521,6 +528,7 @@ export default function ComponentWithAuthEventBinding(
userName: authAttributes[\\"username\\"],
favoriteIceCream: authAttributes[\\"custom:favorite_icecream\\"],
},
schema: schema,
});
return (
/* @ts-ignore: TS2322 */
Expand Down Expand Up @@ -5473,6 +5481,7 @@ import {
useStateMutationAction,
} from \\"@aws-amplify/ui-react/internal\\";
import { Customer } from \\"../models\\";
import { schema } from \\"../models/schema\\";
import { Button, Flex, FlexProps, TextField } from \\"@aws-amplify/ui-react\\";

export type MyFormProps = React.PropsWithChildren<
Expand All @@ -5488,6 +5497,7 @@ export default function MyForm(props: MyFormProps): React.ReactElement {
model: Customer,
id: \\"d9887268-47dd-4899-9568-db5809218751\\",
fields: { username: usernameTextFieldValue },
schema: schema,
});
return (
/* @ts-ignore: TS2322 */
Expand Down
1 change: 1 addition & 0 deletions packages/codegen-ui-react/lib/imports/import-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export enum ImportSource {
UI_REACT_INTERNAL = '@aws-amplify/ui-react/internal',
AMPLIFY_DATASTORE = '@aws-amplify/datastore',
LOCAL_MODELS = '../models',
LOCAL_SCHEMA = '../models/schema',
}

export enum ImportValue {
Expand Down
32 changes: 22 additions & 10 deletions packages/codegen-ui-react/lib/workflow/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export const ActionNameMapping: Partial<Record<Action, ImportValue>> = {
[Action['Amplify.Mutation']]: ImportValue.USE_STATE_MUTATION_ACTION,
};

const DataStoreActions = new Set([
Action['Amplify.DataStoreCreateItemAction'],
Action['Amplify.DataStoreDeleteItemAction'],
Action['Amplify.DataStoreUpdateItemAction'],
]);

function isMutationAction(action: ActionStudioComponentEvent): action is MutationAction {
return (action.action as Action) === Action['Amplify.Mutation'];
}
Expand Down Expand Up @@ -172,22 +178,28 @@ export function buildActionArguments(
importCollection: ImportCollection,
): ObjectLiteralExpression[] | undefined {
if (action.parameters) {
return [
factory.createObjectLiteralExpression(
Object.entries(action.parameters).map(([key, value]) =>
factory.createPropertyAssignment(
factory.createIdentifier(key),
getActionParameterValue(componentMetadata, key, value, importCollection),
),
),
false,
const properties = Object.entries(action.parameters).map(([key, value]) =>
factory.createPropertyAssignment(
factory.createIdentifier(key),
getActionParameterValue(componentMetadata, key, value, importCollection),
),
];
);

if (DataStoreActions.has(action.action as Action)) {
addSchemaToArguments(properties, importCollection);
}
return [factory.createObjectLiteralExpression(properties, false)];
}

return undefined;
}

export function addSchemaToArguments(properties: ts.PropertyAssignment[], importCollection: ImportCollection) {
const SCHEMA = 'schema';
properties.push(factory.createPropertyAssignment(factory.createIdentifier(SCHEMA), factory.createIdentifier(SCHEMA)));
importCollection.addImport(ImportSource.LOCAL_SCHEMA, SCHEMA);
}

export function getActionParameterValue(
componentMetadata: ComponentMetadata,
key: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,21 @@ describe('Workflow', () => {
});

describe('DataStore', () => {
it('supports creating a datastore item', () => {
cy.get('#user-collection').contains('Din Djarin').should('not.exist');
it('supports creating a datastore item, type-casting scalar values', () => {
const expected = 'Din Djarin | age: 200 | isLoggedIn: true';
cy.get('#user-collection').contains(expected).should('not.exist');
cy.get('#create-item').click();
cy.get('#user-collection').contains('Din Djarin');
cy.get('#user-collection').contains(expected);
});

it('supports updating a datastore item', () => {
cy.get('#user-collection').contains('UpdateMe Me');
cy.get('#user-collection').contains('Moff Gideon').should('not.exist');
it('supports updating a datastore item, type-casting scalar values', () => {
const before = 'UpdateMe Me';
const after = 'Moff Gideon | age: 200 | isLoggedIn: true';
cy.get('#user-collection').contains(before);
cy.get('#user-collection').contains(after).should('not.exist');
cy.get('#update-item').click();
cy.get('#user-collection').contains('UpdateMe Me').should('not.exist');
cy.get('#user-collection').contains('Moff Gideon');
cy.get('#user-collection').contains(before).should('not.exist');
cy.get('#user-collection').contains(after);
});

it('supports deleting a datastore item', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
import { useEffect, useState } from 'react';
import { useEffect, useState, useRef } from 'react';
import { AmplifyProvider } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import { DataStore } from 'aws-amplify';
Expand Down Expand Up @@ -128,8 +128,13 @@ const initializeListingTestData = async (): Promise<void> => {

export default function ComponentTests() {
const [isInitialized, setInitialized] = useState(false);
const initializeStarted = useRef(false);

useEffect(() => {
const initializeTestUserData = async () => {
if (initializeStarted.current) {
return;
}
// DataStore.clear() doesn't appear to reliably work in this scenario.
indexedDB.deleteDatabase('amplify-datastore');
await Promise.all([initializeUserTestData(), initializeListingTestData()]);
Expand All @@ -142,6 +147,7 @@ export default function ComponentTests() {
};

initializeTestUserData();
initializeStarted.current = true;
}, []);

if (!isInitialized) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
import { useState, SyntheticEvent, useEffect } from 'react';
import { useState, SyntheticEvent, useEffect, useRef } from 'react';
import '@aws-amplify/ui-react/styles.css';
import { AmplifyProvider, View, Heading, Divider, Button } from '@aws-amplify/ui-react';
import { Hub } from 'aws-amplify';
Expand Down Expand Up @@ -61,6 +61,7 @@ export default function ComplexTests() {
const [hasDisappeared, setDisappeared] = useState(false);
const [authState, setAuthState] = useState<AuthState>('LoggedIn');
const [formIdToUpdate, setFormIdToUpdate] = useState('');
const initializeStarted = useRef(false);

const initializeUserTestData = async (): Promise<void> => {
await DataStore.save(new User({ firstName: 'DeleteMe', lastName: 'Me' }));
Expand Down Expand Up @@ -88,22 +89,29 @@ export default function ComplexTests() {

useEffect(() => {
const initializeTestState = async () => {
if (initializeStarted.current) {
return;
}
// DataStore.clear() doesn't appear to reliably work in this scenario.
indexedDB.deleteDatabase('amplify-datastore');
await initializeUserTestData();
const queriedIdToDelete = (await DataStore.query(User, (criteria) => criteria.firstName('eq', 'DeleteMe')))[0].id;
setIdToDelete(queriedIdToDelete);
const queriedIdToUpdate = (await DataStore.query(User, (criteria) => criteria.firstName('eq', 'UpdateMe')))[0].id;
setIdToUpdate(queriedIdToUpdate);
const queriedFormIdToUpdate = (
await DataStore.query(User, (criteria) => criteria.firstName('eq', 'FormUpdate'))
)[0].id;
setFormIdToUpdate(queriedFormIdToUpdate);
initializeAuthListener();
setInitialized(true);
indexedDB.deleteDatabase('amplify-datastore').onsuccess = async function () {
await initializeUserTestData();
const queriedIdToDelete = (await DataStore.query(User, (criteria) => criteria.firstName('eq', 'DeleteMe')))[0]
.id;
setIdToDelete(queriedIdToDelete);
const queriedIdToUpdate = (await DataStore.query(User, (criteria) => criteria.firstName('eq', 'UpdateMe')))[0]
.id;
setIdToUpdate(queriedIdToUpdate);
const queriedFormIdToUpdate = (
await DataStore.query(User, (criteria) => criteria.firstName('eq', 'FormUpdate'))
)[0].id;
setFormIdToUpdate(queriedFormIdToUpdate);
initializeAuthListener();
setInitialized(true);
};
};

initializeTestState();
initializeStarted.current = true;
}, []);

const complexModels = useDataStoreBinding({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@
"property": "user",
"field": "lastName"
}
},
{
"value": " | age: "
},
{
"collectionBindingProperties": {
"property": "user",
"field": "age"
}
},
{
"value": " | isLoggedIn: "
},
{
"collectionBindingProperties": {
"property": "user",
"field": "isLoggedIn"
}
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
},
"lastName": {
"value": "Djarin"
},
"isLoggedIn": {
"value": "true"
},
"age": {
"value": "200"
}
}
}
Expand Down Expand Up @@ -66,6 +72,12 @@
},
"lastName": {
"value": "Gideon"
},
"isLoggedIn": {
"value": "true"
},
"age": {
"value": "200"
}
}
}
Expand Down

0 comments on commit 79953c5

Please sign in to comment.