Skip to content

Commit

Permalink
fix: prevent @index/@PrimaryKey from referencing itself (#8561)
Browse files Browse the repository at this point in the history
This commit prevents the sortKeyFields on @Index and @PrimaryKey
from including the field the directive is defined on.

Co-authored-by: Colin Ihrig <colihrig@amazon.com>
  • Loading branch information
cjihrig and cjihrig-aws committed Oct 27, 2021
1 parent bf4fb0e commit fc5a134
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 1 deletion.
Expand Up @@ -108,6 +108,22 @@ test('throws if @index uses a sort key field that is a non-scalar', () => {
}).toThrow(`The sort key of index 'wontwork' on type 'Test.email' cannot be a non-scalar.`);
});

test('throws if @index refers to itself', () => {
const schema = `
type Test @model {
id: ID! @index(name: "wontwork", sortKeyFields: ["id"])
email: String
}`;

const transformer = new GraphQLTransform({
transformers: [new ModelTransformer(), new IndexTransformer()],
});

expect(() => {
transformer.transform(schema);
}).toThrow(`@index field 'id' cannot reference itself.`);
});

test('@index with multiple sort keys adds a query field and GSI correctly', () => {
const inputSchema = `
type Test @model {
Expand Down
Expand Up @@ -124,6 +124,22 @@ test('throws if @primaryKey uses a sort key field that is a non-scalar', () => {
}).toThrow(`The primary key's sort key on type 'Test.email' cannot be a non-scalar.`);
});

test('throws if @primaryKey refers to itself', () => {
const schema = `
type Test @model {
id: ID! @primaryKey(sortKeyFields: ["id"])
email: String
}`;

const transformer = new GraphQLTransform({
transformers: [new ModelTransformer(), new PrimaryKeyTransformer()],
});

expect(() => {
transformer.transform(schema);
}).toThrow(`@primaryKey field 'id' cannot reference itself.`);
});

test('handles sortKeyFields being a string instead of an array', () => {
const schema = `
type NonScalar {
Expand Down
Expand Up @@ -17,6 +17,7 @@ import { isScalarOrEnum } from 'graphql-transformer-common';
import { appendSecondaryIndex, constructSyncVTL, updateResolversForIndex } from './resolvers';
import { addKeyConditionInputs, ensureQueryField, updateMutationConditionInput } from './schema';
import { IndexDirectiveConfiguration } from './types';
import { validateNotSelfReferencing } from './utils';

const directiveName = 'index';
const directiveDefinition = `
Expand Down Expand Up @@ -87,6 +88,9 @@ export class IndexTransformer extends TransformerPluginBase {

function validate(config: IndexDirectiveConfiguration, ctx: TransformerContextProvider): void {
const { name, object, field, sortKeyFields } = config;

validateNotSelfReferencing(config);

const modelDirective = object.directives!.find(directive => {
return directive.name.value === 'model';
});
Expand Down
Expand Up @@ -24,6 +24,7 @@ import {
updateMutationConditionInput,
} from './schema';
import { PrimaryKeyDirectiveConfiguration } from './types';
import { validateNotSelfReferencing } from './utils';

const directiveName = 'primaryKey';
const directiveDefinition = `
Expand Down Expand Up @@ -97,6 +98,9 @@ export class PrimaryKeyTransformer extends TransformerPluginBase {

function validate(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): void {
const { object, field, sortKeyFields } = config;

validateNotSelfReferencing(config);

const modelDirective = object.directives!.find(directive => {
return directive.name.value === 'model';
});
Expand Down
14 changes: 13 additions & 1 deletion packages/amplify-graphql-index-transformer/src/utils.ts
@@ -1,6 +1,7 @@
import { InvalidDirectiveError } from '@aws-amplify/graphql-transformer-core';
import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
import { plurality } from 'graphql-transformer-common';
import { PrimaryKeyDirectiveConfiguration } from './types';
import { IndexDirectiveConfiguration, PrimaryKeyDirectiveConfiguration } from './types';

export function lookupResolverName(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider, op: string): string | null {
const { object, modelDirective } = config;
Expand Down Expand Up @@ -36,3 +37,14 @@ export function lookupResolverName(config: PrimaryKeyDirectiveConfiguration, ctx

return resolverName;
}

export function validateNotSelfReferencing(config: IndexDirectiveConfiguration | PrimaryKeyDirectiveConfiguration) {
const { directive, field, sortKeyFields } = config;
const fieldName = field.name.value;

for (const sortKeyField of sortKeyFields) {
if (sortKeyField === fieldName) {
throw new InvalidDirectiveError(`@${directive.name.value} field '${fieldName}' cannot reference itself.`);
}
}
}

0 comments on commit fc5a134

Please sign in to comment.