Skip to content

Commit

Permalink
rebase with latest main
Browse files Browse the repository at this point in the history
  • Loading branch information
sundersc committed Oct 24, 2022
1 parent 10fae0b commit 6d4acd3
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
FieldDefinitionNode,
InputObjectTypeDefinitionNode,
InputValueDefinitionNode,
ListValueNode,
ObjectTypeDefinitionNode,
} from 'graphql';
import {
Expand Down Expand Up @@ -143,6 +144,8 @@ type ModelTransformerOptions = {
SyncConfig?: SyncConfig;
};

const DEFAULT_ID_FIELD_NAME = 'id';

/**
* ModelTransformer
*/
Expand Down Expand Up @@ -504,13 +507,14 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
const dataSource = this.datasourceMap[type.name.value];
const resolverKey = `Update${generateResolverKey(typeName, fieldName)}`;
if (!this.resolverMap[resolverKey]) {
const hasCustomPrimaryKey = this.hasCustomPrimaryKey(type);
const resolver = ctx.resolvers.generateMutationResolver(
typeName,
fieldName,
resolverLogicalId,
dataSource,
MappingTemplate.s3MappingTemplateFromString(
generateUpdateRequestTemplate(typeName, isSyncEnabled),
generateUpdateRequestTemplate(typeName, isSyncEnabled, hasCustomPrimaryKey),
`${typeName}.${fieldName}.req.vtl`,
),
MappingTemplate.s3MappingTemplateFromString(
Expand Down Expand Up @@ -541,13 +545,14 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
const isSyncEnabled = ctx.isProjectUsingDataStore();
const dataSource = this.datasourceMap[type.name.value];
const resolverKey = `delete${generateResolverKey(typeName, fieldName)}`;
const hasCustomPrimaryKey = this.hasCustomPrimaryKey(type);
if (!this.resolverMap[resolverKey]) {
this.resolverMap[resolverKey] = ctx.resolvers.generateMutationResolver(
typeName,
fieldName,
resolverLogicalId,
dataSource,
MappingTemplate.s3MappingTemplateFromString(generateDeleteRequestTemplate(isSyncEnabled), `${typeName}.${fieldName}.req.vtl`),
MappingTemplate.s3MappingTemplateFromString(generateDeleteRequestTemplate(isSyncEnabled, hasCustomPrimaryKey), `${typeName}.${fieldName}.req.vtl`),
MappingTemplate.s3MappingTemplateFromString(
generateDefaultResponseMappingTemplate(isSyncEnabled, true),
`${typeName}.${fieldName}.res.vtl`,
Expand Down Expand Up @@ -625,12 +630,14 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
const dataSource = this.datasourceMap[type.name.value];
const resolverKey = `Sync${generateResolverKey(typeName, fieldName)}`;
if (!this.resolverMap[resolverKey]) {
const hasCustomPrimaryKey = this.hasCustomPrimaryKey(type);
const partitionKeyName = this.getPartitionKeyName(type);
this.resolverMap[resolverKey] = ctx.resolvers.generateQueryResolver(
typeName,
fieldName,
resolverLogicalId,
dataSource,
MappingTemplate.s3MappingTemplateFromString(generateSyncRequestTemplate(), `${typeName}.${fieldName}.req.vtl`),
MappingTemplate.s3MappingTemplateFromString(generateSyncRequestTemplate(hasCustomPrimaryKey, partitionKeyName), `${typeName}.${fieldName}.req.vtl`),
MappingTemplate.s3MappingTemplateFromString(
generateDefaultResponseMappingTemplate(isSyncEnabled),
`${typeName}.${fieldName}.res.vtl`,
Expand Down Expand Up @@ -879,12 +886,13 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
const resolverKey = `Create${generateResolverKey(typeName, fieldName)}`;
const modelIndexFields = type.fields!.filter(field => field.directives?.some(it => it.name.value === 'index')).map(it => it.name.value);
if (!this.resolverMap[resolverKey]) {
const hasCustomPrimaryKey = this.hasCustomPrimaryKey(type);
const resolver = ctx.resolvers.generateMutationResolver(
typeName,
fieldName,
resolverLogicalId,
dataSource,
MappingTemplate.s3MappingTemplateFromString(generateCreateRequestTemplate(type.name.value, modelIndexFields), `${typeName}.${fieldName}.req.vtl`),
MappingTemplate.s3MappingTemplateFromString(generateCreateRequestTemplate(type.name.value, modelIndexFields, hasCustomPrimaryKey), `${typeName}.${fieldName}.req.vtl`),
MappingTemplate.s3MappingTemplateFromString(
generateDefaultResponseMappingTemplate(isSyncEnabled, true),
`${typeName}.${fieldName}.res.vtl`,
Expand Down Expand Up @@ -1391,4 +1399,39 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
EnableDeletionProtection: false,
...options,
});

/**
* Returns true if the model contains a custom primary key.
* Custom Primary Key is a renamed partition key with at least one sort key.
* @param obj ObjectTypeDefinitionNode
* @returns a boolean
*/
private hasCustomPrimaryKey = (obj: ObjectTypeDefinitionNode): boolean => {
const primaryKeyField = obj.fields?.find(field => field.directives?.find(directive => directive.name.value === 'primaryKey'));
if (!primaryKeyField || primaryKeyField.name.value === DEFAULT_ID_FIELD_NAME) {
return false;
}

// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
const primaryKeyDirective = primaryKeyField.directives?.find(directive => directive.name.value === 'primaryKey')!;
const sortKeysArgument = primaryKeyDirective.arguments?.find(arg => arg.name.value === 'sortKeyFields');
if (!sortKeysArgument || (sortKeysArgument.value as ListValueNode)?.values.length === 0) {
return false;
}

return true;
}

/**
* Returns the field name of the partition key.
* @param obj ObjectTypeDefinitionNode
* @returns a string
*/
private getPartitionKeyName = (obj: ObjectTypeDefinitionNode): string => {
const primaryKeyField = obj.fields?.find(field => field.directives?.find(directive => directive.name.value === 'primaryKey'));
if (!primaryKeyField) {
return DEFAULT_ID_FIELD_NAME;
}
return primaryKeyField.name.value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { generateConditionSlot } from './common';
* Generates VTL template in update mutation
* @param modelName Name of the model
*/
export const generateUpdateRequestTemplate = (modelName: string, isSyncEnabled: boolean): string => {
export const generateUpdateRequestTemplate = (modelName: string, isSyncEnabled: boolean, hasCustomPrimaryKey: boolean): string => {
const objectKeyVariable = 'ctx.stash.metadata.modelObjectKey';
const keyFields: StringNode[] = [str('id')];
if (isSyncEnabled) {
Expand Down Expand Up @@ -133,6 +133,8 @@ export const generateUpdateRequestTemplate = (modelName: string, isSyncEnabled:
operation: str('UpdateItem'),
key: ref('Key'),
update: ref('update'),
hasCustomPrimaryKey: raw(`${hasCustomPrimaryKey}`),
populateGSIFields: raw(`${hasCustomPrimaryKey}`),
...(isSyncEnabled && { _version: ref('util.defaultIfNull($args.input["_version"], 0)') }),
}),
),
Expand All @@ -152,7 +154,7 @@ export const generateUpdateRequestTemplate = (modelName: string, isSyncEnabled:
* Generates VTL template in create mutation
* @param modelName Name of the model
*/
export const generateCreateRequestTemplate = (modelName: string, modelIndexFields: string[]): string => {
export const generateCreateRequestTemplate = (modelName: string, modelIndexFields: string[], hasCustomPrimaryKey: boolean): string => {
const statements: Expression[] = [
setArgs,
// Generate conditions
Expand Down Expand Up @@ -182,6 +184,8 @@ export const generateCreateRequestTemplate = (modelName: string, modelIndexField
operation: str('PutItem'),
attributeValues: methodCall(ref('util.dynamodb.toMapValues'), ref('mergedValues')),
condition: ref('condition'),
hasCustomPrimaryKey: raw(`${hasCustomPrimaryKey}`),
populateGSIFields: raw(`${hasCustomPrimaryKey}`),
}),
),

Expand Down Expand Up @@ -256,14 +260,16 @@ export const generateCreateInitSlotTemplate = (modelConfig: ModelDirectiveConfig
* Generates VTL template in delete mutation
*
*/
export const generateDeleteRequestTemplate = (isSyncEnabled: boolean): string => {
export const generateDeleteRequestTemplate = (isSyncEnabled: boolean, hasCustomPrimaryKey: boolean): string => {
const statements: Expression[] = [
setArgs,
set(
ref('DeleteRequest'),
obj({
version: str('2018-05-29'),
operation: str('DeleteItem'),
hasCustomPrimaryKey: raw(`${hasCustomPrimaryKey}`),
populateGSIFields: raw(`${hasCustomPrimaryKey}`),
}),
),
ifElse(
Expand Down
105 changes: 58 additions & 47 deletions packages/amplify-graphql-model-transformer/src/resolvers/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import {
list,
forEach,
nul,
raw,
} from 'graphql-mapping-template';
import { ResourceConstants, setArgs } from 'graphql-transformer-common';
import { ResourceConstants, setArgs, SyncResourceIDs } from 'graphql-transformer-common';

const authFilter = ref('ctx.stash.authFilter');

/**
* Generate get query resolver template
* Generate get query resolver request template
*/
export const generateGetRequestTemplate = (): string => {
const statements: Expression[] = [
Expand Down Expand Up @@ -74,6 +76,9 @@ export const generateGetRequestTemplate = (): string => {
return printBlock('Get Request template')(compoundExpression(statements));
};

/**
* Generates the Get query response template
*/
export const generateGetResponseTemplate = (isSyncEnabled: boolean): string => {
const statements = new Array<Expression>();
if (isSyncEnabled) {
Expand All @@ -96,13 +101,16 @@ export const generateGetResponseTemplate = (isSyncEnabled: boolean): string => {
return printBlock('Get Response template')(compoundExpression(statements));
};

/**
* Generates the List query request template
*/
export const generateListRequestTemplate = (): string => {
const requestVariable = 'ListRequest';
const modelQueryObj = 'ctx.stash.modelQueryExpression';
const indexNameVariable = 'ctx.stash.metadata.index';
const expression = compoundExpression([
setArgs,
set(ref('limit'), methodCall(ref(`util.defaultIfNull`), ref('args.limit'), int(100))),
set(ref('limit'), methodCall(ref('util.defaultIfNull'), ref('args.limit'), int(100))),
set(
ref(requestVariable),
obj({
Expand All @@ -123,7 +131,7 @@ export const generateListRequestTemplate = (): string => {
not(isNullOrEmpty(ref('filter'))),
compoundExpression([
set(
ref(`filterExpression`),
ref('filterExpression'),
methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('filter'))),
),
iff(
Expand All @@ -137,7 +145,7 @@ export const generateListRequestTemplate = (): string => {
equals(methodCall(ref('filterExpression.expressionValues.size')), int(0)),
qref(methodCall(ref('filterExpression.remove'), str('expressionValues'))),
),
set(ref(`${requestVariable}.filter`), ref(`filterExpression`)),
set(ref(`${requestVariable}.filter`), ref('filterExpression')),
]),
),
]),
Expand All @@ -164,45 +172,48 @@ export const generateListRequestTemplate = (): string => {
return printBlock('List Request')(expression);
};

export const generateSyncRequestTemplate = (): string => {
return printBlock('Sync Request template')(
compoundExpression([
setArgs,
ifElse(
not(isNullOrEmpty(authFilter)),
compoundExpression([
set(ref('filter'), authFilter),
iff(not(isNullOrEmpty(ref('args.filter'))), set(ref('filter'), obj({ and: list([ref('filter'), ref('args.filter')]) }))),
]),
iff(not(isNullOrEmpty(ref('args.filter'))), set(ref('filter'), ref('args.filter'))),
),
iff(
not(isNullOrEmpty(ref('filter'))),
compoundExpression([
set(
ref(`filterExpression`),
methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('filter'))),
),
iff(
not(methodCall(ref('util.isNullOrBlank'), ref('filterExpression.expression'))),
compoundExpression([
iff(
equals(methodCall(ref('filterExpression.expressionValues.size')), int(0)),
qref(methodCall(ref('filterExpression.remove'), str('expressionValues'))),
),
set(ref('filter'), ref('filterExpression')),
]),
),
]),
),
obj({
version: str('2018-05-29'),
operation: str('Sync'),
filter: ifElse(ref('filter'), ref('util.toJson($filter)'), nul()),
limit: ref(`util.defaultIfNull($args.limit, ${ResourceConstants.DEFAULT_SYNC_QUERY_PAGE_LIMIT})`),
lastSync: ref('util.toJson($util.defaultIfNull($args.lastSync, null))'),
nextToken: ref('util.toJson($util.defaultIfNull($args.nextToken, null))'),
}),
]),
);
};
/**
* Generates the Sync query request template
*/
export const generateSyncRequestTemplate = (hasCustomPrimaryKey: boolean, partitionKeyName: string): string => printBlock('Sync Request template')(
compoundExpression([
setArgs,
ifElse(
not(isNullOrEmpty(authFilter)),
compoundExpression([
set(ref('filter'), authFilter),
iff(not(isNullOrEmpty(ref('args.filter'))), set(ref('filter'), obj({ and: list([ref('filter'), ref('args.filter')]) }))),
]),
iff(not(isNullOrEmpty(ref('args.filter'))), set(ref('filter'), ref('args.filter'))),
),
iff(
not(isNullOrEmpty(ref('filter'))),
compoundExpression([
set(
ref('filterExpression'),
methodCall(ref('util.parseJson'), methodCall(ref('util.transform.toDynamoDBFilterExpression'), ref('filter'))),
),
iff(
not(methodCall(ref('util.isNullOrBlank'), ref('filterExpression.expression'))),
compoundExpression([
iff(
equals(methodCall(ref('filterExpression.expressionValues.size')), int(0)),
qref(methodCall(ref('filterExpression.remove'), str('expressionValues'))),
),
set(ref('filter'), ref('filterExpression')),
]),
),
]),
),
obj({
version: str('2018-05-29'),
operation: str('Sync'),
filter: ifElse(ref('filter'), ref('util.toJson($filter)'), nul(), true),
limit: ref(`util.defaultIfNull($args.limit, ${ResourceConstants.DEFAULT_SYNC_QUERY_PAGE_LIMIT})`),
lastSync: ref('util.toJson($util.defaultIfNull($args.lastSync, null))'),
nextToken: ref('util.toJson($util.defaultIfNull($args.nextToken, null))'),
basePartitionKey: ifElse(ref('filter'), raw(`${hasCustomPrimaryKey ? `$filter.${partitionKeyName}` : 'null'}`), nul(), true),
deltaIndexName: str(SyncResourceIDs.syncGSIName),
}),
]),
);
Loading

0 comments on commit 6d4acd3

Please sign in to comment.