Merge pull request #8054 from Budibase/plugin-improvements

Plugin fixes + improvements
This commit is contained in:
Andrew Kingston 2022-09-30 16:20:20 +01:00 committed by GitHub
commit 8c561239dd
8 changed files with 81 additions and 58 deletions

View File

@ -143,7 +143,10 @@ export const getComponentSettings = componentType => {
} }
// Ensure whole component name is used // Ensure whole component name is used
if (!componentType.startsWith("@budibase")) { if (
!componentType.startsWith("plugin/") &&
!componentType.startsWith("@budibase")
) {
componentType = `@budibase/standard-components/${componentType}` componentType = `@budibase/standard-components/${componentType}`
} }

View File

@ -243,18 +243,18 @@ export const getDatasourceForProvider = (asset, component) => {
return null return null
} }
// There are different types of setting which can be a datasource, for // For legacy compatibility, we need to be able to handle datasources that are
// example an actual datasource object, or a table ID string. // just strings. These are not generated any more, so could be removed in
// Convert the datasource setting into a proper datasource object so that // future.
// we can use it properly // TODO: remove at some point
if (datasourceSetting.type === "table") { const datasource = component[datasourceSetting?.key]
if (typeof datasource === "string") {
return { return {
tableId: component[datasourceSetting?.key], tableId: datasource,
type: "table", type: "table",
} }
} else {
return component[datasourceSetting?.key]
} }
return datasource
} }
/** /**

View File

@ -88,27 +88,12 @@ export const getFrontendStore = () => {
initialise: async pkg => { initialise: async pkg => {
const { layouts, screens, application, clientLibPath } = pkg const { layouts, screens, application, clientLibPath } = pkg
// Fetch component definitions. await store.actions.components.refreshDefinitions(application.appId)
// Allow errors to propagate.
const components = await API.fetchComponentLibDefinitions(
application.appId
)
// Filter out custom component keys so we can flag them
const customComponents = Object.keys(components).filter(name =>
name.startsWith("plugin/")
)
// Reset store state // Reset store state
store.update(state => ({ store.update(state => ({
...state, ...state,
libraries: application.componentLibraries, libraries: application.componentLibraries,
components,
customComponents,
clientFeatures: {
...INITIAL_FRONTEND_STATE.clientFeatures,
...components.features,
},
name: application.name, name: application.name,
description: application.description, description: application.description,
appId: application.appId, appId: application.appId,
@ -385,6 +370,29 @@ export const getFrontendStore = () => {
}, },
}, },
components: { components: {
refreshDefinitions: async appId => {
if (!appId) {
appId = get(store).appId
}
// Fetch definitions and filter out custom component definitions so we
// can flag them
const components = await API.fetchComponentLibDefinitions(appId)
const customComponents = Object.keys(components).filter(name =>
name.startsWith("plugin/")
)
// Update store
store.update(state => ({
...state,
components,
customComponents,
clientFeatures: {
...INITIAL_FRONTEND_STATE.clientFeatures,
...components.features,
},
}))
},
getDefinition: componentName => { getDefinition: componentName => {
if (!componentName) { if (!componentName) {
return null return null
@ -428,7 +436,7 @@ export const getFrontendStore = () => {
_id: Helpers.uuid(), _id: Helpers.uuid(),
_component: definition.component, _component: definition.component,
_styles: { normal: {}, hover: {}, active: {} }, _styles: { normal: {}, hover: {}, active: {} },
_instanceName: `New ${definition.name}`, _instanceName: `New ${definition.friendlyName || definition.name}`,
...cloneDeep(props), ...cloneDeep(props),
...extras, ...extras,
} }

View File

@ -1,26 +1,28 @@
<script> <script>
import { Select } from "@budibase/bbui" import { Select } from "@budibase/bbui"
import { tables } from "stores/backend" import { createEventDispatcher } from "svelte"
import { tables as tablesStore } from "stores/backend"
export let value export let value
const dispatch = createEventDispatcher()
$: tables = $tablesStore.list.map(m => ({
label: m.name,
tableId: m._id,
type: "table",
}))
const onChange = e => {
const dataSource = tables?.find(x => x.tableId === e.detail)
dispatch("change", dataSource)
}
</script> </script>
<div> <Select
<Select extraThin secondary wide on:change {value}> on:change={onChange}
<option value="">Choose a table</option> value={value?.tableId}
{#each $tables.list as table} options={tables}
<option value={table._id}>{table.name}</option> getOptionValue={x => x.tableId}
{/each} getOptionLabel={x => x.label}
</Select> />
</div>
<style>
div {
flex: 1 1 auto;
display: flex;
flex-direction: row;
}
div :global(> *) {
flex: 1 1 auto;
}
</style>

View File

@ -198,6 +198,8 @@
block: "center", block: "center",
}) })
} }
} else if (type === "reload-plugin") {
await store.actions.components.refreshDefinitions()
} else { } else {
console.warn(`Client sent unknown event type: ${type}`) console.warn(`Client sent unknown event type: ${type}`)
} }

View File

@ -98,6 +98,9 @@ const createBuilderStore = () => {
return state return state
}) })
} }
// Notify the builder so we can reload component definitions
dispatchEvent("reload-plugin")
}, },
} }
return { return {

View File

@ -1,13 +1,19 @@
import { builderStore, environmentStore } from "./stores/index.js" import {
builderStore,
environmentStore,
notificationStore,
} from "./stores/index.js"
import { get } from "svelte/store" import { get } from "svelte/store"
import { io } from "socket.io-client" import { io } from "socket.io-client"
let socket
export const initWebsocket = () => { export const initWebsocket = () => {
const { inBuilder, location } = get(builderStore) const { inBuilder, location } = get(builderStore)
const { cloud } = get(environmentStore) const { cloud } = get(environmentStore)
// Only connect when we're inside the builder preview, for now // Only connect when we're inside the builder preview, for now
if (!inBuilder || !location || cloud) { if (!inBuilder || !location || cloud || socket) {
return return
} }
@ -16,20 +22,20 @@ export const initWebsocket = () => {
const proto = tls ? "wss:" : "ws:" const proto = tls ? "wss:" : "ws:"
const host = location.hostname const host = location.hostname
const port = location.port || (tls ? 443 : 80) const port = location.port || (tls ? 443 : 80)
const socket = io(`${proto}//${host}:${port}`, { socket = io(`${proto}//${host}:${port}`, {
path: "/socket/client", path: "/socket/client",
// Cap reconnection attempts to 10 (total of 95 seconds before giving up) // Cap reconnection attempts to 3 (total of 15 seconds before giving up)
reconnectionAttempts: 10, reconnectionAttempts: 3,
// Delay initial reconnection attempt by 5 seconds // Delay reconnection attempt by 5 seconds
reconnectionDelay: 5000, reconnectionDelay: 5000,
// Then decrease to 10 second intervals reconnectionDelayMax: 5000,
reconnectionDelayMax: 10000, // Timeout after 4 seconds so we never stack requests
// Timeout after 5 seconds so we never stack requests timeout: 4000,
timeout: 5000,
}) })
// Event handlers // Event handlers
socket.on("plugin-update", data => { socket.on("plugin-update", data => {
builderStore.actions.updateUsedPlugin(data.name, data.hash) builderStore.actions.updateUsedPlugin(data.name, data.hash)
notificationStore.actions.info(`"${data.name}" plugin reloaded`)
}) })
} }

View File

@ -8,10 +8,9 @@ export const buildRowEndpoints = API => ({
if (!tableId || !rowId) { if (!tableId || !rowId) {
return null return null
} }
const row = await API.get({ return await API.get({
url: `/api/${tableId}/rows/${rowId}`, url: `/api/${tableId}/rows/${rowId}`,
}) })
return (await API.enrichRows([row], tableId))[0]
}, },
/** /**