Skip to content

Commit

Permalink
feat: support between predicates (#947)
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 committed Mar 20, 2023
1 parent 79daae2 commit fa0daaf
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ export type CollectionOfCustomButtonsProps = React.PropsWithChildren<
width?: Number;
backgroundColor?: String;
buttonColor?: UserPreferences;
buttonEnabled?: UserPreferences;
items?: any[];
overrideItems?: (collectionItem: {
item: any;
Expand All @@ -815,6 +816,7 @@ export default function CollectionOfCustomButtons(
width,
backgroundColor,
buttonColor: buttonColorProp,
buttonEnabled: buttonEnabledProp,
items,
overrideItems,
overrides,
Expand All @@ -824,6 +826,12 @@ export default function CollectionOfCustomButtons(
and: [
{ field: \\"age\\", operand: \\"10\\", operator: \\"gt\\" },
{ field: \\"lastName\\", operand: \\"L\\", operator: \\"beginsWith\\" },
{
and: [
{ field: \\"date\\", operator: \\"ge\\", operand: \\"2022-03-10\\" },
{ field: \\"date\\", operator: \\"le\\", operand: \\"2023-03-11\\" },
],
},
],
};
const buttonUserFilter = createDataStorePredicate<User>(buttonUserFilterObj);
Expand Down Expand Up @@ -854,6 +862,24 @@ export default function CollectionOfCustomButtons(
}).items[0];
const buttonColor =
buttonColorProp !== undefined ? buttonColorProp : buttonColorDataStore;
const buttonEnabledFilterObj = {
and: [
{ field: \\"date\\", operator: \\"ge\\", operand: \\"2022-03-10\\" },
{ field: \\"date\\", operator: \\"le\\", operand: \\"2023-03-11\\" },
],
};
const buttonEnabledFilter = createDataStorePredicate<UserPreferences>(
buttonEnabledFilterObj
);
const buttonEnabledDataStore = useDataStoreBinding({
type: \\"collection\\",
model: UserPreferences,
criteria: buttonEnabledFilter,
}).items[0];
const buttonEnabled =
buttonEnabledProp !== undefined
? buttonEnabledProp
: buttonEnabledDataStore;
return (
/* @ts-ignore: TS2322 */
<Collection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
getBreakpoints,
isValidVariableName,
InternalError,
resolveBetweenPredicateToMultiplePredicates,
} from '@aws-amplify/codegen-ui';
import { EOL } from 'os';
import ts, {
Expand Down Expand Up @@ -1437,6 +1438,11 @@ export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer

private predicateToObjectLiteralExpression(predicate: StudioComponentPredicate): ObjectLiteralExpression {
const { operandType, ...filteredPredicate } = predicate;

if (filteredPredicate.operator === 'between') {
return this.predicateToObjectLiteralExpression(resolveBetweenPredicateToMultiplePredicates(predicate));
}

const objectAssignments = Object.entries(filteredPredicate).map(([key, value]) => {
if (key === 'and' || key === 'or') {
return factory.createPropertyAssignment(
Expand Down
16 changes: 16 additions & 0 deletions packages/codegen-ui/example-schemas/collectionWithBinding.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@
"operator": "eq"
}
}
},
"buttonEnabled": {
"type": "Data",
"bindingProperties": {
"model": "UserPreferences",
"predicate": {
"field": "date",
"operand": "2022-03-10<Amplify.OperandDelimiter>2023-03-11",
"operator": "between"
}
}
}
},
"collectionProperties": {
Expand All @@ -51,6 +62,11 @@
"field": "lastName",
"operand": "L",
"operator": "beginsWith"
},
{
"field": "date",
"operand": "2022-03-10<Amplify.OperandDelimiter>2023-03-11",
"operator": "between"
}
]
}
Expand Down
42 changes: 42 additions & 0 deletions packages/codegen-ui/lib/__tests__/renderer-helper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
isStudioComponentWithVariants,
isEventPropertyBinding,
isAuthProperty,
resolveBetweenPredicateToMultiplePredicates,
OPERAND_DELIMITER,
} from '../renderer-helper';

describe('render-helper', () => {
Expand Down Expand Up @@ -198,4 +200,44 @@ describe('render-helper', () => {
Object.values(otherTypes).forEach((otherType) => expect(isAuthProperty(otherType)).toBeFalsy());
});
});

describe('resolveBetweenPredicateToMultiplePredicates', () => {
it('should throw if not 2 operands', () => {
expect(() =>
resolveBetweenPredicateToMultiplePredicates({
field: 'age',
operator: 'between',
operand: '1',
}),
).toThrow();
expect(() =>
resolveBetweenPredicateToMultiplePredicates({
field: 'age',
operator: 'between',
}),
).toThrow();
expect(() =>
resolveBetweenPredicateToMultiplePredicates({
field: 'age',
operator: 'between',
operand: [1, 2, 3].join(OPERAND_DELIMITER),
}),
).toThrow();
});

it('should resolve predicate', () => {
const betweenPredicateOperands = ['1', '2'];
const betweenPredicate = {
field: 'age',
operator: 'between',
operand: betweenPredicateOperands.join(OPERAND_DELIMITER),
};
expect(resolveBetweenPredicateToMultiplePredicates(betweenPredicate)).toStrictEqual({
and: [
{ field: betweenPredicate.field, operator: 'ge', operand: betweenPredicateOperands[0] },
{ field: betweenPredicate.field, operator: 'le', operand: betweenPredicateOperands[1] },
],
});
});
});
});
27 changes: 27 additions & 0 deletions packages/codegen-ui/lib/renderer-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
import { InvalidInputError } from './errors';
import {
StudioComponent,
StudioComponentAuthProperty,
Expand All @@ -24,6 +25,7 @@ import {
StudioComponentPropertyBinding,
StudioComponentProperty,
StudioComponentSlotBinding,
StudioComponentPredicate,
} from './types';
import { breakpointSizes, BreakpointSizeType } from './utils/breakpoint-utils';

Expand Down Expand Up @@ -101,3 +103,28 @@ export function isEventPropertyBinding(
export function isSlotBinding(prop: StudioComponentPropertyBinding): prop is StudioComponentSlotBinding {
return typeof prop === 'object' && 'type' in prop && prop.type === 'Amplify.Slot';
}

/**
For StudioComponentPredicate, there are cases when
we want multiple operands. This string indicates the end of
one operand and start of another.
Warning: if you change this, saved schemas may break.
*/
export const OPERAND_DELIMITER = '<Amplify.OperandDelimiter>';

export function resolveBetweenPredicateToMultiplePredicates(
betweenPredicate: StudioComponentPredicate,
): StudioComponentPredicate {
const operands = betweenPredicate.operand?.split(OPERAND_DELIMITER);

if (!operands || operands.length !== 2) {
throw new InvalidInputError('There must be 2 operands for a `between` predicate');
}

return {
and: [
{ field: betweenPredicate.field, operator: 'ge', operand: operands[0] },
{ field: betweenPredicate.field, operator: 'le', operand: operands[1] },
],
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const EXPECTED_SUCCESSFUL_CASES = new Set([
'CollectionWithBindingItemsName',
'CompositeDogCard',
'CollectionWithCompositeKeysAndRelationships',
'CollectionWithBetweenPredicate',
'PaginatedCollection',
'SearchableCollection',
'CustomParent',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,15 @@ describe('Generated Components', () => {
cy.contains('Toys: stick, ball');
});
});

it('Supports between predicates', () => {
cy.get('#collectionWithBetweenPredicate').within(() => {
cy.contains('Real');
cy.contains('Last');
cy.contains('Another').should('not.exist');
cy.contains('Too Young').should('not.exist');
});
});
});

describe('Default Value', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import {
ComponentWithAuthBinding,
DataBindingNamedClass,
CollectionWithCompositeKeysAndRelationships,
CollectionWithBetweenPredicate,
} from './ui-components'; // eslint-disable-line import/extensions
import { initializeAuthMockData } from './mock-utils';

Expand Down Expand Up @@ -420,6 +421,7 @@ export default function ComponentTests() {
};
}}
/>
<CollectionWithBetweenPredicate id="collectionWithBetweenPredicate" />
</div>
<div id="default-value">
<h2>Default Value</h2>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"id": "1234-5678-9010",
"componentType": "Collection",
"name": "CollectionWithBetweenPredicate",
"properties": {
"type": {
"value": "list"
},
"isPaginated": {
"value": true
},
"gap": {
"value": "1.5rem"
}
},
"bindingProperties": {},
"collectionProperties": {
"buttonUser": {
"model": "User",
"predicate": {
"and": [
{
"field": "age",
"operand": "5<Amplify.OperandDelimiter>71",
"operator": "between"
},
{
"field": "lastName",
"operand": "LUser0",
"operator": "ne"
}
]
}
}
},
"children": [
{
"componentType": "Flex",
"name": "MyFlex",
"properties": {},
"children": [
{
"componentType": "Button",
"name": "MyButton",
"properties": {
"children": {
"collectionBindingProperties": {
"property": "buttonUser",
"field": "firstName"
},
"defaultValue": "hspain@gmail.com"
}
}
}
]
}
],
"schemaVersion": "1.0"
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ export { default as PaginatedCollection } from './paginatedCollection.json';
export { default as SearchableCollection } from './searchableCollection.json';
export { default as SimpleUserCollection } from './simpleUserCollection.json';
export { default as CollectionWithCompositeKeysAndRelationships } from './collectionWithCompositeKeysAndRelationships.json';
export { default as CollectionWithBetweenPredicate } from './collectionWithBetweenPredicate.json';

0 comments on commit fa0daaf

Please sign in to comment.