Skip to content

Commit b4ac4ed

Browse files
authored
Merge pull request #76 from Bit-Quill/apolloStartFromSchema
Apollo zip from input schema file
2 parents c9d6a39 + f4c1163 commit b4ac4ed

32 files changed

+466
-179
lines changed

README.md

+11-2
Original file line numberDiff line numberDiff line change
@@ -275,15 +275,24 @@ See CDK end to end example [here](https://github.com/aws/amazon-neptune-for-grap
275275
# Generate Apollo Server Artifacts
276276
If you prefer to use Apollo Server instead of AWS App Sync, the utility can generate a ZIP file of Apollo Server artifacts with the CLI option `--create-update-apollo-server` for a standalone server or `--create-update-apollo-server-subgraph` for federated systems.
277277

278-
For example:
278+
Example of starting with a neptune database with data from which to determine the graphQL schema:
279279
```
280280
neptune-for-graphql \
281281
--input-graphdb-schema-neptune-endpoint abc.us-west-2.neptune.amazonaws.com:8182 \
282282
--create-update-apollo-server \
283283
--output-resolver-query-https
284284
```
285285

286-
The command above will generate an `apollo-server-<identifier>-<timestamp>.zip` file which can then be deployed locally by following these steps:
286+
Example of starting with a graphQL schema file:
287+
```
288+
neptune-for-graphql \
289+
--input-schema-file airports.source.schema.graphql \
290+
--create-update-apollo-server-neptune-endpoint abc.us-west-2.neptune.amazonaws.com:8182 \
291+
--create-update-apollo-server \
292+
--output-resolver-query-https
293+
```
294+
295+
The example commands above will generate the file `apollo-server-<identifier>-<timestamp>.zip`, which can then be deployed locally by following these steps:
287296
1. unzip `apollo-server-<identifier>-<timestamp>.zip`
288297
2. change directory into the unzipped folder
289298
3. execute `npm install` to install required dependencies

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@aws/neptune-for-graphql",
3-
"version": "1.1.1",
3+
"version": "1.2.0-beta.1",
44
"description": "CLI utility to create and maintain a GraphQL API for Amazon Neptune",
55
"keywords": [
66
"Amazon Neptune",

src/help.js

+2
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ Parameters
146146
147147
[--create-update-apollo-server -asvr]
148148
[--create-update-apollo-server-subgraph -asub]
149+
[--create-update-apollo-server-neptune-endpoint -ase ] default: --input-graphdb-schema-neptune-endpoint if exists
150+
[ ... more apollo options to come in future roadmap ]
149151
150152
[--output-aws-pipeline-cdk -c ]
151153
[--output-aws-pipeline-cdk-name <value> -cn ] default: Neptune DB name from --input-graphdb-schema-neptune-endpoint if exists

src/main.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ let queryClient = 'sdk'; // or 'http'
5555
let isNeptuneIAMAuth = false;
5656
let createUpdateApolloServer = false;
5757
let createUpdateApolloServerSubgraph = false;
58+
let createUpdateApolloServerEndpoint = '';
5859
let createUpdatePipeline = false;
5960
let createUpdatePipelineName = '';
6061
let createUpdatePipelineEndpoint = '';
@@ -197,6 +198,10 @@ function processArgs() {
197198
createUpdatePipeline = false;
198199
inputCDKpipeline = false;
199200
break;
201+
case '-ase':
202+
case '--create-update-apollo-server-neptune-endpoint':
203+
createUpdateApolloServerEndpoint = array[index + 1];
204+
break;
200205
case '-p':
201206
case '--create-update-aws-pipeline':
202207
createUpdatePipeline = true;
@@ -314,10 +319,15 @@ function createOutputFolder() {
314319

315320
function validateArgs() {
316321
// TODO more argument validation
317-
if (queryClient !== 'http' && (createUpdateApolloServerSubgraph || createUpdateApolloServer)) {
322+
const createApollo = createUpdateApolloServerSubgraph || createUpdateApolloServer;
323+
if (queryClient !== 'http' && createApollo) {
318324
console.error(`Neptune querying using ${queryClient} is not yet supported for Apollo Server. Please use option --output-resolver-query-https.`);
319325
process.exit(1);
320326
}
327+
if (createApollo && !inputGraphDBSchemaNeptuneEndpoint && !createUpdateApolloServerEndpoint) {
328+
console.error(`Apollo artifact creation requires a neptune endpoint. Please specify option --input-graphdb-schema-neptune-endpoint or --create-update-apollo-server-neptune-endpoint.`);
329+
process.exit(1);
330+
}
321331
}
322332

323333
async function main() {
@@ -351,7 +361,12 @@ async function main() {
351361
let neptuneInfo;
352362
// Check if any of the Neptune endpoints are a neptune analytic endpoint and if so, set the neptuneType and IAM to required
353363
// only one of these endpoints are expected to be non-empty at the same time
354-
const nonEmptyEndpoints = [inputGraphDBSchemaNeptuneEndpoint, createUpdatePipelineEndpoint, inputCDKpipelineEndpoint].filter(endpoint => endpoint !== '');
364+
const nonEmptyEndpoints = [
365+
inputGraphDBSchemaNeptuneEndpoint,
366+
createUpdatePipelineEndpoint,
367+
inputCDKpipelineEndpoint,
368+
createUpdateApolloServerEndpoint
369+
].filter(endpoint => endpoint !== '');
355370
if (nonEmptyEndpoints.length > 0) {
356371
neptuneInfo = parseNeptuneEndpoint(nonEmptyEndpoints[0]);
357372
neptuneType = neptuneInfo.neptuneType;

src/zipPackage.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export async function createApolloDeploymentPackage({zipFilePath, resolverFilePa
8181
includePaths: [
8282
{source: path.join(modulePath, '/../templates/ApolloServer')},
8383
{source: resolverFilePath, target: 'output.resolver.graphql.js'},
84-
{source: schemaFilePath, target: 'schema.graphql'},
84+
{source: schemaFilePath, target: 'output.schema.graphql'},
8585
// querying neptune using SDK not yet supported
8686
{source: path.join(modulePath, '/../templates/queryHttpNeptune.mjs')}
8787
],

templates/ApolloServer/index.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import dotenv from 'dotenv';
88

99
dotenv.config();
1010

11-
const typeDefs = gql(readFileSync('./schema.graphql', 'utf-8'));
11+
const typeDefs = gql(readFileSync('./output.schema.graphql', 'utf-8'));
1212
const queryDefinition = typeDefs.definitions.find(
1313
definition => definition.kind === 'ObjectTypeDefinition' && definition.name.value === 'Query'
1414
);

templates/ApolloServer/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "neptune-apollo-server",
3-
"version": "1.0.0",
3+
"version": "1.2.0-beta.1",
44
"description": "",
55
"license": "Apache-2.0",
66
"author": "",

templates/JSResolverOCHTTPSTemplate.js

+13-11
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ express or implied. See the License for the specific language governing
1010
permissions and limitations under the License.
1111
*/
1212

13-
import { astFromValue, buildASTSchema, typeFromAST, GraphQLID } from 'graphql';
13+
import { astFromValue, buildASTSchema, typeFromAST, GraphQLID, GraphQLInputObjectType } from 'graphql';
1414
import { gql } from 'graphql-tag'; // GraphQL library to parse the GraphQL query
1515

1616
const useCallSubquery = false;
@@ -36,7 +36,7 @@ export function resolveGraphDBQueryFromAppSyncEvent(event) {
3636
return resolveGraphDBQueryFromEvent({
3737
field: event.field,
3838
arguments: event.arguments,
39-
selectionSet: gql`${event.selectionSetGraphQL}`.definitions[0].selectionSet
39+
selectionSet: event.selectionSetGraphQL ? gql`${event.selectionSetGraphQL}`.definitions[0].selectionSet : {}
4040
});
4141
}
4242

@@ -58,15 +58,17 @@ export function resolveGraphDBQueryFromEvent(event) {
5858
if (value) {
5959
const inputType = typeFromAST(schema, inputDef.type);
6060
const astValue = astFromValue(value, inputType);
61-
// retrieve an ID field which may not necessarily be named 'id'
62-
const idField = Object.values(inputType.getFields()).find(field => field.type.name === GraphQLID.name);
63-
if (idField) {
64-
// check if id was an input arg
65-
const idValue = astValue.fields.find(f => f.name.value === idField.name);
66-
if (idValue?.value?.kind === 'IntValue') {
67-
// graphql astFromValue function can convert ID integer strings into integer type
68-
// if input args contain an id and the graphql library has interpreted the value as an int, change it to treat the value as a string
69-
idValue.value.kind = 'StringValue';
61+
if (inputType instanceof GraphQLInputObjectType) {
62+
// retrieve an ID field which may not necessarily be named 'id'
63+
const idField = Object.values(inputType.getFields()).find(field => field.type.name === GraphQLID.name);
64+
if (idField) {
65+
// check if id was an input arg
66+
const idValue = astValue.fields.find(f => f.name.value === idField.name);
67+
if (idValue?.value?.kind === 'IntValue') {
68+
// graphql astFromValue function can convert ID integer strings into integer type
69+
// if input args contain an id and the graphql library has interpreted the value as an int, change it to treat the value as a string
70+
idValue.value.kind = 'StringValue';
71+
}
7072
}
7173
}
7274
args.push({

templates/Lambda4AppSyncGraphSDK/package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

templates/Lambda4AppSyncGraphSDK/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "lambda4appsyncgraphsdk",
3-
"version": "1.1.1",
3+
"version": "1.2.0-beta.1",
44
"description": "AWS Lambda function to bridge AppSync to Neptune Analytics",
55
"main": "index.mjs",
66
"type": "module",

templates/Lambda4AppSyncHTTP/package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

templates/Lambda4AppSyncHTTP/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "lambda4appsynchttp",
3-
"version": "1.1.1",
3+
"version": "1.2.0-beta.1",
44
"description": "AWS Lambda function to bridge AppSync to Neptune or Neptune Analytics",
55
"main": "index.mjs",
66
"type": "module",

templates/Lambda4AppSyncSDK/package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

templates/Lambda4AppSyncSDK/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "lambda4appsyncsdk",
3-
"version": "1.1.1",
3+
"version": "1.2.0-beta.1",
44
"description": "AWS Lambda function to bridge AppSync to Neptune",
55
"main": "index.mjs",
66
"type": "module",

test/TestCases/Case01/Case01.05.test.js

+30
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,36 @@ describe('AppSync resolver', () => {
105105
});
106106
});
107107

108+
test('should resolve gremlin query with argument', () => {
109+
const result = resolve({
110+
field: 'getAirportWithGremlin',
111+
arguments: { code: 'YVR' },
112+
selectionSetGraphQL: '{ city }'
113+
});
114+
115+
expect(result).toMatchObject({
116+
query: "g.V().has('airport', 'code', 'YVR').elementMap()",
117+
parameters: {},
118+
language: 'gremlin',
119+
refactorOutput: null
120+
});
121+
});
122+
123+
test('should resolve gremlin query without arguments or selection set', () => {
124+
const result = resolve({
125+
field: 'getCountriesCount',
126+
arguments: { },
127+
selectionSetGraphQL: ''
128+
});
129+
130+
expect(result).toMatchObject({
131+
query: "g.V().hasLabel('country').count()",
132+
parameters: {},
133+
language: 'gremlin',
134+
refactorOutput: null
135+
});
136+
});
137+
108138
function resolve(event) {
109139
return resolverModule.resolveGraphDBQueryFromAppSyncEvent(event);
110140
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
[
22
{ "type": "Airport", "field": "outboundRoutesCountAdd", "action": "add", "value":"outboundRoutesCountAdd: Int @graphQuery(statement: \"MATCH (this)-[r:route]->(a) RETURN count(r)\")"},
33
{ "type": "Mutation", "field": "deleteNodeVersion", "action": "remove", "value": "" },
4-
{ "type": "Mutation", "field": "createNodeVersion", "action": "remove", "value": "" }
4+
{ "type": "Mutation", "field": "createNodeVersion", "action": "remove", "value": "" },
5+
{ "type": "Query", "field": "getAirportWithGremlin", "action": "add", "value":"getAirportWithGremlin(code:String): Airport @graphQuery(statement: \"g.V().has('airport', 'code', '$code').elementMap()\")"},
6+
{ "type": "Query", "field": "getCountriesCount", "action": "add", "value":"getCountriesCount: Int @graphQuery(statement: \"g.V().hasLabel('country').count()\")"}
57
]

0 commit comments

Comments
 (0)