Purging the use of views where possible, this update should remove the use of by_type and all model views.

This commit is contained in:
Michael Drury 2020-10-01 17:22:08 +01:00
parent d36fa0ed39
commit 0a80abfd0e
7 changed files with 159 additions and 60 deletions

View File

@ -6,13 +6,18 @@ const {
POWERUSER_LEVEL_ID, POWERUSER_LEVEL_ID,
ADMIN_LEVEL_ID, ADMIN_LEVEL_ID,
} = require("../../utilities/accessLevels") } = require("../../utilities/accessLevels")
const {
generateAccessLevelID,
getAccessLevelParams,
} = require("../../db/utils")
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const db = new CouchDB(ctx.user.instanceId) const db = new CouchDB(ctx.user.instanceId)
const body = await db.query("database/by_type", { const body = await db.allDocs(
getAccessLevelParams(null, {
include_docs: true, include_docs: true,
key: ["accesslevel"],
}) })
)
const customAccessLevels = body.rows.map(row => row.doc) const customAccessLevels = body.rows.map(row => row.doc)
const staticAccessLevels = [ const staticAccessLevels = [
@ -90,7 +95,7 @@ exports.create = async function(ctx) {
name: ctx.request.body.name, name: ctx.request.body.name,
_rev: ctx.request.body._rev, _rev: ctx.request.body._rev,
permissions: ctx.request.body.permissions || [], permissions: ctx.request.body.permissions || [],
_id: newid(), _id: generateAccessLevelID(newid()),
type: "accesslevel", type: "accesslevel",
} }

View File

@ -12,17 +12,18 @@ const setBuilderToken = require("../../utilities/builder/setBuilderToken")
const fs = require("fs-extra") const fs = require("fs-extra")
const { promisify } = require("util") const { promisify } = require("util")
const chmodr = require("chmodr") const chmodr = require("chmodr")
const { generateAppID, getAppParams } = require("../../db/utils")
const { const {
downloadExtractComponentLibraries, downloadExtractComponentLibraries,
} = require("../../utilities/createAppPackage") } = require("../../utilities/createAppPackage")
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const db = new CouchDB(ClientDb.name(getClientId(ctx))) const db = new CouchDB(ClientDb.name(getClientId(ctx)))
const body = await db.query("client/by_type", { const body = await db.allDocs(
getAppParams(null, {
include_docs: true, include_docs: true,
key: ["app"],
}) })
)
ctx.body = body.rows.map(row => row.doc) ctx.body = body.rows.map(row => row.doc)
} }
@ -48,7 +49,7 @@ exports.create = async function(ctx) {
if (!clientId) { if (!clientId) {
ctx.throw(400, "ClientId not suplied") ctx.throw(400, "ClientId not suplied")
} }
const appId = newid() const appId = generateAppID(newid())
// insert an appId -> clientId lookup // insert an appId -> clientId lookup
const masterDb = new CouchDB("client_app_lookup") const masterDb = new CouchDB("client_app_lookup")

View File

@ -3,6 +3,7 @@ const newid = require("../../db/newid")
const actions = require("../../automations/actions") const actions = require("../../automations/actions")
const logic = require("../../automations/logic") const logic = require("../../automations/logic")
const triggers = require("../../automations/triggers") const triggers = require("../../automations/triggers")
const { getAutomationParams, generateAutomationID } = require("../../db/utils")
/************************* /*************************
* * * *
@ -34,7 +35,7 @@ exports.create = async function(ctx) {
const db = new CouchDB(ctx.user.instanceId) const db = new CouchDB(ctx.user.instanceId)
let automation = ctx.request.body let automation = ctx.request.body
automation._id = newid() automation._id = generateAutomationID(newid())
automation.type = "automation" automation.type = "automation"
automation = cleanAutomationInputs(automation) automation = cleanAutomationInputs(automation)
@ -72,10 +73,11 @@ exports.update = async function(ctx) {
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const db = new CouchDB(ctx.user.instanceId) const db = new CouchDB(ctx.user.instanceId)
const response = await db.query(`database/by_type`, { const response = await db.allDocs(
key: ["automation"], getAutomationParams(null, {
include_docs: true, include_docs: true,
}) })
)
ctx.body = response.rows.map(row => row.doc) ctx.body = response.rows.map(row => row.doc)
} }

View File

@ -1,26 +1,31 @@
const CouchDB = require("../../db") const CouchDB = require("../../db")
const newid = require("../../db/newid") const newid = require("../../db/newid")
const {
getRecordParams,
getModelParams,
generateModelID,
} = require("../../db/utils")
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const db = new CouchDB(ctx.user.instanceId) const db = new CouchDB(ctx.user.instanceId)
const body = await db.query("database/by_type", { const body = await db.allDocs(
getModelParams(null, {
include_docs: true, include_docs: true,
key: ["model"],
}) })
)
ctx.body = body.rows.map(row => row.doc) ctx.body = body.rows.map(row => row.doc)
} }
exports.find = async function(ctx) { exports.find = async function(ctx) {
const db = new CouchDB(ctx.user.instanceId) const db = new CouchDB(ctx.user.instanceId)
const model = await db.get(ctx.params.id) ctx.body = await db.get(ctx.params.id)
ctx.body = model
} }
exports.save = async function(ctx) { exports.save = async function(ctx) {
const db = new CouchDB(ctx.user.instanceId) const db = new CouchDB(ctx.user.instanceId)
const modelToSave = { const modelToSave = {
type: "model", type: "model",
_id: newid(), _id: generateModelID(newid()),
views: {}, views: {},
...ctx.request.body, ...ctx.request.body,
} }
@ -28,9 +33,11 @@ exports.save = async function(ctx) {
// rename record fields when table column is renamed // rename record fields when table column is renamed
const { _rename } = modelToSave const { _rename } = modelToSave
if (_rename) { if (_rename) {
const records = await db.query(`database/all_${modelToSave._id}`, { const records = await db.allDocs(
getRecordParams(modelToSave._id, null, {
include_docs: true, include_docs: true,
}) })
)
const docs = records.rows.map(({ doc }) => { const docs = records.rows.map(({ doc }) => {
doc[_rename.updated] = doc[_rename.old] doc[_rename.updated] = doc[_rename.old]
delete doc[_rename.old] delete doc[_rename.old]
@ -54,7 +61,7 @@ exports.save = async function(ctx) {
modelToSave._rev = result.rev modelToSave._rev = result.rev
const { schema } = ctx.request.body const { schema } = ctx.request.body
for (let key in schema) { for (let key of Object.keys(schema)) {
// model has a linked record // model has a linked record
if (schema[key].type === "link") { if (schema[key].type === "link") {
// create the link field in the other model // create the link field in the other model
@ -71,19 +78,6 @@ exports.save = async function(ctx) {
} }
} }
const designDoc = await db.get("_design/database")
designDoc.views = {
...designDoc.views,
[`all_${modelToSave._id}`]: {
map: `function(doc) {
if (doc.modelId === "${modelToSave._id}") {
emit(doc._id);
}
}`,
},
}
await db.put(designDoc)
ctx.status = 200 ctx.status = 200
ctx.message = `Model ${ctx.request.body.name} saved successfully.` ctx.message = `Model ${ctx.request.body.name} saved successfully.`
ctx.body = modelToSave ctx.body = modelToSave
@ -96,16 +90,18 @@ exports.destroy = async function(ctx) {
await db.remove(modelToDelete) await db.remove(modelToDelete)
const modelViewId = `all_${ctx.params.modelId}`
// Delete all records for that model // Delete all records for that model
const records = await db.query(`database/${modelViewId}`) const records = await db.allDocs(
getRecordParams(ctx.params.modelId, null, {
include_docs: true,
})
)
await db.bulkDocs( await db.bulkDocs(
records.rows.map(record => ({ _id: record.id, _deleted: true })) records.rows.map(record => ({ _id: record.id, _deleted: true }))
) )
// Delete linked record fields in dependent models // Delete linked record fields in dependent models
for (let key in modelToDelete.schema) { for (let key of Object.keys(modelToDelete.schema)) {
const { type, modelId } = modelToDelete.schema[key] const { type, modelId } = modelToDelete.schema[key]
if (type === "link") { if (type === "link") {
const linkedModel = await db.get(modelId) const linkedModel = await db.get(modelId)
@ -114,11 +110,6 @@ exports.destroy = async function(ctx) {
} }
} }
// delete the "all" view
const designDoc = await db.get("_design/database")
delete designDoc.views[modelViewId]
await db.put(designDoc)
ctx.status = 200 ctx.status = 200
ctx.message = `Model ${ctx.params.modelId} deleted.` ctx.message = `Model ${ctx.params.modelId} deleted.`
} }

View File

@ -1,6 +1,9 @@
const CouchDB = require("../../db") const CouchDB = require("../../db")
const validateJs = require("validate.js") const validateJs = require("validate.js")
const newid = require("../../db/newid") const newid = require("../../db/newid")
const { getRecordParams, generateRecordID } = require("../../db/utils")
const MODEL_VIEW_BEGINS_WITH = "all_model:"
function emitEvent(eventType, ctx, record) { function emitEvent(eventType, ctx, record) {
let event = { let event = {
@ -66,7 +69,7 @@ exports.save = async function(ctx) {
record.modelId = ctx.params.modelId record.modelId = ctx.params.modelId
if (!record._rev && !record._id) { if (!record._rev && !record._id) {
record._id = newid() record._id = generateRecordID(record.modelId, newid())
} }
const model = await db.get(record.modelId) const model = await db.get(record.modelId)
@ -133,7 +136,16 @@ exports.save = async function(ctx) {
exports.fetchView = async function(ctx) { exports.fetchView = async function(ctx) {
const db = new CouchDB(ctx.user.instanceId) const db = new CouchDB(ctx.user.instanceId)
const { stats, group, field } = ctx.query const { stats, group, field } = ctx.query
const response = await db.query(`database/${ctx.params.viewName}`, { const viewName = ctx.params.viewName
// if this is a model view being looked for just transfer to that
if (viewName.indexOf(MODEL_VIEW_BEGINS_WITH) === 0) {
ctx.params.modelId = viewName.substring(4)
await exports.fetchModelRecords(ctx)
return
}
const response = await db.query(`database/${viewName}`, {
include_docs: !stats, include_docs: !stats,
group, group,
}) })
@ -154,9 +166,11 @@ exports.fetchView = async function(ctx) {
exports.fetchModelRecords = async function(ctx) { exports.fetchModelRecords = async function(ctx) {
const db = new CouchDB(ctx.user.instanceId) const db = new CouchDB(ctx.user.instanceId)
const response = await db.query(`database/all_${ctx.params.modelId}`, { const response = await db.allDocs(
getRecordParams(ctx.params.modelId, null, {
include_docs: true, include_docs: true,
}) })
)
ctx.body = response.rows.map(row => row.doc) ctx.body = response.rows.map(row => row.doc)
} }

View File

@ -1,6 +1,7 @@
const CouchDB = require("../db") const CouchDB = require("../db")
const emitter = require("../events/index") const emitter = require("../events/index")
const InMemoryQueue = require("./queue/inMemoryQueue") const InMemoryQueue = require("./queue/inMemoryQueue")
const { getAutomationParams } = require("../db/utils")
let automationQueue = new InMemoryQueue() let automationQueue = new InMemoryQueue()
@ -89,12 +90,8 @@ async function queueRelevantRecordAutomations(event, eventType) {
throw `No instanceId specified for ${eventType} - check event emitters.` throw `No instanceId specified for ${eventType} - check event emitters.`
} }
const db = new CouchDB(event.instanceId) const db = new CouchDB(event.instanceId)
const automationsToTrigger = await db.query( const automationsToTrigger = await db.allDocs(
"database/by_automation_trigger", getAutomationParams(null, { include_docs: true })
{
key: [eventType],
include_docs: true,
}
) )
const automations = automationsToTrigger.rows.map(wf => wf.doc) const automations = automationsToTrigger.rows.map(wf => wf.doc)

View File

@ -0,0 +1,89 @@
const DocumentTypes = {
MODEL: "model",
RECORD: "record",
USER: "user",
AUTOMATION: "automation",
LINK: "link",
APP: "app",
ACCESS_LEVEL: "accesslevel",
}
exports.DocumentTypes = DocumentTypes
const UNICODE_MAX = "\ufff0"
function singleInputParams(docType, input, otherProps = {}) {
if (input == null) {
input = ""
}
return {
...otherProps,
startkey: `${docType}:${input}`,
endkey: `${docType}:${input}${UNICODE_MAX}`,
}
}
exports.getModelParams = (modelId, otherProps = {}) => {
return singleInputParams(DocumentTypes.MODEL, modelId, otherProps)
}
exports.generateModelID = modelId => {
return `${DocumentTypes.MODEL}:${modelId}`
}
exports.getRecordParams = (modelId, recordId, otherProps = {}) => {
if (modelId == null && recordId != null) {
throw "Cannot build key for record ID without a model ID"
}
const endOfKey = recordId == null ? `${modelId}:` : `${modelId}:${recordId}`
return {
...otherProps,
startkey: `${DocumentTypes.RECORD}:${endOfKey}`,
endkey: `${DocumentTypes.RECORD}:${endOfKey}${UNICODE_MAX}`,
}
}
exports.generateRecordID = (modelId, recordId) => {
return `${DocumentTypes.RECORD}:${modelId}:${recordId}`
}
exports.getUserParams = (username, otherProps = {}) => {
return singleInputParams(DocumentTypes.USER, username, otherProps)
}
exports.generateUserID = username => {
return `${DocumentTypes.USER}:${username}`
}
exports.getAutomationParams = (automationId, otherProps = {}) => {
return singleInputParams(DocumentTypes.AUTOMATION, automationId, otherProps)
}
exports.generateAutomationID = automationId => {
return `${DocumentTypes.AUTOMATION}:${automationId}`
}
// for now link records still have their own view as we can walk multiple directions
exports.generateLinkID = (modelId1, modelId2, recordId1, recordId2) => {
return `${DocumentTypes.AUTOMATION}:${modelId1}:${modelId2}:${recordId1}:${recordId2}`
}
exports.generateAppID = appId => {
return `${DocumentTypes.APP}:${appId}`
}
exports.getAppParams = (appId, otherProps = {}) => {
return singleInputParams(DocumentTypes.APP, appId, otherProps)
}
exports.generateAccessLevelID = accessLevelId => {
return `${DocumentTypes.ACCESS_LEVEL}:${accessLevelId}`
}
exports.getAccessLevelParams = (accessLevelId, otherProps) => {
return singleInputParams(
DocumentTypes.ACCESS_LEVEL,
accessLevelId,
otherProps
)
}