feat: collection import summaries (#4489)

Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Akash K
2024-11-25 11:42:26 +05:30
committed by GitHub
parent c74c42ebaf
commit b706a43d2e
10 changed files with 450 additions and 43 deletions

View File

@@ -502,8 +502,11 @@
"from_file": "Import from File",
"from_gist": "Import from Gist",
"from_gist_description": "Import from Gist URL",
"from_gist_import_summary": "All hoppscotch features are imported.",
"from_hoppscotch_importer_summary": "All hoppscotch features are imported.",
"from_insomnia": "Import from Insomnia",
"from_insomnia_description": "Import from Insomnia collection",
"from_insomnia_import_summary": "Collections and Requests will be imported.",
"from_json": "Import from Hoppscotch",
"from_json_description": "Import from Hoppscotch collection file",
"from_my_collections": "Import from Personal Collections",
@@ -512,12 +515,15 @@
"from_all_collections_description": "Import any collection from Another Workspace to the current workspace",
"from_openapi": "Import from OpenAPI",
"from_openapi_description": "Import from OpenAPI specification file (YML/JSON)",
"from_openapi_import_summary": "Collections ( will be created from tags ), Requests and response examples will be imported.",
"from_postman": "Import from Postman",
"from_postman_description": "Import from Postman collection",
"from_postman_import_summary": "Collections, Requests and response examples will be imported.",
"from_url": "Import from URL",
"gist_url": "Enter Gist URL",
"from_har": "Import from HAR",
"from_har_description": "Import from HAR file",
"from_har_import_summary": "Requests will be imported to a default collection.",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
@@ -532,7 +538,13 @@
"title": "Import",
"file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of {sizeLimit}MB. Only the first {files} selected will be imported",
"file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of {sizeLimit}MB. Please select another file.",
"success": "Successfully imported"
"success": "Successfully imported",
"import_summary_collections_title": "Collections",
"import_summary_requests_title": "Requests",
"import_summary_responses_title": "Responses",
"import_summary_pre_request_scripts_title": "Pre-request scripts",
"import_summary_test_scripts_title": "Test scripts",
"import_summary_not_supported_by_hoppscotch_import": "We do not support importing {featureLabel} from this source right now."
},
"inspections": {
"description": "Inspect possible errors",

View File

@@ -11,7 +11,7 @@
<script setup lang="ts">
import { HoppCollection } from "@hoppscotch/data"
import * as E from "fp-ts/Either"
import { PropType, computed, ref } from "vue"
import { PropType, Ref, computed, ref } from "vue"
import { FileSource } from "~/helpers/import-export/import/import-sources/FileSource"
import { UrlSource } from "~/helpers/import-export/import/import-sources/UrlSource"
@@ -106,7 +106,6 @@ const handleImportToStore = async (collections: HoppCollection[]) => {
if (E.isRight(importResult)) {
toast.success(t("state.file_imported"))
emit("hide-modal")
} else {
toast.error(t("import.failed"))
}
@@ -175,6 +174,24 @@ const isTeamWorkspace = computed(() => {
return props.collectionsType.type === "team-collections"
})
const currentImportSummary: Ref<{
showImportSummary: boolean
importedCollections: HoppCollection[] | null
}> = ref({
showImportSummary: false,
importedCollections: null,
})
const setCurrentImportSummary = (collections: HoppCollection[]) => {
currentImportSummary.value.importedCollections = collections
currentImportSummary.value.showImportSummary = true
}
const unsetCurrentImportSummary = () => {
currentImportSummary.value.importedCollections = null
currentImportSummary.value.showImportSummary = false
}
const HoppRESTImporter: ImporterOrExporter = {
metadata: {
id: "hopp_rest",
@@ -183,7 +200,9 @@ const HoppRESTImporter: ImporterOrExporter = {
icon: IconFolderPlus,
disabled: false,
applicableTo: ["personal-workspace", "team-workspace", "url-import"],
format: "hoppscotch",
},
importSummary: currentImportSummary,
component: FileSource({
caption: "import.from_file",
acceptedFileTypes: ".json",
@@ -194,6 +213,8 @@ const HoppRESTImporter: ImporterOrExporter = {
if (E.isRight(res)) {
await handleImportToStore(res.right)
setCurrentImportSummary(res.right)
platform.analytics?.logEvent({
type: "HOPP_IMPORT_COLLECTION",
importer: "import.from_json",
@@ -202,10 +223,13 @@ const HoppRESTImporter: ImporterOrExporter = {
})
} else {
showImportFailedError()
unsetCurrentImportSummary()
}
isRESTImporterInProgress.value = false
},
description: "import.from_hoppscotch_importer_summary",
isLoading: isRESTImporterInProgress,
}),
}
@@ -218,6 +242,7 @@ const HoppAllCollectionImporter: ImporterOrExporter = {
icon: IconUser,
disabled: false,
applicableTo: ["personal-workspace", "team-workspace"],
format: "hoppscotch",
},
onSelect() {
if (!currentUser.value) {
@@ -227,19 +252,26 @@ const HoppAllCollectionImporter: ImporterOrExporter = {
return false
},
importSummary: currentImportSummary,
component: defineStep("all_collection_import", AllCollectionImport, () => ({
loading: isAllCollectionImporterInProgress.value,
async onImportCollection(content) {
isAllCollectionImporterInProgress.value = true
await handleImportToStore([content])
try {
await handleImportToStore([content])
setCurrentImportSummary([content])
// our analytics consider this as an export event, so keeping compatibility with that
platform.analytics?.logEvent({
type: "HOPP_EXPORT_COLLECTION",
exporter: "import_to_teams",
platform: "rest",
})
// our analytics consider this as an export event, so keeping compatibility with that
platform.analytics?.logEvent({
type: "HOPP_EXPORT_COLLECTION",
exporter: "import_to_teams",
platform: "rest",
})
} catch (e) {
showImportFailedError()
unsetCurrentImportSummary()
}
isAllCollectionImporterInProgress.value = false
},
@@ -254,7 +286,9 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
icon: IconOpenAPI,
disabled: false,
applicableTo: ["personal-workspace", "team-workspace", "url-import"],
format: "openapi",
},
importSummary: currentImportSummary,
supported_sources: [
{
id: "file_import",
@@ -263,6 +297,7 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
step: FileSource({
caption: "import.from_file",
acceptedFileTypes: ".json, .yaml, .yml",
description: "import.from_openapi_import_summary",
onImportFromFile: async (content) => {
isOpenAPIImporterInProgress.value = true
@@ -271,6 +306,8 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
if (E.isRight(res)) {
await handleImportToStore(res.right)
setCurrentImportSummary(res.right)
platform.analytics?.logEvent({
platform: "rest",
type: "HOPP_IMPORT_COLLECTION",
@@ -279,6 +316,8 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
})
} else {
showImportFailedError()
unsetCurrentImportSummary()
}
isOpenAPIImporterInProgress.value = false
@@ -292,6 +331,7 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
icon: IconLink,
step: UrlSource({
caption: "import.from_url",
description: "import.from_openapi_import_summary",
onImportFromURL: async (content) => {
isOpenAPIImporterInProgress.value = true
@@ -300,6 +340,8 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
if (E.isRight(res)) {
await handleImportToStore(res.right)
setCurrentImportSummary(res.right)
platform.analytics?.logEvent({
platform: "rest",
type: "HOPP_IMPORT_COLLECTION",
@@ -308,6 +350,8 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
})
} else {
showImportFailedError()
unsetCurrentImportSummary()
}
isOpenAPIImporterInProgress.value = false
@@ -326,10 +370,13 @@ const HoppPostmanImporter: ImporterOrExporter = {
icon: IconPostman,
disabled: false,
applicableTo: ["personal-workspace", "team-workspace", "url-import"],
format: "postman",
},
importSummary: currentImportSummary,
component: FileSource({
caption: "import.from_file",
acceptedFileTypes: ".json",
description: "import.from_postman_import_summary",
onImportFromFile: async (content) => {
isPostmanImporterInProgress.value = true
@@ -338,6 +385,8 @@ const HoppPostmanImporter: ImporterOrExporter = {
if (E.isRight(res)) {
await handleImportToStore(res.right)
setCurrentImportSummary(res.right)
platform.analytics?.logEvent({
platform: "rest",
type: "HOPP_IMPORT_COLLECTION",
@@ -346,6 +395,8 @@ const HoppPostmanImporter: ImporterOrExporter = {
})
} else {
showImportFailedError()
unsetCurrentImportSummary()
}
isPostmanImporterInProgress.value = false
@@ -362,10 +413,13 @@ const HoppInsomniaImporter: ImporterOrExporter = {
icon: IconInsomnia,
disabled: false,
applicableTo: ["personal-workspace", "team-workspace", "url-import"],
format: "insomnia",
},
importSummary: currentImportSummary,
component: FileSource({
caption: "import.from_file",
acceptedFileTypes: ".json",
description: "import.from_insomnia_import_summary",
onImportFromFile: async (content) => {
isInsomniaImporterInProgress.value = true
@@ -374,6 +428,8 @@ const HoppInsomniaImporter: ImporterOrExporter = {
if (E.isRight(res)) {
await handleImportToStore(res.right)
setCurrentImportSummary(res.right)
platform.analytics?.logEvent({
platform: "rest",
type: "HOPP_IMPORT_COLLECTION",
@@ -382,6 +438,8 @@ const HoppInsomniaImporter: ImporterOrExporter = {
})
} else {
showImportFailedError()
unsetCurrentImportSummary()
}
isInsomniaImporterInProgress.value = false
@@ -398,9 +456,12 @@ const HoppGistImporter: ImporterOrExporter = {
icon: IconGithub,
disabled: false,
applicableTo: ["personal-workspace", "team-workspace", "url-import"],
format: "hoppscotch",
},
importSummary: currentImportSummary,
component: GistSource({
caption: "import.from_url",
description: "import.from_gist_import_summary",
onImportFromGist: async (content) => {
if (E.isLeft(content)) {
showImportFailedError()
@@ -414,6 +475,8 @@ const HoppGistImporter: ImporterOrExporter = {
if (E.isRight(res)) {
await handleImportToStore(res.right)
setCurrentImportSummary(res.right)
platform.analytics?.logEvent({
platform: "rest",
type: "HOPP_IMPORT_COLLECTION",
@@ -422,6 +485,8 @@ const HoppGistImporter: ImporterOrExporter = {
})
} else {
showImportFailedError()
unsetCurrentImportSummary()
}
isGistImporterInProgress.value = false
@@ -439,7 +504,9 @@ const HoppMyCollectionsExporter: ImporterOrExporter = {
disabled: false,
applicableTo: ["personal-workspace"],
isLoading: isHoppMyCollectionExporterInProgress,
format: "hoppscotch",
},
importSummary: currentImportSummary,
action: async () => {
if (!myCollections.value.length) {
return toast.error(t("error.no_collections_to_export"))
@@ -478,6 +545,7 @@ const HoppTeamCollectionsExporter: ImporterOrExporter = {
applicableTo: ["team-workspace"],
isLoading: isHoppTeamCollectionExporterInProgress,
},
importSummary: currentImportSummary,
action: async () => {
isHoppTeamCollectionExporterInProgress.value = true
if (
@@ -574,10 +642,13 @@ const HARImporter: ImporterOrExporter = {
icon: IconFile,
disabled: false,
applicableTo: ["personal-workspace", "team-workspace"],
format: "har",
},
importSummary: currentImportSummary,
component: FileSource({
caption: "import.from_file",
acceptedFileTypes: ".har",
description: "import.from_har_import_summary",
onImportFromFile: async (content) => {
isHarImporterInProgress.value = true
@@ -586,6 +657,8 @@ const HARImporter: ImporterOrExporter = {
if (E.isRight(res)) {
await handleImportToStore(res.right)
setCurrentImportSummary(res.right)
platform.analytics?.logEvent({
type: "HOPP_IMPORT_COLLECTION",
importer: "import.from_har",
@@ -594,6 +667,8 @@ const HARImporter: ImporterOrExporter = {
})
} else {
showImportFailedError()
unsetCurrentImportSummary()
}
isHarImporterInProgress.value = false

View File

@@ -7,7 +7,7 @@
>
<template #actions>
<HoppButtonSecondary
v-if="hasPreviousStep"
v-if="hasPreviousStep && !isImportSummaryStep"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.go_back')"
:icon="IconArrowLeft"
@@ -23,13 +23,14 @@
import IconArrowLeft from "~icons/lucide/arrow-left"
import { useI18n } from "~/composables/i18n"
import { PropType, ref } from "vue"
import { computed, PropType, ref, watch } from "vue"
import { useSteps, defineStep } from "~/composables/step-components"
import ImportExportList from "./ImportExportList.vue"
import ImportExportSourcesList from "./ImportExportSourcesList.vue"
import { ImporterOrExporter } from "~/components/importExport/types"
import ImportSummary from "~/components/importExport/ImportExportSteps/ImportSummary.vue"
const t = useI18n()
@@ -60,6 +61,10 @@ const {
hasPreviousStep,
} = useSteps()
const isImportSummaryStep = computed(() => {
return currentStep.value.id.startsWith("import_summary_")
})
const selectedImporterID = ref<string | null>(null)
const selectedSourceID = ref<string | null>(null)
@@ -154,10 +159,50 @@ const chooseImportSource = defineStep(
addStep(chooseImporterOrExporter)
addStep(chooseImportSource)
const selectedImporterImportSummary = computed(() => {
const importer = props.importerModules.find(
(i) => i.metadata.id === selectedImporterID.value
)
if (!importer?.importSummary) return null
return importer.importSummary
})
watch(
selectedImporterImportSummary,
(val) => {
if (val?.value.showImportSummary) {
goToStep(`import_summary_${selectedImporterID.value}`)
}
},
{ deep: true }
)
props.importerModules.forEach((importer) => {
if (importer.component) {
addStep(importer.component)
}
const importSummary = importer.importSummary
if (!importSummary) {
return
}
if (importSummary.value) {
addStep({
id: `import_summary_${importer.metadata.id}`,
component: ImportSummary,
props: () => ({
collections: importSummary.value.importedCollections,
importFormat: importer.metadata.format,
"on-close": () => {
emit("hide-modal")
},
}),
})
}
})
const emit = defineEmits<{

View File

@@ -1,22 +1,26 @@
<template>
<div class="space-y-4">
<p class="flex items-center">
<span
class="inline-flex items-center justify-center flex-shrink-0 mr-4 border-4 rounded-full border-primary text-dividerDark"
:class="{
'!text-green-500': hasFile,
}"
>
<icon-lucide-check-circle class="svg-icons" />
</span>
<span>
{{ t(`${caption}`) }}
</span>
</p>
<div>
<p class="flex items-center">
<span
class="inline-flex items-center justify-center flex-shrink-0 mr-4 border-4 rounded-full border-primary text-dividerDark"
:class="{
'!text-green-500': hasFile,
}"
>
<icon-lucide-check-circle class="svg-icons" />
</span>
<span>
{{ t(`${caption}`) }}
</span>
</p>
<div
class="flex flex-col ml-10 border border-dashed rounded border-dividerDark"
>
<p v-if="description" class="ml-10 mt-2 text-secondaryLight">
{{ t(description) }}
</p>
</div>
<div class="flex flex-col border border-dashed rounded border-dividerDark">
<input
id="inputChooseFileToImportFrom"
ref="inputChooseFileToImportFrom"
@@ -71,9 +75,11 @@ const props = withDefaults(
caption: string
acceptedFileTypes: string
loading?: boolean
description?: string
}>(),
{
loading: false,
description: undefined,
}
)

View File

@@ -0,0 +1,242 @@
<script setup lang="ts">
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"
import { computed, Ref, ref, watch } from "vue"
import { useI18n } from "~/composables/i18n"
import IconInfo from "~icons/lucide/info"
import { SupportedImportFormat } from "./../types"
const t = useI18n()
type Feature =
| "collections"
| "requests"
| "responses"
| "preRequestScripts"
| "testScripts"
type FeatureStatus =
| "SUPPORTED"
| "NOT_SUPPORTED_BY_HOPPSCOTCH_IMPORT"
| "NOT_SUPPORTED_BY_SOURCE"
type FeatureWithCount = {
count: number
label: string
id: Feature
}
const props = defineProps<{
importFormat: SupportedImportFormat
collections: HoppCollection[]
onClose: () => void
}>()
const importSourceAndSupportedFeatures: Record<
SupportedImportFormat,
Record<Feature, FeatureStatus>
> = {
hoppscotch: {
collections: "SUPPORTED",
requests: "SUPPORTED",
responses: "SUPPORTED",
preRequestScripts: "SUPPORTED",
testScripts: "SUPPORTED",
},
postman: {
collections: "SUPPORTED",
requests: "SUPPORTED",
responses: "SUPPORTED",
preRequestScripts: "NOT_SUPPORTED_BY_HOPPSCOTCH_IMPORT",
testScripts: "NOT_SUPPORTED_BY_HOPPSCOTCH_IMPORT",
},
insomnia: {
collections: "SUPPORTED",
requests: "SUPPORTED",
responses: "NOT_SUPPORTED_BY_SOURCE",
preRequestScripts: "NOT_SUPPORTED_BY_HOPPSCOTCH_IMPORT",
testScripts: "NOT_SUPPORTED_BY_HOPPSCOTCH_IMPORT",
},
openapi: {
collections: "SUPPORTED",
requests: "SUPPORTED",
responses: "SUPPORTED",
preRequestScripts: "NOT_SUPPORTED_BY_SOURCE",
testScripts: "NOT_SUPPORTED_BY_SOURCE",
},
har: {
collections: "SUPPORTED",
requests: "SUPPORTED",
responses: "NOT_SUPPORTED_BY_HOPPSCOTCH_IMPORT",
preRequestScripts: "NOT_SUPPORTED_BY_SOURCE",
testScripts: "NOT_SUPPORTED_BY_SOURCE",
},
}
const featuresWithCount: Ref<FeatureWithCount[]> = ref([])
const countCollections = (collections: HoppCollection[]) => {
let collectionCount = 0
let requestCount = 0
let preRequestScriptsCount = 0
let testScriptsCount = 0
let responseCount = 0
const flattenHoppCollections = (_collections: HoppCollection[]) => {
_collections.forEach((collection) => {
collectionCount++
collection.requests.forEach((request) => {
requestCount++
const _request = request as HoppRESTRequest
preRequestScriptsCount += !!_request.preRequestScript?.trim() ? 1 : 0
testScriptsCount += !!_request.testScript?.trim() ? 1 : 0
responseCount += _request.responses
? Object.values(_request.responses).length
: 0
})
flattenHoppCollections(collection.folders)
})
}
flattenHoppCollections(collections)
return {
collectionCount,
requestCount,
responseCount,
preRequestScriptsCount,
testScriptsCount,
}
}
watch(
props.collections,
(collections) => {
const {
collectionCount,
requestCount,
responseCount,
preRequestScriptsCount,
testScriptsCount,
} = countCollections(collections)
featuresWithCount.value = [
{
count: collectionCount,
label: "import.import_summary_collections_title",
id: "collections" as const,
},
{
count: requestCount,
label: "import.import_summary_requests_title",
id: "requests" as const,
},
{
count: responseCount,
label: "import.import_summary_responses_title",
id: "responses" as const,
},
{
count: preRequestScriptsCount,
label: "import.import_summary_pre_request_scripts_title",
id: "preRequestScripts" as const,
},
{
count: testScriptsCount,
label: "import.import_summary_test_scripts_title",
id: "testScripts" as const,
},
]
},
{
immediate: true,
}
)
const featureSupportForImportFormat = computed(() => {
return importSourceAndSupportedFeatures[props.importFormat]
})
const visibleFeatures = computed(() => {
return featuresWithCount.value.filter((feature) => {
return (
importSourceAndSupportedFeatures[props.importFormat][feature.id] !==
"NOT_SUPPORTED_BY_SOURCE"
)
})
})
</script>
<template>
<div class="space-y-4">
<div v-for="feature in visibleFeatures" :key="feature.id">
<p class="flex items-center">
<span
class="inline-flex items-center justify-center flex-shrink-0 mr-4 border-4 rounded-full border-primary"
:class="{
'text-green-500':
featureSupportForImportFormat[feature.id] === 'SUPPORTED',
'text-amber-500':
featureSupportForImportFormat[feature.id] ===
'NOT_SUPPORTED_BY_HOPPSCOTCH_IMPORT',
}"
>
<icon-lucide-check-circle
v-if="featureSupportForImportFormat[feature.id] === 'SUPPORTED'"
class="svg-icons"
/>
<IconInfo
v-else-if="
featureSupportForImportFormat[feature.id] ===
'NOT_SUPPORTED_BY_HOPPSCOTCH_IMPORT'
"
class="svg-icons"
/>
</span>
<span>{{ t(feature.label) }}</span>
</p>
<p class="ml-10 text-secondaryLight">
<template
v-if="featureSupportForImportFormat[feature.id] === 'SUPPORTED'"
>
{{ feature.count }}
{{
feature.count != 1
? t(feature.label)
: t(feature.label).slice(0, -1)
}}
Imported
</template>
<template
v-else-if="
featureSupportForImportFormat[feature.id] ===
'NOT_SUPPORTED_BY_HOPPSCOTCH_IMPORT'
"
>
{{
t("import.import_summary_not_supported_by_hoppscotch_import", {
featureLabel: t(feature.label),
})
}}
</template>
</p>
</div>
</div>
<div class="mt-10">
<HoppButtonSecondary
class="w-full"
:label="t('action.close')"
outline
filled
@click="onClose"
/>
</div>
</template>

View File

@@ -1,19 +1,26 @@
<template>
<div class="space-y-4">
<p class="flex items-center">
<span
class="inline-flex items-center justify-center flex-shrink-0 mr-4 border-4 rounded-full border-primary text-dividerDark"
:class="{
'!text-green-500': hasURL,
}"
>
<icon-lucide-check-circle class="svg-icons" />
</span>
<span>
{{ t(caption) }}
</span>
</p>
<p class="flex flex-col ml-10">
<div>
<p class="flex items-center">
<span
class="inline-flex items-center justify-center flex-shrink-0 mr-4 border-4 rounded-full border-primary text-dividerDark"
:class="{
'!text-green-500': hasURL,
}"
>
<icon-lucide-check-circle class="svg-icons" />
</span>
<span>
{{ t(caption) }}
</span>
</p>
<p v-if="description" class="ml-10 mt-2 text-secondaryLight">
{{ t(description) }}
</p>
</div>
<p class="flex flex-col">
<input
v-model="inputChooseGistToImportFrom"
type="url"
@@ -49,8 +56,9 @@ const props = withDefaults(
caption: string
fetchLogic?: (url: string) => Promise<AxiosResponse<any>>
loading?: boolean
description?: string
}>(),
{ fetchLogic: undefined, loading: false }
{ fetchLogic: undefined, loading: false, description: undefined }
)
const emit = defineEmits<{

View File

@@ -1,6 +1,14 @@
import { HoppCollection } from "@hoppscotch/data"
import { Component, Ref } from "vue"
import { defineStep } from "~/composables/step-components"
export type SupportedImportFormat =
| "hoppscotch"
| "postman"
| "insomnia"
| "openapi"
| "har"
// TODO: move the metadata except disabled and isLoading to importers.ts
export type ImporterOrExporter = {
metadata: {
@@ -11,6 +19,7 @@ export type ImporterOrExporter = {
disabled: boolean
applicableTo: Array<"personal-workspace" | "team-workspace" | "url-import">
isLoading?: Ref<boolean>
format?: SupportedImportFormat
}
supported_sources?: {
id: string
@@ -18,6 +27,10 @@ export type ImporterOrExporter = {
icon: Component
step: ReturnType<typeof defineStep>
}[]
importSummary?: Ref<{
showImportSummary: boolean
importedCollections: HoppCollection[] | null
}>
component?: ReturnType<typeof defineStep>
action?: (...args: any[]) => any
onSelect?: () => boolean

View File

@@ -9,6 +9,7 @@ export function FileSource(metadata: {
caption: string
onImportFromFile: (content: string[]) => any | Promise<any>
isLoading?: Ref<boolean>
description?: string
}) {
const stepID = uuidv4()
@@ -17,5 +18,6 @@ export function FileSource(metadata: {
caption: metadata.caption,
onImportFromFile: metadata.onImportFromFile,
loading: metadata.isLoading?.value,
description: metadata.description,
}))
}

View File

@@ -14,11 +14,13 @@ export function GistSource(metadata: {
importResult: E.Either<string, string[]>
) => any | Promise<any>
isLoading?: Ref<boolean>
description?: string
}) {
const stepID = uuidv4()
return defineStep(stepID, UrlImport, () => ({
caption: metadata.caption,
description: metadata.description,
onImportFromURL: (gistResponse: unknown) => {
const fileSchema = z.object({
files: z.record(z.object({ content: z.string() })),

View File

@@ -9,6 +9,7 @@ export function UrlSource(metadata: {
onImportFromURL: (content: string) => any | Promise<any>
fetchLogic?: (url: string) => Promise<any>
isLoading?: Ref<boolean>
description: string
}) {
const stepID = uuidv4()
@@ -20,5 +21,6 @@ export function UrlSource(metadata: {
}
},
loading: metadata.isLoading?.value,
description: metadata.description,
}))
}