diff --git a/.jest.js b/.jest.js index 283dceb..6c15308 100644 --- a/.jest.js +++ b/.jest.js @@ -1,5 +1,7 @@ export default { - 'transform': {}, + transform: { + '\\.[jt]sx?$': 'babel-jest', + }, 'verbose': true, 'testSequencer': './test/jestTestSequencer.js', 'globals': { diff --git a/package.json b/package.json index d7a7a7e..fe08fe4 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "files": [ "neptune-for-graphql.mjs", "./templates/CDKTemplate.js", - "./templates/JSResolverOCHTTPSTemplate.js", + "./templates/JSResolverOCHTTPSTemplate.mjs", "./templates/Lambda4AppSyncHTTP/index.mjs", "./templates/Lambda4AppSyncHTTP/package.json", "./templates/Lambda4AppSyncSDK/index.mjs", diff --git a/src/resolverJS.js b/src/resolverJS.js index 0b229ba..6c8d431 100644 --- a/src/resolverJS.js +++ b/src/resolverJS.js @@ -19,7 +19,7 @@ function resolverJS (schemaModel, queryLanguage, queryClient, __dirname) { if (queryLanguage == 'opencypher') { try { - code = readFileSync(__dirname + '/../templates/JSResolverOCHTTPSTemplate.js'); + code = readFileSync(__dirname + '/../templates/JSResolverOCHTTPSTemplate.mjs'); code = code.toString().replace('TIMESTAMP HERE', (new Date()).toISOString()); code = code.toString().replace('INSERT SCHEMA DATA MODEL HERE', queryDataModelJSON); } catch (err) { diff --git a/templates/CDKTemplate.js b/templates/CDKTemplate.js index 2647274..22639e4 100644 --- a/templates/CDKTemplate.js +++ b/templates/CDKTemplate.js @@ -10,11 +10,11 @@ express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const { Stack, Duration, App } = require('aws-cdk-lib'); -const lambda = require( 'aws-cdk-lib/aws-lambda'); -const iam = require( 'aws-cdk-lib/aws-iam'); -const ec2 = require( 'aws-cdk-lib/aws-ec2'); -const { CfnGraphQLApi, CfnApiKey, CfnGraphQLSchema, CfnDataSource, CfnResolver, CfnFunctionConfiguration } = require( 'aws-cdk-lib/aws-appsync'); +import { Stack, Duration, App } from 'aws-cdk-lib'; +import lambda from 'aws-cdk-lib/aws-lambda'; +import iam from 'aws-cdk-lib/aws-iam'; +import ec2 from 'aws-cdk-lib/aws-ec2'; +import { CfnGraphQLApi, CfnApiKey, CfnGraphQLSchema, CfnDataSource, CfnResolver, CfnFunctionConfiguration } from 'aws-cdk-lib/aws-appsync'; const NAME = ''; const REGION = ''; @@ -290,4 +290,4 @@ export function response(ctx) { } -module.exports = { AppSyncNeptuneStack } \ No newline at end of file +export { AppSyncNeptuneStack }; \ No newline at end of file diff --git a/templates/JSResolverOCHTTPSTemplate.js b/templates/JSResolverOCHTTPSTemplate.mjs similarity index 99% rename from templates/JSResolverOCHTTPSTemplate.js rename to templates/JSResolverOCHTTPSTemplate.mjs index 683893f..8bb7132 100644 --- a/templates/JSResolverOCHTTPSTemplate.js +++ b/templates/JSResolverOCHTTPSTemplate.mjs @@ -10,7 +10,7 @@ express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const gql = require('graphql-tag'); // GraphQL library to parse the GraphQL query +import gql from 'graphql-tag'; // GraphQL library to parse the GraphQL query const useCallSubquery = false; @@ -1017,7 +1017,6 @@ function resolveGremlinQuery(obj, querySchemaInfo) { return gremlinQuery; } - // Function takes the graphql query and output the graphDB query function resolveGraphDBQuery(query) { let executeQuery = { query:'', parameters: {}, language: 'opencypher', refactorOutput: null }; @@ -1048,4 +1047,4 @@ function resolveGraphDBQuery(query) { } -module.exports = { resolveGraphDBQueryFromAppSyncEvent, resolveGraphDBQueryFromApolloQueryEvent, resolveGraphDBQuery, refactorGremlinqueryOutput }; +export { resolveGraphDBQueryFromAppSyncEvent, resolveGraphDBQueryFromApolloQueryEvent, resolveGraphDBQuery, refactorGremlinqueryOutput }; \ No newline at end of file diff --git a/templates/Lambda4AppSyncHTTP/package.json b/templates/Lambda4AppSyncHTTP/package.json index 81e0f77..acd9acc 100644 --- a/templates/Lambda4AppSyncHTTP/package.json +++ b/templates/Lambda4AppSyncHTTP/package.json @@ -8,6 +8,7 @@ }, "author": "", "license": "ISC", + "type": "module", "dependencies": { "aws4-axios": "3.3.0", "axios": "1.7.4", diff --git a/templates/Lambda4AppSyncSDK/package.json b/templates/Lambda4AppSyncSDK/package.json index d566cc8..48cfac0 100644 --- a/templates/Lambda4AppSyncSDK/package.json +++ b/templates/Lambda4AppSyncSDK/package.json @@ -8,6 +8,7 @@ }, "author": "AWS", "license": "Apache-2.0", + "type": "module", "dependencies": { "@aws-sdk/client-neptunedata": "3.403.0", "graphql-tag": "2.12.6" diff --git a/test/TestCases/Case01/Case01.03.test.js b/test/TestCases/Case01/Case01.03.test.js index 69b12c7..2527bbc 100644 --- a/test/TestCases/Case01/Case01.03.test.js +++ b/test/TestCases/Case01/Case01.03.test.js @@ -3,4 +3,4 @@ import { jest } from '@jest/globals'; import { testResolverQueries } from '../../testLib'; -await testResolverQueries('./TestCases/Case01/output/output.resolver.graphql.cjs', './test/TestCases/Case01/queries'); \ No newline at end of file +await testResolverQueries('./TestCases/Case01/output/output.resolver.graphql.mjs', './test/TestCases/Case01/queries'); \ No newline at end of file diff --git a/test/TestCases/Case01/case.json b/test/TestCases/Case01/case.json index a592867..796be49 100644 --- a/test/TestCases/Case01/case.json +++ b/test/TestCases/Case01/case.json @@ -4,7 +4,7 @@ "argv":["--quiet", "--input-schema-file", "./test/TestCases/airports.source.schema.graphql", "--input-schema-changes-file", "./test/TestCases/Case01/input/changesAirport.json", - "--output-js-resolver-file", "./test/TestCases/Case01/output/output.resolver.graphql.cjs", + "--output-js-resolver-file", "./test/TestCases/Case01/output/output.resolver.graphql.mjs", "--output-folder-path", "./test/TestCases/Case01/output", "--output-no-lambda-zip"], "host": "", diff --git a/test/TestCases/Case01/outputReference/output.resolver.graphql.js b/test/TestCases/Case01/outputReference/output.resolver.graphql.js index 21d16ba..165d228 100644 --- a/test/TestCases/Case01/outputReference/output.resolver.graphql.js +++ b/test/TestCases/Case01/outputReference/output.resolver.graphql.js @@ -10,11 +10,11 @@ express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const gql = require('graphql-tag'); // GraphQL library to parse the GraphQL query +import gql from 'graphql-tag'; // GraphQL library to parse the GraphQL query const useCallSubquery = false; -// 2024-11-19T00:06:35.400Z +// 2024-12-04T00:38:40.575Z const schemaDataModelJSON = `{ "kind": "Document", @@ -2000,7 +2000,7 @@ const schemaDataModelJSON = `{ }, "value": { "kind": "StringValue", - "value": "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport)-[:route]->(:airport{code:'$toCode'})", + "value": "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport{country:'$country'})-[:route]->(:airport{code:'$toCode'})", "block": false } } @@ -3465,7 +3465,7 @@ const schemaDataModelJSON = `{ ], "loc": { "start": 0, - "end": 4710 + "end": 4730 } }`; @@ -4468,7 +4468,6 @@ function resolveGremlinQuery(obj, querySchemaInfo) { return gremlinQuery; } - // Function takes the graphql query and output the graphDB query function resolveGraphDBQuery(query) { let executeQuery = { query:'', parameters: {}, language: 'opencypher', refactorOutput: null }; @@ -4499,4 +4498,4 @@ function resolveGraphDBQuery(query) { } -module.exports = { resolveGraphDBQueryFromAppSyncEvent, resolveGraphDBQueryFromApolloQueryEvent, resolveGraphDBQuery, refactorGremlinqueryOutput }; +export { resolveGraphDBQueryFromAppSyncEvent, resolveGraphDBQueryFromApolloQueryEvent, resolveGraphDBQuery, refactorGremlinqueryOutput }; \ No newline at end of file diff --git a/test/TestCases/Case01/outputReference/output.resolver.graphql.cjs b/test/TestCases/Case01/outputReference/output.resolver.graphql.mjs similarity index 99% rename from test/TestCases/Case01/outputReference/output.resolver.graphql.cjs rename to test/TestCases/Case01/outputReference/output.resolver.graphql.mjs index 21d16ba..165d228 100644 --- a/test/TestCases/Case01/outputReference/output.resolver.graphql.cjs +++ b/test/TestCases/Case01/outputReference/output.resolver.graphql.mjs @@ -10,11 +10,11 @@ express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const gql = require('graphql-tag'); // GraphQL library to parse the GraphQL query +import gql from 'graphql-tag'; // GraphQL library to parse the GraphQL query const useCallSubquery = false; -// 2024-11-19T00:06:35.400Z +// 2024-12-04T00:38:40.575Z const schemaDataModelJSON = `{ "kind": "Document", @@ -2000,7 +2000,7 @@ const schemaDataModelJSON = `{ }, "value": { "kind": "StringValue", - "value": "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport)-[:route]->(:airport{code:'$toCode'})", + "value": "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport{country:'$country'})-[:route]->(:airport{code:'$toCode'})", "block": false } } @@ -3465,7 +3465,7 @@ const schemaDataModelJSON = `{ ], "loc": { "start": 0, - "end": 4710 + "end": 4730 } }`; @@ -4468,7 +4468,6 @@ function resolveGremlinQuery(obj, querySchemaInfo) { return gremlinQuery; } - // Function takes the graphql query and output the graphDB query function resolveGraphDBQuery(query) { let executeQuery = { query:'', parameters: {}, language: 'opencypher', refactorOutput: null }; @@ -4499,4 +4498,4 @@ function resolveGraphDBQuery(query) { } -module.exports = { resolveGraphDBQueryFromAppSyncEvent, resolveGraphDBQueryFromApolloQueryEvent, resolveGraphDBQuery, refactorGremlinqueryOutput }; +export { resolveGraphDBQueryFromAppSyncEvent, resolveGraphDBQueryFromApolloQueryEvent, resolveGraphDBQuery, refactorGremlinqueryOutput }; \ No newline at end of file diff --git a/test/TestCases/Case01/outputReference/output.source.schema.graphql b/test/TestCases/Case01/outputReference/output.source.schema.graphql index 127e262..acdea60 100644 --- a/test/TestCases/Case01/outputReference/output.source.schema.graphql +++ b/test/TestCases/Case01/outputReference/output.source.schema.graphql @@ -107,7 +107,7 @@ input Options { type Query { getAirport(code: String): Airport - getAirportConnection(fromCode: String!, toCode: String!): Airport @cypher(statement: "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport)-[:route]->(:airport{code:'$toCode'})") + getAirportConnection(fromCode: String!, toCode: String!): Airport @cypher(statement: "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport{country:'$country'})-[:route]->(:airport{code:'$toCode'})") getAirportWithGremlin(code: String): Airport @graphQuery(statement: "g.V().has('airport', 'code', '$code').elementMap()") getContinentsWithGremlin: [Continent] @graphQuery(statement: "g.V().hasLabel('continent').elementMap().fold()") getCountriesCountGremlin: Int @graphQuery(statement: "g.V().hasLabel('country').count()") diff --git a/test/TestCases/Case01/queries/Query0007.json b/test/TestCases/Case01/queries/Query0007.json index f41436d..cbd4d24 100644 --- a/test/TestCases/Case01/queries/Query0007.json +++ b/test/TestCases/Case01/queries/Query0007.json @@ -1,11 +1,11 @@ { "name": "graphQuery type", - "description": "Query using a graphQuery returing a type", - "graphql": "query MyQuery {\n getAirportConnection(fromCode: \"SEA\", toCode: \"BLQ\") {\n city\n code\n }\n }\n", + "description": "Query using a graphQuery returning a type", + "graphql": "query MyQuery {\n getAirportConnection(fromCode: \"SEA\", country: \"US\", toCode: \"BLQ\") {\n city\n code\n }\n }\n", "parameters": {}, - "resolved": "MATCH (:airport{code: 'SEA'})-[:route]->(getAirportConnection_Airport:airport)-[:route]->(:airport{code:'BLQ'})\nRETURN {city: getAirportConnection_Airport.`city`, code: getAirportConnection_Airport.`code`} LIMIT 1", + "resolved": "MATCH (:airport{code: 'SEA'})-[:route]->(getAirportConnection_Airport:airport{country:'US'})-[:route]->(:airport{code:'BLQ'})\nRETURN {city: getAirportConnection_Airport.`city`, code: getAirportConnection_Airport.`code`} LIMIT 1", "result":{ - "code": "LHR", - "city": "London" + "code": "PHL", + "city": "Philadelphia" } } \ No newline at end of file diff --git a/test/TestCases/Case05/case02.json b/test/TestCases/Case05/case02.json index 006bc71..87b63fc 100644 --- a/test/TestCases/Case05/case02.json +++ b/test/TestCases/Case05/case02.json @@ -5,7 +5,7 @@ "--remove-aws-pipeline-name", "AirportsJestTest", "--output-folder-path", "./test/TestCases/Case05/output"], "host": "", - "port": "", + "port": "", "testOutputFilesSize": ["output.resolver.graphql.js", "output.schema.graphql", "output.source.schema.graphql"], "testOutputFilesContent": ["output.schema.graphql", "output.source.schema.graphql"] } \ No newline at end of file diff --git a/test/TestCases/Case07/outputReference/output.resolver.graphql.js b/test/TestCases/Case07/outputReference/output.resolver.graphql.js index 0ec193c..339aa5e 100644 --- a/test/TestCases/Case07/outputReference/output.resolver.graphql.js +++ b/test/TestCases/Case07/outputReference/output.resolver.graphql.js @@ -10,11 +10,11 @@ express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const gql = require('graphql-tag'); // GraphQL library to parse the GraphQL query +import gql from 'graphql-tag'; // GraphQL library to parse the GraphQL query const useCallSubquery = false; -// 2024-11-19T00:07:26.413Z +// 2024-12-04T00:39:19.017Z const schemaDataModelJSON = `{ "kind": "Document", @@ -1962,7 +1962,7 @@ const schemaDataModelJSON = `{ }, "value": { "kind": "StringValue", - "value": "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport)-[:route]->(:airport{code:'$toCode'})", + "value": "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport{country:'$country'})-[:route]->(:airport{code:'$toCode'})", "block": false } } @@ -3497,7 +3497,7 @@ const schemaDataModelJSON = `{ ], "loc": { "start": 0, - "end": 4915 + "end": 4935 } }`; @@ -4500,7 +4500,6 @@ function resolveGremlinQuery(obj, querySchemaInfo) { return gremlinQuery; } - // Function takes the graphql query and output the graphDB query function resolveGraphDBQuery(query) { let executeQuery = { query:'', parameters: {}, language: 'opencypher', refactorOutput: null }; @@ -4531,4 +4530,4 @@ function resolveGraphDBQuery(query) { } -module.exports = { resolveGraphDBQueryFromAppSyncEvent, resolveGraphDBQueryFromApolloQueryEvent, resolveGraphDBQuery, refactorGremlinqueryOutput }; +export { resolveGraphDBQueryFromAppSyncEvent, resolveGraphDBQueryFromApolloQueryEvent, resolveGraphDBQuery, refactorGremlinqueryOutput }; \ No newline at end of file diff --git a/test/TestCases/Case07/outputReference/output.source.schema.graphql b/test/TestCases/Case07/outputReference/output.source.schema.graphql index 21ebf4b..878103b 100644 --- a/test/TestCases/Case07/outputReference/output.source.schema.graphql +++ b/test/TestCases/Case07/outputReference/output.source.schema.graphql @@ -106,7 +106,7 @@ input Options { type Query { getAirport(code: String): Airport - getAirportConnection(fromCode: String!, toCode: String!): Airport @cypher(statement: "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport)-[:route]->(:airport{code:'$toCode'})") + getAirportConnection(fromCode: String!, toCode: String!): Airport @cypher(statement: "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport{country:'$country'})-[:route]->(:airport{code:'$toCode'})") getAirportWithGremlin(code: String): Airport @graphQuery(statement: "g.V().has('airport', 'code', '$code').elementMap()") getContinentsWithGremlin: [Continent] @graphQuery(statement: "g.V().hasLabel('continent').elementMap().fold()") getCountriesCountGremlin: Int @graphQuery(statement: "g.V().hasLabel('country').count()") diff --git a/test/TestCases/airports.source.schema.graphql b/test/TestCases/airports.source.schema.graphql index 35e3768..5b90341 100644 --- a/test/TestCases/airports.source.schema.graphql +++ b/test/TestCases/airports.source.schema.graphql @@ -106,7 +106,7 @@ input Options { type Query { getAirport(code: String): Airport - getAirportConnection(fromCode: String!, toCode: String!): Airport @cypher(statement: "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport)-[:route]->(:airport{code:'$toCode'})") + getAirportConnection(fromCode: String!, toCode: String!): Airport @cypher(statement: "MATCH (:airport{code: '$fromCode'})-[:route]->(this:airport{country:'$country'})-[:route]->(:airport{code:'$toCode'})") getAirportWithGremlin(code:String): Airport @graphQuery(statement: "g.V().has('airport', 'code', '$code').elementMap()") getContinentsWithGremlin: [Continent] @graphQuery(statement: "g.V().hasLabel('continent').elementMap().fold()") getCountriesCountGremlin: Int @graphQuery(statement: "g.V().hasLabel('country').count()") diff --git a/test/testLib.js b/test/testLib.mjs similarity index 89% rename from test/testLib.js rename to test/testLib.mjs index b4b67ed..b95dcc7 100644 --- a/test/testLib.js +++ b/test/testLib.mjs @@ -128,11 +128,13 @@ async function testResolverQueriesResults(resolverFile, queriesReferenceFolder, const query = JSON.parse(fs.readFileSync(queriesReferenceFolder + "/" +queryFile)); if (query.graphql != "") { const result = resolverModule.resolveGraphDBQuery(query.graphql); + const httpResult = await queryNeptune(query.resolved, result.language, host, port, result.parameters); - + let data = null; - if (result.language == 'opencypher') + if (result.language == 'opencypher') { data = httpResult.results[0][Object.keys(httpResult.results[0])[0]]; + } else { const input = httpResult.result.data; data = JSON.parse(resolverModule.refactorGremlinqueryOutput(input, result.fieldsAlias)); @@ -141,12 +143,20 @@ async function testResolverQueriesResults(resolverFile, queriesReferenceFolder, if (JSON.stringify(data, null, 2) != JSON.stringify(query.result, null, 2)) console.log(JSON.stringify(data, null, 2)); - test(`Resolver Neptune result, ${queryFile}: ${query.name}`, async () => { - expect(JSON.stringify(data, null, 2)).toBe(JSON.stringify(query.result, null, 2)); + test(`Resolver Neptune result, ${queryFile}: ${query.name}`, async () => { + if(typeof query.result === 'number') { // if number + expect(data).toBe(query.result); + } + else if (Object.keys(query.result).length === 1 && Array.isArray(Object.values(query.result)[0])) { // if ONLY single array of objects + expect(data[Object.keys(query.result)[0]]).toEqual(expect.arrayContaining(query.result[Object.keys(query.result)[0]])); + } + else { // if objects or array + objects + expect(data).toEqual(query.result); + } }); } } } -export { readJSONFile, checkFileContains, checkOutputFilesSize, checkOutputFilesContent, testResolverQueries, testResolverQueriesResults, checkOutputZipLambdaUsesSdk }; +export { readJSONFile, checkFileContains, checkOutputFilesSize, checkOutputFilesContent, testResolverQueries, testResolverQueriesResults, checkOutputZipLambdaUsesSdk }; \ No newline at end of file