From 6c0efea8ead916db5b212ab6e316582bb4be5c06 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Thu, 9 Apr 2020 16:42:55 +0100 Subject: [PATCH] record controllers... --- .../common/src/records/validateRecord.mjs | 10 +- packages/common/src/schema/types/array.mjs | 4 +- packages/common/src/schema/types/bool.mjs | 9 +- packages/common/src/schema/types/datetime.mjs | 4 +- packages/common/src/schema/types/file.mjs | 8 +- packages/common/src/schema/types/index.mjs | 4 +- packages/common/src/schema/types/link.mjs | 53 +--- packages/common/src/schema/types/number.mjs | 6 +- packages/common/src/schema/types/string.mjs | 4 +- .../common/src/schema/types/typeHelpers.mjs | 11 +- packages/common/test/testSchema.mjs | 8 +- packages/common/test/validateRecord.spec.js | 244 +++++------------- packages/core/src/common/events.js | 11 - .../controllers/couchdbNamingConventions.js | 12 + .../server/middleware/controllers/record.js | 57 ++-- .../server/middleware/routes/neo/record.js | 7 +- 16 files changed, 146 insertions(+), 306 deletions(-) create mode 100644 packages/server/middleware/controllers/couchdbNamingConventions.js diff --git a/packages/common/src/records/validateRecord.mjs b/packages/common/src/records/validateRecord.mjs index 2419f35300..7f9e774a55 100644 --- a/packages/common/src/records/validateRecord.mjs +++ b/packages/common/src/records/validateRecord.mjs @@ -22,10 +22,10 @@ const validateAllFieldParse = (record, model) => }, []), ]) -const validateAllTypeConstraints = async (record, model) => { +const validateAllTypeConstraints = (record, model) => { const errors = [] for (const field of model.fields) { - $(await validateTypeConstraints(field, record), [ + $(validateTypeConstraints(field, record), [ filter(isNonEmptyString), map(m => ({ message: m, fields: [field.name] })), each(e => errors.push(e)), @@ -55,8 +55,8 @@ const runRecordValidationRules = (record, model) => { ]) } -export const validateRecord = async (schema, record) => { - const model = schema.findModel(record.modelId) +export const validateRecord = (schema, record) => { + const model = schema.findModel(record._modelId) const fieldParseFails = validateAllFieldParse(record, model) // non parsing would cause further issues - exit here @@ -65,7 +65,7 @@ export const validateRecord = async (schema, record) => { } const recordValidationRuleFails = runRecordValidationRules(record, model) - const typeContraintFails = await validateAllTypeConstraints(record, model) + const typeContraintFails = validateAllTypeConstraints(record, model) if ( isEmpty(fieldParseFails) && diff --git a/packages/common/src/schema/types/array.mjs b/packages/common/src/schema/types/array.mjs index 33098e6116..fc0f9158d1 100644 --- a/packages/common/src/schema/types/array.mjs +++ b/packages/common/src/schema/types/array.mjs @@ -47,11 +47,11 @@ const options = { const typeConstraints = [ makerule( - async (val, opts) => val === null || val.length >= opts.minLength, + (val, opts) => val === null || val.length >= opts.minLength, (val, opts) => `must choose ${opts.minLength} or more options` ), makerule( - async (val, opts) => val === null || val.length <= opts.maxLength, + (val, opts) => val === null || val.length <= opts.maxLength, (val, opts) => `cannot choose more than ${opts.maxLength} options` ), ] diff --git a/packages/common/src/schema/types/bool.mjs b/packages/common/src/schema/types/bool.mjs index a9d1ea99eb..9165aca5bd 100644 --- a/packages/common/src/schema/types/bool.mjs +++ b/packages/common/src/schema/types/bool.mjs @@ -6,7 +6,12 @@ import { parsedSuccess, getDefaultExport, } from "./typeHelpers" -import { switchCase, defaultCase, isOneOf, toBoolOrNull } from "../../common/index.mjs" +import { + switchCase, + defaultCase, + isOneOf, + toBoolOrNull, +} from "../../common/index.mjs" const boolFunctions = typeFunctions({ default: constant(null), @@ -31,7 +36,7 @@ const options = { const typeConstraints = [ makerule( - async (val, opts) => opts.allowNulls === true || val !== null, + (val, opts) => opts.allowNulls === true || val !== null, () => "field cannot be null" ), ] diff --git a/packages/common/src/schema/types/datetime.mjs b/packages/common/src/schema/types/datetime.mjs index 0521b1953f..451b03bbe7 100644 --- a/packages/common/src/schema/types/datetime.mjs +++ b/packages/common/src/schema/types/datetime.mjs @@ -50,7 +50,7 @@ const options = { const typeConstraints = [ makerule( - async (val, opts) => + (val, opts) => val === null || isNullOrEmpty(opts.minValue) || val >= opts.minValue, (val, opts) => `value (${val.toString()}) must be greater than or equal to ${ @@ -58,7 +58,7 @@ const typeConstraints = [ }` ), makerule( - async (val, opts) => + (val, opts) => val === null || isNullOrEmpty(opts.maxValue) || val <= opts.maxValue, (val, opts) => `value (${val.toString()}) must be less than or equal to ${ diff --git a/packages/common/src/schema/types/file.mjs b/packages/common/src/schema/types/file.mjs index 88477d9d7b..4171060f62 100644 --- a/packages/common/src/schema/types/file.mjs +++ b/packages/common/src/schema/types/file.mjs @@ -5,7 +5,13 @@ import { parsedSuccess, getDefaultExport, } from "./typeHelpers" -import { switchCase, defaultCase, none, $, splitKey } from "../../common/index.mjs" +import { + switchCase, + defaultCase, + none, + $, + splitKey, +} from "../../common/index.mjs" const illegalCharacters = "*?\\/:<>|\0\b\f\v" diff --git a/packages/common/src/schema/types/index.mjs b/packages/common/src/schema/types/index.mjs index 4c6dd9ff97..974b83bbf9 100644 --- a/packages/common/src/schema/types/index.mjs +++ b/packages/common/src/schema/types/index.mjs @@ -67,8 +67,8 @@ export const validateFieldParse = (field, record) => export const getDefaultOptions = type => getType(type).getDefaultOptions() -export const validateTypeConstraints = async (field, record) => - await getType(field.type).validateTypeConstraints(field, record) +export const validateTypeConstraints = (field, record) => + getType(field.type).validateTypeConstraints(field, record) export const detectType = value => { if (isString(value)) return string diff --git a/packages/common/src/schema/types/link.mjs b/packages/common/src/schema/types/link.mjs index 1d2fcbd3b0..ada1ef0efb 100644 --- a/packages/common/src/schema/types/link.mjs +++ b/packages/common/src/schema/types/link.mjs @@ -1,65 +1,30 @@ -import { isString, isObjectLike, isNull, has } from "lodash/fp" -import { - typeFunctions, - parsedSuccess, - getDefaultExport, - parsedFailed, -} from "./typeHelpers" +import { isString, isUndefined, isNull } from "lodash/fp" +import { typeFunctions, parsedSuccess, getDefaultExport } from "./typeHelpers" import { switchCase, defaultCase, isNonEmptyString, - isArrayOfString, } from "../../common/index.mjs" -const linkNothing = () => ({ key: "" }) +const linkNothing = () => "" const linkFunctions = typeFunctions({ default: linkNothing, }) -const hasStringValue = (ob, path) => has(path)(ob) && isString(ob[path]) - -const isObjectWithKey = v => isObjectLike(v) && hasStringValue(v, "key") - -const tryParseFromString = s => { - try { - const asObj = JSON.parse(s) - if (isObjectWithKey) { - return parsedSuccess(asObj) - } - } catch (_) { - // EMPTY - } - - return parsedFailed(s) -} - const linkTryParse = v => switchCase( - [isObjectWithKey, parsedSuccess], - [isString, tryParseFromString], + [isString, s => parsedSuccess(s)], [isNull, () => parsedSuccess(linkNothing())], - [defaultCase, parsedFailed] + [isUndefined, () => parsedSuccess(linkNothing())], + [defaultCase, s => parsedSuccess(s.toString())] )(v) const options = { - indexNodeKey: { - defaultValue: null, - isValid: isNonEmptyString, - requirementDescription: "must be a non-empty string", - parse: s => s, - }, - displayValue: { + modelId: { defaultValue: "", isValid: isNonEmptyString, - requirementDescription: "must be a non-empty string", - parse: s => s, - }, - reverseIndexNodeKeys: { - defaultValue: null, - isValid: v => isArrayOfString(v) && v.length > 0, - requirementDescription: "must be a non-empty array of strings", + requirementDescription: "must choose a model", parse: s => s, }, } @@ -72,6 +37,6 @@ export default getDefaultExport( linkFunctions, options, typeConstraints, - { key: "key", value: "value" }, + "abcd1234", JSON.stringify ) diff --git a/packages/common/src/schema/types/number.mjs b/packages/common/src/schema/types/number.mjs index a3859dae76..d0dff23287 100644 --- a/packages/common/src/schema/types/number.mjs +++ b/packages/common/src/schema/types/number.mjs @@ -58,7 +58,7 @@ const getDecimalPlaces = val => { const typeConstraints = [ makerule( - async (val, opts) => + (val, opts) => val === null || opts.minValue === null || val >= opts.minValue, (val, opts) => `value (${val.toString()}) must be greater than or equal to ${ @@ -66,7 +66,7 @@ const typeConstraints = [ }` ), makerule( - async (val, opts) => + (val, opts) => val === null || opts.maxValue === null || val <= opts.maxValue, (val, opts) => `value (${val.toString()}) must be less than or equal to ${ @@ -74,7 +74,7 @@ const typeConstraints = [ } options` ), makerule( - async (val, opts) => + (val, opts) => val === null || opts.decimalPlaces >= getDecimalPlaces(val), (val, opts) => `value (${val.toString()}) must have ${ diff --git a/packages/common/src/schema/types/string.mjs b/packages/common/src/schema/types/string.mjs index 38847ee4d0..077751243f 100644 --- a/packages/common/src/schema/types/string.mjs +++ b/packages/common/src/schema/types/string.mjs @@ -50,12 +50,12 @@ const options = { const typeConstraints = [ makerule( - async (val, opts) => + (val, opts) => val === null || opts.maxLength === null || val.length <= opts.maxLength, (val, opts) => `value exceeds maximum length of ${opts.maxLength}` ), makerule( - async (val, opts) => + (val, opts) => val === null || opts.allowDeclaredValuesOnly === false || includes(val)(opts.values), diff --git a/packages/common/src/schema/types/typeHelpers.mjs b/packages/common/src/schema/types/typeHelpers.mjs index 129864dcbf..d584bc6348 100644 --- a/packages/common/src/schema/types/typeHelpers.mjs +++ b/packages/common/src/schema/types/typeHelpers.mjs @@ -46,19 +46,16 @@ export const typeFunctions = specificFunctions => specificFunctions ) -export const validateTypeConstraints = validationRules => async ( - field, - record -) => { +export const validateTypeConstraints = validationRules => (field, record) => { const fieldValue = record[field.name] - const validateRule = async r => - !(await r.isValid(fieldValue, field.typeOptions)) + const validateRule = r => + !r.isValid(fieldValue, field.typeOptions) ? r.getMessage(fieldValue, field.typeOptions) : "" const errors = [] for (const r of validationRules) { - const err = await validateRule(r) + const err = validateRule(r) if (isNotEmpty(err)) errors.push(err) } diff --git a/packages/common/test/testSchema.mjs b/packages/common/test/testSchema.mjs index acde5037d8..f37f9c820d 100644 --- a/packages/common/test/testSchema.mjs +++ b/packages/common/test/testSchema.mjs @@ -2,10 +2,7 @@ import { newModel } from "../src/schema/models.mjs" import { newView } from "../src/schema/views.mjs" import { getNewField } from "../src/schema/fields.mjs" import { fullSchema } from "../src/schema/fullSchema.mjs" -import { - recordValidationRules, - commonRecordValidationRules, -} from "../src/schema/recordValidationRules.mjs" +import { commonRecordValidationRules } from "../src/records/recordValidationRules.mjs" export function testSchema() { const addFieldToModel = (model, { type, name }) => { @@ -21,9 +18,10 @@ export function testSchema() { addFieldToModel(contactModel, { name: "Name" }) addFieldToModel(contactModel, { name: "Is Active", type: "bool" }) addFieldToModel(contactModel, { name: "Created", type: "datetime" }) + addFieldToModel(contactModel, { name: "Status", type: "string" }) contactModel.validationRules.push( - recordValidationRules(commonRecordValidationRules.fieldNotEmpty) + commonRecordValidationRules.fieldNotEmpty("Name") ) const activeContactsView = newView(contactModel.id) diff --git a/packages/common/test/validateRecord.spec.js b/packages/common/test/validateRecord.spec.js index 6fb9f11635..19fb664c5c 100644 --- a/packages/common/test/validateRecord.spec.js +++ b/packages/common/test/validateRecord.spec.js @@ -1,10 +1,8 @@ -import { addHours } from "date-fns" -import { events } from "../src/common" import { testSchema } from "./testSchema.mjs" import { validateRecord } from "../src/records/validateRecord.mjs" import { getNewRecord } from "../src/records/getNewRecord.mjs" -describe("recordApi > validate", () => { +describe("validateRecord", () => { it("should return errors when any fields do not parse", () => { const schema = testSchema() const record = getNewRecord(schema, "Contact") @@ -34,7 +32,7 @@ describe("recordApi > validate", () => { const schema = testSchema() schema.findField("Contact", "Name").typeOptions.maxLength = 5 const record = getNewRecord(schema, "Contact") - record.name = "more than 5 characters" + record.Name = "more than 5 characters" const validationResult = validateRecord(schema, record) expect(validationResult.isValid).toBe(false) @@ -47,213 +45,93 @@ describe("recordApi > validate", () => { const record = getNewRecord(schema, "Deal") record["Estimated Value"] = 10 - const validationResult = recordApi.validate(schema, record) + const validationResult = validateRecord(schema, record) expect(validationResult.isValid).toBe(false) expect(validationResult.errors.length).toBe(1) }) - it("should return error when number field is < minValue", async () => { - const withFieldWithMaxLength = hierarchy => { - const age = find(hierarchy.customerRecord.fields, f => f.name === "age") - age.typeOptions.minValue = 5 - } + it("should return error when number field is < minValue", () => { + const schema = testSchema() + schema.findField("Deal", "Estimated Value").typeOptions.minValue = 5 + const record = getNewRecord(schema, "Deal") + record["Estimated Value"] = 1 - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi } = await setupApphierarchy(hierarchyCreator) - - const tooYoungRecord = recordApi.getNew("/customers", "customer") - tooYoungRecord.age = 3 - - const tooYoungResult = await recordApi.validate(tooYoungRecord) - expect(tooYoungResult.isValid).toBe(false) - expect(tooYoungResult.errors.length).toBe(1) - }) - - it("should return error when number has too many decimal places", async () => { - const withFieldWithMaxLength = (hierarchy, templateApi) => { - const age = find(hierarchy.customerRecord.fields, f => f.name === "age") - age.typeOptions.decimalPlaces = 2 - } - - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi } = await setupApphierarchy(hierarchyCreator) - - const record = recordApi.getNew("/customers", "customer") - record.age = 3.123 - - const validationResult = await recordApi.validate(record) - expect(validationResult.isValid).toBe(false) - expect(validationResult.errors.length).toBe(1) - }) - - it("should return error when datetime field is > maxValue", async () => { - const withFieldWithMaxLength = hierarchy => { - const createddate = find( - hierarchy.customerRecord.fields, - f => f.name === "createddate" - ) - createddate.typeOptions.maxValue = new Date() - } - - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi } = await setupApphierarchy(hierarchyCreator) - - const record = recordApi.getNew("/customers", "customer") - record.createddate = addHours(new Date(), 1) - - const result = await recordApi.validate(record) + const result = validateRecord(schema, record) expect(result.isValid).toBe(false) expect(result.errors.length).toBe(1) }) - it("should return error when number field is < minValue", async () => { - const withFieldWithMaxLength = hierarchy => { - const createddate = find( - hierarchy.customerRecord.fields, - f => f.name === "createddate" - ) - createddate.typeOptions.minValue = addHours(new Date(), 1) - } + it("should return error when number has too many decimal places", () => { + const schema = testSchema() + schema.findField("Deal", "Estimated Value").typeOptions.decimalPlaces = 2 + const record = getNewRecord(schema, "Deal") + record["Estimated Value"] = 1.123 - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi } = await setupApphierarchy(hierarchyCreator) - - const record = recordApi.getNew("/customers", "customer") - record.createddate = new Date() - - const result = await recordApi.validate(record) + const result = validateRecord(schema, record) expect(result.isValid).toBe(false) expect(result.errors.length).toBe(1) }) - it("should return error when string IS NOT one of declared values, and only declared values are allowed", async () => { - const withFieldWithMaxLength = hierarchy => { - const surname = find( - hierarchy.customerRecord.fields, - f => f.name === "surname" - ) - surname.typeOptions.allowDeclaredValuesOnly = true - surname.typeOptions.values = ["thedog"] - } + it("should return error when datetime field is > maxValue", () => { + const schema = testSchema() + schema.findField("Contact", "Created").typeOptions.maxValue = new Date(2020, 1, 1) + const record = getNewRecord(schema, "Contact") + record.Name = "Bob" + record.Created = new Date(2020, 1, 2) - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi } = await setupApphierarchy(hierarchyCreator) - - const record = recordApi.getNew("/customers", "customer") - record.surname = "zeecat" - - const result = await recordApi.validate(record) + const result = validateRecord(schema, record) expect(result.isValid).toBe(false) expect(result.errors.length).toBe(1) }) - it("should not return error when string IS one of declared values, and only declared values are allowed", async () => { - const withFieldWithMaxLength = hierarchy => { - const surname = find( - hierarchy.customerRecord.fields, - f => f.name === "surname" - ) - surname.typeOptions.allowDeclaredValuesOnly = true - surname.typeOptions.values = ["thedog"] - } + it("should return error when number field is < minValue", () => { + const schema = testSchema() + schema.findField("Contact", "Created").typeOptions.minValue = new Date(2020, 1, 2) + const record = getNewRecord(schema, "Contact") + record.Name = "Bob" + record.Created = new Date(2020, 1, 1) - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi, appHierarchy } = await setupApphierarchy( - hierarchyCreator - ) + const result = validateRecord(schema, record) + expect(result.isValid).toBe(false) + expect(result.errors.length).toBe(1) + }) - const record = recordApi.getNew("/customers", "customer") - record.surname = "thedog" + it("should return error when string IS NOT one of declared values, and only declared values are allowed", () => { + const schema = testSchema() + schema.findField("Contact", "Status").typeOptions.allowDeclaredValuesOnly = true + schema.findField("Contact", "Status").typeOptions.values = ["thedog"] + const record = getNewRecord(schema, "Contact") + record.Status = "not allowed" + record.Name = "Bob" - const result = await recordApi.validate(record) + const result = validateRecord(schema, record) + expect(result.isValid).toBe(false) + expect(result.errors.length).toBe(1) + }) + + it("should not return error when string IS one of declared values, and only declared values are allowed", () => { + const schema = testSchema() + schema.findField("Contact", "Status").typeOptions.allowDeclaredValuesOnly = true + schema.findField("Contact", "Status").typeOptions.values = ["thedog"] + const record = getNewRecord(schema, "Contact") + record.Status = "thedog" + record.Name = "Bob" + + const result = validateRecord(schema, record) expect(result.isValid).toBe(true) expect(result.errors.length).toBe(0) }) - it("should not return error when string IS NOT one of declared values, but any values are allowed", async () => { - const withFieldWithMaxLength = (hierarchy, templateApi) => { - const surname = find( - hierarchy.customerRecord.fields, - f => f.name === "surname" - ) - surname.typeOptions.allowDeclaredValuesOnly = false - surname.typeOptions.values = ["thedog"] - } + it("should not return error when string IS NOT one of declared values, but any values are allowed", () => { + const schema = testSchema() + schema.findField("Contact", "Status").typeOptions.allowDeclaredValuesOnly = false + schema.findField("Contact", "Status").typeOptions.values = ["thedog"] + const record = getNewRecord(schema, "Contact") + record.Status = "not one of the values" + record.Name = "Bob" - const hierarchyCreator = hierarchyFactory( - withFields, - withFieldWithMaxLength - ) - const { recordApi, appHierarchy } = await setupApphierarchy( - hierarchyCreator - ) - - const record = recordApi.getNew("/customers", "customer") - record.surname = "zeecat" - - const result = await recordApi.validate(record) + const result = validateRecord(schema, record) expect(result.isValid).toBe(true) expect(result.errors.length).toBe(0) }) - - it("should return error when reference field does not exist in options index", async () => { - const { recordApi, appHierarchy } = await setupApphierarchy( - basicAppHierarchyCreator_WithFields_AndIndexes - ) - - const partner = recordApi.getNew("/partners", "partner") - partner.businessName = "ACME Inc" - await recordApi.save(partner) - - const customer = recordApi.getNew("/customers", "customer") - customer.partner = { key: "incorrect key", name: partner.businessName } - const result = await await recordApi.validate(customer) - expect(result.isValid).toBe(false) - expect(result.errors.length).toBe(1) - }) - - it("should publish invalid events", async () => { - const withValidationRule = (hierarchy, templateApi) => { - templateApi.addRecordValidationRule(hierarchy.customerRecord)( - templateApi.commonRecordValidationRules.fieldNotEmpty("surname") - ) - } - - const hierarchyCreator = hierarchyFactory(withFields, withValidationRule) - - const { recordApi, subscribe } = await setupApphierarchy(hierarchyCreator) - const handler = stubEventHandler() - subscribe(events.recordApi.save.onInvalid, handler.handle) - - const record = recordApi.getNew("/customers", "customer") - record.surname = "" - - try { - await recordApi.save(record) - } catch (e) {} - - const onInvalid = handler.getEvents(events.recordApi.save.onInvalid) - expect(onInvalid.length).toBe(1) - expect(onInvalid[0].context.record).toBeDefined() - expect(onInvalid[0].context.record.key).toBe(record.key) - expect(onInvalid[0].context.validationResult).toBeDefined() - }) }) diff --git a/packages/core/src/common/events.js b/packages/core/src/common/events.js index 82501fbf9d..4c890926a8 100644 --- a/packages/core/src/common/events.js +++ b/packages/core/src/common/events.js @@ -15,17 +15,6 @@ const _events = { uploadFile: common(), downloadFile: common(), }, - indexApi: { - buildIndex: common(), - listItems: common(), - delete: common(), - aggregates: common(), - }, - collectionApi: { - getAllowedRecordTypes: common(), - initialise: common(), - delete: common(), - }, authApi: { authenticate: common(), authenticateTemporaryAccess: common(), diff --git a/packages/server/middleware/controllers/couchdbNamingConventions.js b/packages/server/middleware/controllers/couchdbNamingConventions.js new file mode 100644 index 0000000000..e82042c6af --- /dev/null +++ b/packages/server/middleware/controllers/couchdbNamingConventions.js @@ -0,0 +1,12 @@ +export function allModelsViewName(modelId) { + return `all_${modelId}` +} +export function allModelsDesignDocName(modelId) { + return `all_${modelId}` +} +export function instanceDatabaseId (clientId, instanceId) { + return `instance:${clientId}:${instanceId}` +} +export function clientDatabaseId(clientId) { + return `client:${clientId}` +} diff --git a/packages/server/middleware/controllers/record.js b/packages/server/middleware/controllers/record.js index b8938bc18d..9215dbe3da 100644 --- a/packages/server/middleware/controllers/record.js +++ b/packages/server/middleware/controllers/record.js @@ -1,11 +1,15 @@ const couchdb = require("../../db") -const { cloneDeep, mapValues, keyBy, filter, includes } = require("lodash/fp") +const { cloneDeep, mapValues, keyBy } = require("lodash/fp") const { validateRecord, } = require("../../../common/src/records/validateRecord.mjs") const { events } = require("../../../common/src/common/events.mjs") -const { $, isNonEmptyString } = require("../../../common/src/common") +const { $ } = require("../../../common/src/common") import { safeParseField } from "../../../common/src/schema/types" +import { + allModelsViewName, + allModelsDesignDocName, +} from "./couchdbNamingConventions" async function save(ctx) { const db = couchdb.use(ctx.databaseId) @@ -19,7 +23,7 @@ async function save(ctx) { const validationResult = await validateRecord(ctx.schema, record) if (!validationResult.isValid) { - await app.publish(events.recordApi.save.onInvalid, { + await ctx.publish(events.recordApi.save.onInvalid, { record, validationResult, }) @@ -30,13 +34,13 @@ async function save(ctx) { if (!record._rev) { await db.insert(record) - await app.publish(events.recordApi.save.onRecordCreated, { + await ctx.publish(events.recordApi.save.onRecordCreated, { record: record, }) } else { const oldRecord = await _findRecord(db, ctx.schema, record._id) await db.insert(record) - await app.publish(events.recordApi.save.onRecordUpdated, { + await ctx.publish(events.recordApi.save.onRecordUpdated, { old: oldRecord, new: record, }) @@ -49,16 +53,23 @@ async function save(ctx) { async function fetch(ctx) { const db = couchdb.db.use(ctx.params.databaseId) - - ctx.body = await db.view("database", "all_somemodel", { - include_docs: true, - key: ["app"] - }) + const model = ctx.schema.findModel(ctx.modelName) + ctx.body = db.viewAsStream( + allModelsDesignDocName(model.id), + allModelsViewName(model.id), + { + include_docs: true, + } + ) } async function find(ctx) { const db = couchdb.db.use(ctx.params.databaseId) - const { body, status } = await _findRecord(db, ctx.schema, ctx.params.id) + const { body, status } = await _findRecord( + db, + ctx.schema, + ctx.params.recordId + ) ctx.status = status ctx.body = body } @@ -78,28 +89,6 @@ async function _findRecord(db, schema, id) { mapValues(f => safeParseField(f, storedData)), ]) - const links = $(model.fields, [ - filter( - f => f.type === "reference" && isNonEmptyString(loadedRecord[f.name].key) - ), - map(f => ({ - promise: _findRecord(db, schema, loadedRecord[f.name]._id), - index: getNode(app.hierarchy, f.typeOptions.indexNodeKey), - field: f, - })), - ]) - - if (links.length > 0) { - const refRecords = await Promise.all(map(p => p.promise)(links)) - - for (const ref of links) { - loadedRecord[ref.field.name] = mapRecord( - refRecords[links.indexOf(ref)], - ref.index - ) - } - } - loadedRecord._rev = storedData._rev loadedRecord._id = storedData._id loadedRecord._modelId = storedData._modelId @@ -112,4 +101,4 @@ async function destroy(ctx) { ctx.body = await database.destroy(ctx.params.recordId); } -module.exports = {dave, fetch, destroy, find}; \ No newline at end of file +module.exports = { save, fetch, destroy, find } diff --git a/packages/server/middleware/routes/neo/record.js b/packages/server/middleware/routes/neo/record.js index b626ab6208..fc12241a62 100644 --- a/packages/server/middleware/routes/neo/record.js +++ b/packages/server/middleware/routes/neo/record.js @@ -4,8 +4,9 @@ const controller = require("../../controllers/record"); const router = Router(); router - .get("/api/:databaseId/records", controller.fetch) - .post("/api/:databaseId/records", controller.save) - .delete("/api/:databaseId/records/:recordId", controller.destroy) + .get("/api/:databaseId/records/:modelname", controller.fetch) + .post("/api/:databaseId/record", controller.save) + .get("/api/:databaseId/record/:recordId", controller.find) + .delete("/api/:databaseId/record/:recordId", controller.destroy) module.exports = router; \ No newline at end of file