-
Notifications
You must be signed in to change notification settings - Fork 76
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 4170 create or modify animal enum tables #3252
Lf 4170 create or modify animal enum tables #3252
Conversation
|
||
export const up = async function (knex) { | ||
// Synchronize the permissions_permission_id_seq sequence with the latest permission_id value. | ||
await knex.raw(` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to let the db auto generate the permission id for new rows because it's currently out of sync and you have to manually look it up and add it in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also prefer your proposed way -- but I think there was some issue in the past as there was a conscious decision to explicitly set the id to make it consistent between prod/beta/local. My understanding is you will need to use the existing pattern .. sorry
@navDhammu Sorry it is taking a little longer to review than normal! We have had a lot of unexpected coding challenges happen this month and have been focusing on the Soil Health V1 ready for release. Apologies if it take a little longer. In my opinion -- I really dislike our existing backend tests haha. So I am definitely open to that POC/discussion sometime after this release! My pet peeves with the tests are:
|
@Duncan-Brain No worries! Btw, not related to this ticket but I worked on an experimental PR which I thought might be useful. Once you have the time, if you can have a look and provide some feedback that would be appreciated! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great and tests are passing!
I had to merge integration for migrations to work since we did the release.
I think the only change needed is the permissions -- I agree it is strange the way we do it...
|
||
export const up = async function (knex) { | ||
// Synchronize the permissions_permission_id_seq sequence with the latest permission_id value. | ||
await knex.raw(` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also prefer your proposed way -- but I think there was some issue in the past as there was a conscious decision to explicitly set the id to make it consistent between prod/beta/local. My understanding is you will need to use the existing pattern .. sorry
INSERT INTO animal_identifier_placement (default_type_id, key) | ||
SELECT id as default_type_id, placement as key | ||
FROM default_animal_type dat | ||
CROSS JOIN UNNEST(ARRAY['LEFT_EAR', 'RIGHT_EAR', 'LEFT_LEG', 'RIGHT_LEG']) AS placement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some nice sql'ing!
I personally find sql harder to read than breaking this down into smaller js-like queries and insertions with knex methods. But just a personal preference as my sql ain't as good as I would like it to be!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I initially went with the knex approach but there was a lot of code with some nested loops which wasn't easy to read either so I just went with the shorter sql way.
} | ||
|
||
await Promise.all([1, 2, 3, 5].map(makeRequest)); | ||
requester.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that this is our only instance of keepOpen()
and close()
What happens if something fails before reaching this line? Like db transactions is a try catch needed or chained to the promises in .then()
like the chai example: Search for keepOpen to see this example
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since multiple requests were being made here I thought it's more performant to leave the server open, rather than shutting down and opening on each request. I'm assuming it automatically closes if something throws before reaching the line but I couldn't find anything in the chai docs.
@Duncan-Brain Thank you for reviewing, I've made the requested changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delayed review.
I left a comment, but I'm unsure about one table schema. I'll confirm with the team and get back to you!
await knex('animal_identifier_type').insert([ | ||
{ id: 1, key: 'EAR_TAG' }, | ||
{ id: 2, key: 'LEG_BAND' }, | ||
{ id: 3, key: 'OTHER' }, | ||
]); | ||
|
||
await knex('animal_identifier_placement').insert([ | ||
{ identifier_type_id: 1, key: 'LEFT_EAR' }, | ||
{ identifier_type_id: 1, key: 'RIGHT_EAR' }, | ||
{ identifier_type_id: 2, key: 'LEFT_LEG' }, | ||
{ identifier_type_id: 2, key: 'RIGHT_LEG' }, | ||
{ identifier_type_id: 3, key: 'OTHER' }, | ||
]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm not mistaken, adding ids manually is problematic, and we want to avoid it.
(@Duncan-Brain , was the problem that we couldn't insert a new row because id
was conflicting?)
Here are the examples of how these were done before:
LiteFarm/packages/api/db/migration/20240112220609_create_animal_type_tables.js
Lines 33 to 40 in b03f4b0
// Add initial default types | |
const defaultTypeKeys = ['CATTLE', 'PIGS', 'CHICKEN_BROILERS', 'CHICKEN_LAYERS']; | |
for (const typeKey of defaultTypeKeys) { | |
await knex('default_animal_type').insert({ | |
key: typeKey, | |
}); | |
} |
LiteFarm/packages/api/db/migration/20240115182150_create_animal_breed_tables.js
Lines 62 to 95 in b03f4b0
// Add initial default breeds | |
const defaultBreedKeys = [ | |
{ | |
typeKey: 'CATTLE', | |
breedKeys: ['ANGUS', 'HEREFORD', 'CHAROLAIS'], | |
}, | |
{ | |
typeKey: 'PIGS', | |
breedKeys: ['YORKSHIRE_LARGE_WHITE', 'LANDRACE', 'DUROC'], | |
}, | |
{ | |
typeKey: 'CHICKEN_BROILERS', | |
breedKeys: ['CORNISH_CROSS', 'ROSS_308', 'COBB_500'], | |
}, | |
{ | |
typeKey: 'CHICKEN_LAYERS', | |
breedKeys: ['LEGHORN', 'RHODE_ISLAND_RED', 'PLYMOUTH_ROCK'], | |
}, | |
]; | |
const rows = []; | |
for (const entry of defaultBreedKeys) { | |
const { typeKey, breedKeys } = entry; | |
const { id: typeId } = await knex('default_animal_type').where('key', typeKey).first(); | |
breedKeys.forEach((breedKey) => | |
rows.push({ | |
default_type_id: typeId, | |
key: breedKey, | |
}), | |
); | |
} | |
await knex('default_animal_breed').insert(rows); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's funny because it is the opposite of the permission_id
thing haha. 😢
@SayakaOno I think you are right for the enum table animal_identifier_type
we would prefer to use the auto-increment index. And then map that to the correct place in animal_identifier_placement
.
If I recall many of our problems with setting this would be migration/rollback/testing related. I think another issue was if it was a user interactable table or not...
Here is one such initial conversation: #2857 (comment) and The PR that repaired that id problem: #2882
I always prefer using the autoincrement but for permission_id both Jimmy and Anto have kept the existing way...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you Duncan for finding the discussions in the past!
I added a new section "Auto-Increment ID Conventions" with the links to the API package confluence page.
Please update as you see fit!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still not quite sure I understand why manually setting id in the permissions table is done the way it is. Is it even possible for local/beta/production db to have different id's if using auto-increment? Especially since users cannot edit that table so I'm expecting that it remains stable across environments. I think there are other tables in the schema which aren't able to be edited by a user and yet they still use the auto-increment, so I'm unsure what makes permissions table a special case?
Regarding the id's on animal_identifier_type
, I agree it should've been set with the auto-increment sequence. I think I was just lazy and thought that if its an enum table that won't change then it doesn't matter. But if it caused problems in the past then should probably be changed to auto-increment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So at least for me I have seen auto-increment differences because of failed migrations or undoing-redoing migrations. It seems like because of concurrent transactions the nextval can never be rolled back as part of a transaction. https://www.postgresql.org/docs/9.1/functions-sequence.html (see nextval section)
But agreed that we shouldn't be reliant on it.. maybe a SPIKE jira ticket can be made to explore fixing this for good and addressing all concerns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting I didn't know sequence couldn't be rolled back, thanks for sharing the link. In that case it makes sense for manually entering the permission keys, and also for tables like role
where the id needs to remain stable. But for other tables where we don't care about the value of the id, and only that it's unique in the table, then I think it doesn't really matter if there are differences between environments.
await knex.schema.dropTable('animal_identifier_placement'); | ||
|
||
await knex.schema.createTable('animal_identifier_placement', (table) => { | ||
table.increments('id').primary(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Duncan-Brain this table animal_identifier_placement
doesn't seem correct to me...
![Screenshot 2024-08-01 at 2 22 14 PM](https://private-user-images.githubusercontent.com/33141219/354386604-53cd7a09-2276-46f5-a667-64f5908719d0.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjMyMjU4NjMsIm5iZiI6MTcyMzIyNTU2MywicGF0aCI6Ii8zMzE0MTIxOS8zNTQzODY2MDQtNTNjZDdhMDktMjI3Ni00NmY1LWE2NjctNjRmNTkwODcxOWQwLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDklMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODA5VDE3NDYwM1omWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTI4ZGEwYzc0YmM2ZTNmYjBiNDU0MmU3OTkzNmIyMGRkYmFiZjE4NDczNjgwOTc3YjBlZjczMDQxZmJkM2Y5NWQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.HxbmJ8cYu5SLMvAqs9lesPDYBMo7FJS46aZH5nvYR6k)
I'm not sure about storing the id
in the animal
table as identifier_placement_id
. I would prefer to store the keys directly, or create an enum table like this:
LEFT_EAR
RIGHT_EAR
LEFT_LEG
RIGHT_LEG
OTHER
Then, replace the "key" column with "identifier_placement_id" then omit the id
column.
Can you recall if we discussed this design? If not, I would like to discuss it next week.
(Sorry Navdeep 🙏 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For Nav, to me this looks exactly as described in the Jira ticket at least!
@SayakaOno I am not sure if I remember discussing this. Are you suggesting a relationship table(between type and placement)? Or two ids on the animal (one for type one for placement)?
My understanding is how this was meant to work is that a user will react select an indentifier_type (Ear_tag) then we will filter the identifier_placements (left ear, right ear) based on that original choice for a second react select. The placement_id is added I think because combination (ear_tag) and (left_leg) don't make sense.
The architecture we have chosen seems not great I agree. I would prefer some composite key enum like the following and deleting one of the tables:
Or just omit the "left ear" , "right_ear" selection altogether -- "ear tag" or "leg_band" seems plenty descriptive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your input Duncan!
Nav, we discussed this with the team, and the decision was made to remove the tag placement input.
This means that we won't need the animal_identifier_placement
table. I'm terribly sorry for the update on the requirement, and for the lack of clarity in the initial requirements as well.
In addition to dropping the animal_identifier_placement
table, the expected changes are:
- remove
identifier_placement_id
column fromanimal
table - update models
- update tests
- update routes
- other references?
Since this requires a substantial amount of changes, we could create another ticket/PR for this. (In this case, we could have one PR for animal_identifier_type
, and another for removing animal_identifier_placement
.)
Please let us know what works best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SayakaOno That sounds reasonable and I think separate PR's for each requirement would be better.
Just for reference, here is the ticket to remove |
export const down = async function (knex) { | ||
await knex.schema.dropTable('animal_identifier_type'); | ||
|
||
await knex.raw(` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe something like this is more direct? await knex('rolePermissions').where('permission_id', 171).del();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm more familiar with sql than knex so I have a tendency to just use sql but that looks much better!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah true haha -- it made sense I think when we didnt set the id explicitly!
From what I can tell the principle behind orms is that by using the portable knex functions we could theoretically switch db providers easily just by changing knex config. Raw functions mayyy need to be updated based upon SQL flavour if we were to switch. That and avoiding some common errors like N+1 etc.
So generally my understanding is it is best to stick with knex where possible and use raw for only limited situations. We haven't been picky though about that at all though!
AND p.name = '${PERMISSION_NAME}' | ||
`); | ||
|
||
await knex('permissions').where('name', PERMISSION_NAME).del(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe best to use id? In the extremely unlikely event the name is not unique.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good to me just think we want to use ID in the down function too -- otherwise tests and routes all look great!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
Description
This PR includes some migrations, api for /animal_identifier_types and some small changes to the animal_identifier_placements api as specified in ticket: https://lite-farm.atlassian.net/browse/LF-4170
While working on this I had some issues with some of the tests. I'm not sure if its just me but when I create a new test database and run the migrations on it, I get some errors when trying to run individual files like
animal_identifier_test
(and some other files too). Its happening because initially the database has several constant tables with values already present in them like animal colors and placements, and the cleanup is done in theafterEach
method which means that when the first test is executed the tables are not in an empty state and this is resulting in some errors. It doesn't happen when you run all the tests withnpm run test
because the test that runs first doesn't rely on any non-empty tables so when its finished, the cleanup is done in afterEach and all subsequent tests run with a clean state making all tests pass.Going further on this topic, I had some ideas on improving on the overall testing strategy. Rather than running a cleanup of the whole db after each test, it may be better to run each test in a transaction and rollback once its complete, potentially eliminating the need to manually do the cleanup and maintain a list of tables that need to be cleaned in
testEnvironment.js
. Also it might make the tests run a bit faster.If the dev team @Duncan-Brain , @SayakaOno , @kathyavini is alright with this, I can try to create a sample PR as a proof of concept.
Type of change
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
Checklist: