maro-plugin-http 1.1.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 +15 -0
- package/dist/src/actions/generate_swagger.d.ts +5 -0
- package/dist/src/actions/generate_swagger.js +37 -0
- package/dist/src/commands/postman.d.ts +2 -0
- package/dist/src/commands/postman.js +33 -0
- package/dist/src/commands/swagger.d.ts +2 -0
- package/dist/src/commands/swagger.js +19 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +26 -0
- package/dist/src/lib/config.d.ts +12 -0
- package/dist/src/lib/config.js +35 -0
- package/dist/src/lib/formatter.d.ts +23 -0
- package/dist/src/lib/formatter.js +101 -0
- package/dist/src/lib/http_file.d.ts +12 -0
- package/dist/src/lib/http_file.js +32 -0
- package/dist/src/lib/index.d.ts +6 -0
- package/dist/src/lib/index.js +15 -0
- package/dist/src/lib/postman/body.d.ts +11 -0
- package/dist/src/lib/postman/body.js +16 -0
- package/dist/src/lib/postman/index.d.ts +8 -0
- package/dist/src/lib/postman/index.js +25 -0
- package/dist/src/lib/postman/request.d.ts +10 -0
- package/dist/src/lib/postman/request.js +84 -0
- package/dist/src/lib/postman/types.d.ts +105 -0
- package/dist/src/lib/postman/types.js +7 -0
- package/dist/src/lib/req.d.ts +31 -0
- package/dist/src/lib/req.js +55 -0
- package/dist/src/lib/swagger/file.d.ts +72 -0
- package/dist/src/lib/swagger/file.js +7 -0
- package/dist/src/lib/swagger/index.d.ts +9 -0
- package/dist/src/lib/swagger/index.js +35 -0
- package/dist/src/lib/swagger/operation.d.ts +11 -0
- package/dist/src/lib/swagger/operation.js +58 -0
- package/dist/src/lib/swagger/params.d.ts +11 -0
- package/dist/src/lib/swagger/params.js +17 -0
- package/dist/src/lib/swagger/paths.d.ts +6 -0
- package/dist/src/lib/swagger/paths.js +18 -0
- package/dist/src/lib/swagger/request.d.ts +9 -0
- package/dist/src/lib/swagger/request.js +28 -0
- package/dist/src/steps/GetHttpFile.d.ts +17 -0
- package/dist/src/steps/GetHttpFile.js +22 -0
- package/dist/src/steps/PromptHttpFile.d.ts +13 -0
- package/dist/src/steps/PromptHttpFile.js +17 -0
- package/dist/src/steps/PromptHttpFileRequest.d.ts +20 -0
- package/dist/src/steps/PromptHttpFileRequest.js +24 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# 馃摗 maro-plugin-http
|
|
2
|
+
|
|
3
|
+
Hace hablar a las APIs y desvela su esencia.
|
|
4
|
+
|
|
5
|
+
Con este plugin, Maro puede generar autom谩ticamente documentaci贸n Swagger y colecciones Postman a partir de archivos `.http`, convirtiendo especificaci贸n en exploraci贸n instant谩nea.
|
|
6
|
+
|
|
7
|
+
Descubre, documenta y prueba tus endpoints sin salir de la br煤jula del terminal.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
**Comandos principales**
|
|
11
|
+
- Generar documentaci贸n Swagger desde archivos `.http`.
|
|
12
|
+
- Crear colecciones Postman listas para testear.
|
|
13
|
+
- Traducir tus flujos de API en puentes tangibles para equipos y automatizaciones.
|
|
14
|
+
|
|
15
|
+
Con茅ctate sin fricci贸n con el plano invisible de tus servicios web.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.GenerateSwagger = void 0;
|
|
10
|
+
const maro_1 = require("@maro/maro");
|
|
11
|
+
const swagger_1 = require("../lib/swagger");
|
|
12
|
+
const GetHttpFile_1 = require("../steps/GetHttpFile");
|
|
13
|
+
class GenerateSwagger {
|
|
14
|
+
register() {
|
|
15
|
+
maro_1.ActionRegistry.on(maro_1.MrCreateEvent, (event) => this.execute(event));
|
|
16
|
+
}
|
|
17
|
+
async execute(event) {
|
|
18
|
+
const repo = event.ctx;
|
|
19
|
+
if (!maro_1.AppRepo.isAppRepo(repo.dir))
|
|
20
|
+
return;
|
|
21
|
+
const paths = (0, maro_1.getPaths)("backend");
|
|
22
|
+
if (paths.every((p) => p.path !== repo.dir.path))
|
|
23
|
+
return;
|
|
24
|
+
const app_repo = new maro_1.AppRepo(repo.dir);
|
|
25
|
+
const ctx = maro_1.ExecutionContext.get();
|
|
26
|
+
const { http_file } = await new GetHttpFile_1.GetHttpFile().run(ctx, { app_repo });
|
|
27
|
+
if (!http_file)
|
|
28
|
+
return;
|
|
29
|
+
const file = new swagger_1.Swagger(http_file, app_repo).generate();
|
|
30
|
+
await repo.add(file);
|
|
31
|
+
await repo.commit("fix: add swagger");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.GenerateSwagger = GenerateSwagger;
|
|
35
|
+
__decorate([
|
|
36
|
+
(0, maro_1.loading)("Generating swagger")
|
|
37
|
+
], GenerateSwagger.prototype, "execute", null);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostmanCommand = void 0;
|
|
4
|
+
const maro_1 = require("@maro/maro");
|
|
5
|
+
const http_file_1 = require("../lib/http_file");
|
|
6
|
+
const postman_1 = require("../lib/postman");
|
|
7
|
+
const types_1 = require("../lib/postman/types");
|
|
8
|
+
exports.PostmanCommand = {
|
|
9
|
+
name: "postman",
|
|
10
|
+
description: "Generate postman from http files",
|
|
11
|
+
run: async ({ ctx, config }) => {
|
|
12
|
+
const log = ctx.logger;
|
|
13
|
+
await new maro_1.ValidateConfig({ keys: ["http.collection"] }).run();
|
|
14
|
+
const collection = new maro_1.Dir(config.get("http.collection"));
|
|
15
|
+
const collections = collection.readFiles();
|
|
16
|
+
const files = collections.map((n) => new http_file_1.HttpFile(n.path));
|
|
17
|
+
const postman = new postman_1.Postman();
|
|
18
|
+
postman.addFiles(...files);
|
|
19
|
+
const today = new Date();
|
|
20
|
+
const day = today.getDate().toString().padStart(2, "0");
|
|
21
|
+
const month = (today.getMonth() + 1).toString().padStart(2, "0");
|
|
22
|
+
const year = today.getFullYear();
|
|
23
|
+
const date = `${year}-${month}-${day}`;
|
|
24
|
+
// TODO: support other envs and internal/external
|
|
25
|
+
const name = `Apoderados cert external ${date}.postman_collection`;
|
|
26
|
+
const file_name = `${name}.json`;
|
|
27
|
+
const content = postman.generate(name);
|
|
28
|
+
const postman_dir = collection.getRelative(config.get("http.postman_dir"));
|
|
29
|
+
const file = new types_1.PostmanFile(postman_dir.createFile(file_name).path);
|
|
30
|
+
file.write(content);
|
|
31
|
+
log.success(`${file} generated`);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SwaggerCommand = void 0;
|
|
4
|
+
const maro_1 = require("@maro/maro");
|
|
5
|
+
const swagger_1 = require("../lib/swagger");
|
|
6
|
+
const PromptHttpFile_1 = require("../steps/PromptHttpFile");
|
|
7
|
+
exports.SwaggerCommand = {
|
|
8
|
+
name: "swagger",
|
|
9
|
+
description: "Generate swagger from an http file",
|
|
10
|
+
run: async ({ ctx }) => {
|
|
11
|
+
const log = ctx.logger;
|
|
12
|
+
const { http_file } = await new PromptHttpFile_1.PromptHttpFile().run(ctx);
|
|
13
|
+
const app_name = http_file.service;
|
|
14
|
+
const { app_repo } = await new maro_1.GetAppRepo().run(ctx, { app_name });
|
|
15
|
+
const swagger = new swagger_1.Swagger(http_file, app_repo);
|
|
16
|
+
const file = swagger.generate();
|
|
17
|
+
log.success(`${file} generated`);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const maro_1 = require("@maro/maro");
|
|
4
|
+
const generate_swagger_1 = require("./actions/generate_swagger");
|
|
5
|
+
const postman_1 = require("./commands/postman");
|
|
6
|
+
const swagger_1 = require("./commands/swagger");
|
|
7
|
+
const config_1 = require("./lib/config");
|
|
8
|
+
const Plugin = {
|
|
9
|
+
name: "maro-plugin-http",
|
|
10
|
+
onLoad() {
|
|
11
|
+
maro_1.ConfigRegistry.register(new config_1.HttpConfig());
|
|
12
|
+
if (maro_1.Config.getView().get("http.generate_swagger"))
|
|
13
|
+
new generate_swagger_1.GenerateSwagger().register();
|
|
14
|
+
},
|
|
15
|
+
commands: [
|
|
16
|
+
{
|
|
17
|
+
name: "http",
|
|
18
|
+
description: "Generate postman collections and swagger from .http files",
|
|
19
|
+
subcommands: [
|
|
20
|
+
swagger_1.SwaggerCommand,
|
|
21
|
+
postman_1.PostmanCommand
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
};
|
|
26
|
+
exports.default = Plugin;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ConfigHelp, ConfigSection } from "@maro/maro";
|
|
2
|
+
export declare class HttpConfig implements ConfigSection {
|
|
3
|
+
key: string;
|
|
4
|
+
validate(config: unknown): {
|
|
5
|
+
url_suffix?: string | undefined;
|
|
6
|
+
collection?: string | undefined;
|
|
7
|
+
postman_dir?: string | undefined;
|
|
8
|
+
generate_swagger?: boolean | undefined;
|
|
9
|
+
};
|
|
10
|
+
help(): ConfigHelp[];
|
|
11
|
+
setup(): Promise<{}>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
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.HttpConfig = void 0;
|
|
7
|
+
const v4_1 = __importDefault(require("zod/v4"));
|
|
8
|
+
const schema = v4_1.default.object({
|
|
9
|
+
url_suffix: v4_1.default.string().optional(),
|
|
10
|
+
collection: v4_1.default.string().optional(),
|
|
11
|
+
postman_dir: v4_1.default.string().optional(),
|
|
12
|
+
generate_swagger: v4_1.default.boolean().optional()
|
|
13
|
+
});
|
|
14
|
+
class HttpConfig {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.key = "http";
|
|
17
|
+
}
|
|
18
|
+
validate(config) {
|
|
19
|
+
if (!config)
|
|
20
|
+
return {};
|
|
21
|
+
return schema.parse(config);
|
|
22
|
+
}
|
|
23
|
+
help() {
|
|
24
|
+
return [
|
|
25
|
+
{ key: "url_suffix", description: "Suffix used to extract service name from host variable", type: "string" },
|
|
26
|
+
{ key: "collection", description: "Directory containing HTTP collection files", type: "string" },
|
|
27
|
+
{ key: "postman_dir", description: "Directory to save postman files relative to 'collection'", type: "string" },
|
|
28
|
+
{ key: "generate_swagger", description: "Generate swagger on every AppRepo mr", type: "boolean" }
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
async setup() {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.HttpConfig = HttpConfig;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FileFormatter } from "@maro/maro";
|
|
2
|
+
export declare const valid_methods: readonly ["GET", "PUT", "POST", "PATCH", "DELETE"];
|
|
3
|
+
export type HttpMethod = typeof valid_methods[number];
|
|
4
|
+
export interface ReqObj {
|
|
5
|
+
method: HttpMethod;
|
|
6
|
+
params: Record<string, string | null> | null;
|
|
7
|
+
pathname: string;
|
|
8
|
+
headers: Record<string, string | null> | null;
|
|
9
|
+
body: string;
|
|
10
|
+
}
|
|
11
|
+
export type HttpFileContent = {
|
|
12
|
+
requests: ReqObj[];
|
|
13
|
+
variables: Record<string, string>;
|
|
14
|
+
};
|
|
15
|
+
export declare class HttpFormatter extends FileFormatter<HttpFileContent> {
|
|
16
|
+
toString(_input: HttpFileContent): string;
|
|
17
|
+
fromString(content: string): HttpFileContent;
|
|
18
|
+
private parseRequests;
|
|
19
|
+
private getVariable;
|
|
20
|
+
private getHeader;
|
|
21
|
+
private getVariables;
|
|
22
|
+
exception(path: string): Error | void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpFormatter = exports.valid_methods = void 0;
|
|
4
|
+
const maro_1 = require("@maro/maro");
|
|
5
|
+
const http_file_1 = require("./http_file");
|
|
6
|
+
exports.valid_methods = [
|
|
7
|
+
"GET",
|
|
8
|
+
"PUT",
|
|
9
|
+
"POST",
|
|
10
|
+
"PATCH",
|
|
11
|
+
"DELETE"
|
|
12
|
+
];
|
|
13
|
+
class HttpFormatter extends maro_1.FileFormatter {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
this.getVariable = (l) => {
|
|
17
|
+
const line = l.replaceAll("#", "").replaceAll(" ", "");
|
|
18
|
+
if (!line.startsWith("@"))
|
|
19
|
+
return null;
|
|
20
|
+
const split = line.split("=");
|
|
21
|
+
return { key: split?.[0]?.substring(1).trim(), value: split?.[1]?.trim() ?? null };
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
25
|
+
toString(_input) {
|
|
26
|
+
// TODO: implement Writing http files
|
|
27
|
+
throw new Error("Writing http files is not implemented yet");
|
|
28
|
+
}
|
|
29
|
+
fromString(content) {
|
|
30
|
+
const [globals, ...requestsString] = content.split("###");
|
|
31
|
+
const variables = this.getVariables(globals ?? "");
|
|
32
|
+
const requests = this.parseRequests(requestsString);
|
|
33
|
+
return { requests, variables };
|
|
34
|
+
}
|
|
35
|
+
parseRequests(requests) {
|
|
36
|
+
return requests.map((r) => {
|
|
37
|
+
if (r.trim() === "")
|
|
38
|
+
return null;
|
|
39
|
+
const lines = r.trim().split("\n");
|
|
40
|
+
const method = lines?.[0]?.split(" ")[0];
|
|
41
|
+
if (!method || !exports.valid_methods.includes(method))
|
|
42
|
+
return null;
|
|
43
|
+
let url = lines?.[0]?.split(" ")[1] ?? "";
|
|
44
|
+
let i = 1;
|
|
45
|
+
for (i; i < lines.length; i++) {
|
|
46
|
+
const l = lines?.[i]?.trim();
|
|
47
|
+
if (!l || l === "" || !l.startsWith("?") && !l.startsWith("&"))
|
|
48
|
+
break;
|
|
49
|
+
url = url?.concat(l);
|
|
50
|
+
}
|
|
51
|
+
const [path, searchParams] = url.split("?");
|
|
52
|
+
const pathname = `/${path?.split("/").slice(3).join("/") ?? ""}`;
|
|
53
|
+
const urlSearchParams = new URLSearchParams(searchParams);
|
|
54
|
+
const params = urlSearchParams && urlSearchParams.size > 0
|
|
55
|
+
? Object.fromEntries(Object.entries(Object.fromEntries(urlSearchParams.entries()))) : null;
|
|
56
|
+
let headers = null;
|
|
57
|
+
for (i; i < lines.length; i++) {
|
|
58
|
+
const l = lines[i];
|
|
59
|
+
if (!l || l.trim() === "")
|
|
60
|
+
break;
|
|
61
|
+
if (!headers)
|
|
62
|
+
headers = {};
|
|
63
|
+
const h = this.getHeader(l);
|
|
64
|
+
if (!h)
|
|
65
|
+
continue;
|
|
66
|
+
const { key, value } = h;
|
|
67
|
+
if (key && value)
|
|
68
|
+
headers[key] = value;
|
|
69
|
+
}
|
|
70
|
+
let bodyString = "";
|
|
71
|
+
for (i; i < lines.length; i++) {
|
|
72
|
+
const l = lines[i];
|
|
73
|
+
if (!l)
|
|
74
|
+
continue;
|
|
75
|
+
bodyString = bodyString.concat(l);
|
|
76
|
+
}
|
|
77
|
+
return { method: method, params, pathname, headers, body: bodyString };
|
|
78
|
+
}).filter((e) => e !== null);
|
|
79
|
+
}
|
|
80
|
+
getHeader(l) {
|
|
81
|
+
const split = l.split(":");
|
|
82
|
+
if (split.length <= 1)
|
|
83
|
+
return null;
|
|
84
|
+
return { key: split[0], value: split?.[1]?.trim() };
|
|
85
|
+
}
|
|
86
|
+
getVariables(globals) {
|
|
87
|
+
const line_filter = (l) => l !== "" && l !== "###" && !l.includes("localhost") && l !== "\n";
|
|
88
|
+
return Object.fromEntries(globals.split("\n")
|
|
89
|
+
.filter(line_filter)
|
|
90
|
+
.map((l) => {
|
|
91
|
+
const variable = this.getVariable(l);
|
|
92
|
+
if (!variable?.key || !variable.value)
|
|
93
|
+
return [];
|
|
94
|
+
return [variable.key, variable.value];
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
exception(path) {
|
|
98
|
+
return new http_file_1.InvalidHttpFile(path);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.HttpFormatter = HttpFormatter;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ObjectFile } from "@maro/maro";
|
|
2
|
+
import { HttpFileContent } from "./formatter";
|
|
3
|
+
import { Req } from "./req";
|
|
4
|
+
export declare class HttpFile extends ObjectFile<HttpFileContent> {
|
|
5
|
+
service: string;
|
|
6
|
+
constructor(file_path: string);
|
|
7
|
+
getVariables(): Record<string, string>;
|
|
8
|
+
getRequests(): Req[];
|
|
9
|
+
}
|
|
10
|
+
export declare class InvalidHttpFile extends Error {
|
|
11
|
+
constructor(file_path: string, reason?: string);
|
|
12
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InvalidHttpFile = exports.HttpFile = void 0;
|
|
4
|
+
const maro_1 = require("@maro/maro");
|
|
5
|
+
const formatter_1 = require("./formatter");
|
|
6
|
+
const req_1 = require("./req");
|
|
7
|
+
class HttpFile extends maro_1.ObjectFile {
|
|
8
|
+
constructor(file_path) {
|
|
9
|
+
super(file_path, new formatter_1.HttpFormatter());
|
|
10
|
+
const url_suffix = maro_1.Config.getView().get("http.url_suffix");
|
|
11
|
+
if (!url_suffix)
|
|
12
|
+
throw new maro_1.ConfigError("http.url_suffix");
|
|
13
|
+
// TODO: I hate the idea of having http files linked to a particular service, find a better way
|
|
14
|
+
const service = this.getVariables().host?.split(`-${url_suffix}`)?.[0];
|
|
15
|
+
if (!service)
|
|
16
|
+
throw new InvalidHttpFile(file_path, "Could not find host variable to determine service name");
|
|
17
|
+
this.service = service;
|
|
18
|
+
}
|
|
19
|
+
getVariables() {
|
|
20
|
+
return this.read().variables;
|
|
21
|
+
}
|
|
22
|
+
getRequests() {
|
|
23
|
+
return this.read().requests.map((r) => new req_1.Req(r));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.HttpFile = HttpFile;
|
|
27
|
+
class InvalidHttpFile extends Error {
|
|
28
|
+
constructor(file_path, reason = "") {
|
|
29
|
+
super(`Invalid http file ${file_path} ${reason}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.InvalidHttpFile = InvalidHttpFile;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { HttpMethod, valid_methods } from "./formatter";
|
|
2
|
+
export { HttpFile } from "./http_file";
|
|
3
|
+
export { Req } from "./req";
|
|
4
|
+
export { GetHttpFile } from "../steps/GetHttpFile";
|
|
5
|
+
export { PromptHttpFile } from "../steps/PromptHttpFile";
|
|
6
|
+
export { PromptHttpFileRequest } from "../steps/PromptHttpFileRequest";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PromptHttpFileRequest = exports.PromptHttpFile = exports.GetHttpFile = exports.Req = exports.HttpFile = exports.valid_methods = void 0;
|
|
4
|
+
var formatter_1 = require("./formatter");
|
|
5
|
+
Object.defineProperty(exports, "valid_methods", { enumerable: true, get: function () { return formatter_1.valid_methods; } });
|
|
6
|
+
var http_file_1 = require("./http_file");
|
|
7
|
+
Object.defineProperty(exports, "HttpFile", { enumerable: true, get: function () { return http_file_1.HttpFile; } });
|
|
8
|
+
var req_1 = require("./req");
|
|
9
|
+
Object.defineProperty(exports, "Req", { enumerable: true, get: function () { return req_1.Req; } });
|
|
10
|
+
var GetHttpFile_1 = require("../steps/GetHttpFile");
|
|
11
|
+
Object.defineProperty(exports, "GetHttpFile", { enumerable: true, get: function () { return GetHttpFile_1.GetHttpFile; } });
|
|
12
|
+
var PromptHttpFile_1 = require("../steps/PromptHttpFile");
|
|
13
|
+
Object.defineProperty(exports, "PromptHttpFile", { enumerable: true, get: function () { return PromptHttpFile_1.PromptHttpFile; } });
|
|
14
|
+
var PromptHttpFileRequest_1 = require("../steps/PromptHttpFileRequest");
|
|
15
|
+
Object.defineProperty(exports, "PromptHttpFileRequest", { enumerable: true, get: function () { return PromptHttpFileRequest_1.PromptHttpFileRequest; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Req } from "../req";
|
|
2
|
+
import { PostmanRequestBody } from "./types";
|
|
3
|
+
interface BodyStrategy {
|
|
4
|
+
supports(req: Req): boolean;
|
|
5
|
+
createBody(req: Req): any;
|
|
6
|
+
}
|
|
7
|
+
export declare class JsonBodyStrategy implements BodyStrategy {
|
|
8
|
+
supports(req: Req): boolean;
|
|
9
|
+
createBody(req: Req): PostmanRequestBody;
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JsonBodyStrategy = void 0;
|
|
4
|
+
class JsonBodyStrategy {
|
|
5
|
+
supports(req) {
|
|
6
|
+
return Boolean(req.body?.trim() && req.body.includes("{"));
|
|
7
|
+
}
|
|
8
|
+
createBody(req) {
|
|
9
|
+
return {
|
|
10
|
+
mode: "raw",
|
|
11
|
+
raw: req.body,
|
|
12
|
+
options: { raw: { language: "json" } }
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.JsonBodyStrategy = JsonBodyStrategy;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Postman = void 0;
|
|
4
|
+
const request_1 = require("./request");
|
|
5
|
+
const schema = "https://schema.getpostman.com/json/collection/v2.1.0/collection.json";
|
|
6
|
+
class Postman {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.files = [];
|
|
9
|
+
}
|
|
10
|
+
addFiles(...files) {
|
|
11
|
+
this.files.push(...files);
|
|
12
|
+
return this;
|
|
13
|
+
}
|
|
14
|
+
generate(name) {
|
|
15
|
+
const items = this.files.map(this.convertFile);
|
|
16
|
+
return { info: { name, schema }, item: items };
|
|
17
|
+
}
|
|
18
|
+
convertFile(file) {
|
|
19
|
+
return {
|
|
20
|
+
name: file.service,
|
|
21
|
+
item: file.getRequests().map((r) => new request_1.PostmanRequestAdapter(r).toPostmanItem())
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.Postman = Postman;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Req } from "../req";
|
|
2
|
+
import { PostmanRequestItem } from "./types";
|
|
3
|
+
export declare class PostmanRequestAdapter {
|
|
4
|
+
private readonly req;
|
|
5
|
+
constructor(req: Req);
|
|
6
|
+
toPostmanItem(): PostmanRequestItem;
|
|
7
|
+
private buildUrl;
|
|
8
|
+
private buildHeaders;
|
|
9
|
+
private buildBody;
|
|
10
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostmanRequestAdapter = void 0;
|
|
4
|
+
const req_1 = require("../req");
|
|
5
|
+
const body_1 = require("./body");
|
|
6
|
+
class PostmanRequestAdapter {
|
|
7
|
+
constructor(req) {
|
|
8
|
+
this.req = req;
|
|
9
|
+
}
|
|
10
|
+
toPostmanItem() {
|
|
11
|
+
const builder = new PostmanRequestBuilder();
|
|
12
|
+
builder
|
|
13
|
+
.setName(`${this.req.method.toUpperCase()} ${this.req.pathname}`)
|
|
14
|
+
.setMethod(this.req.method.toUpperCase())
|
|
15
|
+
.setUrl(this.buildUrl())
|
|
16
|
+
.setHeaders(...this.buildHeaders());
|
|
17
|
+
const body = this.buildBody();
|
|
18
|
+
if (body)
|
|
19
|
+
builder.setBody(body);
|
|
20
|
+
return builder.build();
|
|
21
|
+
}
|
|
22
|
+
buildUrl() {
|
|
23
|
+
const path = this.req.pathname.replace(req_1.varRegex, ":$1");
|
|
24
|
+
return {
|
|
25
|
+
raw: path,
|
|
26
|
+
protocol: "http",
|
|
27
|
+
host: [path.split("/")[1] || ""],
|
|
28
|
+
path: path.split("/").slice(2),
|
|
29
|
+
query: Object.entries(this.req.params ?? {})
|
|
30
|
+
.filter(([, v]) => v)
|
|
31
|
+
.map(([key, val]) => ({
|
|
32
|
+
key,
|
|
33
|
+
value: val.toString()
|
|
34
|
+
}))
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
buildHeaders() {
|
|
38
|
+
return Object.entries(this.req.headers ?? {})
|
|
39
|
+
.filter(([, v]) => v)
|
|
40
|
+
.map(([key, value]) => ({ key, value })); // we are filtering nulls
|
|
41
|
+
}
|
|
42
|
+
buildBody() {
|
|
43
|
+
// TODO: probably iterate over available strategies
|
|
44
|
+
// to identify which to use
|
|
45
|
+
const strategy = new body_1.JsonBodyStrategy();
|
|
46
|
+
if (strategy.supports(this.req))
|
|
47
|
+
return strategy.createBody(this.req);
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.PostmanRequestAdapter = PostmanRequestAdapter;
|
|
52
|
+
class PostmanRequestBuilder {
|
|
53
|
+
constructor() {
|
|
54
|
+
this.item = {};
|
|
55
|
+
}
|
|
56
|
+
setName(name) {
|
|
57
|
+
this.item.name = name;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
setMethod(method) {
|
|
61
|
+
this.item.request ??= { method };
|
|
62
|
+
this.item.request.method = method;
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
setUrl(url) {
|
|
66
|
+
this.item.request ??= { url };
|
|
67
|
+
this.item.request.url = url;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
setHeaders(...header) {
|
|
71
|
+
this.item.request ??= { header };
|
|
72
|
+
this.item.request.header = header;
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
setBody(body) {
|
|
76
|
+
this.item.request ??= { body };
|
|
77
|
+
this.item.request.body = body;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
build() {
|
|
81
|
+
// TODO: probably validate this has been properly built
|
|
82
|
+
return this.item;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { JsonFile } from "@maro/maro";
|
|
2
|
+
export type PostmanContent = {
|
|
3
|
+
info: PostmanInfo;
|
|
4
|
+
item: PostmanItem[];
|
|
5
|
+
event?: PostmanEvent[];
|
|
6
|
+
variable?: PostmanVariable[];
|
|
7
|
+
};
|
|
8
|
+
type PostmanInfo = {
|
|
9
|
+
name: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
schema: string;
|
|
12
|
+
};
|
|
13
|
+
type PostmanItem = PostmanFolder | PostmanRequestItem;
|
|
14
|
+
export type PostmanFolder = {
|
|
15
|
+
name: string;
|
|
16
|
+
description?: string | PostmanDescription;
|
|
17
|
+
item: PostmanItem[];
|
|
18
|
+
event?: PostmanEvent[];
|
|
19
|
+
variable?: PostmanVariable[];
|
|
20
|
+
};
|
|
21
|
+
export type PostmanRequestItem = {
|
|
22
|
+
name: string;
|
|
23
|
+
request: PostmanRequest;
|
|
24
|
+
event?: PostmanEvent[];
|
|
25
|
+
};
|
|
26
|
+
type PostmanRequest = {
|
|
27
|
+
method?: string;
|
|
28
|
+
header?: PostmanHeader[];
|
|
29
|
+
url?: PostmanUrl;
|
|
30
|
+
body?: PostmanRequestBody;
|
|
31
|
+
description?: string | PostmanDescription;
|
|
32
|
+
};
|
|
33
|
+
export type PostmanHeader = {
|
|
34
|
+
key: string;
|
|
35
|
+
value: string;
|
|
36
|
+
type?: string;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
};
|
|
39
|
+
export type PostmanUrl = {
|
|
40
|
+
raw?: string;
|
|
41
|
+
protocol?: string;
|
|
42
|
+
host?: string[];
|
|
43
|
+
path?: string[];
|
|
44
|
+
port?: string;
|
|
45
|
+
query?: PostmanQueryParam[];
|
|
46
|
+
variable?: PostmanVariable[];
|
|
47
|
+
};
|
|
48
|
+
type PostmanQueryParam = {
|
|
49
|
+
key: string;
|
|
50
|
+
value?: string;
|
|
51
|
+
description?: string | PostmanDescription;
|
|
52
|
+
disabled?: boolean;
|
|
53
|
+
};
|
|
54
|
+
type PostmanVariable = {
|
|
55
|
+
key: string;
|
|
56
|
+
value?: any;
|
|
57
|
+
description?: string | PostmanDescription;
|
|
58
|
+
type?: string;
|
|
59
|
+
disabled?: boolean;
|
|
60
|
+
};
|
|
61
|
+
export type PostmanRequestBody = {
|
|
62
|
+
mode: "raw" | "urlencoded" | "formdata" | "file" | "graphql";
|
|
63
|
+
raw?: string;
|
|
64
|
+
graphql?: {
|
|
65
|
+
query: string;
|
|
66
|
+
variables?: any;
|
|
67
|
+
};
|
|
68
|
+
urlencoded?: PostmanKeyValue[];
|
|
69
|
+
formdata?: (PostmanKeyValue | PostmanFormDataFile)[];
|
|
70
|
+
file?: {
|
|
71
|
+
src?: string;
|
|
72
|
+
};
|
|
73
|
+
options?: any;
|
|
74
|
+
};
|
|
75
|
+
type PostmanKeyValue = {
|
|
76
|
+
key: string;
|
|
77
|
+
value?: string;
|
|
78
|
+
type?: string;
|
|
79
|
+
disabled?: boolean;
|
|
80
|
+
description?: string | PostmanDescription;
|
|
81
|
+
};
|
|
82
|
+
type PostmanFormDataFile = {
|
|
83
|
+
key: string;
|
|
84
|
+
src: string | string[];
|
|
85
|
+
type: "file";
|
|
86
|
+
disabled?: boolean;
|
|
87
|
+
description?: string | PostmanDescription;
|
|
88
|
+
};
|
|
89
|
+
type PostmanEvent = {
|
|
90
|
+
listen: "test" | "prerequest";
|
|
91
|
+
script: PostmanScript;
|
|
92
|
+
};
|
|
93
|
+
type PostmanScript = {
|
|
94
|
+
id?: string;
|
|
95
|
+
type?: string;
|
|
96
|
+
exec: string[];
|
|
97
|
+
};
|
|
98
|
+
type PostmanDescription = {
|
|
99
|
+
content: string;
|
|
100
|
+
type?: string;
|
|
101
|
+
version?: string;
|
|
102
|
+
};
|
|
103
|
+
export declare class PostmanFile extends JsonFile<PostmanContent> {
|
|
104
|
+
}
|
|
105
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Choice } from "@maro/maro";
|
|
2
|
+
import { HttpMethod, ReqObj } from "./formatter";
|
|
3
|
+
export declare const varRegex: RegExp;
|
|
4
|
+
export declare class Req implements ReqObj {
|
|
5
|
+
method: HttpMethod;
|
|
6
|
+
params: Record<string, string | null> | null;
|
|
7
|
+
pathname: string;
|
|
8
|
+
headers: Record<string, string | null> | null;
|
|
9
|
+
body: string;
|
|
10
|
+
variables: Record<string, string>;
|
|
11
|
+
constructor(reqObj: ReqObj, variables?: {});
|
|
12
|
+
send(url: string, variables?: Record<string, string>): Promise<{
|
|
13
|
+
res: import("axios").AxiosResponse<any, any>;
|
|
14
|
+
config: {
|
|
15
|
+
method: "GET" | "PUT" | "POST" | "PATCH" | "DELETE";
|
|
16
|
+
params: {
|
|
17
|
+
[k: string]: string | undefined;
|
|
18
|
+
};
|
|
19
|
+
headers: Record<string, string | null>;
|
|
20
|
+
url: string;
|
|
21
|
+
data: any;
|
|
22
|
+
};
|
|
23
|
+
}>;
|
|
24
|
+
replaceVariables(string: string, options?: {
|
|
25
|
+
quote_strings?: boolean;
|
|
26
|
+
variables?: Record<string, string | null>;
|
|
27
|
+
}): string;
|
|
28
|
+
private replaceableVariables;
|
|
29
|
+
toString(): string;
|
|
30
|
+
toChoice(): Choice;
|
|
31
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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.Req = exports.varRegex = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
exports.varRegex = /{{(.*?)}}/g;
|
|
9
|
+
class Req {
|
|
10
|
+
constructor(reqObj, variables = {}) {
|
|
11
|
+
this.pathname = reqObj.pathname;
|
|
12
|
+
this.params = reqObj.params;
|
|
13
|
+
this.method = reqObj.method;
|
|
14
|
+
this.headers = reqObj.headers;
|
|
15
|
+
this.body = reqObj.body;
|
|
16
|
+
this.variables = variables;
|
|
17
|
+
}
|
|
18
|
+
async send(url, variables) {
|
|
19
|
+
const vars = this.replaceableVariables();
|
|
20
|
+
const replace = variables ? { ...this.variables, ...Object.fromEntries(vars.map((v) => [v, variables[v] ?? null])) } : this.variables;
|
|
21
|
+
const req_config = {
|
|
22
|
+
method: this.method,
|
|
23
|
+
params: Object.fromEntries(Object.entries(this.params ?? {}).map(([k, v]) => [k, v ? this.replaceVariables(v, { variables: replace }) : undefined])),
|
|
24
|
+
headers: this.headers ?? {},
|
|
25
|
+
url: `${url}${this.replaceVariables(this.pathname, { variables: replace })}`,
|
|
26
|
+
data: JSON.parse(this.replaceVariables(this.body, { quote_strings: true, variables: replace }) ?? "{}")
|
|
27
|
+
};
|
|
28
|
+
const res = await axios_1.default.request(req_config);
|
|
29
|
+
return { res, config: req_config };
|
|
30
|
+
}
|
|
31
|
+
replaceVariables(string, options) {
|
|
32
|
+
let res = string;
|
|
33
|
+
Object.entries({ ...this.variables, ...options?.variables })
|
|
34
|
+
.forEach(([k, v]) => {
|
|
35
|
+
const value = options?.quote_strings && typeof v === "string" ? `"${v.replaceAll("\"", "")}"` : v;
|
|
36
|
+
res = res?.replaceAll(`{{${k}}}`, value ?? "null");
|
|
37
|
+
});
|
|
38
|
+
return res;
|
|
39
|
+
}
|
|
40
|
+
replaceableVariables() {
|
|
41
|
+
const res = [];
|
|
42
|
+
res.push(...Array.from(this.pathname.matchAll(exports.varRegex)).map((m) => m?.[1] ?? ""), ...Array.from(this.body.matchAll(exports.varRegex)).map((m) => m?.[1] ?? ""), ...this.params
|
|
43
|
+
? Object.entries(this.params)
|
|
44
|
+
.flatMap(([, v]) => v ? Array.from(v.matchAll(exports.varRegex)).map((m) => m?.[1] ?? "") : [])
|
|
45
|
+
: []);
|
|
46
|
+
return Array.from(new Set(res));
|
|
47
|
+
}
|
|
48
|
+
toString() {
|
|
49
|
+
return `${this.method} ${this.pathname}`;
|
|
50
|
+
}
|
|
51
|
+
toChoice() {
|
|
52
|
+
return { name: this.toString() };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.Req = Req;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { YamlFile } from "@maro/maro";
|
|
2
|
+
export type SwaggerContent = {
|
|
3
|
+
openapi: string;
|
|
4
|
+
info: SwaggerInfo;
|
|
5
|
+
paths: Record<string, SwaggerPathItem>;
|
|
6
|
+
};
|
|
7
|
+
type SwaggerInfo = {
|
|
8
|
+
title: string;
|
|
9
|
+
version: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
};
|
|
12
|
+
export type SwaggerPathItem = {
|
|
13
|
+
get?: SwaggerOperation;
|
|
14
|
+
post?: SwaggerOperation;
|
|
15
|
+
put?: SwaggerOperation;
|
|
16
|
+
delete?: SwaggerOperation;
|
|
17
|
+
patch?: SwaggerOperation;
|
|
18
|
+
options?: SwaggerOperation;
|
|
19
|
+
head?: SwaggerOperation;
|
|
20
|
+
trace?: SwaggerOperation;
|
|
21
|
+
parameters?: SwaggerParameter[];
|
|
22
|
+
};
|
|
23
|
+
export type SwaggerOperation = {
|
|
24
|
+
tags?: string[];
|
|
25
|
+
summary?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
parameters?: SwaggerParameter[];
|
|
28
|
+
requestBody?: SwaggerRequestBody;
|
|
29
|
+
responses: Record<string, SwaggerResponse>;
|
|
30
|
+
};
|
|
31
|
+
export type SwaggerParameter = {
|
|
32
|
+
name: string;
|
|
33
|
+
in: "query" | "header" | "path" | "cookie";
|
|
34
|
+
required?: boolean;
|
|
35
|
+
description?: string;
|
|
36
|
+
schema?: SwaggerSchema;
|
|
37
|
+
};
|
|
38
|
+
export type SwaggerRequestBody = {
|
|
39
|
+
description?: string;
|
|
40
|
+
required?: boolean;
|
|
41
|
+
content: Record<string, SwaggerMediaType>;
|
|
42
|
+
};
|
|
43
|
+
export type SwaggerResponse = {
|
|
44
|
+
description: string;
|
|
45
|
+
headers?: Record<string, SwaggerHeader>;
|
|
46
|
+
content?: Record<string, SwaggerMediaType>;
|
|
47
|
+
};
|
|
48
|
+
type SwaggerHeader = {
|
|
49
|
+
description?: string;
|
|
50
|
+
required?: boolean;
|
|
51
|
+
schema?: SwaggerSchema;
|
|
52
|
+
};
|
|
53
|
+
type SwaggerMediaType = {
|
|
54
|
+
schema?: SwaggerSchema;
|
|
55
|
+
example?: any;
|
|
56
|
+
examples?: Record<string, any>;
|
|
57
|
+
};
|
|
58
|
+
type SwaggerSchema = {
|
|
59
|
+
type?: string;
|
|
60
|
+
format?: string;
|
|
61
|
+
properties?: Record<string, SwaggerSchema>;
|
|
62
|
+
items?: SwaggerSchema;
|
|
63
|
+
required?: string[];
|
|
64
|
+
enum?: string[];
|
|
65
|
+
nullable?: boolean;
|
|
66
|
+
description?: string;
|
|
67
|
+
default?: any;
|
|
68
|
+
$ref?: string;
|
|
69
|
+
};
|
|
70
|
+
export declare class SwaggerFile extends YamlFile<SwaggerContent> {
|
|
71
|
+
}
|
|
72
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AppRepo } from "@maro/maro";
|
|
2
|
+
import { HttpFile } from "../http_file";
|
|
3
|
+
import { SwaggerFile } from "./file";
|
|
4
|
+
export declare class Swagger {
|
|
5
|
+
private readonly file;
|
|
6
|
+
private readonly app_repo;
|
|
7
|
+
constructor(file: HttpFile, app_repo: AppRepo);
|
|
8
|
+
generate(): SwaggerFile;
|
|
9
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Swagger = void 0;
|
|
4
|
+
const params_1 = require("./params");
|
|
5
|
+
const paths_1 = require("./paths");
|
|
6
|
+
const request_1 = require("./request");
|
|
7
|
+
const file_1 = require("./file");
|
|
8
|
+
class Swagger {
|
|
9
|
+
constructor(file, app_repo) {
|
|
10
|
+
this.file = file;
|
|
11
|
+
this.app_repo = app_repo;
|
|
12
|
+
}
|
|
13
|
+
generate() {
|
|
14
|
+
const { description, version } = this.app_repo.getPackage();
|
|
15
|
+
const pathsBuilder = new paths_1.SwaggerPaths();
|
|
16
|
+
for (const req of this.file.getRequests()) {
|
|
17
|
+
const adapter = new request_1.SwaggerRequestAdapter(req, new params_1.RegexParamExtractionStrategy());
|
|
18
|
+
pathsBuilder.add(adapter.toSwaggerItem());
|
|
19
|
+
}
|
|
20
|
+
const content = {
|
|
21
|
+
openapi: "3.0.2",
|
|
22
|
+
info: {
|
|
23
|
+
title: this.file.service,
|
|
24
|
+
description,
|
|
25
|
+
version
|
|
26
|
+
},
|
|
27
|
+
paths: pathsBuilder.build()
|
|
28
|
+
};
|
|
29
|
+
const file = this.app_repo.dir.sub("swagger").sub("docs").createFile("specification.yaml");
|
|
30
|
+
const swagger = new file_1.SwaggerFile(file.path);
|
|
31
|
+
swagger.write(content);
|
|
32
|
+
return swagger;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.Swagger = Swagger;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SwaggerOperation } from "./file";
|
|
2
|
+
export declare class SwaggerOperationBuilder {
|
|
3
|
+
private parameters;
|
|
4
|
+
private requestBody?;
|
|
5
|
+
private responses;
|
|
6
|
+
withPathParams(names: string[]): this;
|
|
7
|
+
withQueryParams(names: string[]): this;
|
|
8
|
+
withJsonRequestBody(): this;
|
|
9
|
+
withDefaultResponses(): this;
|
|
10
|
+
build(): SwaggerOperation;
|
|
11
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SwaggerOperationBuilder = void 0;
|
|
4
|
+
class SwaggerOperationBuilder {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.parameters = [];
|
|
7
|
+
this.responses = {};
|
|
8
|
+
}
|
|
9
|
+
withPathParams(names) {
|
|
10
|
+
this.parameters.push(...names.map((name) => ({
|
|
11
|
+
name,
|
|
12
|
+
in: "path",
|
|
13
|
+
required: true,
|
|
14
|
+
schema: { type: "string" }
|
|
15
|
+
})));
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
withQueryParams(names) {
|
|
19
|
+
this.parameters.push(...names.map((name) => ({
|
|
20
|
+
name,
|
|
21
|
+
in: "query",
|
|
22
|
+
required: false,
|
|
23
|
+
schema: { type: "string" }
|
|
24
|
+
})));
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
withJsonRequestBody() {
|
|
28
|
+
this.requestBody = {
|
|
29
|
+
content: {
|
|
30
|
+
"application/json": {
|
|
31
|
+
schema: {
|
|
32
|
+
type: "object"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
withDefaultResponses() {
|
|
40
|
+
this.responses = {
|
|
41
|
+
200: { description: "OK" },
|
|
42
|
+
400: { description: "BAD_REQUEST" },
|
|
43
|
+
500: { description: "INTERNAL_SERVER_ERROR" }
|
|
44
|
+
};
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
build() {
|
|
48
|
+
const operation = {
|
|
49
|
+
parameters: this.parameters,
|
|
50
|
+
responses: this.responses
|
|
51
|
+
};
|
|
52
|
+
if (this.requestBody) {
|
|
53
|
+
operation.requestBody = this.requestBody;
|
|
54
|
+
}
|
|
55
|
+
return operation;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.SwaggerOperationBuilder = SwaggerOperationBuilder;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Req } from "../req";
|
|
2
|
+
export interface ExtractedParams {
|
|
3
|
+
pathParams: string[];
|
|
4
|
+
queryParams: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface ParamExtractionStrategy {
|
|
7
|
+
extract(req: Req): ExtractedParams;
|
|
8
|
+
}
|
|
9
|
+
export declare class RegexParamExtractionStrategy implements ParamExtractionStrategy {
|
|
10
|
+
extract(req: Req): ExtractedParams;
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RegexParamExtractionStrategy = void 0;
|
|
4
|
+
const req_1 = require("../req");
|
|
5
|
+
class RegexParamExtractionStrategy {
|
|
6
|
+
extract(req) {
|
|
7
|
+
const pathParams = Array.from(req.pathname.matchAll(req_1.varRegex)).map((m) => m[1]).filter((p) => Boolean(p));
|
|
8
|
+
const queryParams = Object.entries(req.params ?? {})
|
|
9
|
+
.flatMap(([key, value]) => {
|
|
10
|
+
if (!value)
|
|
11
|
+
return [];
|
|
12
|
+
return value.match(req_1.varRegex) ? [key] : [];
|
|
13
|
+
});
|
|
14
|
+
return { pathParams, queryParams };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.RegexParamExtractionStrategy = RegexParamExtractionStrategy;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SwaggerPaths = void 0;
|
|
4
|
+
class SwaggerPaths {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.paths = {};
|
|
7
|
+
}
|
|
8
|
+
add(fragment) {
|
|
9
|
+
for (const [path, methods] of Object.entries(fragment)) {
|
|
10
|
+
this.paths[path] ??= {};
|
|
11
|
+
Object.assign(this.paths[path], methods);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
build() {
|
|
15
|
+
return this.paths;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.SwaggerPaths = SwaggerPaths;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Req } from "../req";
|
|
2
|
+
import { SwaggerPathItem } from "./file";
|
|
3
|
+
import { ParamExtractionStrategy } from "./params";
|
|
4
|
+
export declare class SwaggerRequestAdapter {
|
|
5
|
+
private req;
|
|
6
|
+
private readonly paramStrategy;
|
|
7
|
+
constructor(req: Req, paramStrategy: ParamExtractionStrategy);
|
|
8
|
+
toSwaggerItem(): SwaggerPathItem;
|
|
9
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SwaggerRequestAdapter = void 0;
|
|
4
|
+
const req_1 = require("../req");
|
|
5
|
+
const operation_1 = require("./operation");
|
|
6
|
+
class SwaggerRequestAdapter {
|
|
7
|
+
constructor(req, paramStrategy) {
|
|
8
|
+
this.req = req;
|
|
9
|
+
this.paramStrategy = paramStrategy;
|
|
10
|
+
}
|
|
11
|
+
toSwaggerItem() {
|
|
12
|
+
const path = this.req.pathname.replace(req_1.varRegex, "{$1}");
|
|
13
|
+
const { pathParams, queryParams } = this.paramStrategy.extract(this.req);
|
|
14
|
+
const builder = new operation_1.SwaggerOperationBuilder()
|
|
15
|
+
.withPathParams(pathParams)
|
|
16
|
+
.withQueryParams(queryParams)
|
|
17
|
+
.withDefaultResponses();
|
|
18
|
+
if (this.req.body && this.req.body.trim() !== "{}") {
|
|
19
|
+
builder.withJsonRequestBody();
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
[path]: {
|
|
23
|
+
[this.req.method.toLowerCase()]: builder.build()
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.SwaggerRequestAdapter = SwaggerRequestAdapter;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AppRepo, ExecutionContext, WorkflowStep } from "@maro/maro";
|
|
2
|
+
import { HttpFile } from "../lib/http_file";
|
|
3
|
+
type Reads = {
|
|
4
|
+
app_repo: AppRepo;
|
|
5
|
+
};
|
|
6
|
+
type Writes = {
|
|
7
|
+
http_file?: HttpFile;
|
|
8
|
+
};
|
|
9
|
+
type Options = {};
|
|
10
|
+
export declare class GetHttpFile extends WorkflowStep<Reads, Writes, Options> {
|
|
11
|
+
run(ctx: ExecutionContext, { app_repo }: Reads): Promise<{
|
|
12
|
+
http_file?: undefined;
|
|
13
|
+
} | {
|
|
14
|
+
http_file: HttpFile;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GetHttpFile = void 0;
|
|
4
|
+
const maro_1 = require("@maro/maro");
|
|
5
|
+
const http_file_1 = require("../lib/http_file");
|
|
6
|
+
class GetHttpFile extends maro_1.WorkflowStep {
|
|
7
|
+
async run(ctx, { app_repo }) {
|
|
8
|
+
const log = ctx.logger;
|
|
9
|
+
new maro_1.ValidateConfig({ keys: ["http.collection"] }).run();
|
|
10
|
+
const collection = maro_1.Config.getView().get("http.collection");
|
|
11
|
+
const collections = new maro_1.Dir(collection).readFiles();
|
|
12
|
+
const files = collections.map((n) => new http_file_1.HttpFile(n.path));
|
|
13
|
+
const { name } = await app_repo.getInfo();
|
|
14
|
+
const http_file = files.find((f) => f.service === name);
|
|
15
|
+
if (!http_file) {
|
|
16
|
+
log.error(`Could not find http_file for app ${name}`);
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
return { http_file };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.GetHttpFile = GetHttpFile;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ExecutionContext, WorkflowStep } from "@maro/maro";
|
|
2
|
+
import { HttpFile } from "../lib/http_file";
|
|
3
|
+
type Reads = {};
|
|
4
|
+
type Writes = {
|
|
5
|
+
http_file: HttpFile;
|
|
6
|
+
};
|
|
7
|
+
type Options = {};
|
|
8
|
+
export declare class PromptHttpFile extends WorkflowStep<Reads, Writes, Options> {
|
|
9
|
+
run(ctx: ExecutionContext): Promise<{
|
|
10
|
+
http_file: HttpFile;
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PromptHttpFile = void 0;
|
|
4
|
+
const maro_1 = require("@maro/maro");
|
|
5
|
+
const http_file_1 = require("../lib/http_file");
|
|
6
|
+
class PromptHttpFile extends maro_1.WorkflowStep {
|
|
7
|
+
async run(ctx) {
|
|
8
|
+
await new maro_1.ValidateConfig({ keys: ["http.collection"] }).run();
|
|
9
|
+
const config = maro_1.Config.getView();
|
|
10
|
+
const collection = new maro_1.Dir(config.get("http.collection"));
|
|
11
|
+
const collections = collection.readFiles();
|
|
12
|
+
const files = collections.map((n) => new http_file_1.HttpFile(n.path));
|
|
13
|
+
const http_file = await ctx.ui.promptChoice(files);
|
|
14
|
+
return { http_file };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.PromptHttpFile = PromptHttpFile;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ExecutionContext, WorkflowOptions, WorkflowStep } from "@maro/maro";
|
|
2
|
+
import { HttpFile } from "../lib/http_file";
|
|
3
|
+
import { Req } from "../lib/req";
|
|
4
|
+
type Reads = {
|
|
5
|
+
http_file: HttpFile;
|
|
6
|
+
};
|
|
7
|
+
type Writes<Multiple> = Multiple extends true ? {
|
|
8
|
+
requests: Req[];
|
|
9
|
+
} : {
|
|
10
|
+
request: Req;
|
|
11
|
+
};
|
|
12
|
+
type Options<Multiple> = {
|
|
13
|
+
multiple?: Multiple;
|
|
14
|
+
};
|
|
15
|
+
export declare class PromptHttpFileRequest<Multiple extends boolean = false> extends WorkflowStep<Reads, Writes<Multiple>, Options<Multiple>> {
|
|
16
|
+
options?: WorkflowOptions<Options<Multiple>, Writes<Multiple>> | undefined;
|
|
17
|
+
constructor(options?: WorkflowOptions<Options<Multiple>, Writes<Multiple>> | undefined);
|
|
18
|
+
run(ctx: ExecutionContext, { http_file }: Reads): Promise<Writes<Multiple>>;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PromptHttpFileRequest = void 0;
|
|
4
|
+
const maro_1 = require("@maro/maro");
|
|
5
|
+
class PromptHttpFileRequest extends maro_1.WorkflowStep {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
super(options);
|
|
8
|
+
this.options = options;
|
|
9
|
+
}
|
|
10
|
+
async run(ctx, { http_file }) {
|
|
11
|
+
const multiple = this.options?.multiple;
|
|
12
|
+
const requests = http_file.getRequests()
|
|
13
|
+
.filter((r) => r.pathname !== "/health");
|
|
14
|
+
const service = http_file.service;
|
|
15
|
+
const services = await ctx.ui.promptChoice(requests, {
|
|
16
|
+
multiple,
|
|
17
|
+
message: `Choose request for ${service}`
|
|
18
|
+
});
|
|
19
|
+
if (Array.isArray(services))
|
|
20
|
+
return { requests: services };
|
|
21
|
+
return { request: services };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.PromptHttpFileRequest = PromptHttpFileRequest;
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "maro-plugin-http",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"main": "./dist/src/lib/index.js",
|
|
5
|
+
"types": "./dist/src/lib/index.d.ts",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc --project ./tsconfig.json",
|
|
12
|
+
"start": "node ./dist/src/index.js",
|
|
13
|
+
"clean": "rm -rf node_modules package-lock.json",
|
|
14
|
+
"import": "npm run clean; cp ../../maro/maro-2.0.0.tgz ./maro-2.0.0.tgz; npm install"
|
|
15
|
+
},
|
|
16
|
+
"maro": {
|
|
17
|
+
"plugin": "./dist/src/index.js"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"zod": "3.25.57"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "ISC",
|
|
25
|
+
"description": "",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"typescript": "^5.9.3",
|
|
28
|
+
"@typescript-eslint/eslint-plugin": "8.54.0",
|
|
29
|
+
"@stylistic/eslint-plugin": "2.6.5",
|
|
30
|
+
"eslint": "8.57.1",
|
|
31
|
+
"eslint-plugin-de-morgan": "2.0.0",
|
|
32
|
+
"eslint-plugin-import": "2.32.0",
|
|
33
|
+
"eslint-plugin-math": "0.13.1",
|
|
34
|
+
"eslint-plugin-promise": "7.2.1",
|
|
35
|
+
"eslint-plugin-unused-imports": "4.3.0"
|
|
36
|
+
}
|
|
37
|
+
}
|