clientId fetching before app load, custom views, fixed schema

This commit is contained in:
Martin McKeaveney 2020-04-28 14:39:35 +01:00
parent 44bba145c7
commit a645df082c
37 changed files with 237 additions and 3448 deletions

View File

@ -59,6 +59,7 @@
"@babel/preset-env": "^7.5.5", "@babel/preset-env": "^7.5.5",
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"@rollup/plugin-alias": "^3.0.1", "@rollup/plugin-alias": "^3.0.1",
"@rollup/plugin-json": "^4.0.3",
"@sveltech/routify": "1.5.0-beta.40", "@sveltech/routify": "1.5.0-beta.40",
"babel-jest": "^24.8.0", "babel-jest": "^24.8.0",
"browser-sync": "^2.26.7", "browser-sync": "^2.26.7",

View File

@ -16,7 +16,15 @@
}) })
} }
onMount(() => { onMount(async () => {
const res = await fetch(`/api/client/id`)
const json = await res.json()
store.update(state => {
state.clientId = json
return state
})
window.addEventListener("error", showErrorBanner) window.addEventListener("error", showErrorBanner)
window.addEventListener("unhandledrejection", showErrorBanner) window.addEventListener("unhandledrejection", showErrorBanner)
}) })
@ -26,6 +34,8 @@
<AppNotification /> <AppNotification />
<Modal> {#if $store.clientId}
<Router {routes} /> <Modal>
</Modal> <Router {routes} />
</Modal>
{/if}

View File

@ -1,5 +1,5 @@
const apiCall = method => (url, body) => const apiCall = method => async (url, body) => {
fetch(url, { const response = await fetch(url, {
method: method, method: method,
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -7,6 +7,13 @@ const apiCall = method => (url, body) =>
body: body && JSON.stringify(body), body: body && JSON.stringify(body),
}) })
// if (response.status === 500) {
// throw new Error("Server Error");
// }
return response;
}
const post = apiCall("POST") const post = apiCall("POST")
const get = apiCall("GET") const get = apiCall("GET")
const patch = apiCall("PATCH") const patch = apiCall("PATCH")

View File

@ -8,12 +8,9 @@ import {
export const getBackendUiStore = () => { export const getBackendUiStore = () => {
const INITIAL_BACKEND_UI_STATE = { const INITIAL_BACKEND_UI_STATE = {
selectedView: {
records: [],
name: "",
},
breadcrumbs: [], breadcrumbs: [],
models: [], models: [],
views: [],
users: [], users: [],
selectedDatabase: {}, selectedDatabase: {},
selectedModel: {}, selectedModel: {},
@ -24,12 +21,15 @@ export const getBackendUiStore = () => {
store.actions = { store.actions = {
database: { database: {
select: async db => { select: async db => {
const response = await api.get(`/api/${db.id}/models`) const modelsResponse = await api.get(`/api/${db.id}/models`)
const models = await response.json() const viewsResponse = await api.get(`/api/${db.id}/views`)
const models = await modelsResponse.json()
const views = await viewsResponse.json()
store.update(state => { store.update(state => {
state.selectedDatabase = db state.selectedDatabase = db
state.breadcrumbs = [db.name] state.breadcrumbs = [db.name]
state.models = models state.models = models
state.views = views;
return state return state
}) })
} }

View File

@ -43,8 +43,7 @@ export const getStore = () => {
libraries: null, libraries: null,
showSettings: false, showSettings: false,
useAnalytics: true, useAnalytics: true,
appId: "", appId: ""
clientId: "budibase"
} }
const store = writable(initial) const store = writable(initial)

View File

@ -7,7 +7,7 @@
{#if hasErrors} {#if hasErrors}
<div uk-alert class="uk-alert-danger"> <div uk-alert class="uk-alert-danger">
{#each errors as error} {#each errors as error}
<div>{error.field ? `${error.field}: ` : ''}{error.error}</div> <div>{error.dataPath} {error.message}</div>
{/each} {/each}
</div> </div>
{/if} {/if}

View File

@ -77,10 +77,6 @@
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
) )
// async function fetchRecordsForView(view, instance) {
// return await api.fetchDataForView($backendUiStore.selectedView)
// }
function drillIntoRecord(record) { function drillIntoRecord(record) {
backendUiStore.update(state => { backendUiStore.update(state => {
state.breadcrumbs = [...state.breadcrumbs, record.type, record.id] state.breadcrumbs = [...state.breadcrumbs, record.type, record.id]
@ -100,7 +96,7 @@
<section> <section>
<div class="table-controls"> <div class="table-controls">
<h2 class="title"> <h2 class="title">
{takeRight(2, $backendUiStore.breadcrumbs).join(' / ')} {$backendUiStore.selectedModel.name}
</h2> </h2>
</div> </div>
<table class="uk-table"> <table class="uk-table">

View File

@ -30,11 +30,11 @@ export async function loadRecord(key, { appname, instanceId }) {
export async function saveRecord(record, instanceId) { export async function saveRecord(record, instanceId) {
const SAVE_RECORDS_URL = `/api/${instanceId}/records` const SAVE_RECORDS_URL = `/api/${instanceId}/records`
const response = await api.post(SAVE_RECORDS_URL, record) const response = await api.post(SAVE_RECORDS_URL, record)
return await response.json() return await response.json()
} }
export async function fetchDataForView(viewName, instanceId) { export async function fetchDataForView(viewName, instanceId) {
// const FETCH_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/listRecords/${viewName}`
const FETCH_RECORDS_URL = `/api/${instanceId}/${viewName}/records` const FETCH_RECORDS_URL = `/api/${instanceId}/${viewName}/records`
const response = await api.get(FETCH_RECORDS_URL) const response = await api.get(FETCH_RECORDS_URL)

View File

@ -89,7 +89,6 @@
<div>{key}</div> <div>{key}</div>
</td> </td>
<td>{meta.type}</td> <td>{meta.type}</td>
<!-- <td>{meta.typeOptions.values || ''}</td> -->
<td> <td>
<i <i
class="ri-delete-bin-6-line hoverable" class="ri-delete-bin-6-line hoverable"

View File

@ -16,21 +16,18 @@
getDefaultTypeOptions, getDefaultTypeOptions,
} from "components/common/core" } from "components/common/core"
const FIELD_TYPES = ["string", "number", "boolean"]
export let field = { type: "string" } export let field = { type: "string" }
export let schema export let schema
export let goBack export let goBack
export let onFinished = () => {}
let errors = [] let errors = []
let draftField = cloneDeep(field); let draftField = cloneDeep(field)
const save = () => { const save = () => {
// errors = validate.field(allFields)(clonedField) schema[field.name] = draftField
// if (errors.length > 0) return goBack()
// field.typeOptions = cloneDeep(clonedField.typeOptions)
schema[field.name] = draftField;
goBack();
// onFinished({ ...field, ...clonedField })
} }
</script> </script>
@ -43,37 +40,23 @@
<Dropdown <Dropdown
label="Type" label="Type"
bind:selected={draftField.type} bind:selected={draftField.type}
options={keys(allTypes)} /> options={FIELD_TYPES} />
{#if field.type === 'string'} {#if field.type === 'string'}
<NumberBox <NumberBox label="Max Length" bind:value={draftField.maxLength} />
label="Max Length" <ValuesList label="Categories" bind:values={draftField.values} />
bind:value={draftField.maxLength} /> {:else if field.type === 'boolean'}
<ValuesList <!-- TODO: revisit and fix with JSON schema -->
label="Categories" <Checkbox label="Allow Null" bind:checked={draftField.allowNulls} />
bind:values={draftField.values} />
{:else if field.type === 'bool'}
<!-- TODO: revisit and fix with JSON schema -->
<Checkbox
label="Allow Null"
bind:checked={draftField.allowNulls} />
{:else if field.format === 'datetime'} {:else if field.format === 'datetime'}
<!-- TODO: revisit and fix with JSON schema --> <!-- TODO: revisit and fix with JSON schema -->
<DatePicker <DatePicker label="Min Value" bind:value={draftField.minValue} />
label="Min Value" <DatePicker label="Max Value" bind:value={draftField.maxValue} />
bind:value={draftField.minValue} />
<DatePicker
label="Max Value"
bind:value={draftField.maxValue} />
{:else if field.type === 'number'} {:else if field.type === 'number'}
<NumberBox <NumberBox label="Min Value" bind:value={draftField.minimum} />
label="Min Value" <NumberBox label="Max Value" bind:value={draftField.maximum} />
bind:value={draftField.minimum} />
<NumberBox
label="Max Value"
bind:value={draftField.maximum} />
{:else if draftField.type.startsWith('array')} {:else if draftField.type.startsWith('array')}
<!-- TODO: revisit and fix with JSON schema --> <!-- TODO: revisit and fix with JSON schema -->
<NumberBox <NumberBox
label="Min Length" label="Min Length"
bind:value={draftField.typeOptions.minLength} /> bind:value={draftField.typeOptions.minLength} />

View File

@ -13,6 +13,10 @@
import * as api from "../api" import * as api from "../api"
import ErrorsBox from "components/common/ErrorsBox.svelte" import ErrorsBox from "components/common/ErrorsBox.svelte"
const CLASS_NAME_MAP = {
boolean: "uk-checkbox"
}
export let record = {} export let record = {}
export let onClosed export let onClosed
@ -57,16 +61,15 @@
backendUiStore.update(state => { backendUiStore.update(state => {
state.selectedView = state.selectedView state.selectedView = state.selectedView
return state
onClosed(); onClosed();
return state
}) })
} }
</script> </script>
<div class="actions"> <div class="actions">
<h4 class="budibase__title--4">Create / Edit Record</h4> <h4 class="budibase__title--4">Create / Edit Record</h4>
<!-- <ErrorsBox {errors} /> --> <ErrorsBox {errors} />
{JSON.stringify(errors)}
<form on:submit|preventDefault class="uk-form-stacked"> <form on:submit|preventDefault class="uk-form-stacked">
{#if !record} {#if !record}
<div class="uk-margin"> <div class="uk-margin">
@ -81,7 +84,7 @@
{#each modelSchema as [key, meta]} {#each modelSchema as [key, meta]}
<div class="uk-margin"> <div class="uk-margin">
<RecordFieldControl <RecordFieldControl
{errors} className={CLASS_NAME_MAP[meta.type]}
type={determineInputType(meta)} type={determineInputType(meta)}
label={key} label={key}
bind:value={record[key]} /> bind:value={record[key]} />

View File

@ -3,50 +3,39 @@
import CodeArea from "components/common/CodeArea.svelte" import CodeArea from "components/common/CodeArea.svelte"
import Button from "components/common/Button.svelte" import Button from "components/common/Button.svelte"
import Dropdown from "components/common/Dropdown.svelte" import Dropdown from "components/common/Dropdown.svelte"
import { store } from "builderStore" import { store, backendUiStore } from "builderStore"
import { filter, some, map, compose } from "lodash/fp" import { filter, some, map, compose } from "lodash/fp"
import {
hierarchy as hierarchyFunctions,
common,
} from "../../../../../../core/src/"
import ErrorsBox from "components/common/ErrorsBox.svelte" import ErrorsBox from "components/common/ErrorsBox.svelte"
import ActionButton from "components/common/ActionButton.svelte" import ActionButton from "components/common/ActionButton.svelte"
import api from "builderStore/api"
const SNIPPET_EDITORS = { const SNIPPET_EDITORS = {
MAP: "Map", MAP: "Map",
FILTER: "Filter", FILTER: "Filter",
SHARD: "Shard Name", REDUCE: "Reduce",
} }
let view const COUCHDB_FUNCTION = `function(doc) {
let indexableModels = []
}`
export let onClosed
export let view = {}
let currentSnippetEditor = SNIPPET_EDITORS.MAP let currentSnippetEditor = SNIPPET_EDITORS.MAP
const indexableModelsFromIndex = compose( $: instanceId = $backendUiStore.selectedDatabase.id
map(node => ({
node,
isallowed:
view.allowedModelNodeIds &&
view.allowedModelNodeIds.some(id => node.nodeId === id),
})),
filter(hierarchyFunctions.isModel),
filter(hierarchyFunctions.isDecendant($store.currentNode.parent())),
hierarchyFunctions.getFlattenedHierarchy
)
store.subscribe($store => { function deleteView() {}
view = $store.currentNode
indexableModels = indexableModelsFromIndex($store.hierarchy)
})
const toggleAllowedModel = model => { async function saveView() {
if (model.isallowed) { const SAVE_VIEW_URL = `/api/${instanceId}/views`
view.allowedModelNodeIds = view.allowedModelNodeIds.filter( const response = await api.post(SAVE_VIEW_URL, view)
id => id !== model.node.nodeId backendUiStore.update(state => {
) state.views = [...state.views, response.view]
} else { return state
view.allowedModelNodeIds.push(model.node.nodeId) })
} onClosed();
} }
</script> </script>
@ -63,26 +52,6 @@
<div class="uk-width-1-2@s"> <div class="uk-width-1-2@s">
<Textbox bind:text={view.name} label="Name" /> <Textbox bind:text={view.name} label="Name" />
</div> </div>
<div class="uk-width-1-2@s">
<Dropdown
label="View Type"
bind:selected={view.indexType}
options={['ancestor', 'reference']} />
</div>
</div>
<div class="allowed-records">
<div class="budibase__label--big">
Which models would you like to add to this view?
</div>
{#each indexableModels as model}
<input
class="uk-checkbox"
type="checkbox"
checked={model.isallowed}
on:change={() => toggleAllowedModel(model)} />
<span class="checkbox-model-label">{model.node.name}</span>
{/each}
</div> </div>
<h4 class="budibase__label--big">Snippets</h4> <h4 class="budibase__label--big">Snippets</h4>
@ -98,18 +67,14 @@
<CodeArea bind:text={view.map} label="Map" /> <CodeArea bind:text={view.map} label="Map" />
{:else if currentSnippetEditor === SNIPPET_EDITORS.FILTER} {:else if currentSnippetEditor === SNIPPET_EDITORS.FILTER}
<CodeArea bind:text={view.filter} label="Filter" /> <CodeArea bind:text={view.filter} label="Filter" />
{:else if currentSnippetEditor === SNIPPET_EDITORS.SHARD} {:else if currentSnippetEditor === SNIPPET_EDITORS.REDUCE}
<CodeArea bind:text={view.getShardName} label="Shard Name" /> <CodeArea bind:text={view.reduce} label="Reduce" />
{/if} {/if}
<ActionButton color="secondary" on:click={store.saveCurrentNode}> <ActionButton color="secondary" on:click={saveView}>
Save Save
</ActionButton> </ActionButton>
<ActionButton alert on:click={deleteView}>Delete</ActionButton>
{#if !$store.currentNodeIsNew}
<ActionButton alert on:click={store.deleteCurrentNode}>Delete</ActionButton>
{/if}
</form> </form>
<style> <style>
@ -118,14 +83,6 @@
padding: 15px; padding: 15px;
} }
.allowed-records {
margin: 20px 0px;
}
.allowed-records > span {
margin-right: 30px;
}
.snippet-selector__heading { .snippet-selector__heading {
margin-right: 20px; margin-right: 20px;
opacity: 0.7; opacity: 0.7;
@ -135,10 +92,6 @@
opacity: 1; opacity: 1;
} }
.checkbox-model-label {
text-transform: capitalize;
}
h3 { h3 {
margin: 0 0 0 10px; margin: 0 0 0 10px;
} }

View File

@ -3,6 +3,9 @@
export let value = "" export let value = ""
export let label export let label
export let errors = [] export let errors = []
export let className = "uk-input"
let checked = type === "checkbox" ? value : false;
const handleInput = event => { const handleInput = event => {
if (event.target.type === "checkbox") { if (event.target.type === "checkbox") {
@ -21,8 +24,9 @@
<label>{label}</label> <label>{label}</label>
<input <input
class="uk-input" class={className}
class:uk-form-danger={errors.length > 0} class:uk-form-danger={errors.length > 0}
{checked}
{type} {type}
{value} {value}
on:input={handleInput} on:input={handleInput}

View File

@ -7,12 +7,13 @@
CreateEditModelModal, CreateEditModelModal,
CreateEditViewModal, CreateEditViewModal,
} from "components/database/ModelDataTable/modals" } from "components/database/ModelDataTable/modals"
import api from "builderStore/api"
const { open, close } = getContext("simple-modal") const { open, close } = getContext("simple-modal")
// export let level = 0
export let node export let node
export let type export let type
export let onSelect
let navActive = "" let navActive = ""
@ -20,51 +21,25 @@
index: "ri-eye-line", index: "ri-eye-line",
model: "ri-list-settings-line", model: "ri-list-settings-line",
} }
store.subscribe(state => {
if (state.currentNode) {
navActive = node.nodeId === state.currentNode.nodeId
}
})
function selectModel(model) {
backendUiStore.update(state => {
state.selectedModel = model
state.selectedView = `all_${model._id}`
return state;
})
// store.selectExistingNode(node.nodeId)
// const modalType =
// node.type === "index" ? CreateEditViewModal : CreateEditModelModal
// open(
// modalType,
// {
// onClosed: close,
// },
// { styleContent: { padding: "0" } }
// )
}
</script> </script>
<div> <div>
<div <div
on:click={() => selectModel(node)} on:click={() => onSelect(node)}
class="budibase__nav-item hierarchy-item" class="budibase__nav-item hierarchy-item"
class:capitalized={type === 'model'} class:capitalized={type === 'model'}
class:selected={$backendUiStore.selectedModel._id === node._id}> class:selected={$backendUiStore.selectedView === `all_${node._id}`}>
<i class={ICON_MAP[type]} /> <i class={ICON_MAP[type]} />
<span style="margin-left: 1rem">{node.name}</span> <span style="margin-left: 1rem">{node.name}</span>
<!-- <i
class="ri-edit-line hoverable"
on:click={editModel}
/>
<i
class="ri-delete-bin-7-line hoverable"
on:click={deleteModel}
/> -->
</div> </div>
<!-- {#if node.children}
{#each node.children as child}
<svelte:self node={child} level={level + 1} type="model" />
{/each}
{/if}
{#if node.indexes}
{#each node.indexes as index}
<svelte:self node={index} level={level + 1} type="index" />
{/each}
{/if} -->
</div> </div>
<style> <style>

View File

@ -13,6 +13,17 @@
const { open, close } = getContext("simple-modal") const { open, close } = getContext("simple-modal")
function editModel() {
open(
CreateEditModelModal,
{
model: node,
onClosed: close,
},
{ styleContent: { padding: "0" } }
)
}
function newModel() { function newModel() {
open( open(
CreateEditModelModal, CreateEditModelModal,
@ -24,7 +35,6 @@
} }
function newView() { function newView() {
// store.newRootIndex()
open( open(
CreateEditViewModal, CreateEditViewModal,
{ {
@ -33,6 +43,32 @@
{ styleContent: { padding: "0" } } { styleContent: { padding: "0" } }
) )
} }
function selectModel(model) {
backendUiStore.update(state => {
state.selectedModel = model
state.selectedView = `all_${model._id}`
return state
})
}
async function deleteModel(modelToDelete) {
const DELETE_MODEL_URL = `/api/${instanceId}/models/${node._id}/${node._rev}`
const response = await api.delete(DELETE_MODEL_URL)
backendUiStore.update(state => {
state.models = state.models.filter(model => model._id !== modelToDelete._id)
state.selectedView = {}
return state
})
}
function selectView(view) {
backendUiStore.update(state => {
state.selectedView = view.name
return state
})
}
</script> </script>
<div class="items-root"> <div class="items-root">
@ -41,28 +77,40 @@
<div class="nav-group-header"> <div class="nav-group-header">
<div class="hierarchy-title">Models</div> <div class="hierarchy-title">Models</div>
<div class="uk-inline"> <div class="uk-inline">
<i class="ri-add-line hoverable" /> <i class="ri-add-line hoverable" on:click={newModel} />
<div uk-dropdown="mode: click;">
<ul class="uk-nav uk-dropdown-nav">
<li class="hoverable" on:click={newModel}>Model</li>
<li class="hoverable" on:click={newView}>View</li>
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="hierarchy-items-container"> <div class="hierarchy-items-container">
{#each $backendUiStore.models as model} {#each $backendUiStore.models as model}
<HierarchyRow node={model} type="model" /> <HierarchyRow
{/each} onSelect={selectModel}
<!-- {#each $store.hierarchy.children as model} node={model}
<HierarchyRow node={model} type="model" /> type="model"
/>
{/each} {/each}
</div>
</div>
{#each $store.hierarchy.indexes as index} <div class="hierarchy">
<HierarchyRow node={index} type="index" /> <div class="components-list-container">
{/each} --> <div class="nav-group-header">
<div class="hierarchy-title">Views</div>
<div class="uk-inline">
<i class="ri-add-line hoverable" on:click={newView} />
</div>
</div>
</div>
<div class="hierarchy-items-container">
{#each $backendUiStore.views as view}
<HierarchyRow
onSelect={selectView}
node={view}
type="view"
/>
{/each}
</div> </div>
</div> </div>
</div> </div>

View File

@ -34,7 +34,7 @@
<div class="database-actions"> <div class="database-actions">
<div class="budibase__label--big">{breadcrumbs}</div> <div class="budibase__label--big">{breadcrumbs}</div>
{#if $backendUiStore.selectedDatabase.id} {#if $backendUiStore.selectedModel._id}
<ActionButton primary on:click={createNewRecord}> <ActionButton primary on:click={createNewRecord}>
Create new record Create new record
</ActionButton> </ActionButton>

View File

@ -8,7 +8,7 @@ module.exports = {
type: "string", type: "string",
describe: describe:
"your apps directory - directory will be created if it does not exist", "your apps directory - directory will be created if it does not exist",
default: "~/budibase", default: "~/.budibase",
alias: "d", alias: "d",
}) })
yargs.positional("database", { yargs.positional("database", {

View File

@ -6,7 +6,7 @@ const { join } = require("path")
const initialiseClientDb = require("@budibase/server/db/initialiseClientDb") const initialiseClientDb = require("@budibase/server/db/initialiseClientDb")
const Sqrl = require("squirrelly") const Sqrl = require("squirrelly")
const uuid = require("uuid") const uuid = require("uuid")
const CouchDb = require("@budibase/server/db/client") const CouchDB = require("@budibase/server/db/client")
module.exports = opts => { module.exports = opts => {
run(opts) run(opts)
@ -16,7 +16,7 @@ const run = async opts => {
try { try {
await ensureAppDir(opts) await ensureAppDir(opts)
await prompts(opts) await prompts(opts)
await createClientDatabse(opts) await createClientDatabase(opts)
await createDevEnvFile(opts) await createDevEnvFile(opts)
console.log(chalk.green("Budibase successfully initialised.")) console.log(chalk.green("Budibase successfully initialised."))
} catch (error) { } catch (error) {
@ -55,12 +55,9 @@ const prompts = async opts => {
} }
} }
//https://admin:password@localhost:5984 const createClientDatabase = async opts => {
const createClientDatabse = async opts => {
const couch = CouchDb()
if (opts.clientId === "new") { if (opts.clientId === "new") {
const existing = await couch.allDbs() const existing = await CouchDB.allDbs()
let i = 0 let i = 0
let isExisting = true let isExisting = true
@ -71,8 +68,7 @@ const createClientDatabse = async opts => {
} }
} }
const db = new couch(`client-${opts.clientId}`) const db = new CouchDB(`client-${opts.clientId}`)
console.log(await db.info())
await initialiseClientDb(db) await initialiseClientDb(db)
} }

View File

@ -13,7 +13,7 @@ module.exports = {
type: "string", type: "string",
describe: "budibase apps directory", describe: "budibase apps directory",
alias: "d", alias: "d",
default: "~/budibase", default: "~/.budibase",
}) })
}, },
handler, handler,

View File

@ -10,7 +10,7 @@ module.exports = {
type: "string", type: "string",
describe: "your budibase apps directory", describe: "your budibase apps directory",
alias: "d", alias: "d",
default: "~/budibase", default: "~/.budibase",
}) })
}, },
handler, handler,

View File

@ -1,7 +1,8 @@
const { resolve, join } = require("path") const { resolve, join } = require("path")
const { cwd } = require("process") const { cwd } = require("process")
const buildAppContext = require("@budibase/server/initialise/buildAppContext")
const { homedir } = require("os") const { homedir } = require("os")
const buildAppContext = require("@budibase/server/initialise/buildAppContext")
module.exports.serverFileName = relativePath => module.exports.serverFileName = relativePath =>
resolve(__dirname, "..", "node_modules", "@budibase", "server", relativePath) resolve(__dirname, "..", "node_modules", "@budibase", "server", relativePath)

File diff suppressed because it is too large Load Diff

View File

@ -5,8 +5,7 @@ var getNewRecord = function getNewRecord(schema, modelName) {
var record = { var record = {
_id: (0, _shortid.generate)(), _id: (0, _shortid.generate)(),
modelId: model._id modelId: model._id };
};
for (var field in model.schema.properties) { for (var field in model.schema.properties) {

View File

@ -4,3 +4,4 @@ myapps/
/builder/* /builder/*
!/builder/assets/ !/builder/assets/
public/ public/
db/dev.db/

View File

@ -1,14 +1,17 @@
const PouchDB = require("pouchdb") const PouchDB = require("pouchdb")
const allDbs = require("pouchdb-all-dbs") const allDbs = require("pouchdb-all-dbs")
const os = require("os");
const path = require("path");
module.exports = () => { const BUDIBASE_DIR = path.join(os.homedir(), ".budibase");
const COUCH_DB_URL =
process.env.COUCH_DB_URL || "http://admin:password@localhost:5984"
const DATABASE_TYPE = process.env.DATABASE_TYPE || "couch"
const pouch = PouchDB.defaults({ const COUCH_DB_URL = process.env.COUCH_DB_URL || `leveldb://${BUDIBASE_DIR}/`;
prefix: COUCH_DB_URL, const DATABASE_TYPE = process.env.DATABASE_TYPE || "couch";
})
allDbs(pouch) const Pouch = PouchDB.defaults({
return pouch prefix: COUCH_DB_URL,
} });
allDbs(Pouch);
module.exports = Pouch;

View File

@ -1,3 +1,3 @@
const client = require("./client") const client = require("./client")
module.exports = client() module.exports = client

View File

@ -1,5 +1,5 @@
module.exports = async db => { module.exports = async db => {
await db.put({ const doc = await db.put({
_id: "_design/client", _id: "_design/client",
views: { views: {
by_type: { by_type: {
@ -8,5 +8,6 @@ module.exports = async db => {
}`, }`,
}, },
}, },
}) });
console.log(doc);
} }

View File

@ -1,5 +1,9 @@
const CouchDB = require("../../db"); const CouchDB = require("../../db");
exports.getClientId = async function(ctx) {
ctx.body = process.env.CLIENT_ID;
};
exports.create = async function(ctx) { exports.create = async function(ctx) {
const clientId = `client-${ctx.request.body.clientId}`; const clientId = `client-${ctx.request.body.clientId}`;
const db = new CouchDB(clientId); const db = new CouchDB(clientId);

View File

@ -7,7 +7,9 @@ exports.save = async function(ctx) {
// validation with ajv // validation with ajv
const model = await db.get(record.modelId) const model = await db.get(record.modelId)
const validate = schemaValidator.compile(model.schema) const validate = schemaValidator.compile({
properties: model.schema
});
const valid = validate(record) const valid = validate(record)
if (!valid) { if (!valid) {
@ -49,7 +51,7 @@ exports.save = async function(ctx) {
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const db = new CouchDB(ctx.params.instanceId) const db = new CouchDB(ctx.params.instanceId)
const response = await db.query( const response = await db.query(
`database/${ctx.params.modelId}`, `database/${ctx.params.viewName}`,
{ {
include_docs: true include_docs: true
} }

View File

@ -5,6 +5,7 @@ const { resolve } = require("path")
const builderPath = resolve(process.cwd(), "builder") const builderPath = resolve(process.cwd(), "builder")
exports.serveBuilder = async function(ctx) { exports.serveBuilder = async function(ctx) {
console.log(ctx.file);
await send(ctx, ctx.file, { root: builderPath }) await send(ctx, ctx.file, { root: builderPath })
} }

View File

@ -1,10 +1,24 @@
const CouchDB = require("../../db"); const CouchDB = require("../../db");
const controller = { const controller = {
query: async ctx => {
},
fetch: async ctx => { fetch: async ctx => {
const db = new CouchDB(ctx.params.instanceId); const db = new CouchDB(ctx.params.instanceId);
const designDoc = await db.get("_design/database"); const designDoc = await db.get("_design/database");
ctx.body = designDoc.views; const response = [];
for (let name in designDoc.views) {
if (!name.startsWith("all") && name !== "by_type") {
response.push({
name,
...designDoc.views[name]
})
}
}
ctx.body = response
}, },
create: async ctx => { create: async ctx => {
const db = new CouchDB(ctx.params.instanceId); const db = new CouchDB(ctx.params.instanceId);
@ -18,7 +32,10 @@ const controller = {
const newView = await db.put(designDoc); const newView = await db.put(designDoc);
ctx.body = { ctx.body = {
...newView, view: {
...ctx.request.body,
...newView
},
message: `View ${name} created successfully.`, message: `View ${name} created successfully.`,
status: 200, status: 200,
} }

View File

@ -2,6 +2,7 @@ const Router = require("@koa/router")
const session = require("./session") const session = require("./session")
const StatusCodes = require("../utilities/statusCodes") const StatusCodes = require("../utilities/statusCodes")
const { resolve } = require("path") const { resolve } = require("path")
const { homedir } = require("os")
const send = require("koa-send") const send = require("koa-send")
const routeHandlers = require("./routeHandlers") const routeHandlers = require("./routeHandlers")
const { const {
@ -24,18 +25,17 @@ const modelsRoutes = require("./routes/neo/model");
const viewsRoutes = require("./routes/neo/view"); const viewsRoutes = require("./routes/neo/view");
const staticRoutes = require("./routes/neo/static"); const staticRoutes = require("./routes/neo/static");
const builderPath = resolve(__dirname, "../builder")
module.exports = app => { module.exports = app => {
const router = new Router() const router = new Router()
router router
.use(session(app)) // .use(session(app))
.use(async (ctx, next) => { .use(async (ctx, next) => {
// TODO: temp dev middleware // TODO: temp dev middleware
// ctx.sessionId = ctx.session._sessCtx.externalKey // ctx.sessionId = ctx.session._sessCtx.externalKey
// ctx.session.accessed = true // ctx.session.accessed = true
ctx.isAuthenticated = true; ctx.isAuthenticated = true;
ctx.config = { latestPackagesFolder: resolve(homedir(), ".budibase") }
await next(); await next();
}); });
// .use(async (ctx, next) => { // .use(async (ctx, next) => {

View File

@ -4,6 +4,7 @@ const controller = require("../../controllers/client");
const router = Router(); const router = Router();
router router
.get("/api/client/id", controller.getClientId)
.post("/api/clients", controller.create) .post("/api/clients", controller.create)
.delete("/api/clients/:clientId", controller.destroy); .delete("/api/clients/:clientId", controller.destroy);

View File

@ -4,7 +4,7 @@ const controller = require("../../controllers/record")
const router = Router() const router = Router()
router router
.get("/api/:instanceId/:modelId/records", controller.fetch) .get("/api/:instanceId/:viewName/records", controller.fetch)
.get("/api/:instanceId/records/:recordId", controller.find) .get("/api/:instanceId/records/:recordId", controller.find)
.post("/api/:instanceId/records", controller.save) .post("/api/:instanceId/records", controller.save)
.delete("/api/:instanceId/records/:recordId/:revId", controller.destroy) .delete("/api/:instanceId/records/:recordId/:revId", controller.destroy)

View File

@ -5,7 +5,7 @@ const router = Router();
router router
.param("file", async (file, ctx, next) => { .param("file", async (file, ctx, next) => {
ctx.file = file || "index.html"; ctx.file = file && file.includes(".") ? file : "index.html";
await next(); await next();
}) })
.get("/_builder/:file*", controller.serveBuilder) .get("/_builder/:file*", controller.serveBuilder)

View File

@ -12,6 +12,7 @@ const { join, dirname, resolve } = require("path")
const { $ } = require("@budibase/core").common const { $ } = require("@budibase/core").common
const { intersection, map, values, flatten } = require("lodash/fp") const { intersection, map, values, flatten } = require("lodash/fp")
const { merge } = require("lodash") const { merge } = require("lodash")
const { homedir } = require("os");
const { componentLibraryInfo } = require("./componentLibraryInfo") const { componentLibraryInfo } = require("./componentLibraryInfo")
const buildPage = require("./buildPage") const buildPage = require("./buildPage")
@ -28,7 +29,7 @@ const getAppDefinition = async appPath =>
await readJSON(`${appPath}/appDefinition.json`) await readJSON(`${appPath}/appDefinition.json`)
module.exports.getPackageForBuilder = async (config, application) => { module.exports.getPackageForBuilder = async (config, application) => {
const appPath = resolve(process.cwd(), config.latestPackagesFolder, application._id); const appPath = resolve(config.latestPackagesFolder, application._id);
const pages = await getPages(appPath) const pages = await getPages(appPath)
@ -42,6 +43,8 @@ module.exports.getPackageForBuilder = async (config, application) => {
components: await getComponentDefinitions(appPath, pages), components: await getComponentDefinitions(appPath, pages),
application, application,
clientId: process.env.CLIENT_ID
} }
} }

View File

@ -15,9 +15,9 @@ module.exports.LATEST_VERSIONID = LATEST_VERSIONID
module.exports.runtimePackagesDirectory = runtimePackagesDirectory module.exports.runtimePackagesDirectory = runtimePackagesDirectory
module.exports.getRuntimePackageDirectory = (appContext, appName, versionId) => module.exports.getRuntimePackageDirectory = (appId, versionId) =>
versionId === LATEST_VERSIONID versionId === LATEST_VERSIONID
? getLatestDirectory(appContext, appName) ? getLatestDirectory(appId)
: join(getRuntimeAppsDirectory(appName), versionId) : join(getRuntimeAppsDirectory(appName), versionId)
module.exports.getRuntimeAppsDirectory = getRuntimeAppsDirectory module.exports.getRuntimeAppsDirectory = getRuntimeAppsDirectory