tidy up, styling

This commit is contained in:
Martin McKeaveney 2020-06-02 11:08:53 +01:00
parent 401b6689bc
commit 3a983cea8a
14 changed files with 98 additions and 119 deletions

View File

@ -9,6 +9,17 @@
const { open, close } = getContext("simple-modal") const { open, close } = getContext("simple-modal")
const ACCESS_LEVELS = [
{
name: "Admin",
key: "ADMIN"
},
{
name: "Power User",
key: "POWER_USER"
}
];
let selectedTab = "SETUP" let selectedTab = "SETUP"
let testResult let testResult
@ -92,16 +103,14 @@
</div> </div>
<div class="uk-margin config-item"> <div class="uk-margin config-item">
<label class="uk-form-label">User Access</label> <label class="uk-form-label">User Access</label>
<label> <div class="access-levels">
<input class="uk-checkbox" type="checkbox" name="radio1" /> {#each ACCESS_LEVELS as { name, key }}
Admin <span class="access-level">
</label> <label>{name}</label>
<br /> <input class="uk-checkbox" type="checkbox" />
<label> </span>
<input class="uk-checkbox" type="checkbox" name="radio1" /> {/each}
Power User </div>
</label>
<br />
</div> </div>
</div> </div>
<button class="workflow-button hoverable" on:click={deleteWorkflow}> <button class="workflow-button hoverable" on:click={deleteWorkflow}>
@ -161,6 +170,21 @@
font-weight: 500; font-weight: 500;
} }
.workflow-button:hover {
background: var(--light-grey);
}
.access-level {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20px;
}
.access-level label {
font-weight: normal;
}
.test-result { .test-result {
border: none; border: none;
width: 100%; width: 100%;

View File

@ -119,4 +119,8 @@
font-size: 12px; font-size: 12px;
font-weight: 500; font-weight: 500;
} }
.new-workflow-button:hover {
background: var(--light-grey);
}
</style> </style>

View File

@ -46,16 +46,16 @@ const ACTION = {
record: {}, record: {},
}, },
}, },
FIND_RECORD: { // FIND_RECORD: {
description: "Find a record in your database.", // description: "Find a record in your database.",
tagline: "<b>Find</b> a <b>{{record.model.name}}</b> record", // tagline: "<b>Find</b> a <b>{{record.model.name}}</b> record",
icon: "ri-search-line", // icon: "ri-search-line",
name: "Find Record", // name: "Find Record",
environment: "SERVER", // environment: "SERVER",
params: { // params: {
record: "string", // record: "string",
}, // },
}, // },
CREATE_USER: { CREATE_USER: {
description: "Create a new user.", description: "Create a new user.",
tagline: "Create user <b>{{username}}</b>", tagline: "Create user <b>{{username}}</b>",

View File

@ -5,7 +5,7 @@ import Orchestrator from "./orchestrator"
import clientActions from "./actions" import clientActions from "./actions"
// Execute a workflow from a running budibase app // Execute a workflow from a running budibase app
export const clientStrategy = ({ api, instanceId }) => ({ export const clientStrategy = ({ api }) => ({
context: {}, context: {},
bindContextArgs: function(args) { bindContextArgs: function(args) {
const mappedArgs = { ...args } const mappedArgs = { ...args }
@ -40,7 +40,7 @@ export const clientStrategy = ({ api, instanceId }) => ({
// this workflow block gets executed on the server // this workflow block gets executed on the server
if (block.environment === "SERVER") { if (block.environment === "SERVER") {
const EXECUTE_WORKFLOW_URL = `/api/${instanceId}/workflows/action` const EXECUTE_WORKFLOW_URL = `/api/workflows/action`
const response = await api.post({ const response = await api.post({
url: EXECUTE_WORKFLOW_URL, url: EXECUTE_WORKFLOW_URL,
body: { body: {
@ -58,8 +58,8 @@ export const clientStrategy = ({ api, instanceId }) => ({
}, },
}) })
export const triggerWorkflow = api => async ({ workflow, instanceId }) => { export const triggerWorkflow = api => async ({ workflow }) => {
const workflowOrchestrator = new Orchestrator(api, instanceId) const workflowOrchestrator = new Orchestrator(api)
workflowOrchestrator.strategy = clientStrategy workflowOrchestrator.strategy = clientStrategy
const EXECUTE_WORKFLOW_URL = `/api/workflows/${workflow}` const EXECUTE_WORKFLOW_URL = `/api/workflows/${workflow}`

View File

@ -1,8 +1,3 @@
import { get } from "svelte/store"
import mustache from "mustache"
import { appStore } from "../../state/store"
import clientActions from "./actions"
/** /**
* The workflow orchestrator is a class responsible for executing workflows. * The workflow orchestrator is a class responsible for executing workflows.
* It relies on the strategy pattern, which allows composable behaviour to be * It relies on the strategy pattern, which allows composable behaviour to be
@ -11,13 +6,12 @@ import clientActions from "./actions"
* *
*/ */
export default class Orchestrator { export default class Orchestrator {
constructor(api, instanceId) { constructor(api) {
this.api = api this.api = api
this.instanceId = instanceId
} }
set strategy(strategy) { set strategy(strategy) {
this._strategy = strategy({ api: this.api, instanceId: this.instanceId }) this._strategy = strategy({ api: this.api })
} }
async execute(workflow) { async execute(workflow) {
@ -26,59 +20,3 @@ export default class Orchestrator {
} }
} }
} }
// Execute a workflow from a running budibase app
export const clientStrategy = ({ api, instanceId }) => ({
context: {},
bindContextArgs: function(args) {
const mappedArgs = { ...args }
// bind the workflow action args to the workflow context, if required
for (let arg in args) {
const argValue = args[arg]
// We don't want to render mustache templates on non-strings
if (typeof argValue !== "string") continue
// Render the string with values from the workflow context and state
mappedArgs[arg] = mustache.render(argValue, {
context: this.context,
state: get(appStore),
})
}
return mappedArgs
},
run: async function(workflow) {
for (let block of workflow.steps) {
console.log("Executing workflow block", block)
// This code gets run in the browser
if (block.environment === "CLIENT") {
const action = clientActions[block.actionId]
await action({
context: this.context,
args: this.bindContextArgs(block.args),
id: block.id,
})
}
// this workflow block gets executed on the server
if (block.environment === "SERVER") {
const EXECUTE_WORKFLOW_URL = `/api/${instanceId}/workflows/action`
const response = await api.post({
url: EXECUTE_WORKFLOW_URL,
body: {
action: block.actionId,
args: this.bindContextArgs(block.args, api),
},
})
this.context = {
...this.context,
[block.actionId]: response,
}
}
}
},
})

View File

@ -65,7 +65,6 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => {
const isBound = typeof propValue === "string" && propValue.startsWith("{{") const isBound = typeof propValue === "string" && propValue.startsWith("{{")
if (isBound) { if (isBound) {
console.log("NODE IS BOUND", node)
initialProps[propName] = mustache.render(propValue, { initialProps[propName] = mustache.render(propValue, {
state: currentStoreState, state: currentStoreState,
context, context,

View File

@ -43,6 +43,7 @@ exports.save = async function(ctx) {
const response = await db.post(record) const response = await db.post(record)
record._rev = response.rev record._rev = response.rev
ctx.eventEmitter &&
ctx.eventEmitter.emit(`record:save`, { ctx.eventEmitter.emit(`record:save`, {
record, record,
instanceId: ctx.params.instanceId, instanceId: ctx.params.instanceId,
@ -86,7 +87,7 @@ exports.destroy = async function(ctx) {
return return
} }
ctx.body = await db.remove(ctx.params.recordId, ctx.params.revId) ctx.body = await db.remove(ctx.params.recordId, ctx.params.revId)
ctx.eventEmitter.emit(`record:delete`, record) ctx.eventEmitter && ctx.eventEmitter.emit(`record:delete`, record)
} }
exports.validate = async function(ctx) { exports.validate = async function(ctx) {

View File

@ -1,14 +1,12 @@
const userController = require("../../user") const userController = require("../../user")
module.exports = async function createUser(user) { module.exports = async function createUser({ args, instanceId }) {
console.log("SAVING this user", user)
const ctx = { const ctx = {
params: { params: {
instanceId: "inst_60dd510_700f7dc06735403e81d5af91072d7241", instanceId,
}, },
request: { request: {
body: user, body: args.user,
}, },
} }

View File

@ -1,9 +0,0 @@
export default async function() {
const response = await fetch("www.google.com")
console.log(response)
console.log("CUSTOM ACTION")
return {
message: "CUSTOM_WORKFLOW_SCRIPT",
response,
}
}

View File

@ -1,18 +1,29 @@
const recordController = require("../../record") const recordController = require("../../record")
module.exports = async function saveRecord(args) { module.exports = async function saveRecord({ args, instanceId }) {
const { model, ...record } = args.record
const ctx = { const ctx = {
params: { params: {
instanceId: "inst_60dd510_700f7dc06735403e81d5af91072d7241", instanceId,
modelId: model._id,
}, },
request: { request: {
body: args.record, body: record,
}, },
} }
await recordController.save(ctx) await recordController.save(ctx)
try {
return { return {
record: ctx.body, record: ctx.body,
} }
} catch (err) {
console.error(err)
return {
record: null,
error: err.message,
}
}
} }

View File

@ -2,7 +2,7 @@ const sgMail = require("@sendgrid/mail")
sgMail.setApiKey(process.env.SENDGRID_API_KEY) sgMail.setApiKey(process.env.SENDGRID_API_KEY)
module.exports = async function sendEmail(args) { module.exports = async function sendEmail({ args }) {
const msg = { const msg = {
to: args.to, to: args.to,
from: args.from, from: args.from,
@ -20,7 +20,7 @@ module.exports = async function sendEmail(args) {
console.error(err) console.error(err)
return { return {
success: false, success: false,
err, error: err.message,
} }
} }
} }

View File

@ -54,8 +54,12 @@ exports.find = async function(ctx) {
} }
exports.executeAction = async function(ctx) { exports.executeAction = async function(ctx) {
const workflowAction = require(`./actions/${ctx.request.body.action}`) const { args, action } = ctx.request.body
const response = await workflowAction(ctx.request.body.args) const workflowAction = require(`./actions/${action}`)
const response = await workflowAction({
args,
instanceId: ctx.user.instanceId,
})
ctx.body = response ctx.body = response
} }

View File

@ -15,7 +15,7 @@ router
) )
.put("/api/:instanceId/workflows", authorized(BUILDER), controller.update) .put("/api/:instanceId/workflows", authorized(BUILDER), controller.update)
.post("/api/:instanceId/workflows", authorized(BUILDER), controller.create) .post("/api/:instanceId/workflows", authorized(BUILDER), controller.create)
.post("/api/:instanceId/workflows/action", controller.executeAction) .post("/api/workflows/action", controller.executeAction)
.delete( .delete(
"/api/:instanceId/workflows/:id/:rev", "/api/:instanceId/workflows/:id/:rev",
authorized(BUILDER), authorized(BUILDER),

View File

@ -1,4 +1,4 @@
const { app, BrowserWindow } = require("electron") const { app, BrowserWindow, shell } = require("electron")
const { join } = require("path") const { join } = require("path")
const { homedir } = require("os") const { homedir } = require("os")
const isDev = require("electron-is-dev") const isDev = require("electron-is-dev")
@ -18,6 +18,11 @@ const APP_TITLE = "Budibase Builder"
let win let win
function handleRedirect(e, url) {
e.preventDefault()
shell.openExternal(url)
}
async function createWindow() { async function createWindow() {
app.server = await require("./app")() app.server = await require("./app")()
win = new BrowserWindow({ width: 1920, height: 1080 }) win = new BrowserWindow({ width: 1920, height: 1080 })
@ -28,6 +33,10 @@ async function createWindow() {
} else { } else {
autoUpdater.checkForUpdatesAndNotify() autoUpdater.checkForUpdatesAndNotify()
} }
// open _blank in default browser
win.webContents.on("new-window", handleRedirect)
win.webContents.on("will-navigate", handleRedirect)
} }
app.whenReady().then(createWindow) app.whenReady().then(createWindow)