Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e7f97fa
feat(custom-operations): allow CustomType and RefType in arguments
vaisshnavi7 Nov 8, 2024
0e0561e
Refactor type tests for CustomType and RefType
vaisshnavi7 Nov 8, 2024
1a54cde
Merge branch 'main' into feat/allow-customtype-ref-in-arguments
vaisshnavi7 Nov 13, 2024
ecf2ec2
Merge branch 'main' into feat/allow-customtype-ref-in-arguments
vaisshnavi7 Nov 21, 2024
a0872b9
feat: add support for custom, nested, and ref type arguments (#402)
vaisshnavi7 Nov 26, 2024
2f7e948
Merge branch 'main' into feat/allow-customtype-ref-in-arguments
vaisshnavi7 Nov 26, 2024
45074a3
Merge branch 'main' into feat/allow-customtype-ref-in-arguments
vaisshnavi7 Dec 11, 2024
3517643
feat: Custom type and ref arg handling in SchemaProcessor with tests …
vaisshnavi7 Jan 9, 2025
2e81a9d
Merge remote-tracking branch 'origin/main' into feat/allow-customtype…
vaisshnavi7 Jan 9, 2025
c54ed9b
Fix: Generate nested CustomTypes in schema output
vaisshnavi7 Jan 15, 2025
a7dd990
Merge branch 'main' into feat/allow-customtypes-ref-in-arguments/main
vaisshnavi7 Jan 15, 2025
cc50e20
refactor: Add tests for nested custom types and mixed refs
vaisshnavi7 Jan 15, 2025
fff0b5d
Add test for RefType with nested CustomType in complex structures
vaisshnavi7 Jan 16, 2025
f4b9171
Merge branch 'main' into feat/allow-customtypes-ref-in-arguments/main
stocaaro Jan 16, 2025
949e6ee
fix: Adjust schema component ordering to minimize enum reordering
vaisshnavi7 Jan 17, 2025
cdb02d1
Revert unnecessary changes for custom types and refs
vaisshnavi7 Jan 17, 2025
2f8c281
Merge branch 'main' into feat/allow-customtypes-ref-in-arguments/main
vaisshnavi7 Jan 21, 2025
6b82830
Add unit tests for custom operations with custom types and refs in Cu…
vaisshnavi7 Jan 22, 2025
f1b8e0e
Refactor schemaPreprocessor Consolidate schema components into single…
vaisshnavi7 Jan 23, 2025
acaa7d4
Improve tests for CustomType and RefType arguments in custom operations
vaisshnavi7 Jan 23, 2025
0f19e89
Fix: Support enums in custom types and references
vaisshnavi7 Jan 27, 2025
832a436
Remove empty block for enum ref handling in generateInputTypes
vaisshnavi7 Jan 27, 2025
3b32405
remove unused variable
vaisshnavi7 Jan 27, 2025
8ddcf13
Add unit tests for enum refs and improve ResolveFields type export
vaisshnavi7 Jan 28, 2025
a4c040e
Fix enum handling and duplication in nested custom types
vaisshnavi7 Jan 29, 2025
59c34ba
fix: lint errors for unused variables
vaisshnavi7 Jan 29, 2025
1310383
Merge branch 'main' into feat/allow-customtypes-ref-in-arguments/main
stocaaro Jan 30, 2025
bb7b4f2
test: add test cases for enum handling in arguments
vaisshnavi7 Jan 30, 2025
5b04623
Refactor custom operation input type generation
vaisshnavi7 Feb 4, 2025
29d3596
handle nested inout types
vaisshnavi7 Feb 4, 2025
cb5c732
Merge branch 'main' into feat/allow-customtypes-ref-in-arguments/main
vaisshnavi7 Feb 4, 2025
12cf372
remove unused variables
vaisshnavi7 Feb 4, 2025
e817980
update input naming convention
vaisshnavi7 Feb 6, 2025
e644a53
add comments
vaisshnavi7 Feb 6, 2025
69f1254
Merge branch 'main' into feat/allow-customtypes-ref-in-arguments/main
vaisshnavi7 Feb 6, 2025
bd835e3
Merge branch 'main' into feat/allow-customtypes-ref-in-arguments/main
stocaaro Feb 6, 2025
0f9992e
update error message and refactor
vaisshnavi7 Feb 7, 2025
ed6dd7c
Merge branch 'main' into feat/allow-customtypes-ref-in-arguments/main
vaisshnavi7 Feb 12, 2025
d7682c8
update naming for references
vaisshnavi7 Feb 13, 2025
36afaf4
fix: Update CustomOpArguments type
vaisshnavi7 Feb 18, 2025
7bd9d1d
Add tests for custom operation subscriptions with custom types and refs
vaisshnavi7 Feb 18, 2025
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/polite-crabs-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@aws-amplify/data-schema": minor
---

Allow CustomType and RefType in arguments for custom operations
151 changes: 151 additions & 0 deletions packages/data-schema/__tests__/CustomOperations.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type {
import { configure } from '../src/ModelSchema';
import { Nullable } from '../src/ModelField';
import { defineFunctionStub } from './utils';
import type { CustomOperation } from '../src/CustomOperation';

describe('custom operations return types', () => {
describe('when .ref() a basic custom type', () => {
Expand Down Expand Up @@ -855,3 +856,153 @@ describe('.for() modifier', () => {
a.subscription().for(a.ref('Model'));
});
});

describe('.arguments() modifier', () => {
// Test to verify that CustomType can be used as an argument in custom operations
it('accepts CustomType in arguments', () => {
type ExpectedType = CustomOperation<
any,
'arguments' | 'for',
'queryCustomOperation'
>;

const operation = a.query().arguments({
customArg: a.customType({
field1: a.string(),
field2: a.integer(),
}),
});

type test = Expect<Equal<ExpectedType, typeof operation>>;
});

// Test to verify that RefType can be used as an argument in custom operations
it('accepts RefType in arguments', () => {
type ExpectedType = CustomOperation<
any,
'arguments' | 'for',
'queryCustomOperation'
>;

const operation = a.query().arguments({
refArg: a.ref('SomeType'),
});

type test = Expect<Equal<ExpectedType, typeof operation>>;
});

it('handles deeply nested custom types', () => {
const schema = a.schema({
DeepNested: a.customType({
level1: a.customType({
level2: a.customType({
level3: a.string(),
}),
}),
}),
deepQuery: a
.query()
.arguments({
input: a.ref('DeepNested'),
})
.returns(a.string()),
});

type Schema = ClientSchema<typeof schema>;

type ExpectedArgs = {
input?: {
level1?: {
level2?: {
level3?: string | null;
} | null;
} | null;
} | null;
};

type ActualArgs = Schema['deepQuery']['args'];

type Test = Expect<Equal<ActualArgs, ExpectedArgs>>;
});

it('handles mixed custom types and refs', () => {
const schema = a.schema({
RefType: a.customType({
field: a.string(),
}),
MixedType: a.customType({
nested: a.customType({
refField: a.ref('RefType'),
customField: a.customType({
deepField: a.integer(),
}),
}),
}),
mixedQuery: a
.query()
.arguments({
input: a.ref('MixedType'),
})
.returns(a.string()),
});

type Schema = ClientSchema<typeof schema>;

type ExpectedArgs = {
input?: {
nested?: {
refField?: {
field?: string | null;
} | null;
customField?: {
deepField?: number | null;
} | null;
} | null;
} | null;
};

type ActualArgs = Schema['mixedQuery']['args'];

type Test = Expect<Equal<ActualArgs, ExpectedArgs>>;
});

it('handles RefType with multi-layered custom types in nested structures', () => {
const schema = a.schema({
NestedCustomType: a.customType({
nestedField: a.string(),
}),
RefType: a.customType({
field: a.string(),
nestedCustom: a.ref('NestedCustomType'),
}),
OuterType: a.customType({
refField: a.ref('RefType'),
otherField: a.integer(),
}),
complexQuery: a
.query()
.arguments({
input: a.ref('OuterType'),
})
.returns(a.string()),
});

type Schema = ClientSchema<typeof schema>;

type ExpectedArgs = {
input?: {
refField?: {
field?: string | null;
nestedCustom?: {
nestedField?: string | null;
} | null;
} | null;
otherField?: number | null;
} | null;
};

type ActualArgs = Schema['complexQuery']['args'];

type Test = Expect<Equal<ActualArgs, ExpectedArgs>>;
});
});
78 changes: 78 additions & 0 deletions packages/data-schema/__tests__/CustomOperations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,84 @@ describe('CustomOperation transform', () => {
});
});

describe('custom operations with custom types and refs', () => {
test('Schema with custom query using custom type argument', () => {
const s = a
.schema({
CustomArgType: a.customType({
field: a.string(),
}),
queryWithCustomTypeArg: a
.query()
.arguments({ customArg: a.ref('CustomArgType') })
.returns(a.string())
.handler(a.handler.function('myFunc')),
})
.authorization((allow) => allow.publicApiKey());

const result = s.transform().schema;

expect(result).toMatchSnapshot();
});

test('Schema with custom query using ref argument', () => {
const s = a
.schema({
RefArgType: a.customType({
field: a.string(),
}),
queryWithRefArg: a
.query()
.arguments({ refArg: a.ref('RefArgType') })
.returns(a.string())
.handler(a.handler.function('myFunc')),
})
.authorization((allow) => allow.publicApiKey());

const result = s.transform().schema;

expect(result).toMatchSnapshot();
});

test('Schema with custom mutation using custom type argument', () => {
const s = a
.schema({
CustomArgType: a.customType({
field: a.string(),
}),
mutateWithCustomTypeArg: a
.mutation()
.arguments({ customArg: a.ref('CustomArgType') })
.returns(a.string())
.handler(a.handler.function('myFunc')),
})
.authorization((allow) => allow.publicApiKey());

const result = s.transform().schema;

expect(result).toMatchSnapshot();
});

test('Schema with custom mutation using ref argument', () => {
const s = a
.schema({
RefArgType: a.customType({
field: a.string(),
}),
mutationWithRefArg: a
.mutation()
.arguments({ refArg: a.ref('RefArgType') })
.returns(a.string())
.handler(a.handler.function('myFunc')),
})
.authorization((allow) => allow.publicApiKey());

const result = s.transform().schema;

expect(result).toMatchSnapshot();
});
});

const fakeSecret = () => ({}) as any;

const datasourceConfigMySQL = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,69 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CustomOperation transform custom operations with custom types and refs Schema with custom mutation using custom type argument 1`] = `
"input MutateWithCustomTypeArgCustomArgInput
{
field: String
}

type CustomArgType
{
field: String
}

type Mutation {
mutateWithCustomTypeArg(customArg: MutateWithCustomTypeArgCustomArgInput): String @function(name: "myFunc") @auth(rules: [{allow: public, provider: apiKey}])
}"
`;

exports[`CustomOperation transform custom operations with custom types and refs Schema with custom mutation using ref argument 1`] = `
"input MutationWithRefArgRefArgInput
{
field: String
}

type RefArgType
{
field: String
}

type Mutation {
mutationWithRefArg(refArg: MutationWithRefArgRefArgInput): String @function(name: "myFunc") @auth(rules: [{allow: public, provider: apiKey}])
}"
`;

exports[`CustomOperation transform custom operations with custom types and refs Schema with custom query using custom type argument 1`] = `
"input QueryWithCustomTypeArgCustomArgInput
{
field: String
}

type CustomArgType
{
field: String
}

type Query {
queryWithCustomTypeArg(customArg: QueryWithCustomTypeArgCustomArgInput): String @function(name: "myFunc") @auth(rules: [{allow: public, provider: apiKey}])
}"
`;

exports[`CustomOperation transform custom operations with custom types and refs Schema with custom query using ref argument 1`] = `
"input QueryWithRefArgRefArgInput
{
field: String
}

type RefArgType
{
field: String
}

type Query {
queryWithRefArg(refArg: QueryWithRefArgRefArgInput): String @function(name: "myFunc") @auth(rules: [{allow: public, provider: apiKey}])
}"
`;

exports[`CustomOperation transform dynamo schema Custom Mutation w required arg and enum 1`] = `
"type Post @model @auth(rules: [{allow: private}])
{
Expand Down Expand Up @@ -45,7 +109,7 @@ exports[`CustomOperation transform dynamo schema Custom mutation w inline boolea
`;

exports[`CustomOperation transform dynamo schema Custom mutation w inline custom return type 1`] = `
"type LikePostReturnType
"type LikePostReturnType
{
stringField: String
intField: Int
Expand Down Expand Up @@ -109,7 +173,7 @@ type Query {
`;

exports[`CustomOperation transform dynamo schema Custom query w inline custom return type 1`] = `
"type GetPostDetailsReturnType
"type GetPostDetailsReturnType
{
stringField: String
intField: Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import type { AppSyncResolverHandler } from 'aws-lambda';
import type { CustomType } from '../../CustomType';
import type { FieldTypesOfCustomType } from '../../MappedTypes/ResolveSchema';
import type { ResolveRef } from '../utilities/ResolveRef';
import type { EnumType } from '../../EnumType';
import { ClientSchemaProperty } from './ClientSchemaProperty';
import type { ResolveFields } from '../utilities';

type CustomOperationSubType<Op extends CustomOperationParamShape> =
`custom${Op['typeName']}`;
Expand Down Expand Up @@ -38,7 +38,7 @@ export interface ClientCustomOperation<
* ```
*/
functionHandler: AppSyncResolverHandler<
CustomOpArguments<Op>,
CustomOpArguments<Op, RefBag>,
Copy link
Contributor

Choose a reason for hiding this comment

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

When using the custom operation arguments, the shape of the input fields looks like it hasn't been unwrapped to show the custom operation fields.
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's resolved now with Expand in ResolveFields, ensuring the input fields are properly unwrapped.

This comment was marked as resolved.

// If the final handler is an async function, the Schema['fieldname']['functionhandler']
// should have a return type of `void`. This only applies to `functionHandler` and not
// `returnType` because `returnType` determines the type returned by the mutation / query
Expand All @@ -60,7 +60,7 @@ export interface ClientCustomOperation<
* }
* ```
*/
args: CustomOpArguments<Op>;
args: CustomOpArguments<Op, RefBag>;

/**
* The return type expected by a lambda function handler.
Expand All @@ -84,19 +84,18 @@ export interface ClientCustomOperation<

/**
* Digs out custom operation arguments, mapped to the intended graphql types.
* using the existing ResolveFields utility type. This handles:
* - Basic scalar fields
* - Enum types
* - Custom types (including nested structures)
* - Reference types
*/
type CustomOpArguments<Shape extends CustomOperationParamShape> =
Shape['arguments'] extends null
? never
: ResolveFieldRequirements<{
[FieldName in keyof Shape['arguments']]: Shape['arguments'][FieldName] extends BaseModelField<
infer R
>
? R
: Shape['arguments'][FieldName] extends EnumType<infer Values>
? Values[number] | null
: never;
}>;
type CustomOpArguments<
Shape extends CustomOperationParamShape,
RefBag extends Record<string, any> = any,
> = Shape['arguments'] extends null
? never
: ResolveFields<RefBag, Shape['arguments']>;

/**
* Removes `null | undefined` from the return type if the operation is a subscription,
Expand Down
Loading
Loading