@microsoft/power-apps-cli 0.5.4 → 0.5.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/Constants/HelpStrings.d.ts +3 -3
- package/lib/Constants/HelpStrings.js +3 -3
- package/lib/Constants/HelpStrings.js.map +1 -1
- package/lib/HttpClient/CliHttpClient.d.ts.map +1 -1
- package/lib/HttpClient/CliHttpClient.js +8 -1
- package/lib/HttpClient/CliHttpClient.js.map +1 -1
- package/lib/Verbs/AddDataSource.d.ts.map +1 -1
- package/lib/Verbs/AddDataSource.js +7 -6
- package/lib/Verbs/AddDataSource.js.map +1 -1
- package/lib/Verbs/DeleteDataSource.d.ts +1 -1
- package/lib/Verbs/DeleteDataSource.d.ts.map +1 -1
- package/lib/Verbs/DeleteDataSource.js +2 -1
- package/lib/Verbs/DeleteDataSource.js.map +1 -1
- package/lib/Verbs/ListConnectionReferences.js +6 -6
- package/lib/Verbs/ListConnectionReferences.js.map +1 -1
- package/lib/Verbs/ListEnvironmentVariables.js +6 -6
- package/lib/Verbs/ListEnvironmentVariables.js.map +1 -1
- package/lib/Verbs/Push.js +7 -10
- package/lib/Verbs/Push.js.map +1 -1
- package/lib/Verbs/VerbConstants.d.ts +1 -1
- package/lib/Verbs/VerbConstants.js +3 -3
- package/lib/Verbs/VerbConstants.js.map +1 -1
- package/lib/__tests__/E2eTests/addAndDeleteDataverseDS.test.js +4 -3
- package/lib/__tests__/E2eTests/addAndDeleteDataverseDS.test.js.map +1 -1
- package/lib/__tests__/E2eTests/addTop10Datasources.test.js +3 -2
- package/lib/__tests__/E2eTests/addTop10Datasources.test.js.map +1 -1
- package/lib/__tests__/E2eTests/cliHelp.test.js +4 -11
- package/lib/__tests__/E2eTests/cliHelp.test.js.map +1 -1
- package/lib/__tests__/helpers/e2eTestHelpers.d.ts +24 -0
- package/lib/__tests__/helpers/e2eTestHelpers.d.ts.map +1 -0
- package/lib/__tests__/helpers/e2eTestHelpers.js +28 -0
- package/lib/__tests__/helpers/e2eTestHelpers.js.map +1 -0
- package/lib-cjs/Constants/HelpStrings.d.ts +3 -3
- package/lib-cjs/Constants/HelpStrings.js +3 -3
- package/lib-cjs/Constants/HelpStrings.js.map +1 -1
- package/lib-cjs/HttpClient/CliHttpClient.d.ts.map +1 -1
- package/lib-cjs/HttpClient/CliHttpClient.js +8 -2
- package/lib-cjs/HttpClient/CliHttpClient.js.map +1 -1
- package/lib-cjs/Verbs/AddDataSource.d.ts.map +1 -1
- package/lib-cjs/Verbs/AddDataSource.js +7 -6
- package/lib-cjs/Verbs/AddDataSource.js.map +1 -1
- package/lib-cjs/Verbs/DeleteDataSource.d.ts +1 -1
- package/lib-cjs/Verbs/DeleteDataSource.d.ts.map +1 -1
- package/lib-cjs/Verbs/DeleteDataSource.js +5 -1
- package/lib-cjs/Verbs/DeleteDataSource.js.map +1 -1
- package/lib-cjs/Verbs/ListConnectionReferences.js +6 -6
- package/lib-cjs/Verbs/ListConnectionReferences.js.map +1 -1
- package/lib-cjs/Verbs/ListEnvironmentVariables.js +6 -6
- package/lib-cjs/Verbs/ListEnvironmentVariables.js.map +1 -1
- package/lib-cjs/Verbs/Push.js +11 -16
- package/lib-cjs/Verbs/Push.js.map +1 -1
- package/lib-cjs/Verbs/VerbConstants.d.ts +1 -1
- package/lib-cjs/Verbs/VerbConstants.js +3 -3
- package/lib-cjs/Verbs/VerbConstants.js.map +1 -1
- package/lib-cjs/__tests__/E2eTests/addAndDeleteDataverseDS.test.js +3 -2
- package/lib-cjs/__tests__/E2eTests/addAndDeleteDataverseDS.test.js.map +1 -1
- package/lib-cjs/__tests__/E2eTests/addTop10Datasources.test.js +2 -1
- package/lib-cjs/__tests__/E2eTests/addTop10Datasources.test.js.map +1 -1
- package/lib-cjs/__tests__/E2eTests/cliHelp.test.js +4 -11
- package/lib-cjs/__tests__/E2eTests/cliHelp.test.js.map +1 -1
- package/lib-cjs/__tests__/helpers/e2eTestHelpers.d.ts +23 -0
- package/lib-cjs/__tests__/helpers/e2eTestHelpers.d.ts.map +1 -0
- package/lib-cjs/__tests__/helpers/e2eTestHelpers.js +31 -0
- package/lib-cjs/__tests__/helpers/e2eTestHelpers.js.map +1 -0
- package/node_modules/@microsoft/powerapps-data/package.json +2 -2
- package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/PushApp.js +10 -3
- package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/PushApp.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/codeGenUtils.d.ts +20 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/codeGenUtils.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/codeGenUtils.js +79 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/codeGenUtils.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/dataSourceInfoProcessor.d.ts +2 -2
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/dataSourceInfoProcessor.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/dataSourceInfoProcessor.js +27 -68
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/dataSourceInfoProcessor.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.d.ts +0 -59
- 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 +69 -281
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/nameUtility.d.ts +26 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/nameUtility.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/nameUtility.js +153 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/nameUtility.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/AzureDevOpsModelServiceGenerator.spec.d.ts +5 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/AzureDevOpsModelServiceGenerator.spec.d.ts.map +1 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/AzureDevOpsModelServiceGenerator.spec.js +93 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/AzureDevOpsModelServiceGenerator.spec.js.map +1 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/DataSourceInfoProcessor.spec.js +897 -2
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/DataSourceInfoProcessor.spec.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/ModelServiceGenerator.spec.js +1 -17
- 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.d.ts +5 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/arrayTypeGeneration.spec.d.ts.map +1 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/arrayTypeGeneration.spec.js +63 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/arrayTypeGeneration.spec.js.map +1 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/PushApp.js +9 -4
- 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/codeGenUtils.d.ts +20 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/codeGenUtils.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/codeGenUtils.js +86 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/codeGenUtils.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/dataSourceInfoProcessor.d.ts +2 -2
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/dataSourceInfoProcessor.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/dataSourceInfoProcessor.js +25 -67
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/dataSourceInfoProcessor.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/modelServiceGenerator.d.ts +0 -59
- 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 +65 -279
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/modelServiceGenerator.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/nameUtility.d.ts +26 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/nameUtility.d.ts.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/nameUtility.js +161 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/nameUtility.js.map +1 -1
- package/node_modules/@microsoft/{powerapps-data/lib/components/codeGen/dataSourceInfo.js → powerapps-player-actions/lib-cjs/__tests__/AzureDevOpsModelServiceGenerator.spec.d.ts} +0 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/AzureDevOpsModelServiceGenerator.spec.d.ts.map +1 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/AzureDevOpsModelServiceGenerator.spec.js +206 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/AzureDevOpsModelServiceGenerator.spec.js.map +1 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/DataSourceInfoProcessor.spec.js +1277 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/DataSourceInfoProcessor.spec.js.map +1 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/ModelServiceGenerator.spec.js +0 -32
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/ModelServiceGenerator.spec.js.map +1 -1
- package/node_modules/@microsoft/{powerapps-data/lib/__tests__/dataSourceInfoProcessor.test.d.ts → powerapps-player-actions/lib-cjs/__tests__/arrayTypeGeneration.spec.d.ts} +0 -1
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/arrayTypeGeneration.spec.d.ts.map +1 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/arrayTypeGeneration.spec.js +171 -0
- package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/arrayTypeGeneration.spec.js.map +1 -0
- package/node_modules/@microsoft/powerapps-player-actions/package.json +2 -2
- package/node_modules/@pa-client/powerapps-player-services/package.json +1 -1
- package/package.json +4 -4
- package/node_modules/@microsoft/powerapps-data/lib/__tests__/dataSourceInfoProcessor.test.d.ts.map +0 -1
- package/node_modules/@microsoft/powerapps-data/lib/__tests__/dataSourceInfoProcessor.test.js +0 -782
- package/node_modules/@microsoft/powerapps-data/lib/__tests__/dataSourceInfoProcessor.test.js.map +0 -1
- package/node_modules/@microsoft/powerapps-data/lib/components/codeGen/dataSourceInfo.d.ts +0 -28
- package/node_modules/@microsoft/powerapps-data/lib/components/codeGen/dataSourceInfo.d.ts.map +0 -1
- package/node_modules/@microsoft/powerapps-data/lib/components/codeGen/dataSourceInfo.js.map +0 -1
- package/node_modules/@microsoft/powerapps-data/lib/components/codeGen/dataSourceInfoProcessor.d.ts +0 -10
- package/node_modules/@microsoft/powerapps-data/lib/components/codeGen/dataSourceInfoProcessor.d.ts.map +0 -1
- package/node_modules/@microsoft/powerapps-data/lib/components/codeGen/dataSourceInfoProcessor.js +0 -530
- package/node_modules/@microsoft/powerapps-data/lib/components/codeGen/dataSourceInfoProcessor.js.map +0 -1
|
@@ -3,13 +3,28 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
|
-
import { afterEach, beforeEach, describe, it } from '@jest/globals';
|
|
6
|
+
import { afterEach, beforeEach, describe, it, jest } from '@jest/globals';
|
|
7
7
|
import { clearVfs, setVfs } from '../VfsManager';
|
|
8
8
|
import mockVfs from './mocks/mockVfs';
|
|
9
9
|
import { extractDeclaration } from './Utils';
|
|
10
10
|
import { processDataSourceInfo } from '../CodeGen/dataSourceInfoProcessor';
|
|
11
11
|
import { beforeAll, expect } from '@jest/globals';
|
|
12
12
|
import { getMockLogger } from './mocks/Logger.mock';
|
|
13
|
+
/**
|
|
14
|
+
* Parses the generated TypeScript content and returns the DataSourcesInfo object.
|
|
15
|
+
* This provides type-safe access to the generated data structure instead of fragile string checks.
|
|
16
|
+
* @param generatedContent The generated TypeScript file content
|
|
17
|
+
* @returns The parsed DataSourcesInfo object
|
|
18
|
+
*/
|
|
19
|
+
function parseDataSourcesInfo(generatedContent) {
|
|
20
|
+
const jsonMatch = generatedContent.match(/export const dataSourcesInfo = ({[\s\S]*});?\s*$/);
|
|
21
|
+
if (!jsonMatch) {
|
|
22
|
+
throw new Error('Could not extract dataSourcesInfo from generated content');
|
|
23
|
+
}
|
|
24
|
+
// Remove trailing semicolon if present
|
|
25
|
+
const jsonStr = jsonMatch[1].replace(/;\s*$/, '');
|
|
26
|
+
return JSON.parse(jsonStr);
|
|
27
|
+
}
|
|
13
28
|
async function setupMockSchemas() {
|
|
14
29
|
// Reset the mock filesystem
|
|
15
30
|
mockVfs.reset();
|
|
@@ -25,7 +40,7 @@ const localFilePaths = {
|
|
|
25
40
|
codeGenPath: `${baseTestDir}\\src`,
|
|
26
41
|
};
|
|
27
42
|
const mockLogger = getMockLogger();
|
|
28
|
-
describe('
|
|
43
|
+
describe('dataSourceInfoProcessor with real schema files', () => {
|
|
29
44
|
beforeAll(async () => {
|
|
30
45
|
await setupMockSchemas();
|
|
31
46
|
});
|
|
@@ -51,4 +66,884 @@ describe('ModelServiceGenerator', () => {
|
|
|
51
66
|
});
|
|
52
67
|
});
|
|
53
68
|
});
|
|
69
|
+
describe('dataSourceInfoProcessor with mocked schema files', () => {
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
jest.clearAllMocks();
|
|
72
|
+
mockVfs.reset();
|
|
73
|
+
setVfs(mockVfs);
|
|
74
|
+
});
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
clearVfs();
|
|
77
|
+
});
|
|
78
|
+
describe('processDataSourceInfo', () => {
|
|
79
|
+
it('should process empty folder without errors', async () => {
|
|
80
|
+
// Arrange
|
|
81
|
+
const folderPath = '/test/folder';
|
|
82
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
83
|
+
// Act & Assert
|
|
84
|
+
await expect(processDataSourceInfo(folderPath, mockLogger)).resolves.not.toThrow();
|
|
85
|
+
});
|
|
86
|
+
it('should process folder with JSON files and generate output', async () => {
|
|
87
|
+
// Arrange
|
|
88
|
+
const folderPath = '/test/folder';
|
|
89
|
+
const jsonContent = JSON.stringify({
|
|
90
|
+
name: 'test-api',
|
|
91
|
+
type: 'Microsoft.PowerApps/apis',
|
|
92
|
+
properties: {
|
|
93
|
+
swagger: {
|
|
94
|
+
paths: {
|
|
95
|
+
'/test': {
|
|
96
|
+
get: {
|
|
97
|
+
operationId: 'GetTest',
|
|
98
|
+
responses: {
|
|
99
|
+
'200': {
|
|
100
|
+
description: 'Success',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
// Setup mock file system
|
|
110
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
111
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'test.json'), jsonContent);
|
|
112
|
+
// Act
|
|
113
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
114
|
+
// Assert
|
|
115
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
116
|
+
const outputExists = await mockVfs.exists(outputPath);
|
|
117
|
+
expect(outputExists).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
it('should handle file processing errors with file path context', async () => {
|
|
120
|
+
// Arrange
|
|
121
|
+
const folderPath = '/test/folder';
|
|
122
|
+
const invalidJsonContent = '{ invalid json';
|
|
123
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
124
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'invalid.json'), invalidJsonContent);
|
|
125
|
+
// Act & Assert
|
|
126
|
+
await expect(processDataSourceInfo(folderPath, mockLogger)).rejects.toThrow(/Error processing file.*invalid\.json/);
|
|
127
|
+
});
|
|
128
|
+
it('should create output directory if it does not exist', async () => {
|
|
129
|
+
// Arrange
|
|
130
|
+
const folderPath = '/test/folder';
|
|
131
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
132
|
+
// Act
|
|
133
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
134
|
+
// Assert
|
|
135
|
+
const outputDir = mockVfs.join(folderPath, 'appschemas');
|
|
136
|
+
const dirExists = await mockVfs.exists(outputDir);
|
|
137
|
+
expect(dirExists).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
describe('Swagger API processing', () => {
|
|
141
|
+
it('should process Microsoft.PowerApps/apis type with swagger paths', async () => {
|
|
142
|
+
var _a, _b, _c, _d, _e, _f;
|
|
143
|
+
// Arrange
|
|
144
|
+
const folderPath = '/test/folder';
|
|
145
|
+
const swaggerJson = {
|
|
146
|
+
name: 'users-api',
|
|
147
|
+
type: 'Microsoft.PowerApps/apis',
|
|
148
|
+
properties: {
|
|
149
|
+
swagger: {
|
|
150
|
+
paths: {
|
|
151
|
+
'/users': {
|
|
152
|
+
get: {
|
|
153
|
+
operationId: 'GetUsers',
|
|
154
|
+
parameters: [
|
|
155
|
+
{
|
|
156
|
+
name: 'limit',
|
|
157
|
+
in: 'query',
|
|
158
|
+
required: false,
|
|
159
|
+
type: 'integer',
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
responses: {
|
|
163
|
+
'200': {
|
|
164
|
+
description: 'Success',
|
|
165
|
+
schema: {
|
|
166
|
+
type: 'array',
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
post: {
|
|
172
|
+
operationId: 'CreateUser',
|
|
173
|
+
responses: {
|
|
174
|
+
'201': {
|
|
175
|
+
description: 'Created',
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
185
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'swagger.json'), JSON.stringify(swaggerJson));
|
|
186
|
+
// Act
|
|
187
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
188
|
+
// Assert
|
|
189
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
190
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
191
|
+
const parsed = parseDataSourcesInfo(generatedContent);
|
|
192
|
+
// Verify the data source exists with sanitized name (hyphens become underscores)
|
|
193
|
+
expect(parsed).toHaveProperty('users_api');
|
|
194
|
+
const dataSource = parsed.users_api;
|
|
195
|
+
// Verify APIs are generated correctly
|
|
196
|
+
expect(dataSource.apis).toHaveProperty('GetUsers');
|
|
197
|
+
expect(dataSource.apis).toHaveProperty('CreateUser');
|
|
198
|
+
// Verify GetUsers API details
|
|
199
|
+
const getUsersApi = dataSource.apis.GetUsers;
|
|
200
|
+
expect(getUsersApi.path).toBe('/users');
|
|
201
|
+
expect(getUsersApi.method).toBe('GET');
|
|
202
|
+
expect(getUsersApi.parameters).toBeDefined();
|
|
203
|
+
expect((_a = getUsersApi.parameters) === null || _a === void 0 ? void 0 : _a.length).toBe(1);
|
|
204
|
+
expect((_b = getUsersApi.parameters) === null || _b === void 0 ? void 0 : _b[0].name).toBe('limit');
|
|
205
|
+
expect((_d = (_c = getUsersApi.responseInfo) === null || _c === void 0 ? void 0 : _c['200']) === null || _d === void 0 ? void 0 : _d.type).toBe('array');
|
|
206
|
+
// Verify CreateUser API details
|
|
207
|
+
const createUserApi = dataSource.apis.CreateUser;
|
|
208
|
+
expect(createUserApi.path).toBe('/users');
|
|
209
|
+
expect(createUserApi.method).toBe('POST');
|
|
210
|
+
expect((_f = (_e = createUserApi.responseInfo) === null || _e === void 0 ? void 0 : _e['201']) === null || _f === void 0 ? void 0 : _f.type).toBe('void');
|
|
211
|
+
});
|
|
212
|
+
it('should handle swagger with parameter references', async () => {
|
|
213
|
+
// Arrange
|
|
214
|
+
const folderPath = '/test/folder';
|
|
215
|
+
const swaggerJson = {
|
|
216
|
+
name: 'items-api',
|
|
217
|
+
type: 'Microsoft.PowerApps/apis',
|
|
218
|
+
properties: {
|
|
219
|
+
swagger: {
|
|
220
|
+
paths: {
|
|
221
|
+
'/items': {
|
|
222
|
+
get: {
|
|
223
|
+
operationId: 'GetItems',
|
|
224
|
+
parameters: [
|
|
225
|
+
{
|
|
226
|
+
$ref: '#/swagger/parameters/CommonParam',
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
responses: {
|
|
230
|
+
'200': {
|
|
231
|
+
description: 'Success',
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
parameters: {
|
|
238
|
+
CommonParam: {
|
|
239
|
+
name: 'commonParam',
|
|
240
|
+
in: 'header',
|
|
241
|
+
required: true,
|
|
242
|
+
type: 'string',
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
249
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'swagger-ref.json'), JSON.stringify(swaggerJson));
|
|
250
|
+
// Act
|
|
251
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
252
|
+
// Assert
|
|
253
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
254
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
255
|
+
expect(generatedContent).toContain('items_api');
|
|
256
|
+
expect(generatedContent).toContain('GetItems');
|
|
257
|
+
// Note: Parameter reference resolution is tested but the mock doesn't properly resolve it
|
|
258
|
+
// The actual implementation would resolve the commonParam parameter
|
|
259
|
+
});
|
|
260
|
+
it('should handle responses with schema references', async () => {
|
|
261
|
+
// Arrange
|
|
262
|
+
const folderPath = '/test/folder';
|
|
263
|
+
const swaggerJson = {
|
|
264
|
+
name: 'models-api',
|
|
265
|
+
type: 'Microsoft.PowerApps/apis',
|
|
266
|
+
properties: {
|
|
267
|
+
swagger: {
|
|
268
|
+
paths: {
|
|
269
|
+
'/models': {
|
|
270
|
+
get: {
|
|
271
|
+
operationId: 'GetModels',
|
|
272
|
+
responses: {
|
|
273
|
+
'200': {
|
|
274
|
+
description: 'Success',
|
|
275
|
+
schema: {
|
|
276
|
+
$ref: '#/definitions/ModelResponse',
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
definitions: {
|
|
284
|
+
ModelResponse: {
|
|
285
|
+
type: 'object',
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
292
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'models.json'), JSON.stringify(swaggerJson));
|
|
293
|
+
// Act
|
|
294
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
295
|
+
// Assert
|
|
296
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
297
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
298
|
+
expect(generatedContent).toContain('GetModels');
|
|
299
|
+
expect(generatedContent).toContain('"type": "object"');
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
describe('SQL stored procedure processing', () => {
|
|
303
|
+
it('should process SQL stored procedure with input parameters', async () => {
|
|
304
|
+
var _a, _b;
|
|
305
|
+
// Arrange
|
|
306
|
+
const folderPath = '/test/procedures/GetUserById';
|
|
307
|
+
const procedureJson = {
|
|
308
|
+
name: '[dbo].[GetUserById]',
|
|
309
|
+
schema: {
|
|
310
|
+
procedureresultschema: {
|
|
311
|
+
type: 'object',
|
|
312
|
+
},
|
|
313
|
+
inputparameters: {
|
|
314
|
+
properties: {
|
|
315
|
+
UserId: {
|
|
316
|
+
type: 'integer',
|
|
317
|
+
required: true,
|
|
318
|
+
},
|
|
319
|
+
IncludeDetails: {
|
|
320
|
+
type: 'boolean',
|
|
321
|
+
required: false,
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
328
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'procedure.json'), JSON.stringify(procedureJson));
|
|
329
|
+
// Act
|
|
330
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
331
|
+
// Assert
|
|
332
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
333
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
334
|
+
const parsed = parseDataSourcesInfo(generatedContent);
|
|
335
|
+
// Verify the data source exists (uses parent folder name as key)
|
|
336
|
+
expect(parsed).toHaveProperty('getuserbyid');
|
|
337
|
+
const dataSource = parsed.getuserbyid;
|
|
338
|
+
// Verify the stored procedure API
|
|
339
|
+
expect(dataSource.apis).toHaveProperty('GetUserById');
|
|
340
|
+
const procApi = dataSource.apis.GetUserById;
|
|
341
|
+
expect(procApi.path).toBe('/dbo.GetUserById');
|
|
342
|
+
expect(procApi.method).toBe('POST');
|
|
343
|
+
// Verify parameters are present
|
|
344
|
+
expect(procApi.parameters).toBeDefined();
|
|
345
|
+
const paramNames = (_b = (_a = procApi.parameters) === null || _a === void 0 ? void 0 : _a.map((p) => p.name)) !== null && _b !== void 0 ? _b : [];
|
|
346
|
+
expect(paramNames).toContain('UserId');
|
|
347
|
+
expect(paramNames).toContain('IncludeDetails');
|
|
348
|
+
});
|
|
349
|
+
it('should handle stored procedure without input parameters', async () => {
|
|
350
|
+
// Arrange
|
|
351
|
+
const folderPath = '/test/procedures/GetAllUsers';
|
|
352
|
+
const procedureJson = {
|
|
353
|
+
name: '[dbo].[GetAllUsers]',
|
|
354
|
+
schema: {
|
|
355
|
+
procedureresultschema: {
|
|
356
|
+
type: 'array',
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
361
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'procedure.json'), JSON.stringify(procedureJson));
|
|
362
|
+
// Act
|
|
363
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
364
|
+
// Assert
|
|
365
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
366
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
367
|
+
expect(generatedContent).toContain('getallusers');
|
|
368
|
+
expect(generatedContent).toContain('"/dbo.GetAllUsers"');
|
|
369
|
+
});
|
|
370
|
+
it('should determine property key from parent folder for stored procedures', async () => {
|
|
371
|
+
// Arrange
|
|
372
|
+
const folderPath = '/test/procedures/UserProcedures';
|
|
373
|
+
const procedureJson = {
|
|
374
|
+
name: '[dbo].[GetUser]',
|
|
375
|
+
schema: {
|
|
376
|
+
procedureresultschema: {
|
|
377
|
+
type: 'object',
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
382
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'procedure.json'), JSON.stringify(procedureJson));
|
|
383
|
+
// Act
|
|
384
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
385
|
+
// Assert
|
|
386
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
387
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
388
|
+
expect(generatedContent).toContain('userprocedures');
|
|
389
|
+
});
|
|
390
|
+
it('should handle non-dbo schemas', async () => {
|
|
391
|
+
// Arrange
|
|
392
|
+
const folderPath = '/test/procedures/GetAllUsers';
|
|
393
|
+
const procedureJson = {
|
|
394
|
+
name: '[foo].[GetAllUsers]',
|
|
395
|
+
schema: {
|
|
396
|
+
procedureresultschema: {
|
|
397
|
+
type: 'array',
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
402
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'procedure.json'), JSON.stringify(procedureJson));
|
|
403
|
+
// Act
|
|
404
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
405
|
+
// Assert
|
|
406
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
407
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
408
|
+
expect(generatedContent).toContain('getallusers');
|
|
409
|
+
expect(generatedContent).toContain('"/foo.GetAllUsers"');
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
describe('SQL table processing', () => {
|
|
413
|
+
it('should process SQL table schema with primary key', async () => {
|
|
414
|
+
// Arrange
|
|
415
|
+
const folderPath = '/test/tables';
|
|
416
|
+
const tableJson = {
|
|
417
|
+
name: '[dbo].[Users]',
|
|
418
|
+
schema: {
|
|
419
|
+
items: {
|
|
420
|
+
properties: {
|
|
421
|
+
Id: {
|
|
422
|
+
type: 'integer',
|
|
423
|
+
'x-ms-keyType': 'primary',
|
|
424
|
+
},
|
|
425
|
+
Name: {
|
|
426
|
+
type: 'string',
|
|
427
|
+
},
|
|
428
|
+
Email: {
|
|
429
|
+
type: 'string',
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
436
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'users.json'), JSON.stringify(tableJson));
|
|
437
|
+
// Act
|
|
438
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
439
|
+
// Assert
|
|
440
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
441
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
442
|
+
const parsed = parseDataSourcesInfo(generatedContent);
|
|
443
|
+
// Note: when schema path doesn't contain /sql/, sanitizeName is used which converts [dbo].[Users] to _dbo___users_ (lowercase)
|
|
444
|
+
const expectedKey = '_dbo___users_';
|
|
445
|
+
expect(parsed).toHaveProperty(expectedKey);
|
|
446
|
+
const dataSource = parsed[expectedKey];
|
|
447
|
+
// Verify SQL table properties - tableId gets the cleaned name from sanitizeSqlName
|
|
448
|
+
expect(dataSource.tableId).toBe('Users');
|
|
449
|
+
expect(dataSource.version).toBe('v2');
|
|
450
|
+
expect(dataSource.primaryKey).toBe('Id');
|
|
451
|
+
expect(dataSource.dataSourceType).toBe('Connector');
|
|
452
|
+
});
|
|
453
|
+
it('should handle table without primary key', async () => {
|
|
454
|
+
// Arrange
|
|
455
|
+
const folderPath = '/test/sql/tables';
|
|
456
|
+
const tableJson = {
|
|
457
|
+
name: '[dbo].[Logs]',
|
|
458
|
+
schema: {
|
|
459
|
+
items: {
|
|
460
|
+
properties: {
|
|
461
|
+
Message: {
|
|
462
|
+
type: 'string',
|
|
463
|
+
},
|
|
464
|
+
Timestamp: {
|
|
465
|
+
type: 'string',
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
};
|
|
471
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
472
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'logs.json'), JSON.stringify(tableJson));
|
|
473
|
+
// Act
|
|
474
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
475
|
+
// Assert
|
|
476
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
477
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
478
|
+
const parsed = parseDataSourcesInfo(generatedContent);
|
|
479
|
+
// When schema path contains /sql/, sanitizeSqlName extracts just "Logs" from [dbo].[Logs], then lowercase
|
|
480
|
+
const expectedKey = 'logs';
|
|
481
|
+
expect(parsed).toHaveProperty(expectedKey);
|
|
482
|
+
const dataSource = parsed[expectedKey];
|
|
483
|
+
// Verify primary key is empty when not specified
|
|
484
|
+
expect(dataSource.tableId).toBe('Logs');
|
|
485
|
+
expect(dataSource.primaryKey).toBe('');
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
describe('Property key determination', () => {
|
|
489
|
+
it('should remove [dbo]. prefix and brackets from SQL table names', async () => {
|
|
490
|
+
// Arrange
|
|
491
|
+
const folderPath = '/test/tables';
|
|
492
|
+
const tableJson = {
|
|
493
|
+
name: '[dbo].[ComplexTableName]',
|
|
494
|
+
schema: {
|
|
495
|
+
items: {
|
|
496
|
+
properties: {},
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
501
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'table.json'), JSON.stringify(tableJson));
|
|
502
|
+
// Act
|
|
503
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
504
|
+
// Assert
|
|
505
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
506
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
507
|
+
expect(generatedContent).toContain('ComplexTableName');
|
|
508
|
+
});
|
|
509
|
+
it('should remove [custom_schema]. prefix and brackets from SQL table names', async () => {
|
|
510
|
+
// Arrange
|
|
511
|
+
const folderPath = '/test/tables';
|
|
512
|
+
const tableJson = {
|
|
513
|
+
name: '[custom_schema].[ComplexTableName]',
|
|
514
|
+
schema: {
|
|
515
|
+
items: {
|
|
516
|
+
properties: {},
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
521
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'table.json'), JSON.stringify(tableJson));
|
|
522
|
+
// Act
|
|
523
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
524
|
+
// Assert
|
|
525
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
526
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
527
|
+
expect(generatedContent).toContain('ComplexTableName');
|
|
528
|
+
});
|
|
529
|
+
it('should remove shared_ prefix from non-tabular names', async () => {
|
|
530
|
+
// Arrange
|
|
531
|
+
const folderPath = '/test/shared';
|
|
532
|
+
const sharedJson = {
|
|
533
|
+
name: 'shared_commonservice',
|
|
534
|
+
properties: {
|
|
535
|
+
displayName: 'CommonService',
|
|
536
|
+
},
|
|
537
|
+
schema: {
|
|
538
|
+
type: 'object',
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
542
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'service.json'), JSON.stringify(sharedJson));
|
|
543
|
+
// Act
|
|
544
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
545
|
+
// Assert
|
|
546
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
547
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
548
|
+
expect(generatedContent).toContain('commonservice');
|
|
549
|
+
});
|
|
550
|
+
it('should use title property when available', async () => {
|
|
551
|
+
// Arrange
|
|
552
|
+
const folderPath = '/test/tabular';
|
|
553
|
+
const tabularJson = {
|
|
554
|
+
title: 'UserManagement',
|
|
555
|
+
name: 'internal_name',
|
|
556
|
+
schema: {
|
|
557
|
+
type: 'object',
|
|
558
|
+
},
|
|
559
|
+
};
|
|
560
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
561
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'service.json'), JSON.stringify(tabularJson));
|
|
562
|
+
// Act
|
|
563
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
564
|
+
// Assert
|
|
565
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
566
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
567
|
+
expect(generatedContent).toContain('internal_name');
|
|
568
|
+
});
|
|
569
|
+
it('should fallback to filename when name property is missing', async () => {
|
|
570
|
+
// Arrange
|
|
571
|
+
const folderPath = '/test/fallback';
|
|
572
|
+
const jsonWithoutName = {
|
|
573
|
+
schema: {
|
|
574
|
+
type: 'object',
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
578
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'FallbackService.Schema.json'), JSON.stringify(jsonWithoutName));
|
|
579
|
+
// Act
|
|
580
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
581
|
+
// Assert
|
|
582
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
583
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
584
|
+
const parsed = parseDataSourcesInfo(generatedContent);
|
|
585
|
+
// When name property is missing, fallback to filename (minus .json extension, lowercased)
|
|
586
|
+
const expectedKey = 'fallbackservice.schema';
|
|
587
|
+
expect(parsed).toHaveProperty([expectedKey]);
|
|
588
|
+
});
|
|
589
|
+
it('should handle names with spaces and underscores', async () => {
|
|
590
|
+
// Arrange
|
|
591
|
+
const folderPath = '/test/complexnames';
|
|
592
|
+
const complexNameJson = {
|
|
593
|
+
name: ' Complex_Name With Spaces ',
|
|
594
|
+
title: ' Complex_Name With Spaces ',
|
|
595
|
+
schema: {
|
|
596
|
+
type: 'object',
|
|
597
|
+
},
|
|
598
|
+
};
|
|
599
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
600
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'complex.json'), JSON.stringify(complexNameJson));
|
|
601
|
+
// Act
|
|
602
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
603
|
+
// Assert
|
|
604
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
605
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
606
|
+
// Keys are now lowercase
|
|
607
|
+
expect(generatedContent).toContain('complex_namewithspaces');
|
|
608
|
+
});
|
|
609
|
+
it('should use name property for SharePoint list with underscores and hyphens', async () => {
|
|
610
|
+
// Arrange
|
|
611
|
+
const folderPath = '/test/sharepointspecialname';
|
|
612
|
+
const sharepointListJson = {
|
|
613
|
+
name: 'PaKeMpar_list with underscores-and hype-ns',
|
|
614
|
+
title: 'PaKeMpar_list with underscores-and hype-ns',
|
|
615
|
+
'x-ms-permission': 'read-write',
|
|
616
|
+
'x-ms-capabilities': {
|
|
617
|
+
sortRestrictions: {
|
|
618
|
+
sortable: true,
|
|
619
|
+
unsortableProperties: [],
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
schema: {
|
|
623
|
+
type: 'array',
|
|
624
|
+
items: {
|
|
625
|
+
type: 'object',
|
|
626
|
+
properties: {
|
|
627
|
+
ID: {
|
|
628
|
+
title: 'ID',
|
|
629
|
+
type: 'integer',
|
|
630
|
+
'x-ms-keyType': 'primary',
|
|
631
|
+
},
|
|
632
|
+
Title: {
|
|
633
|
+
title: 'Title',
|
|
634
|
+
type: 'string',
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
referencedEntities: {
|
|
640
|
+
Author: {
|
|
641
|
+
lookupEndpoint: {
|
|
642
|
+
path: '/tables/e34fc1b5-7e29-473a-9863-553afa820351/entities/Author',
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
Editor: {
|
|
646
|
+
lookupEndpoint: {
|
|
647
|
+
path: '/tables/e34fc1b5-7e29-473a-9863-553afa820351/entities/Editor',
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
};
|
|
652
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
653
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'speciallist.json'), JSON.stringify(sharepointListJson));
|
|
654
|
+
// Act
|
|
655
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
656
|
+
// Assert
|
|
657
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
658
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
659
|
+
const parsed = parseDataSourcesInfo(generatedContent);
|
|
660
|
+
// The property key should be sanitized: spaces removed, hyphens converted to underscores
|
|
661
|
+
const expectedKey = 'pakempar_listwithunderscores_andhype_ns';
|
|
662
|
+
expect(parsed).toHaveProperty(expectedKey);
|
|
663
|
+
const dataSource = parsed[expectedKey];
|
|
664
|
+
// tableId should preserve the original name from the schema
|
|
665
|
+
expect(dataSource.tableId).toBe('PaKeMpar_list with underscores-and hype-ns');
|
|
666
|
+
expect(dataSource.primaryKey).toBe('ID');
|
|
667
|
+
expect(dataSource.dataSourceType).toBe('Connector');
|
|
668
|
+
// Should have APIs generated from referencedEntities
|
|
669
|
+
expect(dataSource.apis).toHaveProperty('GetAuthor');
|
|
670
|
+
expect(dataSource.apis).toHaveProperty('GetEditor');
|
|
671
|
+
// Verify API paths have GUIDs without hyphens
|
|
672
|
+
expect(dataSource.apis.GetAuthor.path).toContain('e34fc1b57e29473a9863553afa820351');
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
describe('Merge functionality', () => {
|
|
676
|
+
it('should merge APIs when processing multiple files for same data source', async () => {
|
|
677
|
+
// Arrange
|
|
678
|
+
const folderPath = '/test/merge';
|
|
679
|
+
const api1Json = {
|
|
680
|
+
name: 'shared_testservice',
|
|
681
|
+
type: 'Microsoft.PowerApps/apis',
|
|
682
|
+
properties: {
|
|
683
|
+
displayName: 'TestService',
|
|
684
|
+
swagger: {
|
|
685
|
+
paths: {
|
|
686
|
+
'/api1': {
|
|
687
|
+
get: {
|
|
688
|
+
operationId: 'GetApi1',
|
|
689
|
+
responses: { '200': { description: 'Success' } },
|
|
690
|
+
},
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
},
|
|
695
|
+
};
|
|
696
|
+
const api2Json = {
|
|
697
|
+
name: 'shared_testservice',
|
|
698
|
+
type: 'Microsoft.PowerApps/apis',
|
|
699
|
+
properties: {
|
|
700
|
+
displayName: 'TestService',
|
|
701
|
+
swagger: {
|
|
702
|
+
paths: {
|
|
703
|
+
'/api2': {
|
|
704
|
+
post: {
|
|
705
|
+
operationId: 'PostApi2',
|
|
706
|
+
responses: { '201': { description: 'Created' } },
|
|
707
|
+
},
|
|
708
|
+
},
|
|
709
|
+
},
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
};
|
|
713
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
714
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'api1.json'), JSON.stringify(api1Json));
|
|
715
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'api2.json'), JSON.stringify(api2Json));
|
|
716
|
+
// Act
|
|
717
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
718
|
+
// Assert
|
|
719
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
720
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
721
|
+
expect(generatedContent).toContain('GetApi1');
|
|
722
|
+
expect(generatedContent).toContain('PostApi2');
|
|
723
|
+
expect(generatedContent).toContain('testservice');
|
|
724
|
+
});
|
|
725
|
+
it('should handle duplicate stored procedures gracefully', async () => {
|
|
726
|
+
// Arrange
|
|
727
|
+
const folderPath = '/test/duplicate';
|
|
728
|
+
const procedureJson = {
|
|
729
|
+
name: '[dbo].[DuplicateProc]',
|
|
730
|
+
schema: {
|
|
731
|
+
procedureresultschema: { type: 'object' },
|
|
732
|
+
},
|
|
733
|
+
};
|
|
734
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
735
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'proc1.json'), JSON.stringify(procedureJson));
|
|
736
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'proc2.json'), JSON.stringify(procedureJson));
|
|
737
|
+
// Act & Assert
|
|
738
|
+
// Note: The current implementation merges APIs instead of throwing an error
|
|
739
|
+
// This test verifies that duplicate stored procedures are handled gracefully
|
|
740
|
+
await expect(processDataSourceInfo(folderPath, mockLogger)).resolves.not.toThrow();
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
describe('Response processing', () => {
|
|
744
|
+
it('should handle array responses correctly', async () => {
|
|
745
|
+
var _a, _b, _c, _d;
|
|
746
|
+
// Arrange
|
|
747
|
+
const folderPath = '/test/responses';
|
|
748
|
+
const swaggerJson = {
|
|
749
|
+
name: 'array-api',
|
|
750
|
+
type: 'Microsoft.PowerApps/apis',
|
|
751
|
+
properties: {
|
|
752
|
+
swagger: {
|
|
753
|
+
paths: {
|
|
754
|
+
'/array-endpoint': {
|
|
755
|
+
get: {
|
|
756
|
+
operationId: 'GetArray',
|
|
757
|
+
responses: {
|
|
758
|
+
'200': {
|
|
759
|
+
description: 'Success',
|
|
760
|
+
schema: {
|
|
761
|
+
type: 'array',
|
|
762
|
+
format: 'custom',
|
|
763
|
+
},
|
|
764
|
+
},
|
|
765
|
+
},
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
};
|
|
772
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
773
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'array-response.json'), JSON.stringify(swaggerJson));
|
|
774
|
+
// Act
|
|
775
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
776
|
+
// Assert
|
|
777
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
778
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
779
|
+
const parsed = parseDataSourcesInfo(generatedContent);
|
|
780
|
+
// Verify the data source exists with sanitized name
|
|
781
|
+
expect(parsed).toHaveProperty('array_api');
|
|
782
|
+
const dataSource = parsed.array_api;
|
|
783
|
+
// Verify response info for GetArray
|
|
784
|
+
const getArrayApi = dataSource.apis.GetArray;
|
|
785
|
+
expect((_b = (_a = getArrayApi.responseInfo) === null || _a === void 0 ? void 0 : _a['200']) === null || _b === void 0 ? void 0 : _b.type).toBe('array');
|
|
786
|
+
expect((_d = (_c = getArrayApi.responseInfo) === null || _c === void 0 ? void 0 : _c['200']) === null || _d === void 0 ? void 0 : _d.format).toBe('custom');
|
|
787
|
+
});
|
|
788
|
+
it('should handle void responses when no schema provided', async () => {
|
|
789
|
+
var _a, _b;
|
|
790
|
+
// Arrange
|
|
791
|
+
const folderPath = '/test/responses';
|
|
792
|
+
const swaggerJson = {
|
|
793
|
+
name: 'void-api',
|
|
794
|
+
type: 'Microsoft.PowerApps/apis',
|
|
795
|
+
properties: {
|
|
796
|
+
swagger: {
|
|
797
|
+
paths: {
|
|
798
|
+
'/void-endpoint': {
|
|
799
|
+
delete: {
|
|
800
|
+
operationId: 'DeleteItem',
|
|
801
|
+
responses: {
|
|
802
|
+
'204': {
|
|
803
|
+
description: 'No Content',
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
},
|
|
807
|
+
},
|
|
808
|
+
},
|
|
809
|
+
},
|
|
810
|
+
},
|
|
811
|
+
};
|
|
812
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
813
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'void-response.json'), JSON.stringify(swaggerJson));
|
|
814
|
+
// Act
|
|
815
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
816
|
+
// Assert
|
|
817
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
818
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
819
|
+
const parsed = parseDataSourcesInfo(generatedContent);
|
|
820
|
+
// Verify the data source exists with sanitized name
|
|
821
|
+
expect(parsed).toHaveProperty('void_api');
|
|
822
|
+
const dataSource = parsed.void_api;
|
|
823
|
+
// Verify response info for DeleteItem is void when no schema provided
|
|
824
|
+
const deleteApi = dataSource.apis.DeleteItem;
|
|
825
|
+
expect((_b = (_a = deleteApi.responseInfo) === null || _a === void 0 ? void 0 : _a['204']) === null || _b === void 0 ? void 0 : _b.type).toBe('void');
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
describe('Error handling', () => {
|
|
829
|
+
it('should throw descriptive error for invalid JSON structure', async () => {
|
|
830
|
+
// Arrange
|
|
831
|
+
const folderPath = '/test/invalid';
|
|
832
|
+
const invalidJson = {
|
|
833
|
+
invalidStructure: true,
|
|
834
|
+
};
|
|
835
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
836
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'invalid.json'), JSON.stringify(invalidJson));
|
|
837
|
+
// Act & Assert
|
|
838
|
+
await expect(processDataSourceInfo(folderPath, mockLogger)).rejects.toThrow('The JSON does not represent a valid data source');
|
|
839
|
+
});
|
|
840
|
+
it('should handle nested directory traversal', async () => {
|
|
841
|
+
// Arrange
|
|
842
|
+
const folderPath = '/test/nested';
|
|
843
|
+
const subfolderPath = mockVfs.join(folderPath, 'subfolder');
|
|
844
|
+
const nestedJson = {
|
|
845
|
+
name: 'nested-service',
|
|
846
|
+
schema: { type: 'object' },
|
|
847
|
+
};
|
|
848
|
+
await mockVfs.mkdir(subfolderPath, { recursive: true });
|
|
849
|
+
await mockVfs.writeFile(mockVfs.join(subfolderPath, 'nested.json'), JSON.stringify(nestedJson));
|
|
850
|
+
// Act
|
|
851
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
852
|
+
// Assert - verify output was generated (means nested traversal worked)
|
|
853
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
854
|
+
const outputExists = await mockVfs.exists(outputPath);
|
|
855
|
+
expect(outputExists).toBe(true);
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
describe('Parameter sorting', () => {
|
|
859
|
+
it('should sort parameters with required first', async () => {
|
|
860
|
+
// Arrange
|
|
861
|
+
const folderPath = '/test/sorting';
|
|
862
|
+
const swaggerJson = {
|
|
863
|
+
name: 'sorting-api',
|
|
864
|
+
type: 'Microsoft.PowerApps/apis',
|
|
865
|
+
properties: {
|
|
866
|
+
swagger: {
|
|
867
|
+
paths: {
|
|
868
|
+
'/sorted-params': {
|
|
869
|
+
post: {
|
|
870
|
+
operationId: 'PostWithSortedParams',
|
|
871
|
+
parameters: [
|
|
872
|
+
{
|
|
873
|
+
name: 'optional1',
|
|
874
|
+
in: 'query',
|
|
875
|
+
required: false,
|
|
876
|
+
type: 'string',
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
name: 'required1',
|
|
880
|
+
in: 'body',
|
|
881
|
+
required: true,
|
|
882
|
+
type: 'object',
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
name: 'optional2',
|
|
886
|
+
in: 'header',
|
|
887
|
+
required: false,
|
|
888
|
+
type: 'string',
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
name: 'required2',
|
|
892
|
+
in: 'path',
|
|
893
|
+
required: true,
|
|
894
|
+
type: 'string',
|
|
895
|
+
},
|
|
896
|
+
],
|
|
897
|
+
responses: {
|
|
898
|
+
'200': { description: 'Success' },
|
|
899
|
+
},
|
|
900
|
+
},
|
|
901
|
+
},
|
|
902
|
+
},
|
|
903
|
+
},
|
|
904
|
+
},
|
|
905
|
+
};
|
|
906
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
907
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'sorted.json'), JSON.stringify(swaggerJson));
|
|
908
|
+
// Act
|
|
909
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
910
|
+
// Assert
|
|
911
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
912
|
+
const generatedContent = await mockVfs.readFile(outputPath);
|
|
913
|
+
const parsed = parseDataSourcesInfo(generatedContent);
|
|
914
|
+
// Verify data source exists with sanitized name
|
|
915
|
+
expect(parsed).toHaveProperty('sorting_api');
|
|
916
|
+
const dataSource = parsed.sorting_api;
|
|
917
|
+
const operation = dataSource.apis.PostWithSortedParams;
|
|
918
|
+
const parameters = operation.parameters;
|
|
919
|
+
// Required parameters should come first
|
|
920
|
+
expect(parameters === null || parameters === void 0 ? void 0 : parameters[0].required).toBe(true);
|
|
921
|
+
expect(parameters === null || parameters === void 0 ? void 0 : parameters[1].required).toBe(true);
|
|
922
|
+
expect(parameters === null || parameters === void 0 ? void 0 : parameters[2].required).toBe(false);
|
|
923
|
+
expect(parameters === null || parameters === void 0 ? void 0 : parameters[3].required).toBe(false);
|
|
924
|
+
});
|
|
925
|
+
});
|
|
926
|
+
describe('Output generation', () => {
|
|
927
|
+
it('should generate properly formatted TypeScript output', async () => {
|
|
928
|
+
// Arrange
|
|
929
|
+
const folderPath = '/test/output';
|
|
930
|
+
const simpleJson = {
|
|
931
|
+
name: 'test-service',
|
|
932
|
+
schema: { type: 'object' },
|
|
933
|
+
};
|
|
934
|
+
await mockVfs.mkdir(folderPath, { recursive: true });
|
|
935
|
+
await mockVfs.writeFile(mockVfs.join(folderPath, 'simple.json'), JSON.stringify(simpleJson));
|
|
936
|
+
// Act
|
|
937
|
+
await processDataSourceInfo(folderPath, mockLogger);
|
|
938
|
+
// Assert
|
|
939
|
+
const outputPath = mockVfs.join(folderPath, 'appschemas', 'dataSourcesInfo.ts');
|
|
940
|
+
const content = await mockVfs.readFile(outputPath);
|
|
941
|
+
expect(content).toContain('/*!');
|
|
942
|
+
expect(content).toContain(' * Copyright (C) Microsoft Corporation. All rights reserved.');
|
|
943
|
+
expect(content).toContain(' * This file is auto-generated. Do not modify it manually.');
|
|
944
|
+
expect(content).toContain('export const dataSourcesInfo = ');
|
|
945
|
+
expect(content).toMatch(/;\s*$/); // Should end with semicolon
|
|
946
|
+
});
|
|
947
|
+
});
|
|
948
|
+
});
|
|
54
949
|
//# sourceMappingURL=DataSourceInfoProcessor.spec.js.map
|