diff --git a/.changeset/ninety-ravens-destroy.md b/.changeset/ninety-ravens-destroy.md new file mode 100644 index 000000000..15a6ca49d --- /dev/null +++ b/.changeset/ninety-ravens-destroy.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/data-schema': patch +--- + +fix bug with missing arrays and requireds in custom operation arguments diff --git a/packages/data-schema/__tests__/ModelSchema.test.ts b/packages/data-schema/__tests__/ModelSchema.test.ts index 684330d24..4e4a25c19 100644 --- a/packages/data-schema/__tests__/ModelSchema.test.ts +++ b/packages/data-schema/__tests__/ModelSchema.test.ts @@ -151,3 +151,261 @@ describe('Lambda resource access', () => { ]); }); }); + +describe('custom operation argument inputs', () => { + + it('when the argument is an array of custom type refs the input is an array refs', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testType: a.customType({ + testField: a.string().required(), + }), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testType').array(), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is a required custom type ref the input is a required ref', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testType: a.customType({ + testField: a.string().required(), + }), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testType').required(), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is a required array of custom type refs the input is a required array', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testType: a.customType({ + testField: a.string().required(), + }), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testType').array().required(), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is an array of required custom type refs the input is an array of required refs', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testType: a.customType({ + testField: a.string().required(), + }), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testType').required().array(), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is a required array of required custom type refs the input is a required array of required refs', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testType: a.customType({ + testField: a.string().required(), + }), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testType').required().array().required(), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is an array of enum the input is an array', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testEnum: a.enum(['test1', 'test2']), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testEnum').array(), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is a required enum the input is required', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testEnum: a.enum(['test1', 'test2']), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testEnum').required(), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is a required array of enum refs the input is a required array', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testEnum: a.enum(['test1', 'test2']), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testEnum').array().required(), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is an array of required enum refs the input is a array of required refs', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testEnum: a.enum(['test1', 'test2']), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testEnum').required().array(), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is a required array of required enum refs the input is a required array of required refs', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testEnum: a.enum(['test1', 'test2']), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testEnum').required().array().required(), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is a custom type with an array field the input types field is an array', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testType: a.customType({ + testField: a.string().array(), + }), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testType'), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + + it('when the argument is a custom type with a required field the input types field is required', () => { + const fn1 = defineFunctionStub({}); + + const schema = a + .schema({ + testType: a.customType({ + testField: a.string().required(), + }), + + testMutation: a + .mutation() + .arguments({ + testArgument: a.ref('testType'), + }) + .returns(a.string()) + .handler(a.handler.function(fn1)) + }) + .authorization((allow) => allow.owner()); + + expect(schema.transform().schema).toMatchSnapshot(); + }); + +}) diff --git a/packages/data-schema/__tests__/__snapshots__/ModelSchema.test.ts.snap b/packages/data-schema/__tests__/__snapshots__/ModelSchema.test.ts.snap index cd83da0f6..1e1d3821d 100644 --- a/packages/data-schema/__tests__/__snapshots__/ModelSchema.test.ts.snap +++ b/packages/data-schema/__tests__/__snapshots__/ModelSchema.test.ts.snap @@ -1,5 +1,165 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`custom operation argument inputs when the argument is a custom type with a required field the input types field is required 1`] = ` +"type testType +{ + testField: String! +} + +input testTypeInput { + testField: String! +} + +type Mutation { + testMutation(testArgument: testTypeInput): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is a custom type with an array field the input types field is an array 1`] = ` +"type testType +{ + testField: [String] +} + +input testTypeInput { + testField: [String] +} + +type Mutation { + testMutation(testArgument: testTypeInput): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is a required array of custom type refs the input is a required array 1`] = ` +"type testType +{ + testField: String! +} + +input testTypeInput { + testField: String! +} + +type Mutation { + testMutation(testArgument: [testTypeInput]!): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is a required array of enum refs the input is a required array 1`] = ` +"enum testEnum { + test1 + test2 +} + +type Mutation { + testMutation(testArgument: [testEnum]!): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is a required array of required custom type refs the input is a required array of required refs 1`] = ` +"type testType +{ + testField: String! +} + +input testTypeInput { + testField: String! +} + +type Mutation { + testMutation(testArgument: [testTypeInput!]!): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is a required array of required enum refs the input is a required array of required refs 1`] = ` +"enum testEnum { + test1 + test2 +} + +type Mutation { + testMutation(testArgument: [testEnum!]!): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is a required custom type ref the input is a required ref 1`] = ` +"type testType +{ + testField: String! +} + +input testTypeInput { + testField: String! +} + +type Mutation { + testMutation(testArgument: testTypeInput!): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is a required enum the input is required 1`] = ` +"enum testEnum { + test1 + test2 +} + +type Mutation { + testMutation(testArgument: testEnum!): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is an array of custom type refs the input is an array refs 1`] = ` +"type testType +{ + testField: String! +} + +input testTypeInput { + testField: String! +} + +type Mutation { + testMutation(testArgument: [testTypeInput]): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is an array of enum the input is an array 1`] = ` +"enum testEnum { + test1 + test2 +} + +type Mutation { + testMutation(testArgument: [testEnum]): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is an array of required custom type refs the input is an array of required refs 1`] = ` +"type testType +{ + testField: String! +} + +input testTypeInput { + testField: String! +} + +type Mutation { + testMutation(testArgument: [testTypeInput!]): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + +exports[`custom operation argument inputs when the argument is an array of required enum refs the input is a array of required refs 1`] = ` +"enum testEnum { + test1 + test2 +} + +type Mutation { + testMutation(testArgument: [testEnum!]): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}]) +}" +`; + exports[`empty model auth inherits global auth 1`] = ` "type widget @model @auth(rules: [{allow: owner, ownerField: "owner"}]) { diff --git a/packages/data-schema/src/SchemaProcessor.ts b/packages/data-schema/src/SchemaProcessor.ts index b8b452f29..07ae6a935 100644 --- a/packages/data-schema/src/SchemaProcessor.ts +++ b/packages/data-schema/src/SchemaProcessor.ts @@ -1432,8 +1432,24 @@ function generateInputTypes( if (isRefField(argDef)) { const refType = getRefType(argDef.data.link, operationName); if (refType.type === 'CustomType') { + const { valueRequired, array, arrayRequired } = argDef.data; + const inputTypeName = `${argDef.data.link}Input`; - argDefinitions.push(`${argName}: ${inputTypeName}`); + let field = inputTypeName; + + if (valueRequired === true) { + field += '!'; + } + + if (array) { + field = `[${field}]`; + + if (arrayRequired === true) { + field += '!'; + } + } + + argDefinitions.push(`${argName}: ${field}`); // Process the input type if it hasn't been processed yet if (!processedTypes.has(inputTypeName)) { @@ -1450,7 +1466,23 @@ function generateInputTypes( }); } } else if (refType.type === 'Enum') { - argDefinitions.push(`${argName}: ${argDef.data.link}`); + const { valueRequired, array, arrayRequired } = argDef.data; + + let field = argDef.data.link; + + if (valueRequired === true) { + field += '!'; + } + + if (array) { + field = `[${field}]`; + + if (arrayRequired === true) { + field += '!'; + } + } + + argDefinitions.push(`${argName}: ${field}`); } else { throw new Error( `Unsupported reference type '${refType.type}' for argument '${argName}' in '${operationName}'. ` +