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