Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cyan-moose-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@aws-amplify/data-schema': patch
---

Support nested custom type references and enums with required arrays in custom operations
58 changes: 58 additions & 0 deletions packages/data-schema/__tests__/ModelSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,61 @@ describe('custom operation argument inputs', () => {
});

})

describe('custom mutations should handle array custom types', () => {
it('when the argument is a custom type with nested custom type containing an array field the input preserves nested structure', () => {
const fn1 = defineFunctionStub({});

const schema = a
.schema({
tag: a.enum(['one', 'two']),

innerType: a.customType({
items: a.string().array(),
tags: a.ref('tag').array().required()
}),

outerType: a.customType({
inner: a.ref('innerType').required(),
}),

testMutation: a
.mutation()
.arguments({
testArgument: a.ref('outerType'),
})
.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 inline nested custom type containing required array the input preserves nested structure', () => {
const fn1 = defineFunctionStub({});

const schema = a
.schema({
point: a.customType({
x: a.float(),
y: a.float(),
}),

testType: a.customType({
points: a.ref('point').array().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();
});
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,62 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`custom mutations should handle array custom types when the argument is a custom type with inline nested custom type containing required array the input preserves nested structure 1`] = `
"type point
{
x: Float
y: Float
}

type testType
{
points: [point]!
}

input pointInput {
x: Float
y: Float
}

input testTypeInput {
points: [pointInput]!
}

type Mutation {
testMutation(testArgument: testTypeInput!): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}])
}"
`;

exports[`custom mutations should handle array custom types when the argument is a custom type with nested custom type containing an array field the input preserves nested structure 1`] = `
"enum tag {
one
two
}

type innerType
{
items: [String]
tags: [tag]!
}

type outerType
{
inner: innerType!
}

input innerTypeInput {
items: [String]
tags: [tag]!
}

input outerTypeInput {
inner: innerTypeInput
}

type Mutation {
testMutation(testArgument: outerTypeInput): String @function(name: "FnTestMutation") @auth(rules: [{allow: owner, ownerField: "owner"}])
}"
`;

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
{
Expand Down
28 changes: 26 additions & 2 deletions packages/data-schema/src/SchemaProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,30 @@ const mergeCustomTypeAuthRules = (
}
};

const constructNonScalarFieldDefinition = (
nestedInputTypeName: string,
fieldDef: { data: { valueRequired?: boolean; array?: boolean; arrayRequired?: boolean } }
): { type: 'ref'; link: string; required?: boolean; array?: boolean; arrayRequired?: boolean } => {
const {valueRequired, array, arrayRequired} = fieldDef.data;

const customTypeData: { type: 'ref'; link: string; required?: boolean; array?: boolean; arrayRequired?: boolean } = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we define a type and use it here instead of typing this inline? Also, did you consider the existing types and see if you can reuse them?

type: 'ref',
link: nestedInputTypeName
};

if (valueRequired) {
customTypeData.required = true;
}
if (array) {
customTypeData.array = true;
}
if (arrayRequired) {
customTypeData.arrayRequired = true;
}

return customTypeData;
}

/**
* Generates input types for custom operations in the schema.
*
Expand Down Expand Up @@ -1391,7 +1415,7 @@ function generateInputTypes(
if (refType.type === 'CustomType') {
const nestedInputTypeName = `${fieldDef.data.link}Input`;
processedFields[fieldName] = {
data: { type: 'ref', link: nestedInputTypeName },
data: constructNonScalarFieldDefinition(nestedInputTypeName, fieldDef),
};

// Process the nested type if it hasn't been processed and isn't a circular reference
Expand All @@ -1413,7 +1437,7 @@ function generateInputTypes(
}
} else if (refType.type === 'Enum') {
processedFields[fieldName] = {
data: { type: 'ref', link: fieldDef.data.link },
data: constructNonScalarFieldDefinition(fieldDef.data.link, fieldDef),
};
} else {
throw new Error(
Expand Down