From 6c241295cc61edcb1a8c6983bbac19276aa36821 Mon Sep 17 00:00:00 2001 From: Alexander Harris Date: Mon, 14 Feb 2022 13:11:00 -0800 Subject: [PATCH] fix: sanitize component name before generating statement declarations --- .../studio-ui-codegen-react.test.ts.snap | 43 +++++++++++++++++++ .../__tests__/studio-ui-codegen-react.test.ts | 4 ++ .../lib/__tests__/workflow/action.test.ts | 40 +++++++++++++++++ .../codegen-ui-react/lib/workflow/action.ts | 10 ++++- .../workflow/invalidNameForMethod.json | 29 +++++++++++++ 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 packages/codegen-ui-react/lib/__tests__/workflow/action.test.ts create mode 100644 packages/codegen-ui/example-schemas/workflow/invalidNameForMethod.json 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 5b56ea369..b31377788 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 @@ -5843,6 +5843,49 @@ export default function SetStateWithoutInitialValue( } `; +exports[`amplify render tests mutations supports names that cant be directly turned into methodnames 1`] = ` +Object { + "componentText": "/* eslint-disable */ +import React from \\"react\\"; +import { + EscapeHatchProps, + getOverrideProps, + useNavigateAction, +} from \\"@aws-amplify/ui-react/internal\\"; +import { Flex, FlexProps, Text } from \\"@aws-amplify/ui-react\\"; + +export type InvalidNameForMethodProps = React.PropsWithChildren< + Partial & { + overrides?: EscapeHatchProps | undefined | null; + } +>; +export default function InvalidNameForMethod( + props: InvalidNameForMethodProps +): React.ReactElement { + const { overrides, ...rest } = props; + const loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreClick = + useNavigateAction({ type: \\"url\\", url: \\"emails\\" }); + return ( + /* @ts-ignore: TS2322 */ + + { + loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreClick(); + }} + {...getOverrideProps( + overrides, + \\"\\\\u201CLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.\\\\u201D\\" + )} + > + + ); +} +", + "declaration": undefined, + "renderComponentToFilesystem": [Function], +} +`; + exports[`amplify render tests mutations supports nested mutation 1`] = ` Object { "componentText": "/* eslint-disable */ diff --git a/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react.test.ts b/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react.test.ts index 0ab5dc8d6..3b347f4d0 100644 --- a/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react.test.ts +++ b/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react.test.ts @@ -385,6 +385,10 @@ describe('amplify render tests', () => { it('supports nested mutation', () => { expect(generateWithAmplifyRenderer('workflow/nestedMutation')).toMatchSnapshot(); }); + + it('supports names that cant be directly turned into methodnames', () => { + expect(generateWithAmplifyRenderer('workflow/invalidNameForMethod')).toMatchSnapshot(); + }); }); describe('default value', () => { diff --git a/packages/codegen-ui-react/lib/__tests__/workflow/action.test.ts b/packages/codegen-ui-react/lib/__tests__/workflow/action.test.ts new file mode 100644 index 000000000..6a4b9c38e --- /dev/null +++ b/packages/codegen-ui-react/lib/__tests__/workflow/action.test.ts @@ -0,0 +1,40 @@ +/* + 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 { getActionIdentifier } from '../../workflow/action'; + +describe('getActionIdentifier', () => { + test('generates bindings in the happy case', () => { + expect(getActionIdentifier('SubmitButton', 'click')).toMatch('submitButtonClick'); + }); + + test('generates legal bindings for names with special characters', () => { + expect( + getActionIdentifier( + '“Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.”', + 'click', + ), + ).toMatch('loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreClick'); + }); + + test('generates legal bindings for names starting with a number', () => { + expect( + getActionIdentifier( + '2“Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.”', + 'click', + ), + ).toMatch('loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreClick'); + }); +}); diff --git a/packages/codegen-ui-react/lib/workflow/action.ts b/packages/codegen-ui-react/lib/workflow/action.ts index 3d14c3e45..0402e3236 100644 --- a/packages/codegen-ui-react/lib/workflow/action.ts +++ b/packages/codegen-ui-react/lib/workflow/action.ts @@ -79,8 +79,15 @@ export function getComponentActions(component: StudioComponent | StudioComponent return actions; } +// Scrub all non-alphanum characters, and any leading numbers so we can generate a legal +// variable name. +function sanitizeName(componentName: string): string { + return componentName.replaceAll(/[^a-zA-Z0-9]/g, '').replace(/^[0-9]*/, ''); +} + export function getActionIdentifier(componentName: string | undefined, event: string) { - const name = componentName || ''; + const inputName = componentName || ''; + const name = sanitizeName(inputName); return [name.charAt(0).toLowerCase() + name.slice(1), event.charAt(0).toUpperCase() + event.slice(1)].join(''); } @@ -121,7 +128,6 @@ export function buildMutationActionStatement( action: MutationAction, identifier: string, ) { - // TODO: Hi there const { componentName, property } = action.parameters.state; const childrenPropMapping = getChildPropMappingForComponentName(componentMetadata, componentName); const stateReference = diff --git a/packages/codegen-ui/example-schemas/workflow/invalidNameForMethod.json b/packages/codegen-ui/example-schemas/workflow/invalidNameForMethod.json new file mode 100644 index 000000000..0ac9f1473 --- /dev/null +++ b/packages/codegen-ui/example-schemas/workflow/invalidNameForMethod.json @@ -0,0 +1,29 @@ +{ + "id": "1234-5678-9010", + "componentType": "Flex", + "name": "InvalidNameForMethod", + "properties": {}, + "children": [ + { + "name": "“Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.”", + "componentType": "Text", + "properties": {}, + "bindingProperties": {}, + "events": { + "click": { + "action": "Amplify.Navigation", + "parameters": { + "type": { + "value": "url" + }, + "url": { + "value": "emails", + "type": "string" + } + } + } + } + } + ] + } + \ No newline at end of file