Skip to content

Commit 086091e

Browse files
authored
Added prefix option for custom query and mutation names (aws#120)
Added an option to add a custom prefix to generated query and mutation names -added new --query-prefix option -value is added to the beginning of generated query names ex) --query-prefix airport_ will generate airport_getContinent -added new --query-prefix option to list outputted when user enters -h -added new --mutation-prefix option -value is added to the beginning of generated mutation names ex) --mutation-prefix set to test_ will generate test_createNodeAirport -added new --mutation-prefix option to list outputted when user enters -h -Added unit tests for changed files to check that the generated schema outputs the correct prefix on query and mutation names.
1 parent 977dbf0 commit 086091e

27 files changed

+2418
-52
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ permissions and limitations under the License.
2828
`limit` ([#102](https://github.com/aws/amazon-neptune-for-graphql/pull/102))
2929
* Added support for queries with
3030
sorting ([#105](https://github.com/aws/amazon-neptune-for-graphql/pull/105))
31+
* Added options to prefix query and mutation names ([#120](https://github.com/aws/amazon-neptune-for-graphql/pull/120))
3132

3233
### Improvements
3334

doc/cliReference.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ database you can edit a graphdb schema as use it as input.
9494
The Neptune database endpoint from which the utility extract the graphdb schema.
9595
Format: `host:port`
9696

97+
`--query-prefix`
98+
<br>
99+
Optional prefix to add to generated query names in the GraphQL schema.
100+
101+
`--mutation-prefix`
102+
<br>
103+
Optional prefix to add to generated mutation names in the GraphQL schema.
104+
97105
## Output options
98106

99107
`--output-folder-path <value>, -o <value>`

src/graphdb.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,21 @@ function formatProperties(properties = [], typeOverrides = new Map()) {
125125
}).join('');
126126
}
127127

128-
function graphDBInferenceSchema (graphdbSchema, addMutations) {
128+
/**
129+
* Generates a GraphQL schema for a graph database based on provided node and edge structures.
130+
*
131+
* This function takes a graph database schema definition and transforms it into a complete GraphQL schema
132+
* with types, inputs, queries, and optionally mutations. It handles node and edge relationships,
133+
* property definitions, filtering, sorting, and pagination.
134+
*
135+
* @param {string} graphdbSchema - A JSON string representing the graph database schema with nodeStructures and edgeStructures
136+
* @param {boolean} addMutations - Whether to include mutation operations in the generated schema
137+
* @param {string} queryPrefix='' - Prefix to add to all query operation names
138+
* @param {string} mutationPrefix='' - Prefix to add to all mutation operation names
139+
*
140+
* @returns {string} A complete GraphQL schema definition as a string
141+
*/
142+
function graphDBInferenceSchema (graphdbSchema, { addMutations = true, queryPrefix = '', mutationPrefix = ''} = {}) {
129143
let r = '';
130144
const gdbs = JSON.parse(graphdbSchema);
131145

@@ -319,8 +333,8 @@ function graphDBInferenceSchema (graphdbSchema, addMutations) {
319333
r += `type Query {\n`;
320334
gdbs.nodeStructures.forEach(node => {
321335
let nodeCase = toPascalCase(cleanseLabel(node.label));
322-
r += `\tgetNode${nodeCase}(filter: ${nodeCase}Input): ${nodeCase}\n`;
323-
r += `\tgetNode${nodeCase}s(filter: ${nodeCase}Input, options: Options, sort: [${nodeCase}Sort!]): [${nodeCase}]\n`;
336+
r += `\t${queryPrefix}getNode${nodeCase}(filter: ${nodeCase}Input): ${nodeCase}\n`;
337+
r += `\t${queryPrefix}getNode${nodeCase}s(filter: ${nodeCase}Input, options: Options, sort: [${nodeCase}Sort!]): [${nodeCase}]\n`;
324338
});
325339
r += '}\n\n';
326340

@@ -329,9 +343,9 @@ function graphDBInferenceSchema (graphdbSchema, addMutations) {
329343
r += `type Mutation {\n`;
330344
gdbs.nodeStructures.forEach(node => {
331345
let nodeCase = toPascalCase(cleanseLabel(node.label));
332-
r += `\tcreateNode${nodeCase}(input: ${nodeCase}CreateInput!): ${nodeCase}\n`;
333-
r += `\tupdateNode${nodeCase}(input: ${nodeCase}UpdateInput!): ${nodeCase}\n`;
334-
r += `\tdeleteNode${nodeCase}(_id: ID!): Boolean\n`;
346+
r += `\t${mutationPrefix}createNode${nodeCase}(input: ${nodeCase}CreateInput!): ${nodeCase}\n`;
347+
r += `\t${mutationPrefix}updateNode${nodeCase}(input: ${nodeCase}UpdateInput!): ${nodeCase}\n`;
348+
r += `\t${mutationPrefix}deleteNode${nodeCase}(_id: ID!): Boolean\n`;
335349
});
336350

337351
gdbs.edgeStructures.forEach(edge => {
@@ -341,12 +355,12 @@ function graphDBInferenceSchema (graphdbSchema, addMutations) {
341355
let edgeCase = toPascalCase(cleanseLabel(edge.label));
342356

343357
if (edge.properties.length > 0) {
344-
r += `\tconnectNode${fromCase}ToNode${toCase}Edge${edgeCase}(from_id: ID!, to_id: ID!, edge: ${edgeCase}Input!): ${edgeCase}\n`;
345-
r += `\tupdateEdge${edgeCase}From${fromCase}To${toCase}(from_id: ID!, to_id: ID!, edge: ${edgeCase}Input!): ${edgeCase}\n`;
358+
r += `\t${mutationPrefix}connectNode${fromCase}ToNode${toCase}Edge${edgeCase}(from_id: ID!, to_id: ID!, edge: ${edgeCase}Input!): ${edgeCase}\n`;
359+
r += `\t${mutationPrefix}updateEdge${edgeCase}From${fromCase}To${toCase}(from_id: ID!, to_id: ID!, edge: ${edgeCase}Input!): ${edgeCase}\n`;
346360
} else {
347-
r += `\tconnectNode${fromCase}ToNode${toCase}Edge${edgeCase}(from_id: ID!, to_id: ID!): ${edgeCase}\n`;
361+
r += `\t${mutationPrefix}connectNode${fromCase}ToNode${toCase}Edge${edgeCase}(from_id: ID!, to_id: ID!): ${edgeCase}\n`;
348362
}
349-
r += `\tdeleteEdge${edgeCase}From${fromCase}To${toCase}(from_id: ID!, to_id: ID!): Boolean\n`;
363+
r += `\t${mutationPrefix}deleteEdge${edgeCase}From${fromCase}To${toCase}(from_id: ID!, to_id: ID!): Boolean\n`;
350364
});
351365
});
352366
r += '}\n\n';

src/help.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ Parameters
124124
[--input-graphdb-schema <value> -ig ]
125125
[--input-graphdb-schema-file <value> -igf ]
126126
[--input-graphdb-schema-neptune-endpoint <value> -ie ]
127+
[--query-prefix <value> -qp ]
128+
[--mutation-prefix <value> -mp ]
127129
128130
[--output-folder-path <value> -o ] default: ./output
129131
[--output-schema-file <value> -os ] default: output.schema.graphql

src/main.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ let inputGraphQLSchemaChangesFile = '';
5454
let inputGraphDBSchema = '';
5555
let inputGraphDBSchemaFile = '';
5656
let inputGraphDBSchemaNeptuneEndpoint = '';
57+
let inputQueryPrefix = '';
58+
let inputMutationPrefix = '';
5759
let queryLanguage = 'opencypher'; // or TODO 'gremlin' or 'sparql'
5860
let queryClient = 'sdk'; // or 'http'
5961
let isNeptuneIAMAuth = false;
@@ -168,6 +170,14 @@ function processArgs() {
168170
case '--output-neptune-schema-file':
169171
outputNeptuneSchemaFile = array[index + 1];
170172
break;
173+
case '-qp':
174+
case '--query-prefix':
175+
inputQueryPrefix = array[index + 1];
176+
break;
177+
case '-mp':
178+
case '--mutation-prefix':
179+
inputMutationPrefix = array[index + 1];
180+
break;
171181
case '-org':
172182
case '--output-resolver-query-gremlin':
173183
queryLanguage = 'gremlin';
@@ -409,7 +419,11 @@ async function main() {
409419
// Option 2: inference GraphQL schema from graphDB schema
410420
if (inputGraphDBSchema != '' && inputGraphQLSchema == '' && inputGraphQLSchemaFile == '') {
411421
loggerInfo('Inferencing GraphQL schema from graphDB schema', {toConsole: true});
412-
inputGraphQLSchema = graphDBInferenceSchema(inputGraphDBSchema, outputSchemaMutations);
422+
inputGraphQLSchema = graphDBInferenceSchema(inputGraphDBSchema, {
423+
addMutations: outputSchemaMutations,
424+
queryPrefix: inputQueryPrefix,
425+
mutationPrefix: inputMutationPrefix
426+
});
413427
}
414428

415429
// Option 1: load
@@ -526,7 +540,10 @@ async function main() {
526540
schemaModel = schemaParser(inputGraphQLSchema);
527541

528542
// Validate schema
529-
schemaModel = validatedSchemaModel(schemaModel, quiet);
543+
schemaModel = validatedSchemaModel(schemaModel, {
544+
queryPrefix: inputQueryPrefix,
545+
mutationPrefix: inputMutationPrefix
546+
});
530547

531548
// Generate schema for resolver
532549
const queryDataModelJSON = JSON.stringify(schemaModel, null, 2);

src/schemaModelValidator.js

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { GraphQLID, print } from 'graphql';
1515
import {gql} from 'graphql-tag'
1616
import { loggerInfo, yellow } from "./logger.js";
1717

18-
let quiet = false;
1918
// TODO change variables to local scope instead of global so this module can be used against multiple schemas
2019
const typesToAdd = [];
2120
const queriesToAdd = [];
@@ -137,7 +136,7 @@ function injectChanges(schemaModel) {
137136
}
138137

139138

140-
function addNode(def) {
139+
function addNode(def, { queryPrefix = '', mutationPrefix = ''} = {}) {
141140
let name = def.name.value;
142141
const idField = getIdFieldWithDirective(def);
143142

@@ -191,34 +190,39 @@ function addNode(def) {
191190
typesToAdd.push(`input ${name}Sort {\n${print(sortFields)}\n}`);
192191

193192
// Create query
194-
queriesToAdd.push(`getNode${name}(filter: ${name}Input): ${name}\n`);
195-
queriesToAdd.push(`getNode${name}s(filter: ${name}Input, options: Options, sort: [${name}Sort!]): [${name}]\n`);
193+
const getSingleQueryName = `${queryPrefix}getNode${name}`;
194+
queriesToAdd.push(`${getSingleQueryName}(filter: ${name}Input): ${name}\n`);
195+
const getMultipleQueryName = `${queryPrefix}getNode${name}s`;
196+
queriesToAdd.push(`${getMultipleQueryName}(filter: ${name}Input, options: Options, sort: [${name}Sort!]): [${name}]\n`);
196197

197198
// Create mutation
198-
mutationsToAdd.push(`createNode${name}(input: ${name}CreateInput!): ${name}\n`);
199-
mutationsToAdd.push(`updateNode${name}(input: ${name}UpdateInput!): ${name}\n`);
200-
mutationsToAdd.push(`deleteNode${name}(${print(idFieldToInputValue(idField))}): Boolean\n`);
199+
const createMutationName = `${mutationPrefix}createNode${name}`;
200+
mutationsToAdd.push(`${createMutationName}(input: ${name}CreateInput!): ${name}\n`);
201+
const updateMutationName = `${mutationPrefix}updateNode${name}`;
202+
mutationsToAdd.push(`${updateMutationName}(input: ${name}UpdateInput!): ${name}\n`);
203+
const deleteMutationName = `${mutationPrefix}deleteNode${name}`;
204+
mutationsToAdd.push(`${deleteMutationName}(${print(idFieldToInputValue(idField))}): Boolean\n`);
201205

202206
loggerInfo(`Added input type: ${yellow(name+'Input')}`);
203207
loggerInfo(`Added input type: ${yellow(name+'CreateInput')}`);
204208
loggerInfo(`Added input type: ${yellow(name+'UpdateInput')}`);
205-
loggerInfo(`Added query: ${yellow('getNode' + name)}`);
206-
loggerInfo(`Added query: ${yellow('getNode' + name + 's')}`);
207-
loggerInfo(`Added mutation: ${yellow('createNode' + name)}`);
208-
loggerInfo(`Added mutation: ${yellow('updateNode' + name)}`);
209-
loggerInfo(`Added mutation: ${yellow('deleteNode' + name)}`);
209+
loggerInfo(`Added query: ${yellow(getSingleQueryName)}`);
210+
loggerInfo(`Added query: ${yellow(getMultipleQueryName)}`);
211+
loggerInfo(`Added mutation: ${yellow(createMutationName)}`);
212+
loggerInfo(`Added mutation: ${yellow(updateMutationName)}`);
213+
loggerInfo(`Added mutation: ${yellow(deleteMutationName)}`);
210214
}
211215

212216

213-
function addEdge(from, to, edgeName) {
217+
function addEdge(from, to, edgeName, { mutationPrefix = ''} = {}) {
214218
if (!typesToAdd.some((str) => str.startsWith(`type ${edgeName}`))) {
215219

216220
// Create type
217221
typesToAdd.push(`type ${edgeName} {\n _id: ID! @id\n}`);
218222

219223
// Create mutation
220-
mutationsToAdd.push(`connectNode${from}ToNode${to}Edge${edgeName}(from_id: ID!, to_id: ID!): ${edgeName}\n`);
221-
mutationsToAdd.push(`deleteEdge${edgeName}From${from}To${to}(from_id: ID!, to_id: ID!): Boolean\n`);
224+
mutationsToAdd.push(`${mutationPrefix}connectNode${from}ToNode${to}Edge${edgeName}(from_id: ID!, to_id: ID!): ${edgeName}\n`);
225+
mutationsToAdd.push(`${mutationPrefix}deleteEdge${edgeName}From${from}To${to}(from_id: ID!, to_id: ID!): Boolean\n`);
222226

223227
loggerInfo(`Added type for edge: ${yellow(edgeName)}`);
224228
loggerInfo(`Added mutation: ${yellow(`connectNode${from}ToNode${to}Edge${edgeName}`)}`);
@@ -325,7 +329,7 @@ function isScalarOrEnum(type) {
325329
}
326330

327331

328-
function inferGraphDatabaseDirectives(schemaModel) {
332+
function inferGraphDatabaseDirectives(schemaModel, { queryPrefix = '', mutationPrefix = ''} = {}) {
329333

330334
var currentType = '';
331335
let referencedType = '';
@@ -358,7 +362,7 @@ function inferGraphDatabaseDirectives(schemaModel) {
358362
}
359363
}
360364

361-
addNode(def);
365+
addNode(def,{ queryPrefix, mutationPrefix });
362366
const edgesTypeToAdd = [];
363367

364368
// add relationships
@@ -378,7 +382,7 @@ function inferGraphDatabaseDirectives(schemaModel) {
378382
edgeName = referencedType + 'Edge';
379383
loggerInfo("Infer graph database directive in type: " + yellow(currentType) + " field: " + yellow(field.name.value) + " referenced type: " + yellow(referencedType) + " graph relationship: " + yellow(edgeName));
380384
addRelationshipDirective(field, edgeName, 'OUT');
381-
addEdge(currentType, referencedType, edgeName);
385+
addEdge(currentType, referencedType, edgeName, { mutationPrefix });
382386
if (!edgesTypeToAdd.includes(edgeName)) edgesTypeToAdd.push(edgeName);
383387
}
384388
catch {}
@@ -394,7 +398,7 @@ function inferGraphDatabaseDirectives(schemaModel) {
394398
edgeName = referencedType + 'Edge';
395399
loggerInfo("Infer graph database directive in type: " + yellow(currentType) + " field: " + yellow(field.name.value) + " referenced type: " + yellow(referencedType) + " graph relationship: " + yellow(edgeName));
396400
addRelationshipDirective(field, edgeName, 'OUT');
397-
addEdge(currentType, referencedType, edgeName);
401+
addEdge(currentType, referencedType, edgeName, { mutationPrefix });
398402
if (!edgesTypeToAdd.includes(edgeName)) edgesTypeToAdd.push(edgeName);
399403
}
400404
});
@@ -429,12 +433,10 @@ function inferGraphDatabaseDirectives(schemaModel) {
429433
}
430434

431435

432-
function validatedSchemaModel (schemaModel, quietInput) {
433-
quiet = quietInput;
434-
436+
function validatedSchemaModel (schemaModel, { queryPrefix = '', mutationPrefix = ''} = {}) {
435437
if (!isGraphDBDirectives(schemaModel)) {
436438
loggerInfo("The schema model does not contain any graph database directives.");
437-
schemaModel = inferGraphDatabaseDirectives(schemaModel);
439+
schemaModel = inferGraphDatabaseDirectives(schemaModel, { queryPrefix, mutationPrefix });
438440
}
439441

440442
return schemaModel;

src/schemaParser.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,11 @@ express or implied. See the License for the specific language governing
1010
permissions and limitations under the License.
1111
*/
1212

13-
import gql from 'graphql-tag';
14-
import { print, visit } from 'graphql';
13+
import { print, visit, parse } from 'graphql';
1514

1615

1716
function schemaParser (schema) {
18-
const typeDefs = gql`
19-
${schema}
20-
`;
21-
return typeDefs;
17+
return parse(schema);
2218
}
2319

2420
function schemaStringify (schemaModel, directives = true) {

0 commit comments

Comments
 (0)