Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: move registration of graphql and gql cds.compile.to targets from @sap/cds #125

Merged
merged 43 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5174085
Move compile to gql/graphql to module from @sap/cds
schwma Aug 23, 2023
6fb4545
Use `generateSchema4` instead of `SchemaGenerator`
schwma Aug 23, 2023
3cf4522
Remove unused `SchemaGenerator` class
schwma Aug 23, 2023
6db047a
Extract `cds.compile.to.(gql|graphql)` registration
schwma Aug 23, 2023
4173793
Prettier format
schwma Aug 23, 2023
cf8ebd5
Reword comment
schwma Aug 23, 2023
939933a
Simplify generate-schemas script
schwma Aug 23, 2023
95cbd70
Fix module paths
schwma Aug 23, 2023
72e7a13
WIP comment out require cli
schwma Aug 23, 2023
c7d1e1f
Add option to compile to GraphQLSchema object
schwma Aug 23, 2023
90ba039
Add compile option to lexicographically sort schema
schwma Aug 23, 2023
6dbcf19
Fix registration of gql and graphql getters
schwma Aug 23, 2023
0a1828b
`Object.defineProperty` -> `Object.defineProperties`
schwma Aug 23, 2023
831e368
Rename `compile` imports to `cds_compile_to_gql`
schwma Aug 25, 2023
e630531
Extract require to lazy function for readability
schwma Aug 25, 2023
47899ff
Remove default options destructuring from args
schwma Aug 25, 2023
0c63d7b
Rename `cli.js` to `api.js`
schwma Aug 25, 2023
1bbd9b2
Re-add `SchemaGenerator` class for backwards compatibility
schwma Aug 25, 2023
c7d11fc
`options.object` -> `options.as: 'obj'` to align with `cds.compile.to…
schwma Aug 25, 2023
77e9a8d
Default options to empty object
schwma Aug 25, 2023
caa3951
Fix `SchemaGenerator.generate()` chaining
schwma Sep 6, 2023
1e3103f
Fix missing import
schwma Sep 6, 2023
69e4867
Ensure plugin is loaded to extend `compile` in bookshop-graphql project
schwma Sep 13, 2023
7358240
Merge branch 'main' into CLI-cds.compile.to
schwma Sep 28, 2023
b9b96dc
Remove `formatSchema` util function
schwma Sep 28, 2023
f6b9f07
Make compile format regex case insensitive
schwma Sep 28, 2023
8d154e8
Extract CSN to variable for better readability
schwma Sep 28, 2023
82067d3
Add changelog entry
schwma Sep 29, 2023
678de57
Improve lazy loading and clarify purpose of api module
schwma Sep 29, 2023
24a4c08
Shorten registration and avoid specifying properties twice
schwma Sep 29, 2023
f90d1aa
Rename lazy function
schwma Sep 29, 2023
2ecb222
Use `.to.gql` and `.to.graphql` in tests
schwma Sep 29, 2023
73ae1cd
Use `cds.compile.to` in enrich test
schwma Sep 29, 2023
e4974b8
Ensure compile target registration only happens once
schwma Sep 29, 2023
7d1baa0
Merge branch 'main' into CLI-cds.compile.to
schwma Sep 29, 2023
e1b8cbc
Merge branch 'main' into CLI-cds.compile.to
schwma Oct 11, 2023
c0ff699
Move `cds.compile.to` into register function
schwma Oct 11, 2023
eeaa4db
Registration in `GraphQLAdapter` is not necessary since the plugin sh…
schwma Oct 11, 2023
0fd2e8d
Add comment to explain why all compile targets are lazy loaded
schwma Oct 11, 2023
0990de8
Merge branch 'main' into CLI-cds.compile.to
schwma Oct 11, 2023
b847583
Comment wording
schwma Oct 11, 2023
f3de5ec
Swap require const order
schwma Oct 11, 2023
7cc1d7c
Clarify revisit comment
schwma Oct 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

### Removed
Expand Down
1 change: 1 addition & 0 deletions cds-plugin.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const cds = require('@sap/cds')
require('./lib/api').registerCompileTargets()
const protocols = cds.env.protocols ??= {}
if (!protocols.graphql) protocols.graphql = {
path: "/graphql", impl: "@cap-js/graphql"
Expand Down
23 changes: 23 additions & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const cds = require('@sap/cds')

const TARGETS = ['gql', 'graphql']

function _lazyRegisterCompileTargets() {
const value = require('./compile')
// Lazy load all compile targets if any of them is accessed
// For example .to.gql was called -> load .to.gql and .to.graphql
TARGETS.forEach(target => Object.defineProperty(this, target, { value }))
return value
}

// Register gql and graphql as cds.compile.to targets
const registerCompileTargets = () => {
TARGETS.forEach(target =>
Object.defineProperty(cds.compile.to, target, {
get: _lazyRegisterCompileTargets,
configurable: true
})
)
}

module.exports = { registerCompileTargets }
17 changes: 17 additions & 0 deletions lib/compile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const cds = require('@sap/cds')
const { generateSchema4 } = require('./schema')
const { lexicographicSortSchema, printSchema } = require('graphql')

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)
if (/^obj|object$/i.test(options.as)) return schema

return printSchema(schema)
}

module.exports = cds_compile_to_gql
19 changes: 11 additions & 8 deletions lib/schema/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
const queryGenerator = require('./query')
const mutationGenerator = require('./mutation')
const { GraphQLSchema, printSchema } = require('graphql')
const { GraphQLSchema } = require('graphql')
const { createRootResolvers, registerAliasFieldResolvers } = require('../resolvers')
const { printSchema } = require('graphql')

// REVISIT: remove class with cds^8
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)
this._schema = generateSchema4(services)
return this
}

Expand All @@ -24,7 +21,13 @@ class SchemaGenerator {
}

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 }
3 changes: 3 additions & 0 deletions test/resources/bookshop-graphql/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"dependencies": {
"@cap-js/graphql": "../../.."
},
"cds": {
"requires": {
"db": {
Expand Down
9 changes: 6 additions & 3 deletions test/scripts/generate-schemas.js
Original file line number Diff line number Diff line change
@@ -1,16 +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, cdsFilesToGQLSchema, formatSchema } = require('../util')
const { SCHEMAS_DIR } = require('../util')
const { models } = require('../resources')
const { printSchema } = require('graphql')

;(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 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)
Expand Down
8 changes: 6 additions & 2 deletions test/tests/enrich.test.js
Original file line number Diff line number Diff line change
@@ -1,15 +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 { cdsFilesToGQLSchema, fakeInfoObject } = require('../util')
const { fakeInfoObject } = require('../util')

let bookshopSchema

beforeAll(async () => {
const bookshopModel = models.find(m => m.name === 'bookshop-graphql')
bookshopSchema = await cdsFilesToGQLSchema(bookshopModel.files)
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 () => {
Expand Down
18 changes: 11 additions & 7 deletions test/tests/schema.test.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
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 @cap-js/graphql plugin to ensure .to.gql and .to.graphql compile targets are registered
require('../../cds-plugin')

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 { SCHEMAS_DIR, cdsFilesToGQLSchema, formatSchema } = require('../util')
const { SCHEMAS_DIR } = 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 csn = await cds.load(model.files)
const generatedSchemaObject = cds.compile(csn).to.graphql({ as: 'obj', sort: true })
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)
})
})
Expand Down
15 changes: 2 additions & 13 deletions test/util/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
const path = require('path')
const { buildSchema, lexicographicSortSchema, printSchema, Kind } = require('graphql')
const { Kind } = require('graphql')

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 => printSchema(lexicographicSortSchema(buildSchema(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.
*
Expand Down Expand Up @@ -44,4 +33,4 @@ const fakeInfoObject = (document, schema, parentTypeName, variables) => {
*/
const gql = String.raw

module.exports = { SCHEMAS_DIR, cdsFilesToGQLSchema, formatSchema, fakeInfoObject, gql }
module.exports = { SCHEMAS_DIR, fakeInfoObject, gql }