stop coercing values on the server - do on client instead

This commit is contained in:
Michael Shanks 2020-09-11 21:24:52 +01:00
parent 62db75eafb
commit f975237417
5 changed files with 56 additions and 51 deletions

View File

@ -19,10 +19,13 @@
const modelFields = modelId => { const modelFields = modelId => {
const model = $backendUiStore.models.find(m => m._id === modelId) const model = $backendUiStore.models.find(m => m._id === modelId)
return Object.keys(model.schema) return Object.keys(model.schema).map(k => ({
name: k,
type: model.schema[k].type,
}))
} }
$: fieldNames = $: schemaFields =
parameters && parameters.modelId ? modelFields(parameters.modelId) : [] parameters && parameters.modelId ? modelFields(parameters.modelId) : []
const onFieldsChanged = e => { const onFieldsChanged = e => {
@ -42,7 +45,7 @@
{#if parameters.modelId} {#if parameters.modelId}
<SaveFields <SaveFields
parameterFields={parameters.fields} parameterFields={parameters.fields}
schemaFields={fieldNames} {schemaFields}
on:fieldschanged={onFieldsChanged} /> on:fieldschanged={onFieldsChanged} />
{/if} {/if}

View File

@ -26,7 +26,7 @@
(parameterFields && (parameterFields &&
runtimeToReadableBinding( runtimeToReadableBinding(
bindableProperties, bindableProperties,
parameterFields[name] parameterFields[name].value
)) || )) ||
"", "",
})) }))
@ -56,10 +56,12 @@
const newParameterFields = {} const newParameterFields = {}
for (let field of fields) { for (let field of fields) {
if (field.name) { if (field.name) {
newParameterFields[field.name] = readableToRuntimeBinding( // value and type is needed by the client, so it can parse
bindableProperties, // a string into a correct type
field.value newParameterFields[field.name] = {
) type: schemaFields.find(f => f.name === field.name).type,
value: readableToRuntimeBinding(bindableProperties, field.value),
}
} }
} }
dispatch("fieldschanged", newParameterFields) dispatch("fieldschanged", newParameterFields)
@ -74,8 +76,8 @@
<Label size="m" color="dark">Field</Label> <Label size="m" color="dark">Field</Label>
<Select secondary bind:value={field.name} on:blur={rebuildParameters}> <Select secondary bind:value={field.name} on:blur={rebuildParameters}>
<option value="" /> <option value="" />
{#each schemaFields as fieldName} {#each schemaFields as schemaField}
<option value={fieldName}>{fieldName}</option> <option value={schemaField.name}>{schemaField.name}</option>
{/each} {/each}
</Select> </Select>
<Label size="m" color="dark">Value</Label> <Label size="m" color="dark">Value</Label>

View File

@ -39,15 +39,12 @@
$: parameters._id = `{{ ${recordId} }}` $: parameters._id = `{{ ${recordId} }}`
// just wraps binding in {{ ... }} // just wraps binding in {{ ... }}
const toBindingExpression = bindingPath => { const toBindingExpression = bindingPath => `{{ ${bindingPath} }}`
console.log("yeo")
return `{{ ${bindingPath} }}`
}
// finds the selected idBinding, then reads the table/view // finds the selected idBinding, then reads the table/view
// from the component instance that it belongs to. // from the component instance that it belongs to.
// then returns the field names for that schema // then returns the field names for that schema
const modelInfoFromIdBinding = recordId => { const schemaFromIdBinding = recordId => {
if (!recordId) return [] if (!recordId) return []
const idBinding = bindableProperties.find( const idBinding = bindableProperties.find(
@ -66,15 +63,18 @@
const model = $backendUiStore.models.find(m => m._id === modelInfo.modelId) const model = $backendUiStore.models.find(m => m._id === modelInfo.modelId)
parameters.modelId = modelInfo.modelId parameters.modelId = modelInfo.modelId
return Object.keys(model.schema) return Object.keys(model.schema).map(k => ({
name: k,
type: model.schema[k].type,
}))
} }
let fieldNames let schemaFields
$: { $: {
if (parameters && recordId) { if (parameters && recordId) {
fieldNames = modelInfoFromIdBinding(recordId) schemaFields = schemaFromIdBinding(recordId)
} else { } else {
fieldNames = [] schemaFields = []
} }
} }
@ -104,7 +104,7 @@
{#if recordId} {#if recordId}
<SaveFields <SaveFields
parameterFields={parameters.fields} parameterFields={parameters.fields}
schemaFields={fieldNames} {schemaFields}
on:fieldschanged={onFieldsChanged} /> on:fieldschanged={onFieldsChanged} />
{/if} {/if}

View File

@ -54,10 +54,13 @@ const apiOpts = {
} }
const createRecord = async params => const createRecord = async params =>
await post({ url: `/api/${params.modelId}/records`, body: params.fields }) await post({
url: `/api/${params.modelId}/records`,
body: makeRecordRequestBody(params),
})
const updateRecord = async params => { const updateRecord = async params => {
const record = params.fields const record = makeRecordRequestBody(params)
record._id = params._id record._id = params._id
await patch({ await patch({
url: `/api/${params.modelId}/records/${params._id}`, url: `/api/${params.modelId}/records/${params._id}`,
@ -65,6 +68,32 @@ const updateRecord = async params => {
}) })
} }
const makeRecordRequestBody = parameters => {
const body = {}
for (let fieldName in parameters.fields) {
const field = parameters.fields[fieldName]
// ensure fields sent are of the correct type
if (field.type === "boolean") {
if (field.value === "true") body[fieldName] = true
if (field.value === "false") body[fieldName] = false
} else if (field.type === "number") {
const val = parseFloat(field.value)
if (!isNaN(val)) {
body[fieldName] = val
}
} else if (field.type === "datetime") {
const date = new Date(field.value)
if (!isNaN(date.getTime())) {
body[fieldName] = date.toISOString()
}
} else {
body[fieldName] = field.value
}
}
return body
}
export default { export default {
authenticate: authenticate(apiOpts), authenticate: authenticate(apiOpts),
triggerWorkflow: triggerWorkflow(apiOpts), triggerWorkflow: triggerWorkflow(apiOpts),

View File

@ -22,7 +22,6 @@ exports.patch = async function(ctx) {
if (!model.schema[key]) continue if (!model.schema[key]) continue
record[key] = patchfields[key] record[key] = patchfields[key]
} }
coerceFieldsToCorrectType(record, model)
const validateResult = await validate({ const validateResult = await validate({
record, record,
@ -58,8 +57,6 @@ exports.save = async function(ctx) {
const model = await db.get(record.modelId) const model = await db.get(record.modelId)
coerceFieldsToCorrectType(record, model)
const validateResult = await validate({ const validateResult = await validate({
record, record,
model, model,
@ -195,32 +192,6 @@ exports.validate = async function(ctx) {
ctx.body = errors ctx.body = errors
} }
// this function modifies an incoming record, to allow for things like
// "boolField": "true" (instead of mandating "boolField": true)
// this allows us to use mustash templating to send non-string fields in a request
const coerceFieldsToCorrectType = (record, model) => {
for (let fieldName in record) {
const fieldValue = record[fieldName]
if (model.schema[fieldName]) {
if (
model.schema[fieldName].type === "boolean" &&
typeof fieldValue !== "boolean"
) {
if (fieldValue === "true") record[fieldName] = true
if (fieldValue === "false") record[fieldName] = false
continue
}
if (model.schema[fieldName].type === "number") {
const val = parseFloat(fieldValue)
if (!isNaN(val)) {
record[fieldName] = val
}
}
}
}
}
async function validate({ instanceId, modelId, record, model }) { async function validate({ instanceId, modelId, record, model }) {
if (!model) { if (!model) {
const db = new CouchDB(instanceId) const db = new CouchDB(instanceId)