Merge branch 'plugins-dev-experience' of github.com:Budibase/budibase into plugins-dev-experience

This commit is contained in:
mike12345567 2022-08-11 16:36:07 +01:00
commit ba16af1daa
13 changed files with 158 additions and 45 deletions

View File

@ -178,6 +178,14 @@ export const streamUpload = async (
const objectStore = ObjectStore(bucketName)
await makeSureBucketExists(objectStore, bucketName)
// Set content type for certain known extensions
if (filename?.endsWith(".js")) {
extra = {
...extra,
ContentType: "application/javascript",
}
}
const params = {
Bucket: sanitizeBucket(bucketName),
Key: sanitizeKey(filename),

View File

@ -90,36 +90,14 @@ export const getFrontendStore = () => {
// Fetch component definitions.
// Allow errors to propagate.
let components = await API.fetchComponentLibDefinitions(application.appId)
// Extend definitions with custom components
components["test"] = {
component: "test",
name: "Super cool component",
icon: "Text",
description: "A custom component",
showSettingsBar: false,
hasChildren: true,
settings: [
{
type: "text",
key: "text",
label: "Text",
},
],
context: {
type: "static",
values: [
{
label: "Text prop",
key: "text",
},
],
},
}
const components = await API.fetchComponentLibDefinitions(
application.appId
)
// Filter out custom component keys so we can flag them
let customComponents = ["test"]
const customComponents = Object.keys(components).filter(name =>
name.startsWith("plugin/")
)
// Reset store state
store.update(state => ({
@ -146,6 +124,7 @@ export const getFrontendStore = () => {
version: application.version,
revertableVersion: application.revertableVersion,
navigation: application.navigation || {},
usedPlugins: application.usedPlugins || [],
}))
// Initialise backend stores

View File

@ -41,10 +41,23 @@
}
// Construct iframe template
$: template = iframeTemplate.replace(
/\{\{ CLIENT_LIB_PATH }}/,
$store.clientLibPath
)
$: pluginLinks = generatePluginLinks($store.usedPlugins)
$: template = iframeTemplate
.replace(/\{\{ CLIENT_LIB_PATH }}/, $store.clientLibPath)
.replace(/\{\{ PLUGINS }}/, pluginLinks)
const generatePluginLinks = plugins => {
if (!plugins?.length) {
return ""
}
return plugins
.map(plugin => {
// Split up like this as otherwise parsing fails because the script
// tags confuse svelte
return `<` + `script src="/plugins/${plugin.jsUrl}"></` + `script>`
})
.join("")
}
const placeholderScreen = new Screen()
.name("Screen Placeholder")
@ -92,6 +105,7 @@
? [$store.componentToPaste?._id]
: [],
isBudibaseEvent: true,
usedPlugins: $store.usedPlugins,
}
// Refresh the preview when required

View File

@ -37,9 +37,7 @@ export default `
}
</style>
<script src='{{ CLIENT_LIB_PATH }}'></script>
<script
type="application/javascript"
src="https://cdn.kingston.dev/plugin.min.js"></script>
{{ PLUGINS }}
<script>
function receiveMessage(event) {
if (!event.data) {

View File

@ -121,7 +121,8 @@ const createComponentStore = () => {
if (!state.customComponentManifest) {
state.customComponentManifest = {}
}
state.customComponentManifest[schema.schema.name] = {
const componentName = `plugin/${schema.schema.name}/1.0.0`
state.customComponentManifest[componentName] = {
schema,
Component,
}

View File

@ -10,4 +10,13 @@ export const buildPluginEndpoints = API => ({
json: false,
})
},
/**
* Gets a list of all plugins
*/
getPlugins: async () => {
return await API.get({
url: "/api/plugin",
})
},
})

View File

@ -547,7 +547,7 @@ export const sync = async (ctx: any, next: any) => {
}
}
const updateAppPackage = async (appPackage: any, appId: any) => {
export const updateAppPackage = async (appPackage: any, appId: any) => {
return context.doInAppContext(appId, async () => {
const db = context.getAppDB()
const application = await db.get(DocumentType.APP_METADATA)

View File

@ -1,6 +1,7 @@
const { DocumentType } = require("../../db/utils")
const { DocumentType, getPluginParams } = require("../../db/utils")
const { getComponentLibraryManifest } = require("../../utilities/fileSystem")
const { getAppDB } = require("@budibase/backend-core/context")
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
exports.fetchAppComponentDefinitions = async function (ctx) {
const db = getAppDB()
@ -9,7 +10,6 @@ exports.fetchAppComponentDefinitions = async function (ctx) {
let componentManifests = await Promise.all(
app.componentLibraries.map(async library => {
let manifest = await getComponentLibraryManifest(library)
return {
manifest,
library,
@ -30,5 +30,24 @@ exports.fetchAppComponentDefinitions = async function (ctx) {
}
}
}
// Add custom components
const globalDB = getGlobalDB()
const response = await globalDB.allDocs(
getPluginParams(null, {
include_docs: true,
})
)
response.rows
.map(row => row.doc)
.filter(plugin => plugin.schema.type === "component")
.forEach(plugin => {
const fullComponentName = `plugin/${plugin.name}/${plugin.version}`
definitions[fullComponentName] = {
component: fullComponentName,
...plugin.schema.schema,
}
})
ctx.body = definitions
}

View File

@ -1,7 +1,7 @@
import { ObjectStoreBuckets } from "../../constants"
import { extractPluginTarball } from "../../utilities/fileSystem"
import { getGlobalDB } from "@budibase/backend-core/tenancy"
import { generatePluginID } from "../../db/utils"
import { generatePluginID, getPluginParams } from "../../db/utils"
import { uploadDirectory } from "@budibase/backend-core/objectStore"
export async function upload(ctx: any) {
@ -67,6 +67,14 @@ export async function upload(ctx: any) {
}
}
export async function fetch(ctx: any) {}
export async function fetch(ctx: any) {
const db = getGlobalDB()
const response = await db.allDocs(
getPluginParams(null, {
include_docs: true,
})
)
ctx.body = response.rows.map((row: any) => row.doc)
}
export async function destroy(ctx: any) {}

View File

@ -1,7 +1,14 @@
const { getScreenParams, generateScreenID } = require("../../db/utils")
const {
getScreenParams,
generateScreenID,
getPluginParams,
DocumentTypes,
} = require("../../db/utils")
const { AccessController } = require("@budibase/backend-core/roles")
const { getAppDB } = require("@budibase/backend-core/context")
const { events } = require("@budibase/backend-core")
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
import { updateAppPackage } from "./application"
exports.fetch = async ctx => {
const db = getAppDB()
@ -32,6 +39,48 @@ exports.save = async ctx => {
const response = await db.put(screen)
// Find any custom components being used
let pluginNames = []
findPlugins(screen.props, pluginNames)
if (pluginNames.length) {
const globalDB = getGlobalDB()
const pluginsResponse = await globalDB.allDocs(
getPluginParams(null, {
include_docs: true,
})
)
const requiredPlugins = pluginsResponse.rows
.map(row => row.doc)
.filter(plugin => {
return (
plugin.schema.type === "component" &&
pluginNames.includes(`plugin/${plugin.name}/${plugin.version}`)
)
})
// Update the app metadata
const application = await db.get(DocumentTypes.APP_METADATA)
let usedPlugins = application.usedPlugins || []
let pluginAdded = false
requiredPlugins.forEach(plugin => {
if (!usedPlugins.find(x => x._id === plugin._id)) {
pluginAdded = true
usedPlugins.push({
_id: plugin._id,
name: plugin.name,
version: plugin.version,
jsUrl: plugin.jsUrl,
})
}
})
if (pluginAdded) {
console.log("plugin added! new plugins", usedPlugins)
await updateAppPackage({ usedPlugins }, ctx.appId)
}
}
if (eventFn) {
await eventFn(screen)
}
@ -56,3 +105,18 @@ exports.destroy = async ctx => {
}
ctx.status = 200
}
const findPlugins = (component, foundPlugins) => {
if (!component) {
return
}
if (component._component.startsWith("plugin")) {
if (!foundPlugins.includes(component._component)) {
foundPlugins.push(component._component)
}
}
if (!component._children || !component._children.length) {
return
}
component._children.forEach(child => findPlugins(child, foundPlugins))
}

View File

@ -109,6 +109,7 @@ export const serveApp = async function (ctx: any) {
production: env.isProd(),
appId,
clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
usedPlugins: appInfo.usedPlugins,
})
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)

View File

@ -3,6 +3,7 @@
export let favicon = ""
export let clientLibPath
export let usedPlugins
</script>
<svelte:head>
@ -85,9 +86,13 @@
</script>
<!-- Custom components need inserted after the core client library -->
<!-- But before loadBudibase is called -->
{#if usedPlugins?.length}
{#each usedPlugins as plugin}
<script
type="application/javascript"
src="https://cdn.kingston.dev/plugin.min.js"></script>
src={`/plugins/${plugin.jsUrl}`}></script>
{/each}
{/if}
<script type="application/javascript">
if (window.loadBudibase) {
window.loadBudibase()

View File

@ -384,3 +384,10 @@ exports.getMultiIDParams = ids => {
include_docs: true,
}
}
/**
* Gets parameters for retrieving automations, this is a utility function for the getDocParams function.
*/
exports.getPluginParams = (pluginId = null, otherProps = {}) => {
return getDocParams(DocumentType.PLUGIN, pluginId, otherProps)
}