x-openapi-flow 1.3.6 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +343 -306
- package/adapters/collections/insomnia-adapter.js +211 -15
- package/adapters/collections/postman-adapter.js +195 -15
- package/adapters/ui/redoc/x-openapi-flow-redoc-plugin.js +620 -91
- package/adapters/ui/redoc-adapter.js +132 -5
- package/package.json +4 -2
|
@@ -6,14 +6,179 @@ const { loadApi } = require("../../lib/validator");
|
|
|
6
6
|
const { buildIntermediateModel } = require("../../lib/sdk-generator");
|
|
7
7
|
const { toTitleCase, pathToPostmanUrl, buildLifecycleSequences } = require("../shared/helpers");
|
|
8
8
|
|
|
9
|
+
function getOperationMapById(api) {
|
|
10
|
+
const map = new Map();
|
|
11
|
+
const paths = (api && api.paths) || {};
|
|
12
|
+
|
|
13
|
+
for (const [pathKey, pathItem] of Object.entries(paths)) {
|
|
14
|
+
for (const [method, operation] of Object.entries(pathItem || {})) {
|
|
15
|
+
if (!operation || typeof operation !== "object") continue;
|
|
16
|
+
if (!operation.operationId) continue;
|
|
17
|
+
map.set(operation.operationId, {
|
|
18
|
+
...operation,
|
|
19
|
+
__path: pathKey,
|
|
20
|
+
__method: String(method || "get").toLowerCase(),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return map;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildExampleFromSchema(schema) {
|
|
29
|
+
if (!schema || typeof schema !== "object") return {};
|
|
30
|
+
|
|
31
|
+
if (Object.prototype.hasOwnProperty.call(schema, "example")) {
|
|
32
|
+
return schema.example;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
36
|
+
return schema.enum[0];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (schema.default !== undefined) {
|
|
40
|
+
return schema.default;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const type = schema.type;
|
|
44
|
+
if (type === "string") {
|
|
45
|
+
if (schema.format === "date-time") return "2026-01-01T00:00:00Z";
|
|
46
|
+
if (schema.format === "date") return "2026-01-01";
|
|
47
|
+
if (schema.format === "email") return "user@example.com";
|
|
48
|
+
return "string";
|
|
49
|
+
}
|
|
50
|
+
if (type === "number" || type === "integer") return 0;
|
|
51
|
+
if (type === "boolean") return false;
|
|
52
|
+
if (type === "array") {
|
|
53
|
+
const itemExample = buildExampleFromSchema(schema.items || {});
|
|
54
|
+
return itemExample === undefined ? [] : [itemExample];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const properties = schema.properties || {};
|
|
58
|
+
const required = Array.isArray(schema.required) ? schema.required : Object.keys(properties);
|
|
59
|
+
const payload = {};
|
|
60
|
+
for (const key of required) {
|
|
61
|
+
if (!properties[key]) continue;
|
|
62
|
+
payload[key] = buildExampleFromSchema(properties[key]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (Object.keys(payload).length === 0) {
|
|
66
|
+
for (const [key, propertySchema] of Object.entries(properties)) {
|
|
67
|
+
payload[key] = buildExampleFromSchema(propertySchema);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return payload;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function extractJsonRequestExample(rawOperation) {
|
|
75
|
+
if (!rawOperation || !rawOperation.requestBody || !rawOperation.requestBody.content) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const jsonContent = rawOperation.requestBody.content["application/json"];
|
|
80
|
+
if (!jsonContent) return null;
|
|
81
|
+
|
|
82
|
+
if (jsonContent.example !== undefined) {
|
|
83
|
+
return jsonContent.example;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (jsonContent.examples && typeof jsonContent.examples === "object") {
|
|
87
|
+
const firstExample = Object.values(jsonContent.examples)[0];
|
|
88
|
+
if (firstExample && typeof firstExample === "object" && firstExample.value !== undefined) {
|
|
89
|
+
return firstExample.value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (jsonContent.schema) {
|
|
94
|
+
return buildExampleFromSchema(jsonContent.schema);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function buildJourneyName(sequence, index) {
|
|
101
|
+
if (!Array.isArray(sequence) || sequence.length === 0) {
|
|
102
|
+
return `Journey ${index + 1}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (sequence.length === 1) {
|
|
106
|
+
return `Journey ${index + 1}: ${sequence[0].operationId}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const first = sequence[0].operationId;
|
|
110
|
+
const last = sequence[sequence.length - 1].operationId;
|
|
111
|
+
return `Journey ${index + 1}: ${first} -> ${last}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function buildFlowDescription(operation) {
|
|
115
|
+
const lines = [];
|
|
116
|
+
|
|
117
|
+
if (operation.currentState) {
|
|
118
|
+
lines.push(`Current state: ${operation.currentState}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (Array.isArray(operation.prerequisites) && operation.prerequisites.length > 0) {
|
|
122
|
+
lines.push(`Prerequisites: ${operation.prerequisites.join(", ")}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (Array.isArray(operation.nextOperations) && operation.nextOperations.length > 0) {
|
|
126
|
+
const transitions = operation.nextOperations
|
|
127
|
+
.map((next) => {
|
|
128
|
+
const parts = [];
|
|
129
|
+
if (next.targetState) parts.push(`state ${next.targetState}`);
|
|
130
|
+
if (next.nextOperationId) parts.push(`op ${next.nextOperationId}`);
|
|
131
|
+
if (next.triggerType) parts.push(`trigger ${next.triggerType}`);
|
|
132
|
+
return parts.join(" | ");
|
|
133
|
+
})
|
|
134
|
+
.filter(Boolean);
|
|
135
|
+
if (transitions.length > 0) {
|
|
136
|
+
lines.push(`Next: ${transitions.join(" ; ")}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return lines.join("\n");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function createInsomniaRequest(requestId, parentId, operation, resource, rawOperation) {
|
|
144
|
+
const request = {
|
|
145
|
+
_id: requestId,
|
|
146
|
+
_type: "request",
|
|
147
|
+
parentId,
|
|
148
|
+
name: operation.operationId,
|
|
149
|
+
method: String(operation.httpMethod || "get").toUpperCase(),
|
|
150
|
+
url: `{{ base_url }}${pathToPostmanUrl(operation.path, resource.resourcePropertyName)}`,
|
|
151
|
+
headers: [],
|
|
152
|
+
body: {},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const description = buildFlowDescription(operation);
|
|
156
|
+
if (description) {
|
|
157
|
+
request.description = description;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (["POST", "PUT", "PATCH"].includes(request.method)) {
|
|
161
|
+
request.headers.push({ name: "Content-Type", value: "application/json" });
|
|
162
|
+
const bodyExample = extractJsonRequestExample(rawOperation);
|
|
163
|
+
request.body = {
|
|
164
|
+
mimeType: "application/json",
|
|
165
|
+
text: JSON.stringify(bodyExample !== null ? bodyExample : {}, null, 2),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return request;
|
|
170
|
+
}
|
|
171
|
+
|
|
9
172
|
function generateInsomniaWorkspace(options) {
|
|
10
173
|
const apiPath = path.resolve(options.apiPath);
|
|
11
174
|
const outputPath = path.resolve(options.outputPath || path.join(process.cwd(), "x-openapi-flow.insomnia.json"));
|
|
12
175
|
|
|
13
176
|
const api = loadApi(apiPath);
|
|
14
177
|
const model = buildIntermediateModel(api);
|
|
178
|
+
const operationMapById = getOperationMapById(api);
|
|
15
179
|
|
|
16
180
|
const workspaceId = "wrk_x_openapi_flow";
|
|
181
|
+
const environmentId = "env_x_openapi_flow_base";
|
|
17
182
|
const resources = [
|
|
18
183
|
{
|
|
19
184
|
_id: workspaceId,
|
|
@@ -22,6 +187,15 @@ function generateInsomniaWorkspace(options) {
|
|
|
22
187
|
description: `Generated from ${apiPath}`,
|
|
23
188
|
scope: "collection",
|
|
24
189
|
},
|
|
190
|
+
{
|
|
191
|
+
_id: environmentId,
|
|
192
|
+
_type: "environment",
|
|
193
|
+
parentId: workspaceId,
|
|
194
|
+
name: "Base Environment",
|
|
195
|
+
data: {
|
|
196
|
+
base_url: "http://localhost:3000",
|
|
197
|
+
},
|
|
198
|
+
},
|
|
25
199
|
];
|
|
26
200
|
|
|
27
201
|
for (const resource of model.resources) {
|
|
@@ -34,22 +208,44 @@ function generateInsomniaWorkspace(options) {
|
|
|
34
208
|
});
|
|
35
209
|
|
|
36
210
|
const sequences = buildLifecycleSequences(resource);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
211
|
+
if (sequences.length > 0) {
|
|
212
|
+
sequences.forEach((sequence, sequenceIndex) => {
|
|
213
|
+
const journeyId = `fld_${resource.resourcePropertyName}_journey_${sequenceIndex + 1}`;
|
|
214
|
+
resources.push({
|
|
215
|
+
_id: journeyId,
|
|
216
|
+
_type: "request_group",
|
|
217
|
+
parentId: groupId,
|
|
218
|
+
name: buildJourneyName(sequence, sequenceIndex),
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
sequence.forEach((operation, operationIndex) => {
|
|
222
|
+
const requestId = `req_${resource.resourcePropertyName}_${sequenceIndex + 1}_${operationIndex + 1}`;
|
|
223
|
+
resources.push(
|
|
224
|
+
createInsomniaRequest(
|
|
225
|
+
requestId,
|
|
226
|
+
journeyId,
|
|
227
|
+
operation,
|
|
228
|
+
resource,
|
|
229
|
+
operationMapById.get(operation.operationId)
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
});
|
|
51
233
|
});
|
|
52
|
-
}
|
|
234
|
+
} else {
|
|
235
|
+
const operations = resource.operations.filter((operation) => operation.hasFlow);
|
|
236
|
+
operations.forEach((operation, index) => {
|
|
237
|
+
const requestId = `req_${resource.resourcePropertyName}_${index + 1}`;
|
|
238
|
+
resources.push(
|
|
239
|
+
createInsomniaRequest(
|
|
240
|
+
requestId,
|
|
241
|
+
groupId,
|
|
242
|
+
operation,
|
|
243
|
+
resource,
|
|
244
|
+
operationMapById.get(operation.operationId)
|
|
245
|
+
)
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
53
249
|
}
|
|
54
250
|
|
|
55
251
|
const exportPayload = {
|
|
@@ -6,7 +6,167 @@ const { loadApi } = require("../../lib/validator");
|
|
|
6
6
|
const { buildIntermediateModel } = require("../../lib/sdk-generator");
|
|
7
7
|
const { toTitleCase, pathToPostmanUrl, buildLifecycleSequences } = require("../shared/helpers");
|
|
8
8
|
|
|
9
|
-
function
|
|
9
|
+
function getOperationMapById(api) {
|
|
10
|
+
const map = new Map();
|
|
11
|
+
const paths = (api && api.paths) || {};
|
|
12
|
+
|
|
13
|
+
for (const [pathKey, pathItem] of Object.entries(paths)) {
|
|
14
|
+
for (const [method, operation] of Object.entries(pathItem || {})) {
|
|
15
|
+
if (!operation || typeof operation !== "object") continue;
|
|
16
|
+
if (!operation.operationId) continue;
|
|
17
|
+
map.set(operation.operationId, {
|
|
18
|
+
...operation,
|
|
19
|
+
__path: pathKey,
|
|
20
|
+
__method: String(method || "get").toLowerCase(),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return map;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildExampleFromSchema(schema) {
|
|
29
|
+
if (!schema || typeof schema !== "object") return {};
|
|
30
|
+
|
|
31
|
+
if (Object.prototype.hasOwnProperty.call(schema, "example")) {
|
|
32
|
+
return schema.example;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
36
|
+
return schema.enum[0];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (schema.default !== undefined) {
|
|
40
|
+
return schema.default;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const type = schema.type;
|
|
44
|
+
if (type === "string") {
|
|
45
|
+
if (schema.format === "date-time") return "2026-01-01T00:00:00Z";
|
|
46
|
+
if (schema.format === "date") return "2026-01-01";
|
|
47
|
+
if (schema.format === "email") return "user@example.com";
|
|
48
|
+
return "string";
|
|
49
|
+
}
|
|
50
|
+
if (type === "number" || type === "integer") return 0;
|
|
51
|
+
if (type === "boolean") return false;
|
|
52
|
+
if (type === "array") {
|
|
53
|
+
const itemExample = buildExampleFromSchema(schema.items || {});
|
|
54
|
+
return itemExample === undefined ? [] : [itemExample];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const properties = schema.properties || {};
|
|
58
|
+
const required = Array.isArray(schema.required) ? schema.required : Object.keys(properties);
|
|
59
|
+
const payload = {};
|
|
60
|
+
for (const key of required) {
|
|
61
|
+
if (!properties[key]) continue;
|
|
62
|
+
payload[key] = buildExampleFromSchema(properties[key]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (Object.keys(payload).length === 0) {
|
|
66
|
+
for (const [key, propertySchema] of Object.entries(properties)) {
|
|
67
|
+
payload[key] = buildExampleFromSchema(propertySchema);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return payload;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function extractJsonRequestExample(rawOperation) {
|
|
75
|
+
if (!rawOperation || !rawOperation.requestBody || !rawOperation.requestBody.content) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const jsonContent = rawOperation.requestBody.content["application/json"];
|
|
80
|
+
if (!jsonContent) return null;
|
|
81
|
+
|
|
82
|
+
if (jsonContent.example !== undefined) {
|
|
83
|
+
return jsonContent.example;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (jsonContent.examples && typeof jsonContent.examples === "object") {
|
|
87
|
+
const firstExample = Object.values(jsonContent.examples)[0];
|
|
88
|
+
if (firstExample && typeof firstExample === "object" && firstExample.value !== undefined) {
|
|
89
|
+
return firstExample.value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (jsonContent.schema) {
|
|
94
|
+
return buildExampleFromSchema(jsonContent.schema);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function extractResponseIdKeys(rawOperation) {
|
|
101
|
+
if (!rawOperation || !rawOperation.responses || typeof rawOperation.responses !== "object") {
|
|
102
|
+
return ["id"];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const keys = new Set(["id"]);
|
|
106
|
+
const successResponse = Object.entries(rawOperation.responses).find(([statusCode]) => /^2\d\d$/.test(String(statusCode)));
|
|
107
|
+
const response = successResponse ? successResponse[1] : null;
|
|
108
|
+
const schema = response
|
|
109
|
+
&& response.content
|
|
110
|
+
&& response.content["application/json"]
|
|
111
|
+
&& response.content["application/json"].schema;
|
|
112
|
+
|
|
113
|
+
const properties = schema && schema.properties ? Object.keys(schema.properties) : [];
|
|
114
|
+
properties.forEach((key) => {
|
|
115
|
+
if (key === "id" || /_id$/i.test(key)) {
|
|
116
|
+
keys.add(key);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return [...keys];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function buildPrerequisiteRuleSets(resource) {
|
|
124
|
+
const incomingByTarget = new Map();
|
|
125
|
+
|
|
126
|
+
for (const sourceOperation of resource.operations || []) {
|
|
127
|
+
for (const nextOperation of sourceOperation.nextOperations || []) {
|
|
128
|
+
const target = nextOperation && nextOperation.nextOperationId;
|
|
129
|
+
if (!target) continue;
|
|
130
|
+
|
|
131
|
+
if (!incomingByTarget.has(target)) {
|
|
132
|
+
incomingByTarget.set(target, []);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const prereqSet = Array.from(new Set(Array.isArray(nextOperation.prerequisites) ? nextOperation.prerequisites : []));
|
|
136
|
+
incomingByTarget.get(target).push(prereqSet);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const dedupByTarget = new Map();
|
|
141
|
+
for (const [target, sets] of incomingByTarget.entries()) {
|
|
142
|
+
const unique = new Map();
|
|
143
|
+
for (const set of sets) {
|
|
144
|
+
const key = [...set].sort().join("|");
|
|
145
|
+
if (!unique.has(key)) {
|
|
146
|
+
unique.set(key, set);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
dedupByTarget.set(target, [...unique.values()]);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return dedupByTarget;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildJourneyName(sequence, index) {
|
|
156
|
+
if (!Array.isArray(sequence) || sequence.length === 0) {
|
|
157
|
+
return `Journey ${index + 1}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (sequence.length === 1) {
|
|
161
|
+
return `Journey ${index + 1}: ${sequence[0].operationId}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const first = sequence[0].operationId;
|
|
165
|
+
const last = sequence[sequence.length - 1].operationId;
|
|
166
|
+
return `Journey ${index + 1}: ${first} -> ${last}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function buildPostmanItem(operation, resource, rawOperation) {
|
|
10
170
|
const rawPath = pathToPostmanUrl(operation.path, resource.resourcePropertyName);
|
|
11
171
|
const urlRaw = `{{baseUrl}}${rawPath}`;
|
|
12
172
|
|
|
@@ -31,9 +191,10 @@ function buildPostmanItem(operation, resource) {
|
|
|
31
191
|
};
|
|
32
192
|
|
|
33
193
|
if (["POST", "PUT", "PATCH"].includes(item.request.method)) {
|
|
194
|
+
const bodyExample = extractJsonRequestExample(rawOperation);
|
|
34
195
|
item.request.body = {
|
|
35
196
|
mode: "raw",
|
|
36
|
-
raw:
|
|
197
|
+
raw: JSON.stringify(bodyExample !== null ? bodyExample : {}, null, 2),
|
|
37
198
|
options: { raw: { language: "json" } },
|
|
38
199
|
};
|
|
39
200
|
}
|
|
@@ -41,10 +202,11 @@ function buildPostmanItem(operation, resource) {
|
|
|
41
202
|
return item;
|
|
42
203
|
}
|
|
43
204
|
|
|
44
|
-
function addPostmanScripts(item, operation, resource) {
|
|
45
|
-
const
|
|
205
|
+
function addPostmanScripts(item, operation, resource, ruleSetsByOperation, responseIdKeysByOperation) {
|
|
206
|
+
const ruleSets = JSON.stringify((ruleSetsByOperation.get(operation.operationId) || []));
|
|
46
207
|
const operationId = operation.operationId;
|
|
47
208
|
const idCandidateKey = `${resource.resourcePropertyName}Id`;
|
|
209
|
+
const idCandidateFields = JSON.stringify(responseIdKeysByOperation.get(operationId) || ["id"]);
|
|
48
210
|
|
|
49
211
|
item.event = [
|
|
50
212
|
{
|
|
@@ -52,11 +214,14 @@ function addPostmanScripts(item, operation, resource) {
|
|
|
52
214
|
script: {
|
|
53
215
|
type: "text/javascript",
|
|
54
216
|
exec: [
|
|
55
|
-
`const
|
|
217
|
+
`const ruleSets = ${ruleSets};`,
|
|
56
218
|
"const executed = JSON.parse(pm.collectionVariables.get('flowExecutedOps') || '[]');",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
|
|
219
|
+
"if (ruleSets.length > 0) {",
|
|
220
|
+
" const isSatisfied = ruleSets.some((required) => required.every((operationId) => executed.includes(operationId)));",
|
|
221
|
+
" if (!isSatisfied) {",
|
|
222
|
+
" const expected = ruleSets.map((set) => set.join(' + ')).join(' OR ');",
|
|
223
|
+
` throw new Error('Missing prerequisites for ${operationId}. Expected one of: ' + expected);`,
|
|
224
|
+
" }",
|
|
60
225
|
"}",
|
|
61
226
|
],
|
|
62
227
|
},
|
|
@@ -66,8 +231,11 @@ function addPostmanScripts(item, operation, resource) {
|
|
|
66
231
|
script: {
|
|
67
232
|
type: "text/javascript",
|
|
68
233
|
exec: [
|
|
69
|
-
"
|
|
70
|
-
|
|
234
|
+
"let payload = {};",
|
|
235
|
+
"try { payload = pm.response.json(); } catch (_err) { payload = {}; }",
|
|
236
|
+
`const idFields = ${idCandidateFields};`,
|
|
237
|
+
"const discovered = idFields.find((field) => payload && payload[field] !== undefined && payload[field] !== null);",
|
|
238
|
+
`if (discovered) pm.collectionVariables.set('${idCandidateKey}', String(payload[discovered]));`,
|
|
71
239
|
"const executed = JSON.parse(pm.collectionVariables.get('flowExecutedOps') || '[]');",
|
|
72
240
|
`if (!executed.includes('${operationId}')) executed.push('${operationId}');`,
|
|
73
241
|
"pm.collectionVariables.set('flowExecutedOps', JSON.stringify(executed));",
|
|
@@ -84,6 +252,7 @@ function generatePostmanCollection(options) {
|
|
|
84
252
|
|
|
85
253
|
const api = loadApi(apiPath);
|
|
86
254
|
const model = buildIntermediateModel(api);
|
|
255
|
+
const operationMapById = getOperationMapById(api);
|
|
87
256
|
|
|
88
257
|
const collection = {
|
|
89
258
|
info: {
|
|
@@ -100,6 +269,13 @@ function generatePostmanCollection(options) {
|
|
|
100
269
|
|
|
101
270
|
for (const resource of model.resources) {
|
|
102
271
|
const sequences = buildLifecycleSequences(resource);
|
|
272
|
+
const ruleSetsByOperation = buildPrerequisiteRuleSets(resource);
|
|
273
|
+
const responseIdKeysByOperation = new Map(
|
|
274
|
+
resource.operations.map((operation) => [
|
|
275
|
+
operation.operationId,
|
|
276
|
+
extractResponseIdKeys(operationMapById.get(operation.operationId)),
|
|
277
|
+
])
|
|
278
|
+
);
|
|
103
279
|
const folder = {
|
|
104
280
|
name: `${toTitleCase(resource.resourcePlural || resource.resource)} Lifecycle`,
|
|
105
281
|
item: [],
|
|
@@ -109,18 +285,22 @@ function generatePostmanCollection(options) {
|
|
|
109
285
|
const fallbackItems = resource.operations
|
|
110
286
|
.filter((operation) => operation.hasFlow)
|
|
111
287
|
.map((operation) => {
|
|
112
|
-
const item = buildPostmanItem(operation, resource);
|
|
113
|
-
if (withScripts)
|
|
288
|
+
const item = buildPostmanItem(operation, resource, operationMapById.get(operation.operationId));
|
|
289
|
+
if (withScripts) {
|
|
290
|
+
addPostmanScripts(item, operation, resource, ruleSetsByOperation, responseIdKeysByOperation);
|
|
291
|
+
}
|
|
114
292
|
return item;
|
|
115
293
|
});
|
|
116
294
|
folder.item.push(...fallbackItems);
|
|
117
295
|
} else {
|
|
118
296
|
sequences.forEach((sequence, index) => {
|
|
119
297
|
const journey = {
|
|
120
|
-
name:
|
|
298
|
+
name: buildJourneyName(sequence, index),
|
|
121
299
|
item: sequence.map((operation) => {
|
|
122
|
-
const item = buildPostmanItem(operation, resource);
|
|
123
|
-
if (withScripts)
|
|
300
|
+
const item = buildPostmanItem(operation, resource, operationMapById.get(operation.operationId));
|
|
301
|
+
if (withScripts) {
|
|
302
|
+
addPostmanScripts(item, operation, resource, ruleSetsByOperation, responseIdKeysByOperation);
|
|
303
|
+
}
|
|
124
304
|
return item;
|
|
125
305
|
}),
|
|
126
306
|
};
|