snapshot of https://github.com/mountain-loop/yaak.git (main, 2026-09-23; blobs > 131072B omitted)
This commit is contained in:
13
plugins/importer-postman/package.json
Normal file
13
plugins/importer-postman/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@yaak/importer-postman",
|
||||
"displayName": "Postman Importer",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Import collections from Postman",
|
||||
"main": "./build/index.js",
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"test": "vp test --run tests"
|
||||
}
|
||||
}
|
||||
659
plugins/importer-postman/src/index.ts
Normal file
659
plugins/importer-postman/src/index.ts
Normal file
@@ -0,0 +1,659 @@
|
||||
/* oxlint-disable no-base-to-string */
|
||||
import type {
|
||||
Context,
|
||||
Environment,
|
||||
Folder,
|
||||
HttpRequest,
|
||||
HttpRequestHeader,
|
||||
HttpUrlParameter,
|
||||
PartialImportResources,
|
||||
PluginDefinition,
|
||||
Workspace,
|
||||
} from "@yaakapp/api";
|
||||
import type { ImportPluginResponse } from "@yaakapp/api/lib/plugins/ImporterPlugin";
|
||||
|
||||
const POSTMAN_2_1_0_SCHEMA = "https://schema.getpostman.com/json/collection/v2.1.0/collection.json";
|
||||
const POSTMAN_2_0_0_SCHEMA = "https://schema.getpostman.com/json/collection/v2.0.0/collection.json";
|
||||
const VALID_SCHEMAS = [POSTMAN_2_0_0_SCHEMA, POSTMAN_2_1_0_SCHEMA];
|
||||
|
||||
// Both products happen to spell these the same way, but the names are written
|
||||
// out so anything Yaak's oauth1 plugin can't offer as a select option lands on
|
||||
// the default instead of being passed straight through.
|
||||
const OAUTH1_SIGNATURE_METHODS: Record<string, string> = {
|
||||
"HMAC-SHA1": "HMAC-SHA1",
|
||||
"HMAC-SHA256": "HMAC-SHA256",
|
||||
"HMAC-SHA512": "HMAC-SHA512",
|
||||
"RSA-SHA1": "RSA-SHA1",
|
||||
"RSA-SHA256": "RSA-SHA256",
|
||||
"RSA-SHA512": "RSA-SHA512",
|
||||
PLAINTEXT: "PLAINTEXT",
|
||||
};
|
||||
const DEFAULT_OAUTH1_SIGNATURE_METHOD = "HMAC-SHA1";
|
||||
|
||||
// Postman's dynamic variables that a Yaak template function reproduces exactly.
|
||||
// The faker-backed ones ({{$randomFirstName}} and friends) have no equivalent and
|
||||
// are left alone. Arguments have to be single-quoted; the template parser reads a
|
||||
// double-quoted call as raw text.
|
||||
const POSTMAN_DYNAMIC_VARIABLES: Record<string, string> = {
|
||||
$guid: "uuid.v4()",
|
||||
$randomUUID: "uuid.v4()",
|
||||
$timestamp: "timestamp.unix()",
|
||||
$isoTimestamp: "timestamp.iso8601()",
|
||||
// Postman's $randomInt is an integer from 0 to 1000
|
||||
$randomInt: "random.range(min='0',max='1000',decimals='0')",
|
||||
};
|
||||
|
||||
type AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>;
|
||||
|
||||
interface ExportResources {
|
||||
workspaces: AtLeast<Workspace, "name" | "id" | "model">[];
|
||||
environments: AtLeast<Environment, "name" | "id" | "model" | "workspaceId">[];
|
||||
httpRequests: AtLeast<HttpRequest, "name" | "id" | "model" | "workspaceId">[];
|
||||
folders: AtLeast<Folder, "name" | "id" | "model" | "workspaceId">[];
|
||||
}
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
importer: {
|
||||
name: "Postman",
|
||||
description: "Import postman collections",
|
||||
onImport(_ctx: Context, args: { text: string }) {
|
||||
return convertPostman(args.text);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function convertPostman(contents: string): ImportPluginResponse | undefined {
|
||||
const root = parseJSONToRecord(contents);
|
||||
if (root == null) return;
|
||||
|
||||
const info = toRecord(root.info);
|
||||
const isValidSchema = VALID_SCHEMAS.includes(
|
||||
typeof info.schema === "string" ? info.schema : "n/a",
|
||||
);
|
||||
if (!isValidSchema || !Array.isArray(root.item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const globalAuth = importAuth(root.auth);
|
||||
|
||||
const sourceKeys: Record<string, string> = {};
|
||||
const trackSourceKey = (modelId: string, v: Record<string, unknown>, prefix: string) => {
|
||||
const id = v.id ?? v._postman_id;
|
||||
if (typeof id === "string" && id !== "") sourceKeys[modelId] = `${prefix}:${id}`;
|
||||
};
|
||||
|
||||
const exportResources: ExportResources = {
|
||||
workspaces: [],
|
||||
environments: [],
|
||||
httpRequests: [],
|
||||
folders: [],
|
||||
};
|
||||
|
||||
const workspace: ExportResources["workspaces"][0] = {
|
||||
model: "workspace",
|
||||
id: generateId("workspace"),
|
||||
name: info.name ? String(info.name) : "Postman Import",
|
||||
description: importDescription(info.description),
|
||||
...globalAuth,
|
||||
};
|
||||
trackSourceKey(workspace.id, info, "collection");
|
||||
exportResources.workspaces.push(workspace);
|
||||
|
||||
// Create the base environment
|
||||
const environment: ExportResources["environments"][0] = {
|
||||
model: "environment",
|
||||
id: generateId("environment"),
|
||||
name: "Global Variables",
|
||||
workspaceId: workspace.id,
|
||||
parentModel: "workspace",
|
||||
parentId: null,
|
||||
variables:
|
||||
toArray<{ key: string; value: string }>(root.variable).map((v) => ({
|
||||
name: v.key,
|
||||
value: v.value,
|
||||
})) ?? [],
|
||||
};
|
||||
exportResources.environments.push(environment);
|
||||
|
||||
let sortPriorityIndex = 0;
|
||||
const importItem = (v: Record<string, unknown>, folderId: string | null = null) => {
|
||||
if (typeof v.name === "string" && Array.isArray(v.item)) {
|
||||
const folder: ExportResources["folders"][0] = {
|
||||
model: "folder",
|
||||
sortPriority: sortPriorityIndex++,
|
||||
workspaceId: workspace.id,
|
||||
id: generateId("folder"),
|
||||
name: v.name,
|
||||
folderId,
|
||||
...importAuth(v.auth),
|
||||
};
|
||||
trackSourceKey(folder.id, v, "item");
|
||||
exportResources.folders.push(folder);
|
||||
for (const child of v.item) {
|
||||
importItem(child, folder.id);
|
||||
}
|
||||
} else if (typeof v.name === "string" && "request" in v) {
|
||||
const r = toRecord(v.request);
|
||||
const bodyPatch = importBody(r.body);
|
||||
const requestAuth = importAuth(r.auth);
|
||||
|
||||
const headers: HttpRequestHeader[] = toArray<{
|
||||
key: string;
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
}>(r.header).map((h) => {
|
||||
return {
|
||||
name: h.key,
|
||||
value: h.value,
|
||||
enabled: !h.disabled,
|
||||
};
|
||||
});
|
||||
|
||||
// Add body headers only if they don't already exist
|
||||
for (const bodyPatchHeader of bodyPatch.headers) {
|
||||
const existingHeader = headers.find(
|
||||
(h) => h.name.toLowerCase() === bodyPatchHeader.name.toLowerCase(),
|
||||
);
|
||||
if (existingHeader) {
|
||||
continue;
|
||||
}
|
||||
headers.push(bodyPatchHeader);
|
||||
}
|
||||
|
||||
const { url, urlParameters } = convertUrl(r.url);
|
||||
|
||||
const request: ExportResources["httpRequests"][0] = {
|
||||
model: "http_request",
|
||||
id: generateId("http_request"),
|
||||
workspaceId: workspace.id,
|
||||
folderId,
|
||||
name: v.name,
|
||||
description: importDescription(r.description),
|
||||
method: typeof r.method === "string" ? r.method : "GET",
|
||||
url,
|
||||
urlParameters,
|
||||
body: bodyPatch.body,
|
||||
bodyType: bodyPatch.bodyType,
|
||||
sortPriority: sortPriorityIndex++,
|
||||
headers,
|
||||
...requestAuth,
|
||||
};
|
||||
trackSourceKey(request.id, v, "item");
|
||||
exportResources.httpRequests.push(request);
|
||||
} else {
|
||||
console.log("Unknown item", v, folderId);
|
||||
}
|
||||
};
|
||||
|
||||
for (const item of root.item) {
|
||||
importItem(item);
|
||||
}
|
||||
|
||||
const resources = deleteUndefinedAttrs(
|
||||
convertTemplateSyntax(exportResources),
|
||||
) as PartialImportResources;
|
||||
|
||||
return { resources, sourceKeys };
|
||||
}
|
||||
|
||||
function convertUrl(rawUrl: unknown): Pick<HttpRequest, "url" | "urlParameters"> {
|
||||
if (typeof rawUrl === "string") {
|
||||
return { url: rawUrl, urlParameters: [] };
|
||||
}
|
||||
|
||||
const url = toRecord(rawUrl);
|
||||
|
||||
let v = "";
|
||||
|
||||
if ("protocol" in url && typeof url.protocol === "string") {
|
||||
v += `${url.protocol}://`;
|
||||
}
|
||||
|
||||
if ("host" in url) {
|
||||
v += `${Array.isArray(url.host) ? url.host.join(".") : String(url.host)}`;
|
||||
}
|
||||
|
||||
if ("port" in url && typeof url.port === "string") {
|
||||
v += `:${url.port}`;
|
||||
}
|
||||
|
||||
if ("path" in url) {
|
||||
if (Array.isArray(url.path) && url.path.length > 0) {
|
||||
v += `/${url.path.join("/")}`;
|
||||
} else if (typeof url.path === "string" && url.path.length > 0) {
|
||||
v += `/${url.path.replace(/^\//, "")}`;
|
||||
}
|
||||
}
|
||||
|
||||
const params: HttpUrlParameter[] = [];
|
||||
if ("query" in url && Array.isArray(url.query) && url.query.length > 0) {
|
||||
for (const query of url.query) {
|
||||
params.push({
|
||||
name: query.key ?? "",
|
||||
value: query.value ?? "",
|
||||
enabled: !query.disabled,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ("variable" in url && Array.isArray(url.variable) && url.variable.length > 0) {
|
||||
for (const v of url.variable) {
|
||||
params.push({
|
||||
name: `:${v.key ?? ""}`,
|
||||
value: v.value ?? "",
|
||||
enabled: !v.disabled,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ("hash" in url && typeof url.hash === "string") {
|
||||
v += `#${url.hash}`;
|
||||
}
|
||||
|
||||
return { url: v, urlParameters: params };
|
||||
}
|
||||
|
||||
function importAuth(rawAuth: unknown): Pick<HttpRequest, "authentication" | "authenticationType"> {
|
||||
const auth = toRecord<Record<string, string>>(rawAuth);
|
||||
|
||||
// Helper: Postman stores auth params as an array of { key, value, ... }
|
||||
const pmArrayToObj = (v: unknown): Record<string, unknown> => {
|
||||
if (!Array.isArray(v)) return toRecord(v);
|
||||
const o: Record<string, unknown> = {};
|
||||
for (const i of v) {
|
||||
const ii = toRecord(i);
|
||||
if (typeof ii.key === "string") {
|
||||
o[ii.key] = ii.value;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
};
|
||||
|
||||
const authType: string | undefined = auth.type ? String(auth.type) : undefined;
|
||||
|
||||
if (authType === "noauth") {
|
||||
return {
|
||||
authenticationType: "none",
|
||||
authentication: {},
|
||||
};
|
||||
}
|
||||
|
||||
if ("basic" in auth && authType === "basic") {
|
||||
const b = pmArrayToObj(auth.basic);
|
||||
return {
|
||||
authenticationType: "basic",
|
||||
authentication: {
|
||||
username: String(b.username ?? ""),
|
||||
password: String(b.password ?? ""),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ("bearer" in auth && authType === "bearer") {
|
||||
const b = pmArrayToObj(auth.bearer);
|
||||
// Postman uses key "token"
|
||||
return {
|
||||
authenticationType: "bearer",
|
||||
authentication: {
|
||||
token: String(b.token ?? ""),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ("digest" in auth && authType === "digest") {
|
||||
const d = pmArrayToObj(auth.digest);
|
||||
return {
|
||||
authenticationType: "digest",
|
||||
authentication: {
|
||||
username: d.username != null ? String(d.username) : undefined,
|
||||
password: d.password != null ? String(d.password) : undefined,
|
||||
realm: d.realm != null ? String(d.realm) : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ("ntlm" in auth && authType === "ntlm") {
|
||||
const n = pmArrayToObj(auth.ntlm);
|
||||
return {
|
||||
authenticationType: "windows",
|
||||
authentication: {
|
||||
username: n.username != null ? String(n.username) : undefined,
|
||||
password: n.password != null ? String(n.password) : undefined,
|
||||
domain: n.domain != null ? String(n.domain) : undefined,
|
||||
workstation: n.workstation != null ? String(n.workstation) : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ("awsv4" in auth && authType === "awsv4") {
|
||||
const a = pmArrayToObj(auth.awsv4);
|
||||
return {
|
||||
authenticationType: "awsv4",
|
||||
authentication: {
|
||||
accessKeyId: a.accessKey != null ? String(a.accessKey) : undefined,
|
||||
secretAccessKey: a.secretKey != null ? String(a.secretKey) : undefined,
|
||||
sessionToken: a.sessionToken != null ? String(a.sessionToken) : undefined,
|
||||
region: a.region != null ? String(a.region) : undefined,
|
||||
service: a.service != null ? String(a.service) : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ("apikey" in auth && authType === "apikey") {
|
||||
const a = pmArrayToObj(auth.apikey);
|
||||
return {
|
||||
authenticationType: "apikey",
|
||||
authentication: {
|
||||
location: a.in === "query" ? "query" : "header",
|
||||
// Postman's "key" is the header/parameter name and its "value" is the
|
||||
// secret, matching Yaak's own apikey auth.
|
||||
key: a.key != null ? String(a.key) : undefined,
|
||||
value: a.value != null ? String(a.value) : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ("jwt" in auth && authType === "jwt") {
|
||||
const a = pmArrayToObj(auth.jwt);
|
||||
return {
|
||||
authenticationType: "jwt",
|
||||
authentication: {
|
||||
algorithm: a.algorithm != null ? String(a.algorithm).toUpperCase() : undefined,
|
||||
secret: a.secret != null ? String(a.secret) : undefined,
|
||||
secretBase64: !!a.isSecretBase64Encoded,
|
||||
payload: a.payload != null ? String(a.payload) : undefined,
|
||||
headerPrefix: a.headerPrefix != null ? String(a.headerPrefix) : undefined,
|
||||
location: a.addTokenTo === "header" ? "header" : "query",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ("oauth1" in auth && authType === "oauth1") {
|
||||
const o = pmArrayToObj(auth.oauth1);
|
||||
const signatureMethod =
|
||||
o.signatureMethod != null
|
||||
? String(o.signatureMethod).toUpperCase()
|
||||
: DEFAULT_OAUTH1_SIGNATURE_METHOD;
|
||||
return {
|
||||
authenticationType: "oauth1",
|
||||
authentication: {
|
||||
signatureMethod:
|
||||
OAUTH1_SIGNATURE_METHODS[signatureMethod] ?? DEFAULT_OAUTH1_SIGNATURE_METHOD,
|
||||
consumerKey: o.consumerKey != null ? String(o.consumerKey) : undefined,
|
||||
consumerSecret: o.consumerSecret != null ? String(o.consumerSecret) : undefined,
|
||||
tokenKey: o.token != null ? String(o.token) : undefined,
|
||||
tokenSecret: o.tokenSecret != null ? String(o.tokenSecret) : undefined,
|
||||
privateKey: o.privateKey != null ? String(o.privateKey) : undefined,
|
||||
callback: o.callback != null ? String(o.callback) : undefined,
|
||||
verifier: o.verifier != null ? String(o.verifier) : undefined,
|
||||
timestamp: o.timestamp != null ? String(o.timestamp) : undefined,
|
||||
nonce: o.nonce != null ? String(o.nonce) : undefined,
|
||||
version: o.version != null ? String(o.version) : undefined,
|
||||
realm: o.realm != null ? String(o.realm) : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ("oauth2" in auth && authType === "oauth2") {
|
||||
const o = pmArrayToObj(auth.oauth2);
|
||||
|
||||
let grantType = o.grant_type ? String(o.grant_type) : "authorization_code";
|
||||
let pkcePatch: Record<string, unknown> = {};
|
||||
|
||||
if (grantType === "authorization_code_with_pkce") {
|
||||
grantType = "authorization_code";
|
||||
pkcePatch =
|
||||
o.grant_type === "authorization_code_with_pkce"
|
||||
? {
|
||||
usePkce: true,
|
||||
pkceChallengeMethod: o.challengeAlgorithm ?? undefined,
|
||||
pkceCodeVerifier: o.code_verifier != null ? String(o.code_verifier) : undefined,
|
||||
}
|
||||
: {};
|
||||
} else if (grantType === "password_credentials") {
|
||||
grantType = "password";
|
||||
}
|
||||
|
||||
const accessTokenUrl = o.accessTokenUrl != null ? String(o.accessTokenUrl) : undefined;
|
||||
const audience = o.audience != null ? String(o.audience) : undefined;
|
||||
const authorizationUrl = o.authUrl != null ? String(o.authUrl) : undefined;
|
||||
const clientId = o.clientId != null ? String(o.clientId) : undefined;
|
||||
const clientSecret = o.clientSecret != null ? String(o.clientSecret) : undefined;
|
||||
const credentials = o.client_authentication === "body" ? "body" : undefined;
|
||||
const headerPrefix = o.headerPrefix ?? "Bearer";
|
||||
const password = o.password != null ? String(o.password) : undefined;
|
||||
const redirectUri = o.redirect_uri != null ? String(o.redirect_uri) : undefined;
|
||||
const scope = o.scope != null ? String(o.scope) : undefined;
|
||||
const state = o.state != null ? String(o.state) : undefined;
|
||||
const username = o.username != null ? String(o.username) : undefined;
|
||||
|
||||
let grantPatch: Record<string, unknown> = {};
|
||||
if (grantType === "authorization_code") {
|
||||
grantPatch = {
|
||||
clientSecret,
|
||||
authorizationUrl,
|
||||
accessTokenUrl,
|
||||
redirectUri,
|
||||
state,
|
||||
...pkcePatch,
|
||||
};
|
||||
} else if (grantType === "implicit") {
|
||||
grantPatch = { authorizationUrl, redirectUri, state };
|
||||
} else if (grantType === "password") {
|
||||
grantPatch = { clientSecret, accessTokenUrl, username, password };
|
||||
} else if (grantType === "client_credentials") {
|
||||
grantPatch = { clientSecret, accessTokenUrl };
|
||||
}
|
||||
|
||||
const authentication = {
|
||||
name: "oauth2",
|
||||
grantType,
|
||||
audience,
|
||||
clientId,
|
||||
credentials,
|
||||
headerPrefix,
|
||||
scope,
|
||||
...grantPatch,
|
||||
} as Record<string, unknown>;
|
||||
|
||||
return { authenticationType: "oauth2", authentication };
|
||||
}
|
||||
|
||||
return { authenticationType: null, authentication: {} };
|
||||
}
|
||||
|
||||
function importBody(rawBody: unknown): Pick<HttpRequest, "body" | "bodyType" | "headers"> {
|
||||
const body = toRecord(rawBody) as {
|
||||
mode: string;
|
||||
graphql: { query?: string; variables?: string };
|
||||
urlencoded?: { key?: string; value?: string; disabled?: boolean }[];
|
||||
formdata?: {
|
||||
key?: string;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
contentType?: string;
|
||||
src?: string;
|
||||
}[];
|
||||
raw?: string;
|
||||
options?: { raw?: { language?: string } };
|
||||
file?: { src?: string };
|
||||
};
|
||||
if (body.mode === "graphql") {
|
||||
return {
|
||||
headers: [
|
||||
{
|
||||
name: "Content-Type",
|
||||
value: "application/json",
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
bodyType: "graphql",
|
||||
body: {
|
||||
text: JSON.stringify(
|
||||
{
|
||||
query: body.graphql?.query || "",
|
||||
variables: parseJSONToRecord(body.graphql?.variables || "{}"),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (body.mode === "urlencoded") {
|
||||
return {
|
||||
headers: [
|
||||
{
|
||||
name: "Content-Type",
|
||||
value: "application/x-www-form-urlencoded",
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
bodyType: "application/x-www-form-urlencoded",
|
||||
body: {
|
||||
form: toArray<NonNullable<typeof body.urlencoded>[0]>(body.urlencoded).map((f) => ({
|
||||
enabled: !f.disabled,
|
||||
name: f.key ?? "",
|
||||
value: f.value ?? "",
|
||||
})),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (body.mode === "formdata") {
|
||||
return {
|
||||
headers: [
|
||||
{
|
||||
name: "Content-Type",
|
||||
value: "multipart/form-data",
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
bodyType: "multipart/form-data",
|
||||
body: {
|
||||
form: toArray<NonNullable<typeof body.formdata>[0]>(body.formdata).map((f) =>
|
||||
f.src != null
|
||||
? {
|
||||
enabled: !f.disabled,
|
||||
contentType: f.contentType ?? null,
|
||||
name: f.key ?? "",
|
||||
file: f.src ?? "",
|
||||
}
|
||||
: {
|
||||
enabled: !f.disabled,
|
||||
name: f.key ?? "",
|
||||
value: f.value ?? "",
|
||||
},
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (body.mode === "raw") {
|
||||
return {
|
||||
headers: [
|
||||
{
|
||||
name: "Content-Type",
|
||||
value: body.options?.raw?.language === "json" ? "application/json" : "",
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
bodyType: body.options?.raw?.language === "json" ? "application/json" : "other",
|
||||
body: {
|
||||
text: body.raw ?? "",
|
||||
},
|
||||
};
|
||||
}
|
||||
if (body.mode === "file") {
|
||||
return {
|
||||
headers: [],
|
||||
bodyType: "binary",
|
||||
body: {
|
||||
filePath: body.file?.src,
|
||||
},
|
||||
};
|
||||
}
|
||||
return { headers: [], bodyType: null, body: {} };
|
||||
}
|
||||
|
||||
function parseJSONToRecord<T>(jsonStr: string): Record<string, T> | null {
|
||||
try {
|
||||
return toRecord(JSON.parse(jsonStr));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function toRecord<T>(value: unknown): Record<string, T> {
|
||||
if (value && typeof value === "object" && !Array.isArray(value)) {
|
||||
return value as Record<string, T>;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function toArray<T>(value: unknown): T[] {
|
||||
if (Object.prototype.toString.call(value) === "[object Array]") return value as T[];
|
||||
return [];
|
||||
}
|
||||
|
||||
function importDescription(rawDescription: unknown): string | undefined {
|
||||
if (rawDescription == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof rawDescription === "string") {
|
||||
return rawDescription;
|
||||
}
|
||||
|
||||
if (typeof rawDescription === "object" && !Array.isArray(rawDescription)) {
|
||||
const description = toRecord(rawDescription);
|
||||
if ("content" in description && description.content != null) {
|
||||
return String(description.content);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return String(rawDescription);
|
||||
}
|
||||
|
||||
/** Recursively render all nested object properties */
|
||||
function convertTemplateSyntax<T>(obj: T): T {
|
||||
if (typeof obj === "string") {
|
||||
return obj.replace(/{{\s*(_\.)?([^}]*)\s*}}/g, (_m, _dot, expr) => {
|
||||
const name = String(expr).trim();
|
||||
// hasOwn, so a collection using {{constructor}} doesn't reach Object.prototype
|
||||
if (Object.hasOwn(POSTMAN_DYNAMIC_VARIABLES, name)) {
|
||||
return `\${[${POSTMAN_DYNAMIC_VARIABLES[name]}]}`;
|
||||
}
|
||||
return `\${[${name.replace(/^vault:/, "")}]}`;
|
||||
}) as T;
|
||||
}
|
||||
if (Array.isArray(obj) && obj != null) {
|
||||
return obj.map(convertTemplateSyntax) as T;
|
||||
}
|
||||
if (typeof obj === "object" && obj != null) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).map(([k, v]) => [k, convertTemplateSyntax(v)]),
|
||||
) as T;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function deleteUndefinedAttrs<T>(obj: T): T {
|
||||
if (Array.isArray(obj) && obj != null) {
|
||||
return obj.map(deleteUndefinedAttrs) as T;
|
||||
}
|
||||
if (typeof obj === "object" && obj != null) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj)
|
||||
.filter(([, v]) => v !== undefined)
|
||||
.map(([k, v]) => [k, deleteUndefinedAttrs(v)]),
|
||||
) as T;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
const idCount: Partial<Record<string, number>> = {};
|
||||
|
||||
function generateId(model: string): string {
|
||||
idCount[model] = (idCount[model] ?? -1) + 1;
|
||||
return `GENERATE_ID::${model.toUpperCase()}_${idCount[model]}`;
|
||||
}
|
||||
1032
plugins/importer-postman/tests/fixtures/auth.input.json
vendored
Normal file
1032
plugins/importer-postman/tests/fixtures/auth.input.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
421
plugins/importer-postman/tests/fixtures/auth.output.json
vendored
Normal file
421
plugins/importer-postman/tests/fixtures/auth.output.json
vendored
Normal file
@@ -0,0 +1,421 @@
|
||||
{
|
||||
"resources": {
|
||||
"workspaces": [
|
||||
{
|
||||
"model": "workspace",
|
||||
"id": "GENERATE_ID::WORKSPACE_0",
|
||||
"name": "Authentication",
|
||||
"authenticationType": "basic",
|
||||
"authentication": {
|
||||
"username": "workspace",
|
||||
"password": "workspace_secret"
|
||||
}
|
||||
}
|
||||
],
|
||||
"environments": [
|
||||
{
|
||||
"model": "environment",
|
||||
"id": "GENERATE_ID::ENVIRONMENT_0",
|
||||
"name": "Global Variables",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"parentModel": "workspace",
|
||||
"parentId": null,
|
||||
"variables": [
|
||||
{
|
||||
"name": "COLLECTION VARIABLE",
|
||||
"value": "collection variable"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"httpRequests": [
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_0",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "No Auth",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 0,
|
||||
"headers": [],
|
||||
"authenticationType": "none",
|
||||
"authentication": {}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_1",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "Inherit",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 1,
|
||||
"headers": [],
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_2",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 2 Auth Code",
|
||||
"method": "GET",
|
||||
"url": "${[hello]}",
|
||||
"urlParameters": [],
|
||||
"body": {
|
||||
"text": "{\n \"hello\": \"world\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 2,
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "oauth2",
|
||||
"authentication": {
|
||||
"name": "oauth2",
|
||||
"grantType": "authorization_code",
|
||||
"clientId": "cliend id",
|
||||
"headerPrefix": "Bearer",
|
||||
"scope": "scope",
|
||||
"clientSecret": "clientsecet",
|
||||
"authorizationUrl": "https://github.com/login/oauth/authorize",
|
||||
"accessTokenUrl": "https://github.com/login/oauth/access_token",
|
||||
"redirectUri": "https://callback",
|
||||
"state": "state"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_3",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 2 Auth Code (PKCE)",
|
||||
"method": "GET",
|
||||
"url": "${[hello]}",
|
||||
"urlParameters": [],
|
||||
"body": {
|
||||
"text": "{\n \"hello\": \"world\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 3,
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "oauth2",
|
||||
"authentication": {
|
||||
"name": "oauth2",
|
||||
"grantType": "authorization_code",
|
||||
"clientId": "cliend id",
|
||||
"headerPrefix": "Bearer",
|
||||
"scope": "scope",
|
||||
"clientSecret": "clientsecet",
|
||||
"authorizationUrl": "https://github.com/login/oauth/authorize",
|
||||
"accessTokenUrl": "https://github.com/login/oauth/access_token",
|
||||
"redirectUri": "https://callback",
|
||||
"state": "state",
|
||||
"usePkce": true,
|
||||
"pkceChallengeMethod": "S256",
|
||||
"pkceCodeVerifier": "verifier"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_4",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 2 Implicit",
|
||||
"method": "GET",
|
||||
"url": "${[hello]}",
|
||||
"urlParameters": [],
|
||||
"body": {
|
||||
"text": "{\n \"hello\": \"world\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 4,
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "oauth2",
|
||||
"authentication": {
|
||||
"name": "oauth2",
|
||||
"grantType": "implicit",
|
||||
"clientId": "cliend id",
|
||||
"headerPrefix": "Bearer",
|
||||
"scope": "scope",
|
||||
"authorizationUrl": "https://github.com/login/oauth/authorize",
|
||||
"redirectUri": "https://yaak.app/x/echo",
|
||||
"state": "state"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_5",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 2 Password",
|
||||
"method": "GET",
|
||||
"url": "${[hello]}",
|
||||
"urlParameters": [],
|
||||
"body": {
|
||||
"text": "{\n \"hello\": \"world\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 5,
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "oauth2",
|
||||
"authentication": {
|
||||
"name": "oauth2",
|
||||
"grantType": "password",
|
||||
"clientId": "clientid",
|
||||
"headerPrefix": "Bearer",
|
||||
"scope": "scope",
|
||||
"clientSecret": "clientsecret",
|
||||
"accessTokenUrl": "https://github.com/login/oauth/access_token",
|
||||
"username": "username",
|
||||
"password": "password"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_6",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 2 Client Credentials",
|
||||
"method": "GET",
|
||||
"url": "${[hello]}",
|
||||
"urlParameters": [],
|
||||
"body": {
|
||||
"text": "{\n \"hello\": \"world\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 6,
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "oauth2",
|
||||
"authentication": {
|
||||
"name": "oauth2",
|
||||
"grantType": "client_credentials",
|
||||
"clientId": "clientid",
|
||||
"headerPrefix": "Bearer",
|
||||
"scope": "scope",
|
||||
"clientSecret": "clientsecret",
|
||||
"accessTokenUrl": "https://github.com/login/oauth/access_token"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_7",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "AWS V4",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 7,
|
||||
"headers": [],
|
||||
"authenticationType": "awsv4",
|
||||
"authentication": {
|
||||
"accessKeyId": "access",
|
||||
"secretAccessKey": "secret",
|
||||
"sessionToken": "session",
|
||||
"region": "us-west-1",
|
||||
"service": "s3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_8",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "API Key",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 8,
|
||||
"headers": [],
|
||||
"authenticationType": "apikey",
|
||||
"authentication": {
|
||||
"location": "query",
|
||||
"key": "X-API-Key",
|
||||
"value": "secret-123"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_9",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "JWT",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 9,
|
||||
"headers": [],
|
||||
"authenticationType": "jwt",
|
||||
"authentication": {
|
||||
"algorithm": "HS384",
|
||||
"secret": "mysecret",
|
||||
"secretBase64": false,
|
||||
"payload": "{\n \"my\": \"payload\"\n}",
|
||||
"headerPrefix": "Bearer",
|
||||
"location": "header"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_10",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "Digest",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 10,
|
||||
"headers": [],
|
||||
"authenticationType": "digest",
|
||||
"authentication": {
|
||||
"username": "digest-user",
|
||||
"password": "digest-pass",
|
||||
"realm": "testrealm@yaak.app"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_11",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "NTLM",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 11,
|
||||
"headers": [],
|
||||
"authenticationType": "windows",
|
||||
"authentication": {
|
||||
"username": "ntlm-user",
|
||||
"password": "ntlm-pass",
|
||||
"domain": "CORP",
|
||||
"workstation": "WORKSTATION-01"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_12",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 1",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 12,
|
||||
"headers": [],
|
||||
"authenticationType": "oauth1",
|
||||
"authentication": {
|
||||
"signatureMethod": "HMAC-SHA256",
|
||||
"consumerKey": "consumer-key",
|
||||
"consumerSecret": "consumer-secret",
|
||||
"tokenKey": "access-token",
|
||||
"tokenSecret": "token-secret",
|
||||
"callback": "https://yaak.app/x/callback",
|
||||
"verifier": "verifier-123",
|
||||
"timestamp": "1700000000",
|
||||
"nonce": "9e6dfada256c",
|
||||
"version": "1.0",
|
||||
"realm": "yaak.app"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_13",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": "GENERATE_ID::FOLDER_0",
|
||||
"name": "Folder Child Inherit",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 14,
|
||||
"headers": [],
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_14",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": "GENERATE_ID::FOLDER_0",
|
||||
"name": "Folder Child No Auth",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 15,
|
||||
"headers": [],
|
||||
"authenticationType": "none",
|
||||
"authentication": {}
|
||||
}
|
||||
],
|
||||
"folders": [
|
||||
{
|
||||
"model": "folder",
|
||||
"sortPriority": 13,
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"id": "GENERATE_ID::FOLDER_0",
|
||||
"name": "Folder With Auth",
|
||||
"folderId": null,
|
||||
"authenticationType": "bearer",
|
||||
"authentication": {
|
||||
"token": "folder-token"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceKeys": {
|
||||
"GENERATE_ID::WORKSPACE_0": "collection:9e6dfada-256c-49ea-a38f-7d1b05b7ca2d"
|
||||
}
|
||||
}
|
||||
67
plugins/importer-postman/tests/fixtures/dynamic-variables.input.json
vendored
Normal file
67
plugins/importer-postman/tests/fixtures/dynamic-variables.input.json
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "5c0ffee0-dyn0-4var-9abc-000000000001",
|
||||
"name": "Dynamic Variables",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "Dynamic Variables",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "X-Request-Id",
|
||||
"value": "{{$guid}}"
|
||||
},
|
||||
{
|
||||
"key": "X-Correlation-Id",
|
||||
"value": "{{$randomUUID}}"
|
||||
},
|
||||
{
|
||||
"key": "X-Sent-At",
|
||||
"value": "{{$timestamp}}"
|
||||
},
|
||||
{
|
||||
"key": "X-Sent-At-Iso",
|
||||
"value": "{{$isoTimestamp}}"
|
||||
},
|
||||
{
|
||||
"key": "X-Spaced",
|
||||
"value": "{{ $guid }}"
|
||||
},
|
||||
{
|
||||
"key": "X-Faker",
|
||||
"value": "{{$randomFirstName}}"
|
||||
},
|
||||
{
|
||||
"key": "X-Plain",
|
||||
"value": "{{base_url}}"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "https://yaak.app/x/echo?n={{$randomInt}}",
|
||||
"protocol": "https",
|
||||
"host": ["yaak", "app"],
|
||||
"path": ["x", "echo"],
|
||||
"query": [
|
||||
{
|
||||
"key": "n",
|
||||
"value": "{{$randomInt}}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"id\": \"{{$guid}}\",\n \"at\": {{$timestamp}},\n \"mixed\": \"{{$guid}}/{{base_url}}\"\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
]
|
||||
}
|
||||
95
plugins/importer-postman/tests/fixtures/dynamic-variables.output.json
vendored
Normal file
95
plugins/importer-postman/tests/fixtures/dynamic-variables.output.json
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"resources": {
|
||||
"workspaces": [
|
||||
{
|
||||
"model": "workspace",
|
||||
"id": "GENERATE_ID::WORKSPACE_1",
|
||||
"name": "Dynamic Variables",
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
}
|
||||
],
|
||||
"environments": [
|
||||
{
|
||||
"model": "environment",
|
||||
"id": "GENERATE_ID::ENVIRONMENT_1",
|
||||
"name": "Global Variables",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_1",
|
||||
"parentModel": "workspace",
|
||||
"parentId": null,
|
||||
"variables": []
|
||||
}
|
||||
],
|
||||
"httpRequests": [
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_15",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_1",
|
||||
"folderId": null,
|
||||
"name": "Dynamic Variables",
|
||||
"method": "POST",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [
|
||||
{
|
||||
"name": "n",
|
||||
"value": "${[random.range(min='0',max='1000',decimals='0')]}",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"text": "{\n \"id\": \"${[uuid.v4()]}\",\n \"at\": ${[timestamp.unix()]},\n \"mixed\": \"${[uuid.v4()]}/${[base_url]}\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 0,
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-Request-Id",
|
||||
"value": "${[uuid.v4()]}",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "X-Correlation-Id",
|
||||
"value": "${[uuid.v4()]}",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "X-Sent-At",
|
||||
"value": "${[timestamp.unix()]}",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "X-Sent-At-Iso",
|
||||
"value": "${[timestamp.iso8601()]}",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "X-Spaced",
|
||||
"value": "${[uuid.v4()]}",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "X-Faker",
|
||||
"value": "${[$randomFirstName]}",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "X-Plain",
|
||||
"value": "${[base_url]}",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
}
|
||||
],
|
||||
"folders": []
|
||||
},
|
||||
"sourceKeys": {
|
||||
"GENERATE_ID::WORKSPACE_1": "collection:5c0ffee0-dyn0-4var-9abc-000000000001"
|
||||
}
|
||||
}
|
||||
38
plugins/importer-postman/tests/fixtures/nested.input.json
vendored
Normal file
38
plugins/importer-postman/tests/fixtures/nested.input.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "9e6dfada-256c-49ea-a38f-7d1b05b7ca2d",
|
||||
"name": "New Collection",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json",
|
||||
"_exporter_id": "18798"
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "Top Folder",
|
||||
"item": [
|
||||
{
|
||||
"name": "Nested Folder",
|
||||
"item": [
|
||||
{
|
||||
"name": "Request 1",
|
||||
"request": {
|
||||
"method": "GET"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Request 2",
|
||||
"request": {
|
||||
"method": "GET"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Request 3",
|
||||
"request": {
|
||||
"method": "GET"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
99
plugins/importer-postman/tests/fixtures/nested.output.json
vendored
Normal file
99
plugins/importer-postman/tests/fixtures/nested.output.json
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"resources": {
|
||||
"workspaces": [
|
||||
{
|
||||
"model": "workspace",
|
||||
"id": "GENERATE_ID::WORKSPACE_2",
|
||||
"name": "New Collection",
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
}
|
||||
],
|
||||
"environments": [
|
||||
{
|
||||
"model": "environment",
|
||||
"id": "GENERATE_ID::ENVIRONMENT_2",
|
||||
"name": "Global Variables",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_2",
|
||||
"parentModel": "workspace",
|
||||
"parentId": null,
|
||||
"variables": []
|
||||
}
|
||||
],
|
||||
"httpRequests": [
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_16",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_2",
|
||||
"folderId": "GENERATE_ID::FOLDER_2",
|
||||
"name": "Request 1",
|
||||
"method": "GET",
|
||||
"url": "",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 2,
|
||||
"headers": [],
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_17",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_2",
|
||||
"folderId": "GENERATE_ID::FOLDER_1",
|
||||
"name": "Request 2",
|
||||
"method": "GET",
|
||||
"url": "",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 3,
|
||||
"headers": [],
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_18",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_2",
|
||||
"folderId": null,
|
||||
"name": "Request 3",
|
||||
"method": "GET",
|
||||
"url": "",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 4,
|
||||
"headers": [],
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
}
|
||||
],
|
||||
"folders": [
|
||||
{
|
||||
"model": "folder",
|
||||
"sortPriority": 0,
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_2",
|
||||
"id": "GENERATE_ID::FOLDER_1",
|
||||
"name": "Top Folder",
|
||||
"folderId": null,
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
},
|
||||
{
|
||||
"model": "folder",
|
||||
"sortPriority": 1,
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_2",
|
||||
"id": "GENERATE_ID::FOLDER_2",
|
||||
"name": "Nested Folder",
|
||||
"folderId": "GENERATE_ID::FOLDER_1",
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceKeys": {
|
||||
"GENERATE_ID::WORKSPACE_2": "collection:9e6dfada-256c-49ea-a38f-7d1b05b7ca2d"
|
||||
}
|
||||
}
|
||||
126
plugins/importer-postman/tests/fixtures/params.input.json
vendored
Normal file
126
plugins/importer-postman/tests/fixtures/params.input.json
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "9e6dfada-256c-49ea-a38f-7d1b05b7ca2d",
|
||||
"name": "New Collection",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||
"_exporter_id": "18798"
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "Form URL",
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "my-token",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "X-foo",
|
||||
"value": "bar",
|
||||
"description": "description"
|
||||
},
|
||||
{
|
||||
"key": "Disabled",
|
||||
"value": "tnroant",
|
||||
"description": "ntisorantosra",
|
||||
"disabled": true
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "formdata",
|
||||
"formdata": [
|
||||
{
|
||||
"key": "Key",
|
||||
"contentType": "Custom/COntent",
|
||||
"description": "DEscription",
|
||||
"type": "file",
|
||||
"src": "/Users/gschier/Desktop/Screenshot 2024-05-31 at 12.05.11 PM.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"raw": "example.com/:foo/:bar?q=qqq&",
|
||||
"host": ["example", "com"],
|
||||
"path": [":foo", ":bar"],
|
||||
"query": [
|
||||
{
|
||||
"key": "disabled",
|
||||
"value": "secondvalue",
|
||||
"description": "this is disabled",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"key": "q",
|
||||
"value": "qqq",
|
||||
"description": "hello"
|
||||
},
|
||||
{
|
||||
"key": "",
|
||||
"value": null
|
||||
}
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "foo",
|
||||
"value": "fff",
|
||||
"description": "Description"
|
||||
},
|
||||
{
|
||||
"key": "bar",
|
||||
"value": "bbb",
|
||||
"description": "bbb description"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
],
|
||||
"auth": {
|
||||
"type": "basic",
|
||||
"basic": [
|
||||
{
|
||||
"key": "password",
|
||||
"value": "globalpass",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "username",
|
||||
"value": "globaluser",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "prerequest",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"packages": {},
|
||||
"exec": [""]
|
||||
}
|
||||
},
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"packages": {},
|
||||
"exec": [""]
|
||||
}
|
||||
}
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "COLLECTION VARIABLE",
|
||||
"value": "collection variable",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
107
plugins/importer-postman/tests/fixtures/params.output.json
vendored
Normal file
107
plugins/importer-postman/tests/fixtures/params.output.json
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"resources": {
|
||||
"workspaces": [
|
||||
{
|
||||
"model": "workspace",
|
||||
"id": "GENERATE_ID::WORKSPACE_3",
|
||||
"name": "New Collection",
|
||||
"authenticationType": "basic",
|
||||
"authentication": {
|
||||
"username": "globaluser",
|
||||
"password": "globalpass"
|
||||
}
|
||||
}
|
||||
],
|
||||
"environments": [
|
||||
{
|
||||
"model": "environment",
|
||||
"id": "GENERATE_ID::ENVIRONMENT_3",
|
||||
"name": "Global Variables",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_3",
|
||||
"parentModel": "workspace",
|
||||
"parentId": null,
|
||||
"variables": [
|
||||
{
|
||||
"name": "COLLECTION VARIABLE",
|
||||
"value": "collection variable"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"httpRequests": [
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_19",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_3",
|
||||
"folderId": null,
|
||||
"name": "Form URL",
|
||||
"method": "POST",
|
||||
"url": "example.com/:foo/:bar",
|
||||
"urlParameters": [
|
||||
{
|
||||
"name": "disabled",
|
||||
"value": "secondvalue",
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"name": "q",
|
||||
"value": "qqq",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "",
|
||||
"value": "",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": ":foo",
|
||||
"value": "fff",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": ":bar",
|
||||
"value": "bbb",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"form": [
|
||||
{
|
||||
"enabled": true,
|
||||
"contentType": "Custom/COntent",
|
||||
"name": "Key",
|
||||
"file": "/Users/gschier/Desktop/Screenshot 2024-05-31 at 12.05.11 PM.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bodyType": "multipart/form-data",
|
||||
"sortPriority": 0,
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-foo",
|
||||
"value": "bar",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "Disabled",
|
||||
"value": "tnroant",
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "multipart/form-data",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "bearer",
|
||||
"authentication": {
|
||||
"token": "my-token"
|
||||
}
|
||||
}
|
||||
],
|
||||
"folders": []
|
||||
},
|
||||
"sourceKeys": {
|
||||
"GENERATE_ID::WORKSPACE_3": "collection:9e6dfada-256c-49ea-a38f-7d1b05b7ca2d"
|
||||
}
|
||||
}
|
||||
201
plugins/importer-postman/tests/index.test.ts
Normal file
201
plugins/importer-postman/tests/index.test.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { describe, expect, test } from "vite-plus/test";
|
||||
import { convertPostman } from "../src";
|
||||
|
||||
describe("importer-postman", () => {
|
||||
const p = path.join(__dirname, "fixtures");
|
||||
const fixtures = fs.readdirSync(p);
|
||||
|
||||
for (const fixture of fixtures) {
|
||||
if (fixture.includes(".output")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
test(`Imports ${fixture}`, () => {
|
||||
const contents = fs.readFileSync(path.join(p, fixture), "utf-8");
|
||||
const expected = fs.readFileSync(path.join(p, fixture.replace(".input", ".output")), "utf-8");
|
||||
const result = convertPostman(contents);
|
||||
// console.log(JSON.stringify(result, null, 2))
|
||||
expect(JSON.stringify(result, null, 2)).toEqual(
|
||||
JSON.stringify(JSON.parse(expected), null, 2),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
test("Imports object descriptions without [object Object]", () => {
|
||||
const result = convertPostman(
|
||||
JSON.stringify({
|
||||
info: {
|
||||
name: "Description Test",
|
||||
schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||
},
|
||||
item: [
|
||||
{
|
||||
name: "Request 1",
|
||||
request: {
|
||||
method: "GET",
|
||||
description: {
|
||||
content: "Lijst van klanten",
|
||||
type: "text/plain",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result?.resources.workspaces).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "Description Test",
|
||||
}),
|
||||
]);
|
||||
expect(result?.resources.httpRequests).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "Request 1",
|
||||
description: "Lijst van klanten",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
test("Imports url.path when it is a string instead of an array", () => {
|
||||
const result = convertPostman(
|
||||
JSON.stringify({
|
||||
info: {
|
||||
name: "String Path Test",
|
||||
schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||
},
|
||||
item: [
|
||||
{
|
||||
name: "String Path",
|
||||
request: {
|
||||
method: "GET",
|
||||
url: {
|
||||
host: ["example", "com"],
|
||||
path: "foo/bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result?.resources.httpRequests).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "String Path",
|
||||
url: "example.com/foo/bar",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
test("Keys items by their Postman ID, unchanged by a rename", () => {
|
||||
const collection = (requestName: string) =>
|
||||
JSON.stringify({
|
||||
info: {
|
||||
_postman_id: "collection-id",
|
||||
name: "Keys",
|
||||
schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||
},
|
||||
item: [
|
||||
{
|
||||
id: "folder-id",
|
||||
name: "Folder",
|
||||
item: [
|
||||
{
|
||||
id: "request-id",
|
||||
name: requestName,
|
||||
request: { method: "GET", url: "https://yaak.app" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const before = convertPostman(collection("Original"));
|
||||
const after = convertPostman(collection("Renamed"));
|
||||
|
||||
const keyOf = (result: ReturnType<typeof convertPostman>, id: string | undefined) =>
|
||||
id == null ? undefined : result?.sourceKeys?.[id];
|
||||
|
||||
expect(keyOf(before, before?.resources.httpRequests[0]?.id)).toBe("item:request-id");
|
||||
expect(keyOf(after, after?.resources.httpRequests[0]?.id)).toBe("item:request-id");
|
||||
expect(keyOf(before, before?.resources.folders[0]?.id)).toBe("item:folder-id");
|
||||
expect(keyOf(before, before?.resources.workspaces[0]?.id)).toBe("collection:collection-id");
|
||||
});
|
||||
|
||||
test("Falls back to the default OAuth 1 signature method for unrecognized ones", () => {
|
||||
const result = convertPostman(
|
||||
JSON.stringify({
|
||||
info: {
|
||||
name: "OAuth 1 Signature",
|
||||
schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||
},
|
||||
item: [
|
||||
{
|
||||
name: "Request",
|
||||
request: {
|
||||
method: "GET",
|
||||
url: "https://yaak.app",
|
||||
auth: {
|
||||
type: "oauth1",
|
||||
oauth1: [{ key: "signatureMethod", value: "HMAC-SHA384" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result?.resources.httpRequests).toEqual([
|
||||
expect.objectContaining({
|
||||
authenticationType: "oauth1",
|
||||
authentication: { signatureMethod: "HMAC-SHA1" },
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
test("Leaves {{constructor}} alone instead of reaching Object.prototype", () => {
|
||||
const result = convertPostman(
|
||||
JSON.stringify({
|
||||
info: {
|
||||
name: "Prototype Key",
|
||||
schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||
},
|
||||
item: [
|
||||
{
|
||||
name: "Request",
|
||||
request: {
|
||||
method: "GET",
|
||||
url: "https://yaak.app",
|
||||
header: [
|
||||
{ key: "X-A", value: "{{constructor}}" },
|
||||
{ key: "X-B", value: "{{toString}}" },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result?.resources.httpRequests[0]?.headers).toEqual([
|
||||
{ name: "X-A", value: "${[constructor]}", enabled: true },
|
||||
{ name: "X-B", value: "${[toString]}", enabled: true },
|
||||
]);
|
||||
});
|
||||
|
||||
test("Omits keys for items the collection never identified", () => {
|
||||
const result = convertPostman(
|
||||
JSON.stringify({
|
||||
info: {
|
||||
name: "No IDs",
|
||||
schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||
},
|
||||
item: [{ name: "Request", request: { method: "GET", url: "https://yaak.app" } }],
|
||||
}),
|
||||
);
|
||||
|
||||
const requestId = result?.resources.httpRequests[0]?.id;
|
||||
expect(requestId).toBeDefined();
|
||||
expect(result?.sourceKeys).not.toHaveProperty(requestId as string);
|
||||
});
|
||||
});
|
||||
3
plugins/importer-postman/tsconfig.json
Normal file
3
plugins/importer-postman/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
Reference in New Issue
Block a user