@sap/cds-dk 4.9.7 → 6.0.3

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.
Files changed (145) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/bin/activate.js +4 -4
  3. package/bin/add.js +11 -5
  4. package/bin/bind.js +16 -28
  5. package/bin/build/all.js +2 -2
  6. package/bin/build/index.js +12 -19
  7. package/bin/cds.js +22 -19
  8. package/bin/compile/index.js +12 -3
  9. package/bin/deploy/index.js +15 -9
  10. package/bin/deploy/to-hana.js +4 -4
  11. package/bin/deploy/to-sqlite.js +1 -1
  12. package/bin/env.js +13 -4
  13. package/bin/eval.js +1 -1
  14. package/bin/fix-redirects.js +1 -1
  15. package/bin/import.js +44 -179
  16. package/bin/repl.js +1 -1
  17. package/bin/watch.js +10 -15
  18. package/lib/bind/BindingManager.js +87 -0
  19. package/lib/bind/bindingResolver.js +15 -87
  20. package/lib/bind/bindingUtil.js +18 -11
  21. package/lib/bind/cfKeyProvider.js +58 -29
  22. package/lib/bind/kubernetesCredentialsProvider.js +44 -21
  23. package/lib/bind/userCdsrcJsonStorageProvider.js +12 -18
  24. package/lib/cds.js +13 -0
  25. package/lib/client/base_command.js +3 -2
  26. package/lib/client/extend.js +6 -7
  27. package/lib/client/helper/fs_helper.js +7 -8
  28. package/lib/client/settings_manager.js +14 -10
  29. package/lib/compile/index.js +9 -10
  30. package/lib/compile/openapi/csdl2openapi.js +151 -43
  31. package/lib/compile/openapi/index.js +2 -2
  32. package/lib/compile/to-xsuaa.js +3 -2
  33. package/lib/import/common.js +342 -27
  34. package/lib/import/importapi.js +81 -13
  35. package/lib/import/message.js +4 -4
  36. package/lib/import/odata/index.js +141 -0
  37. package/lib/import/{utils → odata/metadataconverter}/MetadataConverter.js +2 -2
  38. package/lib/import/{utils → odata/metadataconverter}/MetadataConverterFactory.js +1 -1
  39. package/lib/import/{utils → odata/util}/MultiMap.js +0 -0
  40. package/lib/import/{v2parser.js → odata/v2parser.js} +405 -468
  41. package/lib/import/{v4parser.js → odata/v4parser.js} +311 -322
  42. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Aggregation.V1.json +0 -0
  43. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Authorization.V1.json +0 -0
  44. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Capabilities.V1.json +0 -0
  45. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Core.V1.json +0 -0
  46. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Measures.V1.json +0 -0
  47. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Validation.V1.json +0 -0
  48. package/lib/import/openapi/importOpenAPI.js +521 -0
  49. package/lib/import/openapi/index.js +19 -0
  50. package/lib/import/openapi/utilities.js +38 -0
  51. package/lib/index.js +1 -1
  52. package/lib/init/constants.js +7 -2
  53. package/lib/init/index.js +10 -3
  54. package/lib/init/template/_merging/existences.js +79 -19
  55. package/lib/init/template/approuter/files/mta.yaml.hbs +25 -11
  56. package/lib/init/template/approuter/files/package.json +1 -1
  57. package/lib/init/template/approuter/files/xs-app.json.hbs +7 -0
  58. package/lib/init/template/approuter/index.js +61 -54
  59. package/lib/init/template/auditlog/index.js +2 -2
  60. package/lib/init/template/data/index.js +2 -2
  61. package/lib/init/template/hana/files/mta.yaml.hbs +17 -17
  62. package/lib/init/template/hana/index.js +8 -4
  63. package/lib/init/template/helm/index.js +39 -0
  64. package/lib/init/template/helm/update/index.js +28 -0
  65. package/lib/init/template/java/index.js +7 -9
  66. package/lib/init/template/kibana-logging/index.js +5 -6
  67. package/lib/init/template/lint/index.js +1 -1
  68. package/lib/init/template/mta/index.js +4 -4
  69. package/lib/init/template/mtx/files/java/package.json +9 -9
  70. package/lib/init/template/mtx/files/mta.yaml.hbs +0 -7
  71. package/lib/init/template/mtx/files/xs-security.json.hbs +11 -20
  72. package/lib/init/template/mtx/index.js +50 -57
  73. package/lib/init/template/nodejs/files/package.json.hbs +2 -2
  74. package/lib/init/template/notebook/index.js +2 -0
  75. package/lib/init/template/templateBase.js +8 -6
  76. package/lib/init/template/xsuaa/index.js +9 -5
  77. package/lib/init/util/fsUtil.js +25 -10
  78. package/lib/init/util/logger.js +1 -1
  79. package/lib/init/util/mvnArchetypeUtil.js +4 -10
  80. package/lib/init/util/projectReader.js +17 -9
  81. package/lib/init/util/templateUtil.js +30 -34
  82. package/lib/k8s/ChartAdder.js +65 -0
  83. package/lib/k8s/ChartBuilder.js +186 -0
  84. package/lib/k8s/Context.js +71 -0
  85. package/lib/k8s/ImageUtil.js +21 -0
  86. package/lib/k8s/Model.d.ts +53 -0
  87. package/lib/k8s/auditlog/index.js +29 -0
  88. package/lib/k8s/auditlog/templates/auditlog.yaml +1 -0
  89. package/lib/k8s/buildModel.js +70 -0
  90. package/lib/k8s/connectivity/index.js +80 -0
  91. package/lib/k8s/connectivity/templates/connectivity-binding.yaml +9 -0
  92. package/lib/k8s/connectivity/templates/connectivity-proxy-info.yaml +20 -0
  93. package/lib/k8s/connectivity/templates/connectivity.yaml +1 -0
  94. package/lib/k8s/deployment/index.js +81 -0
  95. package/lib/k8s/deployment/subchart/Chart.yaml +6 -0
  96. package/lib/k8s/deployment/subchart/templates/_helpers.tpl +156 -0
  97. package/lib/k8s/deployment/subchart/templates/api-rule.yaml +24 -0
  98. package/lib/k8s/deployment/subchart/templates/deployment.yaml +110 -0
  99. package/lib/k8s/deployment/subchart/templates/network-policy.yaml +16 -0
  100. package/lib/k8s/deployment/subchart/templates/pod-disruption-budget.yaml +11 -0
  101. package/lib/k8s/deployment/subchart/templates/secret.yaml +10 -0
  102. package/lib/k8s/deployment/subchart/templates/service-binding.yaml +27 -0
  103. package/lib/k8s/deployment/subchart/templates/service.yaml +11 -0
  104. package/lib/k8s/deployment/subchart/values-lint.yaml +8 -0
  105. package/lib/k8s/deployment/subchart/values.schema.json +449 -0
  106. package/lib/k8s/deployment/subchart/values.yaml +76 -0
  107. package/lib/k8s/deployment/subchart-override/templates/_web_application_helpers.tpl +81 -0
  108. package/lib/k8s/destinations/index.js +40 -0
  109. package/lib/k8s/destinations/lib.js +16 -0
  110. package/lib/k8s/destinations/templates/destinations.yaml +1 -0
  111. package/lib/k8s/docker/push.js +8 -0
  112. package/lib/k8s/event-mesh/index.js +53 -0
  113. package/lib/k8s/event-mesh/lib.js +9 -0
  114. package/lib/k8s/event-mesh/templates/event-mesh.yaml +1 -0
  115. package/lib/k8s/hana-deployer/index.js +38 -0
  116. package/lib/k8s/hana-deployer/templates/hana-deployer-job.yaml +41 -0
  117. package/lib/k8s/helm/Release.js +80 -0
  118. package/lib/k8s/html5-apps-deployer/index.js +43 -0
  119. package/lib/k8s/html5-apps-deployer/templates/_html5_deployment_helpers.tpl +16 -0
  120. package/lib/k8s/html5-apps-deployer/templates/html5-apps-deployer-job.yaml +53 -0
  121. package/lib/k8s/html5-apps-repo-host/index.js +26 -0
  122. package/lib/k8s/html5-apps-repo-host/lib.js +16 -0
  123. package/lib/k8s/html5-apps-repo-host/templates/html5-apps-repo-host.yaml +1 -0
  124. package/lib/k8s/index.js +153 -0
  125. package/lib/k8s/modules.js +36 -0
  126. package/lib/k8s/multitenancy/index.js +24 -0
  127. package/lib/k8s/multitenancy/lib.js +25 -0
  128. package/lib/k8s/multitenancy/templates/saas-registry.yaml +19 -0
  129. package/lib/k8s/readProject.js +14 -0
  130. package/lib/k8s/resources/helm/chart/templates/NOTES.txt +11 -0
  131. package/lib/k8s/resources/helm/chart/templates/_deployment_helpers.tpl +221 -0
  132. package/lib/k8s/resources/helm/chart/templates/_helpers.tpl +167 -0
  133. package/lib/k8s/resources/helm/chart/templates/_sapcp_helpers.tpl +103 -0
  134. package/lib/k8s/util.js +186 -0
  135. package/lib/k8s/wrapFeature.js +55 -0
  136. package/lib/k8s/xsuaa/index.js +64 -0
  137. package/lib/k8s/xsuaa/lib.js +17 -0
  138. package/lib/k8s/xsuaa/templates/xsuaa.yaml +1 -0
  139. package/lib/lint/generators.js +1 -1
  140. package/lib/util/exec.js +1 -1
  141. package/lib/util/write.js +1 -1
  142. package/lib/watch/node-watch.js +43 -14
  143. package/npm-shrinkwrap.json +923 -2705
  144. package/package.json +5 -5
  145. package/lib/import/index.js +0 -235
@@ -0,0 +1,521 @@
1
+ const {
2
+ cdsName,
3
+ nameFromPath,
4
+ pathAndMethod,
5
+ serviceName,
6
+ } = require("./utilities");
7
+
8
+ module.exports = { importOpenAPI };
9
+
10
+ // we are not interested (yet) in HEAD, OPTIONS, TRACE
11
+ const IS_METHOD = {
12
+ delete: true,
13
+ get: true,
14
+ patch: true,
15
+ post: true,
16
+ put: true,
17
+ };
18
+
19
+ const STANDARD_HEADERS = [
20
+ "accept",
21
+ "accept-encoding",
22
+ "accept-language",
23
+ "authorization",
24
+ "content-type",
25
+ "if-match",
26
+ ];
27
+
28
+ function importOpenAPI(input) {
29
+ const csn = {
30
+ definitions: {},
31
+ meta: { creator: "cds-import-openapi" },
32
+ };
33
+
34
+ const context = {
35
+ serviceName: serviceName(input.info?.title || "TODO.service"),
36
+ oasVersion: input.openapi || input.swagger,
37
+ v3: !!input.openapi,
38
+ schemas: input.openapi ? input.components?.schemas : input.definitions,
39
+ anonymous: [],
40
+ JSON: false,
41
+ };
42
+
43
+ //TODO: complain if neither swagger nor openapi3, i.e. context.oasVersion is undefined
44
+ const service = {
45
+ kind: "service",
46
+ "@Capabilities.BatchSupported": false,
47
+ "@Capabilities.KeyAsSegmentSupported": true,
48
+ };
49
+ if (input.info?.title) service["@Core.Description"] = input.info.title;
50
+ if (input.info?.version) service["@Core.SchemaVersion"] = input.info.version;
51
+ if (input.info?.description)
52
+ service["@Core.LongDescription"] = input.info.description;
53
+ csn.definitions[context.serviceName] = service;
54
+
55
+ operations(context, csn, input);
56
+
57
+ reuseTypes(context, csn);
58
+
59
+ anonymousTypes(context, csn);
60
+
61
+ return csn;
62
+ }
63
+
64
+ function reuseTypes(context, csn) {
65
+ for (const [name, schema] of Object.entries(context.schemas || {})) {
66
+ const typeName = `${context.serviceName}_types.${cdsName(name)}`;
67
+ csn.definitions[typeName] = cdsType(context, schema, false, true);
68
+ csn.definitions[typeName].kind = "type";
69
+ delete csn.definitions[typeName].notNull;
70
+ }
71
+ }
72
+
73
+ function anonymousTypes(context, csn) {
74
+ for (const a of context.anonymous) {
75
+ csn.definitions[a.name] = a.type;
76
+ csn.definitions[a.name].kind = "type";
77
+ }
78
+ if (context.JSON) {
79
+ csn.definitions["common.JSON"] = { kind: "type", type: "cds.LargeString" };
80
+ }
81
+ }
82
+
83
+ function operations(context, csn, input) {
84
+ const reuseParameters = context.v3
85
+ ? input.components?.parameters
86
+ : input.parameters;
87
+ const reuseResponses = context.v3
88
+ ? input.components?.responses
89
+ : input.responses;
90
+ for (const [path, item] of Object.entries(input.paths || {})) {
91
+ const globalParameters = item.parameters || [];
92
+ for (const [method, operation] of Object.entries(item)) {
93
+ if (!IS_METHOD[method]) continue;
94
+ const cdsOperation = {
95
+ kind: method === "get" ? "function" : "action",
96
+ params: {},
97
+ };
98
+ if (operation.tags) cdsOperation["@Common.Label"] = operation.tags[0];
99
+ if (operation.summary)
100
+ cdsOperation["@Core.Description"] = operation.summary;
101
+ if (operation.description)
102
+ cdsOperation["@Core.LongDescription"] = operation.description;
103
+ if (cdsOperation.kind === "action" && method !== "post")
104
+ cdsOperation["@openapi.method"] = method.toUpperCase();
105
+ cdsOperation["@openapi.path"] = path;
106
+
107
+ for (let param of globalParameters.concat(operation.parameters || [])) {
108
+ if (param.$ref) {
109
+ // resolve reuse parameter
110
+ const expectedPrefix = context.v3
111
+ ? "#/components/parameters/"
112
+ : "#/parameters/";
113
+ if (!param.$ref.startsWith(expectedPrefix)) {
114
+ throw new Error(`TODO: unexpected reference ${param.$ref} in parameter for ${method} of ${path}`);
115
+ }
116
+ param = reuseParameters[param.$ref.substring(expectedPrefix.length)];
117
+ }
118
+
119
+ if (param.in === "body") continue;
120
+ if (STANDARD_HEADERS.includes(param.name.toLowerCase())) continue;
121
+
122
+ const cdsParam = cdsType(
123
+ context,
124
+ context.v3 ? param?.schema : param,
125
+ false,
126
+ false,
127
+ true
128
+ );
129
+ if (param.description) cdsParam["@description"] = param.description;
130
+ cdsParam["@openapi.in"] = param.in;
131
+
132
+ if ((param.in === "path" && param.style && param.style !== "simple") ||
133
+ (param.in === "query" && param.style && param.style !== "form")
134
+ )
135
+ cdsParam["@openapi.style"] = param.style;
136
+
137
+ if (param.explode && param.style !== "form")
138
+ cdsParam["@openapi.explode"] = true;
139
+
140
+ if (param.explode === false &&
141
+ (param.style === "form" || (!param.style && param.in === "query"))
142
+ )
143
+ cdsParam["@openapi.explode"] = false;
144
+
145
+ if (param.in === "query" && param.collectionFormat === "csv")
146
+ cdsParam["@openapi.explode"] = false;
147
+ if (param.in === "query" && param.collectionFormat === "ssv")
148
+ cdsParam["@openapi.style"] = "spaceDelimited";
149
+ if (param.in === "query" && param.collectionFormat === "pipes")
150
+ cdsParam["@openapi.style"] = "pipeDelimited";
151
+ if (param.in === "query" && param.allowReserved === true)
152
+ cdsParam["@openapi.allowReserved"] = true;
153
+ if (param.required && param.in !== "path")
154
+ cdsParam["@openapi.required"] = true;
155
+ if (!param.required)
156
+ delete cdsParam.notNull;
157
+
158
+ const name = cdsName(param.name);
159
+ if (name !== param.name) cdsParam["@openapi.name"] = param.name;
160
+ cdsOperation.params[name] = cdsParam;
161
+ }
162
+
163
+ const schema = requestBodySchema(context, operation);
164
+ if (schema) {
165
+ cdsOperation.params.body = cdsType(context, schema);
166
+ cdsOperation.params.body["@openapi.in"] = "body";
167
+ }
168
+
169
+ if (!operation.responses)
170
+ throw new Error(`TODO: no responses for path ${path}, method ${method}`);
171
+
172
+ const successCode = Object.keys(operation.responses || {}).find((r) =>
173
+ r.startsWith("2")
174
+ );
175
+ if (successCode) {
176
+ let response = operation.responses[successCode];
177
+ if (response.$ref) {
178
+ // resolve reuse response
179
+ const expectedPrefix = context.v3
180
+ ? "#/components/responses/"
181
+ : "#/responses/";
182
+ if (!response.$ref.startsWith(expectedPrefix))
183
+ throw new Error(`TODO: unexpected reference ${response.$ref} in ${successCode} response for method ${method} of path ${path}`);
184
+ else
185
+ response =
186
+ reuseResponses[response.$ref.substring(expectedPrefix.length)];
187
+ }
188
+ const responseSchema = context.v3
189
+ ? response.content?.["application/json"]?.schema
190
+ : response.schema;
191
+ if (responseSchema)
192
+ cdsOperation.returns = cdsType(context, responseSchema);
193
+ else if (method === "get")
194
+ cdsOperation.returns = { type: "cds.Boolean" };
195
+ } else if (method === "get")
196
+ cdsOperation.returns = { type: "cds.Boolean" };
197
+
198
+ const operationName = `${context.serviceName}.${nameFromPath(
199
+ path,
200
+ method
201
+ )}`;
202
+
203
+ if (csn.definitions[operationName]) {
204
+ const existing = pathAndMethod(csn.definitions[operationName]);
205
+ throw new Error(`Name collision: same name ${operationName} for method ${method} of path ${path} and method ${existing.method.toLowerCase()} of path ${existing.path}`);
206
+ }
207
+ csn.definitions[operationName] = cdsOperation;
208
+ }
209
+ }
210
+ }
211
+
212
+ function requestBodySchema(context, operation) {
213
+ const requestBody = context.v3
214
+ ? operation.requestBody
215
+ : operation.parameters?.find((p) => p.in === "body");
216
+ return context.v3
217
+ ? requestBody?.content["application/json"]?.schema
218
+ : requestBody?.schema;
219
+ }
220
+
221
+ function cdsType(
222
+ context,
223
+ schema,
224
+ arrayItem = false,
225
+ namedType = false,
226
+ forParameter = false
227
+ ) {
228
+ let type = {};
229
+ let hasIncludes = false;
230
+
231
+ if (!arrayItem) if (schema.title) type["@title"] = schema.title;
232
+ if (schema.description) type["@description"] = schema.description;
233
+
234
+ if (schema.$ref) {
235
+ type.type = referencedType(context, schema.$ref).name;
236
+ return type;
237
+ }
238
+
239
+ let schemaType = normalizeSchemaType(schema);
240
+
241
+ switch (schemaType) {
242
+ case "array":
243
+ //TODO: complain if "xml"
244
+ {
245
+ const itemsType = cdsType(
246
+ context,
247
+ schema.items,
248
+ true,
249
+ false,
250
+ forParameter
251
+ );
252
+ const annotations = Object.keys(itemsType).filter((k) =>
253
+ k.startsWith("@")
254
+ );
255
+ // make anonymous type for item if item has annotations
256
+ if (annotations.length > 0) {
257
+ type.items = anonymousType(context, itemsType);
258
+ } else {
259
+ type.items = itemsType;
260
+ }
261
+ }
262
+ break;
263
+
264
+ case "boolean":
265
+ type.type = "cds.Boolean";
266
+ addDefault(type, schema, forParameter);
267
+ break;
268
+
269
+ case "file":
270
+ type.type = "cds.String";
271
+ break;
272
+
273
+ case "integer":
274
+ type.type = "cds.Integer";
275
+ if (schema.format === "int64") type.type = "cds.Integer64";
276
+ addDefault(type, schema, forParameter);
277
+ addPrimitiveExample(type, schema);
278
+ break;
279
+
280
+ case "number":
281
+ type.type = "cds.Decimal";
282
+ if (schema.format === "double" || schema.format === "float")
283
+ type.type = "cds.Double";
284
+ addDefault(type, schema, forParameter);
285
+ addPrimitiveExample(type, schema);
286
+ break;
287
+
288
+ case "object":
289
+ type.elements = {};
290
+ //TODO: discriminator(context, type, schema);
291
+ if (schema.allOf || schema.anyOf || schema.oneOf) {
292
+ const xOf = (schema.allOf || []).concat(
293
+ schema.anyOf || [],
294
+ schema.oneOf || []
295
+ );
296
+ for (const subSchema of xOf) {
297
+ if (subSchema.$ref) {
298
+ hasIncludes = true;
299
+ const refType = referencedType(context, subSchema.$ref);
300
+ if (!type.includes) type.includes = [];
301
+ type.includes.push(refType.name);
302
+ } else if (subSchema.type === "object" || subSchema.properties) {
303
+ //TODO: what if subSchema has allOf/...? Better recurse here and "merge" the types?
304
+ structElements(context, type, subSchema);
305
+ } else {
306
+ // should not get here
307
+ throw new Error(`Error: object with non-object sub-schema`);
308
+ }
309
+ }
310
+ }
311
+
312
+ structElements(context, type, schema);
313
+
314
+ if (!namedType && hasIncludes) {
315
+ type = anonymousType(context, type);
316
+ }
317
+
318
+ break;
319
+
320
+ case "string":
321
+ switch (schema.format) {
322
+ case "binary":
323
+ type.type = "cds.LargeBinary";
324
+ if (schema.maxLength) type.length = schema.maxLength;
325
+ break;
326
+ case "date":
327
+ type.type = "cds.Date";
328
+ break;
329
+ case "date-time":
330
+ type.type = "cds.Timestamp";
331
+ break;
332
+ case "time":
333
+ type.type = "cds.Time";
334
+ break;
335
+ case "uuid":
336
+ type.type = "cds.UUID";
337
+ break;
338
+ default:
339
+ type.type = "cds.String";
340
+ if (schema.maxLength) type.length = schema.maxLength;
341
+ }
342
+ if (Array.isArray(schema.enum)) {
343
+ type["@assert.range"] = true;
344
+ type.enum = Object.fromEntries(
345
+ schema.enum.map((val) => {
346
+ const key = cdsName(val);
347
+ return key === val ? [val, {}] : [key, { val }];
348
+ })
349
+ );
350
+ }
351
+ addDefault(type, schema, forParameter);
352
+ addPrimitiveExample(type, schema);
353
+ break;
354
+
355
+ case undefined:
356
+ schemaType = bestMatchingType(context, schema);
357
+ if (schemaType) {
358
+ schema.type = schemaType;
359
+ type = cdsType(context, schema, arrayItem, namedType);
360
+ break;
361
+ }
362
+
363
+ // last resort
364
+ // eslint-disable-next-line no-fallthrough
365
+ default:
366
+ {
367
+ context.JSON = true;
368
+ const jsonSchema = minimalSchema(schema);
369
+ if (arrayItem && jsonSchema !== "{}") {
370
+ type = anonymousType(context, {
371
+ type: "common.JSON",
372
+ "@openapi.schema": jsonSchema,
373
+ });
374
+ } else {
375
+ type.type = "common.JSON";
376
+ if (jsonSchema !== "{}") type["@openapi.schema"] = jsonSchema;
377
+ }
378
+ }
379
+ break;
380
+ }
381
+ return type;
382
+ }
383
+
384
+ function addDefault(type, schema, forParameter) {
385
+ if (forParameter) return;
386
+ if (schema.default) type.default = { val: schema.default };
387
+ }
388
+
389
+ function addPrimitiveExample(type, schema) {
390
+ const example = schema.examples?.[0] || schema.example;
391
+ if (!example || example.$ref) return;
392
+ type["@Core.Example.$Type"] = "Core.PrimitiveExampleValue";
393
+ type["@Core.Example.Value"] = example;
394
+ }
395
+
396
+ function minimalSchema(schema) {
397
+ const s = { ...schema };
398
+ delete s.title;
399
+ delete s.description;
400
+ return JSON.stringify(s);
401
+ }
402
+
403
+ function anonymousType(context, type) {
404
+ //TODO: construct newName from path leading here instead of using a counter
405
+ // pro: makes imported models more stable
406
+ // con: may cause name clashes
407
+ const newName = `${context.serviceName}.anonymous.type${context.anonymous.length}`;
408
+ context.anonymous.push({ name: newName, type: type });
409
+ return { type: newName };
410
+ }
411
+
412
+ function bestMatchingType(context, schema) {
413
+ if (schema.type) return schema.type;
414
+ let type;
415
+ if (schema.allOf || schema.anyOf || schema.oneOf) {
416
+ const xOf = (schema.allOf || []).concat(
417
+ schema.anyOf || [],
418
+ schema.oneOf || []
419
+ );
420
+ for (const subSchema of xOf) {
421
+ // determine overall type of this construct
422
+ if (subSchema.$ref) {
423
+ let refType = { schema: subSchema };
424
+ let limit = 10;
425
+ while (refType.schema.$ref && limit-- > 0)
426
+ refType = referencedType(context, refType.schema.$ref);
427
+ if (
428
+ refType.schema.allOf ||
429
+ refType.schema.anyOf ||
430
+ refType.schema.oneOf
431
+ ) {
432
+ const subType = bestMatchingType(context, refType.schema);
433
+ type = betterType(type, { type: subType });
434
+ } else type = betterType(type, refType.schema);
435
+ } else {
436
+ //TODO: nested xOf - not yet encountered
437
+ type = betterType(type, subSchema);
438
+ }
439
+ }
440
+ }
441
+ return type;
442
+ }
443
+
444
+ function referencedType(context, ref) {
445
+ const expectedPrefix = context.v3
446
+ ? "#/components/schemas/"
447
+ : "#/definitions/";
448
+ if (!ref.startsWith(expectedPrefix)) {
449
+ throw new Error(`TODO: unexpected reference ${ref} in schema.`);
450
+ }
451
+ const schemaName = ref.substring(expectedPrefix.length);
452
+ const schema = context.schemas[schemaName];
453
+ return {
454
+ name: `${context.serviceName}_types.${cdsName(schemaName)}`,
455
+ schema,
456
+ };
457
+ }
458
+
459
+ function normalizeSchemaType(schema) {
460
+ if (!schema.type && schema.items) return "array";
461
+
462
+ if (
463
+ !schema.type &&
464
+ (schema.properties ||
465
+ schema.patternProperties ||
466
+ schema.additionalProperties ||
467
+ schema.discriminator)
468
+ )
469
+ return "object";
470
+
471
+ let schemaType = schema.type;
472
+ if (Array.isArray(schemaType)) {
473
+ schemaType = schemaType.sort();
474
+ if (schemaType.includes("null")) {
475
+ const index = schemaType.indexOf("null");
476
+ schemaType.splice(index, 1);
477
+ }
478
+ if (schemaType.length === 1) schemaType = schemaType[0];
479
+ }
480
+ return schemaType;
481
+ }
482
+
483
+ //TODO: better name :-)
484
+ function betterType(currentType, schema) {
485
+ const schemaType = normalizeSchemaType(schema);
486
+ if (currentType === undefined) return schemaType;
487
+ if (currentType === schemaType) return currentType;
488
+ if (currentType === "string" && ["integer", "number"].includes(schemaType))
489
+ return "string";
490
+ if (["integer", "number"].includes(currentType) && schemaType === "string")
491
+ return "string";
492
+ //TODO: more cases here?
493
+ return null;
494
+ }
495
+
496
+ // function discriminator(context, type, schema) {
497
+ // if (!schema.discriminator) return;
498
+ // console.log("hi");
499
+ // const propertyName =
500
+ // schema.discriminator.propertyName || schema.discriminator;
501
+ // type["@openapi.discriminator"] = { propertyName };
502
+ // if (schema.discriminator.mapping) {
503
+ // const mapping = {};
504
+ // for (const [value, reference] of Object.entries(
505
+ // schema.discriminator.mapping
506
+ // )) {
507
+ // mapping[value] = referencedType(context, reference).name;
508
+ // }
509
+ // type["@openapi.discriminator"].mapping = mapping;
510
+ // }
511
+ // }
512
+
513
+ function structElements(context, type, schema) {
514
+ //TODO: interpret "required"
515
+ for (const [prop, propSchema] of Object.entries(schema.properties || {})) {
516
+ const name = cdsName(prop);
517
+ //TODO: check if property "name" already exists and has identical cdsType, warn otherwise
518
+ type.elements[name] = cdsType(context, propSchema);
519
+ if (name !== prop) type.elements[name]["@openapi.name"] = prop;
520
+ }
521
+ }
@@ -0,0 +1,19 @@
1
+ "use strict"
2
+
3
+ const { importOpenAPI } = require("./importOpenAPI");
4
+ let messages = require("../message").getMessages();
5
+
6
+ function openAPI2csn(source) {
7
+ let csn = {};
8
+ try {
9
+ const fileContentToJSON = JSON.parse(source);
10
+ csn = importOpenAPI(fileContentToJSON);
11
+ } catch (e) {
12
+ throw new Error(messages.INVALID_OPENAPI_FILE);
13
+ }
14
+ return csn;
15
+ }
16
+
17
+ module.exports = {
18
+ openAPI2csn
19
+ }
@@ -0,0 +1,38 @@
1
+ module.exports = { cdsName, nameFromPath, pathAndMethod, serviceName };
2
+
3
+ function cdsName(oasName) {
4
+ if (oasName === "") return "_";
5
+ let name = oasName;
6
+ if (name.match(/^(\p{Nd}|\p{Mn}|\p{Mc}|\p{Pc}|\p{Cf})/u)) name = "_" + name;
7
+ return name.replace(/[^_\p{L}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]/gu, "_");
8
+ //TODO: shorten if longer than 128 characters
9
+ }
10
+
11
+ function nameFromPath(path, method) {
12
+ let cleanPath = path
13
+ .substring(1)
14
+ .replace(/\/$/, "")
15
+ .replace(/{[^}]+}/g, ".")
16
+ .replace(/\/\./g, "_")
17
+ .replace(/[^_\p{L}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]/gu, "_");
18
+ if (cleanPath === "") cleanPath = "_";
19
+ //TODO: shorten if longer than 128 characters
20
+ return method === "get" ? cleanPath : `${cleanPath}_${method}`;
21
+ }
22
+
23
+ function pathAndMethod(operation) {
24
+ const path = operation["@openapi.path"];
25
+ const method =
26
+ operation.kind === "function"
27
+ ? "GET"
28
+ : operation["@openapi.method"] || "POST";
29
+ return { path, method };
30
+ }
31
+
32
+ function serviceName(title) {
33
+ return title
34
+ .trim()
35
+ .replace(/(\s|-)+/g, ".")
36
+ .replace(/[^._\p{L}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]/gu, "_");
37
+ //TODO: shorten if longer than 511-129=382 characters
38
+ }
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const cds = require ('@sap/cds')
1
+ const cds = require('./cds')
2
2
  const compile_desc = Reflect.getOwnPropertyDescriptor(cds,'compile')
3
3
 
4
4
  // extending cds to lazy-load additional submodules provided by cds-dk
@@ -1,6 +1,6 @@
1
1
  const os = require('os');
2
2
 
3
- const MAVEN_ARCHETYPE_VERSION = '1.24.0';
3
+ const MAVEN_ARCHETYPE_VERSION = '1.26.0';
4
4
 
5
5
  const constants = {
6
6
  IS_WIN: (os.platform() === 'win32'),
@@ -22,12 +22,15 @@ const constants = {
22
22
  OPTION_CF_MANIFEST: 'cf-manifest',
23
23
  OPTION_PIPELINE: 'pipeline',
24
24
  OPTION_SAMPLES: 'samples',
25
+ OPTION_DATA: 'data',
25
26
  OPTION_NOTEBOOK: 'notebook',
26
27
  OPTION_LINT: 'lint',
27
28
  OPTION_LINT_GLOBAL: 'lint:global',
28
29
  OPTION_LINT_DEV: 'lint:dev',
29
30
  OPTION_JAVA_PACKAGE: 'java:package',
30
31
  OPTION_JAVA_MVN: 'java:mvn',
32
+ OPTION_HELM: 'helm',
33
+ OPTION_HELM_UPDATE: 'helm/update',
31
34
 
32
35
  // node enum
33
36
  PROJECT_TYPE: Object.freeze({ nodejs: 1, java: 2, unknown: 3 }),
@@ -71,11 +74,13 @@ constants.OPTIONS_PRIORITY = [
71
74
  constants.OPTION_AUDITLOG,
72
75
  constants.OPTION_APPROUTER,
73
76
  constants.OPTION_SAMPLES,
77
+ constants.OPTION_DATA,
74
78
  constants.OPTION_KIBANA_LOGGING,
75
79
  constants.OPTION_NOTEBOOK,
76
80
  constants.OPTION_LINT,
77
81
  constants.OPTION_LINT_CUSTOM,
78
- constants.OPTION_LINT_EXAMPLE
82
+ constants.OPTION_LINT_EXAMPLE,
83
+ constants.OPTION_HELM
79
84
  ];
80
85
 
81
86
  module.exports = constants;
package/lib/init/index.js CHANGED
@@ -8,7 +8,7 @@ const Spinner = require('./util/spinner');
8
8
  const { OPTIONS_PRIORITY, COMMAND_ADD, COMMAND_INIT,
9
9
  // eslint-disable-next-line no-unused-vars
10
10
  OPTION_XSUAA, OPTION_AUDITLOG, OPTION_HANA, OPTION_MTA, OPTION_MTX, OPTION_APPROUTER, OPTION_KIBANA_LOGGING,
11
- OPTION_NODEJS, OPTION_JAVA,
11
+ OPTION_NODEJS, OPTION_JAVA, OPTION_HELM,
12
12
  OPTION_PIPELINE, OPTION_SAMPLES, OPTION_CF_MANIFEST, PROJECT_FILES, REGEX_PROJECT_NAME } = require('./constants');
13
13
 
14
14
  module.exports = class CDSGenerator {
@@ -86,6 +86,9 @@ module.exports = class CDSGenerator {
86
86
  guiOptions: {
87
87
  hint: 'Speed up your ramp-up phase with some sample files in your project.'
88
88
  }
89
+ },
90
+ {
91
+ // OPTION_HELM this.config
89
92
  }
90
93
  ]
91
94
 
@@ -106,7 +109,9 @@ module.exports = class CDSGenerator {
106
109
  OPTION_CF_MANIFEST,
107
110
  OPTION_MTA,
108
111
  OPTION_SAMPLES,
109
- OPTION_KIBANA_LOGGING
112
+ OPTION_KIBANA_LOGGING,
113
+ OPTION_HELM,
114
+ OPTION_SAMPLES
110
115
  ],
111
116
  multiple: true,
112
117
  promptOrder: 10 // used to identify ui prompts
@@ -209,7 +214,9 @@ module.exports = class CDSGenerator {
209
214
 
210
215
  for (const template of this.templateList) {
211
216
  const msg = `Adding feature '${template.name}'...`;
212
- if (this.logger.isDebug) {
217
+ if (template.dontAnnounce) {
218
+ await template.run();
219
+ } else if (this.logger.isDebug) {
213
220
  this.logger.log(msg);
214
221
  await template.run();
215
222
  } else {