Merge branch 'plugins-dev-experience' of github.com:Budibase/budibase into plugins-dev-experience
This commit is contained in:
commit
975afc29d2
|
@ -28,6 +28,9 @@
|
|||
let importModal
|
||||
|
||||
$: showImportButton = false
|
||||
$: customIntegrations = Object.entries(integrations).filter(
|
||||
entry => entry[1].custom
|
||||
)
|
||||
|
||||
checkShowImport()
|
||||
|
||||
|
@ -163,9 +166,10 @@
|
|||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{#if customIntegrations.length > 0}
|
||||
<Body size="S">Custom data source</Body>
|
||||
<div class="item-list">
|
||||
{#each Object.entries(integrations).filter(entry => entry[1].custom) as [integrationType, schema]}
|
||||
{#each customIntegrations as [integrationType, schema]}
|
||||
<DatasourceCard
|
||||
on:selected={evt => selectIntegration(evt.detail)}
|
||||
{schema}
|
||||
|
@ -174,6 +178,7 @@
|
|||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</Layout>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
|
|
@ -9,6 +9,19 @@ const { runPkgCommand } = require("../exec")
|
|||
const { join } = require("path")
|
||||
const { success, error, info } = require("../utils")
|
||||
|
||||
function checkInPlugin() {
|
||||
if (!fs.existsSync("package.json")) {
|
||||
throw new Error(
|
||||
"Please run in a plugin directory - must contain package.json"
|
||||
)
|
||||
}
|
||||
if (!fs.existsSync("schema.json")) {
|
||||
throw new Error(
|
||||
"Please run in a plugin directory - must contain schema.json"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function init(opts) {
|
||||
const type = opts["init"] || opts
|
||||
if (!type || !PLUGIN_TYPES_ARR.includes(type)) {
|
||||
|
@ -35,13 +48,15 @@ async function init(opts) {
|
|||
// get the skeleton
|
||||
console.log(info("Retrieving project..."))
|
||||
await getSkeleton(type, name)
|
||||
await fleshOutSkeleton(name, desc, version)
|
||||
await fleshOutSkeleton(type, name, desc, version)
|
||||
console.log(info("Installing dependencies..."))
|
||||
await runPkgCommand("install", join(process.cwd(), name))
|
||||
console.log(info(`Plugin created in directory "${name}"`))
|
||||
}
|
||||
|
||||
async function verify() {
|
||||
// will throw errors if not acceptable
|
||||
checkInPlugin()
|
||||
console.log(info("Verifying plugin..."))
|
||||
const schema = fs.readFileSync("schema.json", "utf8")
|
||||
const pkg = fs.readFileSync("package.json", "utf8")
|
||||
|
|
|
@ -6,7 +6,7 @@ const { join } = require("path")
|
|||
const tar = require("tar")
|
||||
const { processStringSync } = require("@budibase/string-templates")
|
||||
|
||||
const HBS_FILES = ["package.json.hbs", "schema.json.hbs"]
|
||||
const HBS_FILES = ["package.json.hbs", "schema.json.hbs", "README.md.hbs"]
|
||||
|
||||
async function getSkeletonUrl(type) {
|
||||
const resp = await fetch(
|
||||
|
@ -40,7 +40,7 @@ exports.getSkeleton = async (type, name) => {
|
|||
fs.rmSync(tarballFile)
|
||||
}
|
||||
|
||||
exports.fleshOutSkeleton = async (name, description, version) => {
|
||||
exports.fleshOutSkeleton = async (type, name, description, version) => {
|
||||
for (let file of HBS_FILES) {
|
||||
const oldFile = join(name, file),
|
||||
newFile = join(name, file.substring(0, file.length - 4))
|
||||
|
|
|
@ -22,6 +22,8 @@ function validateComponent(schema) {
|
|||
const validator = joi.object({
|
||||
type: joi.string().allow("component").required(),
|
||||
metadata: joi.object().unknown(true).required(),
|
||||
hash: joi.string().optional(),
|
||||
version: joi.string().optional(),
|
||||
schema: joi
|
||||
.object({
|
||||
name: joi.string().required(),
|
||||
|
@ -53,6 +55,8 @@ function validateDatasource(schema) {
|
|||
const validator = joi.object({
|
||||
type: joi.string().allow("datasource").required(),
|
||||
metadata: joi.object().unknown(true).required(),
|
||||
hash: joi.string().optional(),
|
||||
version: joi.string().optional(),
|
||||
schema: joi.object({
|
||||
docs: joi.string(),
|
||||
friendlyName: joi.string().required(),
|
||||
|
|
|
@ -17,15 +17,12 @@ const bullboard = require("./automations/bullboard")
|
|||
const { logAlert } = require("@budibase/backend-core/logging")
|
||||
const { pinoSettings } = require("@budibase/backend-core")
|
||||
const { Thread } = require("./threads")
|
||||
const chokidar = require("chokidar")
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
import redis from "./utilities/redis"
|
||||
import * as migrations from "./migrations"
|
||||
import { events, installation, tenancy } from "@budibase/backend-core"
|
||||
import { createAdminUser, getChecklist } from "./utilities/workerRequests"
|
||||
import { processPlugin } from "./api/controllers/plugin"
|
||||
import { DEFAULT_TENANT_ID } from "@budibase/backend-core/constants"
|
||||
import { watch } from "./watch"
|
||||
|
||||
const app = new Koa()
|
||||
|
||||
|
@ -144,28 +141,7 @@ module.exports = server.listen(env.PORT || 0, async () => {
|
|||
env.PLUGINS_DIR &&
|
||||
fs.existsSync(env.PLUGINS_DIR)
|
||||
) {
|
||||
const watchPath = path.join(env.PLUGINS_DIR, "./**/*.tar.gz")
|
||||
chokidar
|
||||
.watch(watchPath, {
|
||||
ignored: "**/node_modules",
|
||||
awaitWriteFinish: true,
|
||||
})
|
||||
.on("all", async (event: string, path: string) => {
|
||||
// Sanity checks
|
||||
if (!path?.endsWith(".tar.gz") || !fs.existsSync(path)) {
|
||||
return
|
||||
}
|
||||
await tenancy.doInTenant(DEFAULT_TENANT_ID, async () => {
|
||||
try {
|
||||
const split = path.split("/")
|
||||
const name = split[split.length - 1]
|
||||
console.log("Importing plugin:", path)
|
||||
await processPlugin({ name, path })
|
||||
} catch (err) {
|
||||
console.log("Failed to import plugin:", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
watch()
|
||||
}
|
||||
|
||||
// check for version updates
|
||||
|
|
|
@ -93,7 +93,11 @@ module.exports = {
|
|||
for (let plugin of plugins) {
|
||||
if (plugin.name === integration) {
|
||||
// need to use commonJS require due to its dynamic runtime nature
|
||||
return getDatasourcePlugin(plugin.name, plugin.jsUrl)
|
||||
return getDatasourcePlugin(
|
||||
plugin.name,
|
||||
plugin.jsUrl,
|
||||
plugin.schema?.hash
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -352,13 +352,21 @@ exports.extractPluginTarball = async file => {
|
|||
return { metadata, directory: path }
|
||||
}
|
||||
|
||||
exports.getDatasourcePlugin = async (name, url) => {
|
||||
exports.getDatasourcePlugin = async (name, url, hash) => {
|
||||
if (!fs.existsSync(DATASOURCE_PATH)) {
|
||||
fs.mkdirSync(DATASOURCE_PATH)
|
||||
}
|
||||
const filename = join(DATASOURCE_PATH, name)
|
||||
const metadataName = `${filename}.bbmetadata`
|
||||
if (fs.existsSync(filename)) {
|
||||
const currentHash = fs.readFileSync(metadataName, "utf8")
|
||||
// if hash is the same return the file, otherwise remove it and re-download
|
||||
if (currentHash === hash) {
|
||||
return require(filename)
|
||||
} else {
|
||||
console.log(`Updating plugin: ${name}`)
|
||||
fs.unlinkSync(filename)
|
||||
}
|
||||
}
|
||||
const fullUrl = checkSlashesInUrl(
|
||||
`${env.MINIO_URL}/${ObjectStoreBuckets.PLUGINS}/${url}`
|
||||
|
@ -367,6 +375,7 @@ exports.getDatasourcePlugin = async (name, url) => {
|
|||
if (response.status === 200) {
|
||||
const content = await response.text()
|
||||
fs.writeFileSync(filename, content)
|
||||
fs.writeFileSync(metadataName, hash)
|
||||
require(filename)
|
||||
} else {
|
||||
throw new Error(
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import path from "path"
|
||||
import * as env from "./environment"
|
||||
import chokidar from "chokidar"
|
||||
import fs from "fs"
|
||||
import { tenancy } from "@budibase/backend-core"
|
||||
import { DEFAULT_TENANT_ID } from "@budibase/backend-core/constants"
|
||||
import { processPlugin } from "./api/controllers/plugin"
|
||||
|
||||
export function watch() {
|
||||
const watchPath = path.join(env.PLUGINS_DIR, "./**/*.tar.gz")
|
||||
chokidar
|
||||
.watch(watchPath, {
|
||||
ignored: "**/node_modules",
|
||||
awaitWriteFinish: true,
|
||||
})
|
||||
.on("all", async (event: string, path: string) => {
|
||||
// Sanity checks
|
||||
if (!path?.endsWith(".tar.gz") || !fs.existsSync(path)) {
|
||||
return
|
||||
}
|
||||
await tenancy.doInTenant(DEFAULT_TENANT_ID, async () => {
|
||||
try {
|
||||
const split = path.split("/")
|
||||
const name = split[split.length - 1]
|
||||
console.log("Importing plugin:", path)
|
||||
await processPlugin({ name, path })
|
||||
} catch (err) {
|
||||
console.log("Failed to import plugin:", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue