@odata2ts/odata2ts 0.23.1 → 0.24.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +74 -74
  3. package/lib/FactoryFunctionModel.d.ts +13 -13
  4. package/lib/FactoryFunctionModel.js +2 -2
  5. package/lib/FactoryFunctionModel.js.map +1 -1
  6. package/lib/NamingModel.d.ts +182 -182
  7. package/lib/NamingModel.js +11 -11
  8. package/lib/NamingModel.js.map +1 -1
  9. package/lib/OptionModel.d.ts +228 -223
  10. package/lib/OptionModel.js +23 -23
  11. package/lib/OptionModel.js.map +1 -1
  12. package/lib/app.d.ts +8 -8
  13. package/lib/app.js +63 -63
  14. package/lib/app.js.map +1 -1
  15. package/lib/cli.d.ts +3 -3
  16. package/lib/cli.js +153 -153
  17. package/lib/data-model/DataModel.d.ts +85 -85
  18. package/lib/data-model/DataModel.js +134 -134
  19. package/lib/data-model/DataModelDigestion.d.ts +44 -44
  20. package/lib/data-model/DataModelDigestion.js +274 -274
  21. package/lib/data-model/DataModelDigestion.js.map +1 -1
  22. package/lib/data-model/DataModelDigestionV2.d.ts +10 -10
  23. package/lib/data-model/DataModelDigestionV2.js +200 -200
  24. package/lib/data-model/DataModelDigestionV4.d.ts +3 -3
  25. package/lib/data-model/DataModelDigestionV4.js +195 -195
  26. package/lib/data-model/DataTypeModel.d.ts +102 -102
  27. package/lib/data-model/DataTypeModel.js +8 -8
  28. package/lib/data-model/DataTypeModel.js.map +1 -1
  29. package/lib/data-model/NamingHelper.d.ts +55 -55
  30. package/lib/data-model/NamingHelper.js +214 -214
  31. package/lib/data-model/ServiceConfigHelper.d.ts +21 -21
  32. package/lib/data-model/ServiceConfigHelper.js +113 -113
  33. package/lib/data-model/edmx/ODataEdmxModelBase.d.ts +80 -80
  34. package/lib/data-model/edmx/ODataEdmxModelBase.js +2 -2
  35. package/lib/data-model/edmx/ODataEdmxModelBase.js.map +1 -1
  36. package/lib/data-model/edmx/ODataEdmxModelV3.d.ts +63 -63
  37. package/lib/data-model/edmx/ODataEdmxModelV3.js +2 -2
  38. package/lib/data-model/edmx/ODataEdmxModelV4.d.ts +67 -67
  39. package/lib/data-model/edmx/ODataEdmxModelV4.js +2 -2
  40. package/lib/defaultConfig.d.ts +9 -9
  41. package/lib/defaultConfig.js +165 -164
  42. package/lib/defaultConfig.js.map +1 -1
  43. package/lib/evaluateConfig.d.ts +25 -25
  44. package/lib/evaluateConfig.js +82 -82
  45. package/lib/generator/ImportContainer.d.ts +16 -16
  46. package/lib/generator/ImportContainer.js +91 -91
  47. package/lib/generator/ImportContainer.js.map +1 -1
  48. package/lib/generator/ModelGenerator.d.ts +2 -2
  49. package/lib/generator/ModelGenerator.js +211 -183
  50. package/lib/generator/ModelGenerator.js.map +1 -1
  51. package/lib/generator/QueryObjectGenerator.d.ts +2 -2
  52. package/lib/generator/QueryObjectGenerator.js +239 -239
  53. package/lib/generator/ServiceGenerator.d.ts +8 -8
  54. package/lib/generator/ServiceGenerator.js +422 -422
  55. package/lib/generator/ServiceGenerator.js.map +1 -1
  56. package/lib/generator/index.d.ts +3 -3
  57. package/lib/generator/index.js +9 -9
  58. package/lib/index.d.ts +3 -3
  59. package/lib/index.js +6 -6
  60. package/lib/project/ProjectManager.d.ts +30 -30
  61. package/lib/project/ProjectManager.js +177 -175
  62. package/lib/project/ProjectManager.js.map +1 -1
  63. package/lib/project/formatter/BaseFormatter.d.ts +49 -49
  64. package/lib/project/formatter/BaseFormatter.js +30 -30
  65. package/lib/project/formatter/FileFormatter.d.ts +16 -16
  66. package/lib/project/formatter/FileFormatter.js +2 -2
  67. package/lib/project/formatter/NoopFormatter.d.ts +29 -29
  68. package/lib/project/formatter/NoopFormatter.js +42 -42
  69. package/lib/project/formatter/PrettierFormatter.d.ts +46 -46
  70. package/lib/project/formatter/PrettierFormatter.js +92 -92
  71. package/lib/project/formatter/index.d.ts +2 -2
  72. package/lib/project/formatter/index.js +14 -14
  73. package/lib/project/logger/logFilePath.d.ts +1 -1
  74. package/lib/project/logger/logFilePath.js +12 -12
  75. package/lib/run-cli.d.ts +2 -2
  76. package/lib/run-cli.js +6 -6
  77. package/package.json +27 -28
@@ -1,423 +1,423 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateServices = void 0;
4
- const tslib_1 = require("tslib");
5
- const odata_core_1 = require("@odata2ts/odata-core");
6
- const deepmerge_1 = tslib_1.__importDefault(require("deepmerge"));
7
- const ts_morph_1 = require("ts-morph");
8
- const upper_case_first_1 = require("upper-case-first");
9
- const processors_1 = require("xml2js/lib/processors");
10
- const ImportContainer_1 = require("./ImportContainer");
11
- const ROOT_SERVICE = "ODataService";
12
- const RESPONSE_TYPES = {
13
- collection: "ODataCollectionResponse",
14
- model: "ODataModelResponse",
15
- value: "ODataValueResponse",
16
- };
17
- function generateServices(dataModel, project, version, namingHelper) {
18
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
19
- const generator = new ServiceGenerator(dataModel, project, version, namingHelper);
20
- return generator.generate();
21
- });
22
- }
23
- exports.generateServices = generateServices;
24
- class ServiceGenerator {
25
- constructor(dataModel, project, version, namingHelper) {
26
- this.dataModel = dataModel;
27
- this.project = project;
28
- this.version = version;
29
- this.namingHelper = namingHelper;
30
- this.generateQOperationProp = (operation) => {
31
- return {
32
- scope: ts_morph_1.Scope.Private,
33
- name: this.namingHelper.getPrivatePropName(operation.qName),
34
- type: operation.qName,
35
- hasQuestionToken: true,
36
- };
37
- };
38
- }
39
- generate() {
40
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
41
- const sourceFile = yield this.project.createMainServiceFile();
42
- const serviceName = this.namingHelper.getMainServiceName();
43
- const container = this.dataModel.getEntityContainer();
44
- const unboundOperations = [...Object.values(container.functions), ...Object.values(container.actions)];
45
- yield this.generateModelServices();
46
- const importContainer = new ImportContainer_1.ImportContainer(this.namingHelper.getFileNames());
47
- importContainer.addFromClientApi("ODataHttpClient");
48
- importContainer.addFromService(ROOT_SERVICE);
49
- const { properties, methods } = (0, deepmerge_1.default)(this.generateMainServiceProperties(container, importContainer), this.generateMainServiceOperations(unboundOperations, importContainer));
50
- sourceFile.addClass({
51
- isExported: true,
52
- name: serviceName,
53
- typeParameters: ["ClientType extends ODataHttpClient"],
54
- extends: `${ROOT_SERVICE}<ClientType>`,
55
- properties /*: [
56
- {
57
- scope: Scope.Private,
58
- name: this.namingHelper.getPrivatePropName("name"),
59
- type: "string",
60
- initializer: `"${this.namingHelper.getODataServiceName()}"`,
61
- },
62
- ]*/,
63
- methods,
64
- });
65
- sourceFile.addImportDeclarations(importContainer.getImportDeclarations(false));
66
- });
67
- }
68
- generateMainServiceProperties(container, importContainer) {
69
- const result = { properties: [], methods: [] };
70
- Object.values(container.entitySets).forEach(({ name, entityType }) => {
71
- result.methods.push(this.generateRelatedServiceGetter(name, entityType, importContainer));
72
- });
73
- Object.values(container.singletons).forEach((singleton) => {
74
- result.properties.push(this.generateSingletonProp(singleton, importContainer));
75
- result.methods.push(this.generateSingletonGetter(singleton));
76
- });
77
- return result;
78
- }
79
- generateMainServiceOperations(ops, importContainer) {
80
- const result = { properties: [], methods: [] };
81
- ops.forEach(({ operation, name }) => {
82
- result.properties.push(this.generateQOperationProp(operation));
83
- result.methods.push(this.generateMethod(name, operation, importContainer));
84
- });
85
- return result;
86
- }
87
- generateRelatedServiceGetter(propName, entityType, importContainer, currentServiceName) {
88
- const idName = entityType.idModelName;
89
- const idFunctionName = entityType.qIdFunctionName;
90
- const serviceName = this.namingHelper.getServiceName(entityType.name);
91
- const collectionName = this.namingHelper.getCollectionServiceName(entityType.name);
92
- importContainer.addFromClientApi("ODataHttpClient");
93
- importContainer.addGeneratedModel(idName);
94
- importContainer.addGeneratedQObject(idFunctionName);
95
- // make sure to not falsely import self-referential stuff
96
- if (!currentServiceName || currentServiceName !== serviceName) {
97
- importContainer.addGeneratedService(serviceName, collectionName, serviceName);
98
- }
99
- return {
100
- scope: ts_morph_1.Scope.Public,
101
- name: this.namingHelper.getRelatedServiceGetter(propName),
102
- parameters: [
103
- {
104
- name: "id",
105
- type: `${idName} | undefined`,
106
- hasQuestionToken: true,
107
- },
108
- ],
109
- overloads: [
110
- {
111
- parameters: [],
112
- returnType: `${collectionName}<ClientType>`,
113
- },
114
- {
115
- parameters: [
116
- {
117
- name: "id",
118
- type: idName,
119
- },
120
- ],
121
- returnType: `${serviceName}<ClientType>`,
122
- },
123
- ],
124
- statements: [
125
- `const fieldName = "${propName}";`,
126
- 'return typeof id === "undefined" || id === null',
127
- `? new ${collectionName}(this.client, this.getPath(), fieldName)`,
128
- `: new ${serviceName}(this.client, this.getPath(), new ${idFunctionName}(fieldName).buildUrl(id));`,
129
- ],
130
- };
131
- }
132
- generateSingletonProp(singleton, importContainer) {
133
- const { name, entityType } = singleton;
134
- const type = this.namingHelper.getServiceName(entityType.name);
135
- importContainer.addGeneratedService(this.namingHelper.getServiceName(entityType.name), type);
136
- return {
137
- scope: ts_morph_1.Scope.Private,
138
- name: this.namingHelper.getPrivatePropName(name),
139
- type: `${type}<ClientType>`,
140
- hasQuestionToken: true,
141
- };
142
- }
143
- generateSingletonGetter(singleton) {
144
- const { name, odataName, entityType } = singleton;
145
- const propName = "this." + this.namingHelper.getPrivatePropName(name);
146
- const serviceType = this.namingHelper.getServiceName(entityType.name);
147
- return {
148
- scope: ts_morph_1.Scope.Public,
149
- name: this.namingHelper.getRelatedServiceGetter(name),
150
- statements: [
151
- `if(!${propName}) {`,
152
- // prettier-ignore
153
- ` ${propName} = new ${serviceType}(this.client, this.getPath(), "${odataName}")`,
154
- "}",
155
- `return ${propName}`,
156
- ],
157
- };
158
- }
159
- getVersionSuffix() {
160
- return this.version === odata_core_1.ODataVersions.V2 ? "V2" : "V4";
161
- }
162
- generateEntityTypeService(model, serviceName, serviceFile, importContainer) {
163
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
164
- const entityServiceType = "EntityTypeService" + this.getVersionSuffix();
165
- const editableModelName = model.editableName;
166
- const qObjectName = (0, processors_1.firstCharLowerCase)(model.qName);
167
- const operations = this.dataModel.getOperationTypeByBinding(model.name);
168
- const props = [...model.baseProps, ...model.props];
169
- importContainer.addFromService(entityServiceType);
170
- importContainer.addFromClientApi("ODataHttpClient");
171
- importContainer.addGeneratedModel(model.name, editableModelName);
172
- importContainer.addGeneratedQObject(model.qName, qObjectName);
173
- const { properties, methods } = (0, deepmerge_1.default)(this.generateServiceProperties(serviceName, props, importContainer), this.generateServiceOperations(operations, importContainer));
174
- // generate EntityTypeService
175
- serviceFile.addClass({
176
- isExported: true,
177
- name: serviceName,
178
- typeParameters: ["ClientType extends ODataHttpClient"],
179
- extends: entityServiceType + `<ClientType, ${model.name}, ${editableModelName}, ${model.qName}>`,
180
- ctors: [
181
- {
182
- parameters: [
183
- { name: "client", type: "ClientType" },
184
- { name: "basePath", type: "string" },
185
- { name: "name", type: "string" },
186
- ],
187
- statements: [`super(client, basePath, name, ${qObjectName});`],
188
- },
189
- ],
190
- properties,
191
- methods,
192
- });
193
- });
194
- }
195
- generateServiceProperties(serviceName, props, importContainer) {
196
- const collectionServiceType = "CollectionService" + this.getVersionSuffix();
197
- const result = { properties: [], methods: [] };
198
- props.forEach((prop) => {
199
- if ((prop.dataType === "ModelType" /* DataTypes.ModelType */ && !prop.isCollection) || prop.dataType === "ComplexType" /* DataTypes.ComplexType */) {
200
- result.properties.push(this.generateModelProp(prop, collectionServiceType, serviceName, importContainer));
201
- result.methods.push(this.generateModelPropGetter(prop, collectionServiceType));
202
- }
203
- else if (prop.isCollection) {
204
- // collection of entity types
205
- if (prop.dataType === "ModelType" /* DataTypes.ModelType */) {
206
- const entityType = this.dataModel.getModel(prop.type);
207
- result.methods.push(this.generateRelatedServiceGetter(prop.name, entityType, importContainer, serviceName));
208
- }
209
- // collection of primitive or complex types
210
- else {
211
- result.properties.push(this.generatePrimitiveCollectionProp(prop, collectionServiceType, importContainer));
212
- result.methods.push(this.generatePrimitiveCollectionGetter(prop, collectionServiceType));
213
- }
214
- }
215
- });
216
- return result;
217
- }
218
- generateServiceOperations(operations, importContainer) {
219
- const result = { properties: [], methods: [] };
220
- operations.forEach((operation) => {
221
- result.properties.push(this.generateQOperationProp(operation));
222
- result.methods.push(this.generateMethod(operation.name, operation, importContainer));
223
- });
224
- return result;
225
- }
226
- generateModelProp(prop, collectionServiceType, serviceName, importContainer) {
227
- const complexType = this.dataModel.getComplexType(prop.type);
228
- const key = this.namingHelper.getServiceName(prop.type);
229
- let propModelType = prop.isCollection ? this.namingHelper.getCollectionServiceName(prop.type) : key;
230
- if (prop.isCollection && complexType) {
231
- const editableName = complexType.editableName;
232
- importContainer.addFromService(collectionServiceType);
233
- importContainer.addGeneratedModel(complexType.name, editableName);
234
- importContainer.addGeneratedQObject(complexType.qName, (0, processors_1.firstCharLowerCase)(complexType.qName));
235
- propModelType = `${collectionServiceType}<ClientType, ${complexType.name}, ${complexType.qName}, ${editableName}>`;
236
- }
237
- else {
238
- // don't include imports for this type
239
- if (serviceName !== key) {
240
- importContainer.addGeneratedService(key, propModelType);
241
- }
242
- propModelType = `${propModelType}<ClientType>`;
243
- }
244
- return {
245
- scope: ts_morph_1.Scope.Private,
246
- name: this.namingHelper.getPrivatePropName(prop.name),
247
- type: propModelType,
248
- hasQuestionToken: true,
249
- };
250
- }
251
- generatePrimitiveCollectionProp(prop, collectionServiceType, importContainer) {
252
- const isEnum = prop.dataType === "EnumType" /* DataTypes.EnumType */;
253
- const type = isEnum ? `EnumCollection<${prop.type}>` : `${(0, upper_case_first_1.upperCaseFirst)(prop.type)}Collection`;
254
- const qType = isEnum ? "QEnumCollection" : `Q${type}`;
255
- const collectionType = `${collectionServiceType}<ClientType, ${type}, ${qType}>`;
256
- if (!prop.qObject) {
257
- throw new Error("Illegal State: [qObject] must be provided for Collection types!");
258
- }
259
- importContainer.addFromService(collectionServiceType);
260
- importContainer.addFromQObject(prop.qObject, (0, processors_1.firstCharLowerCase)(prop.qObject));
261
- if (isEnum) {
262
- importContainer.addGeneratedModel(prop.type);
263
- importContainer.addFromQObject("EnumCollection");
264
- }
265
- else {
266
- importContainer.addFromQObject(type);
267
- }
268
- return {
269
- scope: ts_morph_1.Scope.Private,
270
- name: this.namingHelper.getPrivatePropName(prop.name),
271
- type: `${collectionType}`,
272
- hasQuestionToken: true,
273
- };
274
- }
275
- generateModelPropGetter(prop, collectionServiceType) {
276
- const complexType = this.dataModel.getComplexType(prop.type);
277
- const isComplexCollection = prop.isCollection && complexType;
278
- const type = isComplexCollection
279
- ? collectionServiceType
280
- : prop.isCollection
281
- ? this.namingHelper.getCollectionServiceName(prop.type)
282
- : this.namingHelper.getServiceName(prop.type);
283
- const typeWithGenerics = isComplexCollection
284
- ? `${collectionServiceType}<ClientType, ${complexType.name}, ${complexType.qName}, ${complexType.editableName}>`
285
- : `${type}<ClientType>`;
286
- const privateSrvProp = "this." + this.namingHelper.getPrivatePropName(prop.name);
287
- return {
288
- scope: ts_morph_1.Scope.Public,
289
- name: this.namingHelper.getRelatedServiceGetter(prop.name),
290
- returnType: typeWithGenerics,
291
- statements: [
292
- `if(!${privateSrvProp}) {`,
293
- // prettier-ignore
294
- ` ${privateSrvProp} = new ${type}(this.client, this.getPath(), "${prop.odataName}"${isComplexCollection ? `, ${(0, processors_1.firstCharLowerCase)(complexType.qName)}` : ""})`,
295
- "}",
296
- `return ${privateSrvProp}`,
297
- ],
298
- };
299
- }
300
- generatePrimitiveCollectionGetter(prop, collectionServiceType) {
301
- const propName = "this." + this.namingHelper.getPrivatePropName(prop.name);
302
- return {
303
- scope: ts_morph_1.Scope.Public,
304
- name: this.namingHelper.getRelatedServiceGetter(prop.name),
305
- statements: [
306
- `if(!${propName}) {`,
307
- // prettier-ignore
308
- ` ${propName} = new ${collectionServiceType}(this.client, this.getPath(), "${prop.odataName}", ${(0, processors_1.firstCharLowerCase)(prop.qObject)})`,
309
- "}",
310
- `return ${propName}`,
311
- ],
312
- };
313
- }
314
- generateEntityCollectionService(model, serviceFile, importContainer) {
315
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
316
- const entitySetServiceType = "EntitySetService" + this.getVersionSuffix();
317
- const editableModelName = model.editableName;
318
- const qObjectName = (0, processors_1.firstCharLowerCase)(model.qName);
319
- importContainer.addFromService(entitySetServiceType);
320
- importContainer.addGeneratedModel(model.idModelName);
321
- importContainer.addGeneratedQObject(model.qIdFunctionName);
322
- const collectionOperations = this.dataModel.getOperationTypeByBinding(`Collection(${model.name})`);
323
- const { properties, methods } = this.generateServiceOperations(collectionOperations, importContainer);
324
- serviceFile.addClass({
325
- isExported: true,
326
- name: this.namingHelper.getCollectionServiceName(model.name),
327
- typeParameters: ["ClientType extends ODataHttpClient"],
328
- extends: entitySetServiceType +
329
- `<ClientType, ${model.name}, ${editableModelName}, ${model.qName}, ${model.idModelName}>`,
330
- ctors: [
331
- {
332
- parameters: [
333
- { name: "client", type: "ClientType" },
334
- { name: "basePath", type: "string" },
335
- { name: "name", type: "string" },
336
- ],
337
- statements: [`super(client, basePath, name, ${qObjectName}, new ${model.qIdFunctionName}(name));`],
338
- },
339
- ],
340
- properties,
341
- methods,
342
- });
343
- });
344
- }
345
- generateModelServices() {
346
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
347
- // build service file for each entity, consisting of EntityTypeService & EntityCollectionService
348
- for (const model of this.dataModel.getModels()) {
349
- const serviceName = this.namingHelper.getServiceName(model.name);
350
- const serviceFile = yield this.project.createServiceFile(serviceName);
351
- const importContainer = new ImportContainer_1.ImportContainer(this.namingHelper.getFileNames());
352
- // entity type service
353
- yield this.generateEntityTypeService(model, serviceName, serviceFile, importContainer);
354
- // entity collection service
355
- yield this.generateEntityCollectionService(model, serviceFile, importContainer);
356
- serviceFile.addImportDeclarations(importContainer.getImportDeclarations(true));
357
- }
358
- // build service file for complex types
359
- for (const model of this.dataModel.getComplexTypes()) {
360
- const serviceName = this.namingHelper.getServiceName(model.name);
361
- const serviceFile = yield this.project.createServiceFile(serviceName);
362
- const importContainer = new ImportContainer_1.ImportContainer(this.namingHelper.getFileNames());
363
- // entity type service
364
- yield this.generateEntityTypeService(model, serviceName, serviceFile, importContainer);
365
- serviceFile.addImportDeclarations(importContainer.getImportDeclarations(true));
366
- }
367
- });
368
- }
369
- generateMethod(name, operation, importContainer) {
370
- var _a, _b;
371
- const isFunc = operation.type === "Function" /* OperationTypes.Function */;
372
- const odataType = ((_a = operation.returnType) === null || _a === void 0 ? void 0 : _a.isCollection)
373
- ? RESPONSE_TYPES.collection + this.getVersionSuffix()
374
- : ((_b = operation.returnType) === null || _b === void 0 ? void 0 : _b.dataType) === "PrimitiveType" /* DataTypes.PrimitiveType */
375
- ? RESPONSE_TYPES.value + this.getVersionSuffix()
376
- : RESPONSE_TYPES.model + this.getVersionSuffix();
377
- const returnType = operation.returnType;
378
- const requestConfigParam = {
379
- name: "requestConfig",
380
- hasQuestionToken: true,
381
- type: "ODataHttpClientConfig<ClientType>",
382
- };
383
- const hasParams = operation.parameters.length > 0;
384
- // importing dependencies
385
- importContainer.addFromClientApi("ODataHttpClientConfig", "ODataResponse");
386
- importContainer.addFromCore(odataType);
387
- if (returnType === null || returnType === void 0 ? void 0 : returnType.type) {
388
- if (["EnumType" /* DataTypes.EnumType */, "ModelType" /* DataTypes.ModelType */, "ComplexType" /* DataTypes.ComplexType */].includes(returnType.dataType)) {
389
- importContainer.addGeneratedModel(returnType.type);
390
- }
391
- }
392
- importContainer.addGeneratedQObject(operation.qName);
393
- if (hasParams) {
394
- importContainer.addGeneratedModel(operation.paramsModelName);
395
- }
396
- const qOpProp = "this." + this.namingHelper.getPrivatePropName(operation.qName);
397
- return {
398
- scope: ts_morph_1.Scope.Public,
399
- isAsync: true,
400
- name,
401
- parameters: hasParams
402
- ? [{ name: "params", type: operation.paramsModelName }, requestConfigParam]
403
- : [requestConfigParam],
404
- returnType: `ODataResponse<${odataType}<${(returnType === null || returnType === void 0 ? void 0 : returnType.type) || "void"}>>`,
405
- statements: [
406
- `if(!${qOpProp}) {`,
407
- ` ${qOpProp} = new ${operation.qName}()`,
408
- "}",
409
- `const url = this.addFullPath(${qOpProp}.buildUrl(${isFunc && hasParams ? "params" : ""}));`,
410
- `${returnType ? "const response = await " : "return"} this.client.${!isFunc
411
- ? // actions: since V4
412
- `post(url, ${hasParams ? `${qOpProp}.convertUserParams(params)` : "{}"}, ${requestConfigParam.name})`
413
- : operation.usePost
414
- ? // V2 POST => BUT values are still query params, they are not part of the request body
415
- `post(url, undefined, ${requestConfigParam.name})`
416
- : // functions: since V2
417
- `get(url, ${requestConfigParam.name})`};`,
418
- returnType ? `return ${qOpProp}.convertResponse(response);` : "",
419
- ],
420
- };
421
- }
422
- }
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateServices = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const odata_core_1 = require("@odata2ts/odata-core");
6
+ const deepmerge_1 = tslib_1.__importDefault(require("deepmerge"));
7
+ const ts_morph_1 = require("ts-morph");
8
+ const upper_case_first_1 = require("upper-case-first");
9
+ const processors_1 = require("xml2js/lib/processors");
10
+ const ImportContainer_1 = require("./ImportContainer");
11
+ const ROOT_SERVICE = "ODataService";
12
+ const RESPONSE_TYPES = {
13
+ collection: "ODataCollectionResponse",
14
+ model: "ODataModelResponse",
15
+ value: "ODataValueResponse",
16
+ };
17
+ function generateServices(dataModel, project, version, namingHelper) {
18
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
19
+ const generator = new ServiceGenerator(dataModel, project, version, namingHelper);
20
+ return generator.generate();
21
+ });
22
+ }
23
+ exports.generateServices = generateServices;
24
+ class ServiceGenerator {
25
+ constructor(dataModel, project, version, namingHelper) {
26
+ this.dataModel = dataModel;
27
+ this.project = project;
28
+ this.version = version;
29
+ this.namingHelper = namingHelper;
30
+ this.generateQOperationProp = (operation) => {
31
+ return {
32
+ scope: ts_morph_1.Scope.Private,
33
+ name: this.namingHelper.getPrivatePropName(operation.qName),
34
+ type: operation.qName,
35
+ hasQuestionToken: true,
36
+ };
37
+ };
38
+ }
39
+ generate() {
40
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
41
+ const sourceFile = yield this.project.createMainServiceFile();
42
+ const serviceName = this.namingHelper.getMainServiceName();
43
+ const container = this.dataModel.getEntityContainer();
44
+ const unboundOperations = [...Object.values(container.functions), ...Object.values(container.actions)];
45
+ yield this.generateModelServices();
46
+ const importContainer = new ImportContainer_1.ImportContainer(this.namingHelper.getFileNames());
47
+ importContainer.addFromClientApi("ODataHttpClient");
48
+ importContainer.addFromService(ROOT_SERVICE);
49
+ const { properties, methods } = (0, deepmerge_1.default)(this.generateMainServiceProperties(container, importContainer), this.generateMainServiceOperations(unboundOperations, importContainer));
50
+ sourceFile.addClass({
51
+ isExported: true,
52
+ name: serviceName,
53
+ typeParameters: ["ClientType extends ODataHttpClient"],
54
+ extends: `${ROOT_SERVICE}<ClientType>`,
55
+ properties /*: [
56
+ {
57
+ scope: Scope.Private,
58
+ name: this.namingHelper.getPrivatePropName("name"),
59
+ type: "string",
60
+ initializer: `"${this.namingHelper.getODataServiceName()}"`,
61
+ },
62
+ ]*/,
63
+ methods,
64
+ });
65
+ sourceFile.addImportDeclarations(importContainer.getImportDeclarations(false));
66
+ });
67
+ }
68
+ generateMainServiceProperties(container, importContainer) {
69
+ const result = { properties: [], methods: [] };
70
+ Object.values(container.entitySets).forEach(({ name, entityType }) => {
71
+ result.methods.push(this.generateRelatedServiceGetter(name, entityType, importContainer));
72
+ });
73
+ Object.values(container.singletons).forEach((singleton) => {
74
+ result.properties.push(this.generateSingletonProp(singleton, importContainer));
75
+ result.methods.push(this.generateSingletonGetter(singleton));
76
+ });
77
+ return result;
78
+ }
79
+ generateMainServiceOperations(ops, importContainer) {
80
+ const result = { properties: [], methods: [] };
81
+ ops.forEach(({ operation, name }) => {
82
+ result.properties.push(this.generateQOperationProp(operation));
83
+ result.methods.push(this.generateMethod(name, operation, importContainer));
84
+ });
85
+ return result;
86
+ }
87
+ generateRelatedServiceGetter(propName, entityType, importContainer, currentServiceName) {
88
+ const idName = entityType.idModelName;
89
+ const idFunctionName = entityType.qIdFunctionName;
90
+ const serviceName = this.namingHelper.getServiceName(entityType.name);
91
+ const collectionName = this.namingHelper.getCollectionServiceName(entityType.name);
92
+ importContainer.addFromClientApi("ODataHttpClient");
93
+ importContainer.addGeneratedModel(idName);
94
+ importContainer.addGeneratedQObject(idFunctionName);
95
+ // make sure to not falsely import self-referential stuff
96
+ if (!currentServiceName || currentServiceName !== serviceName) {
97
+ importContainer.addGeneratedService(serviceName, collectionName, serviceName);
98
+ }
99
+ return {
100
+ scope: ts_morph_1.Scope.Public,
101
+ name: this.namingHelper.getRelatedServiceGetter(propName),
102
+ parameters: [
103
+ {
104
+ name: "id",
105
+ type: `${idName} | undefined`,
106
+ hasQuestionToken: true,
107
+ },
108
+ ],
109
+ overloads: [
110
+ {
111
+ parameters: [],
112
+ returnType: `${collectionName}<ClientType>`,
113
+ },
114
+ {
115
+ parameters: [
116
+ {
117
+ name: "id",
118
+ type: idName,
119
+ },
120
+ ],
121
+ returnType: `${serviceName}<ClientType>`,
122
+ },
123
+ ],
124
+ statements: [
125
+ `const fieldName = "${propName}";`,
126
+ 'return typeof id === "undefined" || id === null',
127
+ `? new ${collectionName}(this.client, this.getPath(), fieldName)`,
128
+ `: new ${serviceName}(this.client, this.getPath(), new ${idFunctionName}(fieldName).buildUrl(id));`,
129
+ ],
130
+ };
131
+ }
132
+ generateSingletonProp(singleton, importContainer) {
133
+ const { name, entityType } = singleton;
134
+ const type = this.namingHelper.getServiceName(entityType.name);
135
+ importContainer.addGeneratedService(this.namingHelper.getServiceName(entityType.name), type);
136
+ return {
137
+ scope: ts_morph_1.Scope.Private,
138
+ name: this.namingHelper.getPrivatePropName(name),
139
+ type: `${type}<ClientType>`,
140
+ hasQuestionToken: true,
141
+ };
142
+ }
143
+ generateSingletonGetter(singleton) {
144
+ const { name, odataName, entityType } = singleton;
145
+ const propName = "this." + this.namingHelper.getPrivatePropName(name);
146
+ const serviceType = this.namingHelper.getServiceName(entityType.name);
147
+ return {
148
+ scope: ts_morph_1.Scope.Public,
149
+ name: this.namingHelper.getRelatedServiceGetter(name),
150
+ statements: [
151
+ `if(!${propName}) {`,
152
+ // prettier-ignore
153
+ ` ${propName} = new ${serviceType}(this.client, this.getPath(), "${odataName}")`,
154
+ "}",
155
+ `return ${propName}`,
156
+ ],
157
+ };
158
+ }
159
+ getVersionSuffix() {
160
+ return this.version === odata_core_1.ODataVersions.V2 ? "V2" : "V4";
161
+ }
162
+ generateEntityTypeService(model, serviceName, serviceFile, importContainer) {
163
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
164
+ const entityServiceType = "EntityTypeService" + this.getVersionSuffix();
165
+ const editableModelName = model.editableName;
166
+ const qObjectName = (0, processors_1.firstCharLowerCase)(model.qName);
167
+ const operations = this.dataModel.getOperationTypeByBinding(model.name);
168
+ const props = [...model.baseProps, ...model.props];
169
+ importContainer.addFromService(entityServiceType);
170
+ importContainer.addFromClientApi("ODataHttpClient");
171
+ importContainer.addGeneratedModel(model.name, editableModelName);
172
+ importContainer.addGeneratedQObject(model.qName, qObjectName);
173
+ const { properties, methods } = (0, deepmerge_1.default)(this.generateServiceProperties(serviceName, props, importContainer), this.generateServiceOperations(operations, importContainer));
174
+ // generate EntityTypeService
175
+ serviceFile.addClass({
176
+ isExported: true,
177
+ name: serviceName,
178
+ typeParameters: ["ClientType extends ODataHttpClient"],
179
+ extends: entityServiceType + `<ClientType, ${model.name}, ${editableModelName}, ${model.qName}>`,
180
+ ctors: [
181
+ {
182
+ parameters: [
183
+ { name: "client", type: "ClientType" },
184
+ { name: "basePath", type: "string" },
185
+ { name: "name", type: "string" },
186
+ ],
187
+ statements: [`super(client, basePath, name, ${qObjectName});`],
188
+ },
189
+ ],
190
+ properties,
191
+ methods,
192
+ });
193
+ });
194
+ }
195
+ generateServiceProperties(serviceName, props, importContainer) {
196
+ const collectionServiceType = "CollectionService" + this.getVersionSuffix();
197
+ const result = { properties: [], methods: [] };
198
+ props.forEach((prop) => {
199
+ if ((prop.dataType === "ModelType" /* DataTypes.ModelType */ && !prop.isCollection) || prop.dataType === "ComplexType" /* DataTypes.ComplexType */) {
200
+ result.properties.push(this.generateModelProp(prop, collectionServiceType, serviceName, importContainer));
201
+ result.methods.push(this.generateModelPropGetter(prop, collectionServiceType));
202
+ }
203
+ else if (prop.isCollection) {
204
+ // collection of entity types
205
+ if (prop.dataType === "ModelType" /* DataTypes.ModelType */) {
206
+ const entityType = this.dataModel.getModel(prop.type);
207
+ result.methods.push(this.generateRelatedServiceGetter(prop.name, entityType, importContainer, serviceName));
208
+ }
209
+ // collection of primitive or complex types
210
+ else {
211
+ result.properties.push(this.generatePrimitiveCollectionProp(prop, collectionServiceType, importContainer));
212
+ result.methods.push(this.generatePrimitiveCollectionGetter(prop, collectionServiceType));
213
+ }
214
+ }
215
+ });
216
+ return result;
217
+ }
218
+ generateServiceOperations(operations, importContainer) {
219
+ const result = { properties: [], methods: [] };
220
+ operations.forEach((operation) => {
221
+ result.properties.push(this.generateQOperationProp(operation));
222
+ result.methods.push(this.generateMethod(operation.name, operation, importContainer));
223
+ });
224
+ return result;
225
+ }
226
+ generateModelProp(prop, collectionServiceType, serviceName, importContainer) {
227
+ const complexType = this.dataModel.getComplexType(prop.type);
228
+ const key = this.namingHelper.getServiceName(prop.type);
229
+ let propModelType = prop.isCollection ? this.namingHelper.getCollectionServiceName(prop.type) : key;
230
+ if (prop.isCollection && complexType) {
231
+ const editableName = complexType.editableName;
232
+ importContainer.addFromService(collectionServiceType);
233
+ importContainer.addGeneratedModel(complexType.name, editableName);
234
+ importContainer.addGeneratedQObject(complexType.qName, (0, processors_1.firstCharLowerCase)(complexType.qName));
235
+ propModelType = `${collectionServiceType}<ClientType, ${complexType.name}, ${complexType.qName}, ${editableName}>`;
236
+ }
237
+ else {
238
+ // don't include imports for this type
239
+ if (serviceName !== key) {
240
+ importContainer.addGeneratedService(key, propModelType);
241
+ }
242
+ propModelType = `${propModelType}<ClientType>`;
243
+ }
244
+ return {
245
+ scope: ts_morph_1.Scope.Private,
246
+ name: this.namingHelper.getPrivatePropName(prop.name),
247
+ type: propModelType,
248
+ hasQuestionToken: true,
249
+ };
250
+ }
251
+ generatePrimitiveCollectionProp(prop, collectionServiceType, importContainer) {
252
+ const isEnum = prop.dataType === "EnumType" /* DataTypes.EnumType */;
253
+ const type = isEnum ? `EnumCollection<${prop.type}>` : `${(0, upper_case_first_1.upperCaseFirst)(prop.type)}Collection`;
254
+ const qType = isEnum ? "QEnumCollection" : `Q${type}`;
255
+ const collectionType = `${collectionServiceType}<ClientType, ${type}, ${qType}>`;
256
+ if (!prop.qObject) {
257
+ throw new Error("Illegal State: [qObject] must be provided for Collection types!");
258
+ }
259
+ importContainer.addFromService(collectionServiceType);
260
+ importContainer.addFromQObject(prop.qObject, (0, processors_1.firstCharLowerCase)(prop.qObject));
261
+ if (isEnum) {
262
+ importContainer.addGeneratedModel(prop.type);
263
+ importContainer.addFromQObject("EnumCollection");
264
+ }
265
+ else {
266
+ importContainer.addFromQObject(type);
267
+ }
268
+ return {
269
+ scope: ts_morph_1.Scope.Private,
270
+ name: this.namingHelper.getPrivatePropName(prop.name),
271
+ type: `${collectionType}`,
272
+ hasQuestionToken: true,
273
+ };
274
+ }
275
+ generateModelPropGetter(prop, collectionServiceType) {
276
+ const complexType = this.dataModel.getComplexType(prop.type);
277
+ const isComplexCollection = prop.isCollection && complexType;
278
+ const type = isComplexCollection
279
+ ? collectionServiceType
280
+ : prop.isCollection
281
+ ? this.namingHelper.getCollectionServiceName(prop.type)
282
+ : this.namingHelper.getServiceName(prop.type);
283
+ const typeWithGenerics = isComplexCollection
284
+ ? `${collectionServiceType}<ClientType, ${complexType.name}, ${complexType.qName}, ${complexType.editableName}>`
285
+ : `${type}<ClientType>`;
286
+ const privateSrvProp = "this." + this.namingHelper.getPrivatePropName(prop.name);
287
+ return {
288
+ scope: ts_morph_1.Scope.Public,
289
+ name: this.namingHelper.getRelatedServiceGetter(prop.name),
290
+ returnType: typeWithGenerics,
291
+ statements: [
292
+ `if(!${privateSrvProp}) {`,
293
+ // prettier-ignore
294
+ ` ${privateSrvProp} = new ${type}(this.client, this.getPath(), "${prop.odataName}"${isComplexCollection ? `, ${(0, processors_1.firstCharLowerCase)(complexType.qName)}` : ""})`,
295
+ "}",
296
+ `return ${privateSrvProp}`,
297
+ ],
298
+ };
299
+ }
300
+ generatePrimitiveCollectionGetter(prop, collectionServiceType) {
301
+ const propName = "this." + this.namingHelper.getPrivatePropName(prop.name);
302
+ return {
303
+ scope: ts_morph_1.Scope.Public,
304
+ name: this.namingHelper.getRelatedServiceGetter(prop.name),
305
+ statements: [
306
+ `if(!${propName}) {`,
307
+ // prettier-ignore
308
+ ` ${propName} = new ${collectionServiceType}(this.client, this.getPath(), "${prop.odataName}", ${(0, processors_1.firstCharLowerCase)(prop.qObject)})`,
309
+ "}",
310
+ `return ${propName}`,
311
+ ],
312
+ };
313
+ }
314
+ generateEntityCollectionService(model, serviceFile, importContainer) {
315
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
316
+ const entitySetServiceType = "EntitySetService" + this.getVersionSuffix();
317
+ const editableModelName = model.editableName;
318
+ const qObjectName = (0, processors_1.firstCharLowerCase)(model.qName);
319
+ importContainer.addFromService(entitySetServiceType);
320
+ importContainer.addGeneratedModel(model.idModelName);
321
+ importContainer.addGeneratedQObject(model.qIdFunctionName);
322
+ const collectionOperations = this.dataModel.getOperationTypeByBinding(`Collection(${model.name})`);
323
+ const { properties, methods } = this.generateServiceOperations(collectionOperations, importContainer);
324
+ serviceFile.addClass({
325
+ isExported: true,
326
+ name: this.namingHelper.getCollectionServiceName(model.name),
327
+ typeParameters: ["ClientType extends ODataHttpClient"],
328
+ extends: entitySetServiceType +
329
+ `<ClientType, ${model.name}, ${editableModelName}, ${model.qName}, ${model.idModelName}>`,
330
+ ctors: [
331
+ {
332
+ parameters: [
333
+ { name: "client", type: "ClientType" },
334
+ { name: "basePath", type: "string" },
335
+ { name: "name", type: "string" },
336
+ ],
337
+ statements: [`super(client, basePath, name, ${qObjectName}, new ${model.qIdFunctionName}(name));`],
338
+ },
339
+ ],
340
+ properties,
341
+ methods,
342
+ });
343
+ });
344
+ }
345
+ generateModelServices() {
346
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
347
+ // build service file for each entity, consisting of EntityTypeService & EntityCollectionService
348
+ for (const model of this.dataModel.getModels()) {
349
+ const serviceName = this.namingHelper.getServiceName(model.name);
350
+ const serviceFile = yield this.project.createServiceFile(serviceName);
351
+ const importContainer = new ImportContainer_1.ImportContainer(this.namingHelper.getFileNames());
352
+ // entity type service
353
+ yield this.generateEntityTypeService(model, serviceName, serviceFile, importContainer);
354
+ // entity collection service
355
+ yield this.generateEntityCollectionService(model, serviceFile, importContainer);
356
+ serviceFile.addImportDeclarations(importContainer.getImportDeclarations(true));
357
+ }
358
+ // build service file for complex types
359
+ for (const model of this.dataModel.getComplexTypes()) {
360
+ const serviceName = this.namingHelper.getServiceName(model.name);
361
+ const serviceFile = yield this.project.createServiceFile(serviceName);
362
+ const importContainer = new ImportContainer_1.ImportContainer(this.namingHelper.getFileNames());
363
+ // entity type service
364
+ yield this.generateEntityTypeService(model, serviceName, serviceFile, importContainer);
365
+ serviceFile.addImportDeclarations(importContainer.getImportDeclarations(true));
366
+ }
367
+ });
368
+ }
369
+ generateMethod(name, operation, importContainer) {
370
+ var _a, _b;
371
+ const isFunc = operation.type === "Function" /* OperationTypes.Function */;
372
+ const odataType = ((_a = operation.returnType) === null || _a === void 0 ? void 0 : _a.isCollection)
373
+ ? RESPONSE_TYPES.collection + this.getVersionSuffix()
374
+ : ((_b = operation.returnType) === null || _b === void 0 ? void 0 : _b.dataType) === "PrimitiveType" /* DataTypes.PrimitiveType */
375
+ ? RESPONSE_TYPES.value + this.getVersionSuffix()
376
+ : RESPONSE_TYPES.model + this.getVersionSuffix();
377
+ const returnType = operation.returnType;
378
+ const requestConfigParam = {
379
+ name: "requestConfig",
380
+ hasQuestionToken: true,
381
+ type: "ODataHttpClientConfig<ClientType>",
382
+ };
383
+ const hasParams = operation.parameters.length > 0;
384
+ // importing dependencies
385
+ importContainer.addFromClientApi("ODataHttpClientConfig", "ODataResponse");
386
+ importContainer.addFromCore(odataType);
387
+ if (returnType === null || returnType === void 0 ? void 0 : returnType.type) {
388
+ if (["EnumType" /* DataTypes.EnumType */, "ModelType" /* DataTypes.ModelType */, "ComplexType" /* DataTypes.ComplexType */].includes(returnType.dataType)) {
389
+ importContainer.addGeneratedModel(returnType.type);
390
+ }
391
+ }
392
+ importContainer.addGeneratedQObject(operation.qName);
393
+ if (hasParams) {
394
+ importContainer.addGeneratedModel(operation.paramsModelName);
395
+ }
396
+ const qOpProp = "this." + this.namingHelper.getPrivatePropName(operation.qName);
397
+ return {
398
+ scope: ts_morph_1.Scope.Public,
399
+ isAsync: true,
400
+ name,
401
+ parameters: hasParams
402
+ ? [{ name: "params", type: operation.paramsModelName }, requestConfigParam]
403
+ : [requestConfigParam],
404
+ returnType: `ODataResponse<${odataType}<${(returnType === null || returnType === void 0 ? void 0 : returnType.type) || "void"}>>`,
405
+ statements: [
406
+ `if(!${qOpProp}) {`,
407
+ ` ${qOpProp} = new ${operation.qName}()`,
408
+ "}",
409
+ `const url = this.addFullPath(${qOpProp}.buildUrl(${isFunc && hasParams ? "params" : ""}));`,
410
+ `${returnType ? "const response = await " : "return"} this.client.${!isFunc
411
+ ? // actions: since V4
412
+ `post(url, ${hasParams ? `${qOpProp}.convertUserParams(params)` : "{}"}, ${requestConfigParam.name})`
413
+ : operation.usePost
414
+ ? // V2 POST => BUT values are still query params, they are not part of the request body
415
+ `post(url, undefined, ${requestConfigParam.name})`
416
+ : // functions: since V2
417
+ `get(url, ${requestConfigParam.name})`};`,
418
+ returnType ? `return ${qOpProp}.convertResponse(response);` : "",
419
+ ],
420
+ };
421
+ }
422
+ }
423
423
  //# sourceMappingURL=ServiceGenerator.js.map