Merge branch 'plugins-dev-experience' of github.com:Budibase/budibase into plugins-dev-experience
This commit is contained in:
commit
ba16af1daa
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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`)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue