From 95136a54f2731d800102f02340e8195a56e6ae53 Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Thu, 29 Sep 2022 19:30:53 +0100
Subject: [PATCH] Adding ability for datasource plugins to have a custom icon
 svg.

---
 .../backend-core/src/objectStore/index.ts     |  5 +++
 .../DatasourceNavigator.svelte                | 10 ++---
 .../IntegrationIcon.svelte                    | 40 +++++++++++++++++++
 .../_components/DatasourceCard.svelte         |  8 +---
 .../DatasourceNavigator/icons/index.js        | 11 +++--
 .../src/components/common/CustomSVG.svelte    | 23 +++++++++++
 packages/server/src/integrations/index.ts     |  3 ++
 packages/types/src/documents/global/plugin.ts |  1 +
 packages/types/src/sdk/datasources.ts         |  1 +
 9 files changed, 88 insertions(+), 14 deletions(-)
 create mode 100644 packages/builder/src/components/backend/DatasourceNavigator/IntegrationIcon.svelte
 create mode 100644 packages/builder/src/components/common/CustomSVG.svelte

diff --git a/packages/backend-core/src/objectStore/index.ts b/packages/backend-core/src/objectStore/index.ts
index a97aa8f65d..17e002cc49 100644
--- a/packages/backend-core/src/objectStore/index.ts
+++ b/packages/backend-core/src/objectStore/index.ts
@@ -182,6 +182,11 @@ export const streamUpload = async (
       ...extra,
       ContentType: "application/javascript",
     }
+  } else if (filename?.endsWith(".svg")) {
+    extra = {
+      ...extra,
+      ContentType: "image",
+    }
   }
 
   const params = {
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte
index 19946a2386..a3531513fb 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte
@@ -13,7 +13,7 @@
     customQueryIconColor,
     customQueryText,
   } from "helpers/data/utils"
-  import { getIcon } from "./icons"
+  import IntegrationIcon from "./IntegrationIcon.svelte"
   import { notifications } from "@budibase/bbui"
 
   let openDataSources = []
@@ -123,10 +123,10 @@
         on:iconClick={() => toggleNode(datasource)}
       >
         <div class="datasource-icon" slot="icon">
-          <svelte:component
-            this={getIcon(datasource.source, datasource.schema)}
-            height="18"
-            width="18"
+          <IntegrationIcon
+            integrationType={datasource.source}
+            schema={datasource.schema}
+            size="18"
           />
         </div>
         {#if datasource._id !== BUDIBASE_INTERNAL_DB}
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/IntegrationIcon.svelte b/packages/builder/src/components/backend/DatasourceNavigator/IntegrationIcon.svelte
new file mode 100644
index 0000000000..54afa35e74
--- /dev/null
+++ b/packages/builder/src/components/backend/DatasourceNavigator/IntegrationIcon.svelte
@@ -0,0 +1,40 @@
+<script>
+  import { getIcon } from "./icons"
+  import CustomSVG from "components/common/CustomSVG.svelte"
+  import { admin } from "stores/portal"
+
+  export let integrationType
+  export let schema
+  export let size = "18"
+
+  $: objectStoreUrl = $admin.cloud ? "https://cdn.budi.live" : ""
+  $: pluginsUrl = `${objectStoreUrl}/plugins`
+  $: iconInfo = getIcon(integrationType, schema)
+
+  async function getSvgFromUrl(info) {
+    const url = `${pluginsUrl}/${info.url}`
+    const resp = await fetch(url, {
+      headers: {
+        pragma: "no-cache",
+        "cache-control": "no-cache",
+      },
+    })
+    let text = await resp.text()
+    // explicitly only want to replace the first instance
+    if (text.includes("height=")) {
+      text = text.replace(/height="\d*"/, `height="${size}"`)
+    }
+    if (text.includes("width=")) {
+      text = text.replace(/width="\d*"/, `width="${size}"`)
+    }
+    return text
+  }
+</script>
+
+{#if iconInfo.icon}
+  <svelte:component this={iconInfo.icon} height={size} width={size} />
+{:else if iconInfo.url}
+  {#await getSvgFromUrl(iconInfo) then retrievedSvg}
+    <CustomSVG {size} svgHtml={retrievedSvg} />
+  {/await}
+{/if}
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte b/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte
index 6dffc70a63..1e966ebb2b 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte
@@ -1,7 +1,7 @@
 <script>
   import { createEventDispatcher } from "svelte"
   import { Heading, Detail } from "@budibase/bbui"
-  import { getIcon } from "../icons"
+  import IntegrationIcon from "../IntegrationIcon.svelte"
 
   export let integration
   export let integrationType
@@ -16,11 +16,7 @@
   class="item hoverable"
 >
   <div class="item-body" class:with-type={!!schema.type}>
-    <svelte:component
-      this={getIcon(integrationType, schema)}
-      height="20"
-      width="20"
-    />
+    <IntegrationIcon {integrationType} {schema} size="25" />
     <div class="text">
       <Heading size="XXS">{schema.friendlyName}</Heading>
       {#if schema.type}
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
index 6d43258f45..18aa361570 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
+++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
@@ -16,6 +16,8 @@ import Firebase from "./Firebase.svelte"
 import Redis from "./Redis.svelte"
 import Snowflake from "./Snowflake.svelte"
 import Custom from "./Custom.svelte"
+import { integrations } from "stores/backend"
+import { get } from "svelte/store"
 
 const ICONS = {
   BUDIBASE: Budibase,
@@ -41,9 +43,12 @@ const ICONS = {
 export default ICONS
 
 export function getIcon(integrationType, schema) {
-  if (schema?.custom || !ICONS[integrationType]) {
-    return ICONS.CUSTOM
+  const integrationList = get(integrations)
+  if (integrationList[integrationType]?.iconUrl) {
+    return { url: integrationList[integrationType].iconUrl }
+  } else if (schema?.custom || !ICONS[integrationType]) {
+    return { icon: ICONS.CUSTOM }
   } else {
-    return ICONS[integrationType]
+    return { icon: ICONS[integrationType] }
   }
 }
diff --git a/packages/builder/src/components/common/CustomSVG.svelte b/packages/builder/src/components/common/CustomSVG.svelte
new file mode 100644
index 0000000000..5ae428c12d
--- /dev/null
+++ b/packages/builder/src/components/common/CustomSVG.svelte
@@ -0,0 +1,23 @@
+<script>
+  import { Helpers } from "@budibase/bbui"
+  export let size
+  export let svgHtml
+
+  function substituteSize(svg) {
+    if (svg.includes("height=")) {
+      svg = svg.replace(/height="\d*"/, `height="${size}"`)
+    }
+    if (svg.includes("width=")) {
+      svg = svg.replace(/width="\d*"/, `width="${size}"`)
+    }
+    if (svg.includes("id=")) {
+      const matches = svg.match(/id="(.*)"/g)
+      for (let match of matches) {
+        svg = svg.replace(new RegExp(match, "g"), Helpers.uuid())
+      }
+    }
+    return svg
+  }
+</script>
+
+{@html substituteSize(svgHtml)}
diff --git a/packages/server/src/integrations/index.ts b/packages/server/src/integrations/index.ts
index 000ab65a33..a542ff8455 100644
--- a/packages/server/src/integrations/index.ts
+++ b/packages/server/src/integrations/index.ts
@@ -78,6 +78,9 @@ module.exports = {
           ...plugin.schema["schema"],
           custom: true,
         }
+        if (plugin.iconUrl) {
+          pluginSchemas[sourceId].iconUrl = plugin.iconUrl
+        }
       }
     }
     return {
diff --git a/packages/types/src/documents/global/plugin.ts b/packages/types/src/documents/global/plugin.ts
index a374d5496c..7f6d4f4995 100644
--- a/packages/types/src/documents/global/plugin.ts
+++ b/packages/types/src/documents/global/plugin.ts
@@ -21,6 +21,7 @@ export interface Plugin extends Document {
   name: string
   version: string
   jsUrl?: string
+  iconUrl?: string
   source: PluginSource
   package: { [key: string]: any }
   hash: string
diff --git a/packages/types/src/sdk/datasources.ts b/packages/types/src/sdk/datasources.ts
index 970613b322..d01d636b86 100644
--- a/packages/types/src/sdk/datasources.ts
+++ b/packages/types/src/sdk/datasources.ts
@@ -96,6 +96,7 @@ export interface Integration {
   description: string
   friendlyName: string
   type?: string
+  iconUrl?: string
   datasource: {}
   query: {
     [key: string]: QueryDefinition