diff --git a/packages/amplify-e2e-tests/src/__tests__/rds-import-vpc.test.ts b/packages/amplify-e2e-tests/src/__tests__/rds-import-vpc.test.ts index a32e891179..72dcc70b01 100644 --- a/packages/amplify-e2e-tests/src/__tests__/rds-import-vpc.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/rds-import-vpc.test.ts @@ -1,17 +1,17 @@ import { - addApiWithoutSchema, - amplifyPush, - apiGqlCompile, - createNewProjectDir, - createRDSInstance, - deleteDBInstance, - deleteProject, - deleteProjectDir, - getAppSyncApi, - getProjectMeta, - importRDSDatabase, + addApiWithoutSchema, + amplifyPush, + apiGqlCompile, + createNewProjectDir, + createRDSInstance, + deleteDBInstance, + deleteProject, + deleteProjectDir, + getAppSyncApi, + getProjectMeta, + importRDSDatabase, initJSProjectWithProfile, - updateSchema, + updateSchema, } from 'amplify-category-api-e2e-core'; import { existsSync, readFileSync } from 'fs-extra'; import generator from 'generate-password'; @@ -31,9 +31,9 @@ const APPSYNC_DATA_SOURCE_TYPE = 'AWS::AppSync::DataSource'; const SNS_TOPIC_REGION = 'us-east-1'; const SNS_TOPIC_ARN = 'arn:aws:sns:us-east-1:582037449441:AmplifyRDSLayerNotification'; -describe("RDS Tests", () => { +describe('RDS Tests', () => { const [db_user, db_password, db_identifier] = generator.generateMultiple(3); - + // Generate settings for RDS instance const username = db_user; const password = db_password; @@ -45,8 +45,7 @@ describe("RDS Tests", () => { const projName = 'rdsimportapi'; let projRoot; - beforeAll(async () => { - }); + beforeAll(async () => {}); afterAll(async () => { await cleanupDatabase(); @@ -82,21 +81,21 @@ describe("RDS Tests", () => { await deleteDBInstance(identifier, region); }; - it("import workflow of mysql relational database within vpc with no public access", async () => { + it('import workflow of mysql relational database within vpc with no public access', async () => { const apiName = 'rdsapivpc'; await initJSProjectWithProfile(projRoot, { disableAmplifyAppCreation: false, name: projName, }); - + const metaAfterInit = getProjectMeta(projRoot); region = metaAfterInit.providers.awscloudformation.Region; await setupDatabase(); - + const rdsSchemaFilePath = path.join(projRoot, 'amplify', 'backend', 'api', apiName, 'schema.rds.graphql'); await addApiWithoutSchema(projRoot, { transformerVersion: 2, apiName }); - + // This only verifies the prompt for VPC access. Does not verify the actual import. await importRDSDatabase(projRoot, { database: 'mysql', // Import the default 'mysql' database @@ -113,14 +112,16 @@ describe("RDS Tests", () => { // Generated schema should contain the types with model directive // db is one of the default table in mysql database - const dbObjectType = schema.definitions.find(d => d.kind === 'ObjectTypeDefinition' && d.name.value === 'db') as ObjectTypeDefinitionNode; + const dbObjectType = schema.definitions.find( + (d) => d.kind === 'ObjectTypeDefinition' && d.name.value === 'db', + ) as ObjectTypeDefinitionNode; expect(dbObjectType).toBeDefined(); - expect(dbObjectType.directives.find(d => d.name.value === 'model')).toBeDefined(); + expect(dbObjectType.directives.find((d) => d.name.value === 'model')).toBeDefined(); const updatedSchema = gql` input AMPLIFY { engine: String = "mysql" - globalAuthRule: AuthRule = {allow: public} + globalAuthRule: AuthRule = { allow: public } } type component @model { @@ -158,7 +159,9 @@ describe("RDS Tests", () => { expect(rdsPatchingLambdaFunction.Properties.Environment.Variables).toBeDefined(); expect(rdsPatchingLambdaFunction.Properties.Environment.Variables.LAMBDA_FUNCTION_ARN).toBeDefined(); const rdsDataSourceLambda = getResource(resources, 'RDSLambdaDataSource', APPSYNC_DATA_SOURCE_TYPE); - expect(rdsPatchingLambdaFunction.Properties.Environment.Variables.LAMBDA_FUNCTION_ARN).toEqual(rdsDataSourceLambda.Properties.LambdaConfig.LambdaFunctionArn); + expect(rdsPatchingLambdaFunction.Properties.Environment.Variables.LAMBDA_FUNCTION_ARN).toEqual( + rdsDataSourceLambda.Properties.LambdaConfig.LambdaFunctionArn, + ); // Validate subscription const rdsPatchingSubscription = getResource(resources, 'RDSPatchingLambdaLogicalID', CDK_SUBSCRIPTION_TYPE); @@ -211,10 +214,10 @@ describe("RDS Tests", () => { expect(err.message).toEqual('GraphQL error: Unable to get the database credentials. Check the logs for more details.'); } }); -}); +}); const getResource = (resources: Map, resourcePrefix: string, resourceType: string): any => { - const keys = Array.from(Object.keys(resources)).filter(key => key.startsWith(resourcePrefix)); + const keys = Array.from(Object.keys(resources)).filter((key) => key.startsWith(resourcePrefix)); for (const key of keys) { const resource = resources[key]; if (resource.Type === resourceType) { @@ -225,16 +228,16 @@ const getResource = (resources: Map, resourcePrefix: string, resour const listComponents = async (client) => { const listComponents = /* GraphQL */ ` - query listComponents { - listComponents { - items { - component_group_id - component_id - component_urn - } + query listComponents { + listComponents { + items { + component_group_id + component_id + component_urn } } - `; + } + `; const listResult: any = await client.query({ query: gql(listComponents), fetchPolicy: 'no-cache', diff --git a/packages/amplify-e2e-tests/src/__tests__/rds-model-v2.test.ts b/packages/amplify-e2e-tests/src/__tests__/rds-model-v2.test.ts index fb7687d09c..3cbc328185 100644 --- a/packages/amplify-e2e-tests/src/__tests__/rds-model-v2.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/rds-model-v2.test.ts @@ -1,18 +1,18 @@ import { - RDSTestDataProvider, - addApiWithoutSchema, - addRDSPortInboundRule, - amplifyPush, - createNewProjectDir, - createRDSInstance, - deleteDBInstance, - deleteProject, - deleteProjectDir, - getAppSyncApi, - getProjectMeta, - importRDSDatabase, - initJSProjectWithProfile, - removeRDSPortInboundRule, + RDSTestDataProvider, + addApiWithoutSchema, + addRDSPortInboundRule, + amplifyPush, + createNewProjectDir, + createRDSInstance, + deleteDBInstance, + deleteProject, + deleteProjectDir, + getAppSyncApi, + getProjectMeta, + importRDSDatabase, + initJSProjectWithProfile, + removeRDSPortInboundRule, } from 'amplify-category-api-e2e-core'; import axios from 'axios'; import { existsSync, readFileSync } from 'fs-extra'; @@ -25,10 +25,10 @@ import gql from 'graphql-tag'; // to deal with bug in cognito-identity-js (global as any).fetch = require('node-fetch'); -describe("RDS Model Directive", () => { - const publicIpCidr = "0.0.0.0/0"; +describe('RDS Model Directive', () => { + const publicIpCidr = '0.0.0.0/0'; const [db_user, db_password, db_identifier] = generator.generateMultiple(3); - + // Generate settings for RDS instance const username = db_user; const password = db_password; @@ -84,11 +84,9 @@ describe("RDS Model Directive", () => { await cleanupDatabase(); }); - beforeEach(async () => { - }); + beforeEach(async () => {}); - afterEach(async () => { - }); + afterEach(async () => {}); const setupDatabase = async () => { // This test performs the below @@ -121,10 +119,10 @@ describe("RDS Model Directive", () => { }); await dbAdapter.runQuery([ - "CREATE TABLE Contact (id VARCHAR(40) PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))", - "CREATE TABLE Person (personId INT PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))", - "CREATE TABLE Employee (ID INT PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))", - "CREATE TABLE Student (studentId INT NOT NULL, classId CHAR(1) NOT NULL, FirstName VARCHAR(20), LastName VARCHAR(50), PRIMARY KEY (studentId, classId))", + 'CREATE TABLE Contact (id VARCHAR(40) PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))', + 'CREATE TABLE Person (personId INT PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))', + 'CREATE TABLE Employee (ID INT PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))', + 'CREATE TABLE Student (studentId INT NOT NULL, classId CHAR(1) NOT NULL, FirstName VARCHAR(20), LastName VARCHAR(50), PRIMARY KEY (studentId, classId))', ]); dbAdapter.cleanup(); }; @@ -149,7 +147,7 @@ describe("RDS Model Directive", () => { const rdsSchemaFilePath = path.join(projRoot, 'amplify', 'backend', 'api', apiName, 'schema.rds.graphql'); await addApiWithoutSchema(projRoot, { transformerVersion: 2, apiName }); - + await importRDSDatabase(projRoot, { database, host, @@ -164,25 +162,27 @@ describe("RDS Model Directive", () => { const schema = parse(schemaContent); // Generated schema should contains the types and fields from the database - const contactObjectType = schema.definitions.find(d => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Contact') as ObjectTypeDefinitionNode; - const personObjectType = schema.definitions.find(d => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Person'); - const employeeObjectType = schema.definitions.find(d => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Employee'); + const contactObjectType = schema.definitions.find( + (d) => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Contact', + ) as ObjectTypeDefinitionNode; + const personObjectType = schema.definitions.find((d) => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Person'); + const employeeObjectType = schema.definitions.find((d) => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Employee'); expect(contactObjectType).toBeDefined(); expect(personObjectType).toBeDefined(); expect(employeeObjectType).toBeDefined(); // Verify the fields in the generated schema on type 'Contacts' - const contactsIdFieldType = contactObjectType.fields.find(f => f.name.value === 'id'); - const contactsFirstNameFieldType = contactObjectType.fields.find(f => f.name.value === 'FirstName'); - const contactsLastNameFieldType = contactObjectType.fields.find(f => f.name.value === 'LastName'); + const contactsIdFieldType = contactObjectType.fields.find((f) => f.name.value === 'id'); + const contactsFirstNameFieldType = contactObjectType.fields.find((f) => f.name.value === 'FirstName'); + const contactsLastNameFieldType = contactObjectType.fields.find((f) => f.name.value === 'LastName'); expect(contactsIdFieldType).toBeDefined(); expect(contactsFirstNameFieldType).toBeDefined(); expect(contactsLastNameFieldType).toBeDefined(); // PrimaryKey directive must be defined on Id field. - expect(contactsIdFieldType.directives.find(d => d.name.value === 'primaryKey')).toBeDefined(); + expect(contactsIdFieldType.directives.find((d) => d.name.value === 'primaryKey')).toBeDefined(); }; test('check CRUDL on contact table with default primary key', async () => { @@ -214,10 +214,12 @@ describe("RDS Model Directive", () => { const listContactsResult = await listContacts(); expect(listContactsResult.data.listContacts.items.length).toEqual(2); - expect(listContactsResult.data.listContacts.items).toEqual(expect.arrayContaining([ - expect.objectContaining({ id: contact1.data.createContact.id, FirstName: 'David', LastName: 'Jones' }), - expect.objectContaining({ id: contact2.data.createContact.id, FirstName: 'Chris', LastName: 'Sundersingh' }), - ])); + expect(listContactsResult.data.listContacts.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: contact1.data.createContact.id, FirstName: 'David', LastName: 'Jones' }), + expect.objectContaining({ id: contact2.data.createContact.id, FirstName: 'Chris', LastName: 'Sundersingh' }), + ]), + ); const deleteContact1 = await deleteContact(contact1.data.createContact.id); expect(deleteContact1.data.deleteContact.id).toEqual(contact1.data.createContact.id); @@ -226,9 +228,11 @@ describe("RDS Model Directive", () => { const listContactsResultAfterDelete = await listContacts(); expect(listContactsResultAfterDelete.data.listContacts.items.length).toEqual(1); - expect(listContactsResultAfterDelete.data.listContacts.items).toEqual(expect.arrayContaining([ - expect.objectContaining({ id: contact2.data.createContact.id, FirstName: 'Chris', LastName: 'Sundersingh' }), - ])); + expect(listContactsResultAfterDelete.data.listContacts.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: contact2.data.createContact.id, FirstName: 'Chris', LastName: 'Sundersingh' }), + ]), + ); }); test('check CRUDL, filter, limit and nextToken on student table with composite key', async () => { @@ -286,55 +290,63 @@ describe("RDS Model Directive", () => { const listStudentsResult = await listStudents(); expect(listStudentsResult.data.listStudents.items.length).toEqual(3); - expect(listStudentsResult.data.listStudents.items).toEqual(expect.arrayContaining([ - expect.objectContaining({ studentId: 1, classId: 'B', FirstName: 'Chris', LastName: 'Sundersingh' }), - expect.objectContaining({ studentId: 2, classId: 'A', FirstName: 'John', LastName: 'Smith' }), - expect.objectContaining({ studentId: 2, classId: 'B', FirstName: 'Jane', LastName: 'Doe' }), - ])); + expect(listStudentsResult.data.listStudents.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ studentId: 1, classId: 'B', FirstName: 'Chris', LastName: 'Sundersingh' }), + expect.objectContaining({ studentId: 2, classId: 'A', FirstName: 'John', LastName: 'Smith' }), + expect.objectContaining({ studentId: 2, classId: 'B', FirstName: 'Jane', LastName: 'Doe' }), + ]), + ); // Validate limit and nextToken const listStudentsResultWithLimit = await listStudents(2); expect(listStudentsResultWithLimit.data.listStudents.items.length).toEqual(2); - expect(listStudentsResultWithLimit.data.listStudents.items).toEqual(expect.arrayContaining([ - expect.objectContaining({ studentId: 1, classId: 'B', FirstName: 'Chris', LastName: 'Sundersingh' }), - expect.objectContaining({ studentId: 2, classId: 'A', FirstName: 'John', LastName: 'Smith' }), - ])); + expect(listStudentsResultWithLimit.data.listStudents.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ studentId: 1, classId: 'B', FirstName: 'Chris', LastName: 'Sundersingh' }), + expect.objectContaining({ studentId: 2, classId: 'A', FirstName: 'John', LastName: 'Smith' }), + ]), + ); expect(listStudentsResultWithLimit.data.listStudents.nextToken).toBeDefined(); const listStudentsResultWithNextToken = await listStudents(2, listStudentsResultWithLimit.data.listStudents.nextToken); expect(listStudentsResultWithNextToken.data.listStudents.items.length).toEqual(1); - expect(listStudentsResultWithNextToken.data.listStudents.items).toEqual(expect.arrayContaining([ - expect.objectContaining({ studentId: 2, classId: 'B', FirstName: 'Jane', LastName: 'Doe' }), - ])); + expect(listStudentsResultWithNextToken.data.listStudents.items).toEqual( + expect.arrayContaining([expect.objectContaining({ studentId: 2, classId: 'B', FirstName: 'Jane', LastName: 'Doe' })]), + ); expect(listStudentsResultWithNextToken.data.listStudents.nextToken).toBeNull(); // Validate filter - const listStudentsResultWithFilter = await listStudents(10, null, { and: [{ FirstName: { eq: 'John' } }, { LastName: { eq: 'Smith' } }] }); + const listStudentsResultWithFilter = await listStudents(10, null, { + and: [{ FirstName: { eq: 'John' } }, { LastName: { eq: 'Smith' } }], + }); expect(listStudentsResultWithFilter.data.listStudents.items.length).toEqual(1); - expect(listStudentsResultWithFilter.data.listStudents.items).toEqual(expect.arrayContaining([ - expect.objectContaining({ studentId: 2, classId: 'A', FirstName: 'John', LastName: 'Smith' }), - ])); + expect(listStudentsResultWithFilter.data.listStudents.items).toEqual( + expect.arrayContaining([expect.objectContaining({ studentId: 2, classId: 'A', FirstName: 'John', LastName: 'Smith' })]), + ); expect(listStudentsResultWithFilter.data.listStudents.nextToken).toBeNull(); const listStudentsResultWithFilter2 = await listStudents(10, null, { FirstName: { size: { eq: 4 } } }); expect(listStudentsResultWithFilter2.data.listStudents.items.length).toEqual(2); - expect(listStudentsResultWithFilter2.data.listStudents.items).toEqual(expect.arrayContaining([ - expect.objectContaining({ studentId: 2, classId: 'A', FirstName: 'John', LastName: 'Smith' }), - expect.objectContaining({ studentId: 2, classId: 'A', FirstName: 'John', LastName: 'Smith' }), - ])); + expect(listStudentsResultWithFilter2.data.listStudents.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ studentId: 2, classId: 'A', FirstName: 'John', LastName: 'Smith' }), + expect.objectContaining({ studentId: 2, classId: 'A', FirstName: 'John', LastName: 'Smith' }), + ]), + ); }); // CURDL on Contact table helpers const createContact = async (firstName: string, lastName: string) => { const createMutation = /* GraphQL */ ` - mutation CreateContact($input: CreateContactInput!, $condition: ModelContactConditionInput) { - createContact(input: $input, condition: $condition) { - id - FirstName - LastName - } + mutation CreateContact($input: CreateContactInput!, $condition: ModelContactConditionInput) { + createContact(input: $input, condition: $condition) { + id + FirstName + LastName } - `; + } + `; const createInput = { input: { FirstName: firstName, @@ -352,14 +364,14 @@ describe("RDS Model Directive", () => { const updateContact = async (id: string, firstName: string, lastName: string) => { const updateMutation = /* GraphQL */ ` - mutation UpdateContact($input: UpdateContactInput!, $condition: ModelContactConditionInput) { - updateContact(input: $input, condition: $condition) { - id - FirstName - LastName - } + mutation UpdateContact($input: UpdateContactInput!, $condition: ModelContactConditionInput) { + updateContact(input: $input, condition: $condition) { + id + FirstName + LastName } - `; + } + `; const updateInput = { input: { id, @@ -378,14 +390,14 @@ describe("RDS Model Directive", () => { const deleteContact = async (id: string) => { const deleteMutation = /* GraphQL */ ` - mutation DeleteContact($input: DeleteContactInput!, $condition: ModelContactConditionInput) { - deleteContact(input: $input, condition: $condition) { - id - FirstName - LastName - } + mutation DeleteContact($input: DeleteContactInput!, $condition: ModelContactConditionInput) { + deleteContact(input: $input, condition: $condition) { + id + FirstName + LastName } - `; + } + `; const deleteInput = { input: { id, @@ -402,14 +414,14 @@ describe("RDS Model Directive", () => { const getContact = async (id: string) => { const getQuery = /* GraphQL */ ` - query GetContact($id: String!) { - getContact(id: $id) { - id - FirstName - LastName - } + query GetContact($id: String!) { + getContact(id: $id) { + id + FirstName + LastName } - `; + } + `; const getInput = { id, }; @@ -424,16 +436,16 @@ describe("RDS Model Directive", () => { const listContacts = async () => { const listQuery = /* GraphQL */ ` - query ListContact { - listContacts { - items { - id - FirstName - LastName - } + query ListContact { + listContacts { + items { + id + FirstName + LastName } } - `; + } + `; const listResult: any = await appSyncClient.query({ query: gql(listQuery), fetchPolicy: 'no-cache', @@ -445,15 +457,15 @@ describe("RDS Model Directive", () => { // CURDL on Student table helpers const createStudent = async (studentId: number, classId: string, firstName: string, lastName: string) => { const createMutation = /* GraphQL */ ` - mutation CreateStuden($input: CreateStudentInput!, $condition: ModelStudentConditionInput) { - createStudent(input: $input, condition: $condition) { - studentId - classId - FirstName - LastName - } + mutation CreateStuden($input: CreateStudentInput!, $condition: ModelStudentConditionInput) { + createStudent(input: $input, condition: $condition) { + studentId + classId + FirstName + LastName } - `; + } + `; const createInput = { input: { studentId, @@ -473,15 +485,15 @@ describe("RDS Model Directive", () => { const updateStudent = async (studentId: number, classId: string, firstName: string, lastName: string) => { const updateMutation = /* GraphQL */ ` - mutation UpdateStudent($input: UpdateStudentInput!, $condition: ModelStudentConditionInput) { - updateStudent(input: $input, condition: $condition) { - studentId, - classId, - FirstName - LastName - } + mutation UpdateStudent($input: UpdateStudentInput!, $condition: ModelStudentConditionInput) { + updateStudent(input: $input, condition: $condition) { + studentId + classId + FirstName + LastName } - `; + } + `; const updateInput = { input: { studentId, @@ -501,15 +513,15 @@ describe("RDS Model Directive", () => { const deleteStudent = async (studentId: number, classId: string) => { const deleteMutation = /* GraphQL */ ` - mutation DeleteStudent($input: DeleteStudentInput!, $condition: ModelStudentConditionInput) { - deleteStudent(input: $input, condition: $condition) { - studentId - classId - FirstName - LastName - } + mutation DeleteStudent($input: DeleteStudentInput!, $condition: ModelStudentConditionInput) { + deleteStudent(input: $input, condition: $condition) { + studentId + classId + FirstName + LastName } - `; + } + `; const deleteInput = { input: { studentId, @@ -527,15 +539,15 @@ describe("RDS Model Directive", () => { const getStudent = async (studentId: number, classId: string) => { const getQuery = /* GraphQL */ ` - query GetStudent($studentId: Int!, $classId: String!) { - getStudent(studentId: $studentId, classId: $classId) { - studentId - classId - FirstName - LastName - } + query GetStudent($studentId: Int!, $classId: String!) { + getStudent(studentId: $studentId, classId: $classId) { + studentId + classId + FirstName + LastName } - `; + } + `; const getInput = { studentId, classId, @@ -551,18 +563,18 @@ describe("RDS Model Directive", () => { const listStudents = async (limit: number = 100, nextToken: string | null = null, filter: any = null) => { const listQuery = /* GraphQL */ ` - query ListStudents($limit: Int, $nextToken: String, $filter: ModelStudentFilterInput) { - listStudents(limit: $limit, nextToken: $nextToken, filter: $filter) { - items { - studentId - classId - FirstName - LastName - } - nextToken + query ListStudents($limit: Int, $nextToken: String, $filter: ModelStudentFilterInput) { + listStudents(limit: $limit, nextToken: $nextToken, filter: $filter) { + items { + studentId + classId + FirstName + LastName } + nextToken } - `; + } + `; const listResult: any = await appSyncClient.query({ query: gql(listQuery), fetchPolicy: 'no-cache', @@ -575,4 +587,4 @@ describe("RDS Model Directive", () => { return listResult; }; -}); +}); diff --git a/packages/amplify-e2e-tests/src/__tests__/rds-relational-directives.test.ts b/packages/amplify-e2e-tests/src/__tests__/rds-relational-directives.test.ts index 47fb05fb1c..e88831d7ee 100644 --- a/packages/amplify-e2e-tests/src/__tests__/rds-relational-directives.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/rds-relational-directives.test.ts @@ -9,8 +9,10 @@ import { deleteProject, deleteProjectDir, getAppSyncApi, - getProjectMeta, importRDSDatabase, initJSProjectWithProfile, - removeRDSPortInboundRule + getProjectMeta, + importRDSDatabase, + initJSProjectWithProfile, + removeRDSPortInboundRule, } from 'amplify-category-api-e2e-core'; import { existsSync, writeFileSync } from 'fs-extra'; import generator from 'generate-password'; @@ -21,10 +23,10 @@ import gql from 'graphql-tag'; // to deal with bug in cognito-identity-js (global as any).fetch = require('node-fetch'); -describe("RDS Relational Directives", () => { - const publicIpCidr = "0.0.0.0/0"; +describe('RDS Relational Directives', () => { + const publicIpCidr = '0.0.0.0/0'; const [db_user, db_password, db_identifier] = generator.generateMultiple(3); - + // Generate settings for RDS instance const username = db_user; const password = db_password; @@ -137,7 +139,7 @@ describe("RDS Relational Directives", () => { const rdsSchemaFilePath = path.join(projRoot, 'amplify', 'backend', 'api', apiName, 'schema.rds.graphql'); await addApiWithoutSchema(projRoot, { transformerVersion: 2, apiName }); - + await importRDSDatabase(projRoot, { database, host, @@ -147,11 +149,11 @@ describe("RDS Relational Directives", () => { useVpc: false, apiExists: true, }); - + const schema = /* GraphQL */ ` input AMPLIFY { engine: String = "mysql" - globalAuthRule: AuthRule = {allow: public} + globalAuthRule: AuthRule = { allow: public } } type Blog @model { id: String! @primaryKey @@ -183,40 +185,44 @@ describe("RDS Relational Directives", () => { expect(getBlog1.data.getBlog.id).toEqual('B-1'); expect(getBlog1.data.getBlog.content).toEqual('Blog 1'); expect(getBlog1.data.getBlog.posts.items.length).toEqual(3); - expect(getBlog1.data.getBlog.posts.items).toEqual(expect.arrayContaining([ - expect.objectContaining({ id: 'P-1A', content: 'Post 1A' }), - expect.objectContaining({ id: 'P-1B', content: 'Post 1B' }), - expect.objectContaining({ id: 'P-1C', content: 'Post 1C' }), - ])); + expect(getBlog1.data.getBlog.posts.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: 'P-1A', content: 'Post 1A' }), + expect.objectContaining({ id: 'P-1B', content: 'Post 1B' }), + expect.objectContaining({ id: 'P-1C', content: 'Post 1C' }), + ]), + ); const getBlog2 = await getBlog('B-2'); expect(getBlog2.data.getBlog.id).toEqual('B-2'); expect(getBlog2.data.getBlog.content).toEqual('Blog 2'); expect(getBlog2.data.getBlog.posts.items.length).toEqual(2); - expect(getBlog2.data.getBlog.posts.items).toEqual(expect.arrayContaining([ - expect.objectContaining({ id: 'P-2A', content: 'Post 2A' }), - expect.objectContaining({ id: 'P-2B', content: 'Post 2B' }), - ])); + expect(getBlog2.data.getBlog.posts.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: 'P-2A', content: 'Post 2A' }), + expect.objectContaining({ id: 'P-2B', content: 'Post 2B' }), + ]), + ); const getBlog3 = await getBlog('B-3'); expect(getBlog3.data.getBlog.id).toEqual('B-3'); expect(getBlog3.data.getBlog.content).toEqual('Blog 3'); expect(getBlog3.data.getBlog.posts.items.length).toEqual(1); - expect(getBlog3.data.getBlog.posts.items).toEqual(expect.arrayContaining([ - expect.objectContaining({ id: 'P-3A', content: 'Post 3A' }), - ])); + expect(getBlog3.data.getBlog.posts.items).toEqual( + expect.arrayContaining([expect.objectContaining({ id: 'P-3A', content: 'Post 3A' })]), + ); }); // CURDL on Blog table helpers const createBlog = async (id: string, content: string): Promise => { const createMutation = /* GraphQL */ ` - mutation CreateBlog($input: CreateBlogInput!, $condition: ModelBlogConditionInput) { - createBlog(input: $input, condition: $condition) { - id - content - } + mutation CreateBlog($input: CreateBlogInput!, $condition: ModelBlogConditionInput) { + createBlog(input: $input, condition: $condition) { + id + content } - `; + } + `; const createInput = { input: { id, @@ -234,13 +240,13 @@ describe("RDS Relational Directives", () => { const updateBlog = async (id: string, content: string): Promise => { const updateMutation = /* GraphQL */ ` - mutation UpdateBlog($input: UpdateBlogInput!, $condition: ModelBlogConditionInput) { - updateBlog(input: $input, condition: $condition) { - id - content - } + mutation UpdateBlog($input: UpdateBlogInput!, $condition: ModelBlogConditionInput) { + updateBlog(input: $input, condition: $condition) { + id + content } - `; + } + `; const updateInput = { input: { id, @@ -258,13 +264,13 @@ describe("RDS Relational Directives", () => { const deleteBlog = async (id: string): Promise => { const deleteMutation = /* GraphQL */ ` - mutation DeleteBlog($input: DeleteBlogInput!, $condition: ModelBlogConditionInput) { - deleteBlog(input: $input, condition: $condition) { - id - content - } + mutation DeleteBlog($input: DeleteBlogInput!, $condition: ModelBlogConditionInput) { + deleteBlog(input: $input, condition: $condition) { + id + content } - `; + } + `; const deleteInput = { input: { id, @@ -281,19 +287,19 @@ describe("RDS Relational Directives", () => { const getBlog = async (id: string): Promise => { const getQuery = /* GraphQL */ ` - query GetBlog($id: String!) { - getBlog(id: $id) { - id - content - posts { - items { - id - content - } + query GetBlog($id: String!) { + getBlog(id: $id) { + id + content + posts { + items { + id + content } } } - `; + } + `; const getInput = { id, }; @@ -308,21 +314,21 @@ describe("RDS Relational Directives", () => { const listBlogs = async (): Promise => { const listQuery = /* GraphQL */ ` - query ListBlogs { - listBlogs { - items { - id - content - posts { - items { - id - content - } + query ListBlogs { + listBlogs { + items { + id + content + posts { + items { + id + content } } } } - `; + } + `; const listResult: any = await appSyncClient.query({ query: gql(listQuery), fetchPolicy: 'no-cache', @@ -334,14 +340,14 @@ describe("RDS Relational Directives", () => { // CURDL on Post table helpers const createPost = async (id: string, content: string, blogId: string): Promise => { const createMutation = /* GraphQL */ ` - mutation CreatePost($input: CreatePostInput!, $condition: ModelPostConditionInput) { - createPost(input: $input, condition: $condition) { - id - content - blogId - } + mutation CreatePost($input: CreatePostInput!, $condition: ModelPostConditionInput) { + createPost(input: $input, condition: $condition) { + id + content + blogId } - `; + } + `; const createInput = { input: { id, @@ -360,13 +366,13 @@ describe("RDS Relational Directives", () => { const updatePost = async (id: string, content: string): Promise => { const updateMutation = /* GraphQL */ ` - mutation UpdatePost($input: UpdatePostInput!, $condition: ModelPostConditionInput) { - updatePost(input: $input, condition: $condition) { - id - content - } + mutation UpdatePost($input: UpdatePostInput!, $condition: ModelPostConditionInput) { + updatePost(input: $input, condition: $condition) { + id + content } - `; + } + `; const updateInput = { input: { id, @@ -384,13 +390,13 @@ describe("RDS Relational Directives", () => { const deletePost = async (id: string): Promise => { const deleteMutation = /* GraphQL */ ` - mutation DeletePost($input: DeletePostInput!, $condition: ModelPostConditionInput) { - deletePost(input: $input, condition: $condition) { - id - content - } + mutation DeletePost($input: DeletePostInput!, $condition: ModelPostConditionInput) { + deletePost(input: $input, condition: $condition) { + id + content } - `; + } + `; const deleteInput = { input: { id, @@ -407,13 +413,13 @@ describe("RDS Relational Directives", () => { const getPost = async (id: string): Promise => { const getQuery = /* GraphQL */ ` - query GetPost($id: String!) { - getPost(id: $id) { - id - content - } + query GetPost($id: String!) { + getPost(id: $id) { + id + content } - `; + } + `; const getInput = { id, }; @@ -428,16 +434,16 @@ describe("RDS Relational Directives", () => { const listPosts = async (limit = 100, nextToken: string | null = null, filter: any = null): Promise => { const listQuery = /* GraphQL */ ` - query ListPosts($limit: Int, $nextToken: String, $filter: ModelPostFilterInput) { - listPosts(limit: $limit, nextToken: $nextToken, filter: $filter) { - items { - id - content - } - nextToken + query ListPosts($limit: Int, $nextToken: String, $filter: ModelPostFilterInput) { + listPosts(limit: $limit, nextToken: $nextToken, filter: $filter) { + items { + id + content } + nextToken } - `; + } + `; const listResult: any = await appSyncClient.query({ query: gql(listQuery), fetchPolicy: 'no-cache', @@ -450,4 +456,4 @@ describe("RDS Relational Directives", () => { return listResult; }; -}); +}); diff --git a/packages/amplify-e2e-tests/src/__tests__/rds-v2.test.ts b/packages/amplify-e2e-tests/src/__tests__/rds-v2.test.ts index 6f131a3c86..35ea63baf6 100644 --- a/packages/amplify-e2e-tests/src/__tests__/rds-v2.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/rds-v2.test.ts @@ -1,15 +1,15 @@ import { - RDSTestDataProvider, - addApiWithoutSchema, - addRDSPortInboundRule, - createNewProjectDir, - createRDSInstance, - deleteDBInstance, - deleteProject, - deleteProjectDir, - importRDSDatabase, - initJSProjectWithProfile, - removeRDSPortInboundRule, + RDSTestDataProvider, + addApiWithoutSchema, + addRDSPortInboundRule, + createNewProjectDir, + createRDSInstance, + deleteDBInstance, + deleteProject, + deleteProjectDir, + importRDSDatabase, + initJSProjectWithProfile, + removeRDSPortInboundRule, } from 'amplify-category-api-e2e-core'; import axios from 'axios'; import { existsSync, readFileSync } from 'fs-extra'; @@ -17,8 +17,8 @@ import generator from 'generate-password'; import { ObjectTypeDefinitionNode, parse } from 'graphql'; import path from 'path'; -describe("RDS Tests", () => { - let publicIpCidr = "0.0.0.0/0"; +describe('RDS Tests', () => { + let publicIpCidr = '0.0.0.0/0'; const [db_user, db_password, db_identifier] = generator.generateMultiple(3); const RDS_MAPPING_FILE = 'https://amplify-rds-layer-resources.s3.amazonaws.com/rds-layer-mapping.json'; @@ -35,7 +35,7 @@ describe("RDS Tests", () => { beforeAll(async () => { // Get the public IP of the machine running the test - const url = "http://api.ipify.org/"; + const url = 'http://api.ipify.org/'; const response = await axios(url); publicIpCidr = `${response.data.trim()}/32`; await setupDatabase(); @@ -87,9 +87,9 @@ describe("RDS Tests", () => { database: db.dbName, }); await dbAdapter.runQuery([ - "CREATE TABLE Contacts (ID INT PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))", - "CREATE TABLE Person (ID INT PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))", - "CREATE TABLE Employee (ID INT PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))", + 'CREATE TABLE Contacts (ID INT PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))', + 'CREATE TABLE Person (ID INT PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))', + 'CREATE TABLE Employee (ID INT PRIMARY KEY, FirstName VARCHAR(20), LastName VARCHAR(50))', ]); dbAdapter.cleanup(); }; @@ -105,7 +105,7 @@ describe("RDS Tests", () => { await deleteDBInstance(identifier, region); }; - it("import workflow of mysql relational database with public access", async () => { + it('import workflow of mysql relational database with public access', async () => { const apiName = 'rdsapi'; await initJSProjectWithProfile(projRoot, { disableAmplifyAppCreation: false, @@ -113,7 +113,7 @@ describe("RDS Tests", () => { const rdsSchemaFilePath = path.join(projRoot, 'amplify', 'backend', 'api', apiName, 'schema.rds.graphql'); await addApiWithoutSchema(projRoot, { transformerVersion: 2, apiName }); - + await importRDSDatabase(projRoot, { database, host, @@ -128,90 +128,92 @@ describe("RDS Tests", () => { const schema = parse(schemaContent); // Generated schema should contains the types and fields from the database - const contactsObjectType = schema.definitions.find(d => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Contacts') as ObjectTypeDefinitionNode; - const personObjectType = schema.definitions.find(d => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Person'); - const employeeObjectType = schema.definitions.find(d => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Employee'); + const contactsObjectType = schema.definitions.find( + (d) => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Contacts', + ) as ObjectTypeDefinitionNode; + const personObjectType = schema.definitions.find((d) => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Person'); + const employeeObjectType = schema.definitions.find((d) => d.kind === 'ObjectTypeDefinition' && d.name.value === 'Employee'); expect(contactsObjectType).toBeDefined(); expect(personObjectType).toBeDefined(); expect(employeeObjectType).toBeDefined(); // Verify the fields in the generated schema on type 'Contacts' - const contactsIdFieldType = contactsObjectType.fields.find(f => f.name.value === 'ID'); - const contactsFirstNameFieldType = contactsObjectType.fields.find(f => f.name.value === 'FirstName'); - const contactsLastNameFieldType = contactsObjectType.fields.find(f => f.name.value === 'LastName'); + const contactsIdFieldType = contactsObjectType.fields.find((f) => f.name.value === 'ID'); + const contactsFirstNameFieldType = contactsObjectType.fields.find((f) => f.name.value === 'FirstName'); + const contactsLastNameFieldType = contactsObjectType.fields.find((f) => f.name.value === 'LastName'); expect(contactsIdFieldType).toBeDefined(); expect(contactsFirstNameFieldType).toBeDefined(); expect(contactsLastNameFieldType).toBeDefined(); // PrimaryKey directive must be defined on Id field. - expect(contactsIdFieldType.directives.find(d => d.name.value === 'primaryKey')).toBeDefined(); + expect(contactsIdFieldType.directives.find((d) => d.name.value === 'primaryKey')).toBeDefined(); }); // This test must be updated if the rds layer mapping file is updated - test("check the rds layer mapping file on the service account is available", async () => { + test('check the rds layer mapping file on the service account is available', async () => { const rdsMappingFile = await axios.get(RDS_MAPPING_FILE); expect(rdsMappingFile).toBeDefined(); expect(rdsMappingFile.data).toBeDefined(); expect(rdsMappingFile.data).toMatchObject({ - "ap-northeast-1": { - "layerRegion": "arn:aws:lambda:ap-northeast-1:582037449441:layer:AmplifyRDSLayer:8" + 'ap-northeast-1': { + layerRegion: 'arn:aws:lambda:ap-northeast-1:582037449441:layer:AmplifyRDSLayer:8', + }, + 'us-east-1': { + layerRegion: 'arn:aws:lambda:us-east-1:582037449441:layer:AmplifyRDSLayer:8', }, - "us-east-1": { - "layerRegion": "arn:aws:lambda:us-east-1:582037449441:layer:AmplifyRDSLayer:8" + 'ap-southeast-1': { + layerRegion: 'arn:aws:lambda:ap-southeast-1:582037449441:layer:AmplifyRDSLayer:8', }, - "ap-southeast-1": { - "layerRegion": "arn:aws:lambda:ap-southeast-1:582037449441:layer:AmplifyRDSLayer:8" + 'eu-west-1': { + layerRegion: 'arn:aws:lambda:eu-west-1:582037449441:layer:AmplifyRDSLayer:8', }, - "eu-west-1": { - "layerRegion": "arn:aws:lambda:eu-west-1:582037449441:layer:AmplifyRDSLayer:8" + 'us-west-1': { + layerRegion: 'arn:aws:lambda:us-west-1:582037449441:layer:AmplifyRDSLayer:8', }, - "us-west-1": { - "layerRegion": "arn:aws:lambda:us-west-1:582037449441:layer:AmplifyRDSLayer:8" + 'ap-east-1': { + layerRegion: 'arn:aws:lambda:ap-east-1:582037449441:layer:AmplifyRDSLayer:8', }, - "ap-east-1": { - "layerRegion": "arn:aws:lambda:ap-east-1:582037449441:layer:AmplifyRDSLayer:8" + 'ap-northeast-2': { + layerRegion: 'arn:aws:lambda:ap-northeast-2:582037449441:layer:AmplifyRDSLayer:8', }, - "ap-northeast-2": { - "layerRegion": "arn:aws:lambda:ap-northeast-2:582037449441:layer:AmplifyRDSLayer:8" + 'ap-northeast-3': { + layerRegion: 'arn:aws:lambda:ap-northeast-3:582037449441:layer:AmplifyRDSLayer:8', }, - "ap-northeast-3": { - "layerRegion": "arn:aws:lambda:ap-northeast-3:582037449441:layer:AmplifyRDSLayer:8" + 'ap-south-1': { + layerRegion: 'arn:aws:lambda:ap-south-1:582037449441:layer:AmplifyRDSLayer:8', }, - "ap-south-1": { - "layerRegion": "arn:aws:lambda:ap-south-1:582037449441:layer:AmplifyRDSLayer:8" + 'ap-southeast-2': { + layerRegion: 'arn:aws:lambda:ap-southeast-2:582037449441:layer:AmplifyRDSLayer:8', }, - "ap-southeast-2": { - "layerRegion": "arn:aws:lambda:ap-southeast-2:582037449441:layer:AmplifyRDSLayer:8" + 'ca-central-1': { + layerRegion: 'arn:aws:lambda:ca-central-1:582037449441:layer:AmplifyRDSLayer:8', }, - "ca-central-1": { - "layerRegion": "arn:aws:lambda:ca-central-1:582037449441:layer:AmplifyRDSLayer:8" + 'eu-central-1': { + layerRegion: 'arn:aws:lambda:eu-central-1:582037449441:layer:AmplifyRDSLayer:8', }, - "eu-central-1": { - "layerRegion": "arn:aws:lambda:eu-central-1:582037449441:layer:AmplifyRDSLayer:8" + 'eu-north-1': { + layerRegion: 'arn:aws:lambda:eu-north-1:582037449441:layer:AmplifyRDSLayer:8', }, - "eu-north-1": { - "layerRegion": "arn:aws:lambda:eu-north-1:582037449441:layer:AmplifyRDSLayer:8" + 'eu-west-2': { + layerRegion: 'arn:aws:lambda:eu-west-2:582037449441:layer:AmplifyRDSLayer:8', }, - "eu-west-2": { - "layerRegion": "arn:aws:lambda:eu-west-2:582037449441:layer:AmplifyRDSLayer:8" + 'eu-west-3': { + layerRegion: 'arn:aws:lambda:eu-west-3:582037449441:layer:AmplifyRDSLayer:8', }, - "eu-west-3": { - "layerRegion": "arn:aws:lambda:eu-west-3:582037449441:layer:AmplifyRDSLayer:8" + 'sa-east-1': { + layerRegion: 'arn:aws:lambda:sa-east-1:582037449441:layer:AmplifyRDSLayer:8', }, - "sa-east-1": { - "layerRegion": "arn:aws:lambda:sa-east-1:582037449441:layer:AmplifyRDSLayer:8" + 'us-east-2': { + layerRegion: 'arn:aws:lambda:us-east-2:582037449441:layer:AmplifyRDSLayer:8', }, - "us-east-2": { - "layerRegion": "arn:aws:lambda:us-east-2:582037449441:layer:AmplifyRDSLayer:8" + 'us-west-2': { + layerRegion: 'arn:aws:lambda:us-west-2:582037449441:layer:AmplifyRDSLayer:8', }, - "us-west-2": { - "layerRegion": "arn:aws:lambda:us-west-2:582037449441:layer:AmplifyRDSLayer:8" + 'me-south-1': { + layerRegion: 'arn:aws:lambda:me-south-1:582037449441:layer:AmplifyRDSLayer:8', }, - "me-south-1": { - "layerRegion": "arn:aws:lambda:me-south-1:582037449441:layer:AmplifyRDSLayer:8" - } }); }); -}); +}); diff --git a/packages/amplify-graphql-relational-transformer/src/__tests__/amplify-graphql-has-many-transformer.test.ts b/packages/amplify-graphql-relational-transformer/src/__tests__/amplify-graphql-has-many-transformer.test.ts index b6e794c0e4..3afbef477f 100644 --- a/packages/amplify-graphql-relational-transformer/src/__tests__/amplify-graphql-has-many-transformer.test.ts +++ b/packages/amplify-graphql-relational-transformer/src/__tests__/amplify-graphql-has-many-transformer.test.ts @@ -912,7 +912,6 @@ describe('@hasMany connection field nullability tests', () => { }); describe('@hasMany directive with RDS datasource', () => { - test('happy case should generate correct resolvers', () => { const modelToDatasourceMap = new Map(); modelToDatasourceMap.set('Blog', { @@ -992,5 +991,4 @@ describe('@hasMany directive with RDS datasource', () => { expect(out.resolvers['System.parts.res.vtl']).toBeDefined(); expect(out.resolvers['System.parts.res.vtl']).toMatchSnapshot(); }); - }); diff --git a/packages/amplify-graphql-relational-transformer/src/__tests__/resolvers.test.ts b/packages/amplify-graphql-relational-transformer/src/__tests__/resolvers.test.ts index dc0a767fd1..a1c68f51df 100644 --- a/packages/amplify-graphql-relational-transformer/src/__tests__/resolvers.test.ts +++ b/packages/amplify-graphql-relational-transformer/src/__tests__/resolvers.test.ts @@ -21,7 +21,7 @@ describe('makeQueryConnectionWithKeyResolver', () => { test('it requires either fields or connection fields to be populated with values', () => { const ddbGenerator = new DDBRelationalResolverGenerator(); expect(() => - ddbGenerator.makeQueryConnectionWithKeyResolver( + ddbGenerator.makeQueryConnectionWithKeyResolver( createPartialMock({ fields: [], connectionFields: [], diff --git a/packages/amplify-graphql-relational-transformer/src/graphql-has-many-transformer.ts b/packages/amplify-graphql-relational-transformer/src/graphql-has-many-transformer.ts index dc3cdeb7f4..b524be1080 100644 --- a/packages/amplify-graphql-relational-transformer/src/graphql-has-many-transformer.ts +++ b/packages/amplify-graphql-relational-transformer/src/graphql-has-many-transformer.ts @@ -186,25 +186,23 @@ const validate = (config: HasManyDirectiveConfiguration, ctx: TransformerContext const dbType = ctx.modelToDatasourceMap.get(getBaseType(field.type))?.dbType ?? DDB_DB_TYPE; config.relatedType = getRelatedType(config, ctx); - + if (dbType === DDB_DB_TYPE) { ensureFieldsArray(config); config.fieldNodes = getFieldsNodes(config, ctx); } - + if (dbType === MYSQL_DB_TYPE) { ensureReferencesArray(config); getReferencesNodes(config, ctx); } - + validateModelDirective(config); if (!isListType(field.type)) { throw new InvalidDirectiveError(`@${directiveName} must be used with a list. Use @hasOne for non-list types.`); } - - config.connectionFields = []; validateRelatedModelDirective(config); validateDisallowedDataStoreRelationships(config, ctx); diff --git a/packages/amplify-graphql-relational-transformer/src/graphql-many-to-many-transformer.ts b/packages/amplify-graphql-relational-transformer/src/graphql-many-to-many-transformer.ts index 530adf3c5c..45dceefe93 100644 --- a/packages/amplify-graphql-relational-transformer/src/graphql-many-to-many-transformer.ts +++ b/packages/amplify-graphql-relational-transformer/src/graphql-many-to-many-transformer.ts @@ -492,7 +492,7 @@ export class ManyToManyTransformer extends TransformerPluginBase { for (const config of this.directiveList) { updateTableForConnection(config, context); - (new DDBRelationalResolverGenerator()).makeQueryConnectionWithKeyResolver(config, context); + new DDBRelationalResolverGenerator().makeQueryConnectionWithKeyResolver(config, context); } }; } diff --git a/packages/amplify-graphql-relational-transformer/src/resolver/ddb-generator.ts b/packages/amplify-graphql-relational-transformer/src/resolver/ddb-generator.ts index 24893fb876..5c3cc49f7c 100644 --- a/packages/amplify-graphql-relational-transformer/src/resolver/ddb-generator.ts +++ b/packages/amplify-graphql-relational-transformer/src/resolver/ddb-generator.ts @@ -1,9 +1,39 @@ -import { MappingTemplate, getKeySchema, getTable } from "@aws-amplify/graphql-transformer-core"; -import { TransformerContextProvider } from "@aws-amplify/graphql-transformer-interfaces"; -import { DynamoDBMappingTemplate, Expression, ObjectNode, bool, compoundExpression, equals, ifElse, iff, int, isNullOrEmpty, list, methodCall, not, nul, obj, print, qref, raw, ref, set, str } from "graphql-mapping-template"; -import { ModelResourceIDs, ResolverResourceIDs, applyCompositeKeyConditionExpression, applyKeyConditionExpression, attributeTypeFromScalar, setArgs, toCamelCase } from "graphql-transformer-common"; -import { HasManyDirectiveConfiguration } from "../types"; -import { RelationalResolverGenerator } from "./generator"; +import { MappingTemplate, getKeySchema, getTable } from '@aws-amplify/graphql-transformer-core'; +import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces'; +import { + DynamoDBMappingTemplate, + Expression, + ObjectNode, + bool, + compoundExpression, + equals, + ifElse, + iff, + int, + isNullOrEmpty, + list, + methodCall, + not, + nul, + obj, + print, + qref, + raw, + ref, + set, + str, +} from 'graphql-mapping-template'; +import { + ModelResourceIDs, + ResolverResourceIDs, + applyCompositeKeyConditionExpression, + applyKeyConditionExpression, + attributeTypeFromScalar, + setArgs, + toCamelCase, +} from 'graphql-transformer-common'; +import { HasManyDirectiveConfiguration } from '../types'; +import { RelationalResolverGenerator } from './generator'; const SORT_KEY_VALUE = 'sortKeyValue'; const CONNECTION_STACK = 'ConnectionStack'; @@ -11,19 +41,18 @@ const authFilter = ref('ctx.stash.authFilter'); const PARTITION_KEY_VALUE = 'partitionKeyValue'; export class DDBRelationalResolverGenerator implements RelationalResolverGenerator { - makeExpression = (keySchema: any[], connectionAttributes: string[]): ObjectNode => { if (keySchema[1] && connectionAttributes[1]) { let condensedSortKeyValue; - + if (connectionAttributes.length > 2) { const rangeKeyFields = connectionAttributes.slice(1); - + condensedSortKeyValue = rangeKeyFields .map((keyField, idx) => `\${${SORT_KEY_VALUE}${idx}}`) .join(ModelResourceIDs.ModelCompositeKeySeparator()); } - + return obj({ expression: str('#partitionKey = :partitionKey AND #sortKey = :sortKey'), expressionNames: obj({ @@ -36,7 +65,7 @@ export class DDBRelationalResolverGenerator implements RelationalResolverGenerat }), }); } - + return obj({ expression: str('#partitionKey = :partitionKey'), expressionNames: obj({ @@ -46,7 +75,7 @@ export class DDBRelationalResolverGenerator implements RelationalResolverGenerat ':partitionKey': ref(`util.dynamodb.toDynamoDB($${PARTITION_KEY_VALUE})`), }), }); - } + }; /** * Create a resolver that queries an item in DynamoDB. @@ -74,18 +103,18 @@ export class DDBRelationalResolverGenerator implements RelationalResolverGenerat ), set(ref('query'), this.makeExpression(keySchema, connectionAttributes)), ]; - + // If the key schema has a sort key but one is not provided for the query, let a sort key be // passed in via $ctx.args. if (keySchema[1] && !connectionAttributes[1]) { const sortKeyFieldName = keySchema[1].attributeName; const sortKeyField = relatedType.fields!.find((f) => f.name.value === sortKeyFieldName); - + if (sortKeyField) { setup.push(applyKeyConditionExpression(sortKeyFieldName, attributeTypeFromScalar(sortKeyField.type), 'query')); } else { const sortKeyFieldNames = sortKeyFieldName.split(ModelResourceIDs.ModelCompositeKeySeparator()); - + setup.push(applyCompositeKeyConditionExpression(sortKeyFieldNames, 'query', toCamelCase(sortKeyFieldNames), sortKeyFieldName)); } } @@ -120,7 +149,7 @@ export class DDBRelationalResolverGenerator implements RelationalResolverGenerat ]), ), ); - + const queryArguments = { query: raw('$util.toJson($query)'), scanIndexForward: ifElse( @@ -132,11 +161,11 @@ export class DDBRelationalResolverGenerator implements RelationalResolverGenerat limit: ref('limit'), nextToken: ifElse(ref('context.args.nextToken'), ref('util.toJson($context.args.nextToken)'), nul()), } as any; - + if (indexName) { queryArguments.index = str(indexName); } - + const queryObj = DynamoDBMappingTemplate.query(queryArguments); const resolverResourceId = ResolverResourceIDs.ResolverResourceID(object.name.value, field.name.value); const resolver = ctx.resolvers.generateQueryResolver( @@ -175,9 +204,8 @@ export class DDBRelationalResolverGenerator implements RelationalResolverGenerat `${object.name.value}.${field.name.value}.res.vtl`, ), ); - + resolver.mapToStack(ctx.stackManager.getStackFor(resolverResourceId, CONNECTION_STACK)); ctx.resolvers.addResolver(object.name.value, field.name.value, resolver); - } - + }; } diff --git a/packages/amplify-graphql-relational-transformer/src/resolver/rds-generator.ts b/packages/amplify-graphql-relational-transformer/src/resolver/rds-generator.ts index 0bb247c3cf..73d356630b 100644 --- a/packages/amplify-graphql-relational-transformer/src/resolver/rds-generator.ts +++ b/packages/amplify-graphql-relational-transformer/src/resolver/rds-generator.ts @@ -1,14 +1,28 @@ import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces'; import { ResolverResourceIDs, ResourceConstants } from 'graphql-transformer-common'; import { MappingTemplate, getPrimaryKeyFields } from '@aws-amplify/graphql-transformer-core'; -import { compoundExpression, ref, set, methodCall, ifElse, printBlock, qref, obj, str, list, Expression, toJson, iff, not } from 'graphql-mapping-template'; +import { + compoundExpression, + ref, + set, + methodCall, + ifElse, + printBlock, + qref, + obj, + str, + list, + Expression, + toJson, + iff, + not, +} from 'graphql-mapping-template'; import { HasManyDirectiveConfiguration } from '../types'; import { RelationalResolverGenerator } from './generator'; const CONNECTION_STACK = 'ConnectionStack'; export class RDSRelationalResolverGenerator implements RelationalResolverGenerator { - /** * Create a resolver that queries an item in RDS. * @param config The connection directive configuration. @@ -18,17 +32,19 @@ export class RDSRelationalResolverGenerator implements RelationalResolverGenerat const { field, references, limit, object, relatedType } = config; const { RDSLambdaDataSourceLogicalID } = ResourceConstants.RESOURCES; const dataSource = ctx.api.host.getDataSource(RDSLambdaDataSourceLogicalID); - + const connectionCondition: Expression[] = []; const primaryKeys = getPrimaryKeyFields(object); references.forEach((r, index) => { connectionCondition.push( - qref(methodCall( - ref('lambdaInput.args.filter.put'), - str(r), - obj({ eq: ref(`util.defaultIfNull($ctx.source.${primaryKeys[index]}, "")`) }), + qref( + methodCall( + ref('lambdaInput.args.filter.put'), + str(r), + obj({ eq: ref(`util.defaultIfNull($ctx.source.${primaryKeys[index]}, "")`) }), + ), ), - )); + ); }); const resolverResourceId = ResolverResourceIDs.ResolverResourceID(object.name.value, field.name.value); const resolver = ctx.resolvers.generateQueryResolver( @@ -45,10 +61,10 @@ export class RDSRelationalResolverGenerator implements RelationalResolverGenerat `${object.name.value}.${field.name.value}.res.vtl`, ), ); - + resolver.mapToStack(ctx.stackManager.getStackFor(resolverResourceId, CONNECTION_STACK)); ctx.resolvers.addResolver(object.name.value, field.name.value, resolver); - } + }; /** * Generate connection request template for RDS. @@ -94,5 +110,4 @@ export class RDSRelationalResolverGenerator implements RelationalResolverGenerat ); return printBlock('ResponseTemplate')(compoundExpression(statements)); }; - } diff --git a/packages/amplify-graphql-relational-transformer/src/utils.ts b/packages/amplify-graphql-relational-transformer/src/utils.ts index 6b63d028b3..0fda3d6f5d 100644 --- a/packages/amplify-graphql-relational-transformer/src/utils.ts +++ b/packages/amplify-graphql-relational-transformer/src/utils.ts @@ -123,7 +123,9 @@ export function ensureFieldsArray(config: HasManyDirectiveConfiguration | HasOne } } -export function ensureReferencesArray(config: HasManyDirectiveConfiguration | HasOneDirectiveConfiguration | BelongsToDirectiveConfiguration) { +export function ensureReferencesArray( + config: HasManyDirectiveConfiguration | HasOneDirectiveConfiguration | BelongsToDirectiveConfiguration, +) { if (!config.references) { throw new InvalidDirectiveError(`References must be passed to @${config.directiveName} directive for RDS models.`); } else if (!Array.isArray(config.references)) { diff --git a/packages/amplify-graphql-transformer-core/API.md b/packages/amplify-graphql-transformer-core/API.md index 0ca5915d3f..1668c78185 100644 --- a/packages/amplify-graphql-transformer-core/API.md +++ b/packages/amplify-graphql-transformer-core/API.md @@ -202,6 +202,9 @@ export const getKeySchema: (table: any, indexName?: string) => any; // @public (undocumented) export const getParameterStoreSecretPath: (secret: string, secretsKey: string, apiName: string, environmentName: string, appId: string) => string; +// @public (undocumented) +export const getPrimaryKeyFields: (type: ObjectTypeDefinitionNode) => string[]; + // @public (undocumented) export const getSortKeyFieldNames: (type: ObjectTypeDefinitionNode) => string[];