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 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) : []
const onFieldsChanged = e => {
@ -42,7 +45,7 @@
{#if parameters.modelId}
<SaveFields
parameterFields={parameters.fields}
schemaFields={fieldNames}
{schemaFields}
on:fieldschanged={onFieldsChanged} />
{/if}

View File

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

View File

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

View File

@ -54,10 +54,13 @@ const apiOpts = {
}
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 record = params.fields
const record = makeRecordRequestBody(params)
record._id = params._id
await patch({
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 {
authenticate: authenticate(apiOpts),
triggerWorkflow: triggerWorkflow(apiOpts),

View File

@ -22,7 +22,6 @@ exports.patch = async function(ctx) {
if (!model.schema[key]) continue
record[key] = patchfields[key]
}
coerceFieldsToCorrectType(record, model)
const validateResult = await validate({
record,
@ -58,8 +57,6 @@ exports.save = async function(ctx) {
const model = await db.get(record.modelId)
coerceFieldsToCorrectType(record, model)
const validateResult = await validate({
record,
model,
@ -195,32 +192,6 @@ exports.validate = async function(ctx) {
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 }) {
if (!model) {
const db = new CouchDB(instanceId)