Browse Source

fix delete functionality

pull/4023/head
kevmodrome 6 years ago
parent
commit
da32029c31
  1. 534
      packages/server/src/api/controllers/record.js

534
packages/server/src/api/controllers/record.js

@ -63,308 +63,308 @@ exports.patch = async function (ctx) {
} }
exports.save = async function (ctx) { exports.save = async function (ctx) {
if (ctx.request.body.type === 'delete') {
await bulkDelete(ctx)
} else {
await saveRecords(ctx)
}
}
exports.fetchView = async function (ctx) {
const instanceId = ctx.user.instanceId const instanceId = ctx.user.instanceId
const db = new CouchDB(instanceId) const db = new CouchDB(instanceId)
let record = ctx.request.body const { stats, group, field } = ctx.query
record.modelId = ctx.params.modelId const viewName = ctx.params.viewName
if (!record._rev && !record._id) { // if this is a model view being looked for just transfer to that
record._id = generateRecordID(record.modelId) if (viewName.indexOf(MODEL_VIEW_BEGINS_WITH) === 0) {
ctx.params.modelId = viewName.substring(4)
await exports.fetchModelRecords(ctx)
return
} }
exports.save = async function (ctx) {
const db = new CouchDB(ctx.user.instanceId)
let record = ctx.request.body
record.modelId = ctx.params.modelId
if (!record._rev && !record._id) {
record._id = generateRecordID(record.modelId)
}
const model = await db.get(record.modelId) const response = await db.query(`database/${viewName}`, {
include_docs: !stats,
record = coerceRecordValues(record, model) group,
})
const validateResult = await validate({
record,
model,
})
if (!validateResult.valid) { if (stats) {
ctx.status = 400 response.rows = response.rows.map(row => ({
ctx.body = { group: row.key,
status: 400, field,
errors: validateResult.errors, ...row.value,
} avg: row.value.sum / row.value.count,
return }))
} } else {
response.rows = response.rows.map(row => row.doc)
}
const existingRecord = record._rev && (await db.get(record._id)) ctx.body = await linkRecords.attachLinkInfo(instanceId, response.rows)
}
// make sure link records are up to date exports.fetchModelRecords = async function (ctx) {
record = await linkRecords.updateLinks({ const instanceId = ctx.user.instanceId
instanceId, const db = new CouchDB(instanceId)
eventType: linkRecords.EventType.RECORD_SAVE, const response = await db.allDocs(
record, getRecordParams(ctx.params.modelId, null, {
modelId: record.modelId, include_docs: true,
model,
}) })
)
ctx.body = response.rows.map(row => row.doc)
ctx.body = await linkRecords.attachLinkInfo(
instanceId,
response.rows.map(row => row.doc)
)
}
if (existingRecord) { exports.search = async function (ctx) {
const response = await db.put(record) const instanceId = ctx.user.instanceId
record._rev = response.rev const db = new CouchDB(instanceId)
record.type = "record" const response = await db.allDocs({
ctx.body = record include_docs: true,
ctx.status = 200 ...ctx.request.body,
ctx.message = `${model.name} updated successfully.` })
return ctx.body = await linkRecords.attachLinkInfo(
} instanceId,
response.rows.map(row => row.doc)
record.type = "record" )
const response = await db.post(record) }
record._rev = response.rev
ctx.eventEmitter && exports.find = async function (ctx) {
ctx.eventEmitter.emitRecord(`record:save`, instanceId, record, model) const instanceId = ctx.user.instanceId
ctx.body = record const db = new CouchDB(instanceId)
ctx.status = 200 const record = await db.get(ctx.params.recordId)
ctx.message = `${model.name} created successfully` if (record.modelId !== ctx.params.modelId) {
ctx.throw(400, "Supplied modelId does not match the records modelId")
return
} }
ctx.body = await linkRecords.attachLinkInfo(instanceId, record)
}
exports.fetchView = async function (ctx) { exports.destroy = async function (ctx) {
const instanceId = ctx.user.instanceId const instanceId = ctx.user.instanceId
const db = new CouchDB(instanceId) const db = new CouchDB(instanceId)
const { stats, group, field } = ctx.query const record = await db.get(ctx.params.recordId)
const viewName = ctx.params.viewName if (record.modelId !== ctx.params.modelId) {
ctx.throw(400, "Supplied modelId doesn't match the record's modelId")
// if this is a model view being looked for just transfer to that return
if (viewName.indexOf(MODEL_VIEW_BEGINS_WITH) === 0) { }
ctx.params.modelId = viewName.substring(4) await linkRecords.updateLinks({
await exports.fetchModelRecords(ctx) instanceId,
return eventType: linkRecords.EventType.RECORD_DELETE,
} record,
modelId: record.modelId,
const response = await db.query(`database/${viewName}`, { })
include_docs: !stats, ctx.body = await db.remove(ctx.params.recordId, ctx.params.revId)
group, ctx.status = 200
})
if (stats) { // for automations include the record that was deleted
response.rows = response.rows.map(row => ({ ctx.record = record
group: row.key, ctx.eventEmitter &&
field, ctx.eventEmitter.emitRecord(`record:delete`, instanceId, record)
...row.value, }
avg: row.value.sum / row.value.count,
}))
} else {
response.rows = response.rows.map(row => row.doc)
}
ctx.body = await linkRecords.attachLinkInfo(instanceId, response.rows) exports.validate = async function (ctx) {
} const errors = await validate({
instanceId: ctx.user.instanceId,
modelId: ctx.params.modelId,
record: ctx.request.body,
})
ctx.status = 200
ctx.body = errors
}
exports.fetchModelRecords = async function (ctx) { async function validate({ instanceId, modelId, record, model }) {
const instanceId = ctx.user.instanceId if (!model) {
const db = new CouchDB(instanceId) const db = new CouchDB(instanceId)
const response = await db.allDocs( model = await db.get(modelId)
getRecordParams(ctx.params.modelId, null, {
include_docs: true,
})
)
ctx.body = response.rows.map(row => row.doc)
ctx.body = await linkRecords.attachLinkInfo(
instanceId,
response.rows.map(row => row.doc)
)
} }
const errors = {}
exports.search = async function (ctx) { for (let fieldName of Object.keys(model.schema)) {
const instanceId = ctx.user.instanceId const res = validateJs.single(
const db = new CouchDB(instanceId) record[fieldName],
const response = await db.allDocs({ model.schema[fieldName].constraints
include_docs: true,
...ctx.request.body,
})
ctx.body = await linkRecords.attachLinkInfo(
instanceId,
response.rows.map(row => row.doc)
) )
if (res) errors[fieldName] = res
} }
return { valid: Object.keys(errors).length === 0, errors }
}
exports.find = async function (ctx) { exports.fetchEnrichedRecord = async function (ctx) {
const instanceId = ctx.user.instanceId const instanceId = ctx.user.instanceId
const db = new CouchDB(instanceId) const db = new CouchDB(instanceId)
const record = await db.get(ctx.params.recordId) const modelId = ctx.params.modelId
if (record.modelId !== ctx.params.modelId) { const recordId = ctx.params.recordId
ctx.throw(400, "Supplied modelId does not match the records modelId") if (instanceId == null || modelId == null || recordId == null) {
return ctx.status = 400
ctx.body = {
status: 400,
error:
"Cannot handle request, URI params have not been successfully prepared.",
} }
ctx.body = await linkRecords.attachLinkInfo(instanceId, record) return
} }
// need model to work out where links go in record
exports.destroy = async function (ctx) { const [model, record] = await Promise.all([db.get(modelId), db.get(recordId)])
const instanceId = ctx.user.instanceId // get the link docs
const db = new CouchDB(instanceId) const linkVals = await linkRecords.getLinkDocuments({
const record = await db.get(ctx.params.recordId) instanceId,
if (record.modelId !== ctx.params.modelId) { modelId,
ctx.throw(400, "Supplied modelId doesn't match the record's modelId") recordId,
return })
// look up the actual records based on the ids
const response = await db.allDocs({
include_docs: true,
keys: linkVals.map(linkVal => linkVal.id),
})
// need to include the IDs in these records for any links they may have
let linkedRecords = await linkRecords.attachLinkInfo(
instanceId,
response.rows.map(row => row.doc)
)
// insert the link records in the correct place throughout the main record
for (let fieldName of Object.keys(model.schema)) {
let field = model.schema[fieldName]
if (field.type === "link") {
record[fieldName] = linkedRecords.filter(
linkRecord => linkRecord.modelId === field.modelId
)
} }
await linkRecords.updateLinks({
instanceId,
eventType: linkRecords.EventType.RECORD_DELETE,
record,
modelId: record.modelId,
})
ctx.body = await db.remove(ctx.params.recordId, ctx.params.revId)
ctx.status = 200
// for automations include the record that was deleted
ctx.record = record
ctx.eventEmitter &&
ctx.eventEmitter.emitRecord(`record:delete`, instanceId, record)
} }
ctx.body = record
ctx.status = 200
}
async function bulkDelete(ctx) { function coerceRecordValues(rec, model) {
const { records } = ctx.request.body const record = cloneDeep(rec)
const db = new CouchDB(ctx.user.instanceId) for (let [key, value] of Object.entries(record)) {
const field = model.schema[key]
await db.bulkDocs( if (!field) continue
records.map(record => ({ ...record, _deleted: true }), (err, res) => {
if (err) { // eslint-disable-next-line no-prototype-builtins
ctx.status = 500 if (TYPE_TRANSFORM_MAP[field.type].hasOwnProperty(value)) {
} else { record[key] = TYPE_TRANSFORM_MAP[field.type][value]
records.forEach(record => { } else if (TYPE_TRANSFORM_MAP[field.type].parse) {
emitEvent(`record:delete`, ctx, record) record[key] = TYPE_TRANSFORM_MAP[field.type].parse(value)
}) }
ctx.status = 200
}
}))
} }
return record
}
exports.validate = async function (ctx) {
const errors = await validate({ async function bulkDelete(ctx) {
instanceId: ctx.user.instanceId, const { records } = ctx.request.body
modelId: ctx.params.modelId, const db = new CouchDB(ctx.user.instanceId)
record: ctx.request.body,
}) await db.bulkDocs(
ctx.status = 200 records.map(record => ({ ...record, _deleted: true }), (err, res) => {
ctx.body = errors if (err) {
ctx.status = 500
} else {
records.forEach(record => {
emitEvent(`record:delete`, ctx, record)
})
ctx.status = 200
}
}))
}
async function saveRecords(ctx) {
const instanceId = ctx.user.instanceId
const db = new CouchDB(instanceId)
let record = ctx.request.body
record.modelId = ctx.params.modelId
if (!record._rev && !record._id) {
record._id = generateRecordID(record.modelId)
} }
async function validate({ instanceId, modelId, record, model }) { const model = await db.get(record.modelId)
if (!model) {
const db = new CouchDB(instanceId) record = coerceRecordValues(record, model)
model = await db.get(modelId)
} const validateResult = await validate({
const errors = {} record,
for (let fieldName of Object.keys(model.schema)) { model,
const res = validateJs.single( })
record[fieldName],
model.schema[fieldName].constraints if (!validateResult.valid) {
) ctx.status = 400
if (res) errors[fieldName] = res ctx.body = {
status: 400,
errors: validateResult.errors,
} }
return { valid: Object.keys(errors).length === 0, errors } return
} }
exports.fetchEnrichedRecord = async function (ctx) { const existingRecord = record._rev && (await db.get(record._id))
const instanceId = ctx.user.instanceId
const db = new CouchDB(instanceId) // make sure link records are up to date
const modelId = ctx.params.modelId record = await linkRecords.updateLinks({
const recordId = ctx.params.recordId instanceId,
if (instanceId == null || modelId == null || recordId == null) { eventType: linkRecords.EventType.RECORD_SAVE,
ctx.status = 400 record,
ctx.body = { modelId: record.modelId,
status: 400, model,
error: })
"Cannot handle request, URI params have not been successfully prepared.",
} if (existingRecord) {
return const response = await db.put(record)
} record._rev = response.rev
// need model to work out where links go in record record.type = "record"
const [model, record] = await Promise.all([db.get(modelId), db.get(recordId)])
// get the link docs
const linkVals = await linkRecords.getLinkDocuments({
instanceId,
modelId,
recordId,
})
// look up the actual records based on the ids
const response = await db.allDocs({
include_docs: true,
keys: linkVals.map(linkVal => linkVal.id),
})
// need to include the IDs in these records for any links they may have
let linkedRecords = await linkRecords.attachLinkInfo(
instanceId,
response.rows.map(row => row.doc)
)
// insert the link records in the correct place throughout the main record
for (let fieldName of Object.keys(model.schema)) {
let field = model.schema[fieldName]
if (field.type === "link") {
record[fieldName] = linkedRecords.filter(
linkRecord => linkRecord.modelId === field.modelId
)
}
}
ctx.body = record ctx.body = record
ctx.status = 200 ctx.status = 200
ctx.message = `${model.name} updated successfully.`
return
} }
function coerceRecordValues(rec, model) { record.type = "record"
const record = cloneDeep(rec) const response = await db.post(record)
for (let [key, value] of Object.entries(record)) { record._rev = response.rev
const field = model.schema[key]
if (!field) continue
// eslint-disable-next-line no-prototype-builtins
if (TYPE_TRANSFORM_MAP[field.type].hasOwnProperty(value)) {
record[key] = TYPE_TRANSFORM_MAP[field.type][value]
} else if (TYPE_TRANSFORM_MAP[field.type].parse) {
record[key] = TYPE_TRANSFORM_MAP[field.type].parse(value)
}
}
return record
}
const TYPE_TRANSFORM_MAP = { ctx.eventEmitter &&
link: { ctx.eventEmitter.emitRecord(`record:save`, instanceId, record, model)
"": [], ctx.body = record
[null]: [], ctx.status = 200
[undefined]: undefined, ctx.message = `${model.name} created successfully`
}, }
options: {
"": "", const TYPE_TRANSFORM_MAP = {
[null]: "", link: {
[undefined]: undefined, "": [],
}, [null]: [],
string: { [undefined]: undefined,
"": "", },
[null]: "", options: {
[undefined]: undefined, "": "",
}, [null]: "",
number: { [undefined]: undefined,
"": null, },
[null]: null, string: {
[undefined]: undefined, "": "",
parse: n => parseFloat(n), [null]: "",
}, [undefined]: undefined,
datetime: { },
"": null, number: {
[undefined]: undefined, "": null,
[null]: null, [null]: null,
}, [undefined]: undefined,
attachment: { parse: n => parseFloat(n),
"": [], },
[null]: [], datetime: {
[undefined]: undefined, "": null,
}, [undefined]: undefined,
boolean: { [null]: null,
"": null, },
[null]: null, attachment: {
[undefined]: undefined, "": [],
true: true, [null]: [],
false: false, [undefined]: undefined,
}, },
} boolean: {
"": null,
[null]: null,
[undefined]: undefined,
true: true,
false: false,
},
}

Loading…
Cancel
Save