Conversation
|
Hello Tamir. Could you try to add a bit more context in the description of the PR. The description is a bit hard to read, and is good to add a section explaining how to test this (steps), this makes the review easier and reduce the chance of misunderstanding the scope and goal of the PR |
anxolin
left a comment
There was a problem hiding this comment.
The description of the PR is way better now. Thanks for applying the changes
| } | ||
|
|
||
| // Helper function to calculate months difference between two dates | ||
| function getMonthsDifference(startDate: Date, endDate: Date): number { |
There was a problem hiding this comment.
This belong to a utils file, in case you need it in another part of the project you don't want to add a dependency to this file
| // For create operation, we handle it in afterCreate since we need the ID | ||
| } | ||
|
|
||
| async function calculateServiceFeeEnabled(solver: Solver, data: SolverData): Promise<void> { |
There was a problem hiding this comment.
why is this called calculate, but returns nothing?
| // For create operation, we handle it in afterCreate since we need the ID | ||
| } | ||
|
|
||
| async function calculateServiceFeeEnabled(solver: Solver, data: SolverData): Promise<void> { |
There was a problem hiding this comment.
Would be positive to add some minmial JsDoc to explain what is the responsability of the function
| } | ||
|
|
||
| async function calculateServiceFeeEnabled(solver: Solver, data: SolverData): Promise<void> { | ||
| data.isServiceFeeEnabled = false; |
There was a problem hiding this comment.
this is mutating the parameter, is this desired?
There was a problem hiding this comment.
Yes, if its not set to true it needs to be false by default
| const joinedDate = new Date(bondingPool.joinedOn); | ||
| const monthsDifference = getMonthsDifference(joinedDate, currentDate); | ||
|
|
||
| if (bondingPool.name === "CoW" && solver.isColocated === "No") { |
There was a problem hiding this comment.
Why name CoW, its hard to me to reason about this logic
There was a problem hiding this comment.
This is the bonding pool name in the referenced table (an existing entry)
There was a problem hiding this comment.
This logic seems to fragile to me. So you are following an undocumented naming convention, that is hidden in a method of this lifecycle.
Also, this method suffers from the same things I mentioned here #85 (comment)
- called calculate, but it mutates
- does too many things, should rely on somethign th
- has a lot of logic that for me is hard to tell if this is what you intended to do. You have bussiness logic mixed there that I can't verify (hardcoded 3 months, skip if joinedOn is missing, ans different rules for collocated cow and for others). How can I tell what I need to verify?
| ); | ||
|
|
||
| if (solver) { | ||
| await calculateServiceFeeEnabled(solver as Solver, solverData); |
There was a problem hiding this comment.
it feels like you want to update something and first you want to do some calculations. If that's the case, is better to make a pure function to decide the new value and then do the update
This function relies on side effects and does too many things. Follow SOLID single responsability principle "https://www.digitalocean.com/community/conceptual-articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design#single-responsibility-principle
There was a problem hiding this comment.
All comments showed outdated code and I couldn't find the original function.
When you address a comment and code changes a lot so GH can placed a discussion in the right place of the new code, is good you include a link to the commit you pushed to address the comment.
There was a problem hiding this comment.
Ideally, you move part of calculateActiveNetworks to a method getActiveNetworks(solver.solver_networks) this way your method is way more readable.
Also, it doesn't calculate, it updates the solver. Calculate implies is READ ONLY, when updates implies it MUTATES which is what you did
So it could be something like:
async function updateActiveNetworks(solver: Solver, data: SolverData): Promise<void> {
const activeNetworks = getActiveNetworks(solver.solver_networks)
data.hasActiveNetworks = activeNetworks.length > 0;
data.activeNetworks = activeNetworks ? activeNetworks.map(network => network..name) : '';
}Not only this is simpler and more readable than the current code, but also, if you remove a network, you won't get the value you expect because of how you return early (you don't update the data if there's no network info).
Original code:
async function calculateActiveNetworks(solver: Solver, data: SolverData): Promise<void> {
if (!solver.solver_networks) {
data.activeNetworks = [];
data.hasActiveNetworks = false;
return;
}
// Filter active networks and extract their names
const activeNetworkNames = solver.solver_networks
.filter(network => network.active)
.map(network => network.network?.name)
.filter(Boolean) as string[]; // Remove any undefined values
data.activeNetworks = activeNetworkNames;
data.hasActiveNetworks = activeNetworkNames.length > 0;
}| const joinedDate = new Date(bondingPool.joinedOn); | ||
| const monthsDifference = getMonthsDifference(joinedDate, currentDate); | ||
|
|
||
| if (bondingPool.name === "CoW" && solver.isColocated === "No") { |
There was a problem hiding this comment.
I don't think we should do this in a lifecycle. Having this logic in the lifecycle means that the BL for updating the CMS is split into two different places, both in Dagster and the CMS itself. That adds unnecessary complexity and makes it harder to maintain in the future. I think it would be better to do this in Dagster and keep a separation where data is transformed and updated through Dagster and these lifecycles handle simple aggregation.
| export default { | ||
| async beforeCreate(event) { | ||
| try { | ||
| await updateActiveNetworks(event); |
There was a problem hiding this comment.
Shouldn't this be done after updating or creating?
|
|
||
| async beforeUpdate(event) { | ||
| try { | ||
| await updateActiveNetworks(event); |
There was a problem hiding this comment.
Since these updates and creates will be done through Dagster, what's the advantage of processing this through the lifecycle vs adding this update as a step in the same Dagster job that will do the update that triggers this hook?
| * Determines if a solver is eligible for service fees based on bonding pool criteria. | ||
| * Pure function that checks eligibility based on time thresholds: | ||
| * - 6+ months for non-colocated CoW pools | ||
| * - 3+ months for colocated pools |
There was a problem hiding this comment.
Just curious, is this comming from EIP, if so. Could you add it?
| * - 6+ months for non-colocated CoW pools | ||
| * - 3+ months for colocated pools | ||
| * @param solver The solver entity to check | ||
| * @returns Boolean indicating if the solver is eligible for service fees |
There was a problem hiding this comment.
TBH, i don't know what is service fee exactly. I assume this is not protocol fee related.
To avoid these things, if you give more context in the PR you avoid people that is not familiar with your current work to get a proper idea
| ); | ||
|
|
||
| if (solver) { | ||
| await calculateServiceFeeEnabled(solver as Solver, solverData); |
There was a problem hiding this comment.
All comments showed outdated code and I couldn't find the original function.
When you address a comment and code changes a lot so GH can placed a discussion in the right place of the new code, is good you include a link to the commit you pushed to address the comment.
| ); | ||
|
|
||
| if (solver) { | ||
| await calculateServiceFeeEnabled(solver as Solver, solverData); |
There was a problem hiding this comment.
Ideally, you move part of calculateActiveNetworks to a method getActiveNetworks(solver.solver_networks) this way your method is way more readable.
Also, it doesn't calculate, it updates the solver. Calculate implies is READ ONLY, when updates implies it MUTATES which is what you did
So it could be something like:
async function updateActiveNetworks(solver: Solver, data: SolverData): Promise<void> {
const activeNetworks = getActiveNetworks(solver.solver_networks)
data.hasActiveNetworks = activeNetworks.length > 0;
data.activeNetworks = activeNetworks ? activeNetworks.map(network => network..name) : '';
}Not only this is simpler and more readable than the current code, but also, if you remove a network, you won't get the value you expect because of how you return early (you don't update the data if there's no network info).
Original code:
async function calculateActiveNetworks(solver: Solver, data: SolverData): Promise<void> {
if (!solver.solver_networks) {
data.activeNetworks = [];
data.hasActiveNetworks = false;
return;
}
// Filter active networks and extract their names
const activeNetworkNames = solver.solver_networks
.filter(network => network.active)
.map(network => network.network?.name)
.filter(Boolean) as string[]; // Remove any undefined values
data.activeNetworks = activeNetworkNames;
data.hasActiveNetworks = activeNetworkNames.length > 0;
}| * @param endDate The end date | ||
| * @returns The number of months between the two dates | ||
| */ | ||
| export function getMonthsDifference(startDate: Date, endDate: Date): number { |
There was a problem hiding this comment.
Not sure if this is the implementation you want. Si hard to review
If you have:
startDate = 31/01/2026
endDate = 01/02/2026
Your algo will show 1 month. But it was just 1 day. Is this correct?
If you have:
startDate = 01/01/2026
endDate = 02/01/2026
It will return 0 months, but its just 1 day too.
| const joinedDate = new Date(bondingPool.joinedOn); | ||
| const monthsDifference = getMonthsDifference(joinedDate, currentDate); | ||
|
|
||
| if (bondingPool.name === "CoW" && solver.isColocated === "No") { |
There was a problem hiding this comment.
This logic seems to fragile to me. So you are following an undocumented naming convention, that is hidden in a method of this lifecycle.
Also, this method suffers from the same things I mentioned here #85 (comment)
- called calculate, but it mutates
- does too many things, should rely on somethign th
- has a lot of logic that for me is hard to tell if this is what you intended to do. You have bussiness logic mixed there that I can't verify (hardcoded 3 months, skip if joinedOn is missing, ans different rules for collocated cow and for others). How can I tell what I need to verify?
As part of our recent efforts to improve data integrity in the CMS we are adding event hooks to certain actions in the cms that update other fields, this way we ensure that data is always up to date in realtime unlike now when fields are not updated because of manual mistakes or things changing in one table but not reflected in another.
In this pr we add some simple lifecycles to the solver table:
before create or update operations on the solver table:
To test this change: