Skip to content

Commit

Permalink
fix(api-graphql): custom types support (#12750)
Browse files Browse the repository at this point in the history
  • Loading branch information
erickriva committed Feb 21, 2024
1 parent 1509592 commit 7d95fa1
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,16 @@ const amplifyConfig = {
},
},

metadata: {
name: 'metadata',
isArray: false,
type: {
nonModel: 'CommunityPostMetadata',
},
isRequired: false,
attributes: [],
},

communityPostPollId: {
name: 'communityPostPollId',
isArray: false,
Expand Down Expand Up @@ -1068,7 +1078,28 @@ const amplifyConfig = {
values: ['NOT_STARTED', 'STARTED', 'DONE', 'CANCELED'],
},
},
nonModels: {},
nonModels: {
CommunityPostMetadata: {
name: 'CommunityPostMetadata',
fields: {
type: {
name: 'type',
isArray: false,
type: 'String',
isRequired: true,
attributes: [],
},

deleted: {
name: 'deleted',
isArray: false,
type: 'Boolean',
isRequired: false,
attributes: [],
},
},
},
},
},
};
export default amplifyConfig;
6 changes: 6 additions & 0 deletions packages/api-graphql/__tests__/fixtures/modeled/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,15 @@ const schema = a.schema({
})
.identifier(['cpk_cluster_key', 'cpk_sort_key']),

CommunityPostMetadata: a.customType({
type: a.string().required(),
deleted: a.boolean(),
}),

CommunityPost: a.model({
id: a.id().required(),
poll: a.hasOne('CommunityPoll'),
metadata: a.ref('CommunityPostMetadata'),
}),
CommunityPoll: a.model({
id: a.id().required(),
Expand Down
129 changes: 112 additions & 17 deletions packages/api-graphql/__tests__/internals/APIClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ describe('flattenItems', () => {

describe('customSelectionSetToIR', () => {
test('specific fields on the model', () => {
const selSet = customSelectionSetToIR(modelIntroSchema.models, 'Todo', [
const selSet = customSelectionSetToIR(modelIntroSchema, 'Todo', [
'id',
'name',
]);
Expand All @@ -182,7 +182,7 @@ describe('flattenItems', () => {
});

test('specific fields on the model and related model', () => {
const selSet = customSelectionSetToIR(modelIntroSchema.models, 'Todo', [
const selSet = customSelectionSetToIR(modelIntroSchema, 'Todo', [
'id',
'name',
'notes.id',
Expand All @@ -203,8 +203,39 @@ describe('flattenItems', () => {
expect(selSet).toEqual(expected);
});

test('related property without any specified field in selectionSet should throw an error', () => {
expect(() =>
customSelectionSetToIR(modelIntroSchema, 'Todo', [
'id',
'name',
'notes',
])
).toThrow('notes must declare a wildcard (*) or a field of model Note');
});

test('inexistent field should throw an error', () => {
expect(() =>
customSelectionSetToIR(modelIntroSchema, 'Todo', [
'id',
'name',
'inexistentField',
'notes.*',
])
).toThrow('inexistentField is not a field of model Todo');
});

test('related inexistent field should throw an error', () => {
expect(() =>
customSelectionSetToIR(modelIntroSchema, 'Todo', [
'id',
'name',
'notes.inexistentField',
])
).toThrow('inexistentField is not a field of model Note');
});

test('specific fields on the model; all fields on related model', () => {
const selSet = customSelectionSetToIR(modelIntroSchema.models, 'Todo', [
const selSet = customSelectionSetToIR(modelIntroSchema, 'Todo', [
'id',
'name',
'notes.*',
Expand All @@ -229,7 +260,7 @@ describe('flattenItems', () => {
});

test('deeply nested on a bi-directional model', () => {
const selSet = customSelectionSetToIR(modelIntroSchema.models, 'Todo', [
const selSet = customSelectionSetToIR(modelIntroSchema, 'Todo', [
'id',
'name',
'notes.todo.notes.todo.notes.todo.notes.*',
Expand Down Expand Up @@ -272,7 +303,7 @@ describe('flattenItems', () => {
});

test("subsequent wildcard doesn't overwrite existing nested object", () => {
const selSet = customSelectionSetToIR(modelIntroSchema.models, 'Todo', [
const selSet = customSelectionSetToIR(modelIntroSchema, 'Todo', [
'id',
'name',
'notes.todo.name',
Expand Down Expand Up @@ -300,12 +331,71 @@ describe('flattenItems', () => {
expect(selSet).toEqual(expected);
});

test('mix of related and non-related fields in a nested model creates a nested object with all necessary fields', () => {
const selSet = customSelectionSetToIR(
modelIntroSchema.models,
'CommunityPost',
['poll.question', 'poll.answers.answer', 'poll.answers.votes.id'],
test('custom type works properly', () => {
const selSet = customSelectionSetToIR(modelIntroSchema, 'CommunityPost', [
'metadata.type',
'poll.question',
]);

const expected = {
metadata: {
type: '',
},
poll: {
question: '',
},
};

expect(selSet).toEqual(expected);
});

test('custom type with wildcard works properly', () => {
const selSet = customSelectionSetToIR(modelIntroSchema, 'CommunityPost', [
'metadata.*',
'poll.question',
]);

const expected = {
metadata: {
type: '',
deleted: '',
},
poll: {
question: '',
},
};

expect(selSet).toEqual(expected);
});

test('custom type with invalid property throws an error', () => {
expect(() =>
customSelectionSetToIR(modelIntroSchema, 'CommunityPost', [
'metadata.inexistentField',
'poll.question',
])
).toThrow(
'inexistentField is not a field of custom type CommunityPostMetadata'
);
});

test('custom type without any properties throws an error', () => {
expect(() =>
customSelectionSetToIR(modelIntroSchema, 'CommunityPost', [
'metadata',
'poll.question',
])
).toThrow(
'metadata must declare a wildcard (*) or a field of custom type CommunityPostMetadata'
);
});

test('mix of related and non-related fields in a nested model creates a nested object with all necessary fields', () => {
const selSet = customSelectionSetToIR(modelIntroSchema, 'CommunityPost', [
'poll.question',
'poll.answers.answer',
'poll.answers.votes.id',
]);

const expected = {
poll: {
Expand All @@ -329,7 +419,7 @@ describe('flattenItems', () => {

describe('generateSelectionSet', () => {
test('it should generate default selection set', () => {
const selSet = generateSelectionSet(modelIntroSchema.models, 'Todo');
const selSet = generateSelectionSet(modelIntroSchema, 'Todo');

const expected =
'id name description status tags createdAt updatedAt todoMetaId owner';
Expand All @@ -338,7 +428,7 @@ describe('flattenItems', () => {
});

test('it should generate custom selection set - top-level fields', () => {
const selSet = generateSelectionSet(modelIntroSchema.models, 'Todo', [
const selSet = generateSelectionSet(modelIntroSchema, 'Todo', [
'id',
'name',
]);
Expand All @@ -349,7 +439,7 @@ describe('flattenItems', () => {
});

test('it should generate custom selection set - specific nested fields', () => {
const selSet = generateSelectionSet(modelIntroSchema.models, 'Todo', [
const selSet = generateSelectionSet(modelIntroSchema, 'Todo', [
'id',
'name',
'notes.id',
Expand All @@ -362,7 +452,7 @@ describe('flattenItems', () => {
});

test('it should generate custom selection set - all nested fields', () => {
const selSet = generateSelectionSet(modelIntroSchema.models, 'Todo', [
const selSet = generateSelectionSet(modelIntroSchema, 'Todo', [
'id',
'name',
'notes.*',
Expand All @@ -375,7 +465,7 @@ describe('flattenItems', () => {
});

test('deeply nested on a bi-directional model', () => {
const selSet = generateSelectionSet(modelIntroSchema.models, 'Todo', [
const selSet = generateSelectionSet(modelIntroSchema, 'Todo', [
'id',
'name',
'notes.todo.notes.todo.notes.todo.notes.*',
Expand All @@ -393,8 +483,13 @@ describe('generateGraphQLDocument()', () => {
describe('for `READ` operation', () => {
const modelOperation = 'READ';
const mockModelDefinitions = {
User: userSchemaModel,
Product: productSchemaModel,
version: 1 as const,
enums: {},
nonModels: {},
models: {
User: userSchemaModel,
Product: productSchemaModel,
}
};

test.each([
Expand Down
Loading

0 comments on commit 7d95fa1

Please sign in to comment.