feat: import collections from URL (#2262)

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
This commit is contained in:
Akash K
2022-05-03 17:54:47 +05:30
committed by GitHub
parent c20339d222
commit 4ef2844a22
13 changed files with 239 additions and 59 deletions

View File

@@ -10,7 +10,7 @@ import { defineImporter, IMPORTER_INVALID_FILE_FORMAT } from "."
// TODO: Add validation to output
const fetchGist = (
url: string
): TO.TaskOption<HoppCollection<HoppRESTRequest>> =>
): TO.TaskOption<HoppCollection<HoppRESTRequest>[]> =>
pipe(
TO.tryCatch(() =>
axios.get(`https://api.github.com/gists/${url.split("/").pop()}`, {
@@ -30,8 +30,10 @@ const fetchGist = (
)
export default defineImporter({
id: "gist",
name: "import.from_gist",
icon: "github",
applicableTo: ["my-collections", "team-collections"],
steps: [
step({
stepName: "URL_IMPORT",

View File

@@ -1,13 +1,22 @@
import { pipe } from "fp-ts/function"
import { pipe, flow } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import * as E from "fp-ts/Either"
import { translateToNewRESTCollection } from "@hoppscotch/data"
import * as O from "fp-ts/Option"
import * as RA from "fp-ts/ReadonlyArray"
import {
translateToNewRESTCollection,
HoppCollection,
HoppRESTRequest,
} from "@hoppscotch/data"
import _isPlainObject from "lodash/isPlainObject"
import { step } from "../steps"
import { defineImporter, IMPORTER_INVALID_FILE_FORMAT } from "."
import { safeParseJSON } from "~/helpers/functional/json"
export default defineImporter({
id: "hoppscotch",
name: "import.from_json",
icon: "folder-plus",
applicableTo: ["my-collections", "team-collections", "url-import"],
steps: [
step({
stepName: "FILE_IMPORT",
@@ -19,16 +28,40 @@ export default defineImporter({
] as const,
importer: ([content]) =>
pipe(
E.tryCatch(
() => {
const x = JSON.parse(content)
return Array.isArray(x)
? x.map((coll: any) => translateToNewRESTCollection(coll))
: [translateToNewRESTCollection(x)]
},
() => IMPORTER_INVALID_FILE_FORMAT
safeParseJSON(content),
O.chain(
flow(
makeCollectionsArray,
RA.map(
flow(
O.fromPredicate(isValidCollection),
O.map(translateToNewRESTCollection)
)
),
O.sequenceArray,
O.map(RA.toArray)
)
),
TE.fromEither
TE.fromOption(() => IMPORTER_INVALID_FILE_FORMAT)
),
})
/**
* checks if a value is a plain object
*/
const isPlainObject = (value: any): value is object => _isPlainObject(value)
/**
* checks if a collection matches the schema for a hoppscotch collection.
* as of now we are only checking if the collection has a "v" key in it.
*/
const isValidCollection = (
collection: unknown
): collection is HoppCollection<HoppRESTRequest> =>
isPlainObject(collection) && "v" in collection
/**
* convert single collection object into an array so it can be handled the same as multiple collections
*/
const makeCollectionsArray = (collections: unknown | unknown[]): unknown[] =>
Array.isArray(collections) ? collections : [collections]

View File

@@ -13,3 +13,10 @@ export const RESTCollectionImporters = [
GistImporter,
MyCollectionsImporter,
] as const
export const URLImporters = [
HoppRESTCollImporter,
OpenAPIImporter,
PostmanImporter,
InsomniaImporter,
] as const

View File

@@ -13,10 +13,18 @@ type HoppImporter<T, StepsType, Errors> = (
stepValues: StepsOutputList<StepsType>
) => TE.TaskEither<Errors, T>
type HoppImporterApplicableTo = Array<
"team-collections" | "my-collections" | "url-import"
>
/**
* Definition for importers
*/
type HoppImporterDefinition<T, Y, E> = {
/**
* the id
*/
id: string
/**
* Name of the importer, shown on the Select Importer dropdown
*/
@@ -30,7 +38,7 @@ type HoppImporterDefinition<T, Y, E> = {
/**
* Identifier for the importer
*/
applicableTo?: Array<"team-collections" | "my-collections">
applicableTo: HoppImporterApplicableTo
/**
* The importer function, It is a Promise because its supposed to be loaded in lazily (dynamic imports ?)
@@ -47,10 +55,11 @@ type HoppImporterDefinition<T, Y, E> = {
* Defines a Hoppscotch importer
*/
export const defineImporter = <ReturnType, StepType, Errors>(input: {
id: string
name: string
icon: string
importer: HoppImporter<ReturnType, StepType, Errors>
applicableTo?: Array<"team-collections" | "my-collections">
applicableTo: HoppImporterApplicableTo
steps: StepType
}) => {
return <HoppImporterDefinition<ReturnType, StepType, Errors>>{

View File

@@ -210,7 +210,9 @@ const getHoppCollections = (doc: InsomniaDoc) =>
)
export default defineImporter({
id: "insomnia",
name: "import.from_insomnia",
applicableTo: ["my-collections", "team-collections", "url-import"],
icon: "insomnia",
steps: [
step({

View File

@@ -6,6 +6,7 @@ import { defineImporter } from "."
import { getRESTCollection } from "~/newstore/collections"
export default defineImporter({
id: "myCollections",
name: "import.from_my_collections",
icon: "user",
applicableTo: ["team-collections"],

View File

@@ -27,7 +27,7 @@ import * as RA from "fp-ts/ReadonlyArray"
import { step } from "../steps"
import { defineImporter, IMPORTER_INVALID_FILE_FORMAT } from "."
const OPENAPI_DEREF_ERROR = "openapi/deref_error" as const
export const OPENAPI_DEREF_ERROR = "openapi/deref_error" as const
// TODO: URL Import Support
@@ -586,7 +586,9 @@ const parseOpenAPIDocContent = (str: string) =>
)
export default defineImporter({
id: "openapi",
name: "import.from_openapi",
applicableTo: ["my-collections", "team-collections", "url-import"],
icon: "file",
steps: [
step({
@@ -603,7 +605,6 @@ export default defineImporter({
fileContent,
parseOpenAPIDocContent,
TE.fromOption(() => IMPORTER_INVALID_FILE_FORMAT),
// Try validating, else the importer is invalid file format
TE.chainW((obj) =>
pipe(
@@ -613,7 +614,6 @@ export default defineImporter({
)
)
),
// Deference the references
TE.chainW((obj) =>
pipe(
@@ -623,7 +623,6 @@ export default defineImporter({
)
)
),
TE.chainW(convertOpenApiDocToHopp)
),
})

View File

@@ -297,7 +297,9 @@ const getHoppFolder = (ig: ItemGroup<Item>): HoppCollection<HoppRESTRequest> =>
export const getHoppCollection = (coll: PMCollection) => getHoppFolder(coll)
export default defineImporter({
id: "postman",
name: "import.from_postman",
applicableTo: ["my-collections", "team-collections", "url-import"],
icon: "postman",
steps: [
step({