refactor: port postman and insomnia importers
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
import HoppRESTCollImporter from "./hopp"
|
import HoppRESTCollImporter from "./hopp"
|
||||||
import OpenAPIImporter from "./openapi"
|
import OpenAPIImporter from "./openapi"
|
||||||
|
import PostmanImporter from "./postman"
|
||||||
|
import InsomniaImporter from "./insomnia"
|
||||||
|
|
||||||
export const RESTCollectionImporters = [
|
export const RESTCollectionImporters = [
|
||||||
HoppRESTCollImporter,
|
HoppRESTCollImporter,
|
||||||
OpenAPIImporter,
|
OpenAPIImporter,
|
||||||
|
PostmanImporter,
|
||||||
|
InsomniaImporter,
|
||||||
] as const
|
] as const
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import * as TE from "fp-ts/TaskEither"
|
||||||
|
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||||
|
import { step } from "../steps"
|
||||||
|
import { parsePostmanCollection } from "./postman"
|
||||||
|
import { defineImporter, IMPORTER_INVALID_FILE_FORMAT } from "."
|
||||||
|
import { parseInsomniaCollection } from "~/helpers/utils/parseInsomniaCollection"
|
||||||
|
import { Collection } from "~/newstore/collections"
|
||||||
|
|
||||||
|
// This Importer definition is less than ideal,
|
||||||
|
// would love an attempt at refactoring this
|
||||||
|
|
||||||
|
export default defineImporter({
|
||||||
|
name: "Insomnia Collection",
|
||||||
|
steps: [
|
||||||
|
step({
|
||||||
|
stepName: "FILE_OR_URL_IMPORT",
|
||||||
|
metadata: {
|
||||||
|
acceptedFileTypes: ".json",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
] as const,
|
||||||
|
importer: ([fileContent]) => {
|
||||||
|
try {
|
||||||
|
let collections = parseInsomniaCollection(fileContent)
|
||||||
|
const content = JSON.stringify(collections)
|
||||||
|
|
||||||
|
if (collections[0]) {
|
||||||
|
const [name, folders, requests] = Object.keys(collections[0])
|
||||||
|
if (
|
||||||
|
name === "name" &&
|
||||||
|
folders === "folders" &&
|
||||||
|
requests === "requests"
|
||||||
|
) {
|
||||||
|
return TE.right(collections as Collection<HoppRESTRequest>[])
|
||||||
|
}
|
||||||
|
|
||||||
|
return TE.left(IMPORTER_INVALID_FILE_FORMAT)
|
||||||
|
} else if (
|
||||||
|
collections.info &&
|
||||||
|
collections.info.schema.includes("v2.1.0")
|
||||||
|
) {
|
||||||
|
// replace the variables, postman uses {{var}}, Hoppscotch uses <<var>>
|
||||||
|
collections = JSON.parse(content.replaceAll(/{{([a-z]+)}}/gi, "<<$1>>"))
|
||||||
|
collections = [parsePostmanCollection(collections)]
|
||||||
|
|
||||||
|
return TE.right(collections as Collection<HoppRESTRequest>[])
|
||||||
|
} else {
|
||||||
|
return TE.left(IMPORTER_INVALID_FILE_FORMAT)
|
||||||
|
}
|
||||||
|
} catch (_e) {
|
||||||
|
return TE.left(IMPORTER_INVALID_FILE_FORMAT)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
203
packages/hoppscotch-app/helpers/import-export/import/postman.ts
Normal file
203
packages/hoppscotch-app/helpers/import-export/import/postman.ts
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
import { HoppRESTRequest, translateToNewRequest } from "@hoppscotch/data"
|
||||||
|
import { pipe } from "fp-ts/function"
|
||||||
|
import * as O from "fp-ts/Option"
|
||||||
|
import * as TE from "fp-ts/TaskEither"
|
||||||
|
import { step } from "../steps"
|
||||||
|
import { defineImporter, IMPORTER_INVALID_FILE_FORMAT } from "."
|
||||||
|
import { Collection, makeCollection } from "~/newstore/collections"
|
||||||
|
|
||||||
|
// TODO: I don't even know what is going on here :/
|
||||||
|
type PostmanCollection = {
|
||||||
|
info?: {
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
name: string
|
||||||
|
item: {
|
||||||
|
name: string
|
||||||
|
request: any
|
||||||
|
item?: any
|
||||||
|
}[]
|
||||||
|
folders?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasFolder = (item: { item?: any }) => {
|
||||||
|
return Object.prototype.hasOwnProperty.call(item, "item")
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parsePostmanCollection = ({
|
||||||
|
info,
|
||||||
|
name,
|
||||||
|
item,
|
||||||
|
}: PostmanCollection) => {
|
||||||
|
const hoppscotchCollection: Collection<HoppRESTRequest> = makeCollection({
|
||||||
|
name: "",
|
||||||
|
folders: [],
|
||||||
|
requests: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
hoppscotchCollection.name = info ? info.name : name
|
||||||
|
|
||||||
|
if (item && item.length > 0) {
|
||||||
|
for (const collectionItem of item) {
|
||||||
|
if (collectionItem.request) {
|
||||||
|
if (
|
||||||
|
Object.prototype.hasOwnProperty.call(hoppscotchCollection, "folders")
|
||||||
|
) {
|
||||||
|
hoppscotchCollection.name = info ? info.name : name
|
||||||
|
hoppscotchCollection.requests.push(
|
||||||
|
parsePostmanRequest(collectionItem)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
hoppscotchCollection.name = name || ""
|
||||||
|
hoppscotchCollection.requests.push(
|
||||||
|
parsePostmanRequest(collectionItem)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (hasFolder(collectionItem)) {
|
||||||
|
hoppscotchCollection.folders.push(
|
||||||
|
parsePostmanCollection(collectionItem as any)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
hoppscotchCollection.requests.push(parsePostmanRequest(collectionItem))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hoppscotchCollection
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Rewrite
|
||||||
|
const parsePostmanRequest = ({
|
||||||
|
name,
|
||||||
|
request,
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
request: any
|
||||||
|
}) => {
|
||||||
|
const pwRequest = {
|
||||||
|
url: "",
|
||||||
|
path: "",
|
||||||
|
method: "",
|
||||||
|
auth: "",
|
||||||
|
httpUser: "",
|
||||||
|
httpPassword: "",
|
||||||
|
passwordFieldType: "password",
|
||||||
|
bearerToken: "",
|
||||||
|
headers: [] as { name?: string; type?: string }[],
|
||||||
|
params: [] as { disabled?: boolean }[],
|
||||||
|
bodyParams: [] as { type?: string }[],
|
||||||
|
body: {
|
||||||
|
body: "",
|
||||||
|
contentType: "application/json",
|
||||||
|
},
|
||||||
|
rawParams: "",
|
||||||
|
rawInput: false,
|
||||||
|
contentType: "",
|
||||||
|
requestType: "",
|
||||||
|
name: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
pwRequest.name = name
|
||||||
|
if (request.url) {
|
||||||
|
const requestObjectUrl = request.url.raw.match(
|
||||||
|
/^(.+:\/\/[^/]+|{[^/]+})(\/[^?]+|).*$/
|
||||||
|
)
|
||||||
|
if (requestObjectUrl) {
|
||||||
|
pwRequest.url = requestObjectUrl[1]
|
||||||
|
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
|
||||||
|
} else {
|
||||||
|
pwRequest.url = request.url.raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pwRequest.method = request.method
|
||||||
|
const itemAuth = request.auth ? request.auth : ""
|
||||||
|
const authType = itemAuth ? itemAuth.type : ""
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (authType === "basic") {
|
||||||
|
pwRequest.auth = "Basic Auth"
|
||||||
|
pwRequest.httpUser =
|
||||||
|
itemAuth.basic[0].key === "username"
|
||||||
|
? itemAuth.basic[0].value
|
||||||
|
: itemAuth.basic[1].value
|
||||||
|
pwRequest.httpPassword =
|
||||||
|
itemAuth.basic[0].key === "password"
|
||||||
|
? itemAuth.basic[0].value
|
||||||
|
: itemAuth.basic[1].value
|
||||||
|
} else if (authType === "oauth2") {
|
||||||
|
pwRequest.auth = "OAuth 2.0"
|
||||||
|
pwRequest.bearerToken =
|
||||||
|
itemAuth.oauth2[0].key === "accessToken"
|
||||||
|
? itemAuth.oauth2[0].value
|
||||||
|
: itemAuth.oauth2[1].value
|
||||||
|
} else if (authType === "bearer") {
|
||||||
|
pwRequest.auth = "Bearer Token"
|
||||||
|
pwRequest.bearerToken = itemAuth.bearer[0].value
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestObjectHeaders = request.header
|
||||||
|
if (requestObjectHeaders) {
|
||||||
|
pwRequest.headers = requestObjectHeaders
|
||||||
|
for (const header of pwRequest.headers) {
|
||||||
|
delete header.name
|
||||||
|
delete header.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (request.url) {
|
||||||
|
const requestObjectParams = request.url.query
|
||||||
|
if (requestObjectParams) {
|
||||||
|
pwRequest.params = requestObjectParams
|
||||||
|
for (const param of pwRequest.params) {
|
||||||
|
delete param.disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (request.body) {
|
||||||
|
if (request.body.mode === "urlencoded") {
|
||||||
|
const params = request.body.urlencoded
|
||||||
|
pwRequest.bodyParams = params || []
|
||||||
|
for (const param of pwRequest.bodyParams) {
|
||||||
|
delete param.type
|
||||||
|
}
|
||||||
|
} else if (request.body.mode === "raw") {
|
||||||
|
pwRequest.rawInput = true
|
||||||
|
pwRequest.rawParams = request.body.raw
|
||||||
|
try {
|
||||||
|
const body = JSON.parse(request.body.raw)
|
||||||
|
pwRequest.body.body = JSON.stringify(body, null, 2)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return translateToNewRequest(pwRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeParseJSON = (str: string) => O.tryCatch(() => JSON.parse(str))
|
||||||
|
|
||||||
|
export default defineImporter({
|
||||||
|
name: "Postman Collection",
|
||||||
|
steps: [
|
||||||
|
step({
|
||||||
|
stepName: "FILE_OR_URL_IMPORT",
|
||||||
|
metadata: {
|
||||||
|
acceptedFileTypes: ".json",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
] as const,
|
||||||
|
importer: ([fileContent]) =>
|
||||||
|
pipe(
|
||||||
|
// Parse to JSON
|
||||||
|
fileContent,
|
||||||
|
safeParseJSON,
|
||||||
|
|
||||||
|
// Parse To Postman Collection
|
||||||
|
O.chain((data) => O.tryCatch(() => parsePostmanCollection(data))),
|
||||||
|
|
||||||
|
// Convert Option to Task Either
|
||||||
|
TE.fromOption(() => IMPORTER_INVALID_FILE_FORMAT)
|
||||||
|
),
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user