From 5174085bd0bd3063eda068a9d626895518501994 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 15:12:50 +0200 Subject: [PATCH 01/39] Move compile to gql/graphql to module from @sap/cds --- cds-plugin.js | 4 ++++ lib/compile.js | 10 ++++++++++ 2 files changed, 14 insertions(+) create mode 100644 lib/compile.js diff --git a/cds-plugin.js b/cds-plugin.js index 17ab8fdc..6a1ef8a7 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -3,3 +3,7 @@ const protocols = cds.env.protocols ??= {} if (!protocols.graphql) protocols.graphql = { path: "/graphql", impl: "@cap-js/graphql" } +Object.assign(cds.compile.to, { + get gql() { return super.gql = require('./lib/compile') }, + get graphql() { return super.graphql = require('./lib/compile') } +}) \ No newline at end of file diff --git a/lib/compile.js b/lib/compile.js new file mode 100644 index 00000000..ff816f36 --- /dev/null +++ b/lib/compile.js @@ -0,0 +1,10 @@ +const cds = require('@sap/cds') +const { SchemaGenerator } = require('./schema') + +function cds_compile_to_gql (csn) { + const m = cds.linked(csn) + const services = Object.fromEntries(m.services.map(s => [s.name, new cds.ApplicationService(s.name, m)])) + return new SchemaGenerator().generate(services).printSchema() +} + +module.exports = cds_compile_to_gql From 6fb45452c104e5e1239f5658d7ffa3826ffa9439 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 15:17:01 +0200 Subject: [PATCH 02/39] Use `generateSchema4` instead of `SchemaGenerator` --- lib/compile.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/compile.js b/lib/compile.js index ff816f36..f351b077 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -1,10 +1,11 @@ const cds = require('@sap/cds') -const { SchemaGenerator } = require('./schema') +const { generateSchema4 } = require('./schema') +const { printSchema } = require('graphql') function cds_compile_to_gql (csn) { const m = cds.linked(csn) const services = Object.fromEntries(m.services.map(s => [s.name, new cds.ApplicationService(s.name, m)])) - return new SchemaGenerator().generate(services).printSchema() + return printSchema(generateSchema4(services)) } module.exports = cds_compile_to_gql From 3cf4522eb6052252d5aef00507538cb62deaa629 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 15:18:51 +0200 Subject: [PATCH 03/39] Remove unused `SchemaGenerator` class --- lib/schema/index.js | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/lib/schema/index.js b/lib/schema/index.js index a96e4701..2640e265 100644 --- a/lib/schema/index.js +++ b/lib/schema/index.js @@ -1,30 +1,16 @@ const queryGenerator = require('./query') const mutationGenerator = require('./mutation') -const { GraphQLSchema, printSchema } = require('graphql') +const { GraphQLSchema } = require('graphql') const { createRootResolvers, registerAliasFieldResolvers } = require('../resolvers') -class SchemaGenerator { - generate(services) { - const resolvers = createRootResolvers(services) - const cache = new Map() - const query = queryGenerator(cache).generateQueryObjectType(services, resolvers.Query) - const mutation = mutationGenerator(cache).generateMutationObjectType(services, resolvers.Mutation) - this._schema = new GraphQLSchema({ query, mutation }) - registerAliasFieldResolvers(this._schema) - return this - } - - getSchema() { - return this._schema - } - - printSchema() { - return printSchema(this._schema) - } -} - function generateSchema4(services) { - return new SchemaGenerator().generate(services).getSchema() + const resolvers = createRootResolvers(services) + const cache = new Map() + const query = queryGenerator(cache).generateQueryObjectType(services, resolvers.Query) + const mutation = mutationGenerator(cache).generateMutationObjectType(services, resolvers.Mutation) + const schema = new GraphQLSchema({ query, mutation }) + registerAliasFieldResolvers(schema) + return schema } -module.exports = { SchemaGenerator, generateSchema4 } +module.exports = { generateSchema4 } From 6db047ae741dd7d9b87200b9fa1179f96a5b99a9 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 15:47:49 +0200 Subject: [PATCH 04/39] Extract `cds.compile.to.(gql|graphql)` registration --- cds-plugin.js | 5 +---- lib/GraphQLAdapter.js | 1 + lib/cli.js | 7 +++++++ 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 lib/cli.js diff --git a/cds-plugin.js b/cds-plugin.js index 6a1ef8a7..f06423de 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -1,9 +1,6 @@ const cds = require('@sap/cds') +require('./lib/cli') const protocols = cds.env.protocols ??= {} if (!protocols.graphql) protocols.graphql = { path: "/graphql", impl: "@cap-js/graphql" } -Object.assign(cds.compile.to, { - get gql() { return super.gql = require('./lib/compile') }, - get graphql() { return super.graphql = require('./lib/compile') } -}) \ No newline at end of file diff --git a/lib/GraphQLAdapter.js b/lib/GraphQLAdapter.js index 497344b0..076af40a 100644 --- a/lib/GraphQLAdapter.js +++ b/lib/GraphQLAdapter.js @@ -1,3 +1,4 @@ +require('./lib/cli') const express = require('express') const { generateSchema4 } = require('./schema') const queryLogger = require('./logger') diff --git a/lib/cli.js b/lib/cli.js new file mode 100644 index 00000000..e3abf395 --- /dev/null +++ b/lib/cli.js @@ -0,0 +1,7 @@ +const cds = require('@sap/cds') + +// Register gql and graphql as cds.compile.to options +Object.assign(cds.compile.to, { + get gql() { return super.gql = require('./lib/compile') }, + get graphql() { return super.graphql = require('./lib/compile') } +}) From 41737939a872c98516f231129d891a937363480c Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 15:49:41 +0200 Subject: [PATCH 05/39] Prettier format --- lib/cli.js | 8 ++++++-- lib/compile.js | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index e3abf395..42825e21 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -2,6 +2,10 @@ const cds = require('@sap/cds') // Register gql and graphql as cds.compile.to options Object.assign(cds.compile.to, { - get gql() { return super.gql = require('./lib/compile') }, - get graphql() { return super.graphql = require('./lib/compile') } + get gql() { + return (super.gql = require('./lib/compile')) + }, + get graphql() { + return (super.graphql = require('./lib/compile')) + } }) diff --git a/lib/compile.js b/lib/compile.js index f351b077..ff5a2f1e 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -2,7 +2,7 @@ const cds = require('@sap/cds') const { generateSchema4 } = require('./schema') const { printSchema } = require('graphql') -function cds_compile_to_gql (csn) { +function cds_compile_to_gql(csn) { const m = cds.linked(csn) const services = Object.fromEntries(m.services.map(s => [s.name, new cds.ApplicationService(s.name, m)])) return printSchema(generateSchema4(services)) From cf8ebd52c4e97414fd336c2a3bf90ff5f8ed2ddb Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 15:51:34 +0200 Subject: [PATCH 06/39] Reword comment --- lib/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli.js b/lib/cli.js index 42825e21..16fb2022 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,6 +1,6 @@ const cds = require('@sap/cds') -// Register gql and graphql as cds.compile.to options +// Register gql and graphql as cds.compile.to targets Object.assign(cds.compile.to, { get gql() { return (super.gql = require('./lib/compile')) From 939933a4296d4e6697f00033564b99ba51594696 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 16:09:32 +0200 Subject: [PATCH 07/39] Simplify generate-schemas script --- test/scripts/generate-schemas.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/scripts/generate-schemas.js b/test/scripts/generate-schemas.js index f1b544cc..34b542fe 100644 --- a/test/scripts/generate-schemas.js +++ b/test/scripts/generate-schemas.js @@ -1,16 +1,16 @@ #!/usr/bin/env node const path = require('path') const fs = require('fs') -const { SCHEMAS_DIR, cdsFilesToGQLSchema, formatSchema } = require('../util') +const { SCHEMAS_DIR, formatSchema } = require('../util') const { models } = require('../resources') -const { printSchema } = require('graphql') +const compile = require('../../lib/compile') ;(async () => { fs.rmSync(SCHEMAS_DIR, { recursive: true, force: true }) fs.mkdirSync(SCHEMAS_DIR) for (const model of models) { console.log(`Generating GraphQL schema "${model.name}.gql"`) - const graphQLSchema = formatSchema(printSchema(await cdsFilesToGQLSchema(model.files))) + const graphQLSchema = formatSchema(compile(await cds.load(model.files))) const schemaPath = path.join(SCHEMAS_DIR, `${model.name}.gql`) const schemaPathDir = path.parse(schemaPath).dir if (!fs.existsSync(schemaPathDir)) fs.mkdirSync(schemaPathDir) From 95cbd70f87ef9676aa301590d93651519a7036ca Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 16:32:29 +0200 Subject: [PATCH 08/39] Fix module paths --- lib/GraphQLAdapter.js | 2 +- lib/cli.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/GraphQLAdapter.js b/lib/GraphQLAdapter.js index 076af40a..c75c7cb8 100644 --- a/lib/GraphQLAdapter.js +++ b/lib/GraphQLAdapter.js @@ -1,4 +1,4 @@ -require('./lib/cli') +require('./cli') const express = require('express') const { generateSchema4 } = require('./schema') const queryLogger = require('./logger') diff --git a/lib/cli.js b/lib/cli.js index 16fb2022..09eac2cd 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -3,9 +3,9 @@ const cds = require('@sap/cds') // Register gql and graphql as cds.compile.to targets Object.assign(cds.compile.to, { get gql() { - return (super.gql = require('./lib/compile')) + return (super.gql = require('./compile')) }, get graphql() { - return (super.graphql = require('./lib/compile')) + return (super.graphql = require('./compile')) } }) From 72e7a139b41cf510442724cc059d19750044cfb2 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 16:33:47 +0200 Subject: [PATCH 09/39] WIP comment out require cli --- cds-plugin.js | 2 +- lib/GraphQLAdapter.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cds-plugin.js b/cds-plugin.js index f06423de..9ef813b5 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -1,5 +1,5 @@ const cds = require('@sap/cds') -require('./lib/cli') +// require('./lib/cli') const protocols = cds.env.protocols ??= {} if (!protocols.graphql) protocols.graphql = { path: "/graphql", impl: "@cap-js/graphql" diff --git a/lib/GraphQLAdapter.js b/lib/GraphQLAdapter.js index c75c7cb8..2729e004 100644 --- a/lib/GraphQLAdapter.js +++ b/lib/GraphQLAdapter.js @@ -1,4 +1,4 @@ -require('./cli') +// require('./cli') const express = require('express') const { generateSchema4 } = require('./schema') const queryLogger = require('./logger') From c7d1e1f7a6c6718641b7d515657236c032b3d1a6 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 16:37:06 +0200 Subject: [PATCH 10/39] Add option to compile to GraphQLSchema object --- lib/compile.js | 6 ++++-- test/scripts/generate-schemas.js | 1 + test/tests/enrich.test.js | 6 ++++-- test/tests/schema.test.js | 5 +++-- test/util/index.js | 11 +---------- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/compile.js b/lib/compile.js index ff5a2f1e..e49a9938 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -2,10 +2,12 @@ const cds = require('@sap/cds') const { generateSchema4 } = require('./schema') const { printSchema } = require('graphql') -function cds_compile_to_gql(csn) { +function cds_compile_to_gql(csn, options = { object = false } = {}) { const m = cds.linked(csn) const services = Object.fromEntries(m.services.map(s => [s.name, new cds.ApplicationService(s.name, m)])) - return printSchema(generateSchema4(services)) + const schema = generateSchema4(services) + if (options.object) return schema + return printSchema(schema) } module.exports = cds_compile_to_gql diff --git a/test/scripts/generate-schemas.js b/test/scripts/generate-schemas.js index 34b542fe..5c69b5f6 100644 --- a/test/scripts/generate-schemas.js +++ b/test/scripts/generate-schemas.js @@ -1,4 +1,5 @@ #!/usr/bin/env node +const cds = require('@sap/cds') const path = require('path') const fs = require('fs') const { SCHEMAS_DIR, formatSchema } = require('../util') diff --git a/test/tests/enrich.test.js b/test/tests/enrich.test.js index 6ec334eb..dbfe1a57 100644 --- a/test/tests/enrich.test.js +++ b/test/tests/enrich.test.js @@ -1,15 +1,17 @@ describe('graphql - enrich AST with parsed inline literal values', () => { + const cds = require('@sap/cds') const { gql } = require('../util') const { parse } = require('graphql') const enrich = require('../../lib/resolvers/parse/ast/enrich') const { models } = require('../resources') - const { cdsFilesToGQLSchema, fakeInfoObject } = require('../util') + const compile = require('../../lib/compile') + const { fakeInfoObject } = require('../util') let bookshopSchema beforeAll(async () => { const bookshopModel = models.find(m => m.name === 'bookshop-graphql') - bookshopSchema = await cdsFilesToGQLSchema(bookshopModel.files) + bookshopSchema = compile(await cds.load(bookshopModel.files), { object: true }) }) test('parsing of literal value as top level argument', async () => { diff --git a/test/tests/schema.test.js b/test/tests/schema.test.js index 6a6ee1aa..729cb34e 100644 --- a/test/tests/schema.test.js +++ b/test/tests/schema.test.js @@ -6,14 +6,15 @@ const cdsVersion = require('@sap/cds').version let { models } = require('../resources') models = models.filter(m => !m.requires_cds || semver.satisfies(cdsVersion, m.requires_cds)) -const { SCHEMAS_DIR, cdsFilesToGQLSchema, formatSchema } = require('../util') +const compile = require('../../lib/compile') +const { SCHEMAS_DIR, formatSchema } = require('../util') const { printSchema, validateSchema } = require('graphql') describe('graphql - schema generation', () => { describe('generated schema should match saved schema', () => { models.forEach(model => { it('should process model ' + model.name, async () => { - const generatedSchemaObject = await cdsFilesToGQLSchema(model.files) + const generatedSchemaObject = compile(await cds.load(model.files), { object: true }) const schemaValidationErrors = validateSchema(generatedSchemaObject) expect(schemaValidationErrors.length).toEqual(0) diff --git a/test/util/index.js b/test/util/index.js index f9c46bc5..78f60669 100644 --- a/test/util/index.js +++ b/test/util/index.js @@ -4,15 +4,6 @@ const { buildSchema, lexicographicSortSchema, printSchema, Kind } = require('gra const SCHEMAS_DIR = path.join(__dirname, '../schemas') -const cdsFilesToGQLSchema = async files => { - const cds = require('@sap/cds/lib') - const { generateSchema4 } = require('../../lib/schema') - - const m = cds.linked(await cds.load(files)) - const services = Object.fromEntries(m.services.map(s => [s.name, new cds.ApplicationService(s.name, m)])) - return generateSchema4(services) -} - const formatSchema = schemaString => prettier.format(printSchema(lexicographicSortSchema(buildSchema(schemaString))), { parser: 'graphql', printWidth: 0 }) @@ -46,4 +37,4 @@ const fakeInfoObject = (document, schema, parentTypeName, variables) => { */ const gql = String.raw -module.exports = { SCHEMAS_DIR, cdsFilesToGQLSchema, formatSchema, fakeInfoObject, gql } +module.exports = { SCHEMAS_DIR, formatSchema, fakeInfoObject, gql } From 90ba0399d350b561eb540c85d0d11f973595d456 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 16:46:57 +0200 Subject: [PATCH 11/39] Add compile option to lexicographically sort schema --- lib/compile.js | 9 ++++++--- test/scripts/generate-schemas.js | 2 +- test/tests/schema.test.js | 2 +- test/util/index.js | 5 ++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/compile.js b/lib/compile.js index e49a9938..3ab038f5 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -1,12 +1,15 @@ const cds = require('@sap/cds') const { generateSchema4 } = require('./schema') -const { printSchema } = require('graphql') +const { printSchema, lexicographicSortSchema } = require('graphql') -function cds_compile_to_gql(csn, options = { object = false } = {}) { +function cds_compile_to_gql(csn, options = ({ object = false, sort = false } = {})) { const m = cds.linked(csn) const services = Object.fromEntries(m.services.map(s => [s.name, new cds.ApplicationService(s.name, m)])) - const schema = generateSchema4(services) + let schema = generateSchema4(services) + + if (options.sort) schema = lexicographicSortSchema(schema) if (options.object) return schema + return printSchema(schema) } diff --git a/test/scripts/generate-schemas.js b/test/scripts/generate-schemas.js index 5c69b5f6..a4bac924 100644 --- a/test/scripts/generate-schemas.js +++ b/test/scripts/generate-schemas.js @@ -11,7 +11,7 @@ const compile = require('../../lib/compile') fs.mkdirSync(SCHEMAS_DIR) for (const model of models) { console.log(`Generating GraphQL schema "${model.name}.gql"`) - const graphQLSchema = formatSchema(compile(await cds.load(model.files))) + const graphQLSchema = formatSchema(compile(await cds.load(model.files), { sort: true })) const schemaPath = path.join(SCHEMAS_DIR, `${model.name}.gql`) const schemaPathDir = path.parse(schemaPath).dir if (!fs.existsSync(schemaPathDir)) fs.mkdirSync(schemaPathDir) diff --git a/test/tests/schema.test.js b/test/tests/schema.test.js index 729cb34e..8fb7ed23 100644 --- a/test/tests/schema.test.js +++ b/test/tests/schema.test.js @@ -14,7 +14,7 @@ describe('graphql - schema generation', () => { describe('generated schema should match saved schema', () => { models.forEach(model => { it('should process model ' + model.name, async () => { - const generatedSchemaObject = compile(await cds.load(model.files), { object: true }) + const generatedSchemaObject = compile(await cds.load(model.files), { object: true, sort: true }) const schemaValidationErrors = validateSchema(generatedSchemaObject) expect(schemaValidationErrors.length).toEqual(0) diff --git a/test/util/index.js b/test/util/index.js index 78f60669..476510fb 100644 --- a/test/util/index.js +++ b/test/util/index.js @@ -1,11 +1,10 @@ const path = require('path') const prettier = require('prettier') -const { buildSchema, lexicographicSortSchema, printSchema, Kind } = require('graphql') +const { Kind } = require('graphql') const SCHEMAS_DIR = path.join(__dirname, '../schemas') -const formatSchema = schemaString => - prettier.format(printSchema(lexicographicSortSchema(buildSchema(schemaString))), { parser: 'graphql', printWidth: 0 }) +const formatSchema = schemaString => prettier.format(schemaString, { parser: 'graphql', printWidth: 0 }) /** * Create a fake/mock object that matches the structure of the info object that is passed to resolver functions by the graphql.js library. From 6dbcf194da7ad62fb087c31d4f7de788a5c2e79b Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 17:21:48 +0200 Subject: [PATCH 12/39] Fix registration of gql and graphql getters --- cds-plugin.js | 2 +- lib/GraphQLAdapter.js | 2 +- lib/cli.js | 12 +++++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cds-plugin.js b/cds-plugin.js index 9ef813b5..f06423de 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -1,5 +1,5 @@ const cds = require('@sap/cds') -// require('./lib/cli') +require('./lib/cli') const protocols = cds.env.protocols ??= {} if (!protocols.graphql) protocols.graphql = { path: "/graphql", impl: "@cap-js/graphql" diff --git a/lib/GraphQLAdapter.js b/lib/GraphQLAdapter.js index 2729e004..c75c7cb8 100644 --- a/lib/GraphQLAdapter.js +++ b/lib/GraphQLAdapter.js @@ -1,4 +1,4 @@ -// require('./cli') +require('./cli') const express = require('express') const { generateSchema4 } = require('./schema') const queryLogger = require('./logger') diff --git a/lib/cli.js b/lib/cli.js index 09eac2cd..4e029c6a 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,11 +1,9 @@ const cds = require('@sap/cds') // Register gql and graphql as cds.compile.to targets -Object.assign(cds.compile.to, { - get gql() { - return (super.gql = require('./compile')) - }, - get graphql() { - return (super.graphql = require('./compile')) - } +Object.defineProperty(cds.compile.to, 'gql', { + get: () => require('./compile') +}) +Object.defineProperty(cds.compile.to, 'graphql', { + get: () => require('./compile') }) From 0a1828b71c6e1ff351391bb898b61a2600b775d8 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 23 Aug 2023 17:26:48 +0200 Subject: [PATCH 13/39] `Object.defineProperty` -> `Object.defineProperties` --- lib/cli.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 4e029c6a..60be0fac 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,9 +1,11 @@ const cds = require('@sap/cds') // Register gql and graphql as cds.compile.to targets -Object.defineProperty(cds.compile.to, 'gql', { - get: () => require('./compile') -}) -Object.defineProperty(cds.compile.to, 'graphql', { - get: () => require('./compile') +Object.defineProperties(cds.compile.to, { + gql: { + get: () => require('./compile') + }, + graphql: { + get: () => require('./compile') + } }) From 831e368a60c855b0b46b08617105b3e85762d482 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 25 Aug 2023 10:13:42 +0200 Subject: [PATCH 14/39] Rename `compile` imports to `cds_compile_to_gql` --- test/scripts/generate-schemas.js | 4 ++-- test/tests/enrich.test.js | 4 ++-- test/tests/schema.test.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/scripts/generate-schemas.js b/test/scripts/generate-schemas.js index a4bac924..696237d8 100644 --- a/test/scripts/generate-schemas.js +++ b/test/scripts/generate-schemas.js @@ -4,14 +4,14 @@ const path = require('path') const fs = require('fs') const { SCHEMAS_DIR, formatSchema } = require('../util') const { models } = require('../resources') -const compile = require('../../lib/compile') +const cds_compile_to_gql = require('../../lib/compile') ;(async () => { fs.rmSync(SCHEMAS_DIR, { recursive: true, force: true }) fs.mkdirSync(SCHEMAS_DIR) for (const model of models) { console.log(`Generating GraphQL schema "${model.name}.gql"`) - const graphQLSchema = formatSchema(compile(await cds.load(model.files), { sort: true })) + const graphQLSchema = formatSchema(cds_compile_to_gql(await cds.load(model.files), { sort: true })) const schemaPath = path.join(SCHEMAS_DIR, `${model.name}.gql`) const schemaPathDir = path.parse(schemaPath).dir if (!fs.existsSync(schemaPathDir)) fs.mkdirSync(schemaPathDir) diff --git a/test/tests/enrich.test.js b/test/tests/enrich.test.js index dbfe1a57..f2cc3803 100644 --- a/test/tests/enrich.test.js +++ b/test/tests/enrich.test.js @@ -4,14 +4,14 @@ describe('graphql - enrich AST with parsed inline literal values', () => { const { parse } = require('graphql') const enrich = require('../../lib/resolvers/parse/ast/enrich') const { models } = require('../resources') - const compile = require('../../lib/compile') + const cds_compile_to_gql = require('../../lib/compile') const { fakeInfoObject } = require('../util') let bookshopSchema beforeAll(async () => { const bookshopModel = models.find(m => m.name === 'bookshop-graphql') - bookshopSchema = compile(await cds.load(bookshopModel.files), { object: true }) + bookshopSchema = cds_compile_to_gql(await cds.load(bookshopModel.files), { object: true }) }) test('parsing of literal value as top level argument', async () => { diff --git a/test/tests/schema.test.js b/test/tests/schema.test.js index 8fb7ed23..cafc0556 100644 --- a/test/tests/schema.test.js +++ b/test/tests/schema.test.js @@ -6,7 +6,7 @@ const cdsVersion = require('@sap/cds').version let { models } = require('../resources') models = models.filter(m => !m.requires_cds || semver.satisfies(cdsVersion, m.requires_cds)) -const compile = require('../../lib/compile') +const cds_compile_to_gql = require('../../lib/compile') const { SCHEMAS_DIR, formatSchema } = require('../util') const { printSchema, validateSchema } = require('graphql') @@ -14,7 +14,7 @@ describe('graphql - schema generation', () => { describe('generated schema should match saved schema', () => { models.forEach(model => { it('should process model ' + model.name, async () => { - const generatedSchemaObject = compile(await cds.load(model.files), { object: true, sort: true }) + const generatedSchemaObject = cds_compile_to_gql(await cds.load(model.files), { object: true, sort: true }) const schemaValidationErrors = validateSchema(generatedSchemaObject) expect(schemaValidationErrors.length).toEqual(0) From e630531c2f03075b01714cc8570f521013995f4a Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 25 Aug 2023 10:14:13 +0200 Subject: [PATCH 15/39] Extract require to lazy function for readability --- lib/cli.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 60be0fac..6608ab9f 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,11 +1,13 @@ const cds = require('@sap/cds') +const _lazy = () => require('./compile') + // Register gql and graphql as cds.compile.to targets Object.defineProperties(cds.compile.to, { gql: { - get: () => require('./compile') + get: _lazy }, graphql: { - get: () => require('./compile') + get: _lazy } }) From 47899ff452c4390e9f2aeeda2a3fa48d63f423be Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 25 Aug 2023 10:14:57 +0200 Subject: [PATCH 16/39] Remove default options destructuring from args --- lib/compile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/compile.js b/lib/compile.js index 3ab038f5..a8700581 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -2,9 +2,10 @@ const cds = require('@sap/cds') const { generateSchema4 } = require('./schema') const { printSchema, lexicographicSortSchema } = require('graphql') -function cds_compile_to_gql(csn, options = ({ object = false, sort = false } = {})) { +function cds_compile_to_gql(csn, options) { const m = cds.linked(csn) const services = Object.fromEntries(m.services.map(s => [s.name, new cds.ApplicationService(s.name, m)])) + let schema = generateSchema4(services) if (options.sort) schema = lexicographicSortSchema(schema) From 0c63d7bfd658afd01b41a57d553d5dc4b9230a33 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 25 Aug 2023 10:16:29 +0200 Subject: [PATCH 17/39] Rename `cli.js` to `api.js` --- cds-plugin.js | 2 +- lib/GraphQLAdapter.js | 2 +- lib/{cli.js => api.js} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename lib/{cli.js => api.js} (100%) diff --git a/cds-plugin.js b/cds-plugin.js index f06423de..e36ba543 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -1,5 +1,5 @@ const cds = require('@sap/cds') -require('./lib/cli') +require('./lib/api') const protocols = cds.env.protocols ??= {} if (!protocols.graphql) protocols.graphql = { path: "/graphql", impl: "@cap-js/graphql" diff --git a/lib/GraphQLAdapter.js b/lib/GraphQLAdapter.js index c75c7cb8..7903778d 100644 --- a/lib/GraphQLAdapter.js +++ b/lib/GraphQLAdapter.js @@ -1,4 +1,4 @@ -require('./cli') +require('./api') const express = require('express') const { generateSchema4 } = require('./schema') const queryLogger = require('./logger') diff --git a/lib/cli.js b/lib/api.js similarity index 100% rename from lib/cli.js rename to lib/api.js From 1bbd9b2e87c7ea8661e286cd19be6ba35d97a6bd Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 25 Aug 2023 10:51:53 +0200 Subject: [PATCH 18/39] Re-add `SchemaGenerator` class for backwards compatibility --- lib/schema/index.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/schema/index.js b/lib/schema/index.js index 2640e265..fdcf0fcf 100644 --- a/lib/schema/index.js +++ b/lib/schema/index.js @@ -3,6 +3,21 @@ const mutationGenerator = require('./mutation') const { GraphQLSchema } = require('graphql') const { createRootResolvers, registerAliasFieldResolvers } = require('../resolvers') +// REVISIT: remove with cds^8 +class SchemaGenerator { + generate(services) { + this._schema = generateSchema4(services) + } + + getSchema() { + return this._schema + } + + printSchema() { + return printSchema(this._schema) + } +} + function generateSchema4(services) { const resolvers = createRootResolvers(services) const cache = new Map() @@ -13,4 +28,4 @@ function generateSchema4(services) { return schema } -module.exports = { generateSchema4 } +module.exports = { SchemaGenerator, generateSchema4 } From c7d11fc8f3fc66c096f32e8d39953736ea15fed6 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 25 Aug 2023 10:55:33 +0200 Subject: [PATCH 19/39] `options.object` -> `options.as: 'obj'` to align with `cds.compile.to.sql` --- lib/compile.js | 2 +- test/tests/enrich.test.js | 2 +- test/tests/schema.test.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compile.js b/lib/compile.js index a8700581..434b04af 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -9,7 +9,7 @@ function cds_compile_to_gql(csn, options) { let schema = generateSchema4(services) if (options.sort) schema = lexicographicSortSchema(schema) - if (options.object) return schema + if (/^obj|object$/.test(options.as)) return schema return printSchema(schema) } diff --git a/test/tests/enrich.test.js b/test/tests/enrich.test.js index f2cc3803..9a5da460 100644 --- a/test/tests/enrich.test.js +++ b/test/tests/enrich.test.js @@ -11,7 +11,7 @@ describe('graphql - enrich AST with parsed inline literal values', () => { beforeAll(async () => { const bookshopModel = models.find(m => m.name === 'bookshop-graphql') - bookshopSchema = cds_compile_to_gql(await cds.load(bookshopModel.files), { object: true }) + bookshopSchema = cds_compile_to_gql(await cds.load(bookshopModel.files), { as: 'object' }) }) test('parsing of literal value as top level argument', async () => { diff --git a/test/tests/schema.test.js b/test/tests/schema.test.js index cafc0556..add8d632 100644 --- a/test/tests/schema.test.js +++ b/test/tests/schema.test.js @@ -14,7 +14,7 @@ describe('graphql - schema generation', () => { describe('generated schema should match saved schema', () => { models.forEach(model => { it('should process model ' + model.name, async () => { - const generatedSchemaObject = cds_compile_to_gql(await cds.load(model.files), { object: true, sort: true }) + const generatedSchemaObject = cds_compile_to_gql(await cds.load(model.files), { as: 'obj', sort: true }) const schemaValidationErrors = validateSchema(generatedSchemaObject) expect(schemaValidationErrors.length).toEqual(0) From 77e9a8d1497f89571bf64b489b11dab94b64f7b7 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 25 Aug 2023 11:07:19 +0200 Subject: [PATCH 20/39] Default options to empty object --- lib/compile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compile.js b/lib/compile.js index 434b04af..0748987c 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -2,7 +2,7 @@ const cds = require('@sap/cds') const { generateSchema4 } = require('./schema') const { printSchema, lexicographicSortSchema } = require('graphql') -function cds_compile_to_gql(csn, options) { +function cds_compile_to_gql(csn, options = {}) { const m = cds.linked(csn) const services = Object.fromEntries(m.services.map(s => [s.name, new cds.ApplicationService(s.name, m)])) From caa39513ea6b1bdfcebae4aa9ea73c3dfd00df55 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 6 Sep 2023 14:23:51 +0200 Subject: [PATCH 21/39] Fix `SchemaGenerator.generate()` chaining --- lib/schema/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/schema/index.js b/lib/schema/index.js index fdcf0fcf..f5ecb7dd 100644 --- a/lib/schema/index.js +++ b/lib/schema/index.js @@ -7,6 +7,7 @@ const { createRootResolvers, registerAliasFieldResolvers } = require('../resolve class SchemaGenerator { generate(services) { this._schema = generateSchema4(services) + return this } getSchema() { From 1e3103f02d65288613d60d9e48d04075241f588b Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 6 Sep 2023 14:24:17 +0200 Subject: [PATCH 22/39] Fix missing import --- lib/schema/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/schema/index.js b/lib/schema/index.js index f5ecb7dd..ba9b2b0a 100644 --- a/lib/schema/index.js +++ b/lib/schema/index.js @@ -2,6 +2,7 @@ const queryGenerator = require('./query') const mutationGenerator = require('./mutation') const { GraphQLSchema } = require('graphql') const { createRootResolvers, registerAliasFieldResolvers } = require('../resolvers') +const { printSchema } = require('graphql') // REVISIT: remove with cds^8 class SchemaGenerator { From 69e48679eceddd8c86d2cb65fc88e3ee160f6431 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 13 Sep 2023 10:47:33 +0200 Subject: [PATCH 23/39] Ensure plugin is loaded to extend `compile` in bookshop-graphql project --- test/resources/bookshop-graphql/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/resources/bookshop-graphql/package.json b/test/resources/bookshop-graphql/package.json index 4b36951d..100137e3 100644 --- a/test/resources/bookshop-graphql/package.json +++ b/test/resources/bookshop-graphql/package.json @@ -1,4 +1,7 @@ { + "dependencies": { + "@cap-js/graphql": "../../.." + }, "cds": { "requires": { "db": { From b9b96dc56e33cefa104fb5f7551bf25aa95f6577 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Thu, 28 Sep 2023 17:28:18 +0200 Subject: [PATCH 24/39] Remove `formatSchema` util function --- test/scripts/generate-schemas.js | 4 ++-- test/tests/schema.test.js | 6 +++--- test/util/index.js | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/test/scripts/generate-schemas.js b/test/scripts/generate-schemas.js index 696237d8..b95f34d8 100644 --- a/test/scripts/generate-schemas.js +++ b/test/scripts/generate-schemas.js @@ -2,7 +2,7 @@ const cds = require('@sap/cds') const path = require('path') const fs = require('fs') -const { SCHEMAS_DIR, formatSchema } = require('../util') +const { SCHEMAS_DIR } = require('../util') const { models } = require('../resources') const cds_compile_to_gql = require('../../lib/compile') @@ -11,7 +11,7 @@ const cds_compile_to_gql = require('../../lib/compile') fs.mkdirSync(SCHEMAS_DIR) for (const model of models) { console.log(`Generating GraphQL schema "${model.name}.gql"`) - const graphQLSchema = formatSchema(cds_compile_to_gql(await cds.load(model.files), { sort: true })) + const graphQLSchema = cds_compile_to_gql(await cds.load(model.files), { sort: true }) const schemaPath = path.join(SCHEMAS_DIR, `${model.name}.gql`) const schemaPathDir = path.parse(schemaPath).dir if (!fs.existsSync(schemaPathDir)) fs.mkdirSync(schemaPathDir) diff --git a/test/tests/schema.test.js b/test/tests/schema.test.js index add8d632..3ee72fe1 100644 --- a/test/tests/schema.test.js +++ b/test/tests/schema.test.js @@ -7,7 +7,7 @@ let { models } = require('../resources') models = models.filter(m => !m.requires_cds || semver.satisfies(cdsVersion, m.requires_cds)) const cds_compile_to_gql = require('../../lib/compile') -const { SCHEMAS_DIR, formatSchema } = require('../util') +const { SCHEMAS_DIR } = require('../util') const { printSchema, validateSchema } = require('graphql') describe('graphql - schema generation', () => { @@ -18,8 +18,8 @@ describe('graphql - schema generation', () => { const schemaValidationErrors = validateSchema(generatedSchemaObject) expect(schemaValidationErrors.length).toEqual(0) - const loadedSchema = formatSchema(fs.readFileSync(path.join(SCHEMAS_DIR, `${model.name}.gql`), 'utf-8')) - const generatedSchema = formatSchema(printSchema(generatedSchemaObject)) + const loadedSchema = fs.readFileSync(path.join(SCHEMAS_DIR, `${model.name}.gql`), 'utf-8') + const generatedSchema = printSchema(generatedSchemaObject) expect(loadedSchema).toEqual(generatedSchema) }) }) diff --git a/test/util/index.js b/test/util/index.js index fefb0518..59b4641e 100644 --- a/test/util/index.js +++ b/test/util/index.js @@ -3,8 +3,6 @@ const { Kind } = require('graphql') const SCHEMAS_DIR = path.join(__dirname, '../schemas') -const formatSchema = schemaString => schemaString - /** * Create a fake/mock object that matches the structure of the info object that is passed to resolver functions by the graphql.js library. * @@ -35,4 +33,4 @@ const fakeInfoObject = (document, schema, parentTypeName, variables) => { */ const gql = String.raw -module.exports = { SCHEMAS_DIR, formatSchema, fakeInfoObject, gql } +module.exports = { SCHEMAS_DIR, fakeInfoObject, gql } From f6b9f07fcafa093c17f1506d5305c9f3719f9cb6 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Thu, 28 Sep 2023 17:30:24 +0200 Subject: [PATCH 25/39] Make compile format regex case insensitive --- lib/compile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compile.js b/lib/compile.js index 0748987c..640e49dd 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -9,7 +9,7 @@ function cds_compile_to_gql(csn, options = {}) { let schema = generateSchema4(services) if (options.sort) schema = lexicographicSortSchema(schema) - if (/^obj|object$/.test(options.as)) return schema + if (/^obj|object$/i.test(options.as)) return schema return printSchema(schema) } From 8d154e8cb9448c4882a3f7bc8676fe104fb233a3 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Thu, 28 Sep 2023 17:47:12 +0200 Subject: [PATCH 26/39] Extract CSN to variable for better readability --- test/tests/schema.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/tests/schema.test.js b/test/tests/schema.test.js index 3ee72fe1..b0aeaca1 100644 --- a/test/tests/schema.test.js +++ b/test/tests/schema.test.js @@ -14,7 +14,8 @@ describe('graphql - schema generation', () => { describe('generated schema should match saved schema', () => { models.forEach(model => { it('should process model ' + model.name, async () => { - const generatedSchemaObject = cds_compile_to_gql(await cds.load(model.files), { as: 'obj', sort: true }) + const csn = await cds.load(model.files) + const generatedSchemaObject = cds_compile_to_gql(csn, { as: 'obj', sort: true }) const schemaValidationErrors = validateSchema(generatedSchemaObject) expect(schemaValidationErrors.length).toEqual(0) From 82067d3547a3813fff50e35cdc015524bf5af4c8 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 29 Sep 2023 10:44:53 +0200 Subject: [PATCH 27/39] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c181b8f6..e089a1b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Moved registration of `cds.compile.to.gql` and `cds.compile.to.graphql` targets from `@sap/cds` to `@cap-js/graphql` + ### Fixed - Malformed responses for convoluted queries in which parts of results are supposed to be returned multiple times, caused by formatting results in-place From 678de57e0b35360a2b3392d315cbef99315eeb0b Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 29 Sep 2023 11:37:21 +0200 Subject: [PATCH 28/39] Improve lazy loading and clarify purpose of api module --- cds-plugin.js | 2 +- lib/GraphQLAdapter.js | 3 ++- lib/api.js | 33 ++++++++++++++++++++++----------- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/cds-plugin.js b/cds-plugin.js index e36ba543..b1fb03a7 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -1,5 +1,5 @@ const cds = require('@sap/cds') -require('./lib/api') +require('./lib/api').registerCompileTargets(cds.compile.to) const protocols = cds.env.protocols ??= {} if (!protocols.graphql) protocols.graphql = { path: "/graphql", impl: "@cap-js/graphql" diff --git a/lib/GraphQLAdapter.js b/lib/GraphQLAdapter.js index 7903778d..bcdca40c 100644 --- a/lib/GraphQLAdapter.js +++ b/lib/GraphQLAdapter.js @@ -1,4 +1,5 @@ -require('./api') +const cds = require('@sap/cds') +require('./api').registerCompileTargets(cds.compile.to) const express = require('express') const { generateSchema4 } = require('./schema') const queryLogger = require('./logger') diff --git a/lib/api.js b/lib/api.js index 6608ab9f..b94c65f0 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,13 +1,24 @@ -const cds = require('@sap/cds') +function _registerLazy() { + const value = require('./compile') + Object.defineProperties(this, { + gql: { value }, + graphql: { value } + }) + return value +} -const _lazy = () => require('./compile') +const registerCompileTargets = cds_compile_to => { + // Register gql and graphql as cds.compile.to targets + Object.defineProperties(cds_compile_to, { + gql: { + get: _registerLazy, + configurable: true + }, + graphql: { + get: _registerLazy, + configurable: true + } + }) +} -// Register gql and graphql as cds.compile.to targets -Object.defineProperties(cds.compile.to, { - gql: { - get: _lazy - }, - graphql: { - get: _lazy - } -}) +module.exports = { registerCompileTargets } From 24a4c082a42ab0e6538d6e9ff404367a1ceb15ef Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 29 Sep 2023 11:49:33 +0200 Subject: [PATCH 29/39] Shorten registration and avoid specifying properties twice --- lib/api.js | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/api.js b/lib/api.js index b94c65f0..1c03aa21 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,24 +1,18 @@ -function _registerLazy() { +const TARGETS = ['gql', 'graphql'] + +function _registerCompileTargetsLazy() { const value = require('./compile') - Object.defineProperties(this, { - gql: { value }, - graphql: { value } - }) + TARGETS.forEach(target => Object.defineProperty(this, target, { value })) return value } -const registerCompileTargets = cds_compile_to => { - // Register gql and graphql as cds.compile.to targets - Object.defineProperties(cds_compile_to, { - gql: { - get: _registerLazy, - configurable: true - }, - graphql: { - get: _registerLazy, +// Register gql and graphql as cds.compile.to targets +const registerCompileTargets = cds_compile_to => + TARGETS.forEach(target => + Object.defineProperty(cds_compile_to, target, { + get: _registerCompileTargetsLazy, configurable: true - } - }) -} + }) + ) module.exports = { registerCompileTargets } From f90d1aa7f7c544263c3768a5893e5613e4a5aedc Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 29 Sep 2023 11:52:58 +0200 Subject: [PATCH 30/39] Rename lazy function --- lib/api.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/api.js b/lib/api.js index 1c03aa21..a9baf1fa 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,6 +1,6 @@ const TARGETS = ['gql', 'graphql'] -function _registerCompileTargetsLazy() { +function _lazyRegisterCompileTargets() { const value = require('./compile') TARGETS.forEach(target => Object.defineProperty(this, target, { value })) return value @@ -10,7 +10,7 @@ function _registerCompileTargetsLazy() { const registerCompileTargets = cds_compile_to => TARGETS.forEach(target => Object.defineProperty(cds_compile_to, target, { - get: _registerCompileTargetsLazy, + get: _lazyRegisterCompileTargets, configurable: true }) ) From 2ecb222112ee3dfbb9cf5dfce55ad272a914e205 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 29 Sep 2023 13:40:50 +0200 Subject: [PATCH 31/39] Use `.to.gql` and `.to.graphql` in tests --- test/scripts/generate-schemas.js | 6 ++++-- test/tests/schema.test.js | 12 +++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/test/scripts/generate-schemas.js b/test/scripts/generate-schemas.js index b95f34d8..57e08a1d 100644 --- a/test/scripts/generate-schemas.js +++ b/test/scripts/generate-schemas.js @@ -1,17 +1,19 @@ #!/usr/bin/env node const cds = require('@sap/cds') +// Load @cap-js/graphql plugin to ensure .to.gql and .to.graphql compile targets are registered +require('../../cds-plugin') const path = require('path') const fs = require('fs') const { SCHEMAS_DIR } = require('../util') const { models } = require('../resources') -const cds_compile_to_gql = require('../../lib/compile') ;(async () => { fs.rmSync(SCHEMAS_DIR, { recursive: true, force: true }) fs.mkdirSync(SCHEMAS_DIR) for (const model of models) { console.log(`Generating GraphQL schema "${model.name}.gql"`) - const graphQLSchema = cds_compile_to_gql(await cds.load(model.files), { sort: true }) + const csn = await cds.load(model.files) + const graphQLSchema = cds.compile(csn).to.gql({ sort: true }) const schemaPath = path.join(SCHEMAS_DIR, `${model.name}.gql`) const schemaPathDir = path.parse(schemaPath).dir if (!fs.existsSync(schemaPathDir)) fs.mkdirSync(schemaPathDir) diff --git a/test/tests/schema.test.js b/test/tests/schema.test.js index b0aeaca1..625cb1b7 100644 --- a/test/tests/schema.test.js +++ b/test/tests/schema.test.js @@ -1,12 +1,14 @@ const fs = require('fs') const path = require('path') const semver = require('semver') -const cdsVersion = require('@sap/cds').version +const { version: cdsVersion } = require('@sap/cds') +// Load GraphQLAdapter to ensure .to.gql and .to.graphql compile targets are registered +require('../../lib/GraphQLAdapter') -let { models } = require('../resources') -models = models.filter(m => !m.requires_cds || semver.satisfies(cdsVersion, m.requires_cds)) +const models = require('../resources').models.filter( + m => !m.requires_cds || semver.satisfies(cdsVersion, m.requires_cds) +) -const cds_compile_to_gql = require('../../lib/compile') const { SCHEMAS_DIR } = require('../util') const { printSchema, validateSchema } = require('graphql') @@ -15,7 +17,7 @@ describe('graphql - schema generation', () => { models.forEach(model => { it('should process model ' + model.name, async () => { const csn = await cds.load(model.files) - const generatedSchemaObject = cds_compile_to_gql(csn, { as: 'obj', sort: true }) + const generatedSchemaObject = cds.compile(csn).to.graphql({ as: 'obj', sort: true }) const schemaValidationErrors = validateSchema(generatedSchemaObject) expect(schemaValidationErrors.length).toEqual(0) From 73ae1cd2f57bfd3c3ab87e64a4f88e7f39229f0b Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 29 Sep 2023 14:03:04 +0200 Subject: [PATCH 32/39] Use `cds.compile.to` in enrich test --- test/tests/enrich.test.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/tests/enrich.test.js b/test/tests/enrich.test.js index 9a5da460..fff63857 100644 --- a/test/tests/enrich.test.js +++ b/test/tests/enrich.test.js @@ -1,17 +1,19 @@ describe('graphql - enrich AST with parsed inline literal values', () => { const cds = require('@sap/cds') + // Load @cap-js/graphql plugin to ensure .to.gql and .to.graphql compile targets are registered + require('../../cds-plugin') const { gql } = require('../util') const { parse } = require('graphql') const enrich = require('../../lib/resolvers/parse/ast/enrich') const { models } = require('../resources') - const cds_compile_to_gql = require('../../lib/compile') const { fakeInfoObject } = require('../util') let bookshopSchema beforeAll(async () => { const bookshopModel = models.find(m => m.name === 'bookshop-graphql') - bookshopSchema = cds_compile_to_gql(await cds.load(bookshopModel.files), { as: 'object' }) + const csn = await cds.load(bookshopModel.files) + bookshopSchema = cds.compile(csn).to.gql({ as: 'object' }) }) test('parsing of literal value as top level argument', async () => { From e4974b8d93d5c55cba64c688208c140ee02c67fb Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 29 Sep 2023 14:19:38 +0200 Subject: [PATCH 33/39] Ensure compile target registration only happens once --- lib/api.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/api.js b/lib/api.js index a9baf1fa..3d3b8637 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,5 +1,7 @@ const TARGETS = ['gql', 'graphql'] +let _compileTargetsRegistered = false + function _lazyRegisterCompileTargets() { const value = require('./compile') TARGETS.forEach(target => Object.defineProperty(this, target, { value })) @@ -7,12 +9,19 @@ function _lazyRegisterCompileTargets() { } // Register gql and graphql as cds.compile.to targets -const registerCompileTargets = cds_compile_to => +const registerCompileTargets = cds_compile_to => { + // This function needs to be called from two different locations: + // - `./cds-plugin.js` -> necessary for non-programmatic usage via the `cds compile -2 ...` command + // - `./lib/GraphQLAdapter.js` -> necessary for programmatic usage, when not loading GraphQLAdapter as a cds-plugin + // Ensure registration only happens once + if (_compileTargetsRegistered) return TARGETS.forEach(target => Object.defineProperty(cds_compile_to, target, { get: _lazyRegisterCompileTargets, configurable: true }) ) + _compileTargetsRegistered = true +} module.exports = { registerCompileTargets } From c0ff69941d4875a6e4fdcb481561b3df35376b81 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 11 Oct 2023 11:27:04 +0200 Subject: [PATCH 34/39] Move `cds.compile.to` into register function --- cds-plugin.js | 2 +- lib/GraphQLAdapter.js | 3 +-- lib/api.js | 6 ++++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cds-plugin.js b/cds-plugin.js index b1fb03a7..0d9b4d22 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -1,5 +1,5 @@ const cds = require('@sap/cds') -require('./lib/api').registerCompileTargets(cds.compile.to) +require('./lib/api').registerCompileTargets() const protocols = cds.env.protocols ??= {} if (!protocols.graphql) protocols.graphql = { path: "/graphql", impl: "@cap-js/graphql" diff --git a/lib/GraphQLAdapter.js b/lib/GraphQLAdapter.js index 82ddcae3..7d12e760 100644 --- a/lib/GraphQLAdapter.js +++ b/lib/GraphQLAdapter.js @@ -1,5 +1,4 @@ -const cds = require('@sap/cds') -require('./api').registerCompileTargets(cds.compile.to) +require('./api').registerCompileTargets() const express = require('express') const { generateSchema4 } = require('./schema') const queryLogger = require('./logger') diff --git a/lib/api.js b/lib/api.js index 3d3b8637..b2e2aee1 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,3 +1,5 @@ +const cds = require('@sap/cds') + const TARGETS = ['gql', 'graphql'] let _compileTargetsRegistered = false @@ -9,14 +11,14 @@ function _lazyRegisterCompileTargets() { } // Register gql and graphql as cds.compile.to targets -const registerCompileTargets = cds_compile_to => { +const registerCompileTargets = () => { // This function needs to be called from two different locations: // - `./cds-plugin.js` -> necessary for non-programmatic usage via the `cds compile -2 ...` command // - `./lib/GraphQLAdapter.js` -> necessary for programmatic usage, when not loading GraphQLAdapter as a cds-plugin // Ensure registration only happens once if (_compileTargetsRegistered) return TARGETS.forEach(target => - Object.defineProperty(cds_compile_to, target, { + Object.defineProperty(cds.compile.to, target, { get: _lazyRegisterCompileTargets, configurable: true }) From eeaa4dbbd584bfd50f733d12459fb9065d75da5d Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 11 Oct 2023 11:30:14 +0200 Subject: [PATCH 35/39] Registration in `GraphQLAdapter` is not necessary since the plugin should always be loaded --- lib/GraphQLAdapter.js | 1 - lib/api.js | 8 -------- test/tests/schema.test.js | 4 ++-- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/lib/GraphQLAdapter.js b/lib/GraphQLAdapter.js index 7d12e760..c9ba9f20 100644 --- a/lib/GraphQLAdapter.js +++ b/lib/GraphQLAdapter.js @@ -1,4 +1,3 @@ -require('./api').registerCompileTargets() const express = require('express') const { generateSchema4 } = require('./schema') const queryLogger = require('./logger') diff --git a/lib/api.js b/lib/api.js index b2e2aee1..daaf9ca3 100644 --- a/lib/api.js +++ b/lib/api.js @@ -2,8 +2,6 @@ const cds = require('@sap/cds') const TARGETS = ['gql', 'graphql'] -let _compileTargetsRegistered = false - function _lazyRegisterCompileTargets() { const value = require('./compile') TARGETS.forEach(target => Object.defineProperty(this, target, { value })) @@ -12,18 +10,12 @@ function _lazyRegisterCompileTargets() { // Register gql and graphql as cds.compile.to targets const registerCompileTargets = () => { - // This function needs to be called from two different locations: - // - `./cds-plugin.js` -> necessary for non-programmatic usage via the `cds compile -2 ...` command - // - `./lib/GraphQLAdapter.js` -> necessary for programmatic usage, when not loading GraphQLAdapter as a cds-plugin - // Ensure registration only happens once - if (_compileTargetsRegistered) return TARGETS.forEach(target => Object.defineProperty(cds.compile.to, target, { get: _lazyRegisterCompileTargets, configurable: true }) ) - _compileTargetsRegistered = true } module.exports = { registerCompileTargets } diff --git a/test/tests/schema.test.js b/test/tests/schema.test.js index 625cb1b7..0090bc51 100644 --- a/test/tests/schema.test.js +++ b/test/tests/schema.test.js @@ -2,8 +2,8 @@ const fs = require('fs') const path = require('path') const semver = require('semver') const { version: cdsVersion } = require('@sap/cds') -// Load GraphQLAdapter to ensure .to.gql and .to.graphql compile targets are registered -require('../../lib/GraphQLAdapter') +// Load @cap-js/graphql plugin to ensure .to.gql and .to.graphql compile targets are registered +require('../../cds-plugin') const models = require('../resources').models.filter( m => !m.requires_cds || semver.satisfies(cdsVersion, m.requires_cds) From 0fd2e8dee50030fc489d4de537fb6535aaae4d79 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 11 Oct 2023 11:34:03 +0200 Subject: [PATCH 36/39] Add comment to explain why all compile targets are lazy loaded --- lib/api.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/api.js b/lib/api.js index daaf9ca3..f040a51d 100644 --- a/lib/api.js +++ b/lib/api.js @@ -4,6 +4,8 @@ const TARGETS = ['gql', 'graphql'] function _lazyRegisterCompileTargets() { const value = require('./compile') + // Lazy load all compile targets if any of them is accessed + // e.g. .to.gql was called -> load .to.gql and .to.graphql TARGETS.forEach(target => Object.defineProperty(this, target, { value })) return value } From b847583d7d13c041ecea79dab50967913a1db0ab Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 11 Oct 2023 11:37:27 +0200 Subject: [PATCH 37/39] Comment wording --- lib/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api.js b/lib/api.js index f040a51d..58e82a3b 100644 --- a/lib/api.js +++ b/lib/api.js @@ -5,7 +5,7 @@ const TARGETS = ['gql', 'graphql'] function _lazyRegisterCompileTargets() { const value = require('./compile') // Lazy load all compile targets if any of them is accessed - // e.g. .to.gql was called -> load .to.gql and .to.graphql + // For example .to.gql was called -> load .to.gql and .to.graphql TARGETS.forEach(target => Object.defineProperty(this, target, { value })) return value } From f3de5ec373d9042e6c6e40ebc4fd80ae55f21b4f Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 11 Oct 2023 11:38:50 +0200 Subject: [PATCH 38/39] Swap require const order --- lib/compile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compile.js b/lib/compile.js index 640e49dd..2e34e54b 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -1,6 +1,6 @@ const cds = require('@sap/cds') const { generateSchema4 } = require('./schema') -const { printSchema, lexicographicSortSchema } = require('graphql') +const { lexicographicSortSchema, printSchema } = require('graphql') function cds_compile_to_gql(csn, options = {}) { const m = cds.linked(csn) From 7cc1d7c5fd3283349d12a8eb7b317e0184a4f805 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 11 Oct 2023 11:45:40 +0200 Subject: [PATCH 39/39] Clarify revisit comment --- lib/schema/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/schema/index.js b/lib/schema/index.js index ba9b2b0a..322b3403 100644 --- a/lib/schema/index.js +++ b/lib/schema/index.js @@ -4,7 +4,7 @@ const { GraphQLSchema } = require('graphql') const { createRootResolvers, registerAliasFieldResolvers } = require('../resolvers') const { printSchema } = require('graphql') -// REVISIT: remove with cds^8 +// REVISIT: remove class with cds^8 class SchemaGenerator { generate(services) { this._schema = generateSchema4(services)