Merge branch 'new-backend' of github.com:Budibase/budibase into new-backend
This commit is contained in:
commit
19c0bbc865
|
@ -13,6 +13,8 @@ import {
|
||||||
constructHierarchy,
|
constructHierarchy,
|
||||||
templateApi,
|
templateApi,
|
||||||
isIndex,
|
isIndex,
|
||||||
|
canDeleteIndex,
|
||||||
|
canDeleteRecord
|
||||||
} from "../../common/core"
|
} from "../../common/core"
|
||||||
|
|
||||||
export const getBackendUiStore = () => {
|
export const getBackendUiStore = () => {
|
||||||
|
@ -183,7 +185,7 @@ export const saveCurrentNode = store => () => {
|
||||||
const defaultIndex = templateApi(state.hierarchy).getNewIndexTemplate(
|
const defaultIndex = templateApi(state.hierarchy).getNewIndexTemplate(
|
||||||
cloned.parent()
|
cloned.parent()
|
||||||
)
|
)
|
||||||
defaultIndex.name = `all_${cloned.collectionName}`
|
defaultIndex.name = `all_${cloned.name}s`
|
||||||
defaultIndex.allowedRecordNodeIds = [cloned.nodeId]
|
defaultIndex.allowedRecordNodeIds = [cloned.nodeId]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,14 +204,27 @@ export const deleteCurrentNode = store => () => {
|
||||||
? state.hierarchy.children.find(node => node !== state.currentNode)
|
? state.hierarchy.children.find(node => node !== state.currentNode)
|
||||||
: nodeToDelete.parent()
|
: nodeToDelete.parent()
|
||||||
|
|
||||||
const recordOrIndexKey = hierarchyFunctions.isRecord(nodeToDelete) ? "children" : "indexes";
|
const isRecord = hierarchyFunctions.isRecord(nodeToDelete)
|
||||||
|
|
||||||
|
const check = isRecord
|
||||||
|
? canDeleteRecord(nodeToDelete)
|
||||||
|
: canDeleteIndex(nodeToDelete)
|
||||||
|
|
||||||
|
if (!check.canDelete) {
|
||||||
|
state.errors = check.errors.map(e => ({ error: e }))
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
const recordOrIndexKey = isRecord ? "children" : "indexes"
|
||||||
|
|
||||||
// remove the selected record or index
|
// remove the selected record or index
|
||||||
nodeToDelete.parent()[recordOrIndexKey] = remove(
|
const newCollection = remove(
|
||||||
nodeToDelete.parent()[recordOrIndexKey],
|
node => node.nodeId === nodeToDelete.nodeId,
|
||||||
node => node.nodeId === nodeToDelete.nodeId
|
nodeToDelete.parent()[recordOrIndexKey]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
nodeToDelete.parent()[recordOrIndexKey] = newCollection
|
||||||
|
|
||||||
state.errors = []
|
state.errors = []
|
||||||
saveBackend(state)
|
saveBackend(state)
|
||||||
return state
|
return state
|
||||||
|
|
|
@ -5,25 +5,14 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if hasErrors}
|
{#if hasErrors}
|
||||||
<div class="error-container">
|
<div uk-alert class="uk-alert-danger">
|
||||||
{#each errors as error}
|
{#each errors as error}
|
||||||
<div class="error-row">
|
<div>
|
||||||
{error.field ? `${error.field}: ` : ''}{error.error}
|
{error.field ? `${error.field}: ` : ''}{error.error}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
|
||||||
.error-container {
|
|
||||||
padding: 10px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: var(--deletion100);
|
|
||||||
border-radius: var(--borderradiusall);
|
|
||||||
background: var(--deletion75);
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-row {
|
|
||||||
padding: 5px 0px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { find, filter, keyBy, flatten, map } from "lodash/fp"
|
||||||
import { generateSchema } from "../../../core/src/indexing/indexSchemaCreator"
|
import { generateSchema } from "../../../core/src/indexing/indexSchemaCreator"
|
||||||
import { generate } from "shortid"
|
import { generate } from "shortid"
|
||||||
|
|
||||||
|
export { canDeleteIndex } from "../../../core/src/templateApi/canDeleteIndex"
|
||||||
|
export { canDeleteRecord } from "../../../core/src/templateApi/canDeleteRecord"
|
||||||
export { userWithFullAccess } from "../../../core/src/index"
|
export { userWithFullAccess } from "../../../core/src/index"
|
||||||
|
|
||||||
export const pipe = common.$
|
export const pipe = common.$
|
||||||
|
|
|
@ -31,9 +31,6 @@
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $store.errors && $store.errors.length > 0}
|
|
||||||
<ErrorsBox errors={$store.errors} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import { store } from "../builderStore"
|
import { store } 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 { hierarchy as hierarchyFunctions, common } from "../../../core/src"
|
||||||
|
import ErrorsBox from "../common/ErrorsBox.svelte"
|
||||||
|
|
||||||
const SNIPPET_EDITORS = {
|
const SNIPPET_EDITORS = {
|
||||||
MAP: "Map",
|
MAP: "Map",
|
||||||
|
@ -49,6 +50,9 @@
|
||||||
</heading>
|
</heading>
|
||||||
<form class="uk-form-stacked root">
|
<form class="uk-form-stacked root">
|
||||||
<h4 class="budibase__label--big">Settings</h4>
|
<h4 class="budibase__label--big">Settings</h4>
|
||||||
|
{#if $store.errors && $store.errors.length > 0}
|
||||||
|
<ErrorsBox errors={$store.errors} />
|
||||||
|
{/if}
|
||||||
<div class="uk-grid-small" uk-grid>
|
<div class="uk-grid-small" uk-grid>
|
||||||
<div class="uk-width-1-2@s">
|
<div class="uk-width-1-2@s">
|
||||||
<Textbox bind:text={index.name} label="Name" />
|
<Textbox bind:text={index.name} label="Name" />
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
const response = await api.createUser(
|
const response = await api.createUser(
|
||||||
password,
|
password,
|
||||||
{
|
{
|
||||||
username,
|
name:username,
|
||||||
accessLevels,
|
accessLevels,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
temporaryAccessId: ""
|
temporaryAccessId: ""
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
import { common, hierarchy } from "../../../core/src"
|
import { common, hierarchy } from "../../../core/src"
|
||||||
import { templateApi, pipe, validate } from "../common/core"
|
import { templateApi, pipe, validate } from "../common/core"
|
||||||
import ActionsHeader from "./ActionsHeader.svelte"
|
import ActionsHeader from "./ActionsHeader.svelte"
|
||||||
|
import ErrorsBox from "../common/ErrorsBox.svelte"
|
||||||
|
|
||||||
let record
|
let record
|
||||||
let getIndexAllowedRecords
|
let getIndexAllowedRecords
|
||||||
|
@ -99,14 +100,15 @@
|
||||||
</heading>
|
</heading>
|
||||||
{#if !editingField}
|
{#if !editingField}
|
||||||
<h4 class="budibase__label--big">Settings</h4>
|
<h4 class="budibase__label--big">Settings</h4>
|
||||||
|
|
||||||
|
{#if $store.errors && $store.errors.length > 0}
|
||||||
|
<ErrorsBox errors={$store.errors} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
<form class="uk-form-stacked">
|
<form class="uk-form-stacked">
|
||||||
|
|
||||||
<Textbox label="Name" bind:text={record.name} on:change={nameChanged} />
|
|
||||||
|
|
||||||
<div class="horizontal-stack">
|
<div class="horizontal-stack">
|
||||||
{#if !record.isSingle}
|
<Textbox label="Name" bind:text={record.name} on:change={nameChanged} />
|
||||||
<Textbox label="Collection Name" bind:text={record.collectionName} />
|
|
||||||
{/if}
|
|
||||||
<div>
|
<div>
|
||||||
<label class="uk-form-label">Parent</label>
|
<label class="uk-form-label">Parent</label>
|
||||||
<div class="uk-form-controls">
|
<div class="uk-form-controls">
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
outline: none;
|
outline: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
background: rgba(0,0,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
outline: none;
|
outline: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
background: rgba(0,0,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--secondary60);
|
color: var(--secondary60);
|
||||||
|
background: rgba(0,0,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.switcher > .selected {
|
.switcher > .selected {
|
||||||
|
|
|
@ -70,6 +70,7 @@
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
outline: none;
|
outline: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
background: rgba(0,0,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
reduce,
|
reduce,
|
||||||
find,
|
find,
|
||||||
} from "lodash/fp"
|
} from "lodash/fp"
|
||||||
import { compileExpression, compileCode } from "../common/compileCode"
|
import { compileCode } from "../common/compileCode"
|
||||||
import { $ } from "../common"
|
import { $ } from "../common"
|
||||||
import { _executeAction } from "./execute"
|
import { _executeAction } from "./execute"
|
||||||
import { BadRequestError, NotFoundError } from "../common/errors"
|
import { BadRequestError, NotFoundError } from "../common/errors"
|
||||||
|
@ -49,7 +49,7 @@ const subscribeTriggers = (
|
||||||
|
|
||||||
const shouldRunTrigger = (trigger, eventContext) => {
|
const shouldRunTrigger = (trigger, eventContext) => {
|
||||||
if (!trigger.condition) return true
|
if (!trigger.condition) return true
|
||||||
const shouldRun = compileExpression(trigger.condition)
|
const shouldRun = compileCode(trigger.condition)
|
||||||
return shouldRun({ context: eventContext })
|
return shouldRun({ context: eventContext })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,25 @@
|
||||||
import {
|
import {
|
||||||
compileExpression as cExp,
|
|
||||||
compileCode as cCode,
|
compileCode as cCode,
|
||||||
} from "@nx-js/compiler-util"
|
} from "@nx-js/compiler-util"
|
||||||
|
import { includes } from "lodash/fp"
|
||||||
|
|
||||||
|
|
||||||
export const compileCode = code => {
|
export const compileCode = code => {
|
||||||
let func
|
let func
|
||||||
|
let safeCode
|
||||||
|
|
||||||
|
if (includes("return ")(code)) {
|
||||||
|
safeCode = code
|
||||||
|
} else {
|
||||||
|
let trimmed = code.trim()
|
||||||
|
trimmed = trimmed.endsWith(";")
|
||||||
|
? trimmed.substring(0, trimmed.length - 1)
|
||||||
|
: trimmed
|
||||||
|
safeCode = `return (${trimmed})`
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
func = cCode(code)
|
func = cCode(safeCode)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
e.message = `Error compiling code : ${code} : ${e.message}`
|
e.message = `Error compiling code : ${code} : ${e.message}`
|
||||||
throw e
|
throw e
|
||||||
|
@ -15,16 +27,3 @@ export const compileCode = code => {
|
||||||
|
|
||||||
return func
|
return func
|
||||||
}
|
}
|
||||||
|
|
||||||
export const compileExpression = code => {
|
|
||||||
let func
|
|
||||||
|
|
||||||
try {
|
|
||||||
func = cExp(code)
|
|
||||||
} catch (e) {
|
|
||||||
e.message = `Error compiling expression : ${code} : ${e.message}`
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
|
|
||||||
return func
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { has, isNumber, isUndefined } from "lodash/fp"
|
import { has, isNumber, isUndefined } from "lodash/fp"
|
||||||
import { compileExpression, compileCode } from "@nx-js/compiler-util"
|
import { compileCode } from "../common/compileCode"
|
||||||
import { safeKey, apiWrapper, events, isNonEmptyString } from "../common"
|
import { safeKey, apiWrapper, events, isNonEmptyString } from "../common"
|
||||||
import { iterateIndex } from "../indexing/read"
|
import { iterateIndex } from "../indexing/read"
|
||||||
import {
|
import {
|
||||||
|
@ -147,7 +147,7 @@ const applyItemToAggregateResult = (indexNode, result, item) => {
|
||||||
const thisGroupResult = result[aggGroup.name]
|
const thisGroupResult = result[aggGroup.name]
|
||||||
|
|
||||||
if (isNonEmptyString(aggGroup.condition)) {
|
if (isNonEmptyString(aggGroup.condition)) {
|
||||||
if (!compileExpression(aggGroup.condition)({ record: item })) {
|
if (!compileCode(aggGroup.condition)({ record: item })) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { compileExpression, compileCode } from "@nx-js/compiler-util"
|
import { compileCode } from "../common/compileCode"
|
||||||
import { isUndefined, keys, cloneDeep, isFunction } from "lodash/fp"
|
import { isUndefined, keys, cloneDeep, isFunction, includes } from "lodash/fp"
|
||||||
import { defineError } from "../common"
|
import { defineError } from "../common"
|
||||||
|
|
||||||
export const filterEval = "FILTER_EVALUATE"
|
export const filterEval = "FILTER_EVALUATE"
|
||||||
|
@ -16,7 +16,7 @@ const getEvaluateResult = () => ({
|
||||||
result: null,
|
result: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const compileFilter = index => compileExpression(index.filter)
|
export const compileFilter = index => compileCode(index.filter)
|
||||||
|
|
||||||
export const compileMap = index => compileCode(index.map)
|
export const compileMap = index => compileCode(index.map)
|
||||||
|
|
||||||
|
@ -46,6 +46,9 @@ export const mapRecord = (record, index) => {
|
||||||
if (isFunction(mapped[key])) {
|
if (isFunction(mapped[key])) {
|
||||||
delete mapped[key]
|
delete mapped[key]
|
||||||
}
|
}
|
||||||
|
if (key === "IsNew") {
|
||||||
|
delete mapped.IsNew
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped.key = record.key
|
mapped.key = record.key
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { compileCode } from "@nx-js/compiler-util"
|
import { compileCode } from "../common/compileCode"
|
||||||
import { filter, includes, map, last } from "lodash/fp"
|
import { filter, includes, map, last } from "lodash/fp"
|
||||||
import {
|
import {
|
||||||
getActualKeyOfParent,
|
getActualKeyOfParent,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { map, reduce, filter, isEmpty, flatten, each } from "lodash/fp"
|
import { map, reduce, filter, isEmpty, flatten, each } from "lodash/fp"
|
||||||
import { compileExpression } from "@nx-js/compiler-util"
|
import { compileCode } from "../common/compileCode"
|
||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
import { getExactNodeForKey } from "../templateApi/hierarchy"
|
import { getExactNodeForKey } from "../templateApi/hierarchy"
|
||||||
import { validateFieldParse, validateTypeConstraints } from "../types"
|
import { validateFieldParse, validateTypeConstraints } from "../types"
|
||||||
|
@ -35,7 +35,7 @@ const validateAllTypeConstraints = async (record, recordNode, context) => {
|
||||||
|
|
||||||
const runRecordValidationRules = (record, recordNode) => {
|
const runRecordValidationRules = (record, recordNode) => {
|
||||||
const runValidationRule = rule => {
|
const runValidationRule = rule => {
|
||||||
const isValid = compileExpression(rule.expressionWhenValid)
|
const isValid = compileCode(rule.expressionWhenValid)
|
||||||
const expressionContext = { record, _ }
|
const expressionContext = { record, _ }
|
||||||
return isValid(expressionContext)
|
return isValid(expressionContext)
|
||||||
? { valid: true }
|
? { valid: true }
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const canDeleteIndex = indexNode => {
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
},[]),
|
},[]),
|
||||||
map(f => `field ${f.name} on record ${f.record.name} uses this index as a reference`)
|
map(f => `field "${f.name}" on record "${f.record.name}" uses this index as a reference`)
|
||||||
])
|
])
|
||||||
|
|
||||||
const lookupIndexes = $(flatHierarchy,[
|
const lookupIndexes = $(flatHierarchy,[
|
||||||
|
@ -37,7 +37,7 @@ export const canDeleteIndex = indexNode => {
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
},[]),
|
},[]),
|
||||||
map(f => `field ${f.name} on record ${f.record.name} uses this index as a lookup`)
|
map(f => `field "${f.name}" on record "${f.record.name}" uses this index as a lookup`)
|
||||||
])
|
])
|
||||||
|
|
||||||
const errors = [
|
const errors = [
|
||||||
|
|
|
@ -22,13 +22,12 @@ export const canDeleteRecord = recordNode => {
|
||||||
const belongsToAncestor = i =>
|
const belongsToAncestor = i =>
|
||||||
ancestors.includes(i.parent())
|
ancestors.includes(i.parent())
|
||||||
|
|
||||||
|
|
||||||
const errorsForNode = node => {
|
const errorsForNode = node => {
|
||||||
const errorsThisNode = $(flatHierarchy, [
|
const errorsThisNode = $(flatHierarchy, [
|
||||||
filter(i => isAncestorIndex(i)
|
filter(i => isAncestorIndex(i)
|
||||||
&& belongsToAncestor(i)
|
&& belongsToAncestor(i)
|
||||||
&& includes(node.nodeId)(i.allowedRecordNodeIds)),
|
&& includes(node.nodeId)(i.allowedRecordNodeIds)),
|
||||||
map(i => `index ${i.name} indexes this record. Please remove the record from allowedRecordIds, or delete the index`)
|
map(i => `index "${i.name}" indexes this record. Please remove the record from the index, or delete the index`)
|
||||||
])
|
])
|
||||||
|
|
||||||
for (let child of node.children) {
|
for (let child of node.children) {
|
||||||
|
@ -40,5 +39,7 @@ export const canDeleteRecord = recordNode => {
|
||||||
return errorsThisNode
|
return errorsThisNode
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorsForNode(recordNode)
|
const errors = errorsForNode(recordNode)
|
||||||
|
|
||||||
|
return { errors, canDelete: errors.length === 0 }
|
||||||
}
|
}
|
|
@ -160,16 +160,17 @@ export const getNewRootLevel = () =>
|
||||||
})
|
})
|
||||||
|
|
||||||
const _getNewRecordTemplate = (parent, name, createDefaultIndex, isSingle) => {
|
const _getNewRecordTemplate = (parent, name, createDefaultIndex, isSingle) => {
|
||||||
|
const nodeId = getNodeId(parent)
|
||||||
const node = constructNode(parent, {
|
const node = constructNode(parent, {
|
||||||
name,
|
name,
|
||||||
type: "record",
|
type: "record",
|
||||||
fields: [],
|
fields: [],
|
||||||
children: [],
|
children: [],
|
||||||
validationRules: [],
|
validationRules: [],
|
||||||
nodeId: getNodeId(parent),
|
nodeId: nodeId,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
estimatedRecordCount: isRecord(parent) ? 500 : 1000000,
|
estimatedRecordCount: isRecord(parent) ? 500 : 1000000,
|
||||||
collectionName: "",
|
collectionName: (nodeId || "").toString(),
|
||||||
isSingle,
|
isSingle,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
isEmpty,
|
isEmpty,
|
||||||
has,
|
has,
|
||||||
} from "lodash/fp"
|
} from "lodash/fp"
|
||||||
import { compileExpression, compileCode } from "@nx-js/compiler-util"
|
import { compileCode } from "../common/compileCode"
|
||||||
import {
|
import {
|
||||||
$,
|
$,
|
||||||
isSomething,
|
isSomething,
|
||||||
|
@ -73,7 +73,7 @@ const aggregateGroupRules = [
|
||||||
"condition does not compile",
|
"condition does not compile",
|
||||||
a =>
|
a =>
|
||||||
isEmpty(a.condition) ||
|
isEmpty(a.condition) ||
|
||||||
executesWithoutException(() => compileExpression(a.condition))
|
executesWithoutException(() => compileCode(a.condition))
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ const triggerRules = actions => [
|
||||||
t => {
|
t => {
|
||||||
if (!t.condition) return true
|
if (!t.condition) return true
|
||||||
try {
|
try {
|
||||||
compileExpression(t.condition)
|
compileCode(t.condition)
|
||||||
return true
|
return true
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { flatten, map, isEmpty } from "lodash/fp"
|
import { flatten, map, isEmpty } from "lodash/fp"
|
||||||
import { compileCode } from "@nx-js/compiler-util"
|
import { compileCode } from "../common/compileCode"
|
||||||
import { isNonEmptyString, executesWithoutException, $ } from "../common"
|
import { isNonEmptyString, executesWithoutException, $ } from "../common"
|
||||||
import { applyRuleSet, makerule } from "../common/validationCommon"
|
import { applyRuleSet, makerule } from "../common/validationCommon"
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {
|
||||||
setupApphierarchy,
|
setupApphierarchy,
|
||||||
basicAppHierarchyCreator_WithFields,
|
basicAppHierarchyCreator_WithFields,
|
||||||
stubEventHandler,
|
stubEventHandler,
|
||||||
|
basicAppHierarchyCreator_WithFields_AndIndexes,
|
||||||
} from "./specHelpers"
|
} from "./specHelpers"
|
||||||
import { canDeleteIndex } from "../src/templateApi/canDeleteIndex"
|
import { canDeleteIndex } from "../src/templateApi/canDeleteIndex"
|
||||||
import { canDeleteRecord } from "../src/templateApi/canDeleteRecord"
|
import { canDeleteRecord } from "../src/templateApi/canDeleteRecord"
|
||||||
|
@ -49,15 +50,37 @@ describe("canDeleteIndex", () => {
|
||||||
|
|
||||||
|
|
||||||
describe("canDeleteRecord", () => {
|
describe("canDeleteRecord", () => {
|
||||||
it("should return no errors when deletion is valid", () => {
|
it("should return no errors when deletion is valid", async () => {
|
||||||
const { appHierarchy } = await setupApphierarchy(
|
const { appHierarchy } = await setupApphierarchy(
|
||||||
basicAppHierarchyCreator_WithFields
|
basicAppHierarchyCreator_WithFields
|
||||||
)
|
)
|
||||||
|
|
||||||
appHierarchy.root.
|
appHierarchy.root.indexes = appHierarchy.root.indexes.filter(i => !i.allowedRecordNodeIds.includes(appHierarchy.customerRecord.nodeId))
|
||||||
const result = canDeleteIndex(appHierarchy.customerRecord)
|
const result = canDeleteRecord(appHierarchy.customerRecord)
|
||||||
|
|
||||||
expect(result.canDelete).toBe(true)
|
expect(result.canDelete).toBe(true)
|
||||||
expect(result.errors).toEqual([])
|
expect(result.errors).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should return errors when record is referenced by hierarchal index", async () => {
|
||||||
|
const { appHierarchy } = await setupApphierarchy(
|
||||||
|
basicAppHierarchyCreator_WithFields
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = canDeleteRecord(appHierarchy.customerRecord)
|
||||||
|
|
||||||
|
expect(result.canDelete).toBe(false)
|
||||||
|
expect(result.errors.some(e => e.includes("customer_index"))).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return errors when record has a child which cannot be deleted", async () => {
|
||||||
|
const { appHierarchy } = await setupApphierarchy(
|
||||||
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = canDeleteRecord(appHierarchy.customerRecord)
|
||||||
|
|
||||||
|
expect(result.canDelete).toBe(false)
|
||||||
|
expect(result.errors.some(e => e.includes("Outstanding Invoices"))).toBe(true)
|
||||||
|
})
|
||||||
})
|
})
|
|
@ -25,7 +25,7 @@ describe("hierarchy node creation", () => {
|
||||||
expect(record.validationRules).toEqual([])
|
expect(record.validationRules).toEqual([])
|
||||||
expect(record.indexes).toEqual([])
|
expect(record.indexes).toEqual([])
|
||||||
expect(record.parent()).toBe(root)
|
expect(record.parent()).toBe(root)
|
||||||
expect(record.collectionName).toBe("")
|
expect(record.collectionName).toBe(record.nodeId.toString())
|
||||||
expect(record.estimatedRecordCount).toBe(1000000)
|
expect(record.estimatedRecordCount).toBe(1000000)
|
||||||
expect(record.isSingle).toBe(false)
|
expect(record.isSingle).toBe(false)
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,7 @@ module.exports = (config, app) => {
|
||||||
ctx.request.body.appDefinition,
|
ctx.request.body.appDefinition,
|
||||||
ctx.request.body.accessLevels
|
ctx.request.body.accessLevels
|
||||||
)
|
)
|
||||||
|
ctx.master.deleteLatestPackageFromCache(ctx.params.appname)
|
||||||
ctx.response.status = StatusCodes.OK
|
ctx.response.status = StatusCodes.OK
|
||||||
})
|
})
|
||||||
.post("/_builder/api/:appname/pages/:pageName", async ctx => {
|
.post("/_builder/api/:appname/pages/:pageName", async ctx => {
|
||||||
|
|
|
@ -5,12 +5,16 @@ const { getRuntimePackageDirectory } = require("../utilities/runtimePackages")
|
||||||
const injectPlugins = require("./injectedPlugins")
|
const injectPlugins = require("./injectedPlugins")
|
||||||
const { cwd } = require("process")
|
const { cwd } = require("process")
|
||||||
|
|
||||||
|
const appDefinitionPath = appPath => join(appPath, "appDefinition.json")
|
||||||
|
const pluginsPath = appPath => join(appPath, "plugins.js")
|
||||||
|
const accessLevelsPath = appPath => join(appPath, "access_levels.json")
|
||||||
|
|
||||||
const createAppPackage = (context, appPath) => {
|
const createAppPackage = (context, appPath) => {
|
||||||
const appDefModule = require(join(appPath, "appDefinition.json"))
|
const appDefModule = require(appDefinitionPath(appPath))
|
||||||
|
|
||||||
const pluginsModule = require(join(appPath, "plugins.js"))
|
const pluginsModule = require(pluginsPath(appPath))
|
||||||
|
|
||||||
const accessLevels = require(join(appPath, "access_levels.json"))
|
const accessLevels = require(accessLevelsPath(appPath))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
appDefinition: appDefModule,
|
appDefinition: appDefModule,
|
||||||
|
@ -87,3 +91,11 @@ module.exports.applictionVersionPackage = async (
|
||||||
await injectPlugins(pkg, context.master, appname, instanceKey)
|
await injectPlugins(pkg, context.master, appname, instanceKey)
|
||||||
return pkg
|
return pkg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.deleteCachedPackage = (context, appname, versionId) => {
|
||||||
|
const appPath = applictionVersionPath(context, appname, versionId)
|
||||||
|
|
||||||
|
delete require.cache[resolve(appDefinitionPath(appPath))]
|
||||||
|
delete require.cache[resolve(pluginsPath(appPath))]
|
||||||
|
delete require.cache[resolve(accessLevelsPath(appPath))]
|
||||||
|
}
|
||||||
|
|
|
@ -11,8 +11,9 @@ const {
|
||||||
masterAppPackage,
|
masterAppPackage,
|
||||||
applictionVersionPackage,
|
applictionVersionPackage,
|
||||||
applictionVersionPublicPaths,
|
applictionVersionPublicPaths,
|
||||||
|
deleteCachedPackage,
|
||||||
} = require("../utilities/createAppPackage")
|
} = require("../utilities/createAppPackage")
|
||||||
const { determineVersionId } = require("./runtimePackages")
|
const { determineVersionId, LATEST_VERSIONID } = require("./runtimePackages")
|
||||||
|
|
||||||
const isMaster = appname => appname === "_master"
|
const isMaster = appname => appname === "_master"
|
||||||
|
|
||||||
|
@ -345,6 +346,10 @@ module.exports = async context => {
|
||||||
await bb.recordApi.save(userInMaster)
|
await bb.recordApi.save(userInMaster)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteLatestPackageFromCache = (appname) => {
|
||||||
|
deleteCachedPackage(context, appname, LATEST_VERSIONID)
|
||||||
|
}
|
||||||
|
|
||||||
const listApplications = () => values(applications)
|
const listApplications = () => values(applications)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -364,5 +369,6 @@ module.exports = async context => {
|
||||||
getFullAccessApiForInstanceId,
|
getFullAccessApiForInstanceId,
|
||||||
getFullAccessApiForMaster,
|
getFullAccessApiForMaster,
|
||||||
getApplicationWithInstances,
|
getApplicationWithInstances,
|
||||||
|
deleteLatestPackageFromCache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue