@microsoft/power-apps-cli 0.6.4 → 0.6.7
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/lib/ArgumentProvider.d.ts +2 -2
- package/lib/ArgumentProvider.d.ts.map +1 -1
- package/lib/ArgumentProvider.js +8 -6
- package/lib/ArgumentProvider.js.map +1 -1
- package/lib/Cli.js +1 -1
- package/lib/Cli.js.map +1 -1
- package/lib/Logger/CliLogger.d.ts +7 -1
- package/lib/Logger/CliLogger.d.ts.map +1 -1
- package/lib/Logger/CliLogger.js +45 -24
- package/lib/Logger/CliLogger.js.map +1 -1
- package/lib/Verbs/Push.d.ts.map +1 -1
- package/lib/Verbs/Push.js +4 -3
- package/lib/Verbs/Push.js.map +1 -1
- package/lib/Verbs/Run.d.ts.map +1 -1
- package/lib/Verbs/Run.js +9 -8
- package/lib/Verbs/Run.js.map +1 -1
- package/lib/Verbs/VerbConstants.d.ts +19 -1
- package/lib/Verbs/VerbConstants.d.ts.map +1 -1
- package/lib/Verbs/VerbConstants.js +23 -5
- package/lib/Verbs/VerbConstants.js.map +1 -1
- package/lib/__tests__/UnitTests/CliLogger.test.js +34 -24
- package/lib/__tests__/UnitTests/CliLogger.test.js.map +1 -1
- package/lib/__tests__/UnitTests/ListEnvironmentVariables.spec.js +4 -4
- package/lib/__tests__/UnitTests/ListEnvironmentVariables.spec.js.map +1 -1
- package/lib/__tests__/mocks/CliLogger.mocks.d.ts +7 -0
- package/lib/__tests__/mocks/CliLogger.mocks.d.ts.map +1 -0
- package/lib/__tests__/mocks/CliLogger.mocks.js +22 -0
- package/lib/__tests__/mocks/CliLogger.mocks.js.map +1 -0
- package/lib-cjs/ArgumentProvider.d.ts +2 -2
- package/lib-cjs/ArgumentProvider.d.ts.map +1 -1
- package/lib-cjs/ArgumentProvider.js +10 -6
- package/lib-cjs/ArgumentProvider.js.map +1 -1
- package/lib-cjs/Cli.js +1 -1
- package/lib-cjs/Cli.js.map +1 -1
- package/lib-cjs/Logger/CliLogger.d.ts +7 -1
- package/lib-cjs/Logger/CliLogger.d.ts.map +1 -1
- package/lib-cjs/Logger/CliLogger.js +58 -30
- package/lib-cjs/Logger/CliLogger.js.map +1 -1
- package/lib-cjs/Verbs/Push.d.ts.map +1 -1
- package/lib-cjs/Verbs/Push.js +4 -3
- package/lib-cjs/Verbs/Push.js.map +1 -1
- package/lib-cjs/Verbs/Run.d.ts.map +1 -1
- package/lib-cjs/Verbs/Run.js +9 -8
- package/lib-cjs/Verbs/Run.js.map +1 -1
- package/lib-cjs/Verbs/VerbConstants.d.ts +19 -1
- package/lib-cjs/Verbs/VerbConstants.d.ts.map +1 -1
- package/lib-cjs/Verbs/VerbConstants.js +24 -6
- package/lib-cjs/Verbs/VerbConstants.js.map +1 -1
- package/lib-cjs/__tests__/UnitTests/CliLogger.test.js +54 -24
- package/lib-cjs/__tests__/UnitTests/CliLogger.test.js.map +1 -1
- package/lib-cjs/__tests__/UnitTests/ListEnvironmentVariables.spec.js +4 -4
- package/lib-cjs/__tests__/UnitTests/ListEnvironmentVariables.spec.js.map +1 -1
- package/lib-cjs/__tests__/mocks/CliLogger.mocks.d.ts +6 -0
- package/lib-cjs/__tests__/mocks/CliLogger.mocks.d.ts.map +1 -0
- package/lib-cjs/__tests__/mocks/CliLogger.mocks.js +26 -0
- package/lib-cjs/__tests__/mocks/CliLogger.mocks.js.map +1 -0
- package/node_modules/@microsoft/powerapps-data/package.json +2 -2
- package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/AddDataSource.js +8 -3
- package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/AddDataSource.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/DeleteDataSource.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/DeleteDataSource.js +7 -2
- package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/DeleteDataSource.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/PushApp.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/PushApp.js +7 -3
- package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/PushApp.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.d.ts +16 -300
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.js +2280 -2320
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/AzureDevOpsModelServiceGenerator.spec.js +11 -3
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/AzureDevOpsModelServiceGenerator.spec.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/DeleteDataSource.spec.js +3 -5
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/DeleteDataSource.spec.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/ModelServiceGenerator.spec.js +95 -20
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/ModelServiceGenerator.spec.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/arrayTypeGeneration.spec.js +6 -2
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/arrayTypeGeneration.spec.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/AddDataSource.js +7 -2
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/AddDataSource.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/DeleteDataSource.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/DeleteDataSource.js +6 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/DeleteDataSource.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/PushApp.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/PushApp.js +14 -10
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/PushApp.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/modelServiceGenerator.d.ts +16 -300
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/modelServiceGenerator.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/modelServiceGenerator.js +2801 -2853
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/modelServiceGenerator.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/AzureDevOpsModelServiceGenerator.spec.js +10 -2
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/AzureDevOpsModelServiceGenerator.spec.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/DeleteDataSource.spec.js +2 -4
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/DeleteDataSource.spec.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/ModelServiceGenerator.spec.js +94 -19
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/ModelServiceGenerator.spec.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/arrayTypeGeneration.spec.js +5 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/arrayTypeGeneration.spec.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/package.json +2 -2
- package/node_modules/@pa-client/powerapps-player-services/lib/Services/PlayerLaunchService/PlayerLaunchService.types.d.ts +12 -0
- package/node_modules/@pa-client/powerapps-player-services/lib/Services/PlayerLaunchService/PlayerLaunchService.types.d.ts.map +1 -1
- package/node_modules/@pa-client/powerapps-player-services/lib/index.d.ts +2 -2
- package/node_modules/@pa-client/powerapps-player-services/lib/index.d.ts.map +1 -1
- package/node_modules/@pa-client/powerapps-player-services/lib/index.js +1 -1
- package/node_modules/@pa-client/powerapps-player-services/lib/index.js.map +1 -1
- package/node_modules/@pa-client/powerapps-player-services/lib-cjs/Services/PlayerLaunchService/PlayerLaunchService.types.d.ts +12 -0
- package/node_modules/@pa-client/powerapps-player-services/lib-cjs/Services/PlayerLaunchService/PlayerLaunchService.types.d.ts.map +1 -1
- package/node_modules/@pa-client/powerapps-player-services/lib-cjs/index.d.ts +2 -2
- package/node_modules/@pa-client/powerapps-player-services/lib-cjs/index.d.ts.map +1 -1
- package/node_modules/@pa-client/powerapps-player-services/lib-cjs/index.js +8 -1
- package/node_modules/@pa-client/powerapps-player-services/lib-cjs/index.js.map +1 -1
- package/node_modules/@pa-client/powerapps-player-services/package.json +1 -1
- package/package.json +4 -4
- package/readme.md +286 -1
package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
3
3
|
*/
|
|
4
|
-
var _a;
|
|
5
4
|
import * as path from 'path';
|
|
6
5
|
import { ParameterPropertyInfo } from '../Common/parameterPropertyInfo';
|
|
7
6
|
import { getVfs } from '../VfsManager';
|
|
@@ -10,7 +9,7 @@ import { extractModelName, formatPropertyName, getServiceName, isValidPropertyNa
|
|
|
10
9
|
/**
|
|
11
10
|
* TypeScript type constants
|
|
12
11
|
*/
|
|
13
|
-
|
|
12
|
+
const TypeScriptTypes = {
|
|
14
13
|
STRING: 'string',
|
|
15
14
|
NUMBER: 'number',
|
|
16
15
|
BOOLEAN: 'boolean',
|
|
@@ -18,2485 +17,2446 @@ export const TypeScriptTypes = {
|
|
|
18
17
|
UNKNOWN: 'unknown',
|
|
19
18
|
OBJECT: 'Record<string, unknown>',
|
|
20
19
|
};
|
|
20
|
+
// Configuration constants for output folders
|
|
21
|
+
const GENERATED_FOLDER = 'generated';
|
|
22
|
+
const MODELS_FOLDER = `${GENERATED_FOLDER}/models`;
|
|
23
|
+
const SERVICES_FOLDER = `${GENERATED_FOLDER}/services`;
|
|
24
|
+
const RELATIVE_MODEL_PATH = '../models';
|
|
25
|
+
let _schemaFolderPath;
|
|
26
|
+
let _outputDir;
|
|
21
27
|
/*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
28
|
+
* The main entry point for the model service generator.
|
|
29
|
+
* It processes the schema files in the specified folder and generates TypeScript models and services.
|
|
25
30
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const scenario = logger.trackScenario('ModelServiceGenerator.Run', { args });
|
|
34
|
-
try {
|
|
35
|
-
const vfs = getVfs();
|
|
36
|
-
const { isValid, schemaFolderPath, outputDir, schemaFilePath } = this.validateArguments(args);
|
|
37
|
-
if (!isValid || !schemaFolderPath || !outputDir) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
_a._schemaFolderPath = schemaFolderPath;
|
|
41
|
-
_a._outputDir = outputDir;
|
|
42
|
-
await this.ensureOutputDirectoryExists(outputDir, vfs);
|
|
43
|
-
let schemaFiles = [];
|
|
44
|
-
if (schemaFilePath) {
|
|
45
|
-
schemaFiles = [schemaFilePath];
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
schemaFiles = await getAllJsonFiles(schemaFolderPath);
|
|
49
|
-
}
|
|
50
|
-
for (const schemaPath of schemaFiles) {
|
|
51
|
-
try {
|
|
52
|
-
await this.processSchema(schemaPath, outputDir, vfs);
|
|
53
|
-
}
|
|
54
|
-
catch (ex) {
|
|
55
|
-
const err = ex;
|
|
56
|
-
throw new Error(`Error processing schema at path '${schemaPath}': ${err.message}`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
// Generate index.ts file
|
|
60
|
-
await this.generateIndexFile(outputDir, vfs);
|
|
61
|
-
scenario.complete();
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
scenario.failure({ error });
|
|
65
|
-
}
|
|
66
|
-
finally {
|
|
67
|
-
_a._schemaFolderPath = undefined;
|
|
68
|
-
_a._outputDir = undefined;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* The validateArguments method validates the command line arguments.
|
|
73
|
-
* It checks if the correct number of arguments is provided and returns an object containing their values.
|
|
74
|
-
* It returns {isValid, schemaFolderPath, outputDir}.
|
|
75
|
-
*/
|
|
76
|
-
static validateArguments(args) {
|
|
77
|
-
if (args.length < 2) {
|
|
78
|
-
return { isValid: false };
|
|
79
|
-
}
|
|
80
|
-
return {
|
|
81
|
-
isValid: true,
|
|
82
|
-
schemaFolderPath: args[0],
|
|
83
|
-
outputDir: args[1],
|
|
84
|
-
schemaFilePath: args[2], // Optional third argument for single schema file
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
/*
|
|
88
|
-
* The EnsureOutputDirectoryExists method ensures that the output directory exists.
|
|
89
|
-
* It creates the directory if it does not exist.
|
|
90
|
-
*/
|
|
91
|
-
static async ensureOutputDirectoryExists(outputDir, vfs) {
|
|
92
|
-
if (!(await vfs.exists(outputDir))) {
|
|
93
|
-
await vfs.mkdir(outputDir, { recursive: true });
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* The processSchema method processes a single schema file.
|
|
98
|
-
* It generates TypeScript models and services based on the schema.
|
|
99
|
-
*/
|
|
100
|
-
static async processSchema(schemaPath, outputDir, vfs) {
|
|
101
|
-
// Destructure result from initializeSchemaProcessing (returns: schema, schemaJson, outputDir, modelName, dataSourceName)
|
|
102
|
-
const { schema, schemaJson, outputDir: processedOutputDir, modelName, dataSourceName, } = await this.initializeSchemaProcessing(schemaPath, outputDir, vfs);
|
|
103
|
-
if (this.isSwaggerSchema(schema)) {
|
|
104
|
-
await this.handleSwaggerSchema(modelName, dataSourceName, schema, processedOutputDir, vfs);
|
|
105
|
-
}
|
|
106
|
-
else if (isSqlStoredProcedure(schema)) {
|
|
107
|
-
await this.handleSqlStoredProcedure(modelName, dataSourceName, schema, schemaJson, processedOutputDir, vfs);
|
|
31
|
+
export async function generateModelService(options) {
|
|
32
|
+
const { logger, schemaFolderPath, codeGenPath, schemaFilePath } = options;
|
|
33
|
+
const scenario = logger.trackScenario('ModelServiceGenerator.GenerateModelService', options);
|
|
34
|
+
try {
|
|
35
|
+
const vfs = getVfs();
|
|
36
|
+
if (!schemaFolderPath || !codeGenPath) {
|
|
37
|
+
return;
|
|
108
38
|
}
|
|
109
|
-
|
|
110
|
-
|
|
39
|
+
_schemaFolderPath = schemaFolderPath;
|
|
40
|
+
_outputDir = codeGenPath;
|
|
41
|
+
await ensureOutputDirectoryExists(codeGenPath, vfs);
|
|
42
|
+
let schemaFiles = [];
|
|
43
|
+
if (schemaFilePath) {
|
|
44
|
+
schemaFiles = [schemaFilePath];
|
|
111
45
|
}
|
|
112
46
|
else {
|
|
113
|
-
await
|
|
47
|
+
schemaFiles = await getAllJsonFiles(schemaFolderPath);
|
|
48
|
+
}
|
|
49
|
+
for (const schemaPath of schemaFiles) {
|
|
50
|
+
try {
|
|
51
|
+
await processSchema(schemaPath, codeGenPath, vfs);
|
|
52
|
+
}
|
|
53
|
+
catch (ex) {
|
|
54
|
+
const err = ex;
|
|
55
|
+
throw new Error(`Error processing schema at path '${schemaPath}': ${err.message}`);
|
|
56
|
+
}
|
|
114
57
|
}
|
|
58
|
+
// Generate index.ts file
|
|
59
|
+
await generateIndexFile(codeGenPath, vfs);
|
|
60
|
+
scenario.complete();
|
|
115
61
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
*/
|
|
119
|
-
static async getModelNameFromSchema(schemaPath, vfs) {
|
|
120
|
-
const schema = await this.loadSchemaFromFile(schemaPath, vfs, false);
|
|
121
|
-
const { modelName } = extractModelName(schema, schemaPath, vfs);
|
|
122
|
-
return modelName;
|
|
62
|
+
catch (error) {
|
|
63
|
+
scenario.failure({ error });
|
|
123
64
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
*/
|
|
128
|
-
static async initializeSchemaProcessing(schemaPath, outputDir, vfs) {
|
|
129
|
-
const { schema, schemaJson } = await this.loadSchemaFromFile(schemaPath, vfs, true);
|
|
130
|
-
await this.ensureOutputDirectoriesExist(outputDir, vfs);
|
|
131
|
-
// Ensure schema is a JsonObject for processing
|
|
132
|
-
if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) {
|
|
133
|
-
throw new Error('Schema must be a valid JSON object');
|
|
134
|
-
}
|
|
135
|
-
// model name is used for the generated TypeScript interface and file names
|
|
136
|
-
// dataSourceName is the logical name of the datasource ex:entity set name for Dataverse
|
|
137
|
-
const { modelName, dataSourceName } = extractModelName(schema, schemaPath, vfs);
|
|
138
|
-
return {
|
|
139
|
-
schema: schema,
|
|
140
|
-
schemaJson,
|
|
141
|
-
outputDir,
|
|
142
|
-
modelName,
|
|
143
|
-
dataSourceName,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
static async loadSchemaFromFile(schemaPath, vfs, asObjectWithRaw = false) {
|
|
147
|
-
if (!(await vfs.exists(schemaPath))) {
|
|
148
|
-
throw new Error(`Schema file not found at path: ${schemaPath}`);
|
|
149
|
-
}
|
|
150
|
-
const schemaJson = await vfs.readFile(schemaPath, 'utf-8');
|
|
151
|
-
const schema = JSON.parse(schemaJson);
|
|
152
|
-
if (asObjectWithRaw) {
|
|
153
|
-
return { schema, schemaJson };
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
return schema;
|
|
157
|
-
}
|
|
65
|
+
finally {
|
|
66
|
+
_schemaFolderPath = undefined;
|
|
67
|
+
_outputDir = undefined;
|
|
158
68
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Clears and regenerates the model and service files.
|
|
72
|
+
*/
|
|
73
|
+
export async function clearAndRegenerateModelService(options) {
|
|
74
|
+
const { schemaFolderPath, codeGenPath, vfs, logger } = options;
|
|
75
|
+
const modelsDir = vfs.join(codeGenPath, MODELS_FOLDER);
|
|
76
|
+
const servicesDir = vfs.join(codeGenPath, SERVICES_FOLDER);
|
|
77
|
+
// Delete Models directory if it exists
|
|
78
|
+
if (await vfs.exists(modelsDir)) {
|
|
79
|
+
await vfs.rmdir(modelsDir);
|
|
80
|
+
}
|
|
81
|
+
// Delete Services directory if it exists
|
|
82
|
+
if (await vfs.exists(servicesDir)) {
|
|
83
|
+
await vfs.rmdir(servicesDir);
|
|
84
|
+
}
|
|
85
|
+
await generateModelService({ schemaFolderPath, codeGenPath, logger });
|
|
86
|
+
}
|
|
87
|
+
/*
|
|
88
|
+
* The EnsureOutputDirectoryExists method ensures that the output directory exists.
|
|
89
|
+
* It creates the directory if it does not exist.
|
|
90
|
+
*/
|
|
91
|
+
async function ensureOutputDirectoryExists(outputDir, vfs) {
|
|
92
|
+
if (!(await vfs.exists(outputDir))) {
|
|
164
93
|
await vfs.mkdir(outputDir, { recursive: true });
|
|
165
|
-
await vfs.mkdir(vfs.join(outputDir, this.MODELS_FOLDER), { recursive: true });
|
|
166
|
-
await vfs.mkdir(vfs.join(outputDir, this.SERVICES_FOLDER), {
|
|
167
|
-
recursive: true,
|
|
168
|
-
});
|
|
169
94
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
typeof schema.properties === 'object' &&
|
|
181
|
-
!Array.isArray(schema.properties) &&
|
|
182
|
-
Object.prototype.hasOwnProperty.call(schema.properties, 'swagger'));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The processSchema method processes a single schema file.
|
|
98
|
+
* It generates TypeScript models and services based on the schema.
|
|
99
|
+
*/
|
|
100
|
+
async function processSchema(schemaPath, outputDir, vfs) {
|
|
101
|
+
// Destructure result from initializeSchemaProcessing (returns: schema, schemaJson, outputDir, modelName, dataSourceName)
|
|
102
|
+
const { schema, schemaJson, outputDir: processedOutputDir, modelName, dataSourceName, } = await initializeSchemaProcessing(schemaPath, outputDir, vfs);
|
|
103
|
+
if (isSwaggerSchema(schema)) {
|
|
104
|
+
await handleSwaggerSchema(modelName, dataSourceName, schema, processedOutputDir, vfs);
|
|
183
105
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
*/
|
|
187
|
-
static isDataverseLookupProperty(propValue) {
|
|
188
|
-
return propValue['x-ms-dataverse-type'] === 'LookupType';
|
|
106
|
+
else if (isSqlStoredProcedure(schema)) {
|
|
107
|
+
await handleSqlStoredProcedure(modelName, dataSourceName, schema, schemaJson, processedOutputDir, vfs);
|
|
189
108
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
*/
|
|
193
|
-
static getDataverseLookupColumns(propertiesNode) {
|
|
194
|
-
const lookupColumns = new Map();
|
|
195
|
-
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
|
196
|
-
const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
|
|
197
|
-
? propValue
|
|
198
|
-
: undefined;
|
|
199
|
-
if (propValueAsObject && this.isDataverseLookupProperty(propValueAsObject)) {
|
|
200
|
-
lookupColumns.set(propName, propValueAsObject);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return lookupColumns;
|
|
109
|
+
else if (isSharepointSchema(schema)) {
|
|
110
|
+
await handleSharepointSchema(modelName, dataSourceName, schema, schemaJson, processedOutputDir, vfs);
|
|
204
111
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
* Example: "cr160_lookupcol" -> "cr160_lookupcol@odata.bind"
|
|
208
|
-
*/
|
|
209
|
-
static generateODataBindPropertyName(schemaName) {
|
|
210
|
-
return `${schemaName}@odata.bind`;
|
|
112
|
+
else {
|
|
113
|
+
await handleStandardSchema(modelName, dataSourceName, schema, schemaJson, processedOutputDir, vfs);
|
|
211
114
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The initializeSchemaProcessing method initializes the schema processing.
|
|
118
|
+
* Returns an object containing the parsed schema, the schema JSON string, the output directory, model name, and data source name.
|
|
119
|
+
*/
|
|
120
|
+
async function initializeSchemaProcessing(schemaPath, outputDir, vfs) {
|
|
121
|
+
const { schema, schemaJson } = await loadSchemaFromFile(schemaPath, vfs, true);
|
|
122
|
+
await ensureOutputDirectoriesExist(outputDir, vfs);
|
|
123
|
+
// Ensure schema is a JsonObject for processing
|
|
124
|
+
if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) {
|
|
125
|
+
throw new Error('Schema must be a valid JSON object');
|
|
126
|
+
}
|
|
127
|
+
// model name is used for the generated TypeScript interface and file names
|
|
128
|
+
// dataSourceName is the logical name of the datasource ex:entity set name for Dataverse
|
|
129
|
+
const { modelName, dataSourceName } = extractModelName(schema, schemaPath, vfs);
|
|
130
|
+
return {
|
|
131
|
+
schema: schema,
|
|
132
|
+
schemaJson,
|
|
133
|
+
outputDir,
|
|
134
|
+
modelName,
|
|
135
|
+
dataSourceName,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
async function loadSchemaFromFile(schemaPath, vfs, asObjectWithRaw = false) {
|
|
139
|
+
if (!(await vfs.exists(schemaPath))) {
|
|
140
|
+
throw new Error(`Schema file not found at path: ${schemaPath}`);
|
|
218
141
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
lines.push('/*!');
|
|
224
|
-
lines.push(' * Copyright (C) Microsoft Corporation. All rights reserved.');
|
|
225
|
-
lines.push(' * This file is autogenerated. Do not edit this file directly.');
|
|
226
|
-
lines.push(' */');
|
|
227
|
-
lines.push('');
|
|
142
|
+
const schemaJson = await vfs.readFile(schemaPath, 'utf-8');
|
|
143
|
+
const schema = JSON.parse(schemaJson);
|
|
144
|
+
if (asObjectWithRaw) {
|
|
145
|
+
return { schema, schemaJson };
|
|
228
146
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
* Creates both a base interface (for PATCH/POST) and an extended interface (for GET).
|
|
232
|
-
*/
|
|
233
|
-
static generateDataverseInterfaces(modelName, definition) {
|
|
234
|
-
const lines = [];
|
|
235
|
-
this.addCopyrightNotice(lines);
|
|
236
|
-
const propertiesNode = this.getPropertiesNode(definition);
|
|
237
|
-
const lookupColumns = this.getDataverseLookupColumns(propertiesNode);
|
|
238
|
-
if (lookupColumns.size === 0) {
|
|
239
|
-
// No lookup columns, generate single interface
|
|
240
|
-
const singleInterface = this.generatePropertyLines(modelName, definition);
|
|
241
|
-
return { baseInterface: lines.join('\n') + singleInterface, extendedInterface: '' };
|
|
242
|
-
}
|
|
243
|
-
// Generate base interface for PATCH operations
|
|
244
|
-
const baseInterfaceName = `${modelName}Base`;
|
|
245
|
-
const baseInterfaceCode = this.generateDataverseBaseInterface(baseInterfaceName, modelName, definition, lookupColumns);
|
|
246
|
-
// Generate extended interface for GET operations
|
|
247
|
-
const extendedInterfaceName = modelName;
|
|
248
|
-
const extendedInterfaceCode = this.generateDataverseExtendedInterface(extendedInterfaceName, baseInterfaceName, definition, lookupColumns);
|
|
249
|
-
const baseInterface = lines.join('\n') + baseInterfaceCode;
|
|
250
|
-
const extendedInterface = extendedInterfaceCode;
|
|
251
|
-
return { baseInterface, extendedInterface };
|
|
147
|
+
else {
|
|
148
|
+
return schema;
|
|
252
149
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The ensureOutputDirectoriesExist method ensures that the required output directories exist.
|
|
153
|
+
* It creates the main output directory, plus 'generated/models' and 'generated/services' subfolders if needed.
|
|
154
|
+
*/
|
|
155
|
+
async function ensureOutputDirectoriesExist(outputDir, vfs) {
|
|
156
|
+
await vfs.mkdir(outputDir, { recursive: true });
|
|
157
|
+
await vfs.mkdir(vfs.join(outputDir, MODELS_FOLDER), { recursive: true });
|
|
158
|
+
await vfs.mkdir(vfs.join(outputDir, SERVICES_FOLDER), {
|
|
159
|
+
recursive: true,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The isSwaggerSchema method checks if the schema is a Swagger schema.
|
|
164
|
+
* Returns true if schema.properties.swagger exists.
|
|
165
|
+
*/
|
|
166
|
+
function isSwaggerSchema(schema) {
|
|
167
|
+
return (typeof schema === 'object' &&
|
|
168
|
+
schema !== null &&
|
|
169
|
+
!Array.isArray(schema) &&
|
|
170
|
+
'properties' in schema &&
|
|
171
|
+
schema.properties !== null &&
|
|
172
|
+
typeof schema.properties === 'object' &&
|
|
173
|
+
!Array.isArray(schema.properties) &&
|
|
174
|
+
Object.prototype.hasOwnProperty.call(schema.properties, 'swagger'));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Checks if a property is a Dataverse lookup column.
|
|
178
|
+
*/
|
|
179
|
+
function isDataverseLookupProperty(propValue) {
|
|
180
|
+
return propValue['x-ms-dataverse-type'] === 'LookupType';
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Gets all Dataverse lookup columns from the properties object.
|
|
184
|
+
*/
|
|
185
|
+
function getDataverseLookupColumns(propertiesNode) {
|
|
186
|
+
const lookupColumns = new Map();
|
|
187
|
+
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
|
188
|
+
const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
|
|
189
|
+
? propValue
|
|
190
|
+
: undefined;
|
|
191
|
+
if (propValueAsObject && isDataverseLookupProperty(propValueAsObject)) {
|
|
192
|
+
lookupColumns.set(propName, propValueAsObject);
|
|
296
193
|
}
|
|
297
|
-
lines.push('}');
|
|
298
|
-
return lines.join('\n');
|
|
299
194
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
195
|
+
return lookupColumns;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Generates the OData bind property name for a lookup column.
|
|
199
|
+
* Example: "cr160_lookupcol" -> "cr160_lookupcol@odata.bind"
|
|
200
|
+
*/
|
|
201
|
+
function generateODataBindPropertyName(schemaName) {
|
|
202
|
+
return `${schemaName}@odata.bind`;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Generates the value property name for a lookup column.
|
|
206
|
+
* Example: "cr160_lookupcol" -> "_cr160_lookupcol_value"
|
|
207
|
+
*/
|
|
208
|
+
function generateLookupValuePropertyName(lookupPropertyName) {
|
|
209
|
+
return `_${lookupPropertyName}_value`;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* The addCopyrightNotice method adds a copyright notice to the generated files.
|
|
213
|
+
*/
|
|
214
|
+
function addCopyrightNotice(lines) {
|
|
215
|
+
lines.push('/*!');
|
|
216
|
+
lines.push(' * Copyright (C) Microsoft Corporation. All rights reserved.');
|
|
217
|
+
lines.push(' * This file is autogenerated. Do not edit this file directly.');
|
|
218
|
+
lines.push(' */');
|
|
219
|
+
lines.push('');
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Generates TypeScript interfaces for Dataverse entities with lookup columns.
|
|
223
|
+
* Creates both a base interface (for PATCH/POST) and an extended interface (for GET).
|
|
224
|
+
*/
|
|
225
|
+
function generateDataverseInterfaces(modelName, definition) {
|
|
226
|
+
const lines = [];
|
|
227
|
+
addCopyrightNotice(lines);
|
|
228
|
+
const propertiesNode = getPropertiesNode(definition);
|
|
229
|
+
const lookupColumns = getDataverseLookupColumns(propertiesNode);
|
|
230
|
+
if (lookupColumns.size === 0) {
|
|
231
|
+
// No lookup columns, generate single interface
|
|
232
|
+
const singleInterface = generatePropertyLines(modelName, definition);
|
|
233
|
+
return { baseInterface: lines.join('\n') + singleInterface, extendedInterface: '' };
|
|
234
|
+
}
|
|
235
|
+
// Generate base interface for PATCH operations
|
|
236
|
+
const baseInterfaceName = `${modelName}Base`;
|
|
237
|
+
const baseInterfaceCode = generateDataverseBaseInterface(baseInterfaceName, modelName, definition, lookupColumns);
|
|
238
|
+
// Generate extended interface for GET operations
|
|
239
|
+
const extendedInterfaceName = modelName;
|
|
240
|
+
const extendedInterfaceCode = generateDataverseExtendedInterface(extendedInterfaceName, baseInterfaceName, definition, lookupColumns);
|
|
241
|
+
const baseInterface = lines.join('\n') + baseInterfaceCode;
|
|
242
|
+
const extendedInterface = extendedInterfaceCode;
|
|
243
|
+
return { baseInterface, extendedInterface };
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Generates the base interface for Dataverse PATCH/POST operations.
|
|
247
|
+
* Replaces lookup columns with @odata.bind properties.
|
|
248
|
+
*/
|
|
249
|
+
function generateDataverseBaseInterface(interfaceName, originalModelName, definition, lookupColumns) {
|
|
250
|
+
const lines = [];
|
|
251
|
+
const requiredFields = getRequiredFields(definition);
|
|
252
|
+
const propertiesNode = getPropertiesNode(definition);
|
|
253
|
+
// Generate optionset enums first - use original model name for enum generation
|
|
254
|
+
const enumLines = generateOptionsetEnums(originalModelName, propertiesNode, '');
|
|
255
|
+
if (enumLines.length > 0) {
|
|
256
|
+
lines.push(...enumLines);
|
|
257
|
+
lines.push('');
|
|
258
|
+
}
|
|
259
|
+
lines.push(`export interface ${convertToValidIdentifier(interfaceName)} {`);
|
|
260
|
+
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
|
261
|
+
const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
|
|
262
|
+
? propValue
|
|
263
|
+
: undefined;
|
|
264
|
+
// Skip read-only properties. They should not be included in PATCH/POST interfaces.
|
|
265
|
+
if (propValueAsObject === null || propValueAsObject === void 0 ? void 0 : propValueAsObject['x-ms-read-only']) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
// Handle lookup columns and their related properties
|
|
269
|
+
if (lookupColumns.has(propName) || isLookupRelatedProperty(propName, lookupColumns)) {
|
|
319
270
|
if (lookupColumns.has(propName)) {
|
|
320
|
-
|
|
271
|
+
// Add @odata.bind property instead
|
|
272
|
+
const schemaName = propValueAsObject === null || propValueAsObject === void 0 ? void 0 : propValueAsObject['x-ms-schema-name'];
|
|
273
|
+
// Ensure schemaName is a string before passing to generateODataBindPropertyName
|
|
274
|
+
const schemaNameStr = typeof schemaName === 'string' ? schemaName : propName;
|
|
275
|
+
const odataBindProp = generateODataBindPropertyName(schemaNameStr);
|
|
276
|
+
const formattedPropName = formatPropertyName(odataBindProp);
|
|
277
|
+
const optionalMark = requiredFields.has(propName) ? '' : '?';
|
|
278
|
+
lines.push(` ${formattedPropName}${optionalMark}: string;`);
|
|
321
279
|
}
|
|
322
|
-
|
|
323
|
-
formattedPropName = normalizePropertyName(formattedPropName);
|
|
324
|
-
const tsType = this.determineTypeScriptType(modelName, propValueAsObject, '', propName);
|
|
325
|
-
const optionalMark = requiredFields.has(propName) ? '' : '?';
|
|
326
|
-
this.addPropertyComment(lines, propValueAsObject, '');
|
|
327
|
-
lines.push(` ${formattedPropName}${optionalMark}: ${tsType};`);
|
|
280
|
+
continue;
|
|
328
281
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const valuePropName = this.generateLookupValuePropertyName(lookupPropName);
|
|
336
|
-
const formattedValuePropName = formatPropertyName(valuePropName);
|
|
337
|
-
lines.push(` ${formattedValuePropName}?: string;`);
|
|
338
|
-
}
|
|
339
|
-
lines.push('}');
|
|
340
|
-
return lines.join('\n');
|
|
282
|
+
let formattedPropName = formatPropertyName(convertToValidIdentifier(propName));
|
|
283
|
+
formattedPropName = normalizePropertyName(formattedPropName);
|
|
284
|
+
const tsType = determineTypeScriptType(originalModelName, propValueAsObject, '', propName);
|
|
285
|
+
const optionalMark = requiredFields.has(propName) ? '' : '?';
|
|
286
|
+
addPropertyComment(lines, propValueAsObject, '');
|
|
287
|
+
lines.push(` ${formattedPropName}${optionalMark}: ${tsType};`);
|
|
341
288
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
289
|
+
lines.push('}');
|
|
290
|
+
return lines.join('\n');
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Generates the extended interface for Dataverse GET operations.
|
|
294
|
+
* Extends the base interface and adds lookup object and value properties.
|
|
295
|
+
*/
|
|
296
|
+
function generateDataverseExtendedInterface(modelName, baseInterfaceName, definition, lookupColumns) {
|
|
297
|
+
const lines = [];
|
|
298
|
+
lines.push(`export interface ${convertToValidIdentifier(modelName)} extends ${convertToValidIdentifier(baseInterfaceName)} {`);
|
|
299
|
+
const requiredFields = getRequiredFields(definition);
|
|
300
|
+
const propertiesNode = getPropertiesNode(definition);
|
|
301
|
+
// Add read-only properties (excluding lookup columns which are handled separately)
|
|
302
|
+
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
|
303
|
+
const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
|
|
304
|
+
? propValue
|
|
305
|
+
: undefined;
|
|
306
|
+
// Only include read-only properties in the extended interface
|
|
307
|
+
if (!(propValueAsObject === null || propValueAsObject === void 0 ? void 0 : propValueAsObject['x-ms-read-only'])) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
// Skip lookup columns as they will be handled in the lookup-specific section
|
|
311
|
+
if (lookupColumns.has(propName)) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
let formattedPropName = formatPropertyName(convertToValidIdentifier(propName));
|
|
315
|
+
formattedPropName = normalizePropertyName(formattedPropName);
|
|
316
|
+
const tsType = determineTypeScriptType(modelName, propValueAsObject, '', propName);
|
|
317
|
+
const optionalMark = requiredFields.has(propName) ? '' : '?';
|
|
318
|
+
addPropertyComment(lines, propValueAsObject, '');
|
|
319
|
+
lines.push(` ${formattedPropName}${optionalMark}: ${tsType};`);
|
|
320
|
+
}
|
|
321
|
+
// Add lookup-specific properties
|
|
322
|
+
const lookupPropNames = Array.from(lookupColumns.keys());
|
|
323
|
+
for (const lookupPropName of lookupPropNames) {
|
|
324
|
+
// Add the lookup object property
|
|
325
|
+
lines.push(` ${formatPropertyName(convertToValidIdentifier(lookupPropName))}?: object;`);
|
|
326
|
+
// Add the lookup value property (_propname_value)
|
|
327
|
+
const valuePropName = generateLookupValuePropertyName(lookupPropName);
|
|
328
|
+
const formattedValuePropName = formatPropertyName(valuePropName);
|
|
329
|
+
lines.push(` ${formattedValuePropName}?: string;`);
|
|
330
|
+
}
|
|
331
|
+
lines.push('}');
|
|
332
|
+
return lines.join('\n');
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Checks if a property is related to a lookup column (like name properties).
|
|
336
|
+
*/
|
|
337
|
+
function isLookupRelatedProperty(propName, lookupColumns) {
|
|
338
|
+
// Check if this property is a name property for any lookup column
|
|
339
|
+
const lookupPropNames = Array.from(lookupColumns.keys());
|
|
340
|
+
for (const lookupPropName of lookupPropNames) {
|
|
341
|
+
if (propName === `${lookupPropName}name`) {
|
|
342
|
+
return true;
|
|
352
343
|
}
|
|
353
|
-
return false;
|
|
354
344
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* The handleSwaggerSchema method handles the Swagger schema.
|
|
349
|
+
* It generates TypeScript models and services from the Swagger schema.
|
|
350
|
+
*/
|
|
351
|
+
async function handleSwaggerSchema(modelName, dataSourceName, schema, outputDir, vfs) {
|
|
352
|
+
const swaggerObject = getSwaggerObject(schema);
|
|
353
|
+
const modelFilePath = vfs.join(outputDir, MODELS_FOLDER, `${modelName}Model.ts`);
|
|
354
|
+
const serviceFilePath = vfs.join(outputDir, SERVICES_FOLDER, `${modelName}Service.ts`);
|
|
355
|
+
const modelLines = [];
|
|
356
|
+
addCopyrightNotice(modelLines);
|
|
357
|
+
processDefinitions(swaggerObject, modelLines);
|
|
358
|
+
const parameterTypes = processParameters(swaggerObject, modelLines);
|
|
359
|
+
await writeToFile(modelFilePath, modelLines, vfs);
|
|
360
|
+
if ('paths' in swaggerObject &&
|
|
361
|
+
typeof swaggerObject.paths === 'object' &&
|
|
362
|
+
swaggerObject.paths !== null &&
|
|
363
|
+
!Array.isArray(swaggerObject.paths)) {
|
|
364
|
+
const serviceCode = generateServiceFromPaths(modelName, dataSourceName, swaggerObject.paths, parameterTypes);
|
|
365
|
+
await writeToFileWithContent(serviceFilePath, serviceCode, vfs);
|
|
375
366
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
return swaggerNode;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* The getSwaggerObject method retrieves the Swagger object from the schema.
|
|
370
|
+
* Throws an error if the schema is not valid Swagger format.
|
|
371
|
+
*/
|
|
372
|
+
function getSwaggerObject(schema) {
|
|
373
|
+
if (schema !== null && typeof schema === 'object' && !Array.isArray(schema)) {
|
|
374
|
+
const properties = schema.properties;
|
|
375
|
+
if (typeof properties === 'object' && properties !== null && !Array.isArray(properties)) {
|
|
376
|
+
const swaggerNode = properties.swagger;
|
|
377
|
+
if (!swaggerNode || typeof swaggerNode !== 'object' || Array.isArray(swaggerNode)) {
|
|
378
|
+
throw new Error("Invalid schema format: 'swagger' node is missing or malformed.");
|
|
389
379
|
}
|
|
380
|
+
return swaggerNode;
|
|
390
381
|
}
|
|
391
|
-
throw new Error('Schema must be a JSON object with properties to extract swagger object');
|
|
392
382
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
383
|
+
throw new Error('Schema must be a JSON object with properties to extract swagger object');
|
|
384
|
+
}
|
|
385
|
+
/*
|
|
386
|
+
* The processDefinitions function processes the definitions node of the Swagger schema.
|
|
387
|
+
* It collects model interface names and pushes generated code lines to modelLines.
|
|
388
|
+
*/
|
|
389
|
+
function processDefinitions(swaggerObject, modelLines) {
|
|
390
|
+
const definitionTypes = [];
|
|
391
|
+
if ('definitions' in swaggerObject &&
|
|
392
|
+
typeof swaggerObject.definitions === 'object' &&
|
|
393
|
+
swaggerObject.definitions !== null &&
|
|
394
|
+
!Array.isArray(swaggerObject.definitions)) {
|
|
395
|
+
const definitionsObject = swaggerObject.definitions;
|
|
396
|
+
for (const [interfaceName, definitionValue] of Object.entries(definitionsObject)) {
|
|
397
|
+
if (definitionValue && typeof definitionValue === 'object' && !Array.isArray(definitionValue)) {
|
|
398
|
+
const modelCode = generateModelFromSchema(interfaceName, definitionValue);
|
|
399
|
+
modelLines.push(modelCode);
|
|
400
|
+
modelLines.push(''); // Add a blank line between models
|
|
401
|
+
definitionTypes.push(interfaceName);
|
|
411
402
|
}
|
|
412
403
|
}
|
|
413
|
-
return definitionTypes;
|
|
414
404
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
405
|
+
return definitionTypes;
|
|
406
|
+
}
|
|
407
|
+
/*
|
|
408
|
+
* The processParameters function processes the parameters node of the Swagger schema.
|
|
409
|
+
*/
|
|
410
|
+
function processParameters(swaggerObject, modelLines) {
|
|
411
|
+
const parameterTypes = {};
|
|
412
|
+
if ('parameters' in swaggerObject &&
|
|
413
|
+
typeof swaggerObject.parameters === 'object' &&
|
|
414
|
+
swaggerObject.parameters !== null &&
|
|
415
|
+
!Array.isArray(swaggerObject.parameters)) {
|
|
416
|
+
const parametersObject = swaggerObject.parameters;
|
|
417
|
+
for (const [interfaceName, parameterValue] of Object.entries(parametersObject)) {
|
|
418
|
+
if (parameterValue && typeof parameterValue === 'object' && !Array.isArray(parameterValue)) {
|
|
419
|
+
const { propertyInfo } = generateModelFromParameters(interfaceName, parameterValue);
|
|
420
|
+
// Optionally: modelLines.push(modelCode); // If you want to collect interface code
|
|
421
|
+
parameterTypes[interfaceName] = propertyInfo;
|
|
431
422
|
}
|
|
432
423
|
}
|
|
433
|
-
return parameterTypes;
|
|
434
|
-
}
|
|
435
|
-
/**
|
|
436
|
-
* Writes the lines to a file at the specified path.
|
|
437
|
-
*
|
|
438
|
-
* @param {string} filePath The path to the file to write
|
|
439
|
-
* @param {string[]} lines The lines to write to the file
|
|
440
|
-
* @param {boolean} [createDirPath=true] Create the directory path if it does not exist
|
|
441
|
-
*/
|
|
442
|
-
static async writeToFile(filePath, lines, vfs, createDirPath = true) {
|
|
443
|
-
const dir = vfs.dirname(filePath);
|
|
444
|
-
if (createDirPath && !(await vfs.exists(dir))) {
|
|
445
|
-
await vfs.mkdir(dir, { recursive: true });
|
|
446
|
-
}
|
|
447
|
-
await vfs.writeFile(filePath, lines.join('\n'));
|
|
448
424
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
425
|
+
return parameterTypes;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Writes the lines to a file at the specified path.
|
|
429
|
+
*
|
|
430
|
+
* @param {string} filePath The path to the file to write
|
|
431
|
+
* @param {string[]} lines The lines to write to the file
|
|
432
|
+
* @param {boolean} [createDirPath=true] Create the directory path if it does not exist
|
|
433
|
+
*/
|
|
434
|
+
async function writeToFile(filePath, lines, vfs, createDirPath = true) {
|
|
435
|
+
const dir = vfs.dirname(filePath);
|
|
436
|
+
if (createDirPath && !(await vfs.exists(dir))) {
|
|
437
|
+
await vfs.mkdir(dir, { recursive: true });
|
|
454
438
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
lines.push(this.generateTypeAliasForEmptyObject(modelName));
|
|
476
|
-
return lines.join('\n');
|
|
477
|
-
}
|
|
478
|
-
// Check if this is an array type definition
|
|
479
|
-
const isArrayType = definition.type === 'array' && itemsNode;
|
|
480
|
-
if (isArrayType) {
|
|
481
|
-
// For array types, generate an interface for the item type and a type alias for the array
|
|
482
|
-
const itemInterfaceName = `${modelName}Item`;
|
|
483
|
-
const itemModelCode = this.generatePropertyLines(itemInterfaceName, itemsNode);
|
|
484
|
-
lines.push(itemModelCode);
|
|
485
|
-
lines.push('');
|
|
486
|
-
lines.push(`export type ${modelName} = ${itemInterfaceName}[];`);
|
|
487
|
-
return lines.join('\n');
|
|
488
|
-
}
|
|
489
|
-
// If itemsNode exists but not an array type, pass that; otherwise, pass the full definition
|
|
490
|
-
const modelCode = itemsNode
|
|
491
|
-
? this.generatePropertyLines(modelName, itemsNode)
|
|
492
|
-
: this.generatePropertyLines(modelName, definition);
|
|
493
|
-
lines.push(modelCode);
|
|
439
|
+
await vfs.writeFile(filePath, lines.join('\n'));
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* The writeToFile method writes the generated code to a file from a string.
|
|
443
|
+
*/
|
|
444
|
+
async function writeToFileWithContent(filePath, content, vfs) {
|
|
445
|
+
await vfs.writeFile(filePath, content);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* The generateModelFromSchema method generates TypeScript models from the schema.
|
|
449
|
+
* Returns a type alias or interface definition string.
|
|
450
|
+
*/
|
|
451
|
+
function generateModelFromSchema(modelName, definition) {
|
|
452
|
+
modelName = convertToValidIdentifier(simplifyGenericInterfaceName(modelName));
|
|
453
|
+
const lines = [];
|
|
454
|
+
// If it's a simple type (e.g., string, number, boolean) and has no "properties" or "items", generate a type alias
|
|
455
|
+
// Note: We exclude definitions with "items" because those are array types that need special handling
|
|
456
|
+
if ('type' in definition && definition.type && !('properties' in definition) && !('items' in definition)) {
|
|
457
|
+
const tsType = mapJsonTypeToTypeScript(definition.type);
|
|
458
|
+
lines.push(`export type ${modelName} = ${tsType};`);
|
|
494
459
|
return lines.join('\n');
|
|
495
460
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
return this.normalizeGenericTypeName(modelName);
|
|
502
|
-
}
|
|
503
|
-
/**
|
|
504
|
-
* The isEmptyObject method checks if the definition is an empty object.
|
|
505
|
-
* Returns an object with isEmpty (boolean) and itemsNode.
|
|
506
|
-
*/
|
|
507
|
-
static isEmptyObject(definition) {
|
|
508
|
-
let itemsNode;
|
|
509
|
-
const hasNoProperties = !('properties' in definition) ||
|
|
510
|
-
typeof definition.properties !== 'object' ||
|
|
511
|
-
definition.properties === null ||
|
|
512
|
-
Array.isArray(definition.properties) ||
|
|
513
|
-
Object.keys(definition.properties).length === 0;
|
|
514
|
-
const hasNoItems = !('items' in definition) ||
|
|
515
|
-
typeof definition.items !== 'object' ||
|
|
516
|
-
definition.items === null ||
|
|
517
|
-
Array.isArray(definition.items) ||
|
|
518
|
-
Object.keys(definition.items).length === 0;
|
|
519
|
-
if ('items' in definition &&
|
|
520
|
-
typeof definition.items === 'object' &&
|
|
521
|
-
definition.items !== null &&
|
|
522
|
-
!Array.isArray(definition.items)) {
|
|
523
|
-
itemsNode = definition.items;
|
|
524
|
-
}
|
|
525
|
-
return { isEmpty: hasNoProperties && hasNoItems, itemsNode };
|
|
526
|
-
}
|
|
527
|
-
/**
|
|
528
|
-
* The generateTypeAliasForEmptyObject method generates a TypeScript type alias for an empty object.
|
|
529
|
-
*/
|
|
530
|
-
static generateTypeAliasForEmptyObject(modelName) {
|
|
531
|
-
return `export type ${convertToValidIdentifier(modelName)} = object;`;
|
|
461
|
+
// If it's an empty object, generate a type alias for object
|
|
462
|
+
const { isEmpty, itemsNode } = isEmptyObject(definition);
|
|
463
|
+
if (isEmpty) {
|
|
464
|
+
lines.push(generateTypeAliasForEmptyObject(modelName));
|
|
465
|
+
return lines.join('\n');
|
|
532
466
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const propertiesNode = this.getPropertiesNode(definition);
|
|
543
|
-
// Generate optionset enums first (before the interface)
|
|
544
|
-
// BUT only for top-level interfaces (addInterfaceName === true)
|
|
545
|
-
// Nested inline objects should not generate enums to avoid exporting them mid-interface
|
|
546
|
-
if (addInterfaceName) {
|
|
547
|
-
// Collect all enums including from nested objects
|
|
548
|
-
const allEnumLines = this.collectAllEnums(modelName, propertiesNode, padding);
|
|
549
|
-
if (allEnumLines.length > 0) {
|
|
550
|
-
lines.push(...allEnumLines);
|
|
551
|
-
lines.push(''); // Add blank line between enums and interface
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
this.addInterfaceDeclarationWithPadding(lines, modelName, addInterfaceName, padding);
|
|
555
|
-
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
|
556
|
-
let formattedPropName = formatPropertyName(convertToValidIdentifier(propName));
|
|
557
|
-
// Normalize specific property names (e.g., "resultsets" => "ResultSets")
|
|
558
|
-
formattedPropName = normalizePropertyName(formattedPropName);
|
|
559
|
-
const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
|
|
560
|
-
? propValue
|
|
561
|
-
: undefined;
|
|
562
|
-
const tsType = this.determineTypeScriptType(modelName, propValueAsObject, padding, propName);
|
|
563
|
-
const optionalMark = requiredFields.has(propName) ? '' : '?';
|
|
564
|
-
this.addPropertyComment(lines, propValueAsObject, padding);
|
|
565
|
-
lines.push(`${padding} ${formattedPropName}${optionalMark}: ${tsType};`);
|
|
566
|
-
}
|
|
567
|
-
this.closeInterfaceDeclarationWithPadding(lines, padding);
|
|
467
|
+
// Check if this is an array type definition
|
|
468
|
+
const isArrayType = definition.type === 'array' && itemsNode;
|
|
469
|
+
if (isArrayType) {
|
|
470
|
+
// For array types, generate an interface for the item type and a type alias for the array
|
|
471
|
+
const itemInterfaceName = `${modelName}Item`;
|
|
472
|
+
const itemModelCode = generatePropertyLines(itemInterfaceName, itemsNode);
|
|
473
|
+
lines.push(itemModelCode);
|
|
474
|
+
lines.push('');
|
|
475
|
+
lines.push(`export type ${modelName} = ${itemInterfaceName}[];`);
|
|
568
476
|
return lines.join('\n');
|
|
569
477
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
478
|
+
// If itemsNode exists but not an array type, pass that; otherwise, pass the full definition
|
|
479
|
+
const modelCode = itemsNode
|
|
480
|
+
? generatePropertyLines(modelName, itemsNode)
|
|
481
|
+
: generatePropertyLines(modelName, definition);
|
|
482
|
+
lines.push(modelCode);
|
|
483
|
+
return lines.join('\n');
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* The simplifyGenericInterfaceName method simplifies the generic interface name.
|
|
487
|
+
* It removes the generic parameters from the name.
|
|
488
|
+
*/
|
|
489
|
+
function simplifyGenericInterfaceName(modelName) {
|
|
490
|
+
return normalizeGenericTypeName(modelName);
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* The isEmptyObject method checks if the definition is an empty object.
|
|
494
|
+
* Returns an object with isEmpty (boolean) and itemsNode.
|
|
495
|
+
*/
|
|
496
|
+
function isEmptyObject(definition) {
|
|
497
|
+
let itemsNode;
|
|
498
|
+
const hasNoProperties = !('properties' in definition) ||
|
|
499
|
+
typeof definition.properties !== 'object' ||
|
|
500
|
+
definition.properties === null ||
|
|
501
|
+
Array.isArray(definition.properties) ||
|
|
502
|
+
Object.keys(definition.properties).length === 0;
|
|
503
|
+
const hasNoItems = !('items' in definition) ||
|
|
504
|
+
typeof definition.items !== 'object' ||
|
|
505
|
+
definition.items === null ||
|
|
506
|
+
Array.isArray(definition.items) ||
|
|
507
|
+
Object.keys(definition.items).length === 0;
|
|
508
|
+
if ('items' in definition &&
|
|
509
|
+
typeof definition.items === 'object' &&
|
|
510
|
+
definition.items !== null &&
|
|
511
|
+
!Array.isArray(definition.items)) {
|
|
512
|
+
itemsNode = definition.items;
|
|
513
|
+
}
|
|
514
|
+
return { isEmpty: hasNoProperties && hasNoItems, itemsNode };
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* The generateTypeAliasForEmptyObject method generates a TypeScript type alias for an empty object.
|
|
518
|
+
*/
|
|
519
|
+
function generateTypeAliasForEmptyObject(modelName) {
|
|
520
|
+
return `export type ${convertToValidIdentifier(modelName)} = object;`;
|
|
521
|
+
}
|
|
522
|
+
/*
|
|
523
|
+
* The GeneratePropertyLines method generates TypeScript property lines from the schema.
|
|
524
|
+
*/
|
|
525
|
+
function generatePropertyLines(modelName, definition, addInterfaceName = true, padding = '') {
|
|
526
|
+
if (!definition || typeof definition !== 'object' || Array.isArray(definition)) {
|
|
527
|
+
throw new Error('The definition must be a valid JSON object.');
|
|
528
|
+
}
|
|
529
|
+
const lines = [];
|
|
530
|
+
const requiredFields = getRequiredFields(definition);
|
|
531
|
+
const propertiesNode = getPropertiesNode(definition);
|
|
532
|
+
// Generate optionset enums first (before the interface)
|
|
533
|
+
// BUT only for top-level interfaces (addInterfaceName === true)
|
|
534
|
+
// Nested inline objects should not generate enums to avoid exporting them mid-interface
|
|
535
|
+
if (addInterfaceName) {
|
|
536
|
+
// Collect all enums including from nested objects
|
|
537
|
+
const allEnumLines = collectAllEnums(modelName, propertiesNode, padding);
|
|
538
|
+
if (allEnumLines.length > 0) {
|
|
539
|
+
lines.push(...allEnumLines);
|
|
540
|
+
lines.push(''); // Add blank line between enums and interface
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
addInterfaceDeclarationWithPadding(lines, modelName, addInterfaceName, padding);
|
|
544
|
+
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
|
545
|
+
let formattedPropName = formatPropertyName(convertToValidIdentifier(propName));
|
|
546
|
+
// Normalize specific property names (e.g., "resultsets" => "ResultSets")
|
|
547
|
+
formattedPropName = normalizePropertyName(formattedPropName);
|
|
548
|
+
const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
|
|
549
|
+
? propValue
|
|
550
|
+
: undefined;
|
|
551
|
+
const tsType = determineTypeScriptType(modelName, propValueAsObject, padding, propName);
|
|
552
|
+
const optionalMark = requiredFields.has(propName) ? '' : '?';
|
|
553
|
+
addPropertyComment(lines, propValueAsObject, padding);
|
|
554
|
+
lines.push(`${padding} ${formattedPropName}${optionalMark}: ${tsType};`);
|
|
555
|
+
}
|
|
556
|
+
closeInterfaceDeclarationWithPadding(lines, padding);
|
|
557
|
+
return lines.join('\n');
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Recursively collects all enum declarations from properties, including nested objects.
|
|
561
|
+
* This ensures enums are hoisted to the top level and not exported mid-interface.
|
|
562
|
+
*/
|
|
563
|
+
function collectAllEnums(modelName, propertiesNode, padding) {
|
|
564
|
+
const enumLines = [];
|
|
565
|
+
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
|
566
|
+
const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
|
|
567
|
+
? propValue
|
|
568
|
+
: undefined;
|
|
569
|
+
if (!propValueAsObject) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
// Check if this property has optionset data
|
|
573
|
+
if (hasOptionsetData(propValueAsObject)) {
|
|
574
|
+
const enumName = generateOptionsetEnumName(modelName, propName);
|
|
575
|
+
const enumDeclaration = generateOptionsetConstDeclaration(enumName, propValueAsObject, padding);
|
|
576
|
+
enumLines.push(enumDeclaration);
|
|
577
|
+
}
|
|
578
|
+
// Recursively collect enums from nested objects
|
|
579
|
+
if (propValueAsObject.properties && typeof propValueAsObject.properties === 'object') {
|
|
580
|
+
const nestedEnums = collectAllEnums(modelName, propValueAsObject.properties, padding);
|
|
581
|
+
enumLines.push(...nestedEnums);
|
|
582
|
+
}
|
|
583
|
+
// Recursively collect enums from array items
|
|
584
|
+
if (propValueAsObject.type === 'array' &&
|
|
585
|
+
propValueAsObject.items &&
|
|
586
|
+
typeof propValueAsObject.items === 'object' &&
|
|
587
|
+
!Array.isArray(propValueAsObject.items)) {
|
|
588
|
+
const itemsObj = propValueAsObject.items;
|
|
589
|
+
if (itemsObj.properties && typeof itemsObj.properties === 'object') {
|
|
590
|
+
const itemEnums = collectAllEnums(modelName, itemsObj.properties, padding);
|
|
591
|
+
enumLines.push(...itemEnums);
|
|
604
592
|
}
|
|
605
593
|
}
|
|
606
|
-
return enumLines;
|
|
607
|
-
}
|
|
608
|
-
/**
|
|
609
|
-
* Checks if a property has optionset data (enum and x-ms-enum-values)
|
|
610
|
-
*/
|
|
611
|
-
static hasOptionsetData(propValue) {
|
|
612
|
-
const hasEnum = Array.isArray(propValue.enum) && propValue.enum.length > 0;
|
|
613
|
-
return hasEnum;
|
|
614
594
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
595
|
+
return enumLines;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Checks if a property has optionset data (enum and x-ms-enum-values)
|
|
599
|
+
*/
|
|
600
|
+
function hasOptionsetData(propValue) {
|
|
601
|
+
const hasEnum = Array.isArray(propValue.enum) && propValue.enum.length > 0;
|
|
602
|
+
return hasEnum;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Generates the enum name for an optionset property
|
|
606
|
+
*/
|
|
607
|
+
function generateOptionsetEnumName(modelName, propName) {
|
|
608
|
+
const formattedPropName = formatPropertyName(propName);
|
|
609
|
+
return `${modelName}${formattedPropName}`;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* The generateOptionsetEnums method generates TypeScript const objects for optionset properties.
|
|
613
|
+
* These const objects are compatible with TypeScript's erasableSyntaxOnly flag and provide
|
|
614
|
+
* type safety while preserving the original numeric values from the schema.
|
|
615
|
+
* @returns An array of const object declaration strings.
|
|
616
|
+
*/
|
|
617
|
+
function generateOptionsetEnums(modelName, propertiesNode, padding) {
|
|
618
|
+
const enumLines = [];
|
|
619
|
+
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
|
620
|
+
const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
|
|
621
|
+
? propValue
|
|
622
|
+
: undefined;
|
|
623
|
+
// Check if this property has optionset data
|
|
624
|
+
if (propValueAsObject && hasOptionsetData(propValueAsObject)) {
|
|
625
|
+
const enumName = generateOptionsetEnumName(modelName, propName);
|
|
626
|
+
const enumDeclaration = generateOptionsetConstDeclaration(enumName, propValueAsObject, padding);
|
|
627
|
+
enumLines.push(enumDeclaration);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return enumLines;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Formats an enum label as a quoted string literal, escaping single quotes.
|
|
634
|
+
* Returns an empty string literal for empty labels.
|
|
635
|
+
*/
|
|
636
|
+
function formatEnumLabel(label) {
|
|
637
|
+
if (!label || (typeof label === 'string' && label.trim() === '')) {
|
|
638
|
+
return `''`; // Placeholder for empty labels
|
|
621
639
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
* @returns An array of const object declaration strings.
|
|
627
|
-
*/
|
|
628
|
-
static generateOptionsetEnums(modelName, propertiesNode, padding) {
|
|
629
|
-
const enumLines = [];
|
|
630
|
-
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
|
631
|
-
const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
|
|
632
|
-
? propValue
|
|
633
|
-
: undefined;
|
|
634
|
-
// Check if this property has optionset data
|
|
635
|
-
if (propValueAsObject && this.hasOptionsetData(propValueAsObject)) {
|
|
636
|
-
const enumName = this.generateOptionsetEnumName(modelName, propName);
|
|
637
|
-
const enumDeclaration = this.generateOptionsetConstDeclaration(enumName, propValueAsObject, padding);
|
|
638
|
-
enumLines.push(enumDeclaration);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
return enumLines;
|
|
640
|
+
else {
|
|
641
|
+
// Escape single quotes in the label to prevent breaking the string literal
|
|
642
|
+
const escapedLabel = label.replace(/'/g, "\\'");
|
|
643
|
+
return `'${escapedLabel}'`;
|
|
642
644
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Generates const object declaration for an optionset property.
|
|
648
|
+
* Creates a const object with typed properties that is compatible with erasableSyntaxOnly.
|
|
649
|
+
* Also generates a corresponding type alias for type safety.
|
|
650
|
+
*/
|
|
651
|
+
function generateOptionsetConstDeclaration(enumName, propValue, padding) {
|
|
652
|
+
// Check for x-ms-enum-values format (array of objects or numbers)
|
|
653
|
+
const enumValues = propValue['x-ms-enum-values'];
|
|
654
|
+
const enumLabels = propValue.enum;
|
|
655
|
+
const lines = [];
|
|
656
|
+
enumName = convertToValidIdentifier(enumName);
|
|
657
|
+
// Check if x-ms-enum-values exists and is an array
|
|
658
|
+
if (enumValues && Array.isArray(enumValues) && enumLabels && Array.isArray(enumLabels)) {
|
|
659
|
+
// Check if x-ms-enum-values contains objects with numeric values (Dataverse format)
|
|
660
|
+
const hasObjectFormat = enumValues.length > 0 && typeof enumValues[0] === 'object' && enumValues[0] !== null;
|
|
661
|
+
if (hasObjectFormat) {
|
|
662
|
+
// This is the Swagger format where x-ms-enum-values contains objects like {displayName, value}
|
|
663
|
+
// Just use the enum array for string union type
|
|
664
|
+
const labelStrings = enumLabels.map((label) => formatEnumLabel(label));
|
|
665
|
+
lines.push(`${padding}export type ${enumName} = ${labelStrings.join('|')};`);
|
|
650
666
|
}
|
|
651
667
|
else {
|
|
652
|
-
//
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
// Check if x-ms-enum-values exists and is an array
|
|
669
|
-
if (enumValues && Array.isArray(enumValues) && enumLabels && Array.isArray(enumLabels)) {
|
|
670
|
-
// Check if x-ms-enum-values contains objects with numeric values (Dataverse format)
|
|
671
|
-
const hasObjectFormat = enumValues.length > 0 && typeof enumValues[0] === 'object' && enumValues[0] !== null;
|
|
672
|
-
if (hasObjectFormat) {
|
|
673
|
-
// This is the Swagger format where x-ms-enum-values contains objects like {displayName, value}
|
|
674
|
-
// Just use the enum array for string union type
|
|
675
|
-
const labelStrings = enumLabels.map((label) => this.formatEnumLabel(label));
|
|
676
|
-
lines.push(`${padding}export type ${enumName} = ${labelStrings.join('|')};`);
|
|
677
|
-
}
|
|
678
|
-
else {
|
|
679
|
-
// This is the Dataverse format where x-ms-enum-values contains numeric values
|
|
680
|
-
const enumNumericValues = enumValues;
|
|
681
|
-
// Generate const object with numeric keys
|
|
682
|
-
lines.push(`${padding}export const ${enumName} = {`);
|
|
683
|
-
// Handle the format where enum contains display names and x-ms-enum-values contains numeric values
|
|
684
|
-
for (let i = 0; i < enumLabels.length; i++) {
|
|
685
|
-
const displayName = enumLabels[i]; // e.g., "Preferred Customer"
|
|
686
|
-
const numericValue = enumNumericValues[i]; // e.g., 1
|
|
687
|
-
// Use the display name directly as the string value
|
|
688
|
-
let sanitizedLabel;
|
|
689
|
-
if (!displayName || (typeof displayName === 'string' && displayName.trim() === '')) {
|
|
690
|
-
// Generate a placeholder name for empty labels
|
|
691
|
-
sanitizedLabel = `EmptyOption${i}`;
|
|
692
|
-
}
|
|
693
|
-
else {
|
|
694
|
-
sanitizedLabel = sanitizeName(displayName);
|
|
695
|
-
}
|
|
696
|
-
const comma = i < enumLabels.length - 1 ? ',' : '';
|
|
697
|
-
lines.push(`${padding} ${numericValue}: '${sanitizedLabel}'${comma}`);
|
|
668
|
+
// This is the Dataverse format where x-ms-enum-values contains numeric values
|
|
669
|
+
const enumNumericValues = enumValues;
|
|
670
|
+
// Generate const object with numeric keys
|
|
671
|
+
lines.push(`${padding}export const ${enumName} = {`);
|
|
672
|
+
// Handle the format where enum contains display names and x-ms-enum-values contains numeric values
|
|
673
|
+
for (let i = 0; i < enumLabels.length; i++) {
|
|
674
|
+
const displayName = enumLabels[i]; // e.g., "Preferred Customer"
|
|
675
|
+
const numericValue = enumNumericValues[i]; // e.g., 1
|
|
676
|
+
// Use the display name directly as the string value
|
|
677
|
+
let sanitizedLabel;
|
|
678
|
+
if (!displayName || (typeof displayName === 'string' && displayName.trim() === '')) {
|
|
679
|
+
// Generate a placeholder name for empty labels
|
|
680
|
+
sanitizedLabel = `EmptyOption${i}`;
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
sanitizedLabel = sanitizeName(displayName);
|
|
698
684
|
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
lines.push(`${padding}export type ${enumName} = keyof typeof ${enumName};`);
|
|
685
|
+
const comma = i < enumLabels.length - 1 ? ',' : '';
|
|
686
|
+
lines.push(`${padding} ${numericValue}: '${sanitizedLabel}'${comma}`);
|
|
702
687
|
}
|
|
688
|
+
lines.push(`${padding}} as const;`);
|
|
689
|
+
// Generate type alias for the numeric values
|
|
690
|
+
lines.push(`${padding}export type ${enumName} = keyof typeof ${enumName};`);
|
|
703
691
|
}
|
|
704
|
-
else {
|
|
705
|
-
// This is the path for swaggers where only enum array exists (no x-ms-enum-values)
|
|
706
|
-
const labelStrings = enumLabels.map((label) => this.formatEnumLabel(label));
|
|
707
|
-
lines.push(`${padding}export type ${enumName} = ${labelStrings.join('|')};`);
|
|
708
|
-
}
|
|
709
|
-
return lines.join('\n');
|
|
710
692
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
static addInterfaceDeclarationWithPadding(lines, modelName, addInterfaceName, padding) {
|
|
716
|
-
if (addInterfaceName) {
|
|
717
|
-
lines.push(`${padding}export interface ${convertToValidIdentifier(modelName)} {`);
|
|
718
|
-
}
|
|
719
|
-
else {
|
|
720
|
-
lines.push(`{`);
|
|
721
|
-
}
|
|
693
|
+
else {
|
|
694
|
+
// This is the path for swaggers where only enum array exists (no x-ms-enum-values)
|
|
695
|
+
const labelStrings = enumLabels.map((label) => formatEnumLabel(label));
|
|
696
|
+
lines.push(`${padding}export type ${enumName} = ${labelStrings.join('|')};`);
|
|
722
697
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
698
|
+
return lines.join('\n');
|
|
699
|
+
}
|
|
700
|
+
/*
|
|
701
|
+
* The addInterfaceDeclarationWithPadding function adds the interface declaration to the lines.
|
|
702
|
+
* It takes the lines to write to, model name, and padding as parameters.
|
|
703
|
+
*/
|
|
704
|
+
function addInterfaceDeclarationWithPadding(lines, modelName, addInterfaceName, padding) {
|
|
705
|
+
if (addInterfaceName) {
|
|
706
|
+
lines.push(`${padding}export interface ${convertToValidIdentifier(modelName)} {`);
|
|
729
707
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
* It returns a Set of required field names.
|
|
733
|
-
*/
|
|
734
|
-
static getRequiredFields(definition) {
|
|
735
|
-
if ('required' in definition && Array.isArray(definition.required)) {
|
|
736
|
-
// Convert all entries to string and add to Set
|
|
737
|
-
return new Set(definition.required.map((r) => String(r)));
|
|
738
|
-
}
|
|
739
|
-
return new Set();
|
|
708
|
+
else {
|
|
709
|
+
lines.push(`{`);
|
|
740
710
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
711
|
+
}
|
|
712
|
+
/*
|
|
713
|
+
* The closeInterfaceDeclarationWithPadding function closes the interface declaration with padding.
|
|
714
|
+
* It takes the lines to write to and padding as parameters.
|
|
715
|
+
*/
|
|
716
|
+
function closeInterfaceDeclarationWithPadding(lines, padding) {
|
|
717
|
+
lines.push(`${padding}}`);
|
|
718
|
+
}
|
|
719
|
+
/*
|
|
720
|
+
* The getRequiredFields method retrieves the required fields from the definition.
|
|
721
|
+
* It returns a Set of required field names.
|
|
722
|
+
*/
|
|
723
|
+
function getRequiredFields(definition) {
|
|
724
|
+
if ('required' in definition && Array.isArray(definition.required)) {
|
|
725
|
+
// Convert all entries to string and add to Set
|
|
726
|
+
return new Set(definition.required.map((r) => String(r)));
|
|
753
727
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
728
|
+
return new Set();
|
|
729
|
+
}
|
|
730
|
+
/*
|
|
731
|
+
* The getPropertiesNode method retrieves the properties node from the definition.
|
|
732
|
+
* It throws an error if the properties node is not found or is not a valid object.
|
|
733
|
+
*/
|
|
734
|
+
function getPropertiesNode(definition) {
|
|
735
|
+
if (!('properties' in definition) ||
|
|
736
|
+
typeof definition.properties !== 'object' ||
|
|
737
|
+
definition.properties === null ||
|
|
738
|
+
Array.isArray(definition.properties)) {
|
|
739
|
+
throw new Error("The definition does not contain a valid 'properties' node.");
|
|
740
|
+
}
|
|
741
|
+
return definition.properties;
|
|
742
|
+
}
|
|
743
|
+
/*
|
|
744
|
+
* The determineTypeScriptType method determines the TypeScript type for a property.
|
|
745
|
+
* It handles different cases such as arrays, nested properties, references, and optionsets.
|
|
746
|
+
*/
|
|
747
|
+
function determineTypeScriptType(modelName, propValue, padding, propName) {
|
|
748
|
+
// Check if this property has optionset data first
|
|
749
|
+
if (propValue && propName && hasOptionsetData(propValue)) {
|
|
750
|
+
const enumName = generateOptionsetEnumName(modelName, propName);
|
|
751
|
+
return enumName;
|
|
776
752
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
* It handles different cases such as nested properties and references.
|
|
780
|
-
*/
|
|
781
|
-
static determineArrayType(modelName, itemsNode, padding) {
|
|
782
|
-
if ('properties' in itemsNode && typeof itemsNode.properties === 'object') {
|
|
783
|
-
const inline = this.generatePropertyLines(modelName, itemsNode, false, padding + ' ');
|
|
784
|
-
return `${inline}[]`;
|
|
785
|
-
}
|
|
786
|
-
else if ('$ref' in itemsNode && typeof itemsNode.$ref === 'string') {
|
|
787
|
-
return `${this.extractReferencedType(itemsNode.$ref)}[]`;
|
|
788
|
-
}
|
|
789
|
-
else {
|
|
790
|
-
const itemType = ('type' in itemsNode ? itemsNode.type : 'unknown');
|
|
791
|
-
return this.mapJsonTypeToTypeScript(itemType) + '[]';
|
|
792
|
-
}
|
|
753
|
+
if ((propValue === null || propValue === void 0 ? void 0 : propValue.type) === 'array' && typeof propValue.items === 'object' && propValue.items !== null) {
|
|
754
|
+
return determineArrayType(modelName, propValue.items, padding);
|
|
793
755
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
* It splits the ref value by '/' and returns the last part as the type name.
|
|
797
|
-
* The result is converted to a valid TypeScript identifier (e.g., spaces become underscores).
|
|
798
|
-
*/
|
|
799
|
-
static extractReferencedType(refValue) {
|
|
800
|
-
const parts = refValue.split('/');
|
|
801
|
-
const typeName = parts[parts.length - 1];
|
|
802
|
-
return convertToValidIdentifier(this.normalizeGenericTypeName(typeName));
|
|
803
|
-
}
|
|
804
|
-
/*
|
|
805
|
-
* The mapDataverseTypeToTypeScript method maps Dataverse types to TypeScript types.
|
|
806
|
-
* Based on official Microsoft documentation: https://learn.microsoft.com/en-us/power-apps/maker/data-platform/types-of-fields
|
|
807
|
-
*/
|
|
808
|
-
static mapDataverseTypeToTypeScript(dataverseType) {
|
|
809
|
-
const lowerType = dataverseType.toLowerCase();
|
|
810
|
-
switch (lowerType) {
|
|
811
|
-
// Text-based types - all map to string
|
|
812
|
-
case 'string':
|
|
813
|
-
case 'stringtype':
|
|
814
|
-
case 'text':
|
|
815
|
-
case 'textarea':
|
|
816
|
-
case 'multilinetext':
|
|
817
|
-
case 'memotype':
|
|
818
|
-
case 'memo':
|
|
819
|
-
case 'email':
|
|
820
|
-
case 'phone':
|
|
821
|
-
case 'url':
|
|
822
|
-
case 'tickersymbol':
|
|
823
|
-
case 'entityname':
|
|
824
|
-
case 'uniqueidentifier':
|
|
825
|
-
case 'uniqueidentifiertype':
|
|
826
|
-
return TypeScriptTypes.STRING;
|
|
827
|
-
// Choice/Picklist types - return string (option labels)
|
|
828
|
-
case 'picklist':
|
|
829
|
-
case 'picklisttype':
|
|
830
|
-
case 'choice':
|
|
831
|
-
case 'choicetype':
|
|
832
|
-
case 'choices':
|
|
833
|
-
case 'choicestype':
|
|
834
|
-
case 'multiselectpicklisttype':
|
|
835
|
-
case 'state':
|
|
836
|
-
case 'statetype':
|
|
837
|
-
case 'status':
|
|
838
|
-
case 'statustype':
|
|
839
|
-
return TypeScriptTypes.STRING;
|
|
840
|
-
// Number types
|
|
841
|
-
case 'integer':
|
|
842
|
-
case 'integertype':
|
|
843
|
-
case 'wholenumber':
|
|
844
|
-
case 'duration':
|
|
845
|
-
case 'language':
|
|
846
|
-
case 'timezone':
|
|
847
|
-
return TypeScriptTypes.NUMBER;
|
|
848
|
-
case 'bigint':
|
|
849
|
-
case 'biginttype':
|
|
850
|
-
case 'big':
|
|
851
|
-
case 'timestamp':
|
|
852
|
-
return TypeScriptTypes.NUMBER;
|
|
853
|
-
case 'decimal':
|
|
854
|
-
case 'decimaltype':
|
|
855
|
-
case 'decimalnumber':
|
|
856
|
-
return TypeScriptTypes.NUMBER;
|
|
857
|
-
case 'double':
|
|
858
|
-
case 'doubletype':
|
|
859
|
-
case 'floatingpointnumber':
|
|
860
|
-
return TypeScriptTypes.NUMBER;
|
|
861
|
-
case 'money':
|
|
862
|
-
case 'moneytype':
|
|
863
|
-
case 'currency':
|
|
864
|
-
return TypeScriptTypes.NUMBER;
|
|
865
|
-
// Boolean type
|
|
866
|
-
case 'boolean':
|
|
867
|
-
case 'booleantype':
|
|
868
|
-
case 'yesno':
|
|
869
|
-
case 'twooptions':
|
|
870
|
-
return TypeScriptTypes.BOOLEAN;
|
|
871
|
-
// Date/Time types
|
|
872
|
-
case 'datetime':
|
|
873
|
-
case 'datetimetype':
|
|
874
|
-
case 'dateonly':
|
|
875
|
-
return TypeScriptTypes.DATE;
|
|
876
|
-
// Lookup types - return string (typically GUID or display name)
|
|
877
|
-
case 'lookup':
|
|
878
|
-
case 'lookuptype':
|
|
879
|
-
case 'customer':
|
|
880
|
-
case 'customertype':
|
|
881
|
-
case 'owner':
|
|
882
|
-
case 'ownertype':
|
|
883
|
-
return TypeScriptTypes.STRING;
|
|
884
|
-
// Binary/File types - return string (base64 or URL)
|
|
885
|
-
case 'file':
|
|
886
|
-
case 'filetype':
|
|
887
|
-
case 'image':
|
|
888
|
-
case 'imagetype':
|
|
889
|
-
return TypeScriptTypes.STRING;
|
|
890
|
-
// Special types
|
|
891
|
-
case 'virtual':
|
|
892
|
-
case 'virtualtype':
|
|
893
|
-
case 'formula':
|
|
894
|
-
case 'formulatype':
|
|
895
|
-
return TypeScriptTypes.STRING;
|
|
896
|
-
default:
|
|
897
|
-
return TypeScriptTypes.STRING; // Default Dataverse fields to string
|
|
898
|
-
}
|
|
756
|
+
else if ((propValue === null || propValue === void 0 ? void 0 : propValue.properties) && typeof propValue.properties === 'object') {
|
|
757
|
+
return generatePropertyLines(modelName, propValue, false, padding + ' ');
|
|
899
758
|
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
*/
|
|
903
|
-
static mapJsonTypeToTypeScript(jsonType) {
|
|
904
|
-
switch (jsonType) {
|
|
905
|
-
case 'string':
|
|
906
|
-
return TypeScriptTypes.STRING;
|
|
907
|
-
case 'number':
|
|
908
|
-
case 'integer':
|
|
909
|
-
return TypeScriptTypes.NUMBER;
|
|
910
|
-
case 'boolean':
|
|
911
|
-
return TypeScriptTypes.BOOLEAN;
|
|
912
|
-
case 'object':
|
|
913
|
-
return TypeScriptTypes.OBJECT;
|
|
914
|
-
default:
|
|
915
|
-
return TypeScriptTypes.UNKNOWN;
|
|
916
|
-
}
|
|
759
|
+
else if (typeof (propValue === null || propValue === void 0 ? void 0 : propValue.$ref) === 'string') {
|
|
760
|
+
return extractReferencedType(propValue.$ref);
|
|
917
761
|
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
*/
|
|
921
|
-
static addPropertyComment(lines, propValue, padding) {
|
|
922
|
-
if ((propValue === null || propValue === void 0 ? void 0 : propValue.description) && typeof propValue.description === 'string') {
|
|
923
|
-
lines.push(`${padding} // ${propValue.description}`);
|
|
924
|
-
}
|
|
762
|
+
else {
|
|
763
|
+
return mapJsonTypeToTypeScript(propValue === null || propValue === void 0 ? void 0 : propValue.type);
|
|
925
764
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
const
|
|
934
|
-
|
|
935
|
-
const propertyDefinition = `${propName}${optionalMark}: ${tsType}`;
|
|
936
|
-
lines.push(` ${propertyDefinition};`);
|
|
937
|
-
this.closeInterfaceDeclaration(lines);
|
|
938
|
-
const propertyInfo = new ParameterPropertyInfo(propName, propertyDefinition);
|
|
939
|
-
return { modelCode: lines.join('\n'), propertyInfo };
|
|
940
|
-
}
|
|
941
|
-
/*
|
|
942
|
-
* The addInterfaceDeclaration method adds the interface declaration to the lines.
|
|
943
|
-
* It takes the lines to write to and model name as parameters.
|
|
944
|
-
*/
|
|
945
|
-
static addInterfaceDeclaration(lines, modelName) {
|
|
946
|
-
lines.push(`export interface ${convertToValidIdentifier(modelName)} {`);
|
|
765
|
+
}
|
|
766
|
+
/*
|
|
767
|
+
* The determineArrayType method determines the TypeScript type for an array property.
|
|
768
|
+
* It handles different cases such as nested properties and references.
|
|
769
|
+
*/
|
|
770
|
+
function determineArrayType(modelName, itemsNode, padding) {
|
|
771
|
+
if ('properties' in itemsNode && typeof itemsNode.properties === 'object') {
|
|
772
|
+
const inline = generatePropertyLines(modelName, itemsNode, false, padding + ' ');
|
|
773
|
+
return `${inline}[]`;
|
|
947
774
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
*/
|
|
951
|
-
static closeInterfaceDeclaration(lines) {
|
|
952
|
-
lines.push('}');
|
|
775
|
+
else if ('$ref' in itemsNode && typeof itemsNode.$ref === 'string') {
|
|
776
|
+
return `${extractReferencedType(itemsNode.$ref)}[]`;
|
|
953
777
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
*/
|
|
958
|
-
static determineParameterType(parameterObject) {
|
|
959
|
-
if ('type' in parameterObject && typeof parameterObject.type === 'string') {
|
|
960
|
-
return this.mapJsonTypeToTypeScript(parameterObject.type);
|
|
961
|
-
}
|
|
962
|
-
return 'unknown';
|
|
778
|
+
else {
|
|
779
|
+
const itemType = ('type' in itemsNode ? itemsNode.type : 'unknown');
|
|
780
|
+
return mapJsonTypeToTypeScript(itemType) + '[]';
|
|
963
781
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
782
|
+
}
|
|
783
|
+
/*
|
|
784
|
+
* The extractReferencedType method extracts the referenced type from the ref value.
|
|
785
|
+
* It splits the ref value by '/' and returns the last part as the type name.
|
|
786
|
+
* The result is converted to a valid TypeScript identifier (e.g., spaces become underscores).
|
|
787
|
+
*/
|
|
788
|
+
function extractReferencedType(refValue) {
|
|
789
|
+
const parts = refValue.split('/');
|
|
790
|
+
const typeName = parts[parts.length - 1];
|
|
791
|
+
return convertToValidIdentifier(normalizeGenericTypeName(typeName));
|
|
792
|
+
}
|
|
793
|
+
/*
|
|
794
|
+
* The mapDataverseTypeToTypeScript method maps Dataverse types to TypeScript types.
|
|
795
|
+
* Based on official Microsoft documentation: https://learn.microsoft.com/en-us/power-apps/maker/data-platform/types-of-fields
|
|
796
|
+
*/
|
|
797
|
+
function mapDataverseTypeToTypeScript(dataverseType) {
|
|
798
|
+
const lowerType = dataverseType.toLowerCase();
|
|
799
|
+
switch (lowerType) {
|
|
800
|
+
// Text-based types - all map to string
|
|
801
|
+
case 'string':
|
|
802
|
+
case 'stringtype':
|
|
803
|
+
case 'text':
|
|
804
|
+
case 'textarea':
|
|
805
|
+
case 'multilinetext':
|
|
806
|
+
case 'memotype':
|
|
807
|
+
case 'memo':
|
|
808
|
+
case 'email':
|
|
809
|
+
case 'phone':
|
|
810
|
+
case 'url':
|
|
811
|
+
case 'tickersymbol':
|
|
812
|
+
case 'entityname':
|
|
813
|
+
case 'uniqueidentifier':
|
|
814
|
+
case 'uniqueidentifiertype':
|
|
815
|
+
return TypeScriptTypes.STRING;
|
|
816
|
+
// Choice/Picklist types - return string (option labels)
|
|
817
|
+
case 'picklist':
|
|
818
|
+
case 'picklisttype':
|
|
819
|
+
case 'choice':
|
|
820
|
+
case 'choicetype':
|
|
821
|
+
case 'choices':
|
|
822
|
+
case 'choicestype':
|
|
823
|
+
case 'multiselectpicklisttype':
|
|
824
|
+
case 'state':
|
|
825
|
+
case 'statetype':
|
|
826
|
+
case 'status':
|
|
827
|
+
case 'statustype':
|
|
828
|
+
return TypeScriptTypes.STRING;
|
|
829
|
+
// Number types
|
|
830
|
+
case 'integer':
|
|
831
|
+
case 'integertype':
|
|
832
|
+
case 'wholenumber':
|
|
833
|
+
case 'duration':
|
|
834
|
+
case 'language':
|
|
835
|
+
case 'timezone':
|
|
836
|
+
return TypeScriptTypes.NUMBER;
|
|
837
|
+
case 'bigint':
|
|
838
|
+
case 'biginttype':
|
|
839
|
+
case 'big':
|
|
840
|
+
case 'timestamp':
|
|
841
|
+
return TypeScriptTypes.NUMBER;
|
|
842
|
+
case 'decimal':
|
|
843
|
+
case 'decimaltype':
|
|
844
|
+
case 'decimalnumber':
|
|
845
|
+
return TypeScriptTypes.NUMBER;
|
|
846
|
+
case 'double':
|
|
847
|
+
case 'doubletype':
|
|
848
|
+
case 'floatingpointnumber':
|
|
849
|
+
return TypeScriptTypes.NUMBER;
|
|
850
|
+
case 'money':
|
|
851
|
+
case 'moneytype':
|
|
852
|
+
case 'currency':
|
|
853
|
+
return TypeScriptTypes.NUMBER;
|
|
854
|
+
// Boolean type
|
|
855
|
+
case 'boolean':
|
|
856
|
+
case 'booleantype':
|
|
857
|
+
case 'yesno':
|
|
858
|
+
case 'twooptions':
|
|
859
|
+
return TypeScriptTypes.BOOLEAN;
|
|
860
|
+
// Date/Time types
|
|
861
|
+
case 'datetime':
|
|
862
|
+
case 'datetimetype':
|
|
863
|
+
case 'dateonly':
|
|
864
|
+
return TypeScriptTypes.DATE;
|
|
865
|
+
// Lookup types - return string (typically GUID or display name)
|
|
866
|
+
case 'lookup':
|
|
867
|
+
case 'lookuptype':
|
|
868
|
+
case 'customer':
|
|
869
|
+
case 'customertype':
|
|
870
|
+
case 'owner':
|
|
871
|
+
case 'ownertype':
|
|
872
|
+
return TypeScriptTypes.STRING;
|
|
873
|
+
// Binary/File types - return string (base64 or URL)
|
|
874
|
+
case 'file':
|
|
875
|
+
case 'filetype':
|
|
876
|
+
case 'image':
|
|
877
|
+
case 'imagetype':
|
|
878
|
+
return TypeScriptTypes.STRING;
|
|
879
|
+
// Special types
|
|
880
|
+
case 'virtual':
|
|
881
|
+
case 'virtualtype':
|
|
882
|
+
case 'formula':
|
|
883
|
+
case 'formulatype':
|
|
884
|
+
return TypeScriptTypes.STRING;
|
|
885
|
+
default:
|
|
886
|
+
return TypeScriptTypes.STRING; // Default Dataverse fields to string
|
|
971
887
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
888
|
+
}
|
|
889
|
+
/*
|
|
890
|
+
* The mapJsonTypeToTypeScript method maps JSON types to TypeScript types.
|
|
891
|
+
*/
|
|
892
|
+
function mapJsonTypeToTypeScript(jsonType) {
|
|
893
|
+
switch (jsonType) {
|
|
894
|
+
case 'string':
|
|
895
|
+
return TypeScriptTypes.STRING;
|
|
896
|
+
case 'number':
|
|
897
|
+
case 'integer':
|
|
898
|
+
return TypeScriptTypes.NUMBER;
|
|
899
|
+
case 'boolean':
|
|
900
|
+
return TypeScriptTypes.BOOLEAN;
|
|
901
|
+
case 'object':
|
|
902
|
+
return TypeScriptTypes.OBJECT;
|
|
903
|
+
default:
|
|
904
|
+
return TypeScriptTypes.UNKNOWN;
|
|
983
905
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
}
|
|
992
|
-
const vfs = getVfs();
|
|
993
|
-
// Services are always emitted under <outputDir>/generated/services
|
|
994
|
-
const servicesDir = vfs.join(_a._outputDir, this.SERVICES_FOLDER);
|
|
995
|
-
// path to dataSourcesInfo.ts
|
|
996
|
-
const dataSourceInfoPath = vfs.join(_a._schemaFolderPath, 'appschemas', 'dataSourcesInfo');
|
|
997
|
-
let relativePath = path.relative(servicesDir, dataSourceInfoPath).replace(/\\/g, '/');
|
|
998
|
-
if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) {
|
|
999
|
-
relativePath = `./${relativePath}`;
|
|
1000
|
-
}
|
|
1001
|
-
// Base imports common to both CRUD & execute contexts
|
|
1002
|
-
const imports = new Set([
|
|
1003
|
-
`import { dataSourcesInfo } from '${relativePath}';`,
|
|
1004
|
-
"import type { IOperationResult } from '@microsoft/power-apps/data';",
|
|
1005
|
-
"import { getClient } from '@microsoft/power-apps/data';",
|
|
1006
|
-
]);
|
|
1007
|
-
// Reserved for future differentiation if importType impacts imports
|
|
1008
|
-
// Currently identical for 'crud' | 'execute' | 'both'.
|
|
1009
|
-
return imports;
|
|
1010
|
-
}
|
|
1011
|
-
/*
|
|
1012
|
-
* The processPaths method processes the paths node of the Swagger schema.
|
|
1013
|
-
* It extracts the operations and generates the methods for the service class.
|
|
1014
|
-
* It takes the paths node, model name, data source name, and imports set as parameters.
|
|
1015
|
-
* It returns a list of operations as strings.
|
|
1016
|
-
*/
|
|
1017
|
-
static processPaths(pathsNode, modelName, dataSourceName, imports, parameterTypes) {
|
|
1018
|
-
const operations = [];
|
|
1019
|
-
for (const pathKey in pathsNode) {
|
|
1020
|
-
if (!Object.prototype.hasOwnProperty.call(pathsNode, pathKey)) {
|
|
1021
|
-
continue;
|
|
1022
|
-
}
|
|
1023
|
-
const pathObject = pathsNode[pathKey];
|
|
1024
|
-
if (typeof pathObject === 'object' && pathObject !== null && !Array.isArray(pathObject)) {
|
|
1025
|
-
const pathObjectTyped = pathObject;
|
|
1026
|
-
for (const operationKey in pathObjectTyped) {
|
|
1027
|
-
if (!Object.prototype.hasOwnProperty.call(pathObjectTyped, operationKey)) {
|
|
1028
|
-
continue;
|
|
1029
|
-
}
|
|
1030
|
-
const operationObject = pathObjectTyped[operationKey];
|
|
1031
|
-
if (typeof operationObject === 'object' &&
|
|
1032
|
-
operationObject !== null &&
|
|
1033
|
-
!Array.isArray(operationObject)) {
|
|
1034
|
-
this.processOperation(operationObject, modelName, dataSourceName, imports, operations, parameterTypes);
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
return operations;
|
|
906
|
+
}
|
|
907
|
+
/*
|
|
908
|
+
* The addPropertyComment method adds a comment for the property.
|
|
909
|
+
*/
|
|
910
|
+
function addPropertyComment(lines, propValue, padding) {
|
|
911
|
+
if ((propValue === null || propValue === void 0 ? void 0 : propValue.description) && typeof propValue.description === 'string') {
|
|
912
|
+
lines.push(`${padding} // ${propValue.description}`);
|
|
1040
913
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
914
|
+
}
|
|
915
|
+
/*
|
|
916
|
+
* The generateModelFromParameters method generates TypeScript models from the parameters.
|
|
917
|
+
*/
|
|
918
|
+
function generateModelFromParameters(modelName, parameterObject) {
|
|
919
|
+
const lines = [];
|
|
920
|
+
addInterfaceDeclaration(lines, modelName);
|
|
921
|
+
const propName = getParameterName(parameterObject, 'name');
|
|
922
|
+
const tsType = determineParameterType(parameterObject);
|
|
923
|
+
const optionalMark = isParameterRequired(parameterObject) ? '' : '?';
|
|
924
|
+
const propertyDefinition = `${propName}${optionalMark}: ${tsType}`;
|
|
925
|
+
lines.push(` ${propertyDefinition};`);
|
|
926
|
+
closeInterfaceDeclaration(lines);
|
|
927
|
+
const propertyInfo = new ParameterPropertyInfo(propName, propertyDefinition);
|
|
928
|
+
return { modelCode: lines.join('\n'), propertyInfo };
|
|
929
|
+
}
|
|
930
|
+
/*
|
|
931
|
+
* The addInterfaceDeclaration method adds the interface declaration to the lines.
|
|
932
|
+
* It takes the lines to write to and model name as parameters.
|
|
933
|
+
*/
|
|
934
|
+
function addInterfaceDeclaration(lines, modelName) {
|
|
935
|
+
lines.push(`export interface ${convertToValidIdentifier(modelName)} {`);
|
|
936
|
+
}
|
|
937
|
+
/*
|
|
938
|
+
* The closeInterfaceDeclaration method closes the interface declaration.
|
|
939
|
+
*/
|
|
940
|
+
function closeInterfaceDeclaration(lines) {
|
|
941
|
+
lines.push('}');
|
|
942
|
+
}
|
|
943
|
+
/*
|
|
944
|
+
* The determineParameterType method determines the type of the parameter.
|
|
945
|
+
* It takes the parameter object as input and returns the type as a string.
|
|
946
|
+
*/
|
|
947
|
+
function determineParameterType(parameterObject) {
|
|
948
|
+
if ('type' in parameterObject && typeof parameterObject.type === 'string') {
|
|
949
|
+
return mapJsonTypeToTypeScript(parameterObject.type);
|
|
950
|
+
}
|
|
951
|
+
return 'unknown';
|
|
952
|
+
}
|
|
953
|
+
/*
|
|
954
|
+
* The isParameterRequired method checks if the parameter is required.
|
|
955
|
+
* It takes the parameter object as input and returns true if required, otherwise false.
|
|
956
|
+
*/
|
|
957
|
+
function isParameterRequired(parameterObject) {
|
|
958
|
+
return ('required' in parameterObject &&
|
|
959
|
+
(parameterObject.required === true || parameterObject.required === 'true'));
|
|
960
|
+
}
|
|
961
|
+
/*
|
|
962
|
+
* The generateServiceFromPaths method generates the service class from the paths node.
|
|
963
|
+
*/
|
|
964
|
+
function generateServiceFromPaths(modelName, dataSourceName, pathsNode, parameterTypes) {
|
|
965
|
+
const lines = [];
|
|
966
|
+
addCopyrightNotice(lines);
|
|
967
|
+
const imports = collectStandardImports();
|
|
968
|
+
const operations = processPaths(pathsNode, modelName, dataSourceName, imports, parameterTypes);
|
|
969
|
+
addImports(lines, imports);
|
|
970
|
+
addClassDefinition(lines, modelName, dataSourceName, operations);
|
|
971
|
+
return lines.join('\n');
|
|
972
|
+
}
|
|
973
|
+
/*
|
|
974
|
+
* The collectStandardImports method collects the standard imports for the service class.
|
|
975
|
+
* It returns a Set of import statements.
|
|
976
|
+
*/
|
|
977
|
+
function collectStandardImports(importType = 'execute') {
|
|
978
|
+
if (!_schemaFolderPath || !_outputDir) {
|
|
979
|
+
throw new Error('collectStandardImports called before generator context was initialized. Ensure generateModelService() was invoked.');
|
|
980
|
+
}
|
|
981
|
+
const vfs = getVfs();
|
|
982
|
+
// Services are always emitted under <outputDir>/generated/services
|
|
983
|
+
const servicesDir = vfs.join(_outputDir, SERVICES_FOLDER);
|
|
984
|
+
// path to dataSourcesInfo.ts
|
|
985
|
+
const dataSourceInfoPath = vfs.join(_schemaFolderPath, 'appschemas', 'dataSourcesInfo');
|
|
986
|
+
let relativePath = path.relative(servicesDir, dataSourceInfoPath).replace(/\\/g, '/');
|
|
987
|
+
if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) {
|
|
988
|
+
relativePath = `./${relativePath}`;
|
|
989
|
+
}
|
|
990
|
+
// Base imports common to both CRUD & execute contexts
|
|
991
|
+
const imports = new Set([
|
|
992
|
+
`import { dataSourcesInfo } from '${relativePath}';`,
|
|
993
|
+
"import type { IOperationResult } from '@microsoft/power-apps/data';",
|
|
994
|
+
"import { getClient } from '@microsoft/power-apps/data';",
|
|
995
|
+
]);
|
|
996
|
+
// Reserved for future differentiation if importType impacts imports
|
|
997
|
+
// Currently identical for 'crud' | 'execute' | 'both'.
|
|
998
|
+
return imports;
|
|
999
|
+
}
|
|
1000
|
+
/*
|
|
1001
|
+
* The processPaths method processes the paths node of the Swagger schema.
|
|
1002
|
+
* It extracts the operations and generates the methods for the service class.
|
|
1003
|
+
* It takes the paths node, model name, data source name, and imports set as parameters.
|
|
1004
|
+
* It returns a list of operations as strings.
|
|
1005
|
+
*/
|
|
1006
|
+
function processPaths(pathsNode, modelName, dataSourceName, imports, parameterTypes) {
|
|
1007
|
+
const operations = [];
|
|
1008
|
+
for (const pathKey in pathsNode) {
|
|
1009
|
+
if (!Object.prototype.hasOwnProperty.call(pathsNode, pathKey)) {
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
const pathObject = pathsNode[pathKey];
|
|
1013
|
+
if (typeof pathObject === 'object' && pathObject !== null && !Array.isArray(pathObject)) {
|
|
1014
|
+
const pathObjectTyped = pathObject;
|
|
1015
|
+
for (const operationKey in pathObjectTyped) {
|
|
1016
|
+
if (!Object.prototype.hasOwnProperty.call(pathObjectTyped, operationKey)) {
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
const operationObject = pathObjectTyped[operationKey];
|
|
1020
|
+
if (typeof operationObject === 'object' &&
|
|
1021
|
+
operationObject !== null &&
|
|
1022
|
+
!Array.isArray(operationObject)) {
|
|
1023
|
+
processOperation(operationObject, modelName, dataSourceName, imports, operations, parameterTypes);
|
|
1084
1024
|
}
|
|
1085
1025
|
}
|
|
1086
1026
|
}
|
|
1087
|
-
return 'void';
|
|
1088
1027
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1028
|
+
return operations;
|
|
1029
|
+
}
|
|
1030
|
+
/*
|
|
1031
|
+
* The processOperation method processes each operation in the path object.
|
|
1032
|
+
* It extracts the operation ID, summary, description, and parameters.
|
|
1033
|
+
* It generates the method for the service class.
|
|
1034
|
+
*/
|
|
1035
|
+
function processOperation(operationObject, modelName, dataSourceName, imports, operations, parameterTypes) {
|
|
1036
|
+
if (!('operationId' in operationObject)) {
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
const operationId = String(operationObject.operationId);
|
|
1040
|
+
const summary = 'summary' in operationObject ? String(operationObject.summary) : '';
|
|
1041
|
+
const description = 'description' in operationObject ? String(operationObject.description) : '';
|
|
1042
|
+
const outputModel = determineOutputModel(operationObject, modelName, imports);
|
|
1043
|
+
const { parametersList, parametersProps } = collectParameters(operationObject, modelName, imports, parameterTypes);
|
|
1044
|
+
generateOperationMethod(operations, operationId, summary, description, parametersList, parametersProps, outputModel, modelName);
|
|
1045
|
+
}
|
|
1046
|
+
/*
|
|
1047
|
+
* The determineOutputModel method determines the output model for the operation.
|
|
1048
|
+
* It takes the operation object, model name, and imports set as parameters.
|
|
1049
|
+
* It returns the output model as a string.
|
|
1050
|
+
*/
|
|
1051
|
+
function determineOutputModel(operationObject, modelName, imports) {
|
|
1052
|
+
if ('responses' in operationObject &&
|
|
1053
|
+
typeof operationObject.responses === 'object' &&
|
|
1054
|
+
operationObject.responses !== null) {
|
|
1055
|
+
const responsesObj = operationObject.responses;
|
|
1056
|
+
if (typeof responsesObj === 'object' &&
|
|
1057
|
+
responsesObj !== null &&
|
|
1058
|
+
!Array.isArray(responsesObj) &&
|
|
1059
|
+
'200' in responsesObj) {
|
|
1060
|
+
const responsesObject = responsesObj;
|
|
1061
|
+
const response200Value = responsesObject['200'];
|
|
1062
|
+
if (typeof response200Value === 'object' &&
|
|
1063
|
+
response200Value !== null &&
|
|
1064
|
+
!Array.isArray(response200Value)) {
|
|
1065
|
+
const response200Obj = response200Value;
|
|
1066
|
+
if ('schema' in response200Obj &&
|
|
1067
|
+
typeof response200Obj.schema === 'object' &&
|
|
1068
|
+
response200Obj.schema !== null &&
|
|
1069
|
+
!Array.isArray(response200Obj.schema)) {
|
|
1070
|
+
const schemaObj = response200Obj.schema;
|
|
1071
|
+
return extractOutputModelFromSchema(schemaObj, modelName, imports);
|
|
1072
|
+
}
|
|
1105
1073
|
}
|
|
1106
1074
|
}
|
|
1107
|
-
return { parametersList, parametersProps };
|
|
1108
1075
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1076
|
+
return 'void';
|
|
1077
|
+
}
|
|
1078
|
+
/*
|
|
1079
|
+
* The collectParameters method collects the parameters from the operation object.
|
|
1080
|
+
* It takes the operation object, model name, and imports set as parameters.
|
|
1081
|
+
* It returns an object containing the parameters list and parameters properties.
|
|
1082
|
+
*/
|
|
1083
|
+
function collectParameters(operationObject, modelName, imports, parameterTypes) {
|
|
1084
|
+
let parametersList = [];
|
|
1085
|
+
let parametersProps = [];
|
|
1086
|
+
if ('parameters' in operationObject && Array.isArray(operationObject.parameters)) {
|
|
1087
|
+
const paramsArray = operationObject.parameters;
|
|
1088
|
+
const orderedParams = getOrderedPropertiesByRequired(paramsArray);
|
|
1089
|
+
for (const property of orderedParams) {
|
|
1090
|
+
// property.Value in C# is property.value in TS - use property.value for the parameter object
|
|
1091
|
+
const result = processRequestParameters(property.value, modelName, imports, parametersList, parametersProps, 'name', parameterTypes);
|
|
1092
|
+
parametersList = result[0];
|
|
1093
|
+
parametersProps = result[1];
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return { parametersList, parametersProps };
|
|
1097
|
+
}
|
|
1098
|
+
/*
|
|
1099
|
+
* The addImports method adds the import statements to the lines.
|
|
1100
|
+
* It takes the lines to write to and the imports set as parameters.
|
|
1101
|
+
*/
|
|
1102
|
+
function addImports(lines, imports) {
|
|
1103
|
+
const sortedImports = Array.from(imports);
|
|
1104
|
+
sortedImports.sort();
|
|
1105
|
+
lines.push(...sortedImports);
|
|
1106
|
+
}
|
|
1107
|
+
/*
|
|
1108
|
+
* The addClassDefinition method adds the class definition to the lines.
|
|
1109
|
+
* It takes the lines to write to, model name, data source name, and operations as parameters.
|
|
1110
|
+
*/
|
|
1111
|
+
function addClassDefinition(lines, modelName, dataSourceName, operations) {
|
|
1112
|
+
addServiceClassHeader(lines, modelName, dataSourceName);
|
|
1113
|
+
lines.push(...operations);
|
|
1114
|
+
lines.push('}');
|
|
1115
|
+
lines.push('');
|
|
1116
|
+
}
|
|
1117
|
+
/*
|
|
1118
|
+
* The addServiceClassHeader method adds the service class header to the lines.
|
|
1119
|
+
* It takes the lines to write to, model name, and data source name as parameters.
|
|
1120
|
+
*/
|
|
1121
|
+
function addServiceClassHeader(lines, modelName, dataSourceName, additionalLine = true) {
|
|
1122
|
+
if (additionalLine) {
|
|
1126
1123
|
lines.push('');
|
|
1127
1124
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
valueObj.items !== null &&
|
|
1182
|
-
!Array.isArray(valueObj.items)) {
|
|
1183
|
-
const itemsObj = valueObj.items;
|
|
1184
|
-
if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
|
|
1185
|
-
const refType = this.extractReferencedType(itemsObj.$ref);
|
|
1186
|
-
setOutputModel(`{ value: ${refType}[] }`);
|
|
1187
|
-
this.addImport(refType, modelName, imports);
|
|
1188
|
-
return true;
|
|
1189
|
-
}
|
|
1125
|
+
lines.push(`export class ${modelName}Service {`);
|
|
1126
|
+
addServiceFields(lines, dataSourceName);
|
|
1127
|
+
}
|
|
1128
|
+
/*
|
|
1129
|
+
* The extractOutputModelFromSchema method extracts the output model from the schema object.
|
|
1130
|
+
*/
|
|
1131
|
+
function extractOutputModelFromSchema(schemaObj, modelName, imports) {
|
|
1132
|
+
let outputModel = '';
|
|
1133
|
+
if (tryExtractArrayOutputModel(schemaObj, modelName, imports, (result) => {
|
|
1134
|
+
outputModel = result;
|
|
1135
|
+
})) {
|
|
1136
|
+
return outputModel;
|
|
1137
|
+
}
|
|
1138
|
+
if (tryExtractDirectRefOutputModel(schemaObj, modelName, imports, (result) => {
|
|
1139
|
+
outputModel = result;
|
|
1140
|
+
})) {
|
|
1141
|
+
return outputModel;
|
|
1142
|
+
}
|
|
1143
|
+
const [found, typeOutputModel] = tryExtractTypeOutputModel(schemaObj);
|
|
1144
|
+
if (found) {
|
|
1145
|
+
outputModel = typeOutputModel;
|
|
1146
|
+
return outputModel;
|
|
1147
|
+
}
|
|
1148
|
+
return 'void'; // Default to void if no specific type is defined
|
|
1149
|
+
}
|
|
1150
|
+
/*
|
|
1151
|
+
* The tryExtractArrayOutputModel method extracts the array output model from the schema object.
|
|
1152
|
+
* Returns true if type is successfully extracted, otherwise false.
|
|
1153
|
+
*/
|
|
1154
|
+
function tryExtractArrayOutputModel(schemaObj, modelName, imports, setOutputModel) {
|
|
1155
|
+
// First case: properties.value.type === 'array' && properties.value.items.$ref
|
|
1156
|
+
if ('properties' in schemaObj &&
|
|
1157
|
+
typeof schemaObj.properties === 'object' &&
|
|
1158
|
+
schemaObj.properties !== null &&
|
|
1159
|
+
!Array.isArray(schemaObj.properties)) {
|
|
1160
|
+
const propertiesObj = schemaObj.properties;
|
|
1161
|
+
if ('value' in propertiesObj &&
|
|
1162
|
+
typeof propertiesObj.value === 'object' &&
|
|
1163
|
+
propertiesObj.value !== null &&
|
|
1164
|
+
!Array.isArray(propertiesObj.value)) {
|
|
1165
|
+
const valueObj = propertiesObj.value;
|
|
1166
|
+
if ('type' in valueObj &&
|
|
1167
|
+
valueObj.type === 'array' &&
|
|
1168
|
+
'items' in valueObj &&
|
|
1169
|
+
typeof valueObj.items === 'object' &&
|
|
1170
|
+
valueObj.items !== null &&
|
|
1171
|
+
!Array.isArray(valueObj.items)) {
|
|
1172
|
+
const itemsObj = valueObj.items;
|
|
1173
|
+
if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
|
|
1174
|
+
const refType = extractReferencedType(itemsObj.$ref);
|
|
1175
|
+
setOutputModel(`{ value: ${refType}[] }`);
|
|
1176
|
+
addImport(refType, modelName, imports);
|
|
1177
|
+
return true;
|
|
1190
1178
|
}
|
|
1191
1179
|
}
|
|
1192
1180
|
}
|
|
1193
|
-
// Second case: type === 'array' && items.$ref
|
|
1194
|
-
if ('type' in schemaObj &&
|
|
1195
|
-
schemaObj.type === 'array' &&
|
|
1196
|
-
'items' in schemaObj &&
|
|
1197
|
-
typeof schemaObj.items === 'object' &&
|
|
1198
|
-
schemaObj.items !== null &&
|
|
1199
|
-
!Array.isArray(schemaObj.items)) {
|
|
1200
|
-
const itemsObj = schemaObj.items;
|
|
1201
|
-
if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
|
|
1202
|
-
const refType = this.extractReferencedType(itemsObj.$ref);
|
|
1203
|
-
setOutputModel(`${refType}[]`);
|
|
1204
|
-
this.addImport(refType, modelName, imports);
|
|
1205
|
-
return true;
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
return false;
|
|
1209
1181
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
const outputModel = this.simplifyGenericTypeName(this.extractReferencedType(itemsObj.$ref));
|
|
1223
|
-
setOutputModel(outputModel);
|
|
1224
|
-
this.addImport(outputModel, modelName, imports);
|
|
1225
|
-
return true;
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
// Case: $ref
|
|
1229
|
-
if ('$ref' in schemaObj && typeof schemaObj.$ref === 'string') {
|
|
1230
|
-
const outputModel = this.simplifyGenericTypeName(this.extractReferencedType(schemaObj.$ref));
|
|
1231
|
-
setOutputModel(outputModel);
|
|
1232
|
-
this.addImport(outputModel, modelName, imports);
|
|
1182
|
+
// Second case: type === 'array' && items.$ref
|
|
1183
|
+
if ('type' in schemaObj &&
|
|
1184
|
+
schemaObj.type === 'array' &&
|
|
1185
|
+
'items' in schemaObj &&
|
|
1186
|
+
typeof schemaObj.items === 'object' &&
|
|
1187
|
+
schemaObj.items !== null &&
|
|
1188
|
+
!Array.isArray(schemaObj.items)) {
|
|
1189
|
+
const itemsObj = schemaObj.items;
|
|
1190
|
+
if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
|
|
1191
|
+
const refType = extractReferencedType(itemsObj.$ref);
|
|
1192
|
+
setOutputModel(`${refType}[]`);
|
|
1193
|
+
addImport(refType, modelName, imports);
|
|
1233
1194
|
return true;
|
|
1234
1195
|
}
|
|
1235
|
-
return false;
|
|
1236
1196
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1197
|
+
return false;
|
|
1198
|
+
}
|
|
1199
|
+
/*
|
|
1200
|
+
* The tryExtractDirectRefOutputModel method extracts the direct reference output model from the schema object.
|
|
1201
|
+
* Returns true if type is successfully extracted, otherwise false.
|
|
1202
|
+
*/
|
|
1203
|
+
function tryExtractDirectRefOutputModel(schemaObj, modelName, imports, setOutputModel) {
|
|
1204
|
+
// Case: items.$ref
|
|
1205
|
+
if ('items' in schemaObj &&
|
|
1206
|
+
typeof schemaObj.items === 'object' &&
|
|
1207
|
+
schemaObj.items !== null &&
|
|
1208
|
+
!Array.isArray(schemaObj.items)) {
|
|
1209
|
+
const itemsObj = schemaObj.items;
|
|
1210
|
+
if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
|
|
1211
|
+
const outputModel = simplifyGenericTypeName(extractReferencedType(itemsObj.$ref));
|
|
1212
|
+
setOutputModel(outputModel);
|
|
1213
|
+
addImport(outputModel, modelName, imports);
|
|
1214
|
+
return true;
|
|
1244
1215
|
}
|
|
1245
|
-
return [false, ''];
|
|
1246
|
-
}
|
|
1247
|
-
/*
|
|
1248
|
-
* The addImport method adds an import statement to the imports set.
|
|
1249
|
-
* Takes the type name, model name, and imports set as parameters.
|
|
1250
|
-
*/
|
|
1251
|
-
static addImport(typeName, modelName, imports) {
|
|
1252
|
-
imports.add(`import type { ${typeName} } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
1253
1216
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1217
|
+
// Case: $ref
|
|
1218
|
+
if ('$ref' in schemaObj && typeof schemaObj.$ref === 'string') {
|
|
1219
|
+
const outputModel = simplifyGenericTypeName(extractReferencedType(schemaObj.$ref));
|
|
1220
|
+
setOutputModel(outputModel);
|
|
1221
|
+
addImport(outputModel, modelName, imports);
|
|
1222
|
+
return true;
|
|
1259
1223
|
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1224
|
+
return false;
|
|
1225
|
+
}
|
|
1226
|
+
/*
|
|
1227
|
+
* The tryExtractTypeOutputModel method extracts the type output model from the schema object.
|
|
1228
|
+
* Returns a tuple: [found: boolean, outputModel: string].
|
|
1229
|
+
*/
|
|
1230
|
+
function tryExtractTypeOutputModel(schemaObj) {
|
|
1231
|
+
if ('type' in schemaObj && typeof schemaObj.type === 'string') {
|
|
1232
|
+
return [true, mapJsonTypeToTypeScript(schemaObj.type)];
|
|
1266
1233
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1234
|
+
return [false, ''];
|
|
1235
|
+
}
|
|
1236
|
+
/*
|
|
1237
|
+
* The addImport method adds an import statement to the imports set.
|
|
1238
|
+
* Takes the type name, model name, and imports set as parameters.
|
|
1239
|
+
*/
|
|
1240
|
+
function addImport(typeName, modelName, imports) {
|
|
1241
|
+
imports.add(`import type { ${typeName} } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
1242
|
+
}
|
|
1243
|
+
/*
|
|
1244
|
+
* The simplifyGenericTypeName method simplifies the generic type name by removing the generic parameters.
|
|
1245
|
+
*/
|
|
1246
|
+
function simplifyGenericTypeName(typeName) {
|
|
1247
|
+
return normalizeGenericTypeName(typeName);
|
|
1248
|
+
}
|
|
1249
|
+
/*
|
|
1250
|
+
* The normalizeGenericTypeName method normalizes the generic type name.
|
|
1251
|
+
* It replaces the brackets with underscores and returns the normalized name.
|
|
1252
|
+
*/
|
|
1253
|
+
function normalizeGenericTypeName(typeName) {
|
|
1254
|
+
return typeName.replace(/\[/g, '_').replace(/\]/g, '');
|
|
1255
|
+
}
|
|
1256
|
+
/*
|
|
1257
|
+
* The processRequestParameters method processes the parameters of the operation.
|
|
1258
|
+
* It extracts the parameter name and type from the schema and adds them to the lists.
|
|
1259
|
+
* It takes the parameter object, model name, imports set, parameters list, and parameters properties as parameters.
|
|
1260
|
+
*/
|
|
1261
|
+
function processRequestParameters(param, modelName, imports, parametersList, parametersProps, paramNodeName, parameterTypes) {
|
|
1262
|
+
if (typeof param !== 'object' || param === null || Array.isArray(param)) {
|
|
1295
1263
|
return [parametersList, parametersProps];
|
|
1296
1264
|
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
static getParameterName(paramObj, paramNodeName) {
|
|
1303
|
-
const refNode = paramObj.$ref;
|
|
1304
|
-
if (refNode && typeof refNode === 'string') {
|
|
1305
|
-
const refValue = refNode;
|
|
1306
|
-
if (refValue.toLowerCase().startsWith('#/parameters/')) {
|
|
1307
|
-
const parameterName = refValue.substring('#/parameters/'.length);
|
|
1308
|
-
return parameterName;
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
const nameNode = paramObj[paramNodeName];
|
|
1312
|
-
return nameNode ? String(nameNode) : 'param';
|
|
1313
|
-
}
|
|
1314
|
-
/*
|
|
1315
|
-
* The getParameterType method extracts the type of the parameter from the schema.
|
|
1316
|
-
* It handles the case where the schema is an array or a reference.
|
|
1317
|
-
* It takes the parameter object, model name, and imports set as parameters.
|
|
1318
|
-
*/
|
|
1319
|
-
static getParameterType(paramObj, modelName, imports) {
|
|
1320
|
-
if ('schema' in paramObj &&
|
|
1321
|
-
typeof paramObj.schema === 'object' &&
|
|
1322
|
-
paramObj.schema !== null &&
|
|
1323
|
-
!Array.isArray(paramObj.schema)) {
|
|
1324
|
-
return this.getSchemaBasedType(paramObj.schema, modelName, imports);
|
|
1325
|
-
}
|
|
1326
|
-
if ('$ref' in paramObj && typeof paramObj.$ref === 'string') {
|
|
1327
|
-
return this.getRefBasedType(paramObj.$ref, modelName, imports);
|
|
1328
|
-
}
|
|
1329
|
-
if ('type' in paramObj && typeof paramObj.type === 'string') {
|
|
1330
|
-
return this.mapJsonTypeToTypeScript(paramObj.type);
|
|
1331
|
-
}
|
|
1332
|
-
return 'unknown';
|
|
1265
|
+
const paramObj = param;
|
|
1266
|
+
const paramName = getParameterName(paramObj, paramNodeName);
|
|
1267
|
+
// Skip 'connectionId' parameter
|
|
1268
|
+
if (paramName.toLowerCase() === 'connectionid') {
|
|
1269
|
+
return [parametersList, parametersProps];
|
|
1333
1270
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
* It takes the schema object, model name, and imports set as parameters.
|
|
1338
|
-
*/
|
|
1339
|
-
static getSchemaBasedType(schemaObj, modelName, imports) {
|
|
1340
|
-
if ('$ref' in schemaObj && typeof schemaObj.$ref === 'string') {
|
|
1341
|
-
return this.getRefBasedType(schemaObj.$ref, modelName, imports);
|
|
1342
|
-
}
|
|
1343
|
-
if ('type' in schemaObj && typeof schemaObj.type === 'string') {
|
|
1344
|
-
const typeValue = schemaObj.type;
|
|
1345
|
-
// Handle binary format as string
|
|
1346
|
-
if ('format' in schemaObj && schemaObj.format === 'binary' && typeValue === 'string') {
|
|
1347
|
-
return 'string';
|
|
1348
|
-
}
|
|
1349
|
-
return this.mapJsonTypeToTypeScript(typeValue);
|
|
1350
|
-
}
|
|
1351
|
-
return 'unknown';
|
|
1271
|
+
if (parameterTypes && parameterTypes[paramName]) {
|
|
1272
|
+
parametersList.push(`${parameterTypes[paramName].propertyDefinition}`);
|
|
1273
|
+
parametersProps.push(`${parameterTypes[paramName].propertyName}`);
|
|
1352
1274
|
}
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
return refType;
|
|
1362
|
-
}
|
|
1363
|
-
/*
|
|
1364
|
-
* The generateOperationMethod method generates a method for the service class.
|
|
1365
|
-
* It takes the operations list, operation ID, summary, description,
|
|
1366
|
-
* parameters list, parameters properties, output model, and data source name as parameters.
|
|
1367
|
-
*/
|
|
1368
|
-
static generateOperationMethod(operations, operationId, summary, description, parametersList, parametersProps, outputModel, modelName) {
|
|
1369
|
-
this.addMethodSummary(operations, summary, description);
|
|
1370
|
-
const operationFuncName = this.formatOperationName(operationId);
|
|
1371
|
-
const parameterSignature = this.getParameterSignature(parametersList);
|
|
1372
|
-
const parameterObject = this.getParameterObject(parametersProps);
|
|
1373
|
-
const parametersType = this.getParametersType(parametersList);
|
|
1374
|
-
if (parametersList.length === 0) {
|
|
1375
|
-
this.addMethodWithoutParameters(operations, operationFuncName, outputModel, operationId, modelName);
|
|
1376
|
-
}
|
|
1377
|
-
else {
|
|
1378
|
-
this.addMethodWithParameters(operations, operationFuncName, parameterSignature, parametersType, parameterObject, outputModel, operationId, modelName);
|
|
1379
|
-
}
|
|
1275
|
+
else {
|
|
1276
|
+
// Only add to imports if not an override
|
|
1277
|
+
const paramType = getParameterType(paramObj, modelName, imports);
|
|
1278
|
+
const optionalMark = isParameterRequired(paramObj) ? '' : '?';
|
|
1279
|
+
// Normalize parameter name
|
|
1280
|
+
const normalizedParamName = paramName.replace(/-/g, '_');
|
|
1281
|
+
parametersList.push(`${normalizedParamName}${optionalMark}: ${paramType}`);
|
|
1282
|
+
parametersProps.push(normalizedParamName);
|
|
1380
1283
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
else {
|
|
1396
|
-
operations.push('');
|
|
1284
|
+
return [parametersList, parametersProps];
|
|
1285
|
+
}
|
|
1286
|
+
/*
|
|
1287
|
+
* The getParameterName method extracts the name of the parameter from the schema.
|
|
1288
|
+
* It handles the case where the schema is an array or a reference.
|
|
1289
|
+
* It takes the parameter object and node name as parameters.
|
|
1290
|
+
*/
|
|
1291
|
+
function getParameterName(paramObj, paramNodeName) {
|
|
1292
|
+
const refNode = paramObj.$ref;
|
|
1293
|
+
if (refNode && typeof refNode === 'string') {
|
|
1294
|
+
const refValue = refNode;
|
|
1295
|
+
if (refValue.toLowerCase().startsWith('#/parameters/')) {
|
|
1296
|
+
const parameterName = refValue.substring('#/parameters/'.length);
|
|
1297
|
+
return parameterName;
|
|
1397
1298
|
}
|
|
1398
1299
|
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
/*
|
|
1414
|
-
* The getParameterObject method generates the parameter object for the service class.
|
|
1415
|
-
* It takes the parameters properties list as input and returns the parameter object as a string.
|
|
1416
|
-
*/
|
|
1417
|
-
static getParameterObject(parametersProps) {
|
|
1418
|
-
return parametersProps.length > 0 ? parametersProps.join(', ') : '';
|
|
1300
|
+
const nameNode = paramObj[paramNodeName];
|
|
1301
|
+
return nameNode ? String(nameNode) : 'param';
|
|
1302
|
+
}
|
|
1303
|
+
/*
|
|
1304
|
+
* The getParameterType method extracts the type of the parameter from the schema.
|
|
1305
|
+
* It handles the case where the schema is an array or a reference.
|
|
1306
|
+
* It takes the parameter object, model name, and imports set as parameters.
|
|
1307
|
+
*/
|
|
1308
|
+
function getParameterType(paramObj, modelName, imports) {
|
|
1309
|
+
if ('schema' in paramObj &&
|
|
1310
|
+
typeof paramObj.schema === 'object' &&
|
|
1311
|
+
paramObj.schema !== null &&
|
|
1312
|
+
!Array.isArray(paramObj.schema)) {
|
|
1313
|
+
return getSchemaBasedType(paramObj.schema, modelName, imports);
|
|
1419
1314
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
* It takes the parameters list as input and returns the parameters type as a string.
|
|
1423
|
-
*/
|
|
1424
|
-
static getParametersType(parametersList) {
|
|
1425
|
-
return parametersList.length > 0 ? `{ ${parametersList.join(', ')} }` : 'object';
|
|
1315
|
+
if ('$ref' in paramObj && typeof paramObj.$ref === 'string') {
|
|
1316
|
+
return getRefBasedType(paramObj.$ref, modelName, imports);
|
|
1426
1317
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
* It takes the operations list, operation function name, output model, data source name,
|
|
1430
|
-
* and operation ID as parameters.
|
|
1431
|
-
*/
|
|
1432
|
-
static addMethodWithoutParameters(operations, operationFuncName, outputModel, operationId, modelName) {
|
|
1433
|
-
operations.push(` public static async ${operationFuncName}(): Promise<IOperationResult<${outputModel}>> {`, ` const result = await ${modelName}Service.client.executeAsync<void, ${outputModel}>(`, ` {`, ` connectorOperation: {`, ` tableName: ${getServiceName(modelName)},`, ` operationName: '${operationId}',`, ` },`, ` });`, ` return result;`, ` }`);
|
|
1318
|
+
if ('type' in paramObj && typeof paramObj.type === 'string') {
|
|
1319
|
+
return mapJsonTypeToTypeScript(paramObj.type);
|
|
1434
1320
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1321
|
+
return 'unknown';
|
|
1322
|
+
}
|
|
1323
|
+
/*
|
|
1324
|
+
* The getSchemaBasedType method extracts the type from the schema object.
|
|
1325
|
+
* It handles the case where the schema is an array or a reference.
|
|
1326
|
+
* It takes the schema object, model name, and imports set as parameters.
|
|
1327
|
+
*/
|
|
1328
|
+
function getSchemaBasedType(schemaObj, modelName, imports) {
|
|
1329
|
+
if ('$ref' in schemaObj && typeof schemaObj.$ref === 'string') {
|
|
1330
|
+
return getRefBasedType(schemaObj.$ref, modelName, imports);
|
|
1442
1331
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
const serviceCode = this.generateStoredProcedureService(modelName, dataSourceName, schema, responseModelName);
|
|
1449
|
-
await this.writeModelFile(outputDir, modelName, modelCode, vfs);
|
|
1450
|
-
await this.writeServiceFile(outputDir, modelName, serviceCode, vfs);
|
|
1451
|
-
}
|
|
1452
|
-
/*
|
|
1453
|
-
* The generateStoredProcedureModel method generates the model code for the stored procedure.
|
|
1454
|
-
* It takes the model name and schema as parameters and returns the generated model code.
|
|
1455
|
-
*/
|
|
1456
|
-
static generateStoredProcedureModel(modelName, schema) {
|
|
1457
|
-
const lines = [];
|
|
1458
|
-
this.addCopyrightNotice(lines);
|
|
1459
|
-
// Validate schema
|
|
1460
|
-
if (typeof schema !== 'object' || schema === null || !('schema' in schema)) {
|
|
1461
|
-
throw new Error('Invalid schema format for stored procedure.');
|
|
1462
|
-
}
|
|
1463
|
-
const schemaNode = schema.schema;
|
|
1464
|
-
// Ensure schemaNode is a JsonObject
|
|
1465
|
-
if (typeof schemaNode !== 'object' || schemaNode === null || Array.isArray(schemaNode)) {
|
|
1466
|
-
throw new Error('Invalid schema node format for stored procedure.');
|
|
1332
|
+
if ('type' in schemaObj && typeof schemaObj.type === 'string') {
|
|
1333
|
+
const typeValue = schemaObj.type;
|
|
1334
|
+
// Handle binary format as string
|
|
1335
|
+
if ('format' in schemaObj && schemaObj.format === 'binary' && typeValue === 'string') {
|
|
1336
|
+
return 'string';
|
|
1467
1337
|
}
|
|
1468
|
-
|
|
1469
|
-
const responseModelName = this.addOutputParametersModel(lines, modelName, schemaNode);
|
|
1470
|
-
return { modelCode: lines.join('\n'), responseModelName };
|
|
1338
|
+
return mapJsonTypeToTypeScript(typeValue);
|
|
1471
1339
|
}
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1340
|
+
return 'unknown';
|
|
1341
|
+
}
|
|
1342
|
+
/*
|
|
1343
|
+
* The getRefBasedType method extracts the referenced type from the ref value.
|
|
1344
|
+
* It adds the import statement for the referenced type to the imports set.
|
|
1345
|
+
* It takes the ref value, model name, and imports set as parameters.
|
|
1346
|
+
*/
|
|
1347
|
+
function getRefBasedType(refValue, modelName, imports) {
|
|
1348
|
+
const refType = extractReferencedType(refValue);
|
|
1349
|
+
addImport(refType, modelName, imports);
|
|
1350
|
+
return refType;
|
|
1351
|
+
}
|
|
1352
|
+
/*
|
|
1353
|
+
* The generateOperationMethod method generates a method for the service class.
|
|
1354
|
+
* It takes the operations list, operation ID, summary, description,
|
|
1355
|
+
* parameters list, parameters properties, output model, and data source name as parameters.
|
|
1356
|
+
*/
|
|
1357
|
+
function generateOperationMethod(operations, operationId, summary, description, parametersList, parametersProps, outputModel, modelName) {
|
|
1358
|
+
addMethodSummary(operations, summary, description);
|
|
1359
|
+
const operationFuncName = formatOperationName(operationId);
|
|
1360
|
+
const parameterSignature = getParameterSignature(parametersList);
|
|
1361
|
+
const parameterObject = getParameterObject(parametersProps);
|
|
1362
|
+
const parametersType = getParametersType(parametersList);
|
|
1363
|
+
if (parametersList.length === 0) {
|
|
1364
|
+
addMethodWithoutParameters(operations, operationFuncName, outputModel, operationId, modelName);
|
|
1365
|
+
}
|
|
1366
|
+
else {
|
|
1367
|
+
addMethodWithParameters(operations, operationFuncName, parameterSignature, parametersType, parameterObject, outputModel, operationId, modelName);
|
|
1486
1368
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
lines.push(''); // Add a blank line between models
|
|
1500
|
-
return responseModelName;
|
|
1369
|
+
}
|
|
1370
|
+
/*
|
|
1371
|
+
* The addMethodSummary method adds a summary and description to the method.
|
|
1372
|
+
* It takes the operations list, summary, and description as parameters.
|
|
1373
|
+
*/
|
|
1374
|
+
function addMethodSummary(operations, summary, description) {
|
|
1375
|
+
if (summary && summary.trim().length > 0) {
|
|
1376
|
+
operations.push('');
|
|
1377
|
+
operations.push(' /**');
|
|
1378
|
+
operations.push(` * ${summary}`);
|
|
1379
|
+
if (description && description.trim().length > 0) {
|
|
1380
|
+
operations.push(` * ${description}`);
|
|
1501
1381
|
}
|
|
1502
|
-
|
|
1382
|
+
operations.push(' */');
|
|
1503
1383
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
* It takes the model name, data source name, schema, and response model name as parameters.
|
|
1507
|
-
*/
|
|
1508
|
-
static generateStoredProcedureService(modelName, dataSourceName, schema, responseModelName) {
|
|
1509
|
-
const lines = [];
|
|
1510
|
-
this.addCopyrightNotice(lines);
|
|
1511
|
-
this.addServiceImports(lines, modelName, responseModelName);
|
|
1512
|
-
this.addStoredProcedureServiceClassDefinition(lines, modelName, dataSourceName, schema, responseModelName);
|
|
1513
|
-
return lines.join('\n');
|
|
1384
|
+
else {
|
|
1385
|
+
operations.push('');
|
|
1514
1386
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1387
|
+
}
|
|
1388
|
+
/*
|
|
1389
|
+
* The formatOperationName method formats the operation name by replacing invalid characters with underscores.
|
|
1390
|
+
* It takes the operation ID as input and returns the formatted operation name.
|
|
1391
|
+
*/
|
|
1392
|
+
function formatOperationName(operationId) {
|
|
1393
|
+
return operationId.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
1394
|
+
}
|
|
1395
|
+
/*
|
|
1396
|
+
* The getParameterSignature method generates the parameter signature for the service class.
|
|
1397
|
+
* It takes the parameters list as input and returns the parameter signature as a string.
|
|
1398
|
+
*/
|
|
1399
|
+
function getParameterSignature(parametersList) {
|
|
1400
|
+
return parametersList.length > 0 ? parametersList.join(', ') : '';
|
|
1401
|
+
}
|
|
1402
|
+
/*
|
|
1403
|
+
* The getParameterObject method generates the parameter object for the service class.
|
|
1404
|
+
* It takes the parameters properties list as input and returns the parameter object as a string.
|
|
1405
|
+
*/
|
|
1406
|
+
function getParameterObject(parametersProps) {
|
|
1407
|
+
return parametersProps.length > 0 ? parametersProps.join(', ') : '';
|
|
1408
|
+
}
|
|
1409
|
+
/*
|
|
1410
|
+
* The getParametersType method generates the parameters type for the service class.
|
|
1411
|
+
* It takes the parameters list as input and returns the parameters type as a string.
|
|
1412
|
+
*/
|
|
1413
|
+
function getParametersType(parametersList) {
|
|
1414
|
+
return parametersList.length > 0 ? `{ ${parametersList.join(', ')} }` : 'object';
|
|
1415
|
+
}
|
|
1416
|
+
/*
|
|
1417
|
+
* The addMethodWithoutParameters method generates a method without parameters for the service class.
|
|
1418
|
+
* It takes the operations list, operation function name, output model, data source name,
|
|
1419
|
+
* and operation ID as parameters.
|
|
1420
|
+
*/
|
|
1421
|
+
function addMethodWithoutParameters(operations, operationFuncName, outputModel, operationId, modelName) {
|
|
1422
|
+
operations.push(` public static async ${operationFuncName}(): Promise<IOperationResult<${outputModel}>> {`, ` const result = await ${modelName}Service.client.executeAsync<void, ${outputModel}>(`, ` {`, ` connectorOperation: {`, ` tableName: ${getServiceName(modelName)},`, ` operationName: '${operationId}',`, ` },`, ` });`, ` return result;`, ` }`);
|
|
1423
|
+
}
|
|
1424
|
+
/*
|
|
1425
|
+
* The addMethodWithParameters method generates a method with parameters for the service class.
|
|
1426
|
+
* It takes the operations list, operation function name, parameter signature, parameters type,
|
|
1427
|
+
* parameter object, output model, data source name, and operation ID as parameters.
|
|
1428
|
+
*/
|
|
1429
|
+
function addMethodWithParameters(operations, operationFuncName, parameterSignature, parametersType, parameterObject, outputModel, operationId, modelName) {
|
|
1430
|
+
operations.push(` public static async ${operationFuncName}(${parameterSignature}): Promise<IOperationResult<${outputModel}>> {`, ` const params: ${parametersType} = { ${parameterObject} };`, ` const result = await ${modelName}Service.client.executeAsync<${parametersType}, ${outputModel}>(`, ` {`, ` connectorOperation: {`, ` tableName: ${getServiceName(modelName)},`, ` operationName: '${operationId}',`, ` parameters: params`, ` },`, ` });`, ` return result;`, ` }`);
|
|
1431
|
+
}
|
|
1432
|
+
/*
|
|
1433
|
+
* The handleSqlStoredProcedure method handles the SQL stored procedure generation.
|
|
1434
|
+
*/
|
|
1435
|
+
async function handleSqlStoredProcedure(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
|
|
1436
|
+
const { modelCode, responseModelName } = generateStoredProcedureModel(modelName, schema);
|
|
1437
|
+
const serviceCode = generateStoredProcedureService(modelName, dataSourceName, schema, responseModelName);
|
|
1438
|
+
await writeModelFile(outputDir, modelName, modelCode, vfs);
|
|
1439
|
+
await writeServiceFile(outputDir, modelName, serviceCode, vfs);
|
|
1440
|
+
}
|
|
1441
|
+
/*
|
|
1442
|
+
* The generateStoredProcedureModel method generates the model code for the stored procedure.
|
|
1443
|
+
* It takes the model name and schema as parameters and returns the generated model code.
|
|
1444
|
+
*/
|
|
1445
|
+
function generateStoredProcedureModel(modelName, schema) {
|
|
1446
|
+
const lines = [];
|
|
1447
|
+
addCopyrightNotice(lines);
|
|
1448
|
+
// Validate schema
|
|
1449
|
+
if (typeof schema !== 'object' || schema === null || !('schema' in schema)) {
|
|
1450
|
+
throw new Error('Invalid schema format for stored procedure.');
|
|
1451
|
+
}
|
|
1452
|
+
const schemaNode = schema.schema;
|
|
1453
|
+
// Ensure schemaNode is a JsonObject
|
|
1454
|
+
if (typeof schemaNode !== 'object' || schemaNode === null || Array.isArray(schemaNode)) {
|
|
1455
|
+
throw new Error('Invalid schema node format for stored procedure.');
|
|
1456
|
+
}
|
|
1457
|
+
addInputParametersModel(lines, modelName, schemaNode);
|
|
1458
|
+
const responseModelName = addOutputParametersModel(lines, modelName, schemaNode);
|
|
1459
|
+
return { modelCode: lines.join('\n'), responseModelName };
|
|
1460
|
+
}
|
|
1461
|
+
/*
|
|
1462
|
+
* The addInputParametersModel method generates the input parameters model for the stored procedure.
|
|
1463
|
+
* It takes the lines to write to, model name, and schema node as parameters.
|
|
1464
|
+
*/
|
|
1465
|
+
function addInputParametersModel(lines, modelName, schemaNode) {
|
|
1466
|
+
if ('inputparameters' in schemaNode &&
|
|
1467
|
+
typeof schemaNode.inputparameters === 'object' &&
|
|
1468
|
+
schemaNode.inputparameters !== null &&
|
|
1469
|
+
!Array.isArray(schemaNode.inputparameters)) {
|
|
1470
|
+
const requestModelName = `${modelName}Request`;
|
|
1471
|
+
const modelCode = generateModelFromSchema(requestModelName, schemaNode.inputparameters);
|
|
1472
|
+
lines.push(modelCode);
|
|
1473
|
+
lines.push(''); // Add a blank line between models
|
|
1524
1474
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1475
|
+
}
|
|
1476
|
+
/*
|
|
1477
|
+
* The addOutputParametersModel method generates the output parameters model for the stored procedure.
|
|
1478
|
+
* It takes the lines to write to, model name, and schema node as parameters.
|
|
1479
|
+
*/
|
|
1480
|
+
function addOutputParametersModel(lines, modelName, schemaNode) {
|
|
1481
|
+
if ('procedureresultschema' in schemaNode &&
|
|
1482
|
+
typeof schemaNode.procedureresultschema === 'object' &&
|
|
1483
|
+
schemaNode.procedureresultschema !== null &&
|
|
1484
|
+
!Array.isArray(schemaNode.procedureresultschema)) {
|
|
1485
|
+
const responseModelName = `${modelName}Response`;
|
|
1486
|
+
const modelCode = generateModelFromSchema(responseModelName, schemaNode.procedureresultschema);
|
|
1487
|
+
lines.push(modelCode);
|
|
1488
|
+
lines.push(''); // Add a blank line between models
|
|
1489
|
+
return responseModelName;
|
|
1534
1490
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1491
|
+
return '';
|
|
1492
|
+
}
|
|
1493
|
+
/*
|
|
1494
|
+
* The generateStoredProcedureService method generates the service code for the stored procedure.
|
|
1495
|
+
* It takes the model name, data source name, schema, and response model name as parameters.
|
|
1496
|
+
*/
|
|
1497
|
+
function generateStoredProcedureService(modelName, dataSourceName, schema, responseModelName) {
|
|
1498
|
+
const lines = [];
|
|
1499
|
+
addCopyrightNotice(lines);
|
|
1500
|
+
addServiceImports(lines, modelName, responseModelName);
|
|
1501
|
+
addStoredProcedureServiceClassDefinition(lines, modelName, dataSourceName, schema, responseModelName);
|
|
1502
|
+
return lines.join('\n');
|
|
1503
|
+
}
|
|
1504
|
+
/*
|
|
1505
|
+
* The addServiceImports method adds necessary imports to the service file.
|
|
1506
|
+
* It takes the lines to write to, model name, and response model name as parameters.
|
|
1507
|
+
*/
|
|
1508
|
+
function addServiceImports(lines, modelName, responseModelName) {
|
|
1509
|
+
const standardImports = collectStandardImports();
|
|
1510
|
+
lines.push(...standardImports); // Add standard imports
|
|
1511
|
+
lines.push(`import type { ${responseModelName} } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
1512
|
+
lines.push('');
|
|
1513
|
+
}
|
|
1514
|
+
/*
|
|
1515
|
+
* The addServiceClassDefinition method generates the service class definition.
|
|
1516
|
+
* It takes the lines to write to, model name, data source name, schema, and response model name as parameters.
|
|
1517
|
+
*/
|
|
1518
|
+
function addStoredProcedureServiceClassDefinition(lines, modelName, dataSourceName, schema, responseModelName) {
|
|
1519
|
+
addServiceClassHeader(lines, modelName, dataSourceName, false);
|
|
1520
|
+
addStoredProcedureServiceMethods(lines, modelName, schema, responseModelName);
|
|
1521
|
+
lines.push('}');
|
|
1522
|
+
lines.push('');
|
|
1523
|
+
}
|
|
1524
|
+
/*
|
|
1525
|
+
* The addStoredProcedureServiceMethods method generates service methods for the stored procedure.
|
|
1526
|
+
* It takes the lines to write to, model name, schema, and response model name as parameters.
|
|
1527
|
+
*/
|
|
1528
|
+
function addStoredProcedureServiceMethods(lines, modelName, schema, responseModelName) {
|
|
1529
|
+
if (schema !== null && typeof schema === 'object' && !Array.isArray(schema)) {
|
|
1530
|
+
const schemaObj = schema;
|
|
1531
|
+
const schemaNode = schemaObj.schema;
|
|
1532
|
+
if (typeof schemaNode === 'object' && schemaNode !== null && !Array.isArray(schemaNode)) {
|
|
1533
|
+
const schemaNodeObj = schemaNode;
|
|
1534
|
+
const inputparameters = schemaNodeObj.inputparameters;
|
|
1535
|
+
if (typeof inputparameters === 'object' &&
|
|
1536
|
+
inputparameters !== null &&
|
|
1537
|
+
!Array.isArray(inputparameters)) {
|
|
1538
|
+
let parametersList = [];
|
|
1539
|
+
let parametersProps = [];
|
|
1540
|
+
const imports = new Set();
|
|
1541
|
+
const inputparametersObj = inputparameters;
|
|
1542
|
+
const inputProperties = inputparametersObj.properties;
|
|
1543
|
+
if (typeof inputProperties === 'object' &&
|
|
1544
|
+
inputProperties !== null &&
|
|
1545
|
+
!Array.isArray(inputProperties)) {
|
|
1546
|
+
// Collect properties with required status and original index
|
|
1547
|
+
const orderedProperties = getOrderedPropertiesByRequired(inputProperties);
|
|
1548
|
+
for (let i = 0; i < orderedProperties.length; i++) {
|
|
1549
|
+
const property = orderedProperties[i];
|
|
1550
|
+
[parametersList, parametersProps] = processRequestParameters(property.value, modelName, imports, parametersList, parametersProps, 'title');
|
|
1563
1551
|
}
|
|
1564
|
-
this.generateOperationMethod(lines, modelName, '', '', parametersList, parametersProps, responseModelName, modelName);
|
|
1565
1552
|
}
|
|
1553
|
+
generateOperationMethod(lines, modelName, '', '', parametersList, parametersProps, responseModelName, modelName);
|
|
1566
1554
|
}
|
|
1567
1555
|
}
|
|
1568
1556
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1557
|
+
}
|
|
1558
|
+
/*
|
|
1559
|
+
* The getOrderedPropertiesByRequired method sorts the properties by required property and original index.
|
|
1560
|
+
*/
|
|
1561
|
+
function getOrderedPropertiesByRequired(propertiesOrArray) {
|
|
1562
|
+
let propertiesObj;
|
|
1563
|
+
if (Array.isArray(propertiesOrArray)) {
|
|
1564
|
+
propertiesObj = {};
|
|
1565
|
+
for (let idx = 0; idx < propertiesOrArray.length; idx++) {
|
|
1566
|
+
propertiesObj[`param_${idx}`] = propertiesOrArray[idx];
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
else if (typeof propertiesOrArray === 'object' && propertiesOrArray !== null) {
|
|
1570
|
+
propertiesObj = propertiesOrArray;
|
|
1571
|
+
}
|
|
1572
|
+
else {
|
|
1573
|
+
throw new Error('Input must be an object or array');
|
|
1574
|
+
}
|
|
1575
|
+
const propertyList = [];
|
|
1576
|
+
let index = 0;
|
|
1577
|
+
for (const key in propertiesObj) {
|
|
1578
|
+
if (!Object.prototype.hasOwnProperty.call(propertiesObj, key)) {
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
const value = propertiesObj[key];
|
|
1582
|
+
let required = false;
|
|
1583
|
+
if (value && typeof value === 'object' && 'required' in value) {
|
|
1584
|
+
required = String(value.required) === 'true';
|
|
1585
|
+
}
|
|
1586
|
+
propertyList.push({ key, value, required, index });
|
|
1587
|
+
index++;
|
|
1588
|
+
}
|
|
1589
|
+
// Required first, then original order
|
|
1590
|
+
propertyList.sort((a, b) => {
|
|
1591
|
+
const reqCompare = Number(b.required) - Number(a.required);
|
|
1592
|
+
if (reqCompare !== 0) {
|
|
1593
|
+
return reqCompare;
|
|
1594
|
+
}
|
|
1595
|
+
return a.index - b.index;
|
|
1596
|
+
});
|
|
1597
|
+
return propertyList.map((p) => ({ key: p.key, value: p.value }));
|
|
1598
|
+
}
|
|
1599
|
+
/*
|
|
1600
|
+
* The WriteModelFile method writes the generated model code to a file.
|
|
1601
|
+
*/
|
|
1602
|
+
async function writeModelFile(outputDir, modelName, modelCode, vfs) {
|
|
1603
|
+
const modelFilePath = vfs.join(outputDir, MODELS_FOLDER, `${modelName}Model.ts`);
|
|
1604
|
+
await writeToFile(modelFilePath, [modelCode], vfs);
|
|
1605
|
+
}
|
|
1606
|
+
/*
|
|
1607
|
+
* The WriteServiceFile method writes the generated service code to a file.
|
|
1608
|
+
* It takes the output directory, model name, and service code as parameters.
|
|
1609
|
+
*/
|
|
1610
|
+
async function writeServiceFile(outputDir, modelName, serviceCode, vfs) {
|
|
1611
|
+
const serviceFilePath = vfs.join(outputDir, SERVICES_FOLDER, `${modelName}Service.ts`);
|
|
1612
|
+
await writeToFile(serviceFilePath, [serviceCode], vfs);
|
|
1613
|
+
}
|
|
1614
|
+
/*
|
|
1615
|
+
* The handleStandardSchema method generates TypeScript models and services from any schema type.
|
|
1616
|
+
* It routes to the appropriate handler based on schema type.
|
|
1617
|
+
*/
|
|
1618
|
+
async function handleStandardSchema(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
|
|
1619
|
+
// Check if this is a Dataverse entity schema (has nested schema.items structure)
|
|
1620
|
+
if (isDataverseEntity(schema)) {
|
|
1621
|
+
await handleDataverseSchema(modelName, dataSourceName, schema, outputDir, vfs);
|
|
1624
1622
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
* It routes to the appropriate handler based on schema type.
|
|
1628
|
-
*/
|
|
1629
|
-
static async handleStandardSchema(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
|
|
1630
|
-
// Check if this is a Dataverse entity schema (has nested schema.items structure)
|
|
1631
|
-
if (isDataverseEntity(schema)) {
|
|
1632
|
-
await this.handleDataverseSchema(modelName, dataSourceName, schema, outputDir, vfs);
|
|
1633
|
-
}
|
|
1634
|
-
else {
|
|
1635
|
-
await this.handleStandardSchemaOnly(modelName, dataSourceName, schemaJson, outputDir, vfs);
|
|
1636
|
-
}
|
|
1637
|
-
await this.ensureCommonModelFileExists(outputDir, vfs);
|
|
1623
|
+
else {
|
|
1624
|
+
await handleStandardSchemaOnly(modelName, dataSourceName, schemaJson, outputDir, vfs);
|
|
1638
1625
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1626
|
+
await ensureCommonModelFileExists(outputDir, vfs);
|
|
1627
|
+
}
|
|
1628
|
+
/*
|
|
1629
|
+
* The handleDataverseSchema method generates TypeScript models and services specifically for Dataverse entities.
|
|
1630
|
+
*/
|
|
1631
|
+
async function handleDataverseSchema(modelName, dataSourceName, schema, outputDir, vfs) {
|
|
1632
|
+
const schemaObject = schema;
|
|
1633
|
+
if (typeof schemaObject.schema !== 'object' || schemaObject.schema === null) {
|
|
1634
|
+
throw new Error(`Invalid Dataverse entity schema structure: missing 'schema' property for ${modelName}`);
|
|
1635
|
+
}
|
|
1636
|
+
const schemaProperty = schemaObject.schema;
|
|
1637
|
+
if (typeof schemaProperty.items !== 'object' || schemaProperty.items === null) {
|
|
1638
|
+
throw new Error(`Invalid Dataverse entity schema structure: missing 'schema.items' property for ${modelName}`);
|
|
1639
|
+
}
|
|
1640
|
+
const itemsNode = schemaProperty.items;
|
|
1641
|
+
let primaryKey = 'id'; // default
|
|
1642
|
+
// Extract primary key from Dataverse entity schema inline
|
|
1643
|
+
const properties = itemsNode.properties;
|
|
1644
|
+
if (properties) {
|
|
1645
|
+
for (const [propName, propValue] of Object.entries(properties)) {
|
|
1646
|
+
if (typeof propValue === 'object' && propValue !== null) {
|
|
1647
|
+
const prop = propValue;
|
|
1648
|
+
if (prop['x-ms-dataverse-primary-id'] === true || prop['x-ms-keyType'] === 'primary') {
|
|
1649
|
+
primaryKey = propName;
|
|
1650
|
+
break;
|
|
1663
1651
|
}
|
|
1664
1652
|
}
|
|
1665
1653
|
}
|
|
1666
|
-
// For Dataverse entities, generate both base and extended interfaces for lookup columns
|
|
1667
|
-
const { baseInterface, extendedInterface } = this.generateDataverseInterfaces(modelName, itemsNode);
|
|
1668
|
-
const hasLookupColumns = !!extendedInterface;
|
|
1669
|
-
if (hasLookupColumns) {
|
|
1670
|
-
// Generate both interfaces when there are lookup columns
|
|
1671
|
-
const combinedModelCode = baseInterface + '\n\n' + extendedInterface;
|
|
1672
|
-
await this.writeModelFile(outputDir, modelName, combinedModelCode, vfs);
|
|
1673
|
-
}
|
|
1674
|
-
else {
|
|
1675
|
-
// Generate single interface when no lookup columns
|
|
1676
|
-
await this.writeModelFile(outputDir, modelName, baseInterface, vfs);
|
|
1677
|
-
}
|
|
1678
|
-
const serviceCode = this.generateDataverseTypeScriptService(modelName, dataSourceName, primaryKey, hasLookupColumns);
|
|
1679
|
-
await this.writeServiceFile(outputDir, modelName, serviceCode, vfs);
|
|
1680
1654
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
const
|
|
1687
|
-
await
|
|
1688
|
-
await this.writeServiceFile(outputDir, modelName, serviceCode, vfs);
|
|
1655
|
+
// For Dataverse entities, generate both base and extended interfaces for lookup columns
|
|
1656
|
+
const { baseInterface, extendedInterface } = generateDataverseInterfaces(modelName, itemsNode);
|
|
1657
|
+
const hasLookupColumns = !!extendedInterface;
|
|
1658
|
+
if (hasLookupColumns) {
|
|
1659
|
+
// Generate both interfaces when there are lookup columns
|
|
1660
|
+
const combinedModelCode = baseInterface + '\n\n' + extendedInterface;
|
|
1661
|
+
await writeModelFile(outputDir, modelName, combinedModelCode, vfs);
|
|
1689
1662
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
*/
|
|
1694
|
-
static async handleSharepointSchema(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
|
|
1695
|
-
// SharePoint-specific processing logic
|
|
1696
|
-
const { modelCode, primaryKey, hasReferenceEntities } = this.generateSharepointTypeScriptModel(schemaJson, modelName);
|
|
1697
|
-
// Generate SharePoint-specific service code
|
|
1698
|
-
const serviceCode = this.generateSharepointService(modelName, dataSourceName, primaryKey, schemaJson, hasReferenceEntities);
|
|
1699
|
-
await this.writeModelFile(outputDir, modelName, modelCode, vfs);
|
|
1700
|
-
await this.writeServiceFile(outputDir, modelName, serviceCode, vfs);
|
|
1701
|
-
await this.ensureCommonModelFileExists(outputDir, vfs);
|
|
1702
|
-
}
|
|
1703
|
-
/**
|
|
1704
|
-
* Generates SharePoint-specific service code.
|
|
1705
|
-
*/
|
|
1706
|
-
static generateSharepointService(modelName, dataSourceName, primaryKey, schemaJson, hasReferenceEntities) {
|
|
1707
|
-
const lines = [];
|
|
1708
|
-
this.addCopyrightNotice(lines);
|
|
1709
|
-
this.addSharepointServiceImports(lines, modelName, hasReferenceEntities);
|
|
1710
|
-
this.addSharepointServiceClassDefinition(lines, modelName, dataSourceName, schemaJson, primaryKey, hasReferenceEntities);
|
|
1711
|
-
return lines.join('\n');
|
|
1663
|
+
else {
|
|
1664
|
+
// Generate single interface when no lookup columns
|
|
1665
|
+
await writeModelFile(outputDir, modelName, baseInterface, vfs);
|
|
1712
1666
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1667
|
+
const serviceCode = generateDataverseTypeScriptService(modelName, dataSourceName, primaryKey, hasLookupColumns);
|
|
1668
|
+
await writeServiceFile(outputDir, modelName, serviceCode, vfs);
|
|
1669
|
+
}
|
|
1670
|
+
/*
|
|
1671
|
+
* The handleStandardSchemaOnly method generates TypeScript models and services for standard (non-Dataverse) schemas.
|
|
1672
|
+
*/
|
|
1673
|
+
async function handleStandardSchemaOnly(modelName, dataSourceName, schemaJson, outputDir, vfs) {
|
|
1674
|
+
const { modelCode, primaryKey } = generateTypeScriptModel(schemaJson, modelName);
|
|
1675
|
+
const serviceCode = generateTypeScriptService(modelName, dataSourceName, primaryKey, false);
|
|
1676
|
+
await writeModelFile(outputDir, modelName, modelCode, vfs);
|
|
1677
|
+
await writeServiceFile(outputDir, modelName, serviceCode, vfs);
|
|
1678
|
+
}
|
|
1679
|
+
/**
|
|
1680
|
+
* The handleSharepointSchema method handles SharePoint schema generation.
|
|
1681
|
+
* It generates TypeScript models and services specifically for SharePoint data sources.
|
|
1682
|
+
*/
|
|
1683
|
+
async function handleSharepointSchema(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
|
|
1684
|
+
// SharePoint-specific processing logic
|
|
1685
|
+
const { modelCode, primaryKey, hasReferenceEntities } = generateSharepointTypeScriptModel(schemaJson, modelName);
|
|
1686
|
+
// Generate SharePoint-specific service code
|
|
1687
|
+
const serviceCode = generateSharepointService(modelName, dataSourceName, primaryKey, schemaJson, hasReferenceEntities);
|
|
1688
|
+
await writeModelFile(outputDir, modelName, modelCode, vfs);
|
|
1689
|
+
await writeServiceFile(outputDir, modelName, serviceCode, vfs);
|
|
1690
|
+
await ensureCommonModelFileExists(outputDir, vfs);
|
|
1691
|
+
}
|
|
1692
|
+
/**
|
|
1693
|
+
* Generates SharePoint-specific service code.
|
|
1694
|
+
*/
|
|
1695
|
+
function generateSharepointService(modelName, dataSourceName, primaryKey, schemaJson, hasReferenceEntities) {
|
|
1696
|
+
const lines = [];
|
|
1697
|
+
addCopyrightNotice(lines);
|
|
1698
|
+
addSharepointServiceImports(lines, modelName, hasReferenceEntities);
|
|
1699
|
+
addSharepointServiceClassDefinition(lines, modelName, dataSourceName, schemaJson, primaryKey, hasReferenceEntities);
|
|
1700
|
+
return lines.join('\n');
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Adds the SharePoint service class definition to the generated code.
|
|
1704
|
+
*/
|
|
1705
|
+
function addSharepointServiceClassDefinition(lines, modelName, dataSourceName, schemaJson, primaryKey, hasReferenceEntities) {
|
|
1706
|
+
addServiceClassHeader(lines, modelName, dataSourceName, true);
|
|
1707
|
+
// Add CRUD methods
|
|
1708
|
+
const methods = [];
|
|
1709
|
+
methods.push(generateSharepointCreateMethod(modelName, primaryKey, hasReferenceEntities));
|
|
1710
|
+
methods.push(generateSharepointUpdateMethod(modelName, primaryKey, hasReferenceEntities));
|
|
1711
|
+
methods.push(generateSharepointDeleteMethod(modelName));
|
|
1712
|
+
methods.push(generateSharepointGetMethod(modelName, hasReferenceEntities));
|
|
1713
|
+
methods.push(generateSharepointGetAllMethod(modelName, hasReferenceEntities));
|
|
1714
|
+
for (const method of methods) {
|
|
1732
1715
|
lines.push('');
|
|
1716
|
+
lines.push(...method);
|
|
1733
1717
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
if (referencedEntities) {
|
|
1746
|
-
this.addGetMethodForReferenceEntitiesCore(lines, modelName);
|
|
1747
|
-
}
|
|
1718
|
+
// Add SharePoint-specific methods
|
|
1719
|
+
addGetMethodForReferenceEntities(lines, modelName, dataSourceName, schemaJson);
|
|
1720
|
+
lines.push('}');
|
|
1721
|
+
lines.push('');
|
|
1722
|
+
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Adds the SharePoint-specific get method for reference entities.
|
|
1725
|
+
*/
|
|
1726
|
+
function addGetMethodForReferenceEntities(lines, modelName, dataSourceName, schemaJson) {
|
|
1727
|
+
if (!schemaJson) {
|
|
1728
|
+
return;
|
|
1748
1729
|
}
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
lines
|
|
1730
|
+
const schema = JSON.parse(schemaJson);
|
|
1731
|
+
const referencedEntities = typeof schema.referencedEntities === 'object' && schema.referencedEntities !== null
|
|
1732
|
+
? schema.referencedEntities
|
|
1733
|
+
: null;
|
|
1734
|
+
if (referencedEntities) {
|
|
1735
|
+
addGetMethodForReferenceEntitiesCore(lines, modelName);
|
|
1755
1736
|
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1737
|
+
}
|
|
1738
|
+
/*
|
|
1739
|
+
* The get method adds get function for sharepoint referenceEntities
|
|
1740
|
+
*/
|
|
1741
|
+
function addGetMethodForReferenceEntitiesCore(lines, modelName) {
|
|
1742
|
+
// Add method summary
|
|
1743
|
+
lines.push(`/**`, `* Utility method to get possible values of a referenced entity by its name.`, `*/`, ` public static async getReferencedEntity(search: string, referencedEntityKeyNormalized: string): Promise<IOperationResult<unknown>> {`, ` const params: {search: string} = { search };`, ` const result = await ${modelName}Service.client.executeAsync<{search: string}, unknown>(`, ` {`, ` connectorOperation: {`, ` tableName: ${getServiceName(modelName)},`, ` operationName: 'Get' + referencedEntityKeyNormalized,`, ` parameters: params`, ` }`, ` });`, ` return result;`, ` }`, ``);
|
|
1744
|
+
}
|
|
1745
|
+
/*
|
|
1746
|
+
* The ensureCommonModelFileExists method checks if the CommonModels.ts file exists in the specified output directory.
|
|
1747
|
+
* If it does not exist, it creates the file with a copyright notice and common model code.
|
|
1748
|
+
*/
|
|
1749
|
+
async function ensureCommonModelFileExists(outputDir, vfs) {
|
|
1750
|
+
// Node.js built-in modules are statically imported at the top of the file
|
|
1751
|
+
const commonModelPath = vfs.join(outputDir, MODELS_FOLDER, 'CommonModels.ts');
|
|
1752
|
+
if (!(await vfs.exists(commonModelPath))) {
|
|
1753
|
+
const lines = [];
|
|
1754
|
+
addCopyrightNotice(lines);
|
|
1755
|
+
addCommonModelCode(lines);
|
|
1756
|
+
await writeToFile(commonModelPath, lines, vfs);
|
|
1769
1757
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1758
|
+
}
|
|
1759
|
+
/*
|
|
1760
|
+
* The addCommonModelCode method generates common TypeScript model code for the CommonModels.ts file.
|
|
1761
|
+
* It includes interfaces for IGetOptions and IGetAllOptions.
|
|
1762
|
+
*/
|
|
1763
|
+
function addCommonModelCode(lines) {
|
|
1764
|
+
lines.push('export interface IGetOptions {');
|
|
1765
|
+
lines.push(' select?: string[];');
|
|
1766
|
+
lines.push('};');
|
|
1767
|
+
lines.push('');
|
|
1768
|
+
lines.push('export interface IGetAllOptions {');
|
|
1769
|
+
lines.push(' maxPageSize?: number;');
|
|
1770
|
+
lines.push(' select?: string[];');
|
|
1771
|
+
lines.push(' filter?: string;');
|
|
1772
|
+
lines.push(' orderBy?: string[];');
|
|
1773
|
+
lines.push(' top?: number;');
|
|
1774
|
+
lines.push(' skip?: number;');
|
|
1775
|
+
lines.push(' skipToken?: string;');
|
|
1776
|
+
lines.push('}');
|
|
1777
|
+
lines.push('');
|
|
1778
|
+
}
|
|
1779
|
+
/*
|
|
1780
|
+
* The generateTypeScriptModel method generates a TypeScript model from the JSON schema.
|
|
1781
|
+
* It returns the generated model code and the primary key of the model.
|
|
1782
|
+
*/
|
|
1783
|
+
function generateTypeScriptModel(schemaJson, modelName) {
|
|
1784
|
+
const root = parseSchemaJson(schemaJson);
|
|
1785
|
+
const propertiesNode = getPropertiesNode(root);
|
|
1786
|
+
const requiredFields = getRequiredFieldsFromStandardSchema(root);
|
|
1787
|
+
const lines = [];
|
|
1788
|
+
addCopyrightNotice(lines);
|
|
1789
|
+
const primaryKeyObj = { value: '' };
|
|
1790
|
+
addModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj);
|
|
1791
|
+
return { modelCode: lines.join('\n'), primaryKey: primaryKeyObj.value };
|
|
1792
|
+
}
|
|
1793
|
+
function generateSharepointTypeScriptModel(schemaJson, modelName) {
|
|
1794
|
+
const root = parseSchemaJson(schemaJson);
|
|
1795
|
+
const propertiesNode = getPropertiesNode(root);
|
|
1796
|
+
const requiredFields = getRequiredFieldsFromStandardSchema(root);
|
|
1797
|
+
const lines = [];
|
|
1798
|
+
addCopyrightNotice(lines);
|
|
1799
|
+
const primaryKeyObj = { value: '' };
|
|
1800
|
+
const { hasReferenceEntities } = addSPModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj, schemaJson);
|
|
1801
|
+
return { modelCode: lines.join('\n'), primaryKey: primaryKeyObj.value, hasReferenceEntities };
|
|
1802
|
+
}
|
|
1803
|
+
/*
|
|
1804
|
+
* The parseSchemaJson method parses the JSON schema and retrieves the items node.
|
|
1805
|
+
* It throws an exception if the schema format is invalid.
|
|
1806
|
+
*/
|
|
1807
|
+
function parseSchemaJson(schemaJson) {
|
|
1808
|
+
const root = JSON.parse(schemaJson);
|
|
1809
|
+
if (!root.schema || !root.schema.items) {
|
|
1810
|
+
throw new Error('Invalid schema format');
|
|
1789
1811
|
}
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
*/
|
|
1794
|
-
static generateTypeScriptModel(schemaJson, modelName) {
|
|
1795
|
-
const root = this.parseSchemaJson(schemaJson);
|
|
1796
|
-
const propertiesNode = this.getPropertiesNode(root);
|
|
1797
|
-
const requiredFields = this.getRequiredFieldsFromStandardSchema(root);
|
|
1798
|
-
const lines = [];
|
|
1799
|
-
this.addCopyrightNotice(lines);
|
|
1800
|
-
const primaryKeyObj = { value: '' };
|
|
1801
|
-
this.addModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj);
|
|
1802
|
-
return { modelCode: lines.join('\n'), primaryKey: primaryKeyObj.value };
|
|
1803
|
-
}
|
|
1804
|
-
static generateSharepointTypeScriptModel(schemaJson, modelName) {
|
|
1805
|
-
const root = this.parseSchemaJson(schemaJson);
|
|
1806
|
-
const propertiesNode = this.getPropertiesNode(root);
|
|
1807
|
-
const requiredFields = this.getRequiredFieldsFromStandardSchema(root);
|
|
1808
|
-
const lines = [];
|
|
1809
|
-
this.addCopyrightNotice(lines);
|
|
1810
|
-
const primaryKeyObj = { value: '' };
|
|
1811
|
-
const { hasReferenceEntities } = this.addSPModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj, schemaJson);
|
|
1812
|
-
return { modelCode: lines.join('\n'), primaryKey: primaryKeyObj.value, hasReferenceEntities };
|
|
1813
|
-
}
|
|
1814
|
-
/*
|
|
1815
|
-
* The parseSchemaJson method parses the JSON schema and retrieves the items node.
|
|
1816
|
-
* It throws an exception if the schema format is invalid.
|
|
1817
|
-
*/
|
|
1818
|
-
static parseSchemaJson(schemaJson) {
|
|
1819
|
-
const root = JSON.parse(schemaJson);
|
|
1820
|
-
if (!root.schema || !root.schema.items) {
|
|
1821
|
-
throw new Error('Invalid schema format');
|
|
1822
|
-
}
|
|
1823
|
-
const items = root.schema.items;
|
|
1824
|
-
if (typeof items !== 'object' || items === null || Array.isArray(items)) {
|
|
1825
|
-
throw new Error('Schema items must be a valid object');
|
|
1826
|
-
}
|
|
1827
|
-
return items;
|
|
1812
|
+
const items = root.schema.items;
|
|
1813
|
+
if (typeof items !== 'object' || items === null || Array.isArray(items)) {
|
|
1814
|
+
throw new Error('Schema items must be a valid object');
|
|
1828
1815
|
}
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
if (!itemsObj.required || !Array.isArray(itemsObj.required)) {
|
|
1839
|
-
return new Set();
|
|
1840
|
-
}
|
|
1841
|
-
return new Set(itemsObj.required.map((r) => String(r)));
|
|
1816
|
+
return items;
|
|
1817
|
+
}
|
|
1818
|
+
/*
|
|
1819
|
+
* The getRequiredFieldsFromStandardSchema method retrieves the required fields from the JSON schema.
|
|
1820
|
+
* It returns a Set of required field names.
|
|
1821
|
+
*/
|
|
1822
|
+
function getRequiredFieldsFromStandardSchema(itemsNode) {
|
|
1823
|
+
if (typeof itemsNode !== 'object' || itemsNode === null || Array.isArray(itemsNode)) {
|
|
1824
|
+
return new Set();
|
|
1842
1825
|
}
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1826
|
+
const itemsObj = itemsNode;
|
|
1827
|
+
if (!itemsObj.required || !Array.isArray(itemsObj.required)) {
|
|
1828
|
+
return new Set();
|
|
1829
|
+
}
|
|
1830
|
+
return new Set(itemsObj.required.map((r) => String(r)));
|
|
1831
|
+
}
|
|
1832
|
+
/*
|
|
1833
|
+
* The addSPModelInterfaceDeclaration method generates the TypeScript interface declaration for the model particular to Sharepoint.
|
|
1834
|
+
*/
|
|
1835
|
+
function addSPModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj, schemaJson) {
|
|
1836
|
+
const result = addSPReferencedEntityModelInterfaceDeclaration(schemaJson, lines);
|
|
1837
|
+
const referenceEntitiesObjectMapping = result.mapping;
|
|
1838
|
+
// lines are already updated by the method above
|
|
1839
|
+
// We will generate 3 interfaces:
|
|
1840
|
+
// A base one with properties that are not reference entities
|
|
1841
|
+
// A "read" model that extends the base one and adds reference entity properties with an object type (inferred from the referencedEntities section)
|
|
1842
|
+
// A "write" model that extends the base one and adds reference entities with the string/string[] type
|
|
1843
|
+
// All 3 interfaces will be exported
|
|
1844
|
+
const baseInterfaceLines = [];
|
|
1845
|
+
const readInterfaceLines = [];
|
|
1846
|
+
const writeInterfaceLines = [];
|
|
1847
|
+
// This will be true if there is at least one referenced entity
|
|
1848
|
+
// This flag is later used to determine if we need to use multiple interfaces in the service file
|
|
1849
|
+
let hasReferenceEntities = false;
|
|
1850
|
+
baseInterfaceLines.push(`export interface ${modelName}Base {`);
|
|
1851
|
+
readInterfaceLines.push(`export interface ${modelName}Read extends ${modelName}Base {`);
|
|
1852
|
+
writeInterfaceLines.push(`export interface ${modelName}Write extends ${modelName}Base {`);
|
|
1853
|
+
// lines.push(`export interface ${modelName} {`);
|
|
1854
|
+
for (const propName of Object.keys(propertiesNode)) {
|
|
1855
|
+
let propNameNormalized = propName;
|
|
1856
|
+
if (!isValidPropertyName(propName)) {
|
|
1857
|
+
propNameNormalized = `"${propName}"`; // Enclose invalid property names in double quotes
|
|
1858
|
+
}
|
|
1859
|
+
const isRequired = requiredFields.has(propName);
|
|
1860
|
+
const optionalMark = isRequired ? '' : '?';
|
|
1861
|
+
if (propName in referenceEntitiesObjectMapping) {
|
|
1862
|
+
if (isSharepointChoiceColumn(propertiesNode[propName])) {
|
|
1863
|
+
// Note: SharePoint choice/multi-choice columns are intentionally mapped to string/string[]
|
|
1864
|
+
// instead of the schema's { Value: string } object. This simplifies the SDK's public
|
|
1865
|
+
// surface and prevents recurring errors in AI agents (especially App Builder) that
|
|
1866
|
+
// frequently mishandled the complex object wrapper.
|
|
1867
|
+
if (propertiesNode[propName].type === 'array') {
|
|
1868
|
+
writeInterfaceLines.push(` ${propNameNormalized}${optionalMark}: string[];`);
|
|
1884
1869
|
}
|
|
1885
1870
|
else {
|
|
1886
|
-
writeInterfaceLines.push(` ${propNameNormalized}${optionalMark}:
|
|
1871
|
+
writeInterfaceLines.push(` ${propNameNormalized}${optionalMark}: string;`);
|
|
1887
1872
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1873
|
+
}
|
|
1874
|
+
else {
|
|
1875
|
+
writeInterfaceLines.push(` ${propNameNormalized}${optionalMark}: ${referenceEntitiesObjectMapping[propName]};`);
|
|
1876
|
+
}
|
|
1877
|
+
readInterfaceLines.push(` ${propNameNormalized}${optionalMark}: ${referenceEntitiesObjectMapping[propName]};`);
|
|
1878
|
+
hasReferenceEntities = true;
|
|
1879
|
+
continue;
|
|
1880
|
+
}
|
|
1881
|
+
addPropertyDeclaration(baseInterfaceLines, propName, propertiesNode[propName], requiredFields, primaryKeyObj);
|
|
1882
|
+
}
|
|
1883
|
+
baseInterfaceLines.push('}');
|
|
1884
|
+
baseInterfaceLines.push('');
|
|
1885
|
+
lines.push(...baseInterfaceLines);
|
|
1886
|
+
if (hasReferenceEntities) {
|
|
1887
|
+
readInterfaceLines.push('}');
|
|
1888
|
+
readInterfaceLines.push('');
|
|
1889
|
+
lines.push(...readInterfaceLines);
|
|
1890
|
+
}
|
|
1891
|
+
if (hasReferenceEntities) {
|
|
1892
|
+
writeInterfaceLines.push('}');
|
|
1893
|
+
writeInterfaceLines.push('');
|
|
1894
|
+
lines.push(...writeInterfaceLines);
|
|
1895
|
+
}
|
|
1896
|
+
return { referenceEntitiesObjectMapping, lines, hasReferenceEntities };
|
|
1897
|
+
}
|
|
1898
|
+
function addSPReferencedEntityModelInterfaceDeclaration(schemaJson, lines) {
|
|
1899
|
+
const referenceEntitiesObjectMapping = {};
|
|
1900
|
+
const schema = JSON.parse(schemaJson);
|
|
1901
|
+
const root = parseSchemaJson(schemaJson);
|
|
1902
|
+
const propertiesNode = getPropertiesNode(root);
|
|
1903
|
+
const referencedEntities = typeof schema.referencedEntities === 'object' && schema.referencedEntities !== null
|
|
1904
|
+
? schema.referencedEntities
|
|
1905
|
+
: null;
|
|
1906
|
+
if (referencedEntities) {
|
|
1907
|
+
for (const referencedEntityKey in referencedEntities) {
|
|
1908
|
+
if (!Object.prototype.hasOwnProperty.call(referencedEntities, referencedEntityKey)) {
|
|
1890
1909
|
continue;
|
|
1891
1910
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
lines.push(
|
|
1901
|
-
}
|
|
1902
|
-
if (hasReferenceEntities) {
|
|
1903
|
-
writeInterfaceLines.push('}');
|
|
1904
|
-
writeInterfaceLines.push('');
|
|
1905
|
-
lines.push(...writeInterfaceLines);
|
|
1911
|
+
referenceEntitiesObjectMapping[referencedEntityKey] = referencedEntityKey + 'Value';
|
|
1912
|
+
if (!(referencedEntityKey in propertiesNode)) {
|
|
1913
|
+
continue;
|
|
1914
|
+
}
|
|
1915
|
+
lines.push(`export interface ${referenceEntitiesObjectMapping[referencedEntityKey]} {`);
|
|
1916
|
+
// Add actual declaration of the referenced entity
|
|
1917
|
+
addInnerPropertyDeclaration(lines, propertiesNode, referencedEntityKey);
|
|
1918
|
+
lines.push('}');
|
|
1919
|
+
lines.push('');
|
|
1906
1920
|
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
if (
|
|
1918
|
-
|
|
1919
|
-
|
|
1921
|
+
}
|
|
1922
|
+
return {
|
|
1923
|
+
mapping: referenceEntitiesObjectMapping,
|
|
1924
|
+
lines,
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
function addInnerPropertyDeclaration(lines, propertiesNode, referencedEntityKey) {
|
|
1928
|
+
const propertyValue = propertiesNode[referencedEntityKey];
|
|
1929
|
+
if (propertyValue !== null && typeof propertyValue === 'object' && !Array.isArray(propertyValue)) {
|
|
1930
|
+
const propObj = propertyValue;
|
|
1931
|
+
if (propObj.properties && typeof propObj.properties === 'object' && !Array.isArray(propObj.properties)) {
|
|
1932
|
+
const properties = propObj.properties;
|
|
1933
|
+
for (const prop in properties) {
|
|
1934
|
+
if (!Object.prototype.hasOwnProperty.call(properties, prop)) {
|
|
1920
1935
|
continue;
|
|
1921
1936
|
}
|
|
1922
|
-
|
|
1923
|
-
if (!(
|
|
1924
|
-
|
|
1937
|
+
const propValue = properties[prop];
|
|
1938
|
+
if (propValue !== null && typeof propValue === 'object' && !Array.isArray(propValue)) {
|
|
1939
|
+
const propValueObj = propValue;
|
|
1940
|
+
const normalizedProp = convertToValidIdentifier(prop);
|
|
1941
|
+
lines.push(` ${normalizedProp}: ${mapJsonTypeToTypeScript(propValueObj.type)};`);
|
|
1925
1942
|
}
|
|
1926
|
-
lines.push(`export interface ${referenceEntitiesObjectMapping[referencedEntityKey]} {`);
|
|
1927
|
-
// Add actual declaration of the referenced entity
|
|
1928
|
-
this.addInnerPropertyDeclaration(lines, propertiesNode, referencedEntityKey);
|
|
1929
|
-
lines.push('}');
|
|
1930
|
-
lines.push('');
|
|
1931
1943
|
}
|
|
1932
1944
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
const propertyValue = propertiesNode[referencedEntityKey];
|
|
1940
|
-
if (propertyValue !== null && typeof propertyValue === 'object' && !Array.isArray(propertyValue)) {
|
|
1941
|
-
const propObj = propertyValue;
|
|
1942
|
-
if (propObj.properties &&
|
|
1943
|
-
typeof propObj.properties === 'object' &&
|
|
1944
|
-
!Array.isArray(propObj.properties)) {
|
|
1945
|
-
const properties = propObj.properties;
|
|
1946
|
-
for (const prop in properties) {
|
|
1947
|
-
if (!Object.prototype.hasOwnProperty.call(properties, prop)) {
|
|
1945
|
+
else if ('items' in propObj && typeof propObj.items === 'object' && propObj.items !== null) {
|
|
1946
|
+
const items = propObj.items;
|
|
1947
|
+
if ('properties' in items && typeof items.properties === 'object' && items.properties !== null) {
|
|
1948
|
+
const itemProperties = items.properties;
|
|
1949
|
+
for (const itemProp in itemProperties) {
|
|
1950
|
+
if (!Object.prototype.hasOwnProperty.call(itemProperties, itemProp)) {
|
|
1948
1951
|
continue;
|
|
1949
1952
|
}
|
|
1950
|
-
const
|
|
1951
|
-
if (
|
|
1952
|
-
const
|
|
1953
|
-
const
|
|
1954
|
-
lines.push(` ${
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
else if ('items' in propObj && typeof propObj.items === 'object' && propObj.items !== null) {
|
|
1959
|
-
const items = propObj.items;
|
|
1960
|
-
if ('properties' in items && typeof items.properties === 'object' && items.properties !== null) {
|
|
1961
|
-
const itemProperties = items.properties;
|
|
1962
|
-
for (const itemProp in itemProperties) {
|
|
1963
|
-
if (!Object.prototype.hasOwnProperty.call(itemProperties, itemProp)) {
|
|
1964
|
-
continue;
|
|
1965
|
-
}
|
|
1966
|
-
const itemPropValue = itemProperties[itemProp];
|
|
1967
|
-
if (itemPropValue !== null &&
|
|
1968
|
-
typeof itemPropValue === 'object' &&
|
|
1969
|
-
!Array.isArray(itemPropValue)) {
|
|
1970
|
-
const itemPropValueObj = itemPropValue;
|
|
1971
|
-
const normalizedItemProp = convertToValidIdentifier(itemProp);
|
|
1972
|
-
lines.push(` ${normalizedItemProp}: ${this.mapJsonTypeToTypeScript(itemPropValueObj.type)};`);
|
|
1973
|
-
}
|
|
1953
|
+
const itemPropValue = itemProperties[itemProp];
|
|
1954
|
+
if (itemPropValue !== null && typeof itemPropValue === 'object' && !Array.isArray(itemPropValue)) {
|
|
1955
|
+
const itemPropValueObj = itemPropValue;
|
|
1956
|
+
const normalizedItemProp = convertToValidIdentifier(itemProp);
|
|
1957
|
+
lines.push(` ${normalizedItemProp}: ${mapJsonTypeToTypeScript(itemPropValueObj.type)};`);
|
|
1974
1958
|
}
|
|
1975
1959
|
}
|
|
1976
1960
|
}
|
|
1977
1961
|
}
|
|
1978
1962
|
}
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
}
|
|
2063
|
-
lines.push(...imports.sort());
|
|
2064
|
-
if (additionalLine) {
|
|
2065
|
-
lines.push('');
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
/*
|
|
2069
|
-
* The addDataverseServiceImports method adds the necessary imports for Dataverse service classes with lookup columns.
|
|
2070
|
-
*/
|
|
2071
|
-
static addDataverseServiceImports(lines, modelName, hasLookupColumns) {
|
|
2072
|
-
const imports = [...this.collectStandardImports('crud')];
|
|
2073
|
-
imports.push(`import type { IGetOptions, IGetAllOptions } from '${this.RELATIVE_MODEL_PATH}/CommonModels';`);
|
|
2074
|
-
if (hasLookupColumns) {
|
|
2075
|
-
// Import both base and extended interfaces
|
|
2076
|
-
imports.push(`import type { ${modelName}Base, ${modelName} } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
2077
|
-
}
|
|
2078
|
-
else {
|
|
2079
|
-
// Import single interface
|
|
2080
|
-
imports.push(`import type { ${modelName}Base } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
2081
|
-
}
|
|
1963
|
+
}
|
|
1964
|
+
/*
|
|
1965
|
+
* The addModelInterfaceDeclaration method generates the TypeScript interface declaration for the model.
|
|
1966
|
+
*/
|
|
1967
|
+
function addModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKey) {
|
|
1968
|
+
lines.push(`export interface ${convertToValidIdentifier(modelName)} {`);
|
|
1969
|
+
primaryKey.value = '';
|
|
1970
|
+
// propertiesNode is an object with keys as property names.
|
|
1971
|
+
for (const propName of Object.keys(propertiesNode)) {
|
|
1972
|
+
addPropertyDeclaration(lines, propName, propertiesNode[propName], requiredFields, primaryKey);
|
|
1973
|
+
}
|
|
1974
|
+
lines.push('}');
|
|
1975
|
+
lines.push('');
|
|
1976
|
+
}
|
|
1977
|
+
/*
|
|
1978
|
+
* The addPropertyDeclaration method generates the TypeScript property declaration for a given JSON property.
|
|
1979
|
+
* It checks if the property name is valid, maps the JSON type to TypeScript type,
|
|
1980
|
+
* and adds the property to the lines list.
|
|
1981
|
+
*/
|
|
1982
|
+
function addPropertyDeclaration(lines, propertyName, propertyValue, requiredFields, primaryKey) {
|
|
1983
|
+
let propName = propertyName;
|
|
1984
|
+
// Extract type information
|
|
1985
|
+
let propType = 'unknown';
|
|
1986
|
+
let dataverseType;
|
|
1987
|
+
if (propertyValue !== null && typeof propertyValue === 'object' && !Array.isArray(propertyValue)) {
|
|
1988
|
+
const propObj = propertyValue;
|
|
1989
|
+
// Check for Dataverse-specific type first
|
|
1990
|
+
if ('x-ms-dataverse-type' in propObj && typeof propObj['x-ms-dataverse-type'] === 'string') {
|
|
1991
|
+
dataverseType = propObj['x-ms-dataverse-type'];
|
|
1992
|
+
// Map Dataverse type to TypeScript type
|
|
1993
|
+
propType = mapDataverseTypeToTypeScript(dataverseType);
|
|
1994
|
+
}
|
|
1995
|
+
else if ('type' in propObj && typeof propObj.type === 'string') {
|
|
1996
|
+
// Map standard JSON type to TypeScript type
|
|
1997
|
+
propType = determineTypeScriptType(propertyName, propObj, '', propName);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
const keyType = typeof propertyValue === 'object' &&
|
|
2001
|
+
propertyValue !== null &&
|
|
2002
|
+
!Array.isArray(propertyValue) &&
|
|
2003
|
+
'x-ms-keyType' in propertyValue
|
|
2004
|
+
? propertyValue['x-ms-keyType']
|
|
2005
|
+
: '';
|
|
2006
|
+
if (keyType === 'primary') {
|
|
2007
|
+
primaryKey.value = propName;
|
|
2008
|
+
}
|
|
2009
|
+
// Check if the property name is valid
|
|
2010
|
+
if (!isValidPropertyName(propName)) {
|
|
2011
|
+
propName = `"${propName}"`; // Enclose invalid property names in double quotes
|
|
2012
|
+
}
|
|
2013
|
+
const isRequired = requiredFields.has(propertyName);
|
|
2014
|
+
const optionalMark = isRequired ? '' : '?';
|
|
2015
|
+
lines.push(` ${propName}${optionalMark}: ${propType};`);
|
|
2016
|
+
}
|
|
2017
|
+
/*
|
|
2018
|
+
* The generateTypeScriptService method generates a TypeScript service class for the model.
|
|
2019
|
+
* It includes methods for CRUD operations and uses the Power SDK for data access.
|
|
2020
|
+
*/
|
|
2021
|
+
function generateTypeScriptService(modelName, dataSourceName, primaryKey, isADataverseEntity) {
|
|
2022
|
+
const lines = [];
|
|
2023
|
+
addCopyrightNotice(lines);
|
|
2024
|
+
addStandardServiceImports(lines, modelName, isADataverseEntity, false);
|
|
2025
|
+
addServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, isADataverseEntity);
|
|
2026
|
+
return lines.join('\n');
|
|
2027
|
+
}
|
|
2028
|
+
/*
|
|
2029
|
+
* The generateDataverseTypeScriptService method generates a TypeScript service class specifically for Dataverse entities.
|
|
2030
|
+
* It handles lookup columns by using base interface for Create/Update and extended interface for Get/GetAll operations.
|
|
2031
|
+
*/
|
|
2032
|
+
function generateDataverseTypeScriptService(modelName, dataSourceName, primaryKey, hasLookupColumns) {
|
|
2033
|
+
const lines = [];
|
|
2034
|
+
addCopyrightNotice(lines);
|
|
2035
|
+
addDataverseServiceImports(lines, modelName, hasLookupColumns);
|
|
2036
|
+
addDataverseServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, hasLookupColumns);
|
|
2037
|
+
return lines.join('\n');
|
|
2038
|
+
}
|
|
2039
|
+
/*
|
|
2040
|
+
* The addStandardServiceImports method adds the necessary imports for the service class.
|
|
2041
|
+
*/
|
|
2042
|
+
function addStandardServiceImports(lines, modelName, isADataverseEntity, additionalLine = true) {
|
|
2043
|
+
const imports = [...collectStandardImports('crud')];
|
|
2044
|
+
imports.push(`import type { IGetOptions, IGetAllOptions } from '${RELATIVE_MODEL_PATH}/CommonModels';`, `import type { ${modelName} } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
2045
|
+
if (isADataverseEntity) {
|
|
2082
2046
|
imports.push("import type { GetEntityMetadataOptions, EntityMetadata } from '@microsoft/power-apps/data/metadata/dataverse';");
|
|
2083
|
-
lines.push(...imports.sort());
|
|
2084
|
-
lines.push('');
|
|
2085
2047
|
}
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
imports.push(`import type { IGetOptions, IGetAllOptions } from '${this.RELATIVE_MODEL_PATH}/CommonModels';`);
|
|
2089
|
-
if (hasReferenceEntities) {
|
|
2090
|
-
// Import both base and read/write interfaces
|
|
2091
|
-
imports.push(`import type { ${modelName}Read, ${modelName}Write } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
2092
|
-
}
|
|
2093
|
-
else {
|
|
2094
|
-
// Import single interface
|
|
2095
|
-
imports.push(`import type { ${modelName}Base } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
2096
|
-
}
|
|
2097
|
-
lines.push(...imports.sort());
|
|
2048
|
+
lines.push(...imports.sort());
|
|
2049
|
+
if (additionalLine) {
|
|
2098
2050
|
lines.push('');
|
|
2099
2051
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2052
|
+
}
|
|
2053
|
+
/*
|
|
2054
|
+
* The addDataverseServiceImports method adds the necessary imports for Dataverse service classes with lookup columns.
|
|
2055
|
+
*/
|
|
2056
|
+
function addDataverseServiceImports(lines, modelName, hasLookupColumns) {
|
|
2057
|
+
const imports = [...collectStandardImports('crud')];
|
|
2058
|
+
imports.push(`import type { IGetOptions, IGetAllOptions } from '${RELATIVE_MODEL_PATH}/CommonModels';`);
|
|
2059
|
+
if (hasLookupColumns) {
|
|
2060
|
+
// Import both base and extended interfaces
|
|
2061
|
+
imports.push(`import type { ${modelName}Base, ${modelName} } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
2062
|
+
}
|
|
2063
|
+
else {
|
|
2064
|
+
// Import single interface
|
|
2065
|
+
imports.push(`import type { ${modelName}Base } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
2066
|
+
}
|
|
2067
|
+
imports.push("import type { GetEntityMetadataOptions, EntityMetadata } from '@microsoft/power-apps/data/metadata/dataverse';");
|
|
2068
|
+
lines.push(...imports.sort());
|
|
2069
|
+
lines.push('');
|
|
2070
|
+
}
|
|
2071
|
+
function addSharepointServiceImports(lines, modelName, hasReferenceEntities) {
|
|
2072
|
+
const imports = [...collectStandardImports('crud')];
|
|
2073
|
+
imports.push(`import type { IGetOptions, IGetAllOptions } from '${RELATIVE_MODEL_PATH}/CommonModels';`);
|
|
2074
|
+
if (hasReferenceEntities) {
|
|
2075
|
+
// Import both base and read/write interfaces
|
|
2076
|
+
imports.push(`import type { ${modelName}Read, ${modelName}Write } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
2077
|
+
}
|
|
2078
|
+
else {
|
|
2079
|
+
// Import single interface
|
|
2080
|
+
imports.push(`import type { ${modelName}Base } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
|
|
2081
|
+
}
|
|
2082
|
+
lines.push(...imports.sort());
|
|
2083
|
+
lines.push('');
|
|
2084
|
+
}
|
|
2085
|
+
/*
|
|
2086
|
+
* The addServiceFields method adds fields to the service class.
|
|
2087
|
+
* It includes the Power SDK instance and a static instance of the service.
|
|
2088
|
+
*/
|
|
2089
|
+
function addServiceFields(lines, dataSourceName) {
|
|
2090
|
+
lines.push(` private static readonly dataSourceName = '${dataSourceName.toLowerCase()}';`);
|
|
2091
|
+
lines.push(``);
|
|
2092
|
+
lines.push(` private static readonly client = getClient(dataSourcesInfo);`);
|
|
2093
|
+
}
|
|
2094
|
+
/*
|
|
2095
|
+
* The AddServiceClassDefinition method generates the service class definition.
|
|
2096
|
+
* It takes the lines to write to, model name, data source name, schema, and response model name as parameters.
|
|
2097
|
+
*/
|
|
2098
|
+
function addServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, isADataverseEntity) {
|
|
2099
|
+
addServiceClassHeader(lines, modelName, dataSourceName, true);
|
|
2100
|
+
addServiceMethods(lines, modelName, dataSourceName, primaryKey, isADataverseEntity);
|
|
2101
|
+
lines.push('}');
|
|
2102
|
+
lines.push('');
|
|
2103
|
+
}
|
|
2104
|
+
/*
|
|
2105
|
+
* The addDataverseServiceClassDefinition method generates the service class definition for Dataverse entities with lookup columns.
|
|
2106
|
+
*/
|
|
2107
|
+
function addDataverseServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, hasLookupColumns) {
|
|
2108
|
+
addServiceClassHeader(lines, modelName, dataSourceName, true);
|
|
2109
|
+
addDataverseServiceMethods(lines, modelName, dataSourceName, primaryKey, hasLookupColumns);
|
|
2110
|
+
lines.push('}');
|
|
2111
|
+
lines.push('');
|
|
2112
|
+
}
|
|
2113
|
+
/*
|
|
2114
|
+
* The addServiceMethods method generates the CRUD methods for the service class.
|
|
2115
|
+
* It includes create, update, delete, get, and getAll methods.
|
|
2116
|
+
*/
|
|
2117
|
+
function addServiceMethods(lines, modelName, dataSourceName, primaryKey, isADataverseEntity) {
|
|
2118
|
+
const methods = [];
|
|
2119
|
+
methods.push(generateCreateMethod(modelName, primaryKey));
|
|
2120
|
+
methods.push(generateUpdateMethod(modelName, primaryKey));
|
|
2121
|
+
methods.push(generateDeleteMethod(modelName));
|
|
2122
|
+
methods.push(generateGetMethod(modelName));
|
|
2123
|
+
methods.push(generateGetAllMethod(modelName));
|
|
2124
|
+
if (isADataverseEntity) {
|
|
2125
|
+
methods.push(generateGetMetadataMethod(modelName));
|
|
2126
|
+
}
|
|
2127
|
+
for (const method of methods) {
|
|
2117
2128
|
lines.push('');
|
|
2129
|
+
lines.push(...method);
|
|
2118
2130
|
}
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2131
|
+
}
|
|
2132
|
+
/*
|
|
2133
|
+
* The addDataverseServiceMethods method generates the CRUD methods for Dataverse service classes with lookup columns.
|
|
2134
|
+
* Uses base interface for Create/Update and extended interface for Get/GetAll operations.
|
|
2135
|
+
*/
|
|
2136
|
+
function addDataverseServiceMethods(lines, modelName, dataSourceName, primaryKey, hasLookupColumns) {
|
|
2137
|
+
const methods = [];
|
|
2138
|
+
methods.push(generateDataverseCreateMethod(modelName, primaryKey, hasLookupColumns));
|
|
2139
|
+
methods.push(generateDataverseUpdateMethod(modelName, primaryKey, hasLookupColumns));
|
|
2140
|
+
methods.push(generateDeleteMethod(modelName));
|
|
2141
|
+
methods.push(generateDataverseGetMethod(modelName, hasLookupColumns));
|
|
2142
|
+
methods.push(generateDataverseGetAllMethod(modelName, hasLookupColumns));
|
|
2143
|
+
methods.push(generateDataverseGetMetadataMethod(modelName, hasLookupColumns));
|
|
2144
|
+
for (const method of methods) {
|
|
2126
2145
|
lines.push('');
|
|
2146
|
+
lines.push(...method);
|
|
2127
2147
|
}
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
}
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
*
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
? `Partial<Omit<${hasReferenceEntities ? `${modelName}Write` : `${modelName}Base`}, '${primaryKey}'>>`
|
|
2287
|
-
: hasReferenceEntities
|
|
2288
|
-
? `${modelName}Write`
|
|
2289
|
-
: `${modelName}Base`;
|
|
2290
|
-
const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
|
|
2291
|
-
return [
|
|
2292
|
-
` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${returnType}>> {`,
|
|
2293
|
-
` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${returnType}>(`,
|
|
2294
|
-
` ${getServiceName(modelName)},`,
|
|
2295
|
-
` id.toString(),`,
|
|
2296
|
-
` changedFields`,
|
|
2297
|
-
` );`,
|
|
2298
|
-
` return result;`,
|
|
2299
|
-
` }`,
|
|
2300
|
-
];
|
|
2301
|
-
}
|
|
2302
|
-
/*
|
|
2303
|
-
* The delete method removes a record by its ID.
|
|
2304
|
-
*/
|
|
2305
|
-
static generateSharepointDeleteMethod(modelName) {
|
|
2306
|
-
return [
|
|
2307
|
-
` public static async delete(id: string): Promise<void> {`,
|
|
2308
|
-
` await ${modelName}Service.client.deleteRecordAsync(`,
|
|
2309
|
-
` ${getServiceName(modelName)},`,
|
|
2310
|
-
` id.toString());`,
|
|
2311
|
-
` }`,
|
|
2312
|
-
];
|
|
2313
|
-
}
|
|
2314
|
-
/*
|
|
2315
|
-
* The get method retrieves a record by its ID.
|
|
2316
|
-
*/
|
|
2317
|
-
static generateSharepointGetMethod(modelName, hasReferenceEntities) {
|
|
2318
|
-
/**
|
|
2319
|
-
* if referenceEntities is true return modelNameRead
|
|
2320
|
-
* else return modelNameBase
|
|
2321
|
-
*/
|
|
2322
|
-
const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
|
|
2323
|
-
/**
|
|
2324
|
-
*/
|
|
2325
|
-
return [
|
|
2326
|
-
` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${returnType}>> {`,
|
|
2327
|
-
` const result = await ${modelName}Service.client.retrieveRecordAsync<${returnType}>(`,
|
|
2328
|
-
` ${getServiceName(modelName)},`,
|
|
2329
|
-
` id.toString(),`,
|
|
2330
|
-
` options`,
|
|
2331
|
-
` );`,
|
|
2332
|
-
` return result;`,
|
|
2333
|
-
` }`,
|
|
2334
|
-
];
|
|
2335
|
-
}
|
|
2336
|
-
/*
|
|
2337
|
-
* The getAll method retrieves all records of the specified model.
|
|
2338
|
-
*/
|
|
2339
|
-
static generateSharepointGetAllMethod(modelName, hasReferenceEntities) {
|
|
2340
|
-
const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
|
|
2341
|
-
return [
|
|
2342
|
-
` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${returnType}[]>> {`,
|
|
2343
|
-
` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${returnType}>(`,
|
|
2344
|
-
` ${getServiceName(modelName)},`,
|
|
2345
|
-
` options`,
|
|
2346
|
-
` );`,
|
|
2347
|
-
` return result;`,
|
|
2348
|
-
` }`,
|
|
2349
|
-
];
|
|
2350
|
-
}
|
|
2351
|
-
/*
|
|
2352
|
-
* The generateDataverseCreateMethod method adds a new record using the base interface for Dataverse entities.
|
|
2353
|
-
*/
|
|
2354
|
-
static generateDataverseCreateMethod(modelName, primaryKey, hasLookupColumns) {
|
|
2355
|
-
const baseInterfaceName = hasLookupColumns ? `${modelName}Base` : modelName;
|
|
2356
|
-
const extendedInterfaceName = modelName;
|
|
2357
|
-
const datatypeForCreate = primaryKey !== '' ? `Omit<${baseInterfaceName}, '${primaryKey}'>` : baseInterfaceName;
|
|
2358
|
-
return [
|
|
2359
|
-
` public static async create(record: ${datatypeForCreate}): Promise<IOperationResult<${extendedInterfaceName}>> {`,
|
|
2360
|
-
` const result = await ${modelName}Service.client.createRecordAsync<${datatypeForCreate}, ${extendedInterfaceName}>(`,
|
|
2361
|
-
` ${getServiceName(modelName)},`,
|
|
2362
|
-
` record`,
|
|
2363
|
-
` );`,
|
|
2364
|
-
` return result;`,
|
|
2365
|
-
` }`,
|
|
2366
|
-
];
|
|
2367
|
-
}
|
|
2368
|
-
/*
|
|
2369
|
-
* The generateDataverseUpdateMethod method modifies an existing record using the base interface for Dataverse entities.
|
|
2370
|
-
*/
|
|
2371
|
-
static generateDataverseUpdateMethod(modelName, primaryKey, hasLookupColumns) {
|
|
2372
|
-
const baseInterfaceName = hasLookupColumns ? `${modelName}Base` : modelName;
|
|
2373
|
-
const extendedInterfaceName = modelName;
|
|
2374
|
-
const datatypeForUpdate = primaryKey !== '' ? `Partial<Omit<${baseInterfaceName}, '${primaryKey}'>>` : baseInterfaceName;
|
|
2375
|
-
return [
|
|
2376
|
-
` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${extendedInterfaceName}>> {`,
|
|
2377
|
-
` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${extendedInterfaceName}>(`,
|
|
2378
|
-
` ${getServiceName(modelName)},`,
|
|
2379
|
-
` id.toString(),`,
|
|
2380
|
-
` changedFields`,
|
|
2381
|
-
` );`,
|
|
2382
|
-
` return result;`,
|
|
2383
|
-
` }`,
|
|
2384
|
-
];
|
|
2385
|
-
}
|
|
2386
|
-
/*
|
|
2387
|
-
* The generateDataverseGetMethod method retrieves a record by its ID using the extended interface for Dataverse entities.
|
|
2388
|
-
*/
|
|
2389
|
-
static generateDataverseGetMethod(modelName, hasLookupColumns) {
|
|
2390
|
-
const extendedInterfaceName = modelName;
|
|
2391
|
-
return [
|
|
2392
|
-
` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${extendedInterfaceName}>> {`,
|
|
2393
|
-
` const result = await ${modelName}Service.client.retrieveRecordAsync<${extendedInterfaceName}>(`,
|
|
2394
|
-
` ${getServiceName(modelName)},`,
|
|
2395
|
-
` id.toString(),`,
|
|
2396
|
-
` options`,
|
|
2397
|
-
` );`,
|
|
2398
|
-
` return result;`,
|
|
2399
|
-
` }`,
|
|
2400
|
-
];
|
|
2401
|
-
}
|
|
2402
|
-
/*
|
|
2403
|
-
* The generateDataverseGetAllMethod method retrieves all records using the extended interface for Dataverse entities.
|
|
2404
|
-
*/
|
|
2405
|
-
static generateDataverseGetAllMethod(modelName, hasLookupColumns) {
|
|
2406
|
-
const extendedInterfaceName = modelName;
|
|
2407
|
-
return [
|
|
2408
|
-
` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${extendedInterfaceName}[]>> {`,
|
|
2409
|
-
` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${extendedInterfaceName}>(`,
|
|
2410
|
-
` ${getServiceName(modelName)},`,
|
|
2411
|
-
` options`,
|
|
2412
|
-
` );`,
|
|
2413
|
-
` return result;`,
|
|
2414
|
-
` }`,
|
|
2415
|
-
];
|
|
2416
|
-
}
|
|
2417
|
-
/*
|
|
2418
|
-
* The generateDataverseGetMetadataMethod method gets metadata using the extended interface for Dataverse entities.
|
|
2148
|
+
}
|
|
2149
|
+
/*
|
|
2150
|
+
* The create method adds a new record to the data source.
|
|
2151
|
+
*/
|
|
2152
|
+
function generateCreateMethod(modelName, primaryKey) {
|
|
2153
|
+
const datatypeForCreate = primaryKey !== '' ? `Omit<${modelName}, '${primaryKey}'>` : modelName;
|
|
2154
|
+
return [
|
|
2155
|
+
` public static async create(record: ${datatypeForCreate}): Promise<IOperationResult<${modelName}>> {`,
|
|
2156
|
+
` const result = await ${modelName}Service.client.createRecordAsync<${datatypeForCreate}, ${modelName}>(`,
|
|
2157
|
+
` ${getServiceName(modelName)},`,
|
|
2158
|
+
` record`,
|
|
2159
|
+
` );`,
|
|
2160
|
+
` return result;`,
|
|
2161
|
+
` }`,
|
|
2162
|
+
];
|
|
2163
|
+
}
|
|
2164
|
+
/*
|
|
2165
|
+
* The update method modifies an existing record.
|
|
2166
|
+
*/
|
|
2167
|
+
function generateUpdateMethod(modelName, primaryKey) {
|
|
2168
|
+
const datatypeForUpdate = primaryKey !== '' ? `Partial<Omit<${modelName}, '${primaryKey}'>>` : modelName;
|
|
2169
|
+
return [
|
|
2170
|
+
` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${modelName}>> {`,
|
|
2171
|
+
` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${modelName}>(`,
|
|
2172
|
+
` ${getServiceName(modelName)},`,
|
|
2173
|
+
` id.toString(),`,
|
|
2174
|
+
` changedFields`,
|
|
2175
|
+
` );`,
|
|
2176
|
+
` return result;`,
|
|
2177
|
+
` }`,
|
|
2178
|
+
];
|
|
2179
|
+
}
|
|
2180
|
+
/*
|
|
2181
|
+
* The delete method removes a record by its ID.
|
|
2182
|
+
*/
|
|
2183
|
+
function generateDeleteMethod(modelName) {
|
|
2184
|
+
return [
|
|
2185
|
+
` public static async delete(id: string): Promise<void> {`,
|
|
2186
|
+
` await ${modelName}Service.client.deleteRecordAsync(`,
|
|
2187
|
+
` ${getServiceName(modelName)},`,
|
|
2188
|
+
` id.toString());`,
|
|
2189
|
+
` }`,
|
|
2190
|
+
];
|
|
2191
|
+
}
|
|
2192
|
+
/*
|
|
2193
|
+
* The get method retrieves a record by its ID.
|
|
2194
|
+
*/
|
|
2195
|
+
function generateGetMethod(modelName) {
|
|
2196
|
+
return [
|
|
2197
|
+
` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${modelName}>> {`,
|
|
2198
|
+
` const result = await ${modelName}Service.client.retrieveRecordAsync<${modelName}>(`,
|
|
2199
|
+
` ${getServiceName(modelName)},`,
|
|
2200
|
+
` id.toString(),`,
|
|
2201
|
+
` options`,
|
|
2202
|
+
` );`,
|
|
2203
|
+
` return result;`,
|
|
2204
|
+
` }`,
|
|
2205
|
+
];
|
|
2206
|
+
}
|
|
2207
|
+
/*
|
|
2208
|
+
* The getAll method retrieves all records of the specified model.
|
|
2209
|
+
*/
|
|
2210
|
+
function generateGetAllMethod(modelName) {
|
|
2211
|
+
return [
|
|
2212
|
+
` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${modelName}[]>> {`,
|
|
2213
|
+
` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${modelName}>(`,
|
|
2214
|
+
` ${getServiceName(modelName)},`,
|
|
2215
|
+
` options`,
|
|
2216
|
+
` );`,
|
|
2217
|
+
` return result;`,
|
|
2218
|
+
` }`,
|
|
2219
|
+
];
|
|
2220
|
+
}
|
|
2221
|
+
function generateGetMetadataMethod(modelName) {
|
|
2222
|
+
return [
|
|
2223
|
+
` public static getMetadata(`,
|
|
2224
|
+
` options: GetEntityMetadataOptions<${modelName}> = {}`,
|
|
2225
|
+
` ): Promise<IOperationResult<Partial<EntityMetadata>>> {`,
|
|
2226
|
+
` return ${modelName}Service.client.executeAsync({`,
|
|
2227
|
+
` dataverseRequest: {`,
|
|
2228
|
+
` action: "getEntityMetadata",`,
|
|
2229
|
+
` parameters: {`,
|
|
2230
|
+
` tableName: ${getServiceName(modelName)},`,
|
|
2231
|
+
` options: options as GetEntityMetadataOptions,`,
|
|
2232
|
+
` },`,
|
|
2233
|
+
` },`,
|
|
2234
|
+
` });`,
|
|
2235
|
+
` }`,
|
|
2236
|
+
];
|
|
2237
|
+
}
|
|
2238
|
+
/*
|
|
2239
|
+
* The create method adds a new record to the data source.
|
|
2240
|
+
*/
|
|
2241
|
+
function generateSharepointCreateMethod(modelName, primaryKey, hasReferenceEntities) {
|
|
2242
|
+
/**
|
|
2243
|
+
* if hasReferenceEntities is true take in modelNameWrite-primaryKey as input and return modelNameRead
|
|
2244
|
+
* else take in modelNameBase-primaryKey as input and return modelNameBase
|
|
2245
|
+
*/
|
|
2246
|
+
const datatypeForCreate = primaryKey !== ''
|
|
2247
|
+
? `Omit<${hasReferenceEntities ? `${modelName}Write` : `${modelName}Base`}, '${primaryKey}'>`
|
|
2248
|
+
: hasReferenceEntities
|
|
2249
|
+
? `${modelName}Write`
|
|
2250
|
+
: `${modelName}Base`;
|
|
2251
|
+
const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
|
|
2252
|
+
return [
|
|
2253
|
+
` public static async create(record: ${datatypeForCreate}): Promise<IOperationResult<${returnType}>> {`,
|
|
2254
|
+
` const result = await ${modelName}Service.client.createRecordAsync<${datatypeForCreate}, ${returnType}>(`,
|
|
2255
|
+
` ${getServiceName(modelName)},`,
|
|
2256
|
+
` record`,
|
|
2257
|
+
` );`,
|
|
2258
|
+
` return result;`,
|
|
2259
|
+
` }`,
|
|
2260
|
+
];
|
|
2261
|
+
}
|
|
2262
|
+
/*
|
|
2263
|
+
* The update method modifies an existing record.
|
|
2264
|
+
*/
|
|
2265
|
+
function generateSharepointUpdateMethod(modelName, primaryKey, hasReferenceEntities) {
|
|
2266
|
+
/**
|
|
2267
|
+
* if hasReferenceEntities is true take in modelNameWrite-primaryKey as input and return modelNameRead
|
|
2268
|
+
* else take in modelNameBase-primaryKey as input and return modelNameBase
|
|
2269
|
+
*/
|
|
2270
|
+
const datatypeForUpdate = primaryKey !== ''
|
|
2271
|
+
? `Partial<Omit<${hasReferenceEntities ? `${modelName}Write` : `${modelName}Base`}, '${primaryKey}'>>`
|
|
2272
|
+
: hasReferenceEntities
|
|
2273
|
+
? `${modelName}Write`
|
|
2274
|
+
: `${modelName}Base`;
|
|
2275
|
+
const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
|
|
2276
|
+
return [
|
|
2277
|
+
` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${returnType}>> {`,
|
|
2278
|
+
` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${returnType}>(`,
|
|
2279
|
+
` ${getServiceName(modelName)},`,
|
|
2280
|
+
` id.toString(),`,
|
|
2281
|
+
` changedFields`,
|
|
2282
|
+
` );`,
|
|
2283
|
+
` return result;`,
|
|
2284
|
+
` }`,
|
|
2285
|
+
];
|
|
2286
|
+
}
|
|
2287
|
+
/*
|
|
2288
|
+
* The delete method removes a record by its ID.
|
|
2289
|
+
*/
|
|
2290
|
+
function generateSharepointDeleteMethod(modelName) {
|
|
2291
|
+
return [
|
|
2292
|
+
` public static async delete(id: string): Promise<void> {`,
|
|
2293
|
+
` await ${modelName}Service.client.deleteRecordAsync(`,
|
|
2294
|
+
` ${getServiceName(modelName)},`,
|
|
2295
|
+
` id.toString());`,
|
|
2296
|
+
` }`,
|
|
2297
|
+
];
|
|
2298
|
+
}
|
|
2299
|
+
/*
|
|
2300
|
+
* The get method retrieves a record by its ID.
|
|
2301
|
+
*/
|
|
2302
|
+
function generateSharepointGetMethod(modelName, hasReferenceEntities) {
|
|
2303
|
+
/**
|
|
2304
|
+
* if referenceEntities is true return modelNameRead
|
|
2305
|
+
* else return modelNameBase
|
|
2419
2306
|
*/
|
|
2420
|
-
|
|
2421
|
-
const extendedInterfaceName = modelName;
|
|
2422
|
-
return [
|
|
2423
|
-
` public static getMetadata(`,
|
|
2424
|
-
` options: GetEntityMetadataOptions<${extendedInterfaceName}> = {}`,
|
|
2425
|
-
` ): Promise<IOperationResult<Partial<EntityMetadata>>> {`,
|
|
2426
|
-
` return ${modelName}Service.client.executeAsync({`,
|
|
2427
|
-
` dataverseRequest: {`,
|
|
2428
|
-
` action: "getEntityMetadata",`,
|
|
2429
|
-
` parameters: {`,
|
|
2430
|
-
` tableName: ${getServiceName(modelName)},`,
|
|
2431
|
-
` options: options as GetEntityMetadataOptions,`,
|
|
2432
|
-
` },`,
|
|
2433
|
-
` },`,
|
|
2434
|
-
` });`,
|
|
2435
|
-
` }`,
|
|
2436
|
-
];
|
|
2437
|
-
}
|
|
2307
|
+
const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
|
|
2438
2308
|
/**
|
|
2439
|
-
* The generateIndexFile method creates an index.ts file that exports all models and services.
|
|
2440
2309
|
*/
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
const
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
}
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2310
|
+
return [
|
|
2311
|
+
` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${returnType}>> {`,
|
|
2312
|
+
` const result = await ${modelName}Service.client.retrieveRecordAsync<${returnType}>(`,
|
|
2313
|
+
` ${getServiceName(modelName)},`,
|
|
2314
|
+
` id.toString(),`,
|
|
2315
|
+
` options`,
|
|
2316
|
+
` );`,
|
|
2317
|
+
` return result;`,
|
|
2318
|
+
` }`,
|
|
2319
|
+
];
|
|
2320
|
+
}
|
|
2321
|
+
/*
|
|
2322
|
+
* The getAll method retrieves all records of the specified model.
|
|
2323
|
+
*/
|
|
2324
|
+
function generateSharepointGetAllMethod(modelName, hasReferenceEntities) {
|
|
2325
|
+
const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
|
|
2326
|
+
return [
|
|
2327
|
+
` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${returnType}[]>> {`,
|
|
2328
|
+
` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${returnType}>(`,
|
|
2329
|
+
` ${getServiceName(modelName)},`,
|
|
2330
|
+
` options`,
|
|
2331
|
+
` );`,
|
|
2332
|
+
` return result;`,
|
|
2333
|
+
` }`,
|
|
2334
|
+
];
|
|
2335
|
+
}
|
|
2336
|
+
/*
|
|
2337
|
+
* The generateDataverseCreateMethod method adds a new record using the base interface for Dataverse entities.
|
|
2338
|
+
*/
|
|
2339
|
+
function generateDataverseCreateMethod(modelName, primaryKey, hasLookupColumns) {
|
|
2340
|
+
const baseInterfaceName = hasLookupColumns ? `${modelName}Base` : modelName;
|
|
2341
|
+
const extendedInterfaceName = modelName;
|
|
2342
|
+
const datatypeForCreate = primaryKey !== '' ? `Omit<${baseInterfaceName}, '${primaryKey}'>` : baseInterfaceName;
|
|
2343
|
+
return [
|
|
2344
|
+
` public static async create(record: ${datatypeForCreate}): Promise<IOperationResult<${extendedInterfaceName}>> {`,
|
|
2345
|
+
` const result = await ${modelName}Service.client.createRecordAsync<${datatypeForCreate}, ${extendedInterfaceName}>(`,
|
|
2346
|
+
` ${getServiceName(modelName)},`,
|
|
2347
|
+
` record`,
|
|
2348
|
+
` );`,
|
|
2349
|
+
` return result;`,
|
|
2350
|
+
` }`,
|
|
2351
|
+
];
|
|
2352
|
+
}
|
|
2353
|
+
/*
|
|
2354
|
+
* The generateDataverseUpdateMethod method modifies an existing record using the base interface for Dataverse entities.
|
|
2355
|
+
*/
|
|
2356
|
+
function generateDataverseUpdateMethod(modelName, primaryKey, hasLookupColumns) {
|
|
2357
|
+
const baseInterfaceName = hasLookupColumns ? `${modelName}Base` : modelName;
|
|
2358
|
+
const extendedInterfaceName = modelName;
|
|
2359
|
+
const datatypeForUpdate = primaryKey !== '' ? `Partial<Omit<${baseInterfaceName}, '${primaryKey}'>>` : baseInterfaceName;
|
|
2360
|
+
return [
|
|
2361
|
+
` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${extendedInterfaceName}>> {`,
|
|
2362
|
+
` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${extendedInterfaceName}>(`,
|
|
2363
|
+
` ${getServiceName(modelName)},`,
|
|
2364
|
+
` id.toString(),`,
|
|
2365
|
+
` changedFields`,
|
|
2366
|
+
` );`,
|
|
2367
|
+
` return result;`,
|
|
2368
|
+
` }`,
|
|
2369
|
+
];
|
|
2370
|
+
}
|
|
2371
|
+
/*
|
|
2372
|
+
* The generateDataverseGetMethod method retrieves a record by its ID using the extended interface for Dataverse entities.
|
|
2373
|
+
*/
|
|
2374
|
+
function generateDataverseGetMethod(modelName, hasLookupColumns) {
|
|
2375
|
+
const extendedInterfaceName = modelName;
|
|
2376
|
+
return [
|
|
2377
|
+
` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${extendedInterfaceName}>> {`,
|
|
2378
|
+
` const result = await ${modelName}Service.client.retrieveRecordAsync<${extendedInterfaceName}>(`,
|
|
2379
|
+
` ${getServiceName(modelName)},`,
|
|
2380
|
+
` id.toString(),`,
|
|
2381
|
+
` options`,
|
|
2382
|
+
` );`,
|
|
2383
|
+
` return result;`,
|
|
2384
|
+
` }`,
|
|
2385
|
+
];
|
|
2386
|
+
}
|
|
2387
|
+
/*
|
|
2388
|
+
* The generateDataverseGetAllMethod method retrieves all records using the extended interface for Dataverse entities.
|
|
2389
|
+
*/
|
|
2390
|
+
function generateDataverseGetAllMethod(modelName, hasLookupColumns) {
|
|
2391
|
+
const extendedInterfaceName = modelName;
|
|
2392
|
+
return [
|
|
2393
|
+
` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${extendedInterfaceName}[]>> {`,
|
|
2394
|
+
` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${extendedInterfaceName}>(`,
|
|
2395
|
+
` ${getServiceName(modelName)},`,
|
|
2396
|
+
` options`,
|
|
2397
|
+
` );`,
|
|
2398
|
+
` return result;`,
|
|
2399
|
+
` }`,
|
|
2400
|
+
];
|
|
2401
|
+
}
|
|
2402
|
+
/*
|
|
2403
|
+
* The generateDataverseGetMetadataMethod method gets metadata using the extended interface for Dataverse entities.
|
|
2404
|
+
*/
|
|
2405
|
+
function generateDataverseGetMetadataMethod(modelName, hasLookupColumns) {
|
|
2406
|
+
const extendedInterfaceName = modelName;
|
|
2407
|
+
return [
|
|
2408
|
+
` public static getMetadata(`,
|
|
2409
|
+
` options: GetEntityMetadataOptions<${extendedInterfaceName}> = {}`,
|
|
2410
|
+
` ): Promise<IOperationResult<Partial<EntityMetadata>>> {`,
|
|
2411
|
+
` return ${modelName}Service.client.executeAsync({`,
|
|
2412
|
+
` dataverseRequest: {`,
|
|
2413
|
+
` action: "getEntityMetadata",`,
|
|
2414
|
+
` parameters: {`,
|
|
2415
|
+
` tableName: ${getServiceName(modelName)},`,
|
|
2416
|
+
` options: options as GetEntityMetadataOptions,`,
|
|
2417
|
+
` },`,
|
|
2418
|
+
` },`,
|
|
2419
|
+
` });`,
|
|
2420
|
+
` }`,
|
|
2421
|
+
];
|
|
2422
|
+
}
|
|
2423
|
+
/**
|
|
2424
|
+
* The generateIndexFile method creates an index.ts file that exports all models and services.
|
|
2425
|
+
*/
|
|
2426
|
+
async function generateIndexFile(outputDir, vfs) {
|
|
2427
|
+
const indexPath = vfs.join(outputDir, GENERATED_FOLDER, 'index.ts');
|
|
2428
|
+
const lines = [];
|
|
2429
|
+
// Add copyright notice
|
|
2430
|
+
addCopyrightNotice(lines);
|
|
2431
|
+
// Export all models from generated/models
|
|
2432
|
+
const modelsDir = vfs.join(outputDir, MODELS_FOLDER);
|
|
2433
|
+
if (await vfs.exists(modelsDir)) {
|
|
2434
|
+
const modelFiles = (await vfs.readdir(modelsDir)).filter((file) => file.endsWith('.ts'));
|
|
2435
|
+
if (modelFiles.length > 0) {
|
|
2436
|
+
lines.push('// Models');
|
|
2437
|
+
for (const file of modelFiles) {
|
|
2438
|
+
const fileName = file.replace('.ts', '');
|
|
2439
|
+
lines.push(`export * as ${fileName} from './models/${fileName}';`);
|
|
2470
2440
|
}
|
|
2441
|
+
lines.push('');
|
|
2471
2442
|
}
|
|
2472
|
-
// Write the index file
|
|
2473
|
-
await this.writeToFile(indexPath, lines, vfs);
|
|
2474
2443
|
}
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
}
|
|
2487
|
-
// Delete Services directory if it exists
|
|
2488
|
-
if (await vfs.exists(servicesDir)) {
|
|
2489
|
-
await vfs.rmdir(servicesDir);
|
|
2444
|
+
// Export all services from generated/services
|
|
2445
|
+
const servicesDir = vfs.join(outputDir, SERVICES_FOLDER);
|
|
2446
|
+
if (await vfs.exists(servicesDir)) {
|
|
2447
|
+
const serviceFiles = (await vfs.readdir(servicesDir)).filter((file) => file.endsWith('.ts'));
|
|
2448
|
+
if (serviceFiles.length > 0) {
|
|
2449
|
+
lines.push('// Services');
|
|
2450
|
+
for (const file of serviceFiles) {
|
|
2451
|
+
const fileName = file.replace('.ts', '');
|
|
2452
|
+
lines.push(`export * from './services/${fileName}';`);
|
|
2453
|
+
}
|
|
2454
|
+
lines.push('');
|
|
2490
2455
|
}
|
|
2491
|
-
await _a.run([schemaFolderPath, outputDir], logger);
|
|
2492
2456
|
}
|
|
2457
|
+
// Write the index file
|
|
2458
|
+
await writeToFile(indexPath, lines, vfs);
|
|
2493
2459
|
}
|
|
2494
|
-
_a = ModelServiceGenerator;
|
|
2495
|
-
// Configuration constants for output folders
|
|
2496
|
-
ModelServiceGenerator.GENERATED_FOLDER = 'generated';
|
|
2497
|
-
ModelServiceGenerator.MODELS_FOLDER = `${_a.GENERATED_FOLDER}/models`;
|
|
2498
|
-
ModelServiceGenerator.SERVICES_FOLDER = `${_a.GENERATED_FOLDER}/services`;
|
|
2499
|
-
ModelServiceGenerator.RELATIVE_MODEL_PATH = '../models';
|
|
2500
2460
|
/**
|
|
2501
2461
|
* Check if the propertyNode is a sharepoint choice or multi select choice
|
|
2502
2462
|
*
|
|
@@ -2504,14 +2464,14 @@ ModelServiceGenerator.RELATIVE_MODEL_PATH = '../models';
|
|
|
2504
2464
|
* @returns {boolean}
|
|
2505
2465
|
*/
|
|
2506
2466
|
function isSharepointChoiceColumn(propNode) {
|
|
2507
|
-
var
|
|
2467
|
+
var _a;
|
|
2508
2468
|
try {
|
|
2509
2469
|
if (!propNode || typeof propNode !== 'object' || Array.isArray(propNode)) {
|
|
2510
2470
|
return false;
|
|
2511
2471
|
}
|
|
2512
2472
|
else if ((propNode === null || propNode === void 0 ? void 0 : propNode['x-ms-capabilities']) !== undefined) {
|
|
2513
2473
|
const capabilities = propNode['x-ms-capabilities'];
|
|
2514
|
-
if (((
|
|
2474
|
+
if (((_a = capabilities === null || capabilities === void 0 ? void 0 : capabilities['x-ms-sp']) === null || _a === void 0 ? void 0 : _a.IsChoice) === true) {
|
|
2515
2475
|
return true;
|
|
2516
2476
|
}
|
|
2517
2477
|
}
|