@sap/cds-dk 9.0.6 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/bin/add.js +9 -6
- package/bin/debug.js +14 -7
- package/bin/repl.js +9 -2
- package/bin/up.js +16 -6
- package/lib/build/provider/fiori/index.js +2 -4
- package/lib/init/constants.js +11 -10
- package/lib/init/index.js +32 -4
- package/lib/init/merge.js +4 -1
- package/lib/init/plugin.js +0 -33
- package/lib/init/projectReader.js +1 -1
- package/lib/init/template/ams/files/Chart.yaml.hbs +3 -1
- package/lib/init/template/approuter/files/mta.yaml.hbs +2 -1
- package/lib/init/template/cloud-logging/files/java-cdsrc.json.hbs +6 -0
- package/lib/init/template/cloud-logging/files/package.json.hbs +7 -0
- package/lib/init/template/cloud-logging/index.js +9 -10
- package/lib/init/template/containerize/index.js +1 -1
- package/lib/init/template/data/as-json.js +3 -3
- package/lib/init/template/hana/index.js +3 -2
- package/lib/init/template/handler/files/extension/package.json +1 -1
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +3 -1
- package/lib/init/template/html5-repo/index.js +1 -1
- package/lib/init/template/http/index.js +55 -53
- package/lib/init/template/ias/files/Chart.yaml.hbs +3 -1
- package/lib/init/template/ias/files/mta.yaml.hbs +2 -2
- package/lib/init/template/ias/index.js +3 -4
- package/lib/init/template/mta/index.js +5 -5
- package/lib/init/template/multitenancy/index.js +3 -4
- package/lib/init/template/nodejs/files/package.json.hbs +1 -1
- package/lib/init/template/portal/index.js +1 -1
- package/lib/init/template/test/files/package.json +8 -0
- package/lib/init/template/test/files/test/test.xs.hbs +36 -0
- package/lib/init/template/test/index.js +152 -0
- package/lib/init/template/typescript/files/package.json +1 -1
- package/lib/init/template/xsuaa/index.js +2 -2
- package/lib/lint/Linter.js +1 -4
- package/lib/lint/eslintHelp.json +84 -0
- package/lib/mtx/auth_manager.js +0 -2
- package/lib/util/axios.js +77 -31
- package/npm-shrinkwrap.json +14 -14
- package/package.json +1 -1
- package/lib/init/template/cloud-logging/files/java-cdsrc.json +0 -5
- package/lib/lint/generators.js +0 -103
|
@@ -216,7 +216,7 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
216
216
|
async #getHostnameAndAuth(service, csn) {
|
|
217
217
|
if (!cds.cli.options['for-app']) {
|
|
218
218
|
const host = service.isJava ? this.#javaHost : this.#nodeHost
|
|
219
|
-
const users = Object.values(await
|
|
219
|
+
const users = Object.values(await HttpTemplate.mockUsersForService(service.name, service.isJava, csn))
|
|
220
220
|
if (users.length) {
|
|
221
221
|
return [host, users[0].name, users[0].password || '']
|
|
222
222
|
}
|
|
@@ -246,58 +246,6 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
/**
|
|
250
|
-
* Reads mock users from the cds.env or the application.yml,
|
|
251
|
-
* returning only those that match to the declared roles of the given service.
|
|
252
|
-
* @param {ServiceData} service
|
|
253
|
-
* @returns {Promise<Record<string, {password: string, roles: string[]}>>} a map of users
|
|
254
|
-
*/
|
|
255
|
-
async #mockUsersForService(service, csn) {
|
|
256
|
-
const declaredRoles = new Set()
|
|
257
|
-
Object.values(csn.definitions)
|
|
258
|
-
.filter(d => d.name.startsWith(service.name))
|
|
259
|
-
.map(d => d['@requires']).filter(Boolean)
|
|
260
|
-
.forEach(req => {
|
|
261
|
-
declaredRoles.add(...(Array.isArray(req) ? req : [req]))
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
const matchingUsers = {}
|
|
265
|
-
const addUserIfMatching = (roles, name, password) => {
|
|
266
|
-
if (!declaredRoles.size || declaredRoles.has('authenticated-user') || roles?.some(r => declaredRoles.has(r))) {
|
|
267
|
-
matchingUsers[name] = { name, password, roles }
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (service.isJava) {
|
|
272
|
-
const javaDefaultUsers = [
|
|
273
|
-
{ name: 'authenticated', roles: ['authenticated-user'] },
|
|
274
|
-
{ name: 'internal', roles: ['internal-user'] },
|
|
275
|
-
{ name: 'system', roles: ['system-user'] },
|
|
276
|
-
]
|
|
277
|
-
const javaUsers = []
|
|
278
|
-
this.javaConfig ??= await _readSpringBootConfig()
|
|
279
|
-
javaUsers.push(
|
|
280
|
-
...this.javaConfig
|
|
281
|
-
.map(cfg => cfg.cds?.security?.mock?.users) // {name: { password, roles }}
|
|
282
|
-
.filter(Boolean)
|
|
283
|
-
.flatMap(usrList => Object.entries(usrList).map(([name, {password, roles}]) => ({ name, password, roles: roles??[] })))
|
|
284
|
-
, ...javaDefaultUsers // add default users at end for lower priority
|
|
285
|
-
)
|
|
286
|
-
javaUsers.forEach(({name, password='', roles}) => addUserIfMatching(roles, name, password))
|
|
287
|
-
}
|
|
288
|
-
// Node
|
|
289
|
-
else if (cds.env.requires.auth?.users) {
|
|
290
|
-
const nodeUsers = Object.entries(cds.env.requires.auth.users)
|
|
291
|
-
nodeUsers.forEach(([name, {password, roles}]) => addUserIfMatching(roles, name, password))
|
|
292
|
-
}
|
|
293
|
-
// add a dummy user as a hint if no user matches the scopes
|
|
294
|
-
if (declaredRoles.size && !matchingUsers.length) {
|
|
295
|
-
const name = `no_user_found_for_roles_${Array.from(declaredRoles).join('_')}`
|
|
296
|
-
matchingUsers[name] = { name: name, password: '', roles: [] }
|
|
297
|
-
}
|
|
298
|
-
return matchingUsers
|
|
299
|
-
}
|
|
300
|
-
|
|
301
249
|
#write(requests, serviceName) {
|
|
302
250
|
const http = requests.join('\n')
|
|
303
251
|
if (cds.cli.options.dry) {
|
|
@@ -332,3 +280,57 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
332
280
|
return `${outputDir}/${serviceName}.http`
|
|
333
281
|
}
|
|
334
282
|
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Reads mock users from the cds.env or the application.yml,
|
|
286
|
+
* returning only those that match to the declared roles of the given service.
|
|
287
|
+
* @param {String} serviceName - the name of the service to check
|
|
288
|
+
* @param {Boolean} isJava - whether the service is a Java service
|
|
289
|
+
* @returns {Promise<Record<string, {password: string, roles: string[]}>>} a map of users
|
|
290
|
+
*/
|
|
291
|
+
module.exports.mockUsersForService = async function(serviceName, isJava, csn) {
|
|
292
|
+
const declaredRoles = new Set()
|
|
293
|
+
Object.values(csn.definitions)
|
|
294
|
+
.filter(d => d.name.startsWith(serviceName))
|
|
295
|
+
.map(d => d['@requires']).filter(Boolean)
|
|
296
|
+
.forEach(req => {
|
|
297
|
+
declaredRoles.add(...(Array.isArray(req) ? req : [req]))
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
const matchingUsers = {}
|
|
301
|
+
const addUserIfMatching = (roles, name, password) => {
|
|
302
|
+
if (!declaredRoles.size || declaredRoles.has('authenticated-user') || roles?.some(r => declaredRoles.has(r))) {
|
|
303
|
+
matchingUsers[name] = { name, password, roles }
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (isJava) {
|
|
308
|
+
const javaDefaultUsers = [
|
|
309
|
+
{ name: 'authenticated', roles: ['authenticated-user'] },
|
|
310
|
+
{ name: 'internal', roles: ['internal-user'] },
|
|
311
|
+
{ name: 'system', roles: ['system-user'] },
|
|
312
|
+
]
|
|
313
|
+
const javaUsers = []
|
|
314
|
+
this.javaConfig ??= await _readSpringBootConfig()
|
|
315
|
+
javaUsers.push(
|
|
316
|
+
...this.javaConfig
|
|
317
|
+
.map(cfg => cfg.cds?.security?.mock?.users) // {name: { password, roles }}
|
|
318
|
+
.filter(Boolean)
|
|
319
|
+
.flatMap(usrList => Object.entries(usrList).map(([name, {password, roles}]) => ({ name, password, roles: roles??[] })))
|
|
320
|
+
, ...javaDefaultUsers // add default users at end for lower priority
|
|
321
|
+
)
|
|
322
|
+
javaUsers.forEach(({name, password='', roles}) => addUserIfMatching(roles, name, password))
|
|
323
|
+
}
|
|
324
|
+
// Node
|
|
325
|
+
else if (cds.env.requires.auth?.users) {
|
|
326
|
+
const nodeUsers = Object.entries(cds.env.requires.auth.users)
|
|
327
|
+
nodeUsers.forEach(([name, {password, roles}]) => addUserIfMatching(roles, name, password))
|
|
328
|
+
}
|
|
329
|
+
// add a dummy user as a hint if no user matches the scopes
|
|
330
|
+
if (declaredRoles.size && !Object.keys(matchingUsers).length) {
|
|
331
|
+
const name = `no_user_found_for_roles_${Array.from(declaredRoles).join('_')}`
|
|
332
|
+
matchingUsers[name] = { name: name, password: '', roles: [] }
|
|
333
|
+
}
|
|
334
|
+
return matchingUsers
|
|
335
|
+
}
|
|
336
|
+
|
|
@@ -60,9 +60,9 @@ resources:
|
|
|
60
60
|
{{#hasApprouter}}
|
|
61
61
|
oauth2-configuration:
|
|
62
62
|
redirect-uris:
|
|
63
|
-
- ~{app-api/app-protocol}
|
|
63
|
+
- ~{app-api/app-protocol}://*.~{app-api/app-uri}/login/callback
|
|
64
64
|
post-logout-redirect-uris:
|
|
65
|
-
- ~{app-api/app-protocol}
|
|
65
|
+
- ~{app-api/app-protocol}://*.~{app-api/app-uri}/*/logout.html
|
|
66
66
|
{{/hasApprouter}}
|
|
67
67
|
{{#hasAms}}
|
|
68
68
|
authorization:
|
|
@@ -2,8 +2,7 @@ const { readProject } = require('../../projectReader')
|
|
|
2
2
|
const { merge, sort } = require('../../merge')
|
|
3
3
|
const { srv4, approuter, mtxSidecar4 } = require('../../registries/mta')
|
|
4
4
|
const cds = require('../../../cds')
|
|
5
|
-
const {
|
|
6
|
-
// const mvn = require('../../mvn')
|
|
5
|
+
const { exists, read } = cds.utils
|
|
7
6
|
|
|
8
7
|
module.exports = class IasTemplate extends require('../../plugin') {
|
|
9
8
|
|
|
@@ -30,7 +29,7 @@ module.exports = class IasTemplate extends require('../../plugin') {
|
|
|
30
29
|
const { isJava, hasMta, hasCfManifest, hasHelm, hasHelmUnifiedRuntime, hasApprouter, hasMultitenancy, srvPath } = project
|
|
31
30
|
|
|
32
31
|
if (hasMultitenancy) {
|
|
33
|
-
const conf = (await read('mtx/sidecar/package.json')
|
|
32
|
+
const conf = exists('mtx/sidecar/package.json') ? await read('mtx/sidecar/package.json') : {}
|
|
34
33
|
conf.cds ??= {}
|
|
35
34
|
const profile = conf.cds.profile
|
|
36
35
|
if (profile) delete conf.cds.profile
|
|
@@ -38,7 +37,7 @@ module.exports = class IasTemplate extends require('../../plugin') {
|
|
|
38
37
|
if (!conf.cds.profiles.includes('subscription-manager')) {
|
|
39
38
|
conf.cds.profiles.push('subscription-manager')
|
|
40
39
|
}
|
|
41
|
-
await
|
|
40
|
+
await merge(conf).into('mtx/sidecar/package.json')
|
|
42
41
|
await merge(__dirname, 'files/sidecar.package.json.hbs').into('mtx/sidecar/package.json')
|
|
43
42
|
}
|
|
44
43
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
const { join } = require('path')
|
|
2
1
|
const cds = require('../../../cds')
|
|
3
2
|
const { exists } = cds.utils
|
|
4
3
|
const { bold } = require('../../../util/term')
|
|
5
|
-
const { copyRenderedYAML } = require('../../../util/fs')
|
|
6
4
|
const { readProject } = require('../../projectReader')
|
|
7
5
|
const { merge, sort } = require('../../merge')
|
|
8
6
|
const { mvn } = require('../../add')
|
|
@@ -19,14 +17,16 @@ module.exports = class MtaTemplate extends require('../../plugin') {
|
|
|
19
17
|
|
|
20
18
|
async run() {
|
|
21
19
|
const project = readProject()
|
|
22
|
-
if (!exists('mta.yaml')) {
|
|
23
|
-
await copyRenderedYAML(join(__dirname, 'files', 'mta.yaml.hbs'), 'mta.yaml', project)
|
|
24
|
-
}
|
|
25
20
|
await merge(__dirname, 'files/package.json').into('package.json')
|
|
26
21
|
await sort('package.json', 'devDependencies')
|
|
27
22
|
if (project.isJava) await mvn.add('cf')
|
|
28
23
|
}
|
|
29
24
|
|
|
25
|
+
async combine() {
|
|
26
|
+
const project = readProject()
|
|
27
|
+
await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', { with: project })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
30
|
async finalize() {
|
|
31
31
|
if (!exists('pom.xml')) console.log(`\nrun ${bold('npm update --package-lock-only')} to freeze dependencies`)
|
|
32
32
|
}
|
|
@@ -84,10 +84,9 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
|
|
|
84
84
|
|
|
85
85
|
if (hasHelm || hasHelmUnifiedRuntime) {
|
|
86
86
|
await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project })
|
|
87
|
-
const domain = hasApprouter ? 'svc.cluster.local:8080' : '{{ .Values.global.domain }}'
|
|
88
87
|
const applicationWorkload = hasApprouter ? 'approuter' : 'srv'
|
|
89
|
-
const
|
|
90
|
-
const prefix = `https://{{ .Release.Name }}-${
|
|
88
|
+
const mtServer = isNodejs ? 'sidecar' : 'srv'
|
|
89
|
+
const prefix = `https://{{ .Release.Name }}-${mtServer}-{{ .Release.Namespace }}.{{ .Values.global.domain }}`
|
|
91
90
|
const getDependencies = `${prefix}${isNodejs ? '/-/cds/saas-provisioning' : '/mt/v1.0/subscriptions'}/dependencies`
|
|
92
91
|
const onSubscription = `${prefix}${isNodejs ? '/-/cds/saas-provisioning/tenant/{tenantId}' : '/mt/v1.0/subscriptions/tenants/{tenantId}'}`
|
|
93
92
|
await merge({
|
|
@@ -105,7 +104,7 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
|
|
|
105
104
|
}}},
|
|
106
105
|
...(isJava) && { srv: { env: {
|
|
107
106
|
CDS_MULTITENANCY_APPUI_URL: `https://{{ .Release.Name }}-${applicationWorkload}-{{ .Release.Namespace }}.{{ .Values.global.domain }}`,
|
|
108
|
-
CDS_MULTITENANCY_SIDECAR_URL: `
|
|
107
|
+
CDS_MULTITENANCY_SIDECAR_URL: `https://{{ .Release.Name }}-sidecar-{{ .Release.Namespace }}.{{ .Values.global.domain }}`
|
|
109
108
|
}}}
|
|
110
109
|
}).into('chart/values.yaml',{ forceOverwrite: true })
|
|
111
110
|
}
|
|
@@ -72,7 +72,7 @@ module.exports = class PortalTemplate extends require('../../plugin') {
|
|
|
72
72
|
if (hasMultitenancy) {
|
|
73
73
|
relationships.push({
|
|
74
74
|
insert: [portal, 'name'],
|
|
75
|
-
into: [
|
|
75
|
+
into: [isJava ? srv : mtxSidecar4('gen/mtx/sidecar'), 'requires', 'name']
|
|
76
76
|
})
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{{! ESM }}
|
|
2
|
+
{{#hasEsmStyle}}
|
|
3
|
+
import cds from '@sap/cds'
|
|
4
|
+
|
|
5
|
+
const { GET, POST, expect, axios } = cds.test (import.meta.dirname+'/..')
|
|
6
|
+
{{/hasEsmStyle}}
|
|
7
|
+
{{^hasEsmStyle}}
|
|
8
|
+
const cds = require('@sap/cds')
|
|
9
|
+
|
|
10
|
+
const { GET, POST, expect, axios } = cds.test (__dirname+'/..')
|
|
11
|
+
{{/hasEsmStyle}}
|
|
12
|
+
{{#user}}
|
|
13
|
+
axios.defaults.auth = { username: '{{user}}', password: '{{password}}' }
|
|
14
|
+
{{/user}}
|
|
15
|
+
|
|
16
|
+
describe('OData APIs', () => {
|
|
17
|
+
|
|
18
|
+
{{#entities}}{{=<% %>=}}
|
|
19
|
+
it('serves <%name%>', async () => {
|
|
20
|
+
const { data } = await GET `<%&urlPath%> ${{ params: { $select: '<%&selectString%>' } }}`
|
|
21
|
+
expect(data.value).to.containSubset([
|
|
22
|
+
<%&elementsString%>,
|
|
23
|
+
])
|
|
24
|
+
})
|
|
25
|
+
<%={{ }}=%>{{/entities}}
|
|
26
|
+
|
|
27
|
+
{{#actions}}{{=<% %>=}}
|
|
28
|
+
it('executes <%name%>', async () => {
|
|
29
|
+
const { data } = await POST `<%&urlPath%> ${
|
|
30
|
+
<%¶msString%>
|
|
31
|
+
}`
|
|
32
|
+
// TODO finish this test
|
|
33
|
+
// expect(data.value).to...
|
|
34
|
+
})
|
|
35
|
+
<%={{ }}=%>{{/actions}}
|
|
36
|
+
})
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
const cds = require('../../../cds')
|
|
2
|
+
const { fs: { promises: { rename } }, path: { basename, dirname, join }, exists } = cds.utils
|
|
3
|
+
const { readProject } = require('../../projectReader')
|
|
4
|
+
const { merge } = require('../../merge')
|
|
5
|
+
const { renderAndCopy } = require('../../template')
|
|
6
|
+
const genDataAsJson = require('../data/as-json')
|
|
7
|
+
const { mockUsersForService } = require('../http')
|
|
8
|
+
const { dim } = require('../../../util/term')
|
|
9
|
+
const { filterStringAsRegex } = require('../../add')
|
|
10
|
+
|
|
11
|
+
module.exports = class Test extends require('../../plugin') {
|
|
12
|
+
|
|
13
|
+
static help() {
|
|
14
|
+
return 'tests for services'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
options() {
|
|
18
|
+
return {
|
|
19
|
+
'filter': {
|
|
20
|
+
type: 'string',
|
|
21
|
+
short: 'f',
|
|
22
|
+
help: `Filter for services or entities or actions matching the given pattern. If it contains meta
|
|
23
|
+
characters like '^' or '*', it is treated as a regular expression,
|
|
24
|
+
otherwise as an include pattern, i.e /.*pattern.*/i`
|
|
25
|
+
},
|
|
26
|
+
'out': {
|
|
27
|
+
type: 'string',
|
|
28
|
+
short: 'o',
|
|
29
|
+
help: `Custom output directory. For Node.js, the default is 'test'.`
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async canRun() {
|
|
36
|
+
const { isJava } = readProject()
|
|
37
|
+
if (isJava) {
|
|
38
|
+
throw `Test generation is not supported in Java projects yet.`
|
|
39
|
+
}
|
|
40
|
+
return true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async run() {
|
|
44
|
+
const proj = readProject()
|
|
45
|
+
await createNodeTest(proj)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {ReturnType<readProject} proj
|
|
51
|
+
*/
|
|
52
|
+
async function createNodeTest(proj) {
|
|
53
|
+
const { filter, force, out='test' } = cds.cli.options
|
|
54
|
+
const csn = await loadModel()
|
|
55
|
+
if (!csn) return console.log(dim(`> skipping, no model found`))
|
|
56
|
+
|
|
57
|
+
const nameFilter = filterStringAsRegex(filter)
|
|
58
|
+
const serviceInfo = cds.compile.to.serviceinfo(csn)
|
|
59
|
+
|
|
60
|
+
for (const service of csn.services) {
|
|
61
|
+
if (!service.$location.file) continue
|
|
62
|
+
const sInfo = serviceInfo.find(s => s.name === service.name)
|
|
63
|
+
|
|
64
|
+
proj.serviceName = service.name
|
|
65
|
+
proj.serviceClass = service.name.split('.').pop()
|
|
66
|
+
proj.servicePath = proj.serviceName ? (proj.serviceName.replace(/\./g, '/')) : ''
|
|
67
|
+
|
|
68
|
+
const entities = Object.entries(service.entities)
|
|
69
|
+
.filter(([name]) => name.match(nameFilter))
|
|
70
|
+
.filter(([name]) => !name.match(/[._]texts$/))
|
|
71
|
+
.filter(([, e]) => !e['@cds.autoexposed']) // only consider non-autoexposed entities to reduce clutter
|
|
72
|
+
.map(([, e]) => e)
|
|
73
|
+
|
|
74
|
+
let entity // use one entity only (don't want to make users test all entities)
|
|
75
|
+
if (entities.length) entity = entities[0]
|
|
76
|
+
|
|
77
|
+
const sampleData = {}
|
|
78
|
+
|
|
79
|
+
if (entity) {
|
|
80
|
+
// see if we have csv data for the entity
|
|
81
|
+
const refData = await cds.deploy.resources(csn)
|
|
82
|
+
const data = genDataAsJson.randomFromReferenceData(entity, csn, refData)
|
|
83
|
+
sampleData[entity.name] = data ?? []
|
|
84
|
+
}
|
|
85
|
+
// mark action data to be generated
|
|
86
|
+
for (const action of Object.keys(service.actions).filter(name => name.match(nameFilter))) {
|
|
87
|
+
sampleData[`${service.name}.${action}`] = []
|
|
88
|
+
}
|
|
89
|
+
await genDataAsJson(sampleData, csn, 1, {referenceData: {}})
|
|
90
|
+
|
|
91
|
+
if (entity) {
|
|
92
|
+
const elements = Object.values(entity.elements ?? {}).sort(genDataAsJson.elementsSorter(csn))
|
|
93
|
+
const keys = elements.filter(el => el.key).map(el => el.name)
|
|
94
|
+
const selectElements = [
|
|
95
|
+
keys[0],
|
|
96
|
+
// select a non-key, non-association element from the entity itself
|
|
97
|
+
elements.filter(el => !keys.includes(el.name)).find(el => !(el instanceof cds.Association))?.name
|
|
98
|
+
].filter(Boolean)
|
|
99
|
+
|
|
100
|
+
proj.entities = [{
|
|
101
|
+
name: entity.name,
|
|
102
|
+
urlPath: `/${sInfo.endpoints[0].path}${entity.name}`,
|
|
103
|
+
selectString: selectElements.join(','),
|
|
104
|
+
elementsString: JSON.stringify(
|
|
105
|
+
Object.entries(sampleData[entity.name][0])
|
|
106
|
+
.filter(([name]) => selectElements.includes(name))
|
|
107
|
+
.reduce((acc, [name, value]) => { acc[name] = value; return acc }, {})
|
|
108
|
+
)
|
|
109
|
+
}]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
proj.actions = Object.entries(service.actions)
|
|
113
|
+
.filter(([name]) => name.match(nameFilter))
|
|
114
|
+
.map(([name]) => {
|
|
115
|
+
return {
|
|
116
|
+
name,
|
|
117
|
+
paramsString: JSON.stringify(sampleData[`${service.name}.${name}`][0]),
|
|
118
|
+
urlPath: `/${sInfo.endpoints[0].path}${name}`,
|
|
119
|
+
}})
|
|
120
|
+
|
|
121
|
+
const users = await mockUsersForService(service.name, false, csn)
|
|
122
|
+
if (Object.keys(users).length) {
|
|
123
|
+
proj.user = Object.keys(users)[0]
|
|
124
|
+
proj.password = users[proj.user].password
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const destFileType = proj.isTypescript ? '.test.ts' : '.test.js'
|
|
128
|
+
const outPath = join(out, service.name + destFileType)
|
|
129
|
+
const destFile = join(cds.root, outPath)
|
|
130
|
+
if (!force && exists(destFile)) {
|
|
131
|
+
console.log(dim(`> skipping ${outPath}`))
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
console.log(dim(`> writing ${outPath}`))
|
|
135
|
+
const destPath = dirname(destFile)
|
|
136
|
+
await renderAndCopy(join(__dirname, 'files/test'), destPath, proj)
|
|
137
|
+
await rename(join(destPath, 'test.xs'), destFile)
|
|
138
|
+
await merge(join(__dirname, 'files/package.json')).into('package.json')
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @returns { Promise<import('@cap-js/cds-types').linked.LinkedCSN | null> }
|
|
144
|
+
*/
|
|
145
|
+
async function loadModel() {
|
|
146
|
+
try {
|
|
147
|
+
return cds.linked(cds.minify(await cds.load([cds.env.roots])))
|
|
148
|
+
} catch (err) {
|
|
149
|
+
if (err.code === 'MODEL_NOT_FOUND') return null
|
|
150
|
+
throw new Error(`Error compiling CDS files. Run 'npm install' and try again.`, {cause:err})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const cds = require('../../../cds')
|
|
2
|
-
const { read } = cds.utils
|
|
2
|
+
const { exists, read } = cds.utils
|
|
3
3
|
const { readProject } = require('../../projectReader')
|
|
4
4
|
const { merge } = require('../../merge')
|
|
5
5
|
const { srv4, xsuaa: xsuaaDefault, xsuaa4, mtxSidecar4 } = require('../../registries/mta')
|
|
@@ -26,7 +26,7 @@ module.exports = class XsuaaTemplate extends require('../../plugin') {
|
|
|
26
26
|
async run() {
|
|
27
27
|
const project = readProject()
|
|
28
28
|
const { configFile } = project
|
|
29
|
-
project.shortcut = typeof (await read(configFile)).cds?.requires?.auth !== 'object'
|
|
29
|
+
project.shortcut = !exists(configFile) || typeof (await read(configFile)).cds?.requires?.auth !== 'object'
|
|
30
30
|
project.profile ??= 'production'
|
|
31
31
|
await merge(__dirname, 'files/package.json.hbs').into(configFile, { project, forceOverwrite: true })
|
|
32
32
|
}
|
package/lib/lint/Linter.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const term = require('../util/term')
|
|
2
2
|
const io = require('./io')
|
|
3
|
-
const generators = require('./generators')
|
|
4
3
|
const cds = require('../../lib/cds')
|
|
5
4
|
const { path, isfile, readdir } = cds.utils
|
|
6
5
|
const { exit } = require('node:process')
|
|
@@ -158,9 +157,7 @@ class Linter {
|
|
|
158
157
|
* }} object containing help, options, flags and shortcuts
|
|
159
158
|
*/
|
|
160
159
|
init() {
|
|
161
|
-
const
|
|
162
|
-
const eslintFlags = generators.genEslintFlags(eslintHelp)
|
|
163
|
-
const [help, options, shortcuts, flags] = generators.genEslintShortcutsAndOpts(eslintHelp, eslintFlags)
|
|
160
|
+
const { help, options, shortcuts, flags } = require('./eslintHelp.json')
|
|
164
161
|
this.help = help
|
|
165
162
|
this.flags = flags
|
|
166
163
|
return { help, flags, options, shortcuts }
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "9.30.1",
|
|
3
|
+
"help": "\n# SYNOPSIS\n\n *cds lint* [options] file.js [file.js] [dir]\n\n Runs environment checks and/or checks the specified models\n based on the ESLint framework\n\n# OPTIONS\n \n Basic configuration:\n --no-config-lookup Disable look up for eslint.config.js\n -c, --config path::String Use this configuration instead of eslint.config.js, eslint.config.mjs, or eslint.config.cjs\n --inspect-config Open the config inspector with the current configuration\n --ext [String] Specify additional file extensions to lint\n --global [String] Define global variables\n --parser String Specify the parser to be used\n --parser-options Object Specify parser options\n \n Specify Rules and Plugins:\n --plugin [String] Specify plugins\n --rule Object Specify rules\n \n Fix Problems:\n --fix Automatically fix problems\n --fix-dry-run Automatically fix problems without saving the changes to the file system\n --fix-type Array Specify the types of fixes to apply (directive, problem, suggestion, layout)\n \n Ignore Files:\n --no-ignore Disable use of ignore files and patterns\n --ignore-pattern [String] Patterns of files to ignore\n \n Use stdin:\n --stdin Lint code provided on <STDIN> - default: false\n --stdin-filename String Specify filename to process STDIN as\n \n Handle Warnings:\n --quiet Report errors only - default: false\n --max-warnings Int Number of warnings to trigger nonzero exit code - default: -1\n \n Output:\n -o, --output-file path::String Specify file to write report to\n -f, --format String Use a specific output format - default: stylish\n --color, --no-color Force enabling/disabling of color\n \n Inline configuration comments:\n --no-inline-config Prevent comments from changing config or rules\n --report-unused-disable-directives Adds reported errors for unused eslint-disable and eslint-enable directives\n --report-unused-disable-directives-severity String Chooses severity level for reporting unused eslint-disable and eslint-enable directives - either: off, warn, error, 0, 1, or 2\n --report-unused-inline-configs String Adds reported errors for unused eslint inline config comments - either: off, warn, error, 0, 1, or 2\n \n Caching:\n --cache Only check changed files - default: false\n --cache-file path::String Path to the cache file. Deprecated: use --cache-location - default: .eslintcache\n --cache-location path::String Path to the cache file or directory\n --cache-strategy String Strategy to use for detecting changed files in the cache - either: metadata or content - default: metadata\n \n Suppressing Violations:\n --suppress-all Suppress all violations - default: false\n --suppress-rule [String] Suppress specific rules\n --suppressions-location path::String Specify the location of the suppressions file\n --prune-suppressions Prune unused suppressions - default: false\n --pass-on-unpruned-suppressions Ignore unused suppressions - default: false\n \n Miscellaneous:\n --init Run config initialization wizard - default: false\n --env-info Output execution environment information - default: false\n --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched\n --exit-on-fatal-error Exit with exit code 2 in case of fatal error - default: false\n --no-warn-ignored Suppress warnings when the file list includes ignored files\n --pass-on-no-patterns Exit with exit code 0 in case no file patterns are passed\n --debug Output debugging information\n -h, --help Show help\n -v, --version Output the version number\n --print-config path::String Print the configuration for the given file\n --stats Add statistics to the lint report - default: false\n --flag [String] Enable a feature flag\n --mcp Start the ESLint MCP server\n \n",
|
|
4
|
+
"options": [
|
|
5
|
+
"--config",
|
|
6
|
+
"--output-file",
|
|
7
|
+
"--format",
|
|
8
|
+
"--ext",
|
|
9
|
+
"--global",
|
|
10
|
+
"--parser",
|
|
11
|
+
"--parser-options",
|
|
12
|
+
"--plugin",
|
|
13
|
+
"--rule",
|
|
14
|
+
"--fix-type",
|
|
15
|
+
"--ignore-pattern",
|
|
16
|
+
"--stdin-filename",
|
|
17
|
+
"--max-warnings",
|
|
18
|
+
"--report-unused-disable-directives-severity",
|
|
19
|
+
"--report-unused-inline-configs",
|
|
20
|
+
"--cache-file",
|
|
21
|
+
"--cache-location",
|
|
22
|
+
"--cache-location",
|
|
23
|
+
"--cache-strategy",
|
|
24
|
+
"--suppress-rule",
|
|
25
|
+
"--suppressions-location",
|
|
26
|
+
"--print-config",
|
|
27
|
+
"--flag"
|
|
28
|
+
],
|
|
29
|
+
"shortcuts": [
|
|
30
|
+
"-c",
|
|
31
|
+
"-o",
|
|
32
|
+
"-f",
|
|
33
|
+
null,
|
|
34
|
+
null,
|
|
35
|
+
null,
|
|
36
|
+
null,
|
|
37
|
+
null,
|
|
38
|
+
null,
|
|
39
|
+
null,
|
|
40
|
+
null,
|
|
41
|
+
null,
|
|
42
|
+
null,
|
|
43
|
+
null,
|
|
44
|
+
null,
|
|
45
|
+
null,
|
|
46
|
+
null,
|
|
47
|
+
null,
|
|
48
|
+
null,
|
|
49
|
+
null,
|
|
50
|
+
null,
|
|
51
|
+
null,
|
|
52
|
+
null,
|
|
53
|
+
"-v",
|
|
54
|
+
"-h"
|
|
55
|
+
],
|
|
56
|
+
"flags": [
|
|
57
|
+
"--version",
|
|
58
|
+
"--help",
|
|
59
|
+
"--no-config-lookup",
|
|
60
|
+
"--inspect-config",
|
|
61
|
+
"--fix",
|
|
62
|
+
"--fix-dry-run",
|
|
63
|
+
"--no-ignore",
|
|
64
|
+
"--stdin",
|
|
65
|
+
"--quiet",
|
|
66
|
+
"--color",
|
|
67
|
+
"--no-color",
|
|
68
|
+
"--no-inline-config",
|
|
69
|
+
"--report-unused-disable-directives",
|
|
70
|
+
"--cache",
|
|
71
|
+
"--suppress-all",
|
|
72
|
+
"--prune-suppressions",
|
|
73
|
+
"--pass-on-unpruned-suppressions",
|
|
74
|
+
"--init",
|
|
75
|
+
"--env-info",
|
|
76
|
+
"--no-error-on-unmatched-pattern",
|
|
77
|
+
"--exit-on-fatal-error",
|
|
78
|
+
"--no-warn-ignored",
|
|
79
|
+
"--pass-on-no-patterns",
|
|
80
|
+
"--debug",
|
|
81
|
+
"--stats",
|
|
82
|
+
"--mcp"
|
|
83
|
+
]
|
|
84
|
+
}
|
package/lib/mtx/auth_manager.js
CHANGED
|
@@ -102,8 +102,6 @@ async function fetchToken(params) {
|
|
|
102
102
|
message = 'token has invalid scope. Ensure your user has the required roles';
|
|
103
103
|
} else if (error.status === 404) {
|
|
104
104
|
message = 'token endpoint not found. Ensure that MTX is running with extensibility enabled';
|
|
105
|
-
} else if (error.message.includes('<html')) { // should already be caught when fetching passcode URL
|
|
106
|
-
message = 'HTML response received. Ensure the route to MTX is configured correctly in App Router';
|
|
107
105
|
} else if (error.status === 401) {
|
|
108
106
|
message = `invalid credentials`;
|
|
109
107
|
if (params.has('passcode')) {
|