Allow non-editable fields to be auto-derived
Some fields shouldn't be editable through the API but can still be modified, because they are derived from another field which is editable. This change fixes a bug where a derived field wouldn't be updated, because it was not on the editable fields whitelist.
This commit is contained in:
parent
937ddcc4d2
commit
9722c173d8
@ -3,10 +3,19 @@ import { valueType } from '../../helpers';
|
|||||||
/** Configuration for a single data field */
|
/** Configuration for a single data field */
|
||||||
export interface DataFieldConfig {
|
export interface DataFieldConfig {
|
||||||
/**
|
/**
|
||||||
* Allow editing the field?
|
* Allow editing the field through the API?
|
||||||
*/
|
*/
|
||||||
edit: boolean;
|
edit: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the editing of the field be allowed - but only when
|
||||||
|
* the change is a result of an edit of another field, from which this field is derived.
|
||||||
|
* Example: editing Land Use Group modifies Land Use Order, too, because LU Order is automatically derived from LU Group.
|
||||||
|
* But Land Use Order itself cannot be modified directly by users.
|
||||||
|
* Default: false
|
||||||
|
*/
|
||||||
|
derivedEdit?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow verifying the field value?
|
* Allow verifying the field value?
|
||||||
* Default: false;
|
* Default: false;
|
||||||
@ -236,6 +245,7 @@ export const dataFieldsConfig = valueType<DataFieldConfig>()({ /* eslint-disable
|
|||||||
},
|
},
|
||||||
current_landuse_order: {
|
current_landuse_order: {
|
||||||
edit: false,
|
edit: false,
|
||||||
|
derivedEdit: true,
|
||||||
verify: false,
|
verify: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ export async function insertEditHistoryRevision(
|
|||||||
|
|
||||||
const columnConfigLookup = Object.assign(
|
const columnConfigLookup = Object.assign(
|
||||||
{},
|
{},
|
||||||
...Object.entries(dataFieldsConfig).filter(([, config]) => config.edit).map(([key, {
|
...Object.entries(dataFieldsConfig).filter(([, config]) => config.edit || config.derivedEdit).map(([key, {
|
||||||
asJson = false,
|
asJson = false,
|
||||||
sqlCast
|
sqlCast
|
||||||
}]) => ({ [key]: {
|
}]) => ({ [key]: {
|
||||||
|
@ -21,6 +21,15 @@ export class InvalidOperationError extends UserError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class InvalidFieldError extends UserError {
|
||||||
|
public fieldName: string;
|
||||||
|
constructor(message?: string, fieldName?: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'InvalidFieldError';
|
||||||
|
this.fieldName = fieldName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class FieldTypeError extends UserError {
|
export class FieldTypeError extends UserError {
|
||||||
public fieldName: string;
|
public fieldName: string;
|
||||||
constructor(message?: string, fieldName?: string) {
|
constructor(message?: string, fieldName?: string) {
|
||||||
|
@ -31,7 +31,11 @@ export async function getBuildingById(id: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FIELD_EDIT_WHITELIST = new Set(Object.entries(dataFieldsConfig).filter(([, value]) => value.edit).map(([key]) => key));
|
/**
|
||||||
|
* List of fields for which modification is allowed
|
||||||
|
* (directly by the user, or for fields that are derived from others)
|
||||||
|
*/
|
||||||
|
const FINAL_FIELD_EDIT_ALLOWLIST = new Set(Object.entries(dataFieldsConfig).filter(([, value]) => value.edit || value.derivedEdit).map(([key]) => key));
|
||||||
|
|
||||||
export async function editBuilding(buildingId: number, building: any, userId: string): Promise<object> { // TODO add proper building type
|
export async function editBuilding(buildingId: number, building: any, userId: string): Promise<object> { // TODO add proper building type
|
||||||
return await updateBuildingData(buildingId, userId, async () => {
|
return await updateBuildingData(buildingId, userId, async () => {
|
||||||
@ -44,7 +48,7 @@ export async function editBuilding(buildingId: number, building: any, userId: st
|
|||||||
delete processedBuilding.geometry_id;
|
delete processedBuilding.geometry_id;
|
||||||
|
|
||||||
// return whitelisted fields to update
|
// return whitelisted fields to update
|
||||||
return pickFields(processedBuilding, FIELD_EDIT_WHITELIST);
|
return pickFields(processedBuilding, FINAL_FIELD_EDIT_ALLOWLIST);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@ import Ajv from 'ajv';
|
|||||||
import addFormats from 'ajv-formats';
|
import addFormats from 'ajv-formats';
|
||||||
|
|
||||||
import { mapObject } from '../../../helpers';
|
import { mapObject } from '../../../helpers';
|
||||||
import { FieldTypeError } from '../../errors/general';
|
import { InvalidFieldError, FieldTypeError } from '../../errors/general';
|
||||||
|
import { dataFieldsConfig } from '../../config/dataFields';
|
||||||
import { fieldSchemaConfig } from '../../config/fieldSchemaConfig';
|
import { fieldSchemaConfig } from '../../config/fieldSchemaConfig';
|
||||||
|
|
||||||
const ajv = new Ajv();
|
const ajv = new Ajv();
|
||||||
@ -10,8 +11,14 @@ addFormats(ajv);
|
|||||||
|
|
||||||
const compiledSchemas = mapObject(fieldSchemaConfig, ([, val]) => ajv.compile(val))
|
const compiledSchemas = mapObject(fieldSchemaConfig, ([, val]) => ajv.compile(val))
|
||||||
|
|
||||||
|
const EXTERNAL_FIELD_EDIT_ALLOWLIST = new Set(Object.entries(dataFieldsConfig).filter(([, value]) => value.edit).map(([key]) => key));
|
||||||
|
|
||||||
export function validateBuildingUpdate(buildingId: number, building: any) {
|
export function validateBuildingUpdate(buildingId: number, building: any) {
|
||||||
for(const field of Object.keys(building)) {
|
for(const field of Object.keys(building)) {
|
||||||
|
if(!EXTERNAL_FIELD_EDIT_ALLOWLIST.has(field)) {
|
||||||
|
throw new InvalidFieldError('Field is not editable', field);
|
||||||
|
}
|
||||||
|
|
||||||
if(field in compiledSchemas) {
|
if(field in compiledSchemas) {
|
||||||
if(!compiledSchemas[field](building[field])) {
|
if(!compiledSchemas[field](building[field])) {
|
||||||
throw new FieldTypeError('Invalid format of data sent', field);
|
throw new FieldTypeError('Invalid format of data sent', field);
|
||||||
|
Loading…
Reference in New Issue
Block a user