Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lf 4380 api route for edit update route for edit animal and batch details #3484

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
02567b0
LF-4380 Add edit route on animals and batches
Duncan-Brain Aug 23, 2024
f0907a2
LF-4380 Make edit endpoints for controller
Duncan-Brain Aug 26, 2024
909ec4b
LF-4380 Allow specifiying enum key for tests
Duncan-Brain Sep 27, 2024
d7ec5c0
LF-4380 Add use relationships to tableCleanup
Duncan-Brain Sep 27, 2024
a9005fd
LF-4380 Add edit test and use async await on requests
Duncan-Brain Sep 27, 2024
6ecee80
LF-4380 Add tests for animal batches and add async await syntax to re…
Duncan-Brain Sep 27, 2024
6b879c9
LF-4380 Allow origin_id to be edited
Duncan-Brain Sep 27, 2024
12fd4b1
LF-4380 Add comment for delineating Remove tests
Duncan-Brain Oct 2, 2024
41e2a67
LF-4380 Move add animal functions to be reused in edit to utility fun…
Duncan-Brain Oct 2, 2024
8581fee
LF-4380 Replace code with utility function to be reused in edit, and …
Duncan-Brain Oct 2, 2024
f994fa6
LF-4380 Add utility functions to add batch and comments about groups too
Duncan-Brain Oct 2, 2024
4338df8
LF-4380 Update animals and batch edit endpoint to use types breeds an…
Duncan-Brain Oct 2, 2024
304d1ee
LF-4380 Consolidate middleware for reuse in edit endpoint
Duncan-Brain Oct 2, 2024
3e4543b
LF-4380 Extract logical checks for reuse
Duncan-Brain Oct 4, 2024
2d398ee
LF-4380 WIP - Add type and breed checks to edit and refine functions
Duncan-Brain Oct 4, 2024
7e2dfa8
LF-4380 Genericize type function
Duncan-Brain Oct 4, 2024
9690443
LF-4380 Reword check breed matched type to account for editing
Duncan-Brain Oct 4, 2024
3eb111a
LF-4380 Finish consolidating breed checks to mirror type checks
Duncan-Brain Oct 4, 2024
dddfdc2
LF-4380 Pass animalOrBatchRecord as prop, rename function, add comments
Duncan-Brain Oct 4, 2024
a4310da
LF-4380 Add sex detail check to edit and adapt logical check for exis…
Duncan-Brain Oct 4, 2024
f0b5bf2
LF-4380 Add use rleationship check to edit
Duncan-Brain Oct 7, 2024
3be3d50
LF-4380 Add null values to type and breed editing
Duncan-Brain Oct 8, 2024
be18671
LF-4380 Add animal origin check and fix logical checks
Duncan-Brain Oct 8, 2024
9b52193
LF-4380 Update comments
Duncan-Brain Oct 8, 2024
49656b7
LF-4380 Move out generic utilities to their own files
Duncan-Brain Oct 9, 2024
3b18ebc
LF-4380 Absolute freaking mystery to me why this happens...
Duncan-Brain Oct 10, 2024
77fa4a7
LF-4380 WIP test coverage up till sex and count - sex and count failing
Duncan-Brain Oct 11, 2024
9954d5f
LF-4380 Cleanup comments, small fixes based on testing, augment type_…
Duncan-Brain Oct 16, 2024
e17cbd0
LF-4380 Fix enum factories with specific db id constraints
Duncan-Brain Oct 16, 2024
87fcbea
LF-4380 Complete test coverage on animal_batches endpoint
Duncan-Brain Oct 16, 2024
ac4c320
LF-4380 Move middleware tests out of EDIT tests and comment functions
Duncan-Brain Oct 16, 2024
dcfb01c
LF-4380 Add mystery fix to animal controller
Duncan-Brain Oct 16, 2024
8d633d9
LF-4380 Copy middleware tests to animal test
Duncan-Brain Oct 16, 2024
c32864a
LF-4380 Remove unreachable test
Duncan-Brain Oct 16, 2024
2a1c525
LF-4380 Fix relation for findById function -- add comments to unreach…
Duncan-Brain Oct 16, 2024
ece3996
LF-4380 Add animal identifier middleware and test
Duncan-Brain Oct 16, 2024
b79fc72
LF-4380 Cleanup groupIds comments
Duncan-Brain Oct 17, 2024
533c8c6
LF-4380 Add licence
Duncan-Brain Oct 17, 2024
a0de1e4
LF-4380 Add fix to new breed creation logic -- add back in previously…
Duncan-Brain Oct 17, 2024
948d956
LF-4380 Simplify core existence logic remove extra functions
Duncan-Brain Oct 17, 2024
f8f8c05
LF-4380 Fix case where nulling existing breed, fix case where adding …
Duncan-Brain Oct 17, 2024
d660b06
LF-4380 Add tests for case where nulling existing breed, fix case whe…
Duncan-Brain Oct 17, 2024
d196bec
LF-4380 Revert utility object on req
Duncan-Brain Oct 18, 2024
c76bc27
LF-4380 Address PR feedback about desired keys, groupIdMap, oneTruthy…
Duncan-Brain Oct 19, 2024
595596d
LF-4380 Rename upsertGraph, checkAnimalSexDetail, preexistingAnimalOr…
Duncan-Brain Oct 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 19 additions & 18 deletions packages/api/src/controllers/animalBatchController.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,25 @@ const animalBatchController = {
const typeIdsMap = {};
const typeBreedIdsMap = {};

const desiredKeys = [
'id',
'count',
'custom_breed_id',
'custom_type_id',
'default_breed_id',
'default_type_id',
'name',
'notes',
'photo_url',
'organic_status',
'supplier',
'price',
'sex_detail',
'origin_id',
'group_ids',
'animal_batch_use_relationships',
];

// select only allowed properties to edit
for (const animalBatch of req.body) {
await checkAndAddCustomTypeAndBreed(
Expand All @@ -118,24 +137,6 @@ const animalBatchController = {
trx,
);

const desiredKeys = [
'id',
'count',
'custom_breed_id',
'custom_type_id',
'default_breed_id',
'default_type_id',
'name',
'notes',
'photo_url',
'organic_status',
'supplier',
'price',
'sex_detail',
'origin_id',
'group_ids',
'animal_batch_use_relationships',
];
const keysExisting = desiredKeys.filter((key) => key in animalBatch);
const data = _pick(animalBatch, keysExisting);
SayakaOno marked this conversation as resolved.
Show resolved Hide resolved

Expand Down
65 changes: 32 additions & 33 deletions packages/api/src/controllers/animalController.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,8 @@ const animalController = {
);

// Format group_ids
const groupIdMap =
individualAnimalResult.group_ids =
individualAnimalResult.group_ids?.map((group) => group.animal_group_id) || [];
individualAnimalResult.group_ids = groupIdMap;

result.push(individualAnimalResult);
}
Expand All @@ -115,6 +114,36 @@ const animalController = {
const typeIdsMap = {};
const typeBreedIdsMap = {};

const desiredKeys = [
'id',
'custom_breed_id',
'custom_type_id',
'default_breed_id',
'default_type_id',
'sex_id',
'name',
'birth_date',
'identifier',
'identifier_color_id',
'identifier_placement_id',
'identifier_type_id',
'identifier_type_other',
'origin_id',
'dam',
'sire',
'brought_in_date',
'weaning_date',
'notes',
'photo_url',
'organic_status',
'supplier',
'price',
'sex_detail',
'origin_id',
'group_ids',
'animal_use_relationships',
];

// select only allowed properties to edit
for (const animal of req.body) {
await checkAndAddCustomTypeAndBreed(
Expand All @@ -125,38 +154,8 @@ const animalController = {
farm_id,
trx,
);
// TODO: Comment out for animals v1?
await checkAndAddGroup(req, animal, farm_id, trx);

const desiredKeys = [
'id',
'custom_breed_id',
'custom_type_id',
'default_breed_id',
'default_type_id',
'sex_id',
'name',
'birth_date',
'identifier',
'identifier_color_id',
'identifier_placement_id',
'identifier_type_id',
'identifier_type_other',
'origin_id',
'dam',
'sire',
'brought_in_date',
'weaning_date',
'notes',
'photo_url',
'organic_status',
'supplier',
'price',
'sex_detail',
'origin_id',
'group_ids',
'animal_use_relationships',
];
await checkAndAddGroup(req, animal, farm_id, trx);

const keysExisting = desiredKeys.filter((key) => key in animal);
const data = _pick(animal, keysExisting);
Expand Down
39 changes: 21 additions & 18 deletions packages/api/src/middleware/validation/checkAnimalOrBatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import { Model, transaction } from 'objection';
import { handleObjectionError } from '../../util/errorCodes.js';
import { oneExists, oneTruthy, setFalsyValuesToNull } from '../../util/middleware.js';
import { someExists, someTruthy, setFalsyValuesToNull } from '../../util/middleware.js';
import {
customError,
checkIsArray,
Expand Down Expand Up @@ -77,13 +77,13 @@ const checkAnimalType = async (animalOrBatch, farm_id, creating = true) => {
const { default_type_id, custom_type_id, type_name } = animalOrBatch;
const typeKeyOptions = ['default_type_id', 'custom_type_id', 'type_name'];
// Skip if all undefined or editing (!creating)
if (creating || oneTruthy([default_type_id, custom_type_id, type_name])) {
if (creating || someTruthy([default_type_id, custom_type_id, type_name])) {
checkExactlyOneIsProvided(
[default_type_id, custom_type_id, type_name],
'default_type_id, custom_type_id, or type_name',
);
}
if (!creating && oneExists(typeKeyOptions, animalOrBatch)) {
if (!creating && someExists(typeKeyOptions, animalOrBatch)) {
// Overwrite with null in db if editing
setFalsyValuesToNull(typeKeyOptions, animalOrBatch);
}
Expand Down Expand Up @@ -141,7 +141,7 @@ const checkCustomBreedMatchesType = (
const typeKeyOptions = ['default_type_id', 'custom_type_id', 'type_name'];

// If not editing type, check record type
if (!oneExists(typeKeyOptions, animalOrBatch) && animalOrBatchRecord) {
if (!someExists(typeKeyOptions, animalOrBatch) && animalOrBatchRecord) {
defaultTypeId = animalOrBatchRecord.default_type_id;
customTypeId = animalOrBatchRecord.custom_type_id;
}
Expand Down Expand Up @@ -174,43 +174,43 @@ const checkAnimalBreed = async (

// Check if breed is present
if (
(creating && oneExists(breedKeyOptions, animalOrBatch)) ||
oneTruthy([default_breed_id, custom_breed_id, breed_name])
(creating && someExists(breedKeyOptions, animalOrBatch)) ||
someTruthy([default_breed_id, custom_breed_id, breed_name])
) {
checkExactlyOneIsProvided(
[default_breed_id, custom_breed_id, breed_name],
'default_breed_id, custom_breed_id, or breed_name',
);
}
// Check if breed is present
if (!creating && oneExists(breedKeyOptions, animalOrBatch)) {
if (!creating && someExists(breedKeyOptions, animalOrBatch)) {
// Overwrite with null in db if editing
setFalsyValuesToNull(breedKeyOptions, animalOrBatch);
}

if (
oneExists(breedKeyOptions, animalOrBatch) &&
!oneTruthy([default_breed_id, custom_breed_id, breed_name])
someExists(breedKeyOptions, animalOrBatch) &&
!someTruthy([default_breed_id, custom_breed_id, breed_name])
) {
// do nothing if nulling breed
} else {
// Check if default breed or default type is present
if (
(oneExists(breedKeyOptions, animalOrBatch) && default_breed_id) ||
(oneExists(typeKeyOptions, animalOrBatch) && default_type_id)
(someExists(breedKeyOptions, animalOrBatch) && default_breed_id) ||
(someExists(typeKeyOptions, animalOrBatch) && default_type_id)
) {
await checkDefaultBreedMatchesType(animalOrBatchRecord, default_breed_id, default_type_id);
}
// Check if custom breed or custom type is present
if (
(oneExists(breedKeyOptions, animalOrBatch) && custom_breed_id && !type_name) ||
(oneExists(typeKeyOptions, animalOrBatch) &&
(someExists(breedKeyOptions, animalOrBatch) && custom_breed_id && !type_name) ||
(someExists(typeKeyOptions, animalOrBatch) &&
(default_type_id || custom_type_id) &&
!breed_name)
) {
let customBreed;
// Find customBreed if exists
if (oneExists(breedKeyOptions, animalOrBatch) && custom_breed_id) {
if (someExists(breedKeyOptions, animalOrBatch) && custom_breed_id) {
checkIdIsNumber(custom_breed_id);
customBreed = await CustomAnimalBreedModel.query()
.whereNotDeleted()
Expand Down Expand Up @@ -296,7 +296,7 @@ const checkAnimalUseRelationship = async (animalOrBatch, animalOrBatchKey) => {

const checkAnimalOrigin = async (animalOrBatch, creating = true) => {
const { origin_id, brought_in_date } = animalOrBatch;
if (oneExists(['origin_id', 'brought_in_date'], animalOrBatch)) {
if (someExists(['origin_id', 'brought_in_date'], animalOrBatch)) {
const broughtInOrigin = await AnimalOriginModel.query().where({ key: 'BROUGHT_IN' }).first();
// Overwrite date with null in db if editing origin_id
if (!creating && origin_id != broughtInOrigin.id) {
Expand All @@ -312,7 +312,7 @@ const checkAnimalOrigin = async (animalOrBatch, creating = true) => {
const checkAnimalIdentifier = async (animalOrBatch, animalOrBatchKey, creating = true) => {
if (animalOrBatchKey === 'animal') {
const { identifier_type_id, identifier_type_other } = animalOrBatch;
if (oneExists(['identifier_type_id', 'identifier_type_other'], animalOrBatch)) {
if (someExists(['identifier_type_id', 'identifier_type_other'], animalOrBatch)) {
const otherIdentifier = await AnimalIdentifierType.query().where({ key: 'OTHER' }).first();
// Overwrite date with null in db if editing origin_id
if (!creating && identifier_type_id != otherIdentifier.id) {
Expand Down Expand Up @@ -344,7 +344,10 @@ const checkAndAddCustomTypesOrBreeds = (
let defaultBreedId = default_breed_id;
let customBreedId = custom_breed_id;

if (!oneExists(['default_breed_id', 'custom_breed_id'], animalOrBatch) && animalOrBatchRecord) {
if (
!someExists(['default_breed_id', 'custom_breed_id'], animalOrBatch) &&
animalOrBatchRecord
) {
defaultBreedId = animalOrBatchRecord.default_breed_id;
customBreedId = animalOrBatchRecord.custom_breed_id;
}
Expand All @@ -361,7 +364,7 @@ const checkAndAddCustomTypesOrBreeds = (
let defaultTypeId = default_type_id;
let customTypeId = custom_type_id;

if (!oneExists(['default_type_id', 'custom_type_id'], animalOrBatch) && animalOrBatchRecord) {
if (!someExists(['default_type_id', 'custom_type_id'], animalOrBatch) && animalOrBatchRecord) {
defaultTypeId = animalOrBatchRecord.default_type_id;
customTypeId = animalOrBatchRecord.custom_type_id;
}
Expand Down
10 changes: 6 additions & 4 deletions packages/api/src/util/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@ export const notExactlyOneValue = (values) => {
};

// Checks an array of object keys against object -- at least one of the properties is defined
export const oneExists = (keys, object) => {
export const someExists = (keys, object) => {
return keys.some((key) => key in object);
};

// Checks an array for at least one truthy value
export const oneTruthy = (values) => values.some((value) => !!value);
export const someTruthy = (values) => values.some((value) => !!value);

// Sets falsy values to null for editing values that may have values for exclusive constraints -- does not handle zeros yet
// Sets falsy values to null for editing values that may have values for exclusive constraints
export const setFalsyValuesToNull = (array, obj) => {
for (const val of array) {
obj[val] = obj[val] || null;
if (obj[val] !== 0 && obj[val] !== false) {
obj[val] ??= null;
Duncan-Brain marked this conversation as resolved.
Show resolved Hide resolved
}
}
};
Loading