diff --git a/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte b/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte new file mode 100644 index 0000000000..2dac2fec79 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte @@ -0,0 +1,73 @@ + + +
dispatcher("selected", integrationType)} + class="item hoverable" +> +
+ +
+ {schema.friendlyName} + {#if schema.type} + {schema.type || ""} + {/if} +
+
+
+ + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Custom.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Custom.svelte index 354b7a3358..0d4b7219aa 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/icons/Custom.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Custom.svelte @@ -1,8 +1,6 @@ - - - - - - + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte index 2d5e6976bf..945cd574d8 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte @@ -18,6 +18,7 @@ import { createRestDatasource } from "builderStore/datasource" import { goto } from "@roxi/routify" import ImportRestQueriesModal from "./ImportRestQueriesModal.svelte" + import DatasourceCard from "../_components/DatasourceCard.svelte" export let modal let integrations = {} @@ -49,6 +50,9 @@ schema: selected.datasource, auth: selected.auth, } + if (selected.friendlyName) { + integration.name = selected.friendlyName + } checkShowImport() } @@ -92,14 +96,6 @@ } integrations = newIntegrations } - - function getIcon(integrationType, schema) { - if (schema.custom) { - return ICONS.CUSTOM - } else { - return ICONS[integrationType] - } - } @@ -158,28 +154,24 @@ Connect to an external data source
- {#each Object.entries(integrations).filter(([key]) => key !== IntegrationTypes.INTERNAL) as [integrationType, schema]} -
selectIntegration(integrationType)} - class="item hoverable" - > -
- -
- {schema.friendlyName} - {#if schema.type} - {schema.type || ""} - {/if} -
-
-
+ {#each Object.entries(integrations).filter(([key, val]) => key !== IntegrationTypes.INTERNAL && !val.custom) as [integrationType, schema]} + selectIntegration(evt.detail)} + {schema} + bind:integrationType + {integration} + /> {/each}
+ Custom data source + {#each Object.entries(integrations).filter(entry => entry[1].custom) as [integrationType, schema]} + selectIntegration(evt.detail)} + {schema} + bind:integrationType + {integration} + /> + {/each}
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte index 0aaa2b201d..c8a5bc96eb 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte @@ -14,8 +14,14 @@ let datasource = cloneDeep(integration) let skipFetch = false + $: name = + IntegrationNames[datasource.type] || datasource.name || datasource.type + async function saveDatasource() { try { + if (!datasource.name) { + datasource.name = name + } const resp = await save(datasource, skipFetch) $goto(`./datasource/${resp._id}`) notifications.success(`Datasource updated successfully.`) @@ -32,7 +38,7 @@ saveDatasource()} onCancel={() => modal.show()} confirmText={datasource.plus diff --git a/packages/server/src/integrations/index.ts b/packages/server/src/integrations/index.ts index bf267e46cc..b9523061a4 100644 --- a/packages/server/src/integrations/index.ts +++ b/packages/server/src/integrations/index.ts @@ -15,6 +15,7 @@ import redis from "./redis" import snowflake from "./snowflake" import { getPlugins } from "../api/controllers/plugin" import { SourceName, Integration, PluginType } from "@budibase/types" +import { getDatasourcePlugin } from "../utilities/fileSystem" const environment = require("../environment") const { cloneDeep } = require("lodash") @@ -65,6 +66,8 @@ if (environment.SELF_HOSTED) { DEFINITIONS[SourceName.GOOGLE_SHEETS] = googlesheets.schema } +function isIntegrationAvailable(integration: string) {} + module.exports = { getDefinitions: async () => { const plugins = await getPlugins(PluginType.DATASOURCE) @@ -82,7 +85,16 @@ module.exports = { ...pluginSchemas, } }, - getIntegration: async () => { - return INTEGRATIONS + getIntegration: async (integration: string) => { + if (INTEGRATIONS[integration]) { + return INTEGRATIONS[integration] + } + const plugins = await getPlugins(PluginType.DATASOURCE) + for (let plugin of plugins) { + if (plugin.name === integration) { + // need to use commonJS require due to its dynamic runtime nature + return getDatasourcePlugin(plugin.name, plugin.jsUrl) + } + } }, } diff --git a/packages/server/src/utilities/fileSystem/index.js b/packages/server/src/utilities/fileSystem/index.js index 94c5a92375..d65ee48f05 100644 --- a/packages/server/src/utilities/fileSystem/index.js +++ b/packages/server/src/utilities/fileSystem/index.js @@ -17,6 +17,7 @@ const { downloadTarball, } = require("./utilities") const { updateClientLibrary } = require("./clientLibrary") +const { checkSlashesInUrl } = require("../") const env = require("../../environment") const { USER_METDATA_PREFIX, @@ -26,9 +27,11 @@ const { const MemoryStream = require("memorystream") const { getAppId } = require("@budibase/backend-core/context") const tar = require("tar") +const fetch = require("node-fetch") const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..") const NODE_MODULES_PATH = join(TOP_LEVEL_PATH, "node_modules") +const DATASOURCE_PATH = join(budibaseTempDir(), "datasource") /** * The single stack system (Cloud and Builder) should not make use of the file system where possible, @@ -348,6 +351,26 @@ exports.extractPluginTarball = async file => { return { metadata, directory: path } } +exports.getDatasourcePlugin = async (name, url) => { + if (!fs.existsSync(DATASOURCE_PATH)) { + fs.mkdirSync(DATASOURCE_PATH) + } + const filename = join(DATASOURCE_PATH, name) + if (fs.existsSync(filename)) { + return require(filename) + } + const response = await fetch(checkSlashesInUrl(`${env.MINIO_URL}/${url}`)) + if (response.status === 200) { + const content = await response.text() + fs.writeFileSync(filename, content) + require(filename) + } else { + throw new Error( + `Unable to retrieve plugin - reason: ${await response.text()}` + ) + } +} + /** * Full function definition for below can be found in the utilities. */