Rewrite landuse domain logic without landuse class

This commit is contained in:
Maciej Ziarkowski 2020-03-19 13:45:35 +00:00
parent 4bda0f0aa6
commit 914d7e56d4
3 changed files with 36 additions and 103 deletions

View File

@ -1,15 +1,7 @@
import * as _ from 'lodash'; import * as _ from 'lodash';
import { DomainLogicError } from '../../../errors/general';
import { LandUseState, updateLandUse } from '../../domainLogic/landUse'; import { LandUseState, updateLandUse } from '../../domainLogic/landUse';
const testClassToGroup = {
'Animal breeding places': 'Agriculture',
'Egg grading place': 'Agriculture',
'Fish farm': 'Fisheries',
'Brewery': 'Manufacturing',
'Business meeting places': 'Offices'
};
const testGroupToOrder = { const testGroupToOrder = {
'Agriculture': 'Agriculture And Fisheries', 'Agriculture': 'Agriculture And Fisheries',
'Fisheries': 'Agriculture And Fisheries', 'Fisheries': 'Agriculture And Fisheries',
@ -18,12 +10,7 @@ const testGroupToOrder = {
}; };
jest.mock('../../../dataAccess/landUse', () => ({ jest.mock('../../../dataAccess/landUse', () => ({
getLandUseGroupFromClass: jest.fn((classes: string[]) => { getLandUseOrderFromGroup: jest.fn((groups: string[]) => {
const groups = _.chain(classes).map(c => testClassToGroup[c]).uniq().value();
return Promise.resolve(groups);
}),
getLandUseOrderGromGroup: jest.fn((groups: string[]) => {
const orders = _.chain(groups).map(g => testGroupToOrder[g]).uniq().value(); const orders = _.chain(groups).map(g => testGroupToOrder[g]).uniq().value();
let result: string; let result: string;
@ -38,66 +25,73 @@ jest.mock('../../../dataAccess/landUse', () => ({
describe('updateLandUse()', () => { describe('updateLandUse()', () => {
beforeEach(() => { beforeEach(() => {
jest.resetAllMocks(); jest.clearAllMocks();
}); });
it.each([ it.each([
[{ [{
landUseClass: [],
landUseGroup: [], landUseGroup: [],
landUseOrder: null landUseOrder: null
}, { }, {
landUseClass: ['Animal breeding places'] landUseGroup: ['Agriculture']
}, { }, {
landUseClass: ['Animal breeding places'],
landUseGroup: ['Agriculture'], landUseGroup: ['Agriculture'],
landUseOrder: 'Agriculture And Fisheries' landUseOrder: 'Agriculture And Fisheries'
}], }],
[{ [{
landUseClass: ['Animal breeding places'],
landUseGroup: ['Agriculture'], landUseGroup: ['Agriculture'],
landUseOrder: 'Agriculture And Fisheries' landUseOrder: 'Agriculture And Fisheries'
}, { }, {
landUseClass: ['Fish farm'] landUseGroup: ['Fisheries']
}, { }, {
landUseClass: ['Fish farm'],
landUseGroup: ['Fisheries'], landUseGroup: ['Fisheries'],
landUseOrder: 'Agriculture And Fisheries' landUseOrder: 'Agriculture And Fisheries'
}], }],
[{ [{
landUseClass: ['Animal breeding places'],
landUseGroup: ['Agriculture'], landUseGroup: ['Agriculture'],
landUseOrder: 'Agriculture And Fisheries' landUseOrder: 'Agriculture And Fisheries'
}, { }, {
landUseClass: ['Animal breeding places', 'Business meeting places'] landUseGroup: ['Agriculture', 'Offices'],
}, { }, {
landUseClass: ['Animal breeding places', 'Business meeting places'],
landUseGroup: ['Agriculture', 'Offices'], landUseGroup: ['Agriculture', 'Offices'],
landUseOrder: 'Mixed Use' landUseOrder: 'Mixed Use'
}] }]
])('Should derive higher level land use classifications from lower level ones', ])('Should derive land use order from group',
async (landUse: LandUseState, landUseUpdate: Partial<LandUseState>, expectedUpdate: LandUseState) => { async (landUse: LandUseState, landUseUpdate: Partial<LandUseState>, expectedUpdate: LandUseState) => {
const result = await updateLandUse(landUse, landUseUpdate); const result = await updateLandUse(landUse, landUseUpdate);
expect(result).toBe(expectedUpdate); expect(result).toEqual(expectedUpdate);
} }
); );
it.each([ it.each([
[{ [{
landUseClass: ['Fish farm'], landUseGroup: ['Agriculture'],
landUseGroup: ['Fisheries'],
landUseOrder: 'Agriculture And Fisheries' landUseOrder: 'Agriculture And Fisheries'
}, { }, {
landUseGroup: [] landUseGroup: []
}] }, {
])('Should error when update breaks an automatic chain of classifications', landUseGroup: [],
async (landUse: LandUseState, landUseUpdate: Partial<LandUseState>) => { landUseOrder: null
const resultPromise = updateLandUse(landUse, landUseUpdate); }],
expect(resultPromise).rejects.toBeInstanceOf(DomainLogicError); [{
landUseGroup: ['Agriculture', 'Offices'],
landUseOrder: 'Mixed Use',
}, {
landUseGroup: ['Agriculture'],
}, {
landUseGroup: ['Agriculture'],
landUseOrder: 'Agriculture And Fisheries'
}]
])('Should remove derived land use order when land use group is removed',
async (landUse: LandUseState, landUseUpdate: Partial<LandUseState>, expectedUpdate: LandUseState) => {
const result = await updateLandUse(landUse, landUseUpdate);
expect(result).toEqual(expectedUpdate);
} }
); );
}); });

View File

@ -1,75 +1,18 @@
import _ from 'lodash'; import _ from 'lodash';
import { isNullishOrEmpty } from '../../../helpers'; import { getLandUseOrderFromGroup } from '../../dataAccess/landUse';
import { getLandUseGroupFromClass, getLandUseOrderFromGroup } from '../../dataAccess/landUse';
import { getCurrentBuildingDataById } from '../building';
export interface LandUseState { export interface LandUseState {
landUseClass: string[];
landUseGroup: string[]; landUseGroup: string[];
landUseOrder: string; landUseOrder: string;
} }
export async function updateLandUse(landUse: LandUseState, landUseUpdate: Partial<LandUseState>): Promise<LandUseState> { export async function updateLandUse(landUse: LandUseState, landUseUpdate: Partial<LandUseState>): Promise<LandUseState> {
throw new Error('Not implemented'); const landUseGroupUpdate = landUseUpdate.landUseGroup;
} const landUseOrderUpdate = await getLandUseOrderFromGroup(landUseGroupUpdate);
export async function processCurrentLandUseClassifications(buildingId: number, buildingUpdate: any): Promise<any> { return {
let updateData = _.pick(await getCurrentBuildingDataById(buildingId), [ landUseGroup: landUseGroupUpdate,
'current_landuse_class', landUseOrder: landUseOrderUpdate
'current_landuse_group', };
'current_landuse_order'
]);
updateData = Object.assign({}, updateData, getClearValues(buildingUpdate));
const updateFrom = getUpdateStartingStage(buildingUpdate);
if(updateFrom === 'class') {
updateData.current_landuse_class = buildingUpdate.current_landuse_class;
updateData.current_landuse_group = await getLandUseGroupFromClass(updateData.current_landuse_class);
updateData.current_landuse_order = await getLandUseOrderFromGroup(updateData.current_landuse_group);
} else if (updateFrom === 'group') {
if (isNullishOrEmpty(updateData.current_landuse_class)) {
updateData.current_landuse_group = buildingUpdate.current_landuse_group;
updateData.current_landuse_order = await getLandUseOrderFromGroup(buildingUpdate.current_landuse_group);
} else {
throw new Error('Trying to update current_landuse_group field but a more detailed field (current_landuse_class) is already filled');
}
} else if (updateFrom === 'order') {
if (isNullishOrEmpty(updateData.current_landuse_class) && isNullishOrEmpty(updateData.current_landuse_group)) {
updateData.current_landuse_order = buildingUpdate.current_landuse_order;
} else {
throw new Error('Trying to update current_landuse_order field but a more detailed field (current_landuse_class or current_landuse_group) is already filled');
}
}
return Object.assign({}, buildingUpdate, updateData);
}
function getClearValues(building) {
const clearValues: any = {};
if(building.hasOwnProperty('current_landuse_class') && isNullishOrEmpty(building.current_landuse_class)) {
clearValues.current_landuse_class = [];
}
if(building.hasOwnProperty('current_landuse_group') && isNullishOrEmpty(building.current_landuse_group)) {
clearValues.current_landuse_group = [];
}
if(building.hasOwnProperty('current_landuse_order') && isNullishOrEmpty(building.current_landuse_order)) {
clearValues.current_landuse_order = null;
}
return clearValues;
}
/**
* Choose which level of the land use classification hierarchy the update should start from.
* @param building
*/
function getUpdateStartingStage(building) {
if(building.hasOwnProperty('current_landuse_class') && !isNullishOrEmpty(building.current_landuse_class)) {
return 'class';
} else if(building.hasOwnProperty('current_landuse_group') && !isNullishOrEmpty(building.current_landuse_group)) {
return 'group';
} else if(building.hasOwnProperty('current_landuse_order') && !isNullishOrEmpty(building.current_landuse_order)) {
return 'order';
} else return 'none';
} }

View File

@ -6,7 +6,7 @@ import { getCurrentBuildingDataById } from '../building';
import { updateLandUse } from './landUse'; import { updateLandUse } from './landUse';
export async function processBuildingUpdate(buildingId: number, buildingUpdate: any): Promise<any> { export async function processBuildingUpdate(buildingId: number, buildingUpdate: any): Promise<any> {
if(hasAnyOwnProperty(buildingUpdate, ['current_landuse_class', 'current_landuse_group', 'current_landuse_order'])) { if(hasAnyOwnProperty(buildingUpdate, ['current_landuse_group'])) {
buildingUpdate = await processCurrentLandUseClassifications(buildingId, buildingUpdate); buildingUpdate = await processCurrentLandUseClassifications(buildingId, buildingUpdate);
} }
@ -18,18 +18,14 @@ async function processCurrentLandUseClassifications(buildingId: number, building
const currentLandUseUpdate = await updateLandUse( const currentLandUseUpdate = await updateLandUse(
{ {
landUseClass: currentBuildingData.current_landuse_class,
landUseGroup: currentBuildingData.current_landuse_group, landUseGroup: currentBuildingData.current_landuse_group,
landUseOrder: currentBuildingData.current_landuse_order landUseOrder: currentBuildingData.current_landuse_order
}, { }, {
landUseClass: buildingUpdate.current_landuse_class, landUseGroup: buildingUpdate.current_landuse_group
landUseGroup: buildingUpdate.current_landuse_group,
landUseOrder: buildingUpdate.current_landuse_order
} }
); );
return Object.assign({}, buildingUpdate, { return Object.assign({}, buildingUpdate, {
current_landuse_class: currentLandUseUpdate.landUseClass,
current_landuse_group: currentLandUseUpdate.landUseGroup, current_landuse_group: currentLandUseUpdate.landUseGroup,
current_landuse_order: currentLandUseUpdate.landUseOrder, current_landuse_order: currentLandUseUpdate.landUseOrder,
}); });