@sap/cds-dk 8.8.2 → 8.9.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 +24 -1
- package/bin/deploy/index.js +7 -3
- package/bin/init.js +5 -5
- package/bin/repl.js +4 -0
- package/bin/test.js +6 -4
- package/bin/up.js +77 -0
- package/bin/watch.js +0 -43
- package/lib/bind/shared.js +1 -1
- package/lib/build/buildTaskEngine.js +10 -9
- package/lib/build/buildTaskFactory.js +0 -45
- package/lib/build/plugins/helm/files/subcharts/content-deployment/Chart.yaml +1 -1
- package/lib/build/plugins/helm/files/subcharts/content-deployment/templates/job.yaml +8 -0
- package/lib/build/plugins/helm/files/subcharts/content-deployment/values.schema.json +87 -0
- package/lib/build/plugins/helm/files/subcharts/content-deployment/values.yaml +29 -1
- package/lib/build/plugins/helm/files/subcharts/service-instance/Chart.yaml +1 -1
- package/lib/build/plugins/helm/files/subcharts/service-instance/templates/serviceinstance.yaml +1 -0
- package/lib/build/plugins/helm/files/subcharts/service-instance/values.yaml +1 -1
- package/lib/build/plugins/helm/files/subcharts/web-application/Chart.yaml +1 -1
- package/lib/build/plugins/helm/files/subcharts/web-application/templates/deployment.yaml +8 -0
- package/lib/build/plugins/helm/files/subcharts/web-application/values.schema.json +87 -0
- package/lib/build/plugins/helm/files/subcharts/web-application/values.yaml +29 -1
- package/lib/build/provider/internalBuildTaskProvider.js +1 -1
- package/lib/build/provider/mtx-extension/index.js +5 -0
- package/lib/deploy/to-hana/hana.js +95 -43
- package/lib/deploy/to-hana/mtaUtil.js +1 -2
- package/lib/env/schema.js +12 -88
- package/lib/import/common.js +23 -15
- package/lib/import/odata.package.json +7 -0
- package/lib/init/constants.js +2 -1
- package/lib/init/index.js +13 -16
- package/lib/init/mvn.js +1 -1
- package/lib/init/plugin.js +1 -1
- package/lib/init/projectReader.js +5 -1
- package/lib/init/template/approuter/files/package.json +1 -4
- package/lib/init/template/attachments/files/package.json.hbs +1 -1
- package/lib/init/template/cloud-logging/files/java-cdsrc.json +5 -0
- package/lib/init/template/cloud-logging/index.js +9 -5
- package/lib/init/template/containerize/files/containerize.yaml.hbs +1 -1
- package/lib/init/template/data/index.js +3 -3
- package/lib/init/template/extension/files/.base/index.cds +15 -0
- package/lib/init/template/extension/files/app/extension.cds.hbs +47 -0
- package/lib/init/template/extension/files/test/data/Authors.csv +4 -0
- package/lib/init/template/extension/files/test/data/my.bookshop-Books.csv +8 -0
- package/lib/init/template/extension/index.js +99 -0
- package/lib/init/template/handler/files/extension/handler.xs.hbs +10 -0
- package/lib/init/template/handler/files/extension/package.json +10 -0
- package/lib/init/template/handler/index.js +83 -20
- package/lib/init/template/http/index.js +3 -3
- package/lib/init/template/java/index.js +3 -3
- package/lib/init/template/mta/index.js +1 -1
- package/lib/init/template/nodejs/index.js +3 -3
- package/lib/init/template/pipeline/index.js +1 -1
- package/lib/init/template/sample/index.js +6 -0
- package/lib/init/template/typer/files/package.json +0 -3
- package/lib/init/template/typer/index.js +2 -2
- package/lib/init/validate.js +4 -7
- package/lib/mtx/pull.js +9 -1
- package/lib/util/question.js +24 -21
- package/npm-shrinkwrap.json +18 -18
- package/package.json +2 -2
- /package/lib/init/template/handler/files/{handler.xs.hbs → standard/handler.xs.hbs} +0 -0
|
@@ -1283,6 +1283,56 @@
|
|
|
1283
1283
|
}
|
|
1284
1284
|
}
|
|
1285
1285
|
}
|
|
1286
|
+
},
|
|
1287
|
+
"nodeSelector": {
|
|
1288
|
+
"$id": "#/properties/nodeSelector",
|
|
1289
|
+
"title": "Node selector",
|
|
1290
|
+
"type": "object",
|
|
1291
|
+
"patternProperties": {
|
|
1292
|
+
"^[-._a-zA-Z][-._a-zA-Z0-9]*$": {
|
|
1293
|
+
"type": "string"
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
},
|
|
1297
|
+
"nodeAffinity": {
|
|
1298
|
+
"$id": "#/properties/nodeAffinity",
|
|
1299
|
+
"title": "Node affinity",
|
|
1300
|
+
"type": "object",
|
|
1301
|
+
"additionalProperties": false,
|
|
1302
|
+
"properties": {
|
|
1303
|
+
"requiredDuringSchedulingIgnoredDuringExecution": {
|
|
1304
|
+
"type": "object",
|
|
1305
|
+
"additionalProperties": false,
|
|
1306
|
+
"properties": {
|
|
1307
|
+
"nodeSelectorTerms": {
|
|
1308
|
+
"type": "array",
|
|
1309
|
+
"description": "A node selector terms, associated with the corresponding weight.",
|
|
1310
|
+
"items": {
|
|
1311
|
+
"$ref": "#/definitions/NodeSelectorTerm"
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
},
|
|
1315
|
+
"required": ["nodeSelectorTerms"]
|
|
1316
|
+
},
|
|
1317
|
+
"preferredDuringSchedulingIgnoredDuringExecution": {
|
|
1318
|
+
"type": "array",
|
|
1319
|
+
"items": {
|
|
1320
|
+
"type": "object",
|
|
1321
|
+
"additionalProperties": false,
|
|
1322
|
+
"properties": {
|
|
1323
|
+
"weight": {
|
|
1324
|
+
"type": "integer",
|
|
1325
|
+
"description": "Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100."
|
|
1326
|
+
},
|
|
1327
|
+
"preference": {
|
|
1328
|
+
"description": "A node selector term, associated with the corresponding weight.",
|
|
1329
|
+
"$ref": "#/definitions/NodeSelectorTerm"
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
"required": ["weight", "preference"]
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1286
1336
|
}
|
|
1287
1337
|
},
|
|
1288
1338
|
"definitions": {
|
|
@@ -1355,6 +1405,43 @@
|
|
|
1355
1405
|
"type": "integer",
|
|
1356
1406
|
"minimum": 1,
|
|
1357
1407
|
"maximum": 65535
|
|
1408
|
+
},
|
|
1409
|
+
"NodeSelectorRequirement": {
|
|
1410
|
+
"description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
|
|
1411
|
+
"type": "object",
|
|
1412
|
+
"additionalProperties": false,
|
|
1413
|
+
"properties": {
|
|
1414
|
+
"key": {
|
|
1415
|
+
"description": "The label key that the selector applies to.",
|
|
1416
|
+
"type": "string"
|
|
1417
|
+
},
|
|
1418
|
+
"operator": {
|
|
1419
|
+
"description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.",
|
|
1420
|
+
"type": "string",
|
|
1421
|
+
"enum": ["In", "NotIn", "Exists", "DoesNotExist", "Gt", "Lt"]
|
|
1422
|
+
},
|
|
1423
|
+
"values": {
|
|
1424
|
+
"description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.",
|
|
1425
|
+
"items": {
|
|
1426
|
+
"type": "string"
|
|
1427
|
+
},
|
|
1428
|
+
"type": "array"
|
|
1429
|
+
}
|
|
1430
|
+
},
|
|
1431
|
+
"required": ["key", "operator"]
|
|
1432
|
+
},
|
|
1433
|
+
"NodeSelectorTerm": {
|
|
1434
|
+
"description": "A null or empty node selector term matches no objects. The requirements of them are ANDed.",
|
|
1435
|
+
"type": "object",
|
|
1436
|
+
"additionalProperties": false,
|
|
1437
|
+
"properties": {
|
|
1438
|
+
"matchExpressions": {
|
|
1439
|
+
"type": "array",
|
|
1440
|
+
"items": {
|
|
1441
|
+
"$ref": "#/definitions/NodeSelectorRequirement"
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1358
1445
|
}
|
|
1359
1446
|
}
|
|
1360
1447
|
}
|
|
@@ -14,7 +14,7 @@ global:
|
|
|
14
14
|
_internal:
|
|
15
15
|
web-application:
|
|
16
16
|
versions:
|
|
17
|
-
1.
|
|
17
|
+
1.24.0: true
|
|
18
18
|
## Will be used instead of the `.Chart.Name`, e.g. when generating the Deployment name.
|
|
19
19
|
# nameOverride: name
|
|
20
20
|
|
|
@@ -331,3 +331,31 @@ hpa:
|
|
|
331
331
|
## See https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/horizontal-pod-autoscaler-v2/#HorizontalPodAutoscalerSpec
|
|
332
332
|
# behavior: {}
|
|
333
333
|
# metrics: {}
|
|
334
|
+
## Node label selector. See https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector
|
|
335
|
+
# nodeSelector:
|
|
336
|
+
# kubernetes.io/os: linux
|
|
337
|
+
|
|
338
|
+
## Node Affinity configuration. See https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity
|
|
339
|
+
# nodeAffinity:
|
|
340
|
+
# requiredDuringSchedulingIgnoredDuringExecution:
|
|
341
|
+
# nodeSelectorTerms:
|
|
342
|
+
# - matchExpressions:
|
|
343
|
+
# - key: kubernetes.io/os
|
|
344
|
+
# operator: In
|
|
345
|
+
# values:
|
|
346
|
+
# - linux
|
|
347
|
+
# preferredDuringSchedulingIgnoredDuringExecution:
|
|
348
|
+
# - weight: 1
|
|
349
|
+
# preference:
|
|
350
|
+
# matchExpressions:
|
|
351
|
+
# - key: label-1
|
|
352
|
+
# operator: In
|
|
353
|
+
# values:
|
|
354
|
+
# - key-1
|
|
355
|
+
# - weight: 50
|
|
356
|
+
# preference:
|
|
357
|
+
# matchExpressions:
|
|
358
|
+
# - key: label-2
|
|
359
|
+
# operator: In
|
|
360
|
+
# values:
|
|
361
|
+
# - key-2
|
|
@@ -171,7 +171,7 @@ class InternalBuildTaskProvider extends BuildTaskProvider {
|
|
|
171
171
|
}
|
|
172
172
|
throw new BuildError(`Invalid MTX sidecar configuration - profile 'mtx-sidecar' not set.`)
|
|
173
173
|
}
|
|
174
|
-
return { for: BUILD_TASK_MTX_SIDECAR }
|
|
174
|
+
return { src: MTX_SIDECAR_FOLDER, for: BUILD_TASK_MTX_SIDECAR }
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
if (sidecarEnv?.requires["cds.xt.ModelProviderService"]?._in_sidecar || sidecarEnv?.profiles?.includes('mtx-sidecar')) {
|
|
@@ -54,6 +54,11 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
|
|
|
54
54
|
// reclassify base model warnings as info messages
|
|
55
55
|
extensionCompilation.reclassifyBaseModelMessages(this.messages, saasPkgPath)
|
|
56
56
|
|
|
57
|
+
// Throw error if messages contain errors
|
|
58
|
+
if (this.messages.some(msg => msg.severity === 'Error')) {
|
|
59
|
+
throw new BuildError('CDS compilation failed', this.messages);
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
if (extensionModel) {
|
|
58
63
|
// IMPORTANT NOTE: perform all model operations before the linter is called as the linter
|
|
59
64
|
// will modify the model causing the creation of 'unresolved' associations
|
|
@@ -47,7 +47,7 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
if (dry) {
|
|
50
|
-
console.log('
|
|
50
|
+
console.log('--- running in dry mode ---');
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
if (vcapFile) {
|
|
@@ -91,60 +91,116 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
91
91
|
// the primary database is always built and deployed
|
|
92
92
|
const dest = path.join(cds.env.build.target, cds.env.folders.db);
|
|
93
93
|
|
|
94
|
-
let serviceName = pServiceName;
|
|
95
|
-
let serviceKeyName;
|
|
96
|
-
const { instance, key } = cds.requires.db?.binding ?? {};
|
|
97
|
-
if (!serviceName && instance) {
|
|
98
|
-
serviceName = instance;
|
|
99
|
-
serviceKeyName = key;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
94
|
if (undeployWhitelist) {
|
|
103
95
|
console.log('Writing undeploy.json');
|
|
104
96
|
await write(undeployWhitelist).to(dest, 'undeploy.json')
|
|
105
97
|
}
|
|
106
98
|
|
|
107
99
|
const hasVCAPEnv = Object.keys(vcapEnv).length > 0;
|
|
108
|
-
if (!serviceName && hasVCAPEnv) {
|
|
109
|
-
await mkdirp(dest);
|
|
110
|
-
} else {
|
|
111
|
-
const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
|
|
112
|
-
await this._getOrCreateCFService(dest, serviceName, serviceKeyName, tunnelAddress);
|
|
113
100
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
101
|
+
let bindOptions
|
|
102
|
+
if (cds.cli.options.on === 'k8s') {
|
|
103
|
+
const cp = require('node:child_process')
|
|
104
|
+
const appName = pServiceName ?? path.basename(cds.root)
|
|
105
|
+
const instance =
|
|
106
|
+
`apiVersion: services.cloud.sap.com/v1
|
|
107
|
+
kind: ServiceInstance
|
|
108
|
+
metadata:
|
|
109
|
+
name: ${appName}-db
|
|
110
|
+
spec:
|
|
111
|
+
serviceOfferingName: hana
|
|
112
|
+
servicePlanName: hdi-shared\n`
|
|
113
|
+
const binding =
|
|
114
|
+
`apiVersion: services.cloud.sap.com/v1
|
|
115
|
+
kind: ServiceBinding
|
|
116
|
+
metadata:
|
|
117
|
+
name: ${appName}-db-binding
|
|
118
|
+
spec:
|
|
119
|
+
serviceInstanceName: ${appName}-db
|
|
120
|
+
secretName: ${appName}-db-secret
|
|
121
|
+
\n`
|
|
119
122
|
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
try {
|
|
124
|
+
cp.execSync('kubectl apply -f -', { input: instance })
|
|
125
|
+
cp.execSync('kubectl apply -f -', { input: binding })
|
|
126
|
+
cp.execSync(`kubectl wait --for=condition=Ready servicebinding/${appName}-db-binding --timeout=120s`)
|
|
127
|
+
cp.execSync(`kubectl wait --for=condition=Ready serviceinstance/${appName}-db --timeout=120s`)
|
|
128
|
+
} catch (error) {
|
|
129
|
+
const descr = cp.execSync(`kubectl describe serviceinstance/${appName}-db`).toString()
|
|
130
|
+
const message = descr.match(/Message:\s+(.*)/)?.[1] || 'None'
|
|
131
|
+
throw error.message + `\nInstance Message: ${message}`
|
|
122
132
|
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
console.log(`${bold('Starting deployment to SAP HANA ...')}`);
|
|
126
|
-
DEBUG?.({ dest, vcapEnv, hdiOptions })
|
|
127
|
-
await hdi.deploy(dest, vcapEnv, hdiOptions);
|
|
128
133
|
|
|
129
|
-
|
|
130
|
-
const
|
|
134
|
+
const { data: encoded } = JSON.parse(cp.execSync(`kubectl get secrets/${appName}-db-secret -o json`).toString())
|
|
135
|
+
const credentials = {}
|
|
136
|
+
for (const [key, value] of Object.entries(encoded)) {
|
|
137
|
+
credentials[key] = Buffer.from(value, 'base64').toString('utf-8');
|
|
138
|
+
}
|
|
139
|
+
vcapEnv.VCAP_SERVICES ??= {
|
|
140
|
+
hana: [{
|
|
141
|
+
tags: ['hana'],
|
|
142
|
+
credentials
|
|
143
|
+
}]
|
|
144
|
+
}
|
|
145
|
+
bindOptions = {
|
|
131
146
|
...options,
|
|
132
|
-
to: `${
|
|
147
|
+
to: `${appName}-db-binding`,
|
|
133
148
|
for: options.for,
|
|
134
149
|
kind: BUILD_TASK_HANA
|
|
135
150
|
}
|
|
136
|
-
|
|
137
|
-
|
|
151
|
+
} else {
|
|
152
|
+
let serviceName = pServiceName;
|
|
153
|
+
let serviceKeyName;
|
|
154
|
+
const { instance, key } = cds.requires.db?.binding ?? {};
|
|
155
|
+
if (!serviceName && instance) {
|
|
156
|
+
serviceName = instance;
|
|
157
|
+
serviceKeyName = key;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!serviceName && hasVCAPEnv) {
|
|
161
|
+
await mkdirp(dest);
|
|
162
|
+
} else {
|
|
163
|
+
const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } = await this._getOrCreateCFService(dest, serviceName, serviceKeyName, tunnelAddress);
|
|
164
|
+
serviceName ??= cfServiceInstanceName;
|
|
165
|
+
serviceKeyName ??= cfServiceInstanceKeyName
|
|
166
|
+
if (!vcapEnv.VCAP_SERVICES || serviceName) {
|
|
167
|
+
vcapEnv.VCAP_SERVICES = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (storeCredentials) {
|
|
171
|
+
await this._addInstanceToDefaultEnvJson([dest, cds.root], cfServiceInstanceName, serviceKey);
|
|
172
|
+
}
|
|
173
|
+
if (!hasVCAPEnv) {
|
|
174
|
+
bindOptions = {
|
|
175
|
+
...options,
|
|
176
|
+
to: `${serviceName}:${serviceKeyName}`,
|
|
177
|
+
for: options.for,
|
|
178
|
+
kind: BUILD_TASK_HANA
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
138
182
|
}
|
|
139
183
|
|
|
184
|
+
console.log(`${bold('starting deployment to SAP HANA ...')}`);
|
|
185
|
+
DEBUG?.({ dest, vcapEnv, hdiOptions })
|
|
186
|
+
await hdi.deploy(dest, vcapEnv, hdiOptions);
|
|
187
|
+
|
|
140
188
|
if (storeCredentials) {
|
|
141
189
|
await gitUtil.ensureFileIsGitignored('default-env.json', cds.root);
|
|
142
190
|
}
|
|
143
191
|
|
|
144
|
-
|
|
192
|
+
if (!hasVCAPEnv) {
|
|
193
|
+
const services = await bind(bindOptions)
|
|
194
|
+
await storeBindings(services, bindOptions)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const { env4 } = require('../../init/projectReader')
|
|
198
|
+
if (env4('production').requires?.db?.kind !== 'hana') {
|
|
199
|
+
console.log(`use ${info('cds add hana')} to configure the project for SAP HANA.\n`);
|
|
200
|
+
}
|
|
145
201
|
}
|
|
146
202
|
|
|
147
|
-
console.log(`
|
|
203
|
+
console.log(`successfully finished deployment`);
|
|
148
204
|
|
|
149
205
|
if (warnings.length > 0) {
|
|
150
206
|
console.warn(warn('\nWARNING:'));
|
|
@@ -156,7 +212,6 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
156
212
|
|
|
157
213
|
|
|
158
214
|
async _getOrCreateCFService(currentModelFolder, serviceName, serviceKeyName, tunnelAddress) {
|
|
159
|
-
const modelName = path.basename(currentModelFolder);
|
|
160
215
|
|
|
161
216
|
// get from param
|
|
162
217
|
let cfServiceInstanceMta;
|
|
@@ -164,13 +219,18 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
164
219
|
if (serviceName) {
|
|
165
220
|
cfServiceInstanceName = serviceName;
|
|
166
221
|
} else {
|
|
222
|
+
const modelName = path.basename(currentModelFolder);
|
|
167
223
|
const cfServiceDescriptor = await mtaUtil.getHanaDbModuleDescriptor(cds.root, modelName);
|
|
168
224
|
cfServiceInstanceName = cfServiceDescriptor.hdiServiceName;
|
|
169
225
|
cfServiceInstanceMta = cfServiceDescriptor.hdiService
|
|
170
226
|
}
|
|
171
227
|
|
|
172
228
|
console.log();
|
|
173
|
-
|
|
229
|
+
|
|
230
|
+
// valid service name chars: alpha-numeric, hyphens, and underscores
|
|
231
|
+
if (/[^\w-_]+/g.exec(cfServiceInstanceName)) {
|
|
232
|
+
throw `Service name ${cfServiceInstanceName} must only contain alpha-numeric, hyphens, and underscores.`;
|
|
233
|
+
}
|
|
174
234
|
console.log(`Using container ${bold(cfServiceInstanceName)}`);
|
|
175
235
|
|
|
176
236
|
const cfConfig = cfServiceInstanceMta?.parameters?.config;
|
|
@@ -251,14 +311,6 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
251
311
|
}
|
|
252
312
|
|
|
253
313
|
|
|
254
|
-
_validateServiceInstanceName(serviceInstanceName) {
|
|
255
|
-
// valid service name chars: alpha-numeric, hyphens, and underscores
|
|
256
|
-
if (/[^\w-_]+/g.exec(serviceInstanceName)) {
|
|
257
|
-
throw `Service name ${serviceInstanceName} must only contain alpha-numeric, hyphens, and underscores.`;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
314
|
_injectTunnelAddress(credentials, tunnelAddress) {
|
|
263
315
|
if (!/\w+:\d+/.test(tunnelAddress)) {
|
|
264
316
|
throw `Invalid tunnel address '${tunnelAddress}' - must be in form 'host:port'`;
|
package/lib/env/schema.js
CHANGED
|
@@ -1,69 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
function _local(id) {
|
|
3
|
-
return require.resolve(id, {
|
|
4
|
-
paths: [
|
|
5
|
-
process.cwd(), // project-local module is preferred
|
|
6
|
-
__dirname // otherwise from our own dependencies
|
|
7
|
-
]
|
|
8
|
-
})
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function _robustRequire(file) {
|
|
12
|
-
const fs = require('fs')
|
|
13
|
-
const path = require('path')
|
|
14
|
-
|
|
15
|
-
file = path.normalize(file)
|
|
16
|
-
let content
|
|
17
|
-
try {
|
|
18
|
-
content = fs.readFileSync(file, 'utf8')
|
|
19
|
-
} catch (e) {
|
|
20
|
-
// file is not readable -> nothing to do
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
return JSON.parse(content)
|
|
26
|
-
} catch (e) {
|
|
27
|
-
// invalid json -> ignore and try regex
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const result = {}
|
|
31
|
-
try {
|
|
32
|
-
const deps = JSON.parse(content.match(/"dependencies"\s*:\s*({[^}]*})/)[1])
|
|
33
|
-
result.dependencies = deps
|
|
34
|
-
} catch (e) { /* ignore */ }
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
const devDeps = JSON.parse(content.match(/"devDependencies"\s*:\s*({[^}]*})/)[1])
|
|
38
|
-
result.devDependencies = devDeps
|
|
39
|
-
} catch (e) { /* ignore */ }
|
|
40
|
-
|
|
41
|
-
return result
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
_robustRequire.resolve = function (id, options = {}) {
|
|
45
|
-
const fs = require('fs')
|
|
46
|
-
const path = require('path')
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
return require.resolve(id, options)
|
|
50
|
-
} catch (err) {
|
|
51
|
-
// ignore
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const [, mod, file] = /(.+)\/([^/]+)$/.exec(id)
|
|
55
|
-
let modPath = path.join(process.cwd(), 'node_modules', mod, file)
|
|
56
|
-
const ext = path.extname(modPath)
|
|
57
|
-
if (!ext) {
|
|
58
|
-
modPath = modPath + '.js'
|
|
59
|
-
}
|
|
60
|
-
if (fs.existsSync(modPath)) {
|
|
61
|
-
return modPath
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
throw new Error('not found')
|
|
65
|
-
}
|
|
66
|
-
|
|
67
1
|
function mergePluginSchema(pluginSchema) {
|
|
68
2
|
// contribution points also appear in schema file ./schemas/cds-rc.js
|
|
69
3
|
return {
|
|
@@ -140,36 +74,26 @@ module.exports = async function overlay4(schemaName) {
|
|
|
140
74
|
}
|
|
141
75
|
|
|
142
76
|
try {
|
|
143
|
-
const
|
|
144
|
-
if (
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
await mergePluginSchema(pluginPackageJson.cds.schema).into(result);
|
|
156
|
-
}
|
|
157
|
-
} catch (err) {
|
|
158
|
-
if (err.code !== 'ENOENT') {
|
|
159
|
-
console.error(`Error loading schema from plugin ${key}: ${err.message}`)
|
|
160
|
-
}
|
|
161
|
-
}
|
|
77
|
+
const plugins = await cds.plugins;
|
|
78
|
+
if (plugins) {
|
|
79
|
+
for (const [key, { impl }] of Object.entries(plugins)) {
|
|
80
|
+
const pluginPackageJsonFile = path.join(path.dirname(impl), 'package.json')
|
|
81
|
+
try {
|
|
82
|
+
const pluginPackageJson = require(pluginPackageJsonFile)
|
|
83
|
+
if (pluginPackageJson.cds?.schema) {
|
|
84
|
+
await mergePluginSchema(pluginPackageJson.cds.schema).into(result);
|
|
85
|
+
}
|
|
86
|
+
} catch (err) {
|
|
87
|
+
if (err.code !== 'ENOENT') {
|
|
88
|
+
console.error(`Error loading schema from plugin ${key}: ${err.message}`)
|
|
162
89
|
}
|
|
163
90
|
}
|
|
164
|
-
} finally {
|
|
165
|
-
pluginsLib.require = oldRequire
|
|
166
91
|
}
|
|
167
92
|
}
|
|
168
93
|
} catch (err) {
|
|
169
94
|
console.error(err.message)
|
|
170
95
|
}
|
|
171
96
|
|
|
172
|
-
|
|
173
97
|
result.title = oldTitle
|
|
174
98
|
result.description = oldDescription
|
|
175
99
|
return result
|
package/lib/import/common.js
CHANGED
|
@@ -282,15 +282,14 @@ async function _generateChecksumValidate(dest, output, force) {
|
|
|
282
282
|
return "/* checksum : " + currentChecksum + " */\n" + output;
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
// Modify the output to
|
|
286
|
-
function
|
|
287
|
-
|
|
285
|
+
// Modify the output to add using statements with the dependency services
|
|
286
|
+
function _addUsingStatementsForDependent(output) {
|
|
287
|
+
if (usingStatements.length === 0)
|
|
288
|
+
return output;
|
|
289
|
+
|
|
290
|
+
let usings = usingStatements.join('\n') + '\n';
|
|
288
291
|
usingStatements = [];
|
|
289
|
-
|
|
290
|
-
let endIndex = output.indexOf(';', startIndex);
|
|
291
|
-
let usingStatement = output.substring(startIndex, endIndex+1);
|
|
292
|
-
output = output.replace(usingStatement, using);
|
|
293
|
-
return output;
|
|
292
|
+
return usings + output;
|
|
294
293
|
}
|
|
295
294
|
|
|
296
295
|
async function _getResult(output, as, dest, force, kind, isDependent) {
|
|
@@ -308,11 +307,11 @@ async function _getResult(output, as, dest, force, kind, isDependent) {
|
|
|
308
307
|
dest += '.' + extension;
|
|
309
308
|
}
|
|
310
309
|
|
|
311
|
-
if (as && as
|
|
310
|
+
if (as && as !== "csn" && as !== "json") {
|
|
312
311
|
// cdsCompiler[as] will convert the csn output into various output formats
|
|
313
312
|
output = cdsCompiler[as](output);
|
|
314
313
|
if (isDependent && kind === 'V4') {
|
|
315
|
-
output =
|
|
314
|
+
output = _addUsingStatementsForDependent(output);
|
|
316
315
|
}
|
|
317
316
|
}
|
|
318
317
|
|
|
@@ -400,6 +399,17 @@ async function _updateNodeConfig(dest, services, kind, cwd, options) {
|
|
|
400
399
|
}
|
|
401
400
|
}
|
|
402
401
|
|
|
402
|
+
if (kind.startsWith('odata')) {
|
|
403
|
+
pkgConf.dependencies ??= {};
|
|
404
|
+
const { dependencies } = require('./odata.package.json');
|
|
405
|
+
for (const dep in dependencies) {
|
|
406
|
+
if (!pkgConf.dependencies?.[dep]) {
|
|
407
|
+
pkgConf.dependencies[dep] = dependencies[dep];
|
|
408
|
+
package_json_updated = true;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
403
413
|
if (package_json_updated) {
|
|
404
414
|
await write(package_json, JSON.stringify(pkgConf, null, ' '));
|
|
405
415
|
console.log(`[cds] - updated ./package.json`);
|
|
@@ -495,11 +505,9 @@ async function postProcess(filePath, options, csn, cwd) {
|
|
|
495
505
|
await write(csnFile, serviceCsn, "utf-8");
|
|
496
506
|
|
|
497
507
|
if (options.isDependency) {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
usingStatements.push(`using { ${services} } from './${path.basename(csnFile)}';`);
|
|
502
|
-
}
|
|
508
|
+
// `cdsc.to.cdl() adds `using { foo.bar }` on-demand. We only need to include correct path.
|
|
509
|
+
// TODO: Should be part of `csn.requires`, and let ´to.cdl()` render them.
|
|
510
|
+
usingStatements.push(`using from './${path.basename(csnFile.replace(`'`, `''`))}';`);
|
|
503
511
|
}
|
|
504
512
|
|
|
505
513
|
_printUsageHint(csnFile, services, cwd);
|
package/lib/init/constants.js
CHANGED
|
@@ -5,7 +5,7 @@ module.exports = {
|
|
|
5
5
|
COMMAND_ADD: 'add',
|
|
6
6
|
|
|
7
7
|
JAVA_LTS_VERSIONS: [17, 21],
|
|
8
|
-
MAVEN_ARCHETYPE_VERSION: '3.8.
|
|
8
|
+
MAVEN_ARCHETYPE_VERSION: '3.8.1',
|
|
9
9
|
|
|
10
10
|
OPTIONS: Object.freeze({
|
|
11
11
|
COMPLETION: 'completion',
|
|
@@ -16,6 +16,7 @@ module.exports = {
|
|
|
16
16
|
ESM: 'esm',
|
|
17
17
|
TYPER: 'typer',
|
|
18
18
|
TYPESCRIPT: 'typescript', // must run before SAMPLE, as that influences whether .js or .ts files are created
|
|
19
|
+
EXTENSION: 'extension',
|
|
19
20
|
SAMPLES: 'samples', // deprecated
|
|
20
21
|
TINY_SAMPLE: 'tiny-sample',
|
|
21
22
|
SAMPLE: 'sample',
|