From ec9c6454f1dc79a2894883aa6e939d6b436c1d60 Mon Sep 17 00:00:00 2001 From: Dean Date: Mon, 30 Oct 2023 08:42:18 +0000 Subject: [PATCH 01/13] Added integration --- .../server/src/integrations/googlecloud.ts | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 packages/server/src/integrations/googlecloud.ts diff --git a/packages/server/src/integrations/googlecloud.ts b/packages/server/src/integrations/googlecloud.ts new file mode 100644 index 00000000000..bdce7a09a29 --- /dev/null +++ b/packages/server/src/integrations/googlecloud.ts @@ -0,0 +1,144 @@ +import { + Integration, + QueryType, + IntegrationBase, + DatasourceFieldType, + DatasourceFeature, + ConnectionInfo, +} from "@budibase/types" +import { Storage } from "@google-cloud/storage" +import { OAuth2Client, auth } from "google-auth-library" +import { configs, cache, context, HTTPError } from "@budibase/backend-core" + +/* + Auth Type + Service Account + OAuth2 using https://www.googleapis.com/auth/cloud-platform +*/ + +interface GoogleCloudConfig { + //type: string, //service-account + projectId: string + // privateKeyId: string + privateKey: string + clientEmail: string + // clientId: string + + auth: OAuthClientConfig + continueSetupId?: string +} + +// Move google oauth to util, if they are applicable. +// Interfaces taken from Sheets i +interface OAuthClientConfig { + appId: string + accessToken: string + refreshToken: string +} + +interface AuthTokenRequest { + client_id: string + client_secret: string + refresh_token: string +} + +interface AuthTokenResponse { + access_token: string +} + +const SCHEMA: Integration = { + // docs: "https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html", + docs: "https://googleapis.dev/nodejs/storage/latest", + auth: { + type: "google", + }, + description: + "Google Cloud Storage is an object storage service that offers industry-leading scalability, data availability, security, and performance.", + friendlyName: "GoogleCloud Storage", + type: "Object store", + features: { + [DatasourceFeature.CONNECTION_CHECKING]: true, + }, + //dependsOn + datasource: { + projectId: { + type: DatasourceFieldType.STRING, + required: true, + }, + privateKey: { + type: DatasourceFieldType.LONGFORM, + required: true, + }, + clientEmail: { + type: DatasourceFieldType.STRING, + required: true, + }, + }, + query: { + read: { + type: QueryType.FIELDS, + fields: { + bucket: { + type: DatasourceFieldType.STRING, + required: true, + }, + }, + }, + }, +} + +class GoogleCloudIntegration implements IntegrationBase { + private readonly config: GoogleCloudConfig + private storage: any + + constructor(config: GoogleCloudConfig) { + ;(this.config = config), + (this.storage = new Storage({ + projectId: this.config.projectId, + scopes: "https://www.googleapis.com/auth/cloud-platform", + credentials: { + client_email: this.config.clientEmail, + private_key: this.config.privateKey.split(String.raw`\n`).join("\n"), + }, + })) + } + + async testConnection() { + const response: ConnectionInfo = { + connected: false, + } + try { + await this.connect() + return { connected: true } + } catch (e: any) { + return { + connected: false, + error: e.message as string, + } + } + } + + async connect() { + /* + Other features + Self host option keyfilename + WIF - workload identity federation. + */ + + await this.storage.getBuckets() + } + + async read(query: { bucket: string }) { + const bucket = this.storage.bucket(query.bucket) + // Does support paging. Could possibly + const [files, queryForPage2] = await bucket.getFiles({ + autoPaginate: false, + }) + return files.map((file: any) => file.metadata) + } +} + +export default { + schema: SCHEMA, + integration: GoogleCloudIntegration, +} From 954ad8cf3222b7dea841808aa9693aaacbf70ba7 Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 2 Nov 2023 14:44:15 +0000 Subject: [PATCH 02/13] Fixes and refactoring --- .../icons/GoogleCloudStorage.svelte | 62 +++++++ .../DatasourceNavigator/icons/index.js | 2 + .../design/settings/componentSettings.js | 3 + .../actions/GoogleCloudUpload.svelte | 33 ++++ .../ButtonActionEditor/actions/index.js | 1 + .../controls/ButtonActionEditor/manifest.json | 11 ++ .../GoogleCloudStorageDataSourceSelect.svelte | 15 ++ .../new/_components/componentStructure.json | 1 + packages/client/manifest.json | 60 +++++++ .../app/forms/GoogleCloudUpload.svelte | 155 ++++++++++++++++++ .../client/src/components/app/forms/index.js | 1 + packages/client/src/utils/buttonActions.js | 12 ++ packages/server/package.json | 1 + .../src/api/controllers/static/index.ts | 39 ++++- packages/server/src/integrations/index.ts | 3 + packages/types/src/sdk/datasources.ts | 1 + 16 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 packages/builder/src/components/backend/DatasourceNavigator/icons/GoogleCloudStorage.svelte create mode 100644 packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/GoogleCloudUpload.svelte create mode 100644 packages/builder/src/components/design/settings/controls/GoogleCloudStorageDataSourceSelect.svelte create mode 100644 packages/client/src/components/app/forms/GoogleCloudUpload.svelte diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/GoogleCloudStorage.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/GoogleCloudStorage.svelte new file mode 100644 index 00000000000..fc1cbd8336c --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/GoogleCloudStorage.svelte @@ -0,0 +1,62 @@ +Icon_24px_CloudStorage_Color diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js index 2486942dea7..c414f7a25b7 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js @@ -16,6 +16,7 @@ import Firebase from "./Firebase.svelte" import Redis from "./Redis.svelte" import Snowflake from "./Snowflake.svelte" import Custom from "./Custom.svelte" +import GoogleCloudStorage from "./GoogleCloudStorage.svelte" import { integrations } from "stores/backend" import { get } from "svelte/store" @@ -38,6 +39,7 @@ const ICONS = { REDIS: Redis, SNOWFLAKE: Snowflake, CUSTOM: Custom, + GOOGLE_CLOUD: GoogleCloudStorage } export default ICONS diff --git a/packages/builder/src/components/design/settings/componentSettings.js b/packages/builder/src/components/design/settings/componentSettings.js index 232b4bef317..b3f8f0e8ebb 100644 --- a/packages/builder/src/components/design/settings/componentSettings.js +++ b/packages/builder/src/components/design/settings/componentSettings.js @@ -1,6 +1,7 @@ import { Checkbox, Select, RadioGroup, Stepper, Input } from "@budibase/bbui" import DataSourceSelect from "./controls/DataSourceSelect.svelte" import S3DataSourceSelect from "./controls/S3DataSourceSelect.svelte" +import GoogleCloudStorageDataSourceSelect from "./controls/GoogleCloudStorageDataSourceSelect.svelte" import DataProviderSelect from "./controls/DataProviderSelect.svelte" import ButtonActionEditor from "./controls/ButtonActionEditor/ButtonActionEditor.svelte" import TableSelect from "./controls/TableSelect.svelte" @@ -32,6 +33,7 @@ const componentMap = { radio: RadioGroup, dataSource: DataSourceSelect, "dataSource/s3": S3DataSourceSelect, + "dataSource/googleCloudStorage": GoogleCloudStorageDataSourceSelect, dataProvider: DataProviderSelect, boolean: Checkbox, number: Stepper, @@ -63,6 +65,7 @@ const componentMap = { "field/datetime": FormFieldSelect, "field/attachment": FormFieldSelect, "field/s3": Input, + "field/gcs": Input, "field/link": FormFieldSelect, "field/array": FormFieldSelect, "field/json": FormFieldSelect, diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/GoogleCloudUpload.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/GoogleCloudUpload.svelte new file mode 100644 index 00000000000..8a2935b4765 --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/GoogleCloudUpload.svelte @@ -0,0 +1,33 @@ + + +
+ + diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json index dd129be11eb..25d7c2e7d91 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json @@ -71,6 +71,7 @@ "multifieldselect", "s3upload", "codescanner", + "googlecloudupload", "bbreferencefield" ] }, diff --git a/packages/client/manifest.json b/packages/client/manifest.json index eef1e50b7c5..28912d7b2f8 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -3973,6 +3973,66 @@ } ] }, + "googlecloudupload": { + "name": "Google Cloud Storage File Upload", + "icon": "UploadToCloud", + "styles": ["size"], + "editable": true, + "size": { + "width": 400, + "height": 200 + }, + "settings": [ + { + "type": "field/gcs", + "label": "Field", + "key": "field", + "required": true + }, + { + "type": "text", + "label": "Label", + "key": "label" + }, + { + "type": "dataSource/googleCloudStorage", + "label": "Google Cloud Storage", + "key": "datasourceId" + }, + { + "type": "text", + "label": "Bucket", + "key": "bucket" + }, + { + "type": "text", + "label": "File name", + "key": "key" + }, + { + "type": "event", + "label": "On change", + "key": "onChange", + "context": [ + { + "label": "Field Value", + "key": "value" + } + ] + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false + }, + { + "type": "validation/attachment", + "label": "Validation", + "key": "validation" + } + ] + }, "dataprovider": { "name": "Data Provider", "icon": "Data", diff --git a/packages/client/src/components/app/forms/GoogleCloudUpload.svelte b/packages/client/src/components/app/forms/GoogleCloudUpload.svelte new file mode 100644 index 00000000000..a914a07bff4 --- /dev/null +++ b/packages/client/src/components/app/forms/GoogleCloudUpload.svelte @@ -0,0 +1,155 @@ + + + +
+ {#if fieldState} + + {/if} + {#if loading} +
+
+ +
+ {/if} +
+ + + diff --git a/packages/client/src/components/app/forms/index.js b/packages/client/src/components/app/forms/index.js index 5804d3a79d7..b5f0372cd2b 100644 --- a/packages/client/src/components/app/forms/index.js +++ b/packages/client/src/components/app/forms/index.js @@ -14,5 +14,6 @@ export { default as passwordfield } from "./PasswordField.svelte" export { default as formstep } from "./FormStep.svelte" export { default as jsonfield } from "./JSONField.svelte" export { default as s3upload } from "./S3Upload.svelte" +export { default as googlecloudupload } from "./GoogleCloudUpload.svelte" export { default as codescanner } from "./CodeScannerField.svelte" export { default as bbreferencefield } from "./BBReferenceField.svelte" diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 9b4640dbb40..721fe42b458 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -338,6 +338,17 @@ const updateStateHandler = action => { } } +const googleCloudUploadHandler = async action => { + const { componentId } = action.parameters + if (!componentId) { + return + } + const res = await uploadStore.actions.processFileUpload(componentId) + return { + publicUrl: res?.publicUrl, + } +} + const s3UploadHandler = async action => { const { componentId } = action.parameters if (!componentId) { @@ -429,6 +440,7 @@ const handlerMap = { ["Change Form Step"]: changeFormStepHandler, ["Update State"]: updateStateHandler, ["Upload File to S3"]: s3UploadHandler, + ["Upload File to GoogleCloud"]: googleCloudUploadHandler, ["Export Data"]: exportDataHandler, ["Continue if / Stop if"]: continueIfHandler, ["Show Notification"]: showNotificationHandler, diff --git a/packages/server/package.json b/packages/server/package.json index c845f7889d0..e6faed054c9 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -53,6 +53,7 @@ "@bull-board/koa": "3.9.4", "@elastic/elasticsearch": "7.10.0", "@google-cloud/firestore": "6.8.0", + "@google-cloud/storage": "^7.5.0", "@koa/router": "8.0.8", "@socket.io/redis-adapter": "^8.2.1", "airtable": "0.10.1", diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 8fbc0db910f..250500f705f 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -25,6 +25,7 @@ import fs from "fs" import sdk from "../../../sdk" import * as pro from "@budibase/pro" import { App, Ctx, ProcessAttachmentResponse, Upload } from "@budibase/types" +import { Storage } from "@google-cloud/storage" const send = require("koa-send") @@ -266,9 +267,14 @@ export const getSignedUploadURL = async function (ctx: Ctx) { const awsRegion = (datasource?.config?.region || "eu-west-1") as string if (datasource?.source === "S3") { const { bucket, key } = ctx.request.body || {} + + // Ensure we aren't using a custom endpoint + if (datasource?.config?.endpoint) { + ctx.throw(400, "S3 datasources with custom endpoints are not supported") + } + if (!bucket || !key) { ctx.throw(400, "bucket and key values are required") - return } try { const s3 = new AWS.S3({ @@ -284,6 +290,37 @@ export const getSignedUploadURL = async function (ctx: Ctx) { } catch (error: any) { ctx.throw(400, error) } + } else if (datasource?.source === "GOOGLE_CLOUD") { + const { bucket, key } = ctx.request.body || {} + + const parsedKey = datasource?.config?.privateKey + .split(String.raw`\n`) + .join("\n") + + publicUrl = `https://storage.cloud.google.com/${bucket}/${key}` + + try { + const storage = new Storage({ + projectId: datasource?.config?.projectId, + scopes: "https://www.googleapis.com/auth/cloud-platform", + credentials: { + client_email: datasource?.config?.clientEmail, + private_key: parsedKey, + }, + }) + // 15 minute default duration. + const [url] = await storage + .bucket(bucket) + .file(key) + .getSignedUrl({ + version: "v4", + action: "write", + expires: Date.now() + 15 * 60 * 1000, + }) + signedUrl = url + } catch (error: any) { + ctx.throw(400, error) + } } ctx.body = { signedUrl, publicUrl } diff --git a/packages/server/src/integrations/index.ts b/packages/server/src/integrations/index.ts index 49761bac85d..df29f0769fb 100644 --- a/packages/server/src/integrations/index.ts +++ b/packages/server/src/integrations/index.ts @@ -19,6 +19,7 @@ import { getDatasourcePlugin } from "../utilities/fileSystem" import env from "../environment" import cloneDeep from "lodash/cloneDeep" import sdk from "../sdk" +import googlecloud from "./googlecloud" const DEFINITIONS: Record = { [SourceName.POSTGRES]: postgres.schema, @@ -37,6 +38,7 @@ const DEFINITIONS: Record = { [SourceName.REDIS]: redis.schema, [SourceName.SNOWFLAKE]: snowflake.schema, [SourceName.ORACLE]: undefined, + [SourceName.GOOGLE_CLOUD]: googlecloud.schema, } const INTEGRATIONS: Record = { @@ -56,6 +58,7 @@ const INTEGRATIONS: Record = { [SourceName.REDIS]: redis.integration, [SourceName.SNOWFLAKE]: snowflake.integration, [SourceName.ORACLE]: undefined, + [SourceName.GOOGLE_CLOUD]: googlecloud.integration, } // optionally add oracle integration if the oracle binary can be installed diff --git a/packages/types/src/sdk/datasources.ts b/packages/types/src/sdk/datasources.ts index 7a335eb3b9b..8eca5bc7cd4 100644 --- a/packages/types/src/sdk/datasources.ts +++ b/packages/types/src/sdk/datasources.ts @@ -56,6 +56,7 @@ export enum SourceName { FIRESTORE = "FIRESTORE", REDIS = "REDIS", SNOWFLAKE = "SNOWFLAKE", + GOOGLE_CLOUD = "GOOGLE_CLOUD", } export enum IncludeRelationship { From 1b8c6a62356dc78ad7fad684e5489657e438d16d Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 3 Nov 2023 09:30:25 +0000 Subject: [PATCH 03/13] Tidying up and added some field types to the query screen --- .../integration/QueryFieldsBuilder.svelte | 16 ++- .../server/src/integrations/googlecloud.ts | 105 +++++++++++++----- 2 files changed, 90 insertions(+), 31 deletions(-) diff --git a/packages/builder/src/components/integration/QueryFieldsBuilder.svelte b/packages/builder/src/components/integration/QueryFieldsBuilder.svelte index 340dc7839ca..95b27900027 100644 --- a/packages/builder/src/components/integration/QueryFieldsBuilder.svelte +++ b/packages/builder/src/components/integration/QueryFieldsBuilder.svelte @@ -1,5 +1,5 @@ - -
- {#if fieldState} - - {/if} - {#if loading} -
-
- -
- {/if} -
- - - + {onChange} + storeFieldType={"googlecloudupload"} +/> diff --git a/packages/client/src/components/app/forms/S3Upload.svelte b/packages/client/src/components/app/forms/S3Upload.svelte index 0147cbca6e9..591a0f7067b 100644 --- a/packages/client/src/components/app/forms/S3Upload.svelte +++ b/packages/client/src/components/app/forms/S3Upload.svelte @@ -1,7 +1,5 @@ - -
- {#if fieldState} - - {/if} - {#if loading} -
-
- -
- {/if} -
- - - + {onChange} + storeFieldType={"s3upload"} +/> diff --git a/packages/client/src/components/app/forms/StoreUpload.svelte b/packages/client/src/components/app/forms/StoreUpload.svelte new file mode 100644 index 00000000000..fc22f1718cf --- /dev/null +++ b/packages/client/src/components/app/forms/StoreUpload.svelte @@ -0,0 +1,157 @@ + + + +
+ {#if fieldState} + + {/if} + {#if loading} +
+
+ +
+ {/if} +
+ + + diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 2e22bc59c75..5898965ed8e 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -1,4 +1,5 @@ import { InvalidFileExtensions } from "@budibase/shared-core" +import { Duration } from "@budibase/backend-core" require("svelte/register") @@ -322,6 +323,10 @@ export const getSignedUploadURL = async function (ctx: Ctx) { } else if (datasource?.source === "GOOGLE_CLOUD") { const { bucket, key } = ctx.request.body || {} + if (!(bucket && key)) { + ctx.throw(400, "Request requires a destination bucket and file key") + } + const parsedKey = datasource?.config?.privateKey .split(String.raw`\n`) .join("\n") @@ -344,7 +349,7 @@ export const getSignedUploadURL = async function (ctx: Ctx) { .getSignedUrl({ version: "v4", action: "write", - expires: Date.now() + 15 * 60 * 1000, + expires: Date.now() + Duration.fromMinutes(15).toMs(), }) signedUrl = url } catch (error: any) { From fb7e74cb1145bab856e2621d407ddc6c4fd956a6 Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 1 Feb 2024 12:43:53 +0000 Subject: [PATCH 08/13] Linting --- packages/server/src/api/controllers/static/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 5898965ed8e..7ad252a8f4f 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -1,5 +1,4 @@ import { InvalidFileExtensions } from "@budibase/shared-core" -import { Duration } from "@budibase/backend-core" require("svelte/register") @@ -20,6 +19,7 @@ import { utils, configs, BadRequestError, + Duration, } from "@budibase/backend-core" import AWS from "aws-sdk" import fs from "fs" From fdd728f5fb6f0747bc62da2d4e203836bb25163a Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 2 Feb 2024 10:40:59 +0000 Subject: [PATCH 09/13] Bump account portal --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 52f51dcfb96..cc12291732e 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 52f51dcfb96d3fe58c8cc7a905e7d733f7cd84c2 +Subproject commit cc12291732ee902dc832bc7d93cf2086ffdf0cff From de30ea284a12da3c03e23f6de130ddb141b2a589 Mon Sep 17 00:00:00 2001 From: Dean Date: Tue, 13 Feb 2024 10:46:11 +0000 Subject: [PATCH 10/13] Bump account portal --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index cc12291732e..ba40a467484 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit cc12291732ee902dc832bc7d93cf2086ffdf0cff +Subproject commit ba40a467484963f8041144345469f4b395e31acc From 2b3150543e5f0216070dc37dc54533cea7743fd4 Mon Sep 17 00:00:00 2001 From: Dean Date: Tue, 13 Feb 2024 11:07:47 +0000 Subject: [PATCH 11/13] packages/account-portal --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index ba40a467484..d5a59daa6a0 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit ba40a467484963f8041144345469f4b395e31acc +Subproject commit d5a59daa6a0536bc276ae16fd46e56fde39f8244 From c0177aa73e8273c240080cc6ddbe418eb379169d Mon Sep 17 00:00:00 2001 From: Dean Date: Wed, 13 Mar 2024 09:37:55 +0000 Subject: [PATCH 12/13] Added monolith refactor updates --- .../ButtonActionEditor/actions/GoogleCloudUpload.svelte | 6 +++--- .../controls/GoogleCloudStorageDataSourceSelect.svelte | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/GoogleCloudUpload.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/GoogleCloudUpload.svelte index 8a2935b4765..2126f59cce5 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/GoogleCloudUpload.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/GoogleCloudUpload.svelte @@ -1,11 +1,11 @@ diff --git a/packages/builder/src/components/design/settings/controls/GoogleCloudStorageDataSourceSelect.svelte b/packages/builder/src/components/design/settings/controls/GoogleCloudStorageDataSourceSelect.svelte index eec41090b91..db010500ea8 100644 --- a/packages/builder/src/components/design/settings/controls/GoogleCloudStorageDataSourceSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/GoogleCloudStorageDataSourceSelect.svelte @@ -1,6 +1,6 @@