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-4121 Create PATCH endpoints for animals and batches #3137

Merged

Conversation

kathyavini
Copy link
Collaborator

@kathyavini kathyavini commented Feb 21, 2024

Description

This adds PATCH endpoints and tests specifically for handling animal/animal batch removal. Other properties cannot be updated via this iteration of controller.

Notes:

  • The "removed" property was removed following PR LF-4120 Add fields to animal and animal batch tables #3129 discussion. An animal/animal batch should be considered "removed" if it has a animal_removal_reason_id
  • This is a removal PATCH and not a general PATCH. I also left an explanatory comment in the code, but many more constraints would have to be handled to make this a general PATCH; I recommend doing that when editing an animal/batch has been refined and brought into scope

Jira link: https://lite-farm.atlassian.net/browse/LF-4121

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

  • Passes test case
  • UI components visually reviewed on desktop view
  • UI components visually reviewed on mobile view
  • Other (please explain)

Checklist:

  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • The precommit and linting ran successfully
  • I have added or updated language tags for text that's part of the UI
  • I have added "MISSING" for all new language tags to languages I don't speak
  • I have added the GNU General Public License to all new files

Also added a note about hasFarmAccess for discussion; it should not be used here
It was just for discussion this morning and has been discussed :)
Animal and animal batch had too many string properties to handle them one by one in the controller as had been the previous pattern. I also don't think strings were being trimmed on insert (add controllers) and this will handle that as well.
Realized in reading the test cases that a more general patch would need to restate all of the various checks in addAnimal, which should be handled in some other way when those requirements have been scoped.
Add logging to function since this is required for Sentry reporting
…POST

Database check is enforced and already handled by error handler
@kathyavini kathyavini added the enhancement New feature or request label Feb 21, 2024
@kathyavini kathyavini self-assigned this Feb 21, 2024
@kathyavini kathyavini requested review from a team as code owners February 21, 2024 23:51
@kathyavini kathyavini requested review from antsgar and SayakaOno and removed request for a team February 21, 2024 23:51
@kathyavini kathyavini marked this pull request as draft February 21, 2024 23:51
await super.$beforeUpdate(opt, queryContext);
this.trimStringProperties();
}

Copy link
Collaborator Author

@kathyavini kathyavini Feb 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this here because:

  1. There were too many string properties on this table to check individually in the controller. I added it originally for a general PATCH, but right now it will apply to the POST endpoints and the single string property -- removal_explanation of PATCH
  2. String trimming had not been included on the POST endpoint. Since addAnimals() is already a pretty lengthy controller, I did not want to add to it.

Curious if people like this pattern enough to generalize it; for now it is just on these two models.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a great idea!

@kathyavini kathyavini changed the base branch from integration to LF-4120-add-fields-to-animal-and-animal-batch-tables February 22, 2024 00:00
@@ -268,19 +268,4 @@ export default {

return query.first();
},

Copy link
Collaborator Author

@kathyavini kathyavini Feb 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted from baseController to general /utils so it could be used in models as well. Also upon reflection it never needed to be in a controller to start with.

@kathyavini kathyavini changed the title LF-4121 Create PATH endpoints for animals and batches LF-4121 Create PATCH endpoints for animals and batches Feb 22, 2024
This is much easier than messing with the JSON schema validator
const sentAnimalIds = req.body.map(({ id }) => id);
const invalidAnimalIds = [];

for (const id of sentAnimalIds) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to loop req.body as you did on line 206.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh, then there is no need to create a new array!

Comment on lines 31 to 33
return Object.entries(this.jsonSchema.properties)
.filter(([, value]) => value.type.includes('string'))
.map(([key]) => key);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to do filter and map in a for loop for better performance.

Copy link
Collaborator Author

@kathyavini kathyavini Feb 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the improvement in performance be specifically from using for...of, or from not having to iterate two times? For instance, if the filter and map were combined in a reduce instead?

  static get stringProperties() {
    return Object.entries(this.jsonSchema.properties).reduce((acc, [key, value]) => {
      if (value.type.includes('string')) {
        acc.push(key);
      }
      return acc;
    }, []);
  }

I've love to read about method efficiency, do you know of a good source?

Edit: Or I guess you didn't specify for...of! So it could also be

  static get stringProperties() {
    let stringProperties = [];
    for (const key in this.jsonSchema.properties) {
      if (this.jsonSchema.properties[key].type.includes('string')) {
        stringProperties.push(key);
      }
    }
    return stringProperties;
  }

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry! It seems better to prioritize readability, so no changes are necessary!

My suggestion was to make the array in one loop instead of two.
I know reduce() and looping an object keys (for in) are not great in terms of performance, but I can't find good sources...
I learned data structure from a Udemy course, but I will share other resources when I find!

If I were to do it in one loop, I would do:

const stringProperties = [];
for (const [key, value] of Object.entries(this.jsonSchema.properties)) {
    if (value.type.includes('string')) {
        stringProperties.push(key);
    }
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like your version the best!

I'm happy to hear reduce is not good for performance because in my opinion it's also not nice for readability 😂

await super.$beforeUpdate(opt, queryContext);
this.trimStringProperties();
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a great idea!

@kathyavini kathyavini changed the base branch from LF-4120-add-fields-to-animal-and-animal-batch-tables to integration February 23, 2024 22:11
@kathyavini kathyavini marked this pull request as ready for review February 23, 2024 22:12
@kathyavini kathyavini marked this pull request as draft February 23, 2024 22:14
@kathyavini kathyavini changed the base branch from integration to LF-4120-add-fields-to-animal-and-animal-batch-tables February 23, 2024 22:15
@kathyavini kathyavini changed the base branch from LF-4120-add-fields-to-animal-and-animal-batch-tables to integration February 26, 2024 17:24
@kathyavini kathyavini marked this pull request as ready for review February 26, 2024 17:24
@kathyavini kathyavini requested a review from SayakaOno February 26, 2024 17:25
Copy link
Collaborator

@SayakaOno SayakaOno left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that removed is removed, I feel like we should have separate routes for removal. It feels unstable to rely on just animal_removal_reason_id which is optional...

  • I was referring to taskController, we should probably check if animals/batches have been already removed?
  • I can't remember, but did we need timestamp for removal...?

@kathyavini
Copy link
Collaborator Author

@SayakaOno

  1. By unstable, do you mean it feels like it would be easier to remove by accident vs having to send the two properties removed and animal_removal_reason_id together, or use a dedicated route like /animals/remove?ids=1,2,3? Or is it something else? I don't think animal_removal_removal_reason_id is optional -- by definition if it's not given, the animal is not removed, so it's implicitly non-optional -- or am I missing something there? Either way I like the dedicated route idea because it leaves the base PATCH open for edit. However, it's a little less RESTful. @antsgar what do you think?
  2. I just asked Anto this question in Jira a bit ago for DELETE... I saw in finances we error for deleting an already deleted expense/revenue type, but permit it for sale/expense 🤷 In the current controller it would be functionally equivalent to updating the removal reason, which I didn't really see a reason to prohibit, but we definitely could.
  3. Hmmm, do you mean like the abandoned_date in tasks? It wasn't included any of the tickets but I wonder if we will need it for historical tracking. But abandoned_date is user-supplied in the task abandonment flow, and a date is not part of the UI here (we could just timestamp, but as always users might be reporting the removal at a different time than the removal occurred!) So gosh, I don't know 😅

@antsgar can you remind me if we have had any discussion of how removed + timeline are going to interact? It seems that we would need a removal date, which means the user has to supply it?

@SayakaOno
Copy link
Collaborator

@kathyavini

  1. When we had removed, I understood that the route is for removal when removed was passed from clients. If the client wanted to use the route to remove animals, it needed to pass both removed and animal_removal_reason_id. But now, client's intention is less clear, so the client can add removal_explanation without animal_removal_reason_id, and inconsistency occurs in the DB. (now we can use animal_removal_reason_id as the flag though) Probably removal_explanation should be ignored without animal_removal_reason_id, but the check will be more complicated than when having two routes. Also, if I am not mistaken, tests don't prove that the API call for removal removes the animals... tests for general patch API requests could be the same, which is my other concern.
  2. 👍
  3. I'm not sure what it should be, but I will just link the doc.

@kathyavini
Copy link
Collaborator Author

kathyavini commented Feb 26, 2024

@SayakaOno uh oh, there it is then

Screenshot 2024-02-26 at 1 34 22 PM

Probably should have been caught at grooming or planning that the date didn't make it into the UI 😦 Time to check back in with @loicsans and @antsgar

Edit: Although on second read I guess it could equally be that we create a date but don't show it until some future screen / flow and that is what the table cell refers to 🤔

@antsgar
Copy link
Collaborator

antsgar commented Feb 27, 2024

@kathyavini ah that's my bad, I didn't notice the date thing! We'd probably need to add a removed_date field to the tables then. My assumption is that this doesn't mean a date for when the animal actually died / got sold / etc, but the timestamp for when it was registered as removed in the UI, which would mean we wouldn't have an actual UI field to fill it in but just set it when the record is marked as deleted, but could we double check with Loïc just in case?

@kathyavini
Copy link
Collaborator Author

kathyavini commented Feb 28, 2024

Some updates following group discussion:

  • The lack of dates was a true error throughout the flow; thank you for caching @SayakaOno! The fullstack change to add date handling (from component down to necessary database migration) is now a subtask at the end of the removal story
  • Team was true neutral about the handling of re-removal. Most likely reason + date will both be modifiable on the same future screen so we will not want to prohibit PATCH on an existing animal_removal_reason_id, if that proves untrue in the future, a property check is easily added to the invalid ids loop
  • In general (and perhaps with some mild remaining team reservations), we are proceeding with treating the animal_removal_removal_id as any other PATCH-able property. Because it is such an important property, I have added database checks on it to the test suite.

@SayakaOno
Copy link
Collaborator

Are we going to allow removal_explanation to be added without animal_removal_reason_id?

@kathyavini
Copy link
Collaborator Author

@SayakaOno we discussed yesterday (sorry maybe we should have repeated today) that we pretty much don't care about that property, since it is true optional and we will only display it in client when there is a removal reason id provided (i.e. when the animal is removed). Obviously the client also won't send a PATCH request where it the explanation is present without reason id, but is there another situation to be concerned about?

Copy link
Collaborator

@SayakaOno SayakaOno left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kathyavini No, I can compromise. When looking at the data, I would wonder why active animals have removal_explanation. I'm thinking about not only the webapp but also postman and external API users (which we probably don't need to think about anymore) as its clients.

@kathyavini
Copy link
Collaborator Author

@SayakaOno got it, that makes sense.

Well we have to migrate on the table anyway for date, so I like the idea of a constraint in that same migration for explanation. @antsgar may I add that to the date ticket if that sounds right -- that db table should never show date or explanation if the animal is currently active?

(I also feel like we just came full circle from removing db checks in the previous backend ticket 😅)

@antsgar
Copy link
Collaborator

antsgar commented Feb 29, 2024

@kathyavini yeah that sounds reasonable! Lol yeah back to constraints, but at least there's one less field to worry about

@antsgar antsgar added this pull request to the merge queue Feb 29, 2024
Merged via the queue into integration with commit 7d06e9e Feb 29, 2024
5 checks passed
@antsgar antsgar deleted the LF-4121-create-patch-endpoints-for-animals-and-batches branch February 29, 2024 11:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants