diff --git a/lib/api/address/models.js b/lib/api/address/models.js index 1aca0ce5..c5029fec 100644 --- a/lib/api/address/models.js +++ b/lib/api/address/models.js @@ -1,42 +1,46 @@ import {Op} from 'sequelize' import {Address} from '../../util/sequelize.js' -export async function getAddress(addressID) { - return Address.findByPk(addressID, {raw: true}) -} +export const getAddress = addressID => Address.findByPk(addressID, {raw: true}) -export async function getAddresses(addressIDs) { - return Address.findAll({where: {id: addressIDs}, raw: true}) -} +export const getAddresses = addressIDs => Address.findAll({where: {id: addressIDs}, raw: true}) -export async function getAllAddressIDsFromDistrict(districtID) { +export const getAllAddressIDsFromDistrict = async districtID => { const addresses = await Address.findAll({where: {districtID}, attributes: ['id'], raw: true}) return addresses.map(address => address.id) } -export async function getAllAddressIDsOutsideDistrict(addressIDs, districtID) { +export const getAllAddressIDsOutsideDistrict = async (addressIDs, districtID) => { const addresses = await Address.findAll({where: {id: addressIDs, districtID: {[Op.ne]: districtID}}, attributes: ['id'], raw: true}) return addresses.map(address => address.id) } -export async function setAddresses(addresses) { - return Address.bulkCreate(addresses) -} +export const setAddresses = addresses => Address.bulkCreate(addresses) -export async function updateAddresses(addresses) { +export const updateAddresses = async addresses => { const bulkOperations = addresses.map(address => Address.update(address, {where: {id: address.id}})) return Promise.all(bulkOperations) } -export async function deleteAddress(addressID) { - return Address.destroy({where: {id: addressID}}) -} +export const patchAddresses = async addresses => { + const bulkOperations = addresses.map(async address => { + // Separate meta from the rest of the object to process the update separately + const {meta, ...addressRest} = address + const addressID = address.id + const addressDB = await Address.findByPk(addressID) + addressDB.set(addressRest) + addressDB.meta = {...addressDB.meta, ...meta} + return addressDB.save() + }) -export async function deleteAddresses(addressIDs) { - return Address.destroy({where: {id: addressIDs}}) + return Promise.all(bulkOperations) } -export async function getAllDistrictIDsFromAddresses(addressIDs) { +export const deleteAddress = addressID => Address.destroy({where: {id: addressID}}) + +export const deleteAddresses = addressIDs => Address.destroy({where: {id: addressIDs}}) + +export const getAllDistrictIDsFromAddresses = async addressIDs => { const addresses = await Address.findAll({where: {id: addressIDs}, attributes: ['districtID'], raw: true}) return addresses.map(address => address.districtID) } diff --git a/lib/api/address/schema.js b/lib/api/address/schema.js index ffd097d4..a52d4817 100644 --- a/lib/api/address/schema.js +++ b/lib/api/address/schema.js @@ -15,29 +15,36 @@ const metaSchema = object({ export const banAddressSchema = object({ id: banID.required(), - mainCommonToponymID: banID.required(), + mainCommonToponymID: banID.when('$isPatch', { + is: true, + otherwise: schema => schema.required(), + }), secondaryCommonToponymIDs: array().of(banID), - districtID: banID.required(), - number: number().positive().integer().required(), + districtID: banID.when('$isPatch', { + is: true, + otherwise: schema => schema.required(), + }), + number: number().positive().integer().when('$isPatch', { + is: true, + otherwise: schema => schema.required(), + }), suffix: string().trim(), - labels: array().of(labelSchema), + labels: array().of(labelSchema).when('$isPatch', { + is: true, + // eslint-disable-next-line unicorn/no-thenable + then: schema => schema.default(null).nullable(), + }), certified: boolean(), - positions: array().of(positionSchema).required(), - updateDate: date().required(), - meta: metaSchema -}).noUnknown() - -export const banAddressSchemaForPatch = object({ - id: banID.required(), - mainCommonToponymID: banID, - secondaryCommonToponymIDs: array().of(banID), - districtID: banID, - number: number().positive().integer(), - suffix: string().trim(), - labels: array().of(labelSchema).default(null).nullable(), - certified: boolean().default(false), - positions: array().of(positionSchema).default(null).nullable(), - updateDate: date(), + positions: array().of(positionSchema).when('$isPatch', { + is: true, + // eslint-disable-next-line unicorn/no-thenable + then: schema => schema.default(null).nullable(), + otherwise: schema => schema.required(), + }), + updateDate: date().when('$isPatch', { + is: true, + otherwise: schema => schema.required(), + }), meta: metaSchema }).noUnknown() diff --git a/lib/api/address/utils.js b/lib/api/address/utils.js index 35b1e3b5..a21ad460 100644 --- a/lib/api/address/utils.js +++ b/lib/api/address/utils.js @@ -1,7 +1,7 @@ import {checkDataFormat, dataValidationReportFrom, checkIdsIsUniq, checkIdsIsVacant, checkIdsIsAvailable, checkDataShema, checkIdsShema, checkIfCommonToponymsExist, checkIfDistrictsExist} from '../helper.js' import {banID} from '../schema.js' import {getAddresses, getAllAddressIDsFromDistrict, getAllAddressIDsOutsideDistrict} from './models.js' -import {banAddressSchema, banAddressSchemaForPatch} from './schema.js' +import {banAddressSchema} from './schema.js' const getExistingAddressIDs = async addressIDs => { const existingAddresses = await getAddresses(addressIDs) @@ -50,24 +50,6 @@ export const checkAddressesRequest = async (addresses, actionType) => { switch (actionType) { case 'insert': case 'update': - report = checkDataFormat( - `The request require an Array of address but receive ${typeof addresses}`, - 'No address send to job', - addresses - ) - || await checkAddressesIDsRequest(addresses.map(address => address.id), actionType, false) - || await checkDataShema('Invalid format', addresses, banAddressSchema) - || await checkIfCommonToponymsExist(addresses.reduce((acc, {mainCommonToponymID, secondaryCommonToponymIDs}) => { - const ids = [mainCommonToponymID] - if (secondaryCommonToponymIDs) { - ids.push(...secondaryCommonToponymIDs) - } - - return [...acc, ...ids] - }, [])) - || await checkIfDistrictsExist(addresses.map(({districtID}) => districtID)) - || dataValidationReportFrom(true) - break case 'patch': report = checkDataFormat( `The request require an Array of address but receive ${typeof addresses}`, @@ -75,7 +57,7 @@ export const checkAddressesRequest = async (addresses, actionType) => { addresses ) || await checkAddressesIDsRequest(addresses.map(address => address.id), actionType, false) - || await checkDataShema('Invalid format', addresses, banAddressSchemaForPatch) + || await checkDataShema('Invalid format', addresses, banAddressSchema, {isPatch: actionType === 'patch'}) || await checkIfCommonToponymsExist(addresses.reduce((acc, {mainCommonToponymID, secondaryCommonToponymIDs}) => { const ids = mainCommonToponymID ? [mainCommonToponymID] : [] if (secondaryCommonToponymIDs) { diff --git a/lib/api/common-toponym/models.js b/lib/api/common-toponym/models.js index 49c5a16b..d0beb31a 100644 --- a/lib/api/common-toponym/models.js +++ b/lib/api/common-toponym/models.js @@ -1,48 +1,52 @@ import {Op} from 'sequelize' import {CommonToponym} from '../../util/sequelize.js' -export async function getCommonToponym(commonToponymID) { - return CommonToponym.findByPk(commonToponymID, {raw: true}) -} +export const getCommonToponym = commonToponymID => CommonToponym.findByPk(commonToponymID, {raw: true}) -export async function getCommonToponyms(commonToponymIDs) { - return CommonToponym.findAll({where: {id: commonToponymIDs}, raw: true}) -} +export const getCommonToponyms = commonToponymIDs => CommonToponym.findAll({where: {id: commonToponymIDs}, raw: true}) -export async function getAllCommonToponymIDsFromDistrict(districtID) { +export const getAllCommonToponymIDsFromDistrict = async districtID => { const commonToponyms = await CommonToponym.findAll( {where: {districtID}, attributes: ['id'], raw: true} ) return commonToponyms.map(commonToponym => commonToponym.id) } -export async function getAllCommonToponymIDsOutsideDistrict(commonToponymIDs, districtID) { +export const getAllCommonToponymIDsOutsideDistrict = async (commonToponymIDs, districtID) => { const commonToponyms = await CommonToponym.findAll( {where: {id: commonToponymIDs, districtID: {[Op.ne]: districtID}}, attributes: ['id'], raw: true} ) return commonToponyms.map(commonToponym => commonToponym.id) } -export async function setCommonToponyms(commonToponyms) { - return CommonToponym.bulkCreate(commonToponyms) -} +export const setCommonToponyms = commonToponyms => CommonToponym.bulkCreate(commonToponyms) -export async function updateCommonToponyms(commonToponyms) { +export const updateCommonToponyms = commonToponyms => { const bulkOperations = commonToponyms.map(commonToponym => CommonToponym.update(commonToponym, {where: {id: commonToponym.id}}) ) return Promise.all(bulkOperations) } -export async function deleteCommonToponym(commonToponymID) { - return CommonToponym.destroy({where: {id: commonToponymID}}) -} +export const patchCommonToponyms = async commonToponyms => { + const bulkOperations = commonToponyms.map(async commonToponym => { + // Separate meta from the rest of the object to process the update separately + const {meta, ...commonToponymRest} = commonToponym + const commonToponymID = commonToponym.id + const commonToponymDB = await CommonToponym.findByPk(commonToponymID) + commonToponymDB.set(commonToponymRest) + commonToponymDB.meta = {...commonToponymDB.meta, ...meta} + return commonToponymDB.save() + }) -export async function deleteCommonToponyms(commonToponymIDs) { - return CommonToponym.destroy({where: {id: commonToponymIDs}}) + return Promise.all(bulkOperations) } -export async function getAllDistrictIDsFromCommonToponyms(commonToponymIDs) { +export const deleteCommonToponym = commonToponymID => CommonToponym.destroy({where: {id: commonToponymID}}) + +export const deleteCommonToponyms = commonToponymIDs => CommonToponym.destroy({where: {id: commonToponymIDs}}) + +export const getAllDistrictIDsFromCommonToponyms = async commonToponymIDs => { const commonToponyms = await CommonToponym.findAll({where: {id: commonToponymIDs}, attributes: ['districtID'], raw: true}) return commonToponyms.map(commonToponym => commonToponym.districtID) } diff --git a/lib/api/common-toponym/schema.js b/lib/api/common-toponym/schema.js index c039eb4e..451e2178 100644 --- a/lib/api/common-toponym/schema.js +++ b/lib/api/common-toponym/schema.js @@ -8,19 +8,21 @@ const metaSchema = object({ export const banCommonToponymSchema = object({ id: banID.required(), - districtID: banID.required(), - labels: array().of(labelSchema).required(), + districtID: banID.when('$isPatch', { + is: true, + otherwise: schema => schema.required(), + }), + labels: array().of(labelSchema).when('$isPatch', { + is: true, + // eslint-disable-next-line unicorn/no-thenable + then: schema => schema.default(null).nullable(), + otherwise: schema => schema.required(), + }), geometry: geometrySchema.default(null).nullable(), - updateDate: date().required(), - meta: metaSchema -}).noUnknown() - -export const banCommonToponymSchemaForPatch = object({ - id: banID.required(), - districtID: banID, - labels: array().of(labelSchema).default(null).nullable(), - geometry: geometrySchema.default(null).nullable(), - updateDate: date(), + updateDate: date().when('$isPatch', { + is: true, + otherwise: schema => schema.required(), + }), meta: metaSchema }).noUnknown() diff --git a/lib/api/common-toponym/utils.js b/lib/api/common-toponym/utils.js index 3076ff8b..d088eed9 100644 --- a/lib/api/common-toponym/utils.js +++ b/lib/api/common-toponym/utils.js @@ -1,7 +1,7 @@ import {checkDataFormat, dataValidationReportFrom, checkIdsIsUniq, checkIdsIsVacant, checkIdsIsAvailable, checkDataShema, checkIdsShema, checkIfDistrictsExist} from '../helper.js' import {banID} from '../schema.js' import {getCommonToponyms, getAllCommonToponymIDsFromDistrict, getAllCommonToponymIDsOutsideDistrict} from './models.js' -import {banCommonToponymSchema, banCommonToponymSchemaForPatch} from './schema.js' +import {banCommonToponymSchema} from './schema.js' const getExistingCommonToponymIDs = async commonToponymIDs => { const existingCommonToponyms = await getCommonToponyms(commonToponymIDs) @@ -50,16 +50,6 @@ export const checkCommonToponymsRequest = async (commonToponyms, actionType) => switch (actionType) { case 'insert': case 'update': - report = checkDataFormat( - `The request require an Array of common toponym but receive ${typeof commonToponyms}`, - 'No common toponym send to job', - commonToponyms - ) - || await checkCommonToponymsIDsRequest(commonToponyms.map(commonToponym => commonToponym.id), actionType, false) - || await checkDataShema('Invalid common toponym format', commonToponyms, banCommonToponymSchema) - || await checkIfDistrictsExist(commonToponyms.map(({districtID}) => districtID)) - || dataValidationReportFrom(true) - break case 'patch': report = checkDataFormat( `The request require an Array of common toponym but receive ${typeof commonToponyms}`, @@ -67,7 +57,7 @@ export const checkCommonToponymsRequest = async (commonToponyms, actionType) => commonToponyms ) || await checkCommonToponymsIDsRequest(commonToponyms.map(commonToponym => commonToponym.id), actionType, false) - || await checkDataShema('Invalid common toponym format', commonToponyms, banCommonToponymSchemaForPatch) + || await checkDataShema('Invalid common toponym format', commonToponyms, banCommonToponymSchema, {isPatch: actionType === 'patch'}) || await checkIfDistrictsExist(commonToponyms.reduce((acc, {districtID}) => { if (districtID) { return [...acc, districtID] diff --git a/lib/api/consumers/api-consumer.js b/lib/api/consumers/api-consumer.js index 5a0803f1..3d1a3c3f 100644 --- a/lib/api/consumers/api-consumer.js +++ b/lib/api/consumers/api-consumer.js @@ -1,10 +1,10 @@ import queue from '../../util/queue.cjs' import {getJobStatus, setJobStatus} from '../job-status/models.js' -import {setAddresses, updateAddresses, deleteAddresses, getAllDistrictIDsFromAddresses} from '../address/models.js' +import {setAddresses, updateAddresses, patchAddresses, deleteAddresses, getAllDistrictIDsFromAddresses} from '../address/models.js' import {checkAddressesRequest, checkAddressesIDsRequest} from '../address/utils.js' -import {setCommonToponyms, updateCommonToponyms, deleteCommonToponyms, getAllDistrictIDsFromCommonToponyms} from '../common-toponym/models.js' +import {setCommonToponyms, updateCommonToponyms, patchCommonToponyms, deleteCommonToponyms, getAllDistrictIDsFromCommonToponyms} from '../common-toponym/models.js' import {checkCommonToponymsRequest, checkCommonToponymsIDsRequest} from '../common-toponym/utils.js' -import {setDistricts, updateDistricts, deleteDistricts} from '../district/models.js' +import {setDistricts, updateDistricts, patchDistricts, deleteDistricts} from '../district/models.js' import {checkDistrictsRequest, checkDistrictsIDsRequest} from '../district/utils.js' import {dataValidationReportFrom, formatObjectWithDefaults, addOrUpdateJob} from '../helper.js' import {addressDefaultOptionalValues} from '../address/schema.js' @@ -93,7 +93,7 @@ const addressConsumer = async (jobType, payload, statusID) => { } case 'patch': - await updateAddresses(payload) + await patchAddresses(payload) break case 'delete': await deleteAddresses(payload) @@ -155,7 +155,7 @@ const commonToponymConsumer = async (jobType, payload, statusID) => { } case 'patch': - await updateCommonToponyms(payload) + await patchCommonToponyms(payload) break case 'delete': await deleteCommonToponyms(payload) @@ -217,7 +217,7 @@ const districtConsumer = async (jobType, payload, statusID) => { } case 'patch': - await updateDistricts(payload) + await patchDistricts(payload) break case 'delete': await deleteDistricts(payload) diff --git a/lib/api/district/models.js b/lib/api/district/models.js index 1777b2d3..b65a58e9 100644 --- a/lib/api/district/models.js +++ b/lib/api/district/models.js @@ -1,30 +1,32 @@ import {District} from '../../util/sequelize.js' -export async function getDistrict(districtID) { - return District.findByPk(districtID, {raw: true}) -} +export const getDistrict = districtID => District.findByPk(districtID, {raw: true}) -export async function getDistricts(districtIDs) { - return District.findAll({where: {id: districtIDs}, raw: true}) -} +export const getDistricts = districtIDs => District.findAll({where: {id: districtIDs}, raw: true}) -export async function getDistrictsFromCog(cog) { - return District.findAll({where: {meta: {insee: {cog}}}, raw: true}) -} +export const getDistrictsFromCog = cog => District.findAll({where: {meta: {insee: {cog}}}, raw: true}) -export async function setDistricts(districts) { - return District.bulkCreate(districts) -} +export const setDistricts = districts => District.bulkCreate(districts) -export async function updateDistricts(districts) { +export const updateDistricts = districts => { const promises = districts.map(district => District.update(district, {where: {id: district.id}})) return Promise.all(promises) } -export async function deleteDistrict(districtID) { - return District.destroy({where: {id: districtID}}) -} +export const patchDistricts = async districts => { + const bulkOperations = districts.map(async district => { + // Separate meta from the rest of the object to process the update separately + const {meta, ...districtRest} = district + const districtID = district.id + const districtDB = await District.findByPk(districtID) + districtDB.set(districtRest) + districtDB.meta = {...districtDB.meta, ...meta} + return districtDB.save() + }) -export async function deleteDistricts(districtIDs) { - return District.destroy({where: {id: districtIDs}}) + return Promise.all(bulkOperations) } + +export const deleteDistrict = districtID => District.destroy({where: {id: districtID}}) + +export const deleteDistricts = districtIDs => District.destroy({where: {id: districtIDs}}) diff --git a/lib/api/district/schema.js b/lib/api/district/schema.js index 806db9c6..963ed09a 100644 --- a/lib/api/district/schema.js +++ b/lib/api/district/schema.js @@ -1,10 +1,17 @@ -import {object, array, date, bool} from 'yup' -import {banID, labelSchema, inseeSchema, balSchema} from '../schema.js' +import {object, string, array, date, bool} from 'yup' +import {banID, labelSchema, balSchema} from '../schema.js' const configSchema = object({ useBanId: bool() }).noUnknown() +const inseeSchema = object({ + cog: string().trim().length(5).when('$isPatch', { + is: true, + otherwise: schema => schema.required(), + }), +}) + const metaSchema = object({ insee: inseeSchema, bal: balSchema, @@ -12,19 +19,23 @@ const metaSchema = object({ export const banDistrictSchema = object({ id: banID.required(), - labels: array().of(labelSchema).required(), - updateDate: date().required(), - config: configSchema, - meta: metaSchema -}).noUnknown() - -export const banDistrictSchemaForPatch = object({ - id: banID.required(), - labels: array().of(labelSchema).default(null).nullable(), - updateDate: date(), + labels: array().of(labelSchema).when('$isPatch', { + is: true, + // eslint-disable-next-line unicorn/no-thenable + then: schema => schema.default(null).nullable(), + otherwise: schema => schema.required(), + }), + updateDate: date().when('$isPatch', { + is: true, + otherwise: schema => schema.required(), + }), config: configSchema, - meta: metaSchema.default(null).nullable(), -}).noUnknown() + meta: metaSchema.when('$isPatch', { + is: true, + // eslint-disable-next-line unicorn/no-thenable + then: schema => schema.default(null).nullable(), + }), +}) export const districtDefaultOptionalValues = { config: undefined, diff --git a/lib/api/district/utils.js b/lib/api/district/utils.js index 031ba64e..6e60c5b1 100644 --- a/lib/api/district/utils.js +++ b/lib/api/district/utils.js @@ -1,7 +1,7 @@ import {checkDataFormat, dataValidationReportFrom, checkIdsIsUniq, checkIdsIsVacant, checkIdsIsAvailable, checkDataShema, checkIdsShema} from '../helper.js' import {banID} from '../schema.js' import {getDistricts} from './models.js' -import {banDistrictSchema, banDistrictSchemaForPatch} from './schema.js' +import {banDistrictSchema} from './schema.js' const getExistingDistrictIDs = async districtIDs => { const existingDistricts = await getDistricts(districtIDs) @@ -57,7 +57,7 @@ export const checkDistrictsRequest = async (districts, actionType) => { districts ) || await checkDistrictsIDsRequest(districts.map(district => district.id), actionType, false) - || await checkDataShema('Invalid format', districts, actionType === 'patch' ? banDistrictSchemaForPatch : banDistrictSchema) + || await checkDataShema('Invalid format', districts, banDistrictSchema, {isPatch: actionType === 'patch'}) || dataValidationReportFrom(true) break default: diff --git a/lib/api/helper.js b/lib/api/helper.js index 0c138ca3..cab8ce01 100644 --- a/lib/api/helper.js +++ b/lib/api/helper.js @@ -75,9 +75,9 @@ export const checkIdsShema = async (err = 'Invalid IDs format', ids, schema) => } } -const dataSchemaValidation = async (data, schema) => { +const dataSchemaValidation = async (data, schema, context) => { try { - await schema.validate(data, {strict: true, abortEarly: false}) + await schema.validate(data, {strict: true, abortEarly: false, context}) } catch (error) { return dataValidationReportFrom(false, `Invalid data format (id: ${data.id})`, error.errors) } @@ -85,8 +85,8 @@ const dataSchemaValidation = async (data, schema) => { return dataValidationReportFrom(true) } -export const checkDataShema = async (err = 'Invalid data format', dataArr, schema) => { - const dataArrValidationPromises = dataArr.map(data => dataSchemaValidation(data, schema)) +export const checkDataShema = async (err = 'Invalid data format', dataArr, schema, context) => { + const dataArrValidationPromises = dataArr.map(data => dataSchemaValidation(data, schema, context)) const dataArrValidation = await Promise.all(dataArrValidationPromises) const invalidDataArr = dataArrValidation.filter(({isValid}) => !isValid) if (invalidDataArr.length > 0) { diff --git a/lib/api/schema.js b/lib/api/schema.js index 5b5d350e..fbdd8660 100644 --- a/lib/api/schema.js +++ b/lib/api/schema.js @@ -12,10 +12,6 @@ export const geometrySchema = object({ coordinates: array().length(2).of(number()).required(), }).noUnknown() -export const inseeSchema = object({ - cog: string().trim().length(5).required() -}).noUnknown() - export const cadastreSchema = object({ ids: array().of(string().trim()) }).noUnknown()