norn-cli 1.6.0 → 1.6.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/AGENTS.md +9 -1
- package/CHANGELOG.md +23 -0
- package/dist/cli.js +246 -80
- package/package.json +1 -1
- package/out/assertionRunner.js +0 -537
- package/out/chatParticipant.js +0 -722
- package/out/cli/colors.js +0 -129
- package/out/cli/formatters/assertion.js +0 -75
- package/out/cli/formatters/index.js +0 -23
- package/out/cli/formatters/response.js +0 -106
- package/out/cli/formatters/summary.js +0 -187
- package/out/cli/redaction.js +0 -237
- package/out/cli/reporters/html.js +0 -634
- package/out/cli/reporters/index.js +0 -22
- package/out/cli/reporters/junit.js +0 -211
- package/out/cli.js +0 -989
- package/out/codeLensProvider.js +0 -248
- package/out/compareContentProvider.js +0 -85
- package/out/completionProvider.js +0 -2404
- package/out/contractDecorationProvider.js +0 -243
- package/out/coverageCalculator.js +0 -837
- package/out/coveragePanel.js +0 -545
- package/out/diagnosticProvider.js +0 -1113
- package/out/environmentProvider.js +0 -442
- package/out/extension.js +0 -1114
- package/out/httpClient.js +0 -269
- package/out/jsonFileReader.js +0 -320
- package/out/nornPrompt.js +0 -580
- package/out/nornapiParser.js +0 -326
- package/out/parser.js +0 -725
- package/out/responsePanel.js +0 -4674
- package/out/schemaGenerator.js +0 -393
- package/out/scriptRunner.js +0 -419
- package/out/sequenceRunner.js +0 -3046
- package/out/swaggerBodyIntellisenseCache.js +0 -147
- package/out/swaggerParser.js +0 -419
- package/out/test/coverageCalculator.test.js +0 -100
- package/out/test/extension.test.js +0 -48
- package/out/testProvider.js +0 -658
- package/out/validationCache.js +0 -245
|
@@ -1,147 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getCachedSwaggerSpec = getCachedSwaggerSpec;
|
|
7
|
-
exports.clearSwaggerCache = clearSwaggerCache;
|
|
8
|
-
exports.invalidateSwaggerCache = invalidateSwaggerCache;
|
|
9
|
-
exports.parseSwaggerSpec = parseSwaggerSpec;
|
|
10
|
-
exports.generateNornapiContent = generateNornapiContent;
|
|
11
|
-
exports.extractResponseSchemas = extractResponseSchemas;
|
|
12
|
-
exports.extractRequestBodySchemas = extractRequestBodySchemas;
|
|
13
|
-
exports.generateSchemaFilename = generateSchemaFilename;
|
|
14
|
-
const axios_1 = __importDefault(require("axios"));
|
|
15
|
-
/**
|
|
16
|
-
* Cache for parsed swagger specs (cleared when extension deactivates)
|
|
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
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Get a cached swagger spec, or fetch and parse if not cached
|
|
106
|
-
*/
|
|
107
|
-
async function getCachedSwaggerSpec(url) {
|
|
108
|
-
if (swaggerCache.has(url)) {
|
|
109
|
-
return swaggerCache.get(url);
|
|
110
|
-
}
|
|
111
|
-
const spec = await parseSwaggerSpec(url);
|
|
112
|
-
swaggerCache.set(url, spec);
|
|
113
|
-
return spec;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Clear the swagger cache (call on extension deactivation)
|
|
117
|
-
*/
|
|
118
|
-
function clearSwaggerCache() {
|
|
119
|
-
swaggerCache.clear();
|
|
120
|
-
rawSwaggerSpecCache.clear();
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Invalidate a specific URL from the cache (for manual refresh)
|
|
124
|
-
*/
|
|
125
|
-
function invalidateSwaggerCache(url) {
|
|
126
|
-
if (url) {
|
|
127
|
-
swaggerCache.delete(url);
|
|
128
|
-
rawSwaggerSpecCache.delete(url);
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
swaggerCache.clear();
|
|
132
|
-
rawSwaggerSpecCache.clear();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Fetches and parses an OpenAPI/Swagger specification from a URL.
|
|
137
|
-
*/
|
|
138
|
-
async function parseSwaggerSpec(url) {
|
|
139
|
-
const spec = await getRawSwaggerSpec(url);
|
|
140
|
-
// Determine if it's OpenAPI 3.x or Swagger 2.0
|
|
141
|
-
const isOpenAPI3 = isOpenApi3Spec(spec);
|
|
142
|
-
// Extract base URL
|
|
143
|
-
let baseUrl = '';
|
|
144
|
-
if (isOpenAPI3) {
|
|
145
|
-
// OpenAPI 3.x uses servers array
|
|
146
|
-
if (spec.servers && spec.servers.length > 0) {
|
|
147
|
-
baseUrl = spec.servers[0].url;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
// Swagger 2.0 uses host + basePath + schemes
|
|
152
|
-
const scheme = spec.schemes?.[0] || 'https';
|
|
153
|
-
const host = spec.host || '';
|
|
154
|
-
const basePath = spec.basePath || '';
|
|
155
|
-
if (host) {
|
|
156
|
-
baseUrl = `${scheme}://${host}${basePath}`;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
// Extract title and version
|
|
160
|
-
const title = spec.info?.title || 'API';
|
|
161
|
-
const version = spec.info?.version || '1.0.0';
|
|
162
|
-
// Group endpoints by tag
|
|
163
|
-
const sectionMap = new Map();
|
|
164
|
-
// Parse paths
|
|
165
|
-
const paths = spec.paths || {};
|
|
166
|
-
for (const [path, pathItem] of Object.entries(paths)) {
|
|
167
|
-
const pathObj = pathItem;
|
|
168
|
-
for (const method of ['get', 'post', 'put', 'delete', 'patch', 'head', 'options']) {
|
|
169
|
-
const operation = pathObj[method];
|
|
170
|
-
if (!operation)
|
|
171
|
-
continue;
|
|
172
|
-
// Get operation details
|
|
173
|
-
const operationId = operation.operationId || generateOperationId(method, path);
|
|
174
|
-
const tags = operation.tags || ['default'];
|
|
175
|
-
const summary = operation.summary;
|
|
176
|
-
// Extract path parameters
|
|
177
|
-
const parameters = [];
|
|
178
|
-
const pathParams = path.match(/\{([^}]+)\}/g);
|
|
179
|
-
if (pathParams) {
|
|
180
|
-
for (const param of pathParams) {
|
|
181
|
-
parameters.push(param.replace(/[{}]/g, ''));
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// Also check query parameters that are required
|
|
185
|
-
const paramDefs = operation.parameters;
|
|
186
|
-
if (paramDefs) {
|
|
187
|
-
for (const param of paramDefs) {
|
|
188
|
-
if (param.in === 'query' && param.required) {
|
|
189
|
-
parameters.push(param.name);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
// Extract response codes from operation.responses
|
|
194
|
-
const responseCodes = [];
|
|
195
|
-
const responses = operation.responses;
|
|
196
|
-
if (responses) {
|
|
197
|
-
for (const code of Object.keys(responses)) {
|
|
198
|
-
// Include numeric status codes (skip 'default')
|
|
199
|
-
if (/^\d{3}$/.test(code)) {
|
|
200
|
-
responseCodes.push(code);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
// Sort numerically
|
|
204
|
-
responseCodes.sort((a, b) => parseInt(a) - parseInt(b));
|
|
205
|
-
}
|
|
206
|
-
const endpoint = {
|
|
207
|
-
name: sanitizeOperationId(operationId),
|
|
208
|
-
method: method.toUpperCase(),
|
|
209
|
-
path,
|
|
210
|
-
parameters,
|
|
211
|
-
tag: tags[0] || 'default',
|
|
212
|
-
summary,
|
|
213
|
-
responseCodes: responseCodes.length > 0 ? responseCodes : ['200'] // Default to 200 if no responses defined
|
|
214
|
-
};
|
|
215
|
-
// Add to section
|
|
216
|
-
const tag = endpoint.tag;
|
|
217
|
-
if (!sectionMap.has(tag)) {
|
|
218
|
-
sectionMap.set(tag, []);
|
|
219
|
-
}
|
|
220
|
-
sectionMap.get(tag).push(endpoint);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
// Convert to sections array
|
|
224
|
-
const sections = [];
|
|
225
|
-
// Get tag descriptions if available
|
|
226
|
-
const tagDescriptions = new Map();
|
|
227
|
-
if (spec.tags) {
|
|
228
|
-
for (const tag of spec.tags) {
|
|
229
|
-
if (tag.description) {
|
|
230
|
-
tagDescriptions.set(tag.name, tag.description);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
for (const [name, endpoints] of sectionMap) {
|
|
235
|
-
sections.push({
|
|
236
|
-
name,
|
|
237
|
-
description: tagDescriptions.get(name),
|
|
238
|
-
endpoints
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
// Sort sections alphabetically
|
|
242
|
-
sections.sort((a, b) => a.name.localeCompare(b.name));
|
|
243
|
-
return {
|
|
244
|
-
title,
|
|
245
|
-
version,
|
|
246
|
-
baseUrl,
|
|
247
|
-
sections
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Generate an operation ID from method and path if not provided
|
|
252
|
-
*/
|
|
253
|
-
function generateOperationId(method, path) {
|
|
254
|
-
// Convert /pet/{petId} to getPetByPetId
|
|
255
|
-
const parts = path.split('/').filter(p => p && !p.startsWith('{'));
|
|
256
|
-
const pathName = parts.map(p => p.charAt(0).toUpperCase() + p.slice(1)).join('');
|
|
257
|
-
return method.toLowerCase() + pathName;
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Sanitize operation ID to be a valid identifier
|
|
261
|
-
*/
|
|
262
|
-
function sanitizeOperationId(operationId) {
|
|
263
|
-
// Remove invalid characters and ensure it starts with a letter
|
|
264
|
-
let sanitized = operationId
|
|
265
|
-
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
266
|
-
.replace(/_+/g, '_')
|
|
267
|
-
.replace(/^_+|_+$/g, '');
|
|
268
|
-
// Ensure it starts with a capital letter
|
|
269
|
-
if (sanitized && /^[a-z]/.test(sanitized)) {
|
|
270
|
-
sanitized = sanitized.charAt(0).toUpperCase() + sanitized.slice(1);
|
|
271
|
-
}
|
|
272
|
-
return sanitized || 'Endpoint';
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Generate .nornapi content for selected sections
|
|
276
|
-
*/
|
|
277
|
-
function generateNornapiContent(spec, selectedSections, customBaseUrl) {
|
|
278
|
-
const lines = [];
|
|
279
|
-
const baseUrl = customBaseUrl || spec.baseUrl;
|
|
280
|
-
// Add endpoints section
|
|
281
|
-
lines.push('endpoints');
|
|
282
|
-
const sectionsToInclude = selectedSections.includes('All')
|
|
283
|
-
? spec.sections
|
|
284
|
-
: spec.sections.filter(s => selectedSections.includes(s.name));
|
|
285
|
-
for (const section of sectionsToInclude) {
|
|
286
|
-
// Add section comment
|
|
287
|
-
lines.push(` # ${section.name}${section.description ? ` - ${section.description}` : ''}`);
|
|
288
|
-
for (const endpoint of section.endpoints) {
|
|
289
|
-
// Build the endpoint line with explicit method for full parser/editor support
|
|
290
|
-
const path = endpoint.path;
|
|
291
|
-
lines.push(` ${endpoint.name}: ${endpoint.method} ${baseUrl}${path}`);
|
|
292
|
-
}
|
|
293
|
-
lines.push('');
|
|
294
|
-
}
|
|
295
|
-
lines.push('end endpoints');
|
|
296
|
-
return lines.join('\n');
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Extract response schemas from an OpenAPI/Swagger spec
|
|
300
|
-
* Resolves $ref references to inline schemas
|
|
301
|
-
* @param url URL of the OpenAPI spec
|
|
302
|
-
* @returns Array of response schemas
|
|
303
|
-
*/
|
|
304
|
-
async function extractResponseSchemas(url) {
|
|
305
|
-
const spec = await getRawSwaggerSpec(url);
|
|
306
|
-
const schemas = [];
|
|
307
|
-
const isOpenAPI3 = isOpenApi3Spec(spec);
|
|
308
|
-
// Parse paths
|
|
309
|
-
const paths = spec.paths || {};
|
|
310
|
-
for (const [pathStr, pathItem] of Object.entries(paths)) {
|
|
311
|
-
const pathObj = pathItem;
|
|
312
|
-
for (const method of ['get', 'post', 'put', 'delete', 'patch', 'head', 'options']) {
|
|
313
|
-
const operation = pathObj[method];
|
|
314
|
-
if (!operation)
|
|
315
|
-
continue;
|
|
316
|
-
const operationId = operation.operationId || generateOperationId(method, pathStr);
|
|
317
|
-
const responses = operation.responses;
|
|
318
|
-
if (!responses)
|
|
319
|
-
continue;
|
|
320
|
-
for (const [statusCode, responseObj] of Object.entries(responses)) {
|
|
321
|
-
// Skip non-numeric status codes like 'default'
|
|
322
|
-
if (!/^\d{3}$/.test(statusCode))
|
|
323
|
-
continue;
|
|
324
|
-
let schema = null;
|
|
325
|
-
if (isOpenAPI3) {
|
|
326
|
-
// OpenAPI 3.x: responses[code].content["application/json"].schema
|
|
327
|
-
const content = responseObj.content;
|
|
328
|
-
schema = getPreferredJsonSchema(content);
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
// Swagger 2.0: responses[code].schema
|
|
332
|
-
if (responseObj.schema) {
|
|
333
|
-
schema = responseObj.schema;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
if (schema) {
|
|
337
|
-
// Resolve all $ref references
|
|
338
|
-
const resolvedSchema = resolveSchemaRefs(schema, spec);
|
|
339
|
-
// Add JSON Schema meta properties
|
|
340
|
-
const jsonSchema = addJsonSchemaMeta(resolvedSchema, `${sanitizeOperationId(operationId)} ${statusCode} Response`);
|
|
341
|
-
schemas.push({
|
|
342
|
-
operationId: sanitizeOperationId(operationId),
|
|
343
|
-
method: method.toUpperCase(),
|
|
344
|
-
path: pathStr,
|
|
345
|
-
statusCode,
|
|
346
|
-
schema: jsonSchema
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
return schemas;
|
|
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
|
-
}
|
|
413
|
-
/**
|
|
414
|
-
* Generate schema filename from operation details
|
|
415
|
-
*/
|
|
416
|
-
function generateSchemaFilename(operationId, statusCode) {
|
|
417
|
-
return `${operationId}-${statusCode}.schema.json`;
|
|
418
|
-
}
|
|
419
|
-
//# sourceMappingURL=swaggerParser.js.map
|
|
@@ -1,100 +0,0 @@
|
|
|
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
|