norn-cli 1.4.0 → 1.4.2
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/.norn-cache/swagger-body-intellisense.json +1 -1
- package/CHANGELOG.md +16 -0
- package/out/chatParticipant.js +722 -0
- package/out/cli.js +99 -36
- package/out/codeLensProvider.js +14 -20
- package/out/completionProvider.js +543 -25
- package/out/coverageCalculator.js +250 -169
- package/out/coveragePanel.js +7 -4
- package/out/diagnosticProvider.js +135 -2
- package/out/environmentProvider.js +96 -27
- package/out/extension.js +98 -9
- package/out/nornPrompt.js +580 -0
- package/out/swaggerBodyIntellisenseCache.js +147 -0
- package/out/swaggerParser.js +154 -74
- package/out/test/coverageCalculator.test.js +100 -0
- package/out/testProvider.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Swagger Body IntelliSense Cache
|
|
4
|
+
*
|
|
5
|
+
* Persists request-body schemas extracted from Swagger/OpenAPI specs.
|
|
6
|
+
* The cache is used by completionProvider to provide endpoint request-body
|
|
7
|
+
* templates and inline key suggestions without fetching remote specs.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.loadSwaggerBodyCache = loadSwaggerBodyCache;
|
|
44
|
+
exports.getCachedRequestBodySchemasForUrl = getCachedRequestBodySchemasForUrl;
|
|
45
|
+
exports.saveRequestBodySchemasForUrl = saveRequestBodySchemasForUrl;
|
|
46
|
+
exports.invalidateSwaggerBodyCache = invalidateSwaggerBodyCache;
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const vscode = __importStar(require("vscode"));
|
|
50
|
+
const CACHE_VERSION = 1;
|
|
51
|
+
const CACHE_FOLDER = '.norn-cache';
|
|
52
|
+
const CACHE_FILE = 'swagger-body-intellisense.json';
|
|
53
|
+
function getWorkspaceRoot() {
|
|
54
|
+
const workspaceFolders = vscode.workspace.workspaceFolders;
|
|
55
|
+
if (workspaceFolders && workspaceFolders.length > 0) {
|
|
56
|
+
return workspaceFolders[0].uri.fsPath;
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
function getCachePath() {
|
|
61
|
+
const root = getWorkspaceRoot();
|
|
62
|
+
if (!root) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return path.join(root, CACHE_FOLDER, CACHE_FILE);
|
|
66
|
+
}
|
|
67
|
+
function ensureCacheDir() {
|
|
68
|
+
const root = getWorkspaceRoot();
|
|
69
|
+
if (!root) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const cacheDir = path.join(root, CACHE_FOLDER);
|
|
73
|
+
if (!fs.existsSync(cacheDir)) {
|
|
74
|
+
try {
|
|
75
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
function loadSwaggerBodyCache() {
|
|
84
|
+
const cachePath = getCachePath();
|
|
85
|
+
if (!cachePath || !fs.existsSync(cachePath)) {
|
|
86
|
+
return { version: CACHE_VERSION, urls: {} };
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const content = fs.readFileSync(cachePath, 'utf-8');
|
|
90
|
+
const parsed = JSON.parse(content);
|
|
91
|
+
if (parsed.version !== CACHE_VERSION || !parsed.urls || typeof parsed.urls !== 'object') {
|
|
92
|
+
return { version: CACHE_VERSION, urls: {} };
|
|
93
|
+
}
|
|
94
|
+
return parsed;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return { version: CACHE_VERSION, urls: {} };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function saveSwaggerBodyCache(cache) {
|
|
101
|
+
if (!ensureCacheDir()) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const cachePath = getCachePath();
|
|
105
|
+
if (!cachePath) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
fs.writeFileSync(cachePath, JSON.stringify(cache, null, 2), 'utf-8');
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function getCachedRequestBodySchemasForUrl(url) {
|
|
117
|
+
const cache = loadSwaggerBodyCache();
|
|
118
|
+
return cache.urls[url];
|
|
119
|
+
}
|
|
120
|
+
function saveRequestBodySchemasForUrl(url, baseUrl, schemas) {
|
|
121
|
+
const cache = loadSwaggerBodyCache();
|
|
122
|
+
cache.urls[url] = {
|
|
123
|
+
baseUrl,
|
|
124
|
+
fetchedAt: new Date().toISOString(),
|
|
125
|
+
schemas: schemas.map(schema => ({
|
|
126
|
+
operationId: schema.operationId,
|
|
127
|
+
method: schema.method.toUpperCase(),
|
|
128
|
+
path: schema.path,
|
|
129
|
+
required: Array.isArray(schema.required) ? schema.required : [],
|
|
130
|
+
schema: schema.schema
|
|
131
|
+
}))
|
|
132
|
+
};
|
|
133
|
+
saveSwaggerBodyCache(cache);
|
|
134
|
+
}
|
|
135
|
+
function invalidateSwaggerBodyCache(url) {
|
|
136
|
+
if (url) {
|
|
137
|
+
const cache = loadSwaggerBodyCache();
|
|
138
|
+
delete cache.urls[url];
|
|
139
|
+
saveSwaggerBodyCache(cache);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
saveSwaggerBodyCache({
|
|
143
|
+
version: CACHE_VERSION,
|
|
144
|
+
urls: {}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=swaggerBodyIntellisenseCache.js.map
|
package/out/swaggerParser.js
CHANGED
|
@@ -9,12 +9,98 @@ exports.invalidateSwaggerCache = invalidateSwaggerCache;
|
|
|
9
9
|
exports.parseSwaggerSpec = parseSwaggerSpec;
|
|
10
10
|
exports.generateNornapiContent = generateNornapiContent;
|
|
11
11
|
exports.extractResponseSchemas = extractResponseSchemas;
|
|
12
|
+
exports.extractRequestBodySchemas = extractRequestBodySchemas;
|
|
12
13
|
exports.generateSchemaFilename = generateSchemaFilename;
|
|
13
14
|
const axios_1 = __importDefault(require("axios"));
|
|
14
15
|
/**
|
|
15
16
|
* Cache for parsed swagger specs (cleared when extension deactivates)
|
|
16
17
|
*/
|
|
17
18
|
const swaggerCache = new Map();
|
|
19
|
+
const rawSwaggerSpecCache = new Map();
|
|
20
|
+
async function getRawSwaggerSpec(url) {
|
|
21
|
+
if (rawSwaggerSpecCache.has(url)) {
|
|
22
|
+
return rawSwaggerSpecCache.get(url);
|
|
23
|
+
}
|
|
24
|
+
const response = await axios_1.default.get(url, {
|
|
25
|
+
headers: {
|
|
26
|
+
'Accept': 'application/json'
|
|
27
|
+
},
|
|
28
|
+
timeout: 30000
|
|
29
|
+
});
|
|
30
|
+
rawSwaggerSpecCache.set(url, response.data);
|
|
31
|
+
return response.data;
|
|
32
|
+
}
|
|
33
|
+
function isOpenApi3Spec(spec) {
|
|
34
|
+
return typeof spec?.openapi === 'string' && spec.openapi.startsWith('3.');
|
|
35
|
+
}
|
|
36
|
+
function resolveRef(ref, root) {
|
|
37
|
+
if (!ref.startsWith('#/')) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const parts = ref.slice(2).split('/');
|
|
41
|
+
let current = root;
|
|
42
|
+
for (const part of parts) {
|
|
43
|
+
if (current === undefined) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
current = current[part];
|
|
47
|
+
}
|
|
48
|
+
return current;
|
|
49
|
+
}
|
|
50
|
+
function resolveSchemaRefs(schema, root, visited = new Set()) {
|
|
51
|
+
if (!schema || typeof schema !== 'object') {
|
|
52
|
+
return schema;
|
|
53
|
+
}
|
|
54
|
+
if (schema.$ref && typeof schema.$ref === 'string') {
|
|
55
|
+
if (visited.has(schema.$ref)) {
|
|
56
|
+
return { type: 'object' };
|
|
57
|
+
}
|
|
58
|
+
visited.add(schema.$ref);
|
|
59
|
+
const resolved = resolveRef(schema.$ref, root);
|
|
60
|
+
if (resolved) {
|
|
61
|
+
return resolveSchemaRefs(resolved, root, visited);
|
|
62
|
+
}
|
|
63
|
+
return schema;
|
|
64
|
+
}
|
|
65
|
+
const result = Array.isArray(schema) ? [] : {};
|
|
66
|
+
for (const key of Object.keys(schema)) {
|
|
67
|
+
if (key === '$ref') {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const value = schema[key];
|
|
71
|
+
if (typeof value === 'object' && value !== null) {
|
|
72
|
+
result[key] = resolveSchemaRefs(value, root, new Set(visited));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
result[key] = value;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
function getPreferredJsonSchema(content) {
|
|
81
|
+
if (!content) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
if (content['application/json']?.schema) {
|
|
85
|
+
return content['application/json'].schema;
|
|
86
|
+
}
|
|
87
|
+
const jsonKey = Object.keys(content).find(key => key.toLowerCase().includes('json') && content[key]?.schema);
|
|
88
|
+
if (jsonKey) {
|
|
89
|
+
return content[jsonKey].schema;
|
|
90
|
+
}
|
|
91
|
+
if (content['*/*']?.schema) {
|
|
92
|
+
return content['*/*'].schema;
|
|
93
|
+
}
|
|
94
|
+
const firstSchemaKey = Object.keys(content).find(key => content[key]?.schema);
|
|
95
|
+
return firstSchemaKey ? content[firstSchemaKey].schema : undefined;
|
|
96
|
+
}
|
|
97
|
+
function addJsonSchemaMeta(schema, title) {
|
|
98
|
+
return {
|
|
99
|
+
'$schema': 'http://json-schema.org/draft-07/schema#',
|
|
100
|
+
title,
|
|
101
|
+
...schema
|
|
102
|
+
};
|
|
103
|
+
}
|
|
18
104
|
/**
|
|
19
105
|
* Get a cached swagger spec, or fetch and parse if not cached
|
|
20
106
|
*/
|
|
@@ -31,6 +117,7 @@ async function getCachedSwaggerSpec(url) {
|
|
|
31
117
|
*/
|
|
32
118
|
function clearSwaggerCache() {
|
|
33
119
|
swaggerCache.clear();
|
|
120
|
+
rawSwaggerSpecCache.clear();
|
|
34
121
|
}
|
|
35
122
|
/**
|
|
36
123
|
* Invalidate a specific URL from the cache (for manual refresh)
|
|
@@ -38,25 +125,20 @@ function clearSwaggerCache() {
|
|
|
38
125
|
function invalidateSwaggerCache(url) {
|
|
39
126
|
if (url) {
|
|
40
127
|
swaggerCache.delete(url);
|
|
128
|
+
rawSwaggerSpecCache.delete(url);
|
|
41
129
|
}
|
|
42
130
|
else {
|
|
43
131
|
swaggerCache.clear();
|
|
132
|
+
rawSwaggerSpecCache.clear();
|
|
44
133
|
}
|
|
45
134
|
}
|
|
46
135
|
/**
|
|
47
136
|
* Fetches and parses an OpenAPI/Swagger specification from a URL.
|
|
48
137
|
*/
|
|
49
138
|
async function parseSwaggerSpec(url) {
|
|
50
|
-
|
|
51
|
-
const response = await axios_1.default.get(url, {
|
|
52
|
-
headers: {
|
|
53
|
-
'Accept': 'application/json'
|
|
54
|
-
},
|
|
55
|
-
timeout: 30000
|
|
56
|
-
});
|
|
57
|
-
const spec = response.data;
|
|
139
|
+
const spec = await getRawSwaggerSpec(url);
|
|
58
140
|
// Determine if it's OpenAPI 3.x or Swagger 2.0
|
|
59
|
-
const isOpenAPI3 = spec
|
|
141
|
+
const isOpenAPI3 = isOpenApi3Spec(spec);
|
|
60
142
|
// Extract base URL
|
|
61
143
|
let baseUrl = '';
|
|
62
144
|
if (isOpenAPI3) {
|
|
@@ -220,61 +302,9 @@ function generateNornapiContent(spec, selectedSections, customBaseUrl) {
|
|
|
220
302
|
* @returns Array of response schemas
|
|
221
303
|
*/
|
|
222
304
|
async function extractResponseSchemas(url) {
|
|
223
|
-
const
|
|
224
|
-
headers: { 'Accept': 'application/json' },
|
|
225
|
-
timeout: 30000
|
|
226
|
-
});
|
|
227
|
-
const spec = response.data;
|
|
305
|
+
const spec = await getRawSwaggerSpec(url);
|
|
228
306
|
const schemas = [];
|
|
229
|
-
const isOpenAPI3 = spec
|
|
230
|
-
// Helper to resolve $ref references
|
|
231
|
-
const resolveRef = (ref, root) => {
|
|
232
|
-
// $ref format: "#/components/schemas/Pet" or "#/definitions/Pet"
|
|
233
|
-
if (!ref.startsWith('#/')) {
|
|
234
|
-
return undefined;
|
|
235
|
-
}
|
|
236
|
-
const parts = ref.slice(2).split('/');
|
|
237
|
-
let current = root;
|
|
238
|
-
for (const part of parts) {
|
|
239
|
-
if (current === undefined)
|
|
240
|
-
return undefined;
|
|
241
|
-
current = current[part];
|
|
242
|
-
}
|
|
243
|
-
return current;
|
|
244
|
-
};
|
|
245
|
-
// Recursively resolve all $ref in a schema
|
|
246
|
-
const resolveSchemaRefs = (schema, root, visited = new Set()) => {
|
|
247
|
-
if (!schema || typeof schema !== 'object') {
|
|
248
|
-
return schema;
|
|
249
|
-
}
|
|
250
|
-
// Handle $ref
|
|
251
|
-
if (schema.$ref && typeof schema.$ref === 'string') {
|
|
252
|
-
if (visited.has(schema.$ref)) {
|
|
253
|
-
// Circular reference - return simple object type
|
|
254
|
-
return { type: 'object' };
|
|
255
|
-
}
|
|
256
|
-
visited.add(schema.$ref);
|
|
257
|
-
const resolved = resolveRef(schema.$ref, root);
|
|
258
|
-
if (resolved) {
|
|
259
|
-
return resolveSchemaRefs(resolved, root, visited);
|
|
260
|
-
}
|
|
261
|
-
return schema;
|
|
262
|
-
}
|
|
263
|
-
// Recursively resolve properties, items, etc.
|
|
264
|
-
const result = Array.isArray(schema) ? [] : {};
|
|
265
|
-
for (const key of Object.keys(schema)) {
|
|
266
|
-
if (key === '$ref')
|
|
267
|
-
continue;
|
|
268
|
-
const value = schema[key];
|
|
269
|
-
if (typeof value === 'object' && value !== null) {
|
|
270
|
-
result[key] = resolveSchemaRefs(value, root, new Set(visited));
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
result[key] = value;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return result;
|
|
277
|
-
};
|
|
307
|
+
const isOpenAPI3 = isOpenApi3Spec(spec);
|
|
278
308
|
// Parse paths
|
|
279
309
|
const paths = spec.paths || {};
|
|
280
310
|
for (const [pathStr, pathItem] of Object.entries(paths)) {
|
|
@@ -295,12 +325,7 @@ async function extractResponseSchemas(url) {
|
|
|
295
325
|
if (isOpenAPI3) {
|
|
296
326
|
// OpenAPI 3.x: responses[code].content["application/json"].schema
|
|
297
327
|
const content = responseObj.content;
|
|
298
|
-
|
|
299
|
-
const jsonContent = content['application/json'] || content['*/*'];
|
|
300
|
-
if (jsonContent?.schema) {
|
|
301
|
-
schema = jsonContent.schema;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
328
|
+
schema = getPreferredJsonSchema(content);
|
|
304
329
|
}
|
|
305
330
|
else {
|
|
306
331
|
// Swagger 2.0: responses[code].schema
|
|
@@ -312,11 +337,7 @@ async function extractResponseSchemas(url) {
|
|
|
312
337
|
// Resolve all $ref references
|
|
313
338
|
const resolvedSchema = resolveSchemaRefs(schema, spec);
|
|
314
339
|
// Add JSON Schema meta properties
|
|
315
|
-
const jsonSchema = {
|
|
316
|
-
'$schema': 'http://json-schema.org/draft-07/schema#',
|
|
317
|
-
title: `${sanitizeOperationId(operationId)} ${statusCode} Response`,
|
|
318
|
-
...resolvedSchema
|
|
319
|
-
};
|
|
340
|
+
const jsonSchema = addJsonSchemaMeta(resolvedSchema, `${sanitizeOperationId(operationId)} ${statusCode} Response`);
|
|
320
341
|
schemas.push({
|
|
321
342
|
operationId: sanitizeOperationId(operationId),
|
|
322
343
|
method: method.toUpperCase(),
|
|
@@ -330,6 +351,65 @@ async function extractResponseSchemas(url) {
|
|
|
330
351
|
}
|
|
331
352
|
return schemas;
|
|
332
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Extract request body schemas from an OpenAPI/Swagger spec
|
|
356
|
+
* Resolves $ref references to inline schemas
|
|
357
|
+
* @param url URL of the OpenAPI spec
|
|
358
|
+
* @returns Array of request body schemas
|
|
359
|
+
*/
|
|
360
|
+
async function extractRequestBodySchemas(url) {
|
|
361
|
+
const spec = await getRawSwaggerSpec(url);
|
|
362
|
+
const schemas = [];
|
|
363
|
+
const isOpenAPI3 = isOpenApi3Spec(spec);
|
|
364
|
+
const paths = spec.paths || {};
|
|
365
|
+
for (const [pathStr, pathItem] of Object.entries(paths)) {
|
|
366
|
+
const pathObj = pathItem;
|
|
367
|
+
for (const method of ['get', 'post', 'put', 'delete', 'patch', 'head', 'options']) {
|
|
368
|
+
const operation = pathObj[method];
|
|
369
|
+
if (!operation) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
let schema = null;
|
|
373
|
+
if (isOpenAPI3) {
|
|
374
|
+
// OpenAPI 3.x request body:
|
|
375
|
+
// operation.requestBody.content["application/json"].schema
|
|
376
|
+
let requestBody = operation.requestBody;
|
|
377
|
+
if (requestBody && typeof requestBody.$ref === 'string') {
|
|
378
|
+
requestBody = resolveRef(requestBody.$ref, spec);
|
|
379
|
+
}
|
|
380
|
+
const content = requestBody?.content;
|
|
381
|
+
schema = getPreferredJsonSchema(content);
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
// Swagger 2.0 request body:
|
|
385
|
+
// operation.parameters[] with in: "body"
|
|
386
|
+
const pathParameters = Array.isArray(pathObj.parameters) ? pathObj.parameters : [];
|
|
387
|
+
const operationParameters = Array.isArray(operation.parameters) ? operation.parameters : [];
|
|
388
|
+
const allParameters = [...pathParameters, ...operationParameters];
|
|
389
|
+
const bodyParameter = allParameters.find((param) => param?.in === 'body' && param?.schema);
|
|
390
|
+
if (bodyParameter?.schema) {
|
|
391
|
+
schema = bodyParameter.schema;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (!schema) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
const operationId = sanitizeOperationId(operation.operationId || generateOperationId(method, pathStr));
|
|
398
|
+
const resolvedSchema = resolveSchemaRefs(schema, spec);
|
|
399
|
+
const required = Array.isArray(resolvedSchema?.required)
|
|
400
|
+
? resolvedSchema.required.filter((name) => typeof name === 'string')
|
|
401
|
+
: [];
|
|
402
|
+
schemas.push({
|
|
403
|
+
operationId,
|
|
404
|
+
method: method.toUpperCase(),
|
|
405
|
+
path: pathStr,
|
|
406
|
+
required,
|
|
407
|
+
schema: addJsonSchemaMeta(resolvedSchema, `${operationId} Request`)
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return schemas;
|
|
412
|
+
}
|
|
333
413
|
/**
|
|
334
414
|
* Generate schema filename from operation details
|
|
335
415
|
*/
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const assert = __importStar(require("assert"));
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const vscode = __importStar(require("vscode"));
|
|
40
|
+
const coverageCalculator_1 = require("../coverageCalculator");
|
|
41
|
+
const swaggerParser = __importStar(require("../swaggerParser"));
|
|
42
|
+
suite('Coverage Calculator Regression', () => {
|
|
43
|
+
test('maps {{baseUrl}}/{{version}} endpoint paths to swagger paths', async () => {
|
|
44
|
+
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
|
|
45
|
+
assert.ok(workspaceRoot, 'Workspace root is required for this test');
|
|
46
|
+
const tempRoot = fs.mkdtempSync(path.join(workspaceRoot, '.coverage-template-regression-'));
|
|
47
|
+
const nornapiPath = path.join(tempRoot, 'pet.nornapi');
|
|
48
|
+
const nornPath = path.join(tempRoot, 'pet.norn');
|
|
49
|
+
const nornapiContent = `swagger "https://petstore.swagger.io/v2/swagger.json"
|
|
50
|
+
|
|
51
|
+
endpoints
|
|
52
|
+
GetPetById: GET {{baseUrl}}/{{version}}/pet/{petId}
|
|
53
|
+
end endpoints
|
|
54
|
+
`;
|
|
55
|
+
const nornContent = `test sequence GetPet
|
|
56
|
+
GET GetPetById(1)
|
|
57
|
+
assert $1.status == 200
|
|
58
|
+
end sequence
|
|
59
|
+
`;
|
|
60
|
+
fs.writeFileSync(nornapiPath, nornapiContent, 'utf-8');
|
|
61
|
+
fs.writeFileSync(nornPath, nornContent, 'utf-8');
|
|
62
|
+
const originalGetCachedSwaggerSpec = swaggerParser.getCachedSwaggerSpec;
|
|
63
|
+
try {
|
|
64
|
+
swaggerParser.getCachedSwaggerSpec = async () => ({
|
|
65
|
+
title: 'Petstore',
|
|
66
|
+
version: '1.0.0',
|
|
67
|
+
baseUrl: 'https://petstore.swagger.io/v2',
|
|
68
|
+
sections: [
|
|
69
|
+
{
|
|
70
|
+
name: 'pet',
|
|
71
|
+
endpoints: [
|
|
72
|
+
{
|
|
73
|
+
name: 'getPetById',
|
|
74
|
+
method: 'GET',
|
|
75
|
+
path: '/pet/{petId}',
|
|
76
|
+
parameters: ['petId'],
|
|
77
|
+
tag: 'pet',
|
|
78
|
+
responseCodes: ['200', '404']
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
});
|
|
84
|
+
(0, coverageCalculator_1.clearCoverageCache)();
|
|
85
|
+
const coverage = await (0, coverageCalculator_1.getCoverageForNornapiFile)(nornapiPath);
|
|
86
|
+
assert.strictEqual(coverage.hasSwagger, true);
|
|
87
|
+
assert.strictEqual(coverage.total, 2);
|
|
88
|
+
assert.strictEqual(coverage.covered, 1);
|
|
89
|
+
const endpoint = coverage.specs[0]?.endpoints.find(e => e.method === 'GET' && e.path === '/pet/{petId}');
|
|
90
|
+
assert.ok(endpoint, 'Expected /pet/{petId} endpoint to be present');
|
|
91
|
+
const status200 = endpoint.responseCodes.find(code => code.code === '200');
|
|
92
|
+
assert.ok(status200?.covered, 'Expected 200 status code to be covered');
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
swaggerParser.getCachedSwaggerSpec = originalGetCachedSwaggerSpec;
|
|
96
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=coverageCalculator.test.js.map
|
package/out/testProvider.js
CHANGED
|
@@ -395,7 +395,7 @@ class NornTestController {
|
|
|
395
395
|
const content = await fs.readFile(data.uri.fsPath, 'utf-8');
|
|
396
396
|
const fileVariables = (0, parser_1.extractVariables)(content);
|
|
397
397
|
// Get environment variables
|
|
398
|
-
const envVars = (0, environmentProvider_1.getEnvironmentVariables)();
|
|
398
|
+
const envVars = (0, environmentProvider_1.getEnvironmentVariables)(data.uri.fsPath);
|
|
399
399
|
// Merge variables (env vars take precedence)
|
|
400
400
|
const mergedVariables = { ...fileVariables, ...envVars };
|
|
401
401
|
// If this is a theory case, add case params
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "norn-cli",
|
|
3
3
|
"displayName": "Norn - REST Client",
|
|
4
4
|
"description": "A powerful REST client for making HTTP requests with sequences, variables, scripts, and cookie support",
|
|
5
|
-
"version": "1.4.
|
|
5
|
+
"version": "1.4.2",
|
|
6
6
|
"publisher": "Norn-PeterKrustanov",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Peter Krastanov"
|