This makes it so that Googlesheets works correctly as part of onboarding in the Cloud, in self host the googlesheets onboarding option has been hidden as it is too complex to get the user to setup the correct integrations with their google account while doing the first time onboarding to an app.

This commit is contained in:
mike12345567 2023-03-29 19:58:24 +01:00
parent 0257617ba1
commit 32c78ec9bf
9 changed files with 100 additions and 29 deletions

View File

@ -162,10 +162,7 @@ export async function getGoogleConfig(): Promise<
export async function getGoogleDatasourceConfig(): Promise< export async function getGoogleDatasourceConfig(): Promise<
GoogleInnerConfig | undefined GoogleInnerConfig | undefined
> { > {
if (!env.isDev() && !env.SELF_HOSTED) { return getDefaultGoogleConfig()
// always use the env vars in cloud
return getDefaultGoogleConfig()
}
// prefer the config in self-host // prefer the config in self-host
let config = await getGoogleConfig() let config = await getGoogleConfig()
@ -179,13 +176,14 @@ export async function getGoogleDatasourceConfig(): Promise<
} }
export function getDefaultGoogleConfig(): GoogleInnerConfig | undefined { export function getDefaultGoogleConfig(): GoogleInnerConfig | undefined {
if (environment.GOOGLE_CLIENT_ID && environment.GOOGLE_CLIENT_SECRET) { //if (environment.GOOGLE_CLIENT_ID && environment.GOOGLE_CLIENT_SECRET) {
return { return {
clientID: environment.GOOGLE_CLIENT_ID!, clientID:
clientSecret: environment.GOOGLE_CLIENT_SECRET!, "77746844610-62k43m9b4so4gcmf6ibs7p3l7jv81rug.apps.googleusercontent.com",
activated: true, clientSecret: "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",
} activated: true,
} }
//}
} }
// OIDC // OIDC

View File

@ -13,6 +13,7 @@ type Passport = {
async function fetchGoogleCreds() { async function fetchGoogleCreds() {
let config = await configs.getGoogleDatasourceConfig() let config = await configs.getGoogleDatasourceConfig()
console.log(config)
if (!config) { if (!config) {
throw new Error("No google configuration found") throw new Error("No google configuration found")
@ -78,17 +79,23 @@ export async function postAuth(
), ),
{ successRedirect: "/", failureRedirect: "/error" }, { successRedirect: "/", failureRedirect: "/error" },
async (err: any, tokens: string[]) => { async (err: any, tokens: string[]) => {
const baseUrl = `/builder/app/${authStateCookie.appId}/data`
// update the DB for the datasource with all the user info // update the DB for the datasource with all the user info
await doWithDB(authStateCookie.appId, async (db: Database) => { await doWithDB(authStateCookie.appId, async (db: Database) => {
const datasource = await db.get(authStateCookie.datasourceId) let datasource
try {
datasource = await db.get(authStateCookie.datasourceId)
} catch (err: any) {
if (err.status === 404) {
ctx.redirect(baseUrl)
}
}
if (!datasource.config) { if (!datasource.config) {
datasource.config = {} datasource.config = {}
} }
datasource.config.auth = { type: "google", ...tokens } datasource.config.auth = { type: "google", ...tokens }
await db.put(datasource) await db.put(datasource)
ctx.redirect( ctx.redirect(`${baseUrl}/datasource/${authStateCookie.datasourceId}`)
`/builder/app/${authStateCookie.appId}/data/datasource/${authStateCookie.datasourceId}`
)
}) })
} }
)(ctx, next) )(ctx, next)

View File

@ -5,18 +5,26 @@
export let preAuthStep export let preAuthStep
export let datasource export let datasource
export let disabled
$: tenantId = $auth.tenantId $: tenantId = $auth.tenantId
</script> </script>
<button <button
class:disabled
{disabled}
on:click={async () => { on:click={async () => {
let ds = datasource let ds = datasource
let appId = $store.appId
if (!ds) { if (!ds) {
ds = await preAuthStep() const resp = await preAuthStep()
if (resp.datasource && resp.appId) {
ds = resp.datasource
appId = resp.appId
}
} }
window.open( window.open(
`/api/global/auth/${tenantId}/datasource/google?datasourceId=${ds._id}&appId=${$store.appId}`, `/api/global/auth/${tenantId}/datasource/google?datasourceId=${ds._id}&appId=${appId}`,
"_blank" "_blank"
) )
}} }}
@ -26,6 +34,10 @@
</button> </button>
<style> <style>
.disabled {
opacity: 0.5;
}
button { button {
width: 195px; width: 195px;
height: 40px; height: 40px;

View File

@ -1,12 +1,15 @@
<script> <script>
import { Button, FancyForm, FancyInput, FancyCheckbox } from "@budibase/bbui" import { Button, FancyForm, FancyInput, FancyCheckbox } from "@budibase/bbui"
import GoogleButton from "components/backend/DatasourceNavigator/_components/GoogleButton.svelte"
import { capitalise } from "helpers/helpers" import { capitalise } from "helpers/helpers"
import PanelHeader from "./PanelHeader.svelte" import PanelHeader from "./PanelHeader.svelte"
import { helpers } from "@budibase/shared-core"
export let title = "" export let title = ""
export let onBack = null export let onBack = null
export let onNext = () => {} export let onNext = () => {}
export let fields = {} export let fields = {}
export let type = ""
let errors = {} let errors = {}
@ -57,8 +60,9 @@
} }
$: isValid = getIsValid(fields, errors, values) $: isValid = getIsValid(fields, errors, values)
$: isGoogle = helpers.isGoogleSheets(type)
const handleNext = () => { const handleNext = async () => {
const parsedValues = {} const parsedValues = {}
Object.entries(values).forEach(([name, value]) => { Object.entries(values).forEach(([name, value]) => {
@ -69,7 +73,10 @@
} }
}) })
return onNext(parsedValues) if (isGoogle) {
parsedValues.isGoogle = isGoogle
}
return await onNext(parsedValues)
} }
</script> </script>
@ -99,7 +106,11 @@
{/each} {/each}
</FancyForm> </FancyForm>
</div> </div>
<Button cta disabled={!isValid} on:click={handleNext}>Connect</Button> {#if isGoogle}
<GoogleButton disabled={!isValid} preAuthStep={handleNext} />
{:else}
<Button cta disabled={!isValid} on:click={handleNext}>Connect</Button>
{/if}
</div> </div>
<style> <style>

View File

@ -4,7 +4,7 @@
import DataPanel from "./_components/DataPanel.svelte" import DataPanel from "./_components/DataPanel.svelte"
import DatasourceConfigPanel from "./_components/DatasourceConfigPanel.svelte" import DatasourceConfigPanel from "./_components/DatasourceConfigPanel.svelte"
import ExampleApp from "./_components/ExampleApp.svelte" import ExampleApp from "./_components/ExampleApp.svelte"
import { FancyButton, notifications, Modal } from "@budibase/bbui" import { FancyButton, notifications, Modal, Body } from "@budibase/bbui"
import IntegrationIcon from "components/backend/DatasourceNavigator/IntegrationIcon.svelte" import IntegrationIcon from "components/backend/DatasourceNavigator/IntegrationIcon.svelte"
import { SplitPage } from "@budibase/frontend-core" import { SplitPage } from "@budibase/frontend-core"
import { API } from "api" import { API } from "api"
@ -17,6 +17,7 @@
import createFromScratchScreen from "builderStore/store/screenTemplates/createFromScratchScreen" import createFromScratchScreen from "builderStore/store/screenTemplates/createFromScratchScreen"
import { Roles } from "constants/backend" import { Roles } from "constants/backend"
import Spinner from "components/common/Spinner.svelte" import Spinner from "components/common/Spinner.svelte"
import { helpers } from "@budibase/shared-core"
let name = "My first app" let name = "My first app"
let url = "my-first-app" let url = "my-first-app"
@ -25,10 +26,12 @@
let plusIntegrations = {} let plusIntegrations = {}
let integrationsLoading = true let integrationsLoading = true
$: getIntegrations()
let creationLoading = false let creationLoading = false
let uploadModal let uploadModal
let googleComplete = false
$: getIntegrations()
$: cloudHosted = $admin.cloud
const createApp = async useSampleData => { const createApp = async useSampleData => {
creationLoading = true creationLoading = true
@ -62,6 +65,7 @@
await store.actions.screens.save(defaultScreenTemplate) await store.actions.screens.save(defaultScreenTemplate)
appId = createdApp.instance._id appId = createdApp.instance._id
return createdApp
} catch (e) { } catch (e) {
creationLoading = false creationLoading = false
throw e throw e
@ -74,6 +78,10 @@
const newPlusIntegrations = {} const newPlusIntegrations = {}
Object.entries($integrations).forEach(([integrationType, schema]) => { Object.entries($integrations).forEach(([integrationType, schema]) => {
// google sheets not available in self-host
if (helpers.isGoogleSheets(integrationType) && !cloudHosted) {
return
}
if (schema?.plus) { if (schema?.plus) {
newPlusIntegrations[integrationType] = schema newPlusIntegrations[integrationType] = schema
} }
@ -92,12 +100,17 @@
notifications.success(`App created successfully`) notifications.success(`App created successfully`)
} }
const handleCreateApp = async ({ datasourceConfig, useSampleData }) => { const handleCreateApp = async ({
datasourceConfig,
useSampleData,
isGoogle,
}) => {
try { try {
await createApp(useSampleData) const app = await createApp(useSampleData)
let datasource
if (datasourceConfig) { if (datasourceConfig) {
await saveDatasource({ datasource = await saveDatasource({
plus: true, plus: true,
auth: undefined, auth: undefined,
name: plusIntegrations[stage].friendlyName, name: plusIntegrations[stage].friendlyName,
@ -107,7 +120,14 @@
}) })
} }
goToApp() store.set()
if (isGoogle) {
googleComplete = true
return { datasource, appId: app.appId }
} else {
goToApp()
}
} catch (e) { } catch (e) {
console.log(e) console.log(e)
creationLoading = false creationLoading = false
@ -127,8 +147,15 @@
<SplitPage> <SplitPage>
{#if stage === "name"} {#if stage === "name"}
<NamePanel bind:name bind:url onNext={() => (stage = "data")} /> <NamePanel bind:name bind:url onNext={() => (stage = "data")} />
{:else if googleComplete}
<div class="centered">
<Body
>Please login to your Google account in the new tab which as opened to
continue.</Body
>
</div>
{:else if integrationsLoading || creationLoading} {:else if integrationsLoading || creationLoading}
<div class="spinner"> <div class="centered">
<Spinner /> <Spinner />
</div> </div>
{:else if stage === "data"} {:else if stage === "data"}
@ -174,8 +201,13 @@
<DatasourceConfigPanel <DatasourceConfigPanel
title={plusIntegrations[stage].friendlyName} title={plusIntegrations[stage].friendlyName}
fields={plusIntegrations[stage].datasource} fields={plusIntegrations[stage].datasource}
type={stage}
onBack={() => (stage = "data")} onBack={() => (stage = "data")}
onNext={data => handleCreateApp({ datasourceConfig: data })} onNext={data => {
const isGoogle = data.isGoogle
delete data.isGoogle
return handleCreateApp({ datasourceConfig: data, isGoogle })
}}
/> />
{:else} {:else}
<p>There was an problem. Please refresh the page and try again.</p> <p>There was an problem. Please refresh the page and try again.</p>
@ -186,7 +218,7 @@
</SplitPage> </SplitPage>
<style> <style>
.spinner { .centered {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;

View File

@ -245,6 +245,10 @@ class GoogleSheetsIntegration implements DatasourcePlus {
} }
async buildSchema(datasourceId: string, entities: Record<string, Table>) { async buildSchema(datasourceId: string, entities: Record<string, Table>) {
// not fully configured yet
if (!this.config.auth) {
return
}
await this.connect() await this.connect()
const sheets = this.client.sheetsByIndex const sheets = this.client.sheetsByIndex
const tables: Record<string, Table> = {} const tables: Record<string, Table> = {}

View File

@ -0,0 +1,2 @@
export * from "./helpers"
export * from "./integrations"

View File

@ -0,0 +1,5 @@
import { SourceName } from "@budibase/types"
export function isGoogleSheets(type: SourceName) {
return type === SourceName.GOOGLE_SHEETS
}