Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: alias model names collision with component types #733

Merged
merged 2 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ jobs:
with:
path: amplify-cli
repository: aws-amplify/amplify-cli
- name: Setup Node.js LTS
- name: Setup Node.js LTS/gallium
uses: actions/setup-node@v2
with:
node-version: lts/*
node-version: lts/gallium
- name: Build amplify-codegen-ui
working-directory: amplify-codegen-ui
run: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,14 +345,14 @@ exports[`amplify render tests actions with conditional in parameters 1`] = `
Object {
"componentText": "/* eslint-disable */
import * as React from \\"react\\";
import { User } from \\"../models\\";
import {
EscapeHatchProps,
createDataStorePredicate,
getOverrideProps,
useDataStoreBinding,
useStateMutationAction,
} from \\"@aws-amplify/ui-react/internal\\";
import { User } from \\"../models\\";
import { Button, Flex, FlexProps, Text } from \\"@aws-amplify/ui-react\\";

export type ConditionalInMutationProps = React.PropsWithChildren<
Expand Down Expand Up @@ -578,6 +578,39 @@ export default function DataBindingNamedClass(
"
`;

exports[`amplify render tests bindings data supports model with conflicting component type 1`] = `
"/* eslint-disable */
import * as React from \\"react\\";
import { Flex as Flex0 } from \\"../models\\";
import {
EscapeHatchProps,
getOverrideProps,
} from \\"@aws-amplify/ui-react/internal\\";
import { Text, TextProps } from \\"@aws-amplify/ui-react\\";

export type DataBindingNamedClassProps = React.PropsWithChildren<
Partial<TextProps> & {
class?: Flex0;
} & {
overrides?: EscapeHatchProps | undefined | null;
}
>;
export default function DataBindingNamedClass(
props: DataBindingNamedClassProps
): React.ReactElement {
const { class: classProp, overrides, ...rest } = props;
return (
/* @ts-ignore: TS2322 */
<Text
children={classProp?.name}
{...rest}
{...getOverrideProps(overrides, \\"DataBindingNamedClass\\")}
></Text>
);
}
"
`;

exports[`amplify render tests collection should not render nested query if the data schema is not provided 1`] = `
"/* eslint-disable */
import * as React from \\"react\\";
Expand Down Expand Up @@ -639,13 +672,13 @@ export default function AuthorProfileCollection(
exports[`amplify render tests collection should render collection with data binding 1`] = `
"/* eslint-disable */
import * as React from \\"react\\";
import { UserPreferences, User } from \\"../models\\";
import {
EscapeHatchProps,
createDataStorePredicate,
getOverrideProps,
useDataStoreBinding,
} from \\"@aws-amplify/ui-react/internal\\";
import { User, UserPreferences } from \\"../models\\";
import {
Button,
Collection,
Expand Down Expand Up @@ -742,14 +775,14 @@ exports[`amplify render tests collection should render collection with data bind
Object {
"componentText": "/* eslint-disable */
import * as React from \\"react\\";
import { UserPreferences, User } from \\"../models\\";
import {
EscapeHatchProps,
createDataStorePredicate,
getOverrideProps,
useDataStoreBinding,
} from \\"@aws-amplify/ui-react/internal\\";
import { SortDirection, SortPredicate } from \\"@aws-amplify/datastore\\";
import { User, UserPreferences } from \\"../models\\";
import {
Button,
Collection,
Expand Down Expand Up @@ -853,13 +886,13 @@ export default function CollectionOfCustomButtons(
exports[`amplify render tests collection should render collection with data binding if binding name is items 1`] = `
"/* eslint-disable */
import * as React from \\"react\\";
import { UserPreferences, User } from \\"../models\\";
import {
EscapeHatchProps,
createDataStorePredicate,
getOverrideProps,
useDataStoreBinding,
} from \\"@aws-amplify/ui-react/internal\\";
import { User, UserPreferences } from \\"../models\\";
import {
Button,
Collection,
Expand Down Expand Up @@ -1058,6 +1091,123 @@ export default function ListingCardCollection(
"
`;

exports[`amplify render tests collection should render if model name collides with component types 1`] = `
"/* eslint-disable */
import * as React from \\"react\\";
import { Flex as Flex0, FlexModel, Button as Button0 } from \\"../models\\";
import {
EscapeHatchProps,
createDataStorePredicate,
getOverrideProps,
useDataStoreBinding,
} from \\"@aws-amplify/ui-react/internal\\";
import { SortDirection, SortPredicate } from \\"@aws-amplify/datastore\\";
import {
Button,
Collection,
CollectionProps,
Flex,
} from \\"@aws-amplify/ui-react\\";
import { MyFlexProps } from \\"./MyFlex\\";

export type CollectionWithModelNameCollisionsProps = React.PropsWithChildren<
Partial<CollectionProps<any>> & {
backgroundColor?: String;
buttonColor?: Flex0;
buttonShape?: FlexModel;
items?: any[];
overrideItems?: (collectionItem: {
item: any;
index: number;
}) => MyFlexProps;
} & {
overrides?: EscapeHatchProps | undefined | null;
}
>;
export default function CollectionWithModelNameCollisions(
props: CollectionWithModelNameCollisionsProps
): React.ReactElement {
const {
backgroundColor,
buttonColor: buttonColorProp,
buttonShape: buttonShapeProp,
items,
overrideItems,
overrides,
...rest
} = props;
const buttonModelFilterObj = {
and: [
{ field: \\"age\\", operand: \\"10\\", operator: \\"gt\\" },
{ field: \\"lastName\\", operand: \\"L\\", operator: \\"beginsWith\\" },
],
};
const buttonModelFilter =
createDataStorePredicate<Button0>(buttonModelFilterObj);
const buttonModelPagination = {
sort: (s: SortPredicate<Button0>) => s.lastName(SortDirection.ASCENDING),
};
const buttonModelDataStore = useDataStoreBinding({
type: \\"collection\\",
model: Button0,
criteria: buttonModelFilter,
pagination: buttonModelPagination,
}).items;
const buttonModel = items !== undefined ? items : buttonModelDataStore;
const buttonColorFilterObj = {
field: \\"userID\\",
operand: \\"user@email.com\\",
operator: \\"eq\\",
};
const buttonColorFilter =
createDataStorePredicate<Flex0>(buttonColorFilterObj);
const buttonColorDataStore = useDataStoreBinding({
type: \\"collection\\",
model: Flex0,
criteria: buttonColorFilter,
}).items[0];
const buttonColor =
buttonColorProp !== undefined ? buttonColorProp : buttonColorDataStore;
const buttonShapeFilterObj = {
field: \\"userID\\",
operand: \\"user@email.com\\",
operator: \\"eq\\",
};
const buttonShapeFilter =
createDataStorePredicate<FlexModel>(buttonShapeFilterObj);
const buttonShapeDataStore = useDataStoreBinding({
type: \\"collection\\",
model: FlexModel,
criteria: buttonShapeFilter,
}).items[0];
const buttonShape =
buttonShapeProp !== undefined ? buttonShapeProp : buttonShapeDataStore;
return (
/* @ts-ignore: TS2322 */
<Collection
type=\\"list\\"
items={buttonModel || []}
{...rest}
{...getOverrideProps(overrides, \\"CollectionWithModelNameCollisions\\")}
>
{(item, index) => (
<Flex
key={item.id}
{...(overrideItems && overrideItems({ item, index }))}
>
<Button
backgroundColor={buttonColor?.favoriteColor}
children={item.lastName}
{...(overrideItems && overrideItems({ item, index }))}
></Button>
</Flex>
)}
</Collection>
);
}
"
`;

exports[`amplify render tests collection should render nested query if model has a hasMany relationship 1`] = `
"/* eslint-disable */
import * as React from \\"react\\";
Expand Down Expand Up @@ -5981,6 +6131,7 @@ exports[`amplify render tests mutations supports all initial value binding types
Object {
"componentText": "/* eslint-disable */
import * as React from \\"react\\";
import { User } from \\"../models\\";
import {
EscapeHatchProps,
createDataStorePredicate,
Expand All @@ -5989,7 +6140,6 @@ import {
useDataStoreBinding,
useStateMutationAction,
} from \\"@aws-amplify/ui-react/internal\\";
import { User } from \\"../models\\";
import { useEffect } from \\"react\\";
import {
Button,
Expand Down Expand Up @@ -6283,12 +6433,12 @@ exports[`amplify render tests mutations supports invalid statement names for mut
Object {
"componentText": "/* eslint-disable */
import * as React from \\"react\\";
import { Listing } from \\"../models\\";
import {
EscapeHatchProps,
getOverrideProps,
useStateMutationAction,
} from \\"@aws-amplify/ui-react/internal\\";
import { Listing } from \\"../models\\";
import { Flex, FlexProps, Image, Text } from \\"@aws-amplify/ui-react\\";

export type CardAProps = React.PropsWithChildren<
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 { ImportCollection } from '../../imports';
import { ImportCollection, ImportSource } from '../../imports';
import { assertASTMatchesSnapshot } from '../__utils__';

function assertImportCollectionMatchesSnapshot(importCollection: ImportCollection) {
Expand Down Expand Up @@ -69,6 +69,22 @@ describe('ImportCollection', () => {
importCollection.addImport('@aws-amplify/ui-react', 'getOverrideProps');
assertImportCollectionMatchesSnapshot(importCollection);
});
test('model imports colliding with exisiting imports', () => {
const importCollection = new ImportCollection({
componentNameToTypeMap: { TextInput: 'TextField' },
hasAuthBindings: false,
requiredDataModels: [],
stateReferences: [],
});
importCollection.addImport(ImportSource.LOCAL_MODELS, 'TextField0');
importCollection.addImport(ImportSource.LOCAL_MODELS, 'TextField');
importCollection.addImport(ImportSource.LOCAL_MODELS, 'TestModel');
importCollection.addImport(ImportSource.LOCAL_MODELS, 'ButtonProps');
expect(importCollection.getMappedAlias(ImportSource.LOCAL_MODELS, 'TextField')).toEqual('TextField1');
expect(importCollection.getMappedAlias(ImportSource.LOCAL_MODELS, 'TextField0')).toEqual('TextField0');
expect(importCollection.getMappedAlias(ImportSource.LOCAL_MODELS, 'TestModel')).toEqual('TestModel');
expect(importCollection.getMappedAlias(ImportSource.LOCAL_MODELS, 'ButtonProps')).toEqual('ButtonProps0');
});
});

test('mergeCollections', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ describe('amplify render tests', () => {
const { componentText } = generateWithAmplifyRenderer('authorCollectionComponent');
expect(componentText).toMatchSnapshot();
});
it('should render if model name collides with component types', () => {
const { componentText } = generateWithAmplifyRenderer('collectionWithModelNameCollisions');
expect(componentText).toMatchSnapshot();
});
});

describe('complex examples', () => {
Expand Down Expand Up @@ -594,6 +598,9 @@ describe('amplify render tests', () => {
it('supports bindings with reserved keywords', () => {
expect(generateWithAmplifyRenderer('bindings/data/dataBindingNamedClass').componentText).toMatchSnapshot();
});
it('supports model with conflicting component type', () => {
expect(generateWithAmplifyRenderer('bindings/data/dataBindingNamedFlex').componentText).toMatchSnapshot();
});
});
});
});
12 changes: 12 additions & 0 deletions packages/codegen-ui-react/lib/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,15 @@
limitations under the License.
*/
export const lowerCaseFirst = (input: string) => input.charAt(0).toLowerCase() + input.slice(1);

export const createUniqueName = (name: string, isNameUsed: (input: string) => boolean) => {
if (!isNameUsed(name)) {
return name;
}
let count = 0;
const prospectiveNewName = name;
while (isNameUsed(prospectiveNewName + count)) {
count += 1;
}
return prospectiveNewName + count;
};
Loading