Skip to content

Commit b659df8

Browse files
authored
feat: use serviceinfo to determine services to compile (#178)
Use `serviceinfo` to determine services to compile instead of directly inspecting service definition for `@protocol` or `@graphql` annotations, since this logic is duplicated in `@sap/cds`.
1 parent a56c0cb commit b659df8

File tree

6 files changed

+135
-40
lines changed

6 files changed

+135
-40
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Changed
1515

16+
- Bump required `@sap/cds` version to `>=7.8`
1617
- To improve performance, binary payloads are no longer validated to check if they are properly base64 or base64url encoded
1718
- Bump required `node` version to `^16` due to usage of `Buffer.toString('base64url')`
19+
- Use `cds.compile.to.serviceinfo` to determine if a service should be compiled to GraphQL schema
1820

1921
### Fixed
2022

lib/compile.js

+7-20
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,16 @@ const cds = require('@sap/cds')
22
const { generateSchema4 } = require('./schema')
33
const { lexicographicSortSchema, printSchema } = require('graphql')
44

5-
const _isServiceAnnotatedWithGraphQL = service => {
6-
const { definition } = service
7-
8-
if (definition['@graphql']) return true
9-
10-
const protocol = definition['@protocol']
11-
if (protocol) {
12-
// @protocol: 'graphql' or @protocol: ['graphql', 'odata']
13-
const protocols = Array.isArray(protocol) ? protocol : [protocol]
14-
// Normalize objects such as { kind: 'graphql' } to strings
15-
return protocols.map(p => (typeof p === 'object' ? p.kind : p)).some(p => p.match(/graphql/i))
16-
}
17-
18-
return false
19-
}
20-
215
function cds_compile_to_gql(csn, options = {}) {
22-
const m = cds.linked(csn)
6+
const model = cds.linked(csn)
7+
const serviceinfo = cds.compile.to.serviceinfo(csn, options)
238
const services = Object.fromEntries(
24-
m.services
25-
.map(s => [s.name, new cds.ApplicationService(s.name, m)])
9+
model.services
10+
.map(s => [s.name, new cds.ApplicationService(s.name, model)])
2611
// Only compile services with GraphQL endpoints
27-
.filter(([_, service]) => _isServiceAnnotatedWithGraphQL(service))
12+
.filter(([_, service]) =>
13+
serviceinfo.find(s => s.name === service.name)?.endpoints.some(e => e.kind === 'graphql')
14+
)
2815
)
2916

3017
let schema = generateSchema4(services)

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"graphql-http": "^1.18.0"
3737
},
3838
"peerDependencies": {
39-
"@sap/cds": ">=7.3"
39+
"@sap/cds": ">=7.8"
4040
},
4141
"devDependencies": {
4242
"@cap-js/graphql": "file:.",
+21-19
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,49 @@
1-
service NotAnnotated {
1+
context protocols {
22
entity A {
33
key id : UUID;
44
}
55
}
66

7+
service NotAnnotated {
8+
entity A as projection on protocols.A;
9+
}
10+
711
@protocol: 'none'
812
service AnnotatedWithAtProtocolNone {
9-
entity A {
10-
key id : UUID;
11-
}
13+
entity A as projection on protocols.A;
1214
}
1315

1416
@protocol: 'odata'
1517
service AnnotatedWithNonGraphQL {
16-
entity A {
17-
key id : UUID;
18-
}
18+
entity A as projection on protocols.A;
1919
}
2020

2121
@graphql
2222
service AnnotatedWithAtGraphQL {
23-
entity A {
24-
key id : UUID;
25-
}
23+
entity A as projection on protocols.A;
2624
}
2725

2826
@protocol: 'graphql'
2927
service AnnotatedWithAtProtocolString {
30-
entity A {
31-
key id : UUID;
32-
}
28+
entity A as projection on protocols.A;
3329
}
3430

3531
@protocol: ['graphql']
3632
service AnnotatedWithAtProtocolStringList {
37-
entity A {
38-
key id : UUID;
39-
}
33+
entity A as projection on protocols.A;
4034
}
4135

4236
@protocol: [{kind: 'graphql'}]
4337
service AnnotatedWithAtProtocolObjectList {
44-
entity A {
45-
key id : UUID;
46-
}
38+
entity A as projection on protocols.A;
39+
}
40+
41+
@protocol: { graphql }
42+
service AnnotatedWithAtProtocolObjectWithKey {
43+
entity A as projection on protocols.A;
44+
}
45+
46+
@protocol: { graphql: 'dummy' }
47+
service AnnotatedWithAtProtocolObjectWithKeyAndValue {
48+
entity A as projection on protocols.A;
4749
}

test/schemas/annotations.gql

+72
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,74 @@ type AnnotatedWithAtProtocolObjectList_input {
6666
A: AnnotatedWithAtProtocolObjectList_A_input
6767
}
6868

69+
type AnnotatedWithAtProtocolObjectWithKey {
70+
A(filter: [AnnotatedWithAtProtocolObjectWithKey_A_filter], orderBy: [AnnotatedWithAtProtocolObjectWithKey_A_orderBy], skip: Int, top: Int): AnnotatedWithAtProtocolObjectWithKey_A_connection
71+
}
72+
73+
type AnnotatedWithAtProtocolObjectWithKeyAndValue {
74+
A(filter: [AnnotatedWithAtProtocolObjectWithKeyAndValue_A_filter], orderBy: [AnnotatedWithAtProtocolObjectWithKeyAndValue_A_orderBy], skip: Int, top: Int): AnnotatedWithAtProtocolObjectWithKeyAndValue_A_connection
75+
}
76+
77+
type AnnotatedWithAtProtocolObjectWithKeyAndValue_A {
78+
id: ID
79+
}
80+
81+
input AnnotatedWithAtProtocolObjectWithKeyAndValue_A_C {
82+
id: ID
83+
}
84+
85+
type AnnotatedWithAtProtocolObjectWithKeyAndValue_A_connection {
86+
nodes: [AnnotatedWithAtProtocolObjectWithKeyAndValue_A]
87+
totalCount: Int
88+
}
89+
90+
input AnnotatedWithAtProtocolObjectWithKeyAndValue_A_filter {
91+
id: [ID_filter]
92+
}
93+
94+
type AnnotatedWithAtProtocolObjectWithKeyAndValue_A_input {
95+
create(input: [AnnotatedWithAtProtocolObjectWithKeyAndValue_A_C]!): [AnnotatedWithAtProtocolObjectWithKeyAndValue_A]
96+
delete(filter: [AnnotatedWithAtProtocolObjectWithKeyAndValue_A_filter]!): Int
97+
}
98+
99+
input AnnotatedWithAtProtocolObjectWithKeyAndValue_A_orderBy {
100+
id: SortDirection
101+
}
102+
103+
type AnnotatedWithAtProtocolObjectWithKeyAndValue_input {
104+
A: AnnotatedWithAtProtocolObjectWithKeyAndValue_A_input
105+
}
106+
107+
type AnnotatedWithAtProtocolObjectWithKey_A {
108+
id: ID
109+
}
110+
111+
input AnnotatedWithAtProtocolObjectWithKey_A_C {
112+
id: ID
113+
}
114+
115+
type AnnotatedWithAtProtocolObjectWithKey_A_connection {
116+
nodes: [AnnotatedWithAtProtocolObjectWithKey_A]
117+
totalCount: Int
118+
}
119+
120+
input AnnotatedWithAtProtocolObjectWithKey_A_filter {
121+
id: [ID_filter]
122+
}
123+
124+
type AnnotatedWithAtProtocolObjectWithKey_A_input {
125+
create(input: [AnnotatedWithAtProtocolObjectWithKey_A_C]!): [AnnotatedWithAtProtocolObjectWithKey_A]
126+
delete(filter: [AnnotatedWithAtProtocolObjectWithKey_A_filter]!): Int
127+
}
128+
129+
input AnnotatedWithAtProtocolObjectWithKey_A_orderBy {
130+
id: SortDirection
131+
}
132+
133+
type AnnotatedWithAtProtocolObjectWithKey_input {
134+
A: AnnotatedWithAtProtocolObjectWithKey_A_input
135+
}
136+
69137
type AnnotatedWithAtProtocolString {
70138
A(filter: [AnnotatedWithAtProtocolString_A_filter], orderBy: [AnnotatedWithAtProtocolString_A_orderBy], skip: Int, top: Int): AnnotatedWithAtProtocolString_A_connection
71139
}
@@ -147,13 +215,17 @@ input ID_filter {
147215
type Mutation {
148216
AnnotatedWithAtGraphQL: AnnotatedWithAtGraphQL_input
149217
AnnotatedWithAtProtocolObjectList: AnnotatedWithAtProtocolObjectList_input
218+
AnnotatedWithAtProtocolObjectWithKey: AnnotatedWithAtProtocolObjectWithKey_input
219+
AnnotatedWithAtProtocolObjectWithKeyAndValue: AnnotatedWithAtProtocolObjectWithKeyAndValue_input
150220
AnnotatedWithAtProtocolString: AnnotatedWithAtProtocolString_input
151221
AnnotatedWithAtProtocolStringList: AnnotatedWithAtProtocolStringList_input
152222
}
153223

154224
type Query {
155225
AnnotatedWithAtGraphQL: AnnotatedWithAtGraphQL
156226
AnnotatedWithAtProtocolObjectList: AnnotatedWithAtProtocolObjectList
227+
AnnotatedWithAtProtocolObjectWithKey: AnnotatedWithAtProtocolObjectWithKey
228+
AnnotatedWithAtProtocolObjectWithKeyAndValue: AnnotatedWithAtProtocolObjectWithKeyAndValue
157229
AnnotatedWithAtProtocolString: AnnotatedWithAtProtocolString
158230
AnnotatedWithAtProtocolStringList: AnnotatedWithAtProtocolStringList
159231
}

test/tests/annotations.test.js

+32
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,37 @@ describe('graphql - annotations', () => {
123123
const response = await POST(path, { query })
124124
expect(response.data).not.toHaveProperty('errors')
125125
})
126+
127+
test('service annotated with "@protocol: { graphql }" is served at configured path', async () => {
128+
const query = gql`
129+
{
130+
AnnotatedWithAtProtocolObjectWithKey {
131+
A {
132+
nodes {
133+
id
134+
}
135+
}
136+
}
137+
}
138+
`
139+
const response = await POST(path, { query })
140+
expect(response.data).not.toHaveProperty('errors')
141+
})
142+
143+
test('service annotated with "@protocol: { graphql: \'dummy\' }" is served at configured path', async () => {
144+
const query = gql`
145+
{
146+
AnnotatedWithAtProtocolObjectWithKeyAndValue {
147+
A {
148+
nodes {
149+
id
150+
}
151+
}
152+
}
153+
}
154+
`
155+
const response = await POST(path, { query })
156+
expect(response.data).not.toHaveProperty('errors')
157+
})
126158
})
127159
})

0 commit comments

Comments
 (0)