x-openapi-flow 1.3.0 → 1.3.1
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 +108 -3
- package/adapters/collections/insomnia-adapter.js +73 -0
- package/adapters/collections/postman-adapter.js +145 -0
- package/adapters/docs/doc-adapter.js +119 -0
- package/adapters/flow-output-adapters.js +15 -0
- package/adapters/shared/helpers.js +87 -0
- package/adapters/ui/redoc/x-openapi-flow-redoc-plugin.js +127 -0
- package/adapters/ui/redoc-adapter.js +75 -0
- package/bin/x-openapi-flow.js +766 -0
- package/lib/sdk-generator.js +673 -0
- package/package.json +9 -4
- package/templates/go/README.md +3 -0
- package/templates/kotlin/README.md +3 -0
- package/templates/python/README.md +3 -0
- package/templates/typescript/flow-helpers.hbs +26 -0
- package/templates/typescript/http-client.hbs +37 -0
- package/templates/typescript/index.hbs +16 -0
- package/templates/typescript/resource.hbs +24 -0
- package/examples/swagger-ui/index.html +0 -33
- /package/{lib → adapters/ui}/swagger-ui/x-openapi-flow-plugin.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x-openapi-flow",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "OpenAPI extension for resource workflow and lifecycle management",
|
|
5
5
|
"main": "lib/validator.js",
|
|
6
6
|
"repository": {
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
"bin",
|
|
16
16
|
"lib",
|
|
17
|
+
"adapters",
|
|
18
|
+
"templates",
|
|
19
|
+
"schema",
|
|
17
20
|
"schema",
|
|
18
21
|
"examples",
|
|
19
22
|
"README.md",
|
|
@@ -29,9 +32,10 @@
|
|
|
29
32
|
"x-openapi-flow": "bin/x-openapi-flow.js"
|
|
30
33
|
},
|
|
31
34
|
"scripts": {
|
|
32
|
-
"test": "npm run test:cli && npm run test:ui && npm run test:smoke",
|
|
33
|
-
"test:cli": "node --test tests/cli.test.js",
|
|
34
|
-
"test:ui": "node --test tests/plugin-ui.test.js",
|
|
35
|
+
"test": "npm run test:cli && npm run test:ui && npm run test:integration && npm run test:smoke",
|
|
36
|
+
"test:cli": "node --test tests/cli/cli.test.js",
|
|
37
|
+
"test:ui": "node --test tests/plugins/plugin-ui.test.js",
|
|
38
|
+
"test:integration": "node --test tests/integration/*.test.js",
|
|
35
39
|
"test:smoke": "node bin/x-openapi-flow.js validate examples/payment-api.yaml --profile strict"
|
|
36
40
|
},
|
|
37
41
|
"keywords": [
|
|
@@ -45,6 +49,7 @@
|
|
|
45
49
|
"license": "MIT",
|
|
46
50
|
"dependencies": {
|
|
47
51
|
"ajv": "^8.17.1",
|
|
52
|
+
"handlebars": "^4.7.8",
|
|
48
53
|
"js-yaml": "^4.1.0"
|
|
49
54
|
}
|
|
50
55
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface FlowExecutable {
|
|
2
|
+
[methodName: string]: (...args: unknown[]) => Promise<unknown>;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export async function runFlow(
|
|
6
|
+
executable: FlowExecutable,
|
|
7
|
+
flow: string,
|
|
8
|
+
...args: unknown[]
|
|
9
|
+
): Promise<unknown> {
|
|
10
|
+
const steps = flow
|
|
11
|
+
.split("->")
|
|
12
|
+
.map((step) => step.trim())
|
|
13
|
+
.filter(Boolean);
|
|
14
|
+
|
|
15
|
+
let current: unknown = executable;
|
|
16
|
+
for (const step of steps) {
|
|
17
|
+
const target = current as FlowExecutable;
|
|
18
|
+
if (!target || typeof target[step] !== "function") {
|
|
19
|
+
throw new Error(`Flow step '${step}' is not available in the current state.`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
current = await target[step](...args);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return current;
|
|
26
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface RequestOptions {
|
|
2
|
+
body?: unknown;
|
|
3
|
+
headers?: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface HttpClient {
|
|
7
|
+
request(method: string, path: string, options?: RequestOptions): Promise<unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class FetchHttpClient implements HttpClient {
|
|
11
|
+
constructor(
|
|
12
|
+
private readonly baseUrl: string,
|
|
13
|
+
private readonly defaultHeaders: Record<string, string> = {},
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
async request(method: string, path: string, options: RequestOptions = {}): Promise<unknown> {
|
|
17
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
18
|
+
method,
|
|
19
|
+
headers: {
|
|
20
|
+
"content-type": "application/json",
|
|
21
|
+
...this.defaultHeaders,
|
|
22
|
+
...(options.headers || {}),
|
|
23
|
+
},
|
|
24
|
+
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new Error(`HTTP ${response.status} when calling ${method} ${path}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (response.status === 204) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return response.json();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HttpClient } from "./http-client";
|
|
2
|
+
{{#each resources}}
|
|
3
|
+
import { {{resourceClassName}}Resource } from "./resources/{{resourceClassName}}";
|
|
4
|
+
{{/each}}
|
|
5
|
+
|
|
6
|
+
export class FlowApiClient {
|
|
7
|
+
{{#each resources}}
|
|
8
|
+
public readonly {{resourcePropertyName}}: {{resourceClassName}}Resource;
|
|
9
|
+
{{/each}}
|
|
10
|
+
|
|
11
|
+
constructor(httpClient: HttpClient) {
|
|
12
|
+
{{#each resources}}
|
|
13
|
+
this.{{resourcePropertyName}} = new {{resourceClassName}}Resource(httpClient);
|
|
14
|
+
{{/each}}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { HttpClient, RequestOptions } from "../http-client";
|
|
2
|
+
|
|
3
|
+
const ensurePrerequisites = (
|
|
4
|
+
completedOperations: Set<string>,
|
|
5
|
+
requiredOperationIds: string[],
|
|
6
|
+
methodName: string,
|
|
7
|
+
): void => {
|
|
8
|
+
const missing = requiredOperationIds.filter((operationId) => !completedOperations.has(operationId));
|
|
9
|
+
if (missing.length > 0) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`Cannot call ${methodName} before prerequisites are satisfied: ${missing.join(", ")}`,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
{{{sharedTypesCode}}}
|
|
17
|
+
|
|
18
|
+
{{{stateClassesCode}}}
|
|
19
|
+
|
|
20
|
+
export class {{resourceClassName}}Resource {
|
|
21
|
+
constructor(private readonly httpClient: HttpClient) {}
|
|
22
|
+
|
|
23
|
+
{{{serviceMethodsCode}}}
|
|
24
|
+
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>x-openapi-flow + Swagger UI</title>
|
|
7
|
-
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
|
|
8
|
-
<style>
|
|
9
|
-
body { margin: 0; }
|
|
10
|
-
#swagger-ui { max-width: 1200px; margin: 0 auto; }
|
|
11
|
-
</style>
|
|
12
|
-
</head>
|
|
13
|
-
<body>
|
|
14
|
-
<div id="swagger-ui"></div>
|
|
15
|
-
|
|
16
|
-
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
|
|
17
|
-
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js"></script>
|
|
18
|
-
<script>
|
|
19
|
-
window.XOpenApiFlowGraphImageUrl = "https://raw.githubusercontent.com/tiago-marques/x-openapi-flow/main/docs/assets/graph-order-guided.svg";
|
|
20
|
-
</script>
|
|
21
|
-
<script src="../../lib/swagger-ui/x-openapi-flow-plugin.js"></script>
|
|
22
|
-
<script>
|
|
23
|
-
window.ui = SwaggerUIBundle({
|
|
24
|
-
url: "../payment-api.yaml",
|
|
25
|
-
dom_id: "#swagger-ui",
|
|
26
|
-
deepLinking: true,
|
|
27
|
-
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
|
|
28
|
-
plugins: [window.XOpenApiFlowPlugin],
|
|
29
|
-
showExtensions: true,
|
|
30
|
-
});
|
|
31
|
-
</script>
|
|
32
|
-
</body>
|
|
33
|
-
</html>
|
|
File without changes
|