diff --git a/src/CDKPipelineApp.js b/src/CDKPipelineApp.js index 4dc8342..b43449d 100644 --- a/src/CDKPipelineApp.js +++ b/src/CDKPipelineApp.js @@ -167,6 +167,10 @@ async function createAWSpipelineCDK({ CDKFile = CDKFile.replace( "const NEPTUNE_DB_NAME = '';", `const NEPTUNE_DB_NAME = '${NEPTUNE_DB_NAME}';` ); CDKFile = CDKFile.replace( "const NEPTUNE_TYPE = '';", `const NEPTUNE_TYPE = '${NEPTUNE_TYPE}';` ); CDKFile = CDKFile.replace( "const NEPTUNE_DBSubnetGroup = null;", `const NEPTUNE_DBSubnetGroup = '${NEPTUNE_DBSubnetGroup}';` ); + if (neptuneClusterInfo) { + CDKFile = CDKFile.replace("const NEPTUNE_DBSubnetIds = null;", `const NEPTUNE_DBSubnetIds = '${neptuneClusterInfo.dbSubnetIds}';`); + CDKFile = CDKFile.replace("const NEPTUNE_VpcSecurityGroupId = null;",`const NEPTUNE_VpcSecurityGroupId = '${neptuneClusterInfo.vpcSecurityGroupId}';`); + } CDKFile = CDKFile.replace( "const NEPTUNE_IAM_AUTH = false;", `const NEPTUNE_IAM_AUTH = ${isNeptuneIAMAuth};` ); CDKFile = CDKFile.replace( "const NEPTUNE_IAM_POLICY_RESOURCE = '*';", `const NEPTUNE_IAM_POLICY_RESOURCE = '${NEPTUNE_IAM_POLICY_RESOURCE}';` ); diff --git a/src/NeptuneSchema.js b/src/NeptuneSchema.js index 25aa474..1baccf9 100644 --- a/src/NeptuneSchema.js +++ b/src/NeptuneSchema.js @@ -15,7 +15,6 @@ import { aws4Interceptor } from "aws4-axios"; import { fromNodeProviderChain } from "@aws-sdk/credential-providers"; import { NeptunedataClient, ExecuteOpenCypherQueryCommand } from "@aws-sdk/client-neptunedata"; import { loggerDebug, loggerError, loggerInfo, yellow } from "./logger.js"; -import { parseNeptuneDomainFromHost, parseNeptuneGraphName } from "./util.js"; import { ExecuteQueryCommand, GetGraphSummaryCommand, NeptuneGraphClient } from "@aws-sdk/client-neptune-graph"; const NEPTUNE_DB = 'neptune-db'; @@ -25,7 +24,8 @@ const HTTP_LANGUAGE = 'openCypher'; const NEPTUNE_GRAPH_LANGUAGE = 'OPEN_CYPHER'; let HOST = ''; let PORT = 8182; -let REGION = '' +let REGION = ''; +let DOMAIN = ''; let SAMPLE = 5000; let NEPTUNE_TYPE = NEPTUNE_DB; let NAME = ''; @@ -331,12 +331,13 @@ async function getEdgesDirectionsCardinality() { } -function setGetNeptuneSchemaParameters(host, port, region, neptuneType) { - HOST = host; - PORT = port; - REGION = region; - NEPTUNE_TYPE = neptuneType; - NAME = parseNeptuneGraphName(host); +function setGetNeptuneSchemaParameters(neptuneInfo) { + HOST = neptuneInfo.host; + PORT = neptuneInfo.port; + REGION = neptuneInfo.region; + NEPTUNE_TYPE = neptuneInfo.neptuneType; + NAME = neptuneInfo.graphName; + DOMAIN = neptuneInfo.domain; } function getNeptunedataClient() { @@ -354,7 +355,7 @@ function getNeptuneGraphClient() { loggerInfo('Instantiating NeptuneGraphClient') neptuneGraphClient = new NeptuneGraphClient({ port: PORT, - host: parseNeptuneDomainFromHost(HOST), + host: DOMAIN, region: REGION, protocol: NEPTUNE_GRAPH_PROTOCOL, }); diff --git a/src/main.js b/src/main.js index 20cc3ed..75a1170 100644 --- a/src/main.js +++ b/src/main.js @@ -29,7 +29,7 @@ let spinner = null; // find global installation dir import path from 'path'; import { fileURLToPath } from 'url'; -import { parseNeptuneDomainFromEndpoint } from "./util.js"; +import { parseNeptuneEndpoint } from "./util.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -63,7 +63,7 @@ let createUpdatePipelineNeptuneDatabaseName = ''; let removePipelineName = ''; let inputCDKpipeline = false; let inputCDKpipelineName = ''; -let inputCDKpipelineEnpoint = ''; +let inputCDKpipelineEndpoint = ''; let inputCDKpipelineFile = ''; let inputCDKpipelineRegion = ''; let inputCDKpipelineDatabaseName = ''; @@ -212,10 +212,14 @@ function processArgs() { inputCDKpipeline = true; break; case '-ce': + // support miss-spelled option for backwards compatibility - could be removed for next major release + case '--output-aws-pipeline-cdk-neptume-endpoint': case '--output-aws-pipeline-cdk-neptune-endpoint': - inputCDKpipelineEnpoint = array[index + 1]; + inputCDKpipelineEndpoint = array[index + 1]; break; case '-cd': + // support miss-spelled option for backwards compatibility - could be removed for next major release + case '--output-aws-pipeline-cdk-neptume-database-name': case '--output-aws-pipeline-cdk-neptune-database-name': inputCDKpipelineDatabaseName = array[index + 1]; break; @@ -285,37 +289,31 @@ async function main() { } } + + let neptuneInfo; // Check if any of the Neptune endpoints are a neptune analytic endpoint and if so, set the neptuneType and IAM to required - const nonEmptyEndpoints = [inputGraphDBSchemaNeptuneEndpoint, createUpdatePipelineEndpoint, inputCDKpipelineEnpoint].filter(endpoint => endpoint !== ''); - const isNeptuneAnalyticsGraph = nonEmptyEndpoints.length > 0 && parseNeptuneDomainFromEndpoint(nonEmptyEndpoints[0]).includes(NEPTUNE_GRAPH); - if (isNeptuneAnalyticsGraph) { - neptuneType = NEPTUNE_GRAPH; - // neptune analytics requires IAM - loggerInfo("Detected neptune-graph from input endpoint - setting IAM auth to true as it is required for neptune analytics") - isNeptuneIAMAuth = true; + // only one of these endpoints are expected to be non-empty at the same time + const nonEmptyEndpoints = [inputGraphDBSchemaNeptuneEndpoint, createUpdatePipelineEndpoint, inputCDKpipelineEndpoint].filter(endpoint => endpoint !== ''); + if (nonEmptyEndpoints.length > 0) { + neptuneInfo = parseNeptuneEndpoint(nonEmptyEndpoints[0]); + neptuneType = neptuneInfo.neptuneType; + if (neptuneType === NEPTUNE_GRAPH) { + // neptune analytics requires IAM + loggerInfo("Detected neptune-graph from input endpoint - setting IAM auth to true as it is required for neptune analytics") + isNeptuneIAMAuth = true; + } } // Get Neptune schema from endpoint if (inputGraphDBSchemaNeptuneEndpoint != '' && inputGraphDBSchema == '' && inputGraphDBSchemaFile == '') { - let endpointParts = inputGraphDBSchemaNeptuneEndpoint.split(':'); - if (endpointParts.length != 2) { - loggerError('Neptune endpoint must be in the form of host:port'); - process.exit(1); + if (!neptuneInfo) { + neptuneInfo = parseNeptuneEndpoint(inputGraphDBSchemaNeptuneEndpoint); } - let neptuneHost = endpointParts[0]; - let neptunePort = endpointParts[1]; - - let neptuneRegionParts = inputGraphDBSchemaNeptuneEndpoint.split('.'); - let neptuneRegion = ''; - if (neptuneType === NEPTUNE_DB) - neptuneRegion = neptuneRegionParts[2]; - else - neptuneRegion = neptuneRegionParts[1]; loggerInfo('Retrieving Neptune schema'); - loggerDebug('Getting Neptune schema from endpoint: ' + yellow(neptuneHost + ':' + neptunePort), {toConsole: true}); + loggerDebug('Getting Neptune schema from endpoint: ' + yellow(inputGraphDBSchemaNeptuneEndpoint), {toConsole: true}); - setGetNeptuneSchemaParameters(neptuneHost, neptunePort, neptuneRegion, neptuneType); + setGetNeptuneSchemaParameters(neptuneInfo); let startTime = performance.now(); inputGraphDBSchema = await getNeptuneSchema(); let endTime = performance.now(); @@ -372,15 +370,11 @@ async function main() { process.exit(1); } if (createUpdatePipelineEndpoint != '') { - let parts = createUpdatePipelineEndpoint.split('.'); - createUpdatePipelineNeptuneDatabaseName = parts[0]; - - let parsedRegion; - if (neptuneType === NEPTUNE_DB) { - parsedRegion = parts[2]; - } else { - parsedRegion = parts[1]; + if (!neptuneInfo) { + neptuneInfo = parseNeptuneEndpoint(createUpdatePipelineEndpoint); } + createUpdatePipelineNeptuneDatabaseName = neptuneInfo.graphName; + const parsedRegion = neptuneInfo.region; if (createUpdatePipelineRegion !== parsedRegion) { if (createUpdatePipelineRegion !== '') { @@ -399,27 +393,37 @@ async function main() { // CDK if (inputCDKpipeline) { if (!inputGraphDBSchemaNeptuneEndpoint == '') { - inputCDKpipelineEnpoint = inputGraphDBSchemaNeptuneEndpoint; + inputCDKpipelineEndpoint = inputGraphDBSchemaNeptuneEndpoint; } - if (inputCDKpipelineEnpoint == '' && + if (inputCDKpipelineEndpoint == '' && inputCDKpipelineRegion == '' && inputCDKpipelineDatabaseName == '') { loggerError('AWS CDK: is required a Neptune endpoint, or a Neptune database name and region.'); process.exit(1); } - if (inputCDKpipelineEnpoint == '' && + if (inputCDKpipelineEndpoint == '' && !inputCDKpipelineRegion == '' && inputCDKpipelineDatabaseName == '') { loggerError('AWS CDK: a Neptune database name is required.'); process.exit(1); } - if (inputCDKpipelineEnpoint == '' && + if (inputCDKpipelineEndpoint == '' && inputCDKpipelineRegion == '' && !inputCDKpipelineDatabaseName == '') { loggerError('AWS CDK: a Neptune database region is required.'); process.exit(1); } - if (inputCDKpipelineEnpoint != '') { - let parts = inputCDKpipelineEnpoint.split('.'); - inputCDKpipelineDatabaseName = parts[0]; - inputCDKpipelineRegion = parts[2]; + if (inputCDKpipelineEndpoint != '') { + if (!neptuneInfo) { + neptuneInfo = parseNeptuneEndpoint(inputCDKpipelineEndpoint); + } + inputCDKpipelineDatabaseName = neptuneInfo.graphName; + const parsedRegion = neptuneInfo.region; + if (inputCDKpipelineRegion !== parsedRegion) { + if (inputCDKpipelineRegion !== '') { + loggerInfo('Switching CDK region from ' + inputCDKpipelineRegion + ' to region parsed from endpoint: ' + parsedRegion); + } else { + loggerInfo('Region parsed from CDK endpoint: ' + parsedRegion); + } + inputCDKpipelineRegion = parsedRegion; + } } if (inputCDKpipelineName == '') { inputCDKpipelineName = inputCDKpipelineDatabaseName; @@ -576,13 +580,11 @@ async function main() { // Create Update AWS Pipeline if (createUpdatePipeline) { try { - let endpointParts = createUpdatePipelineEndpoint.split(':'); - if (endpointParts.length < 2) { - loggerError('Neptune endpoint must be in the form of host:port'); - process.exit(1); + if (!neptuneInfo) { + neptuneInfo = parseNeptuneEndpoint(createUpdatePipelineEndpoint); } - let neptuneHost = endpointParts[0]; - let neptunePort = endpointParts[1]; + let neptuneHost = neptuneInfo.host; + let neptunePort = neptuneInfo.port; await createUpdateAWSpipeline( createUpdatePipelineName, createUpdatePipelineNeptuneDatabaseName, @@ -608,14 +610,11 @@ async function main() { try { loggerInfo('Creating CDK File', {toConsole: true}); - let endpointParts = inputCDKpipelineEnpoint.split(':'); - if (endpointParts.length < 2) { - loggerError('Neptune endpoint must be in the form of host:port'); - process.exit(1); + if (!neptuneInfo) { + neptuneInfo = parseNeptuneEndpoint(inputCDKpipelineEndpoint); } - let neptuneHost = endpointParts[0]; - let neptunePort = endpointParts[1]; - + let neptuneHost = neptuneInfo.host; + let neptunePort = neptuneInfo.port; if (inputCDKpipelineFile == '') { inputCDKpipelineFile = `${outputFolderPath}/${inputCDKpipelineName}-cdk.js`; diff --git a/src/pipelineResources.js b/src/pipelineResources.js index 972be34..ebaa8df 100644 --- a/src/pipelineResources.js +++ b/src/pipelineResources.js @@ -330,19 +330,6 @@ async function createLambdaRole() { if (NEPTUNE_IAM_AUTH) { - - let action = []; - if (NEPTUNE_TYPE === NEPTUNE_DB) { - action = [ - "neptune-db:DeleteDataViaQuery", - "neptune-db:connect", - "neptune-db:ReadDataViaQuery", - "neptune-db:WriteDataViaQuery" - ]; - } else { - action = ["neptune-graph:*"] - } - // Create Neptune query policy startSpinner('Creating policy for Neptune queries', true); let policyName = NAME+"NeptuneQueryPolicy"; @@ -352,7 +339,12 @@ async function createLambdaRole() { Statement: [ { Effect: "Allow", - Action: action, + Action: [ + NEPTUNE_TYPE + ':connect', + NEPTUNE_TYPE + ':DeleteDataViaQuery', + NEPTUNE_TYPE + ':ReadDataViaQuery', + NEPTUNE_TYPE + ':WriteDataViaQuery' + ], Resource: NEPTUNE_IAM_POLICY_RESOURCE }, ], diff --git a/src/test/util.test.js b/src/test/util.test.js index 9e10f6a..304fd78 100644 --- a/src/test/util.test.js +++ b/src/test/util.test.js @@ -1,37 +1,46 @@ -import {parseNeptuneDomainFromEndpoint, parseNeptuneDomainFromHost, parseNeptuneGraphName} from '../util.js'; +import {parseNeptuneDomainFromHost, parseNeptuneEndpoint} from '../util.js'; test('parse domain from neptune cluster host', () => { - expect(parseNeptuneDomainFromHost('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com')).toBe('neptune.amazonaws.com'); + expect(parseNeptuneDomainFromHost('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com')) + .toBe('neptune.amazonaws.com'); }); test('parse domain from neptune analytics host', () => { - expect(parseNeptuneDomainFromHost('g-abcdef.us-west-2.neptune-graph.amazonaws.com')).toBe('neptune-graph.amazonaws.com'); + expect(parseNeptuneDomainFromHost('g-abcdef.us-west-2.neptune-graph.amazonaws.com')) + .toBe('neptune-graph.amazonaws.com'); }); test('parse domain from host without enough parts throws error', () => { - expect(() => parseNeptuneDomainFromHost('invalid.com')).toThrow('Cannot parse neptune host invalid.com because it has 2 part(s) delimited by . but expected at least 5'); + expect(() => parseNeptuneDomainFromHost('invalid.com')) + .toThrow('Cannot parse neptune host invalid.com because it has 2 part(s) delimited by . but expected at least 5'); }); -test('parse domain from neptune cluster endpoint', () => { - expect(parseNeptuneDomainFromEndpoint('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com:8182')).toBe('neptune.amazonaws.com'); +test('parse neptune db endpoint', () => { + let neptuneInfo = parseNeptuneEndpoint('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com:8182'); + expect(neptuneInfo).toHaveProperty('port', '8182'); + expect(neptuneInfo).toHaveProperty('host', 'db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com'); + expect(neptuneInfo).toHaveProperty('domain', 'neptune.amazonaws.com'); + expect(neptuneInfo).toHaveProperty('region', 'us-west-2'); + expect(neptuneInfo).toHaveProperty('graphName', 'db-neptune-abc-def'); + expect(neptuneInfo).toHaveProperty('neptuneType', 'neptune-db'); }); -test('parse domain from neptune analytics endpoint', () => { - expect(parseNeptuneDomainFromEndpoint('g-abcdef.us-west-2.neptune-graph.amazonaws.com:8182')).toBe('neptune-graph.amazonaws.com'); +test('parse neptune analytics endpoint', () => { + let neptuneInfo = parseNeptuneEndpoint('g-abcdef.us-east-1.neptune-graph.amazonaws.com:8183'); + expect(neptuneInfo).toHaveProperty('port', '8183'); + expect(neptuneInfo).toHaveProperty('host', 'g-abcdef.us-east-1.neptune-graph.amazonaws.com'); + expect(neptuneInfo).toHaveProperty('domain', 'neptune-graph.amazonaws.com'); + expect(neptuneInfo).toHaveProperty('region', 'us-east-1'); + expect(neptuneInfo).toHaveProperty('graphName', 'g-abcdef'); + expect(neptuneInfo).toHaveProperty('neptuneType', 'neptune-graph'); }); -test('parse domain from endpoint without enough parts throws error', () => { - expect(() => parseNeptuneDomainFromEndpoint('g-abcdef.us-west-2.neptune-graph.amazonaws.com')).toThrow('Cannot parse domain from neptune endpoint g-abcdef.us-west-2.neptune-graph.amazonaws.com because it has 1 part(s) delimited by : but expected 2'); +test('parse neptune endpoint without port throws error', () => { + expect(() => parseNeptuneEndpoint('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com')) + .toThrow('Cannot parse neptune endpoint db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com because it is not in expected format of host:port'); }); -test('parse name from neptune cluster host', () => { - expect(parseNeptuneGraphName('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com')).toBe('db-neptune-abc-def'); -}); - -test('parse name from neptune analytics host', () => { - expect(parseNeptuneGraphName('g-abcdef.us-west-2.neptune-graph.amazonaws.com')).toBe('g-abcdef'); -}); - -test('parse name from host without enough parts throws error', () => { - expect(() => parseNeptuneGraphName('invalid.com')).toThrow('Cannot parse neptune host invalid.com because it has 2 part(s) delimited by . but expected at least 5'); +test('parse neptune endpoint not enough parts in domain throws error', () => { + expect(() => parseNeptuneEndpoint('invalid.com:1234')) + .toThrow('Cannot parse neptune host invalid.com because it has 2 part(s) delimited by . but expected at least 5'); }); \ No newline at end of file diff --git a/src/util.js b/src/util.js index 600a17c..03f25a2 100644 --- a/src/util.js +++ b/src/util.js @@ -13,7 +13,8 @@ permissions and limitations under the License. const MIN_HOST_PARTS = 5; const NUM_DOMAIN_PARTS = 3; const HOST_DELIMITER = '.'; -const ENDPOINT_DELIMITER = ':'; +const NEPTUNE_GRAPH = 'neptune-graph'; +const NEPTUNE_DB = 'neptune-db'; /** * Splits a neptune host into its parts, throwing an Error if there are unexpected number of parts. @@ -29,6 +30,13 @@ function splitHost(neptuneHost) { return parts; } +function getDomainFromHostParts(hostParts) { + // last 3 parts of the host make up the domain + // ie. neptune.amazonaws.com or neptune-graph.amazonaws.com + let domainParts = hostParts.splice(hostParts.length - NUM_DOMAIN_PARTS, NUM_DOMAIN_PARTS); + return domainParts.join(HOST_DELIMITER); +} + /** * Parses the domain from the given neptune db or neptune analytics host. * @@ -38,42 +46,35 @@ function splitHost(neptuneHost) { * @param neptuneHost */ function parseNeptuneDomainFromHost(neptuneHost) { - let parts = splitHost(neptuneHost); - // last 3 parts of the host make up the domain - // ie. neptune.amazonaws.com or neptune-graph.amazonaws.com - let domainParts = parts.splice(parts.length - NUM_DOMAIN_PARTS, NUM_DOMAIN_PARTS); - return domainParts.join(HOST_DELIMITER); + return getDomainFromHostParts(splitHost(neptuneHost)); } /** - * Parses the domain from the given neptune db or neptune analytics endpoint. - * - * Example: g-abcdef.us-west-2.neptune-graph.amazonaws.com:8182 ==> neptune-graph.amazonaws.com - * Example: db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com:8182 ==> neptune.amazonaws.com + * Parses a neptune endpoint into its parts. * * @param neptuneEndpoint + * @returns {{graphName: (string), port: (string), domain: (string), neptuneType: (string), host: (string), region: (string)}} */ -function parseNeptuneDomainFromEndpoint(neptuneEndpoint) { - let parts = neptuneEndpoint.split(ENDPOINT_DELIMITER); - if (parts.length !== 2) { - throw Error('Cannot parse domain from neptune endpoint ' + neptuneEndpoint + ' because it has ' + - parts.length + ' part(s) delimited by ' + ENDPOINT_DELIMITER + ' but expected 2'); +function parseNeptuneEndpoint(neptuneEndpoint) { + let endpointParts = neptuneEndpoint.split(':'); + if (endpointParts.length !== 2) { + throw Error('Cannot parse neptune endpoint ' + neptuneEndpoint + ' because it is not in expected format of host:port'); } - return parseNeptuneDomainFromHost(parts[0]); -} -/** - * Parses the neptune graph name from the given neptune db or neptune analytics host. - * - * Example: g-abcdef.us-west-2.neptune-graph.amazonaws.com ==> g-abcdef - * Example: db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com ==> db-neptune-abc-def - * - * @param neptuneHost - */ -function parseNeptuneGraphName(neptuneHost) { - let parts = splitHost(neptuneHost); - // graph name is the first part - return parts[0]; + const host = endpointParts[0]; + const hostParts = splitHost(host); + const domain = getDomainFromHostParts(hostParts); + const neptuneType = domain.includes(NEPTUNE_GRAPH) ? NEPTUNE_GRAPH : NEPTUNE_DB; + const region = neptuneType === NEPTUNE_DB ? hostParts[2] : hostParts[1]; + + return { + port: endpointParts[1], + host: host, + domain: domain, + region: region, + graphName: hostParts[0], + neptuneType: neptuneType + }; } -export {parseNeptuneDomainFromHost, parseNeptuneDomainFromEndpoint, parseNeptuneGraphName}; \ No newline at end of file +export {parseNeptuneDomainFromHost, parseNeptuneEndpoint}; \ No newline at end of file diff --git a/templates/CDKTemplate.js b/templates/CDKTemplate.js index 218e3d5..2647274 100644 --- a/templates/CDKTemplate.js +++ b/templates/CDKTemplate.js @@ -24,6 +24,8 @@ const NEPTUNE_PORT = ''; const NEPTUNE_DB_NAME = ''; const NEPTUNE_TYPE = ''; const NEPTUNE_DBSubnetGroup = null; +const NEPTUNE_DBSubnetIds = null; +const NEPTUNE_VpcSecurityGroupId = null; const NEPTUNE_IAM_AUTH = false; const NEPTUNE_IAM_POLICY_RESOURCE = '*'; const LAMBDA_FUNCTION_NAME = ''; @@ -85,10 +87,10 @@ class AppSyncNeptuneStack extends Stack { sid: NAME + "NeptuneQueryPolicy", effect: iam.Effect.ALLOW, actions: [ - "neptune-db:connect", - "neptune-db:DeleteDataViaQuery", - "neptune-db:ReadDataViaQuery", - "neptune-db:WriteDataViaQuery" + NEPTUNE_TYPE + ':connect', + NEPTUNE_TYPE + ':DeleteDataViaQuery', + NEPTUNE_TYPE + ':ReadDataViaQuery', + NEPTUNE_TYPE + ':WriteDataViaQuery' ], resources: [NEPTUNE_IAM_POLICY_RESOURCE] })], @@ -102,20 +104,28 @@ class AppSyncNeptuneStack extends Stack { }); lambda_role.addManagedPolicy( iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole')); - + + let subnets = NEPTUNE_DBSubnetIds.split(',').map((subnetId) => ec2.Subnet.fromSubnetId(this, 'neptuneSubnet-' + subnetId, subnetId)); + echoLambda = new lambda.Function(this, LAMBDA_FUNCTION_NAME, { functionName: LAMBDA_FUNCTION_NAME, description: 'Neptune GraphQL Resolver for AppSync', - code: lambda.Code.fromAsset(LAMBDA_ZIP_FILE), + code: lambda.Code.fromAsset(LAMBDA_ZIP_FILE), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_18_X, - timeout: Duration.seconds(15), - memorySize: 128, + timeout: Duration.seconds(15), + memorySize: 128, environment: env, vpc: neptune_vpc, + vpcSubnets: { + subnets: subnets + }, + securityGroups: [ + ec2.SecurityGroup.fromSecurityGroupId(this, 'neptuneSecurityGroup', NEPTUNE_VpcSecurityGroupId) + ], allowPublicSubnet: 'true', roleArn: lambda_role.roleArn - }); + }); } echoLambda.node.addDependency(lambda_role); diff --git a/test/TestCases/Case06/Case06.02.test.js b/test/TestCases/Case06/Case06.02.test.js new file mode 100644 index 0000000..b653954 --- /dev/null +++ b/test/TestCases/Case06/Case06.02.test.js @@ -0,0 +1,15 @@ +import {checkFileContains, readJSONFile} from '../../testLib'; + +const casetest = readJSONFile('./test/TestCases/Case06/case.json'); +let neptuneType = 'neptune-db'; +if (casetest.host.includes('neptune-graph')) { + neptuneType = 'neptune-graph'; +} +checkFileContains('./test/TestCases/Case06/output/AirportCDKTestJest-cdk.js', [ + 'const NAME = \'AirportCDKTestJest\';', + 'const NEPTUNE_HOST = \'' + casetest.host + '\';', + 'const NEPTUNE_PORT = \'' + casetest.port + '\';', + 'const NEPTUNE_TYPE = \'' + neptuneType + '\';', + 'vpcSubnets', + 'securityGroups' +]); diff --git a/test/testLib.js b/test/testLib.js index 9d6407d..b4b67ed 100644 --- a/test/testLib.js +++ b/test/testLib.js @@ -34,7 +34,7 @@ function replacePlaceholderWithEnvironmentVariable(text, placeholder) { // remove angle brackets let envVarName = placeholder.replace(/[<>]/g, ''); if (process.env[envVarName]) { - return text.replace(placeholder, process.env[envVarName]); + return text.replaceAll(placeholder, process.env[envVarName]); } throw new Error('Expected environment variable ' + envVarName + ' is not set'); } @@ -85,6 +85,15 @@ function checkOutputZipLambdaUsesSdk(outputFolder, zipFile) { }); } +function checkFileContains(outputFile, expectedContent = []) { + const fileContent = fs.readFileSync(outputFile, 'utf8'); + expectedContent.forEach(expected => { + test(outputFile + ' contains text ' + expected, async () => { + expect(fileContent).toContain(expected); + }); + }) +} + async function loadResolver(file) { return await import(file); } @@ -140,4 +149,4 @@ async function testResolverQueriesResults(resolverFile, queriesReferenceFolder, } -export { readJSONFile, checkOutputFilesSize, checkOutputFilesContent, testResolverQueries, testResolverQueriesResults, checkOutputZipLambdaUsesSdk }; +export { readJSONFile, checkFileContains, checkOutputFilesSize, checkOutputFilesContent, testResolverQueries, testResolverQueriesResults, checkOutputZipLambdaUsesSdk };