@neo4j/graphql-ogm
Version:
GraphQL powered OGM for Neo4j and Javascript applications
223 lines (222 loc) • 9.21 kB
JavaScript
;
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@graphql-codegen/core");
const typescriptPlugin = __importStar(require("@graphql-codegen/typescript"));
const fs = __importStar(require("fs"));
const graphql = __importStar(require("graphql"));
const prettier_1 = __importDefault(require("prettier"));
const upper_first_1 = require("./utils/upper-first");
/* This function will generate TypeScript aggregate input types
/ Because aggregating is all selectionSet based...
/ We make some typescript types, where the corresponding object that will be reflected into a selectionSet
/ ---Before---
ogm.Model.aggregate({
selectionSet: `
title {
min
max
}
imdbRating {
avg
}
`
})
---After---
ogm.Model.aggregate({
aggregate: {
title: {
min: true
max: true
},
imdbRating: {
avg: true
}
}
})
*/
function createAggregationInput({ basedOnSearch, typeName, aggregateSelections = {}, input, }) {
const interfaceStrs = [`export interface ${typeName} {`];
const [, start] = input.split(basedOnSearch);
const [body] = start.split(`}`);
const lines = body.split("\n").filter(Boolean);
lines.forEach((line) => {
const [fieldName, type] = line.split(": ").map((x) => x.trim().replace(";", ""));
if (fieldName === "__typename?") {
return;
}
if (type.endsWith(`AggregateSelectionNonNullable`) || type.endsWith(`AggregateSelectionNullable`)) {
const newTypeName = `${type.replace(`Selection`, "Input")}`;
if (!aggregateSelections[type]) {
const createdInput = createAggregationInput({
basedOnSearch: `export type ${type} = {`,
typeName: newTypeName,
aggregateSelections,
input,
});
aggregateSelections[type] = createdInput[0];
}
interfaceStrs.push(`${removeOptional(fieldName)}?: ${newTypeName};`);
return;
}
interfaceStrs.push(`${removeOptional(fieldName)}?: boolean;`);
});
interfaceStrs.push("}");
return [interfaceStrs.join("\n"), aggregateSelections];
}
function hasConnectOrCreate(node, ogm) {
for (const relation of node.relationFields) {
const refNode = ogm["nodes"].find((x) => x.name === relation.typeMeta.name);
if (refNode && refNode.uniqueFields.length > 0) {
return true;
}
}
return false;
}
async function generate(options) {
await options.ogm.init();
const config = {
config: {},
plugins: [
{
typescript: {},
},
],
filename: options.outFile || "some-random-file-name-thats-not-used",
documents: [],
schemaAst: options.ogm.schema,
schema: graphql.parse(graphql.printSchema(options.ogm.schema)),
pluginMap: {
typescript: typescriptPlugin,
},
};
const output = await (0, core_1.codegen)(config);
const content = [`import type { SelectionSetNode, DocumentNode } from "graphql";`, output];
const aggregateSelections = {};
const modeMap = {};
options.ogm["nodes"].forEach((node) => {
const modelName = `${node.name}Model`;
const hasFulltextArg = Boolean(node.fulltextDirective);
modeMap[node.name] = modelName;
const aggregationInput = createAggregationInput({
basedOnSearch: `__typename?: '${node.aggregateTypeNames.selection}';`,
typeName: node.aggregateTypeNames.input,
aggregateSelections,
input: output,
});
const nodeHasConnectOrCreate = hasConnectOrCreate(node, options.ogm);
const normalizedNodeName = (0, upper_first_1.upperFirst)(node.singular);
const model = `
${Object.values(aggregationInput[1]).join("\n")}
${aggregationInput[0]}
export declare class ${modelName} {
public find(args?: {
where?: ${normalizedNodeName}Where;
${hasFulltextArg ? `fulltext?: ${normalizedNodeName}Fulltext;` : ""}
options?: ${normalizedNodeName}Options;
selectionSet?: string | DocumentNode | SelectionSetNode;
args?: any;
context?: any;
rootValue?: any;
}): Promise<${normalizedNodeName}[]>
public create(args: {
input: ${normalizedNodeName}CreateInput[];
selectionSet?: string | DocumentNode | SelectionSetNode;
args?: any;
context?: any;
rootValue?: any;
}): Promise<Create${(0, upper_first_1.upperFirst)(node.plural)}MutationResponse>
public update(args: {
where?: ${normalizedNodeName}Where;
update?: ${normalizedNodeName}UpdateInput;
${node.relationFields.length ? `connect?: ${normalizedNodeName}ConnectInput` : ""}
${node.relationFields.length ? `disconnect?: ${normalizedNodeName}DisconnectInput` : ""}
${node.relationFields.length ? `create?: ${normalizedNodeName}CreateInput` : ""}
${nodeHasConnectOrCreate ? `connectOrCreate?: ${normalizedNodeName}ConnectOrCreateInput` : ""}
selectionSet?: string | DocumentNode | SelectionSetNode;
args?: any;
context?: any;
rootValue?: any;
}): Promise<Update${(0, upper_first_1.upperFirst)(node.plural)}MutationResponse>
public delete(args: {
where?: ${normalizedNodeName}Where;
${node.relationFields.length ? `delete?: ${normalizedNodeName}DeleteInput` : ""}
context?: any;
rootValue?: any;
}): Promise<{ nodesDeleted: number; relationshipsDeleted: number; }>
public aggregate(args: {
where?: ${normalizedNodeName}Where;
${hasFulltextArg ? `fulltext?: ${normalizedNodeName}Fulltext;` : ""}
aggregate: ${node.name}AggregateSelectionInput;
context?: any;
rootValue?: any;
}): Promise<${normalizedNodeName}AggregateSelection>
}
`;
content.push(model);
});
content.push(`
export interface ModelMap {
${Object.entries(modeMap)
.map(([k, v]) => `${k}: ${v}`)
.join(";\n")}
}
`);
const formattedContent = prettier_1.default.format(content.join("\n"), { parser: "typescript" });
if (options.noWrite) {
return formattedContent;
}
if (!options.outFile) {
throw new Error("outFile or noWrite required");
}
await fs.promises.writeFile(options.outFile, formattedContent);
return undefined;
}
exports.default = generate;
function removeOptional(type) {
return type.replace(/\?$/, "");
}
//# sourceMappingURL=generate.js.map