Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
export let onSubmit: (_value: {
config: Record<string, any>
name: string
}) => Promise<void> | void = () => {}
}) => Promise<typeof keepOpen | void> = () => Promise.resolve(undefined)
export let showNameField: boolean = false
export let nameFieldValue: string = ""

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<script>
<script lang="ts">
import { ModalContent, Body, Layout, Link } from "@budibase/bbui"
import { organisation } from "@/stores/portal"
import GoogleButton from "./GoogleButton.svelte"
import { bb } from "@/stores/bb"
import { createEventDispatcher } from "svelte"

const dispatch = createEventDispatcher()
const dispatch = createEventDispatcher<{ close: void }>()

$: isGoogleConfigured = !!$organisation.googleDatasourceConfigured
</script>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script>
<script lang="ts">
import GoogleLogo from "assets/google-logo.png"
import { auth } from "@/stores/portal"
import { appStore } from "@/stores/builder"

export let disabled
export let samePage
export let samePage: boolean
export let disabled: boolean = false

$: tenantId = $auth.tenantId
</script>
Expand All @@ -16,7 +16,7 @@
let appId = $appStore.appId
const url = `/api/global/auth/${tenantId}/datasource/google?appId=${appId}`
if (samePage) {
window.location = url
window.location.href = url
} else {
window.open(url, "_blank")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,46 @@
<script>
import { Modal, keepOpen, notifications } from "@budibase/bbui"
import { goto } from "@roxi/routify"
import { IntegrationTypes } from "@/constants/backend"
import GoogleAuthPrompt from "./GoogleAuthPrompt.svelte"

import { get } from "svelte/store"
import TableImportSelection from "@/components/backend/Datasources/TableImportSelection/index.svelte"
<script lang="ts">
import DatasourceConfigEditor from "@/components/backend/Datasources/ConfigEditor/index.svelte"
import TableImportSelection from "@/components/backend/Datasources/TableImportSelection/index.svelte"
import { IntegrationTypes } from "@/constants/backend"
import { datasources } from "@/stores/builder"
import { createOnGoogleAuthStore } from "./stores/onGoogleAuth.js"
import { createDatasourceCreationStore } from "./stores/datasourceCreation.js"
import { configFromIntegration } from "@/stores/selectors"
import { Modal, keepOpen, notifications } from "@budibase/bbui"
import { goto } from "@roxi/routify"
import { get } from "svelte/store"
import GoogleAuthPrompt from "./GoogleAuthPrompt.svelte"
import { createDatasourceCreationStore } from "./stores/datasourceCreation"
import { createOnGoogleAuthStore } from "./stores/onGoogleAuth"
import type { UIIntegration } from "@budibase/types"

$goto

export let loading = false
const store = createDatasourceCreationStore()
const onGoogleAuth = createOnGoogleAuthStore()
let modal
let modal: Modal

const handleStoreChanges = (store, modal, goto) => {
type DatasourceConfig = Record<string, unknown>

const handleStoreChanges = (
store: typeof $store,
modal: Modal,
goto: typeof $goto
) => {
store.stage === null ? modal?.hide() : modal?.show()

if (store.finished) {
if (store.finished && store.datasource) {
const datasource = store.datasource
const queryString =
store.datasource.plus || store.datasource.source === "REST"
datasource.plus || datasource.source === "REST"
? ""
: "?promptQuery=true"
goto(`./datasource/${store.datasource._id}${queryString}`)
goto(`./datasource/${datasource._id}${queryString}`)
}
}

$: handleStoreChanges($store, modal, $goto)

export function show(integration) {
export function show(integration: UIIntegration) {
if (integration.name === IntegrationTypes.REST) {
// A REST integration is created immediately, we don't need to display a config modal.
loading = true
Expand Down Expand Up @@ -63,21 +70,35 @@
store.editConfigStage()
})

const createDatasource = async config => {
const createDatasource = async (
config: DatasourceConfig
): Promise<typeof keepOpen> => {
try {
const datasource = await datasources.create({
integration: get(store).integration,
integration: get(store).integration!,
config,
})
store.setDatasource(datasource)

notifications.success("Datasource created successfully")
} catch (e) {
} catch (e: any) {
notifications.error(`Error creating datasource: ${e.message}`)
}

return keepOpen
}

function ensure<K extends keyof typeof $store>(
key: K,
config: typeof $store
) {
if (!config[key]) {
const error = `${key} is not set`
notifications.error(error)
throw new Error(error)
}
return config[key]
}
</script>

<Modal on:hide={store.cancel} bind:this={modal}>
Expand All @@ -89,14 +110,14 @@
/>
{:else if $store.stage === "editConfig"}
<DatasourceConfigEditor
integration={$store.integration}
config={$store.config}
onSubmit={({ config }) => createDatasource(config)}
integration={$store.integration!}
config={ensure("config", $store)}
onSubmit={async ({ config }) => await createDatasource(config)}
/>
{:else if $store.stage === "selectTables"}
<TableImportSelection
integration={$store.integration}
datasource={$store.datasource}
integration={ensure("integration", $store)}
datasource={ensure("datasource", $store)}
onComplete={store.markAsFinished}
/>
{/if}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@ import {
createDatasourceCreationStore,
} from "./datasourceCreation"
import { get } from "svelte/store"
import type { Datasource, UIIntegration } from "@budibase/types"

vi.mock("@/stores/selectors", () => ({
shouldIntegrationFetchTableNames: vi.fn(),
}))
declare module "vitest" {
export interface TestContext {
store: ReturnType<typeof createDatasourceCreationStore>
integration: UIIntegration
config: Record<string, unknown>
datasource: Partial<Datasource>
}
}

describe("datasource creation store", () => {
beforeEach(ctx => {
vi.clearAllMocks()
ctx.store = createDatasourceCreationStore()

ctx.integration = { data: "integration" }
ctx.integration = { type: "integration" } as UIIntegration
ctx.config = { data: "config" }
ctx.datasource = { data: "datasource" }
ctx.datasource = { name: "datasource" }
})

describe("store creation", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,41 @@
import { get, writable } from "svelte/store"
import { get, writable, type Writable } from "svelte/store"
import { shouldIntegrationFetchTableNames } from "@/stores/selectors"
import type { Datasource, UIIntegration } from "@budibase/types"

export const defaultStore = {
type CreationStage = "googleAuth" | "editConfig" | "selectTables" | null

type DatasourceCreationState = {
finished: boolean
stage: CreationStage
integration: UIIntegration | null
config: Record<string, unknown> | null
datasource: Datasource | null
}

interface DatasourceCreationStore extends Writable<DatasourceCreationState> {
cancel: () => void
googleAuthStage: () => void
setIntegration: (integration: UIIntegration) => void
setConfig: (config: Record<string, unknown>) => void
editConfigStage: () => void
setDatasource: (datasource: Datasource) => void
selectTablesStage: () => void
markAsFinished: () => void
}

export const defaultStore: DatasourceCreationState = {
finished: false,
stage: null,
integration: null,
config: null,
datasource: null,
}

export const createDatasourceCreationStore = () => {
const store = writable(defaultStore)
export const createDatasourceCreationStore = (): Omit<
DatasourceCreationStore,
"set" | "update"
> => {
const store = writable(defaultStore) as DatasourceCreationStore

store.cancel = () => {
const $store = get(store)
Expand Down Expand Up @@ -56,7 +81,14 @@ export const createDatasourceCreationStore = () => {
const $store = get(store)
store.set({ ...$store, datasource })

if (shouldIntegrationFetchTableNames($store.integration)) {
const { integration } = $store

if (!integration) {
const error = "Integration must be set"
throw new Error(error)
}

if (shouldIntegrationFetchTableNames(integration)) {
store.selectTablesStage()
} else {
store.markAsFinished()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,18 +1,48 @@
import { it, expect, describe, beforeEach, vi } from "vitest"
import { get } from "svelte/store"
import { createOnGoogleAuthStore } from "./onGoogleAuth"
import { writable, get } from "svelte/store"
// eslint-disable-next-line
import { params } from "@roxi/routify"
// eslint-disable-next-line
import { integrations } from "@/stores/builder"
import { IntegrationTypes } from "@/constants/backend"

declare module "vitest" {
export interface TestContext {
callback: ReturnType<typeof vi.fn>
}
}

const { paramsStore, integrationsStore } = vi.hoisted(() => {
const createWritable = <T>(initial: T) => {
let value = initial
const subscribers = new Set<(next: T) => void>()
return {
set: (next: T) => {
value = next
subscribers.forEach(fn => fn(value))
},
update: (fn: (current: T) => T) => {
const next = fn(value)
value = next
subscribers.forEach(cb => cb(value))
},
subscribe: (fn: (next: T) => void) => {
fn(value)
subscribers.add(fn)
return () => subscribers.delete(fn)
},
}
}

return {
paramsStore: createWritable<Record<string, string | undefined>>({}),
integrationsStore: createWritable<Record<string, unknown>>({}),
}
})

vi.mock("@roxi/routify", () => ({
params: vi.fn(),
params: paramsStore,
}))

vi.mock("@/stores/builder", () => ({
integrations: vi.fn(),
integrations: integrationsStore,
}))

vi.stubGlobal("history", { replaceState: vi.fn() })
Expand All @@ -21,17 +51,15 @@ vi.stubGlobal("window", { location: { pathname: "/current-path" } })
describe("google auth store", () => {
beforeEach(ctx => {
vi.clearAllMocks()
// eslint-disable-next-line no-import-assign
integrations = writable({
integrationsStore.set({
[IntegrationTypes.GOOGLE_SHEETS]: { data: "integration" },
})
ctx.callback = vi.fn()
})

describe("with id present", () => {
beforeEach(ctx => {
// eslint-disable-next-line no-import-assign
params = writable({ "?continue_google_setup": "googleId" })
paramsStore.set({ "?continue_google_setup": "googleId" })
get(createOnGoogleAuthStore())(ctx.callback)
})

Expand All @@ -48,18 +76,13 @@ describe("google auth store", () => {

it("clears the query param", () => {
expect(history.replaceState).toHaveBeenCalledTimes(1)
expect(history.replaceState).toHaveBeenCalledWith(
{},
null,
`/current-path`
)
expect(history.replaceState).toHaveBeenCalledWith({}, "", `/current-path`)
})
})

describe("without id present", () => {
beforeEach(ctx => {
// eslint-disable-next-line no-import-assign
params = writable({})
paramsStore.set({})
get(createOnGoogleAuthStore())(ctx.callback)
})

Expand Down
Loading
Loading