in packages/graphql-key-transformer/src/KeyTransformer.ts [995:1321]
operation: str('Query'),
limit: ref('limit'),
query: ref(ResourceConstants.SNIPPETS.ModelQueryExpression),
index: str(index),
}),
),
ifElse(
raw(`!$util.isNull($ctx.args.sortDirection)
&& $ctx.args.sortDirection == "DESC"`),
set(ref(`${requestVariable}.scanIndexForward`), bool(false)),
set(ref(`${requestVariable}.scanIndexForward`), bool(true)),
),
iff(ref('context.args.nextToken'), set(ref(`${requestVariable}.nextToken`), ref('context.args.nextToken')), true),
iff(
ref('context.args.filter'),
set(ref(`${requestVariable}.filter`), ref('util.parseJson("$util.transform.toDynamoDBFilterExpression($ctx.args.filter)")')),
true,
),
raw(`$util.toJson($${requestVariable})`),
]),
),
ResponseMappingTemplate: print(
compoundExpression([
iff(ref('ctx.error'), raw('$util.error($ctx.error.message, $ctx.error.type)')),
raw('$util.toJson($ctx.result)'),
]),
),
});
}
function setQuerySnippet(definition: ObjectTypeDefinitionNode, directive: DirectiveNode, ctx: TransformerContext, isListResolver: boolean) {
const args: KeyArguments = getDirectiveArguments(directive);
const keys = args.fields;
const keyTypes = keys.map(k => {
const field = definition.fields.find(f => f.name.value === k);
return attributeTypeFromType(field.type, ctx);
});
const expressions: Expression[] = [];
// if @key has only Hash key then we've to add sortDirection validation to the VTL as it will not work
// TODO: when we will have featureflags we can fix it by not generating sortDirection parameter at all for these operations
if (keys.length === 1) {
const sortDirectionValidation = iff(
raw(`!$util.isNull($ctx.args.sortDirection)`),
raw(`$util.error("sortDirection is not supported for List operations without a Sort key defined.", "InvalidArgumentsError")`),
);
expressions.push(sortDirectionValidation);
} else if (isListResolver === true && keys.length >= 1) {
// We only need this check for List queries, and not for @key queries
const sortDirectionValidation = iff(
and([raw(`$util.isNull($ctx.args.${keys[0]})`), raw(`!$util.isNull($ctx.args.sortDirection)`)]),
raw(`$util.error("When providing argument 'sortDirection' you must also provide argument '${keys[0]}'.", "InvalidArgumentsError")`),
);
expressions.push(sortDirectionValidation);
}
expressions.push(
set(ref(ResourceConstants.SNIPPETS.ModelQueryExpression), obj({})),
applyKeyExpressionForCompositeKey(keys, keyTypes, ResourceConstants.SNIPPETS.ModelQueryExpression),
);
return block(`Set query expression for @key`, expressions);
}
function addHashField(
definition: ObjectTypeDefinitionNode,
args: KeyArguments,
elems: InputValueDefinitionNode[],
): InputValueDefinitionNode[] {
let hashFieldName = args.fields[0];
const hashField = definition.fields.find(field => field.name.value === hashFieldName);
const hashKey = makeInputValueDefinition(hashFieldName, makeNamedType(getBaseType(hashField.type)));
return [hashKey, ...elems];
}
function addSimpleSortKey(
ctx: TransformerContext,
definition: ObjectTypeDefinitionNode,
args: KeyArguments,
elems: InputValueDefinitionNode[],
): InputValueDefinitionNode[] {
let sortKeyName = args.fields[1];
const sortField = definition.fields.find(field => field.name.value === sortKeyName);
const baseType = getBaseType(sortField.type);
const resolvedTypeIfEnum = (ctx.getType(baseType) as EnumTypeDefinitionNode) ? 'String' : undefined;
const resolvedType = resolvedTypeIfEnum ? resolvedTypeIfEnum : baseType;
const hashKey = makeInputValueDefinition(sortKeyName, makeNamedType(ModelResourceIDs.ModelKeyConditionInputTypeName(resolvedType)));
return [hashKey, ...elems];
}
function addCompositeSortKey(
definition: ObjectTypeDefinitionNode,
args: KeyArguments,
elems: InputValueDefinitionNode[],
): InputValueDefinitionNode[] {
let sortKeyNames = args.fields.slice(1);
const compositeSortKeyName = toCamelCase(sortKeyNames);
const hashKey = makeInputValueDefinition(
compositeSortKeyName,
makeNamedType(ModelResourceIDs.ModelCompositeKeyConditionInputTypeName(definition.name.value, toUpper(args.name || 'Primary'))),
);
return [hashKey, ...elems];
}
function joinSnippets(lines: string[]): string {
return lines.join('\n');
}
// QueryMap doesnt Support Composite Keys
function setSyncQueryMapSnippet(directive: DirectiveNode, isTable: boolean) {
const args: KeyArguments = getDirectiveArguments(directive);
const keys = args.fields;
const expressions: Expression[] = [];
const index: String = isTable ? 'dbTable' : args.name;
let key: String = '';
if (keys.length === 1) {
key = `${keys[0]}+null`;
} else if (keys.length > 1) {
key = keys.join('+');
}
expressions.push(raw(`$util.qr($QueryMap.put('${key}' , '${index}'))`), raw(`$util.qr($PkMap.put('${keys[0]}' , '${index}'))`));
return block(`Set query expression for @key`, expressions);
}
function setSyncQueryFilterSnippet() {
const expressions: Expression[] = [];
expressions.push(
compoundExpression([
set(ref('filterArgsMap'), ref('ctx.args.filter.get("and")')),
ifElse(
raw(`!$util.isNullOrEmpty($filterArgsMap) && ($util.isNull($ctx.args.lastSync) || $ctx.args.lastSync == 0)`),
compoundExpression([
set(ref('json'), raw(`$filterArgsMap`)),
forEach(ref('item'), ref(`json`), [
set(ref('ind'), ref('foreach.index')),
forEach(ref('entry'), ref('item.entrySet()'), [
iff(
raw(`$ind == 0 && !$util.isNullOrEmpty($entry.value.eq) && !$util.isNullOrEmpty($PkMap.get($entry.key))`),
compoundExpression([
set(ref('pk'), ref('entry.key')),
set(ref('scan'), bool(false)),
raw('$util.qr($ctx.args.put($pk,$entry.value.eq))'),
set(ref('index'), ref('PkMap.get($pk)')),
]),
),
ifElse(
raw('$ind == 1 && !$util.isNullOrEmpty($pk) && !$util.isNullOrEmpty($QueryMap.get("${pk}+$entry.key"))'),
compoundExpression([
set(ref('sk'), ref('entry.key')),
raw('$util.qr($ctx.args.put($sk,$entry.value))'),
set(ref('index'), ref('QueryMap.get("${pk}+$sk")')),
]),
iff(raw('$ind > 0'), qref('$filterMap.put($entry.key,$entry.value)')),
),
]),
]),
]),
set(ref(`filterMap`), raw(`$ctx.args.filter`)),
),
]),
);
return block(`Set query expression for @key`, expressions);
}
function generateSyncResolverInit() {
const expressions: Expression[] = [];
expressions.push(
set(ref('index'), str('')),
set(ref('scan'), bool(true)),
set(ref('filterMap'), obj({})),
set(ref('QueryMap'), obj({})),
set(ref('PkMap'), obj({})),
set(ref('filterArgsMap'), obj({})),
);
return block(`Set map initialization for @key`, expressions);
}
function setSyncKeyExpressionForHashKey(queryExprReference: string) {
const expressions: Expression[] = [];
expressions.push(
set(ref(ResourceConstants.SNIPPETS.ModelQueryExpression), obj({})),
iff(
raw(`!$util.isNull($pk)`),
compoundExpression([
set(ref(`${queryExprReference}.expression`), str(`#pk = :pk`)),
set(ref(`${queryExprReference}.expressionNames`), obj({ [`#pk`]: str('$pk') })),
set(
ref(`${queryExprReference}.expressionValues`),
obj({ [`:pk`]: ref('util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.get($pk)))') }),
),
]),
),
);
return block(`Set Primary Key initialization @key`, expressions);
}
function setSyncKeyExpressionForRangeKey(queryExprReference: string) {
return block('Applying Key Condition', [
iff(
raw(`!$util.isNull($ctx.args.get($sk)) && !$util.isNull($ctx.args.get($sk).beginsWith)`),
compoundExpression([
set(ref(`${queryExprReference}.expression`), raw(`"$${queryExprReference}.expression AND begins_with(#sortKey, :sortKey)"`)),
qref(`$${queryExprReference}.expressionNames.put("#sortKey", $sk)`),
qref(
`$${queryExprReference}.expressionValues.put(":sortKey", $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.get($sk).beginsWith)))`,
),
]),
),
iff(
raw(`!$util.isNull($ctx.args.get($sk)) && !$util.isNull($ctx.args.get($sk).between)`),
compoundExpression([
set(
ref(`${queryExprReference}.expression`),
raw(`"$${queryExprReference}.expression AND #sortKey BETWEEN :sortKey0 AND :sortKey1"`),
),
qref(`$${queryExprReference}.expressionNames.put("#sortKey", $sk)`),
qref(
`$${queryExprReference}.expressionValues.put(":sortKey", $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.get($sk).between[0])))`,
),
qref(
`$${queryExprReference}.expressionValues.put(":sortKey", $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.get($sk).between[1])))`,
),
]),
),
iff(
raw(`!$util.isNull($ctx.args.get($sk)) && !$util.isNull($ctx.args.get($sk).eq)`),
compoundExpression([
set(ref(`${queryExprReference}.expression`), raw(`"$${queryExprReference}.expression AND #sortKey = :sortKey"`)),
qref(`$${queryExprReference}.expressionNames.put("#sortKey", $sk)`),
qref(
`$${queryExprReference}.expressionValues.put(":sortKey", $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.get($sk).eq)))`,
),
]),
),
iff(
raw(`!$util.isNull($ctx.args.get($sk)) && !$util.isNull($ctx.args.get($sk).lt)`),
compoundExpression([
set(ref(`${queryExprReference}.expression`), raw(`"$${queryExprReference}.expression AND #sortKey < :sortKey"`)),
qref(`$${queryExprReference}.expressionNames.put("#sortKey", $sk)`),
qref(
`$${queryExprReference}.expressionValues.put(":sortKey", $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.get($sk).lt)))`,
),
]),
),
iff(
raw(`!$util.isNull($ctx.args.get($sk)) && !$util.isNull($ctx.args.get($sk).le)`),
compoundExpression([
set(ref(`${queryExprReference}.expression`), raw(`"$${queryExprReference}.expression AND #sortKey <= :sortKey"`)),
qref(`$${queryExprReference}.expressionNames.put("#sortKey", $sk)`),
qref(
`$${queryExprReference}.expressionValues.put(":sortKey", $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.get($sk).le)))`,
),
]),
),
iff(
raw(`!$util.isNull($ctx.args.get($sk)) && !$util.isNull($ctx.args.get($sk).gt)`),
compoundExpression([
set(ref(`${queryExprReference}.expression`), raw(`"$${queryExprReference}.expression AND #sortKey > :sortKey"`)),
qref(`$${queryExprReference}.expressionNames.put("#sortKey", $sk)`),
qref(
`$${queryExprReference}.expressionValues.put(":sortKey", $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.get($sk).gt)))`,
),
]),
),
iff(
raw(`!$util.isNull($ctx.args.get($sk)) && !$util.isNull($ctx.args.get($sk).ge)`),
compoundExpression([
set(ref(`${queryExprReference}.expression`), raw(`"$${queryExprReference}.expression AND #sortKey >= :sortKey"`)),
qref(`$${queryExprReference}.expressionNames.put("#sortKey", $sk)`),
qref(
`$${queryExprReference}.expressionValues.put(":sortKey", $util.parseJson($util.dynamodb.toDynamoDBJson($ctx.args.get($sk).ge)))`,
),
]),
),
]);
}
function makeSyncQueryResolver() {
const requestVariable = 'QueryRequest';
const expressions: Expression[] = [];
expressions.push(
ifElse(
raw('!$scan'),
compoundExpression([
set(ref('limit'), ref(`util.defaultIfNull($context.args.limit, ${ResourceConstants.DEFAULT_PAGE_LIMIT})`)),
set(
ref(requestVariable),
obj({
version: str(RESOLVER_VERSION_ID),
operation: str('Sync'),
limit: ref('limit'),
query: ref(ResourceConstants.SNIPPETS.ModelQueryExpression),
}),
),
ifElse(
raw(`!$util.isNull($ctx.args.sortDirection)
&& $ctx.args.sortDirection == "DESC"`),
set(ref(`${requestVariable}.scanIndexForward`), bool(false)),
set(ref(`${requestVariable}.scanIndexForward`), bool(true)),
),
iff(ref('context.args.nextToken'), set(ref(`${requestVariable}.nextToken`), ref('context.args.nextToken')), true),
iff(
raw('!$util.isNullOrEmpty($filterMap)'),
set(ref(`${requestVariable}.filter`), ref('util.parseJson($util.transform.toDynamoDBFilterExpression($filterMap))')),
),
iff(raw(`$index != "dbTable"`), set(ref(`${requestVariable}.index`), ref('index'))),
raw(`$util.toJson($${requestVariable})`),
]),
DynamoDBMappingTemplate.syncItem({
filter: ifElse(
raw('!$util.isNullOrEmpty($ctx.args.filter)'),
ref('util.transform.toDynamoDBFilterExpression($ctx.args.filter)'),
nul(),
),
limit: ref(`util.defaultIfNull($ctx.args.limit, ${ResourceConstants.DEFAULT_SYNC_QUERY_PAGE_LIMIT})`),
lastSync: ref('util.toJson($util.defaultIfNull($ctx.args.lastSync, null))'),
nextToken: ref('util.toJson($util.defaultIfNull($ctx.args.nextToken, null))'),
}),
),
);
return block(` Set query expression for @key`, expressions);
}
function constructSyncResolver(directive: DirectiveNode, ctx: TransformerContext, syncResolver: any, isTable: boolean) {
if (ctx.metadata.has(ResourceConstants.SNIPPETS.SyncResolverKey)) {
const resolverMap = ctx.metadata.get(ResourceConstants.SNIPPETS.SyncResolverKey);
if (resolverMap.has(syncResolver)) {