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

This commit is contained in:
Andrew Kingston 2022-08-22 11:27:18 +01:00
commit f144288cf8
8 changed files with 88 additions and 43 deletions

View File

@ -28,6 +28,9 @@
let importModal
$: showImportButton = false
$: customIntegrations = Object.entries(integrations).filter(
entry => entry[1].custom
)
checkShowImport()
@ -163,17 +166,19 @@
/>
{/each}
</div>
<Body size="S">Custom data source</Body>
<div class="item-list">
{#each Object.entries(integrations).filter(entry => entry[1].custom) as [integrationType, schema]}
<DatasourceCard
on:selected={evt => selectIntegration(evt.detail)}
{schema}
bind:integrationType
{integration}
/>
{/each}
</div>
{#if customIntegrations.length > 0}
<Body size="S">Custom data source</Body>
<div class="item-list">
{#each customIntegrations as [integrationType, schema]}
<DatasourceCard
on:selected={evt => selectIntegration(evt.detail)}
{schema}
bind:integrationType
{integration}
/>
{/each}
</div>
{/if}
</Layout>
</ModalContent>
</Modal>

View File

@ -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")

View File

@ -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))

View File

@ -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(),

View File

@ -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

View File

@ -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
)
}
}
},

View File

@ -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)) {
return require(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(

View File

@ -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)
}
})
})
}