-
-
Notifications
You must be signed in to change notification settings - Fork 52
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
Support nested objects and arrays in the model definition #113
Conversation
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.
Hey, @gidesan. Thank you for working on the nested objects support in the models!
I've done the code review and have a few questions and clarifications that I'd appreciate you answering. Overall, I think the changes are good, I just need to get my ahead around flattenning the nested object keys and handling them like pointers. I wonder if there's a more efficient way to this, so we don't have to flatten potentially deep data structures.
If you update against the latest main branch, you'll get |
Did this as well. Hope I didn't mess up the git history |
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.
Hey, @gidesan. Thank you once more for covering my suggestions!
I do have some concerns about some API changes in the parsing function, but I don't wish to block you while I think of a way to solve them. There are a few comments regarding moving the tests and reusing existing utilities that you may address if you have time.
If you're comfortable with Git, we should clean up the commit history for this change. Each commit should represent a finite meaningful change. For example:
You can leave this formatting to me if you feel unsure. We can rebase the commits once the change is finished. |
Tbh I used not-so-meaningful commit messages thinking that we would have squashed them in the end. Even my email should be changed in the commits. |
I should have addressed your new suggestions, thanks a lot :) |
Really impatient for this PR to be merged :) We would like to use this library to generate all factories for all of our mocks in cypress/jest testing and for localhost development too. However not having primitive arrays is blocking us for choosing this library :s |
I've squashed the commits and rebased the branch so it's up-to-date with the main one. I'll give it a final round of review and then it should be ready to go. Thank you for being patient. |
return entity | ||
} | ||
|
||
if (isFunction(propertyDefinition)) { |
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.
What was your intention behind adding this explicit isFunction
check?
Type-wise, at this point of processing propertyDefinition
is a base type getter (() => BaseType
), in other words: always a function. Perhaps you wanted to guard this reduce against malformed user input?
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.
After 2 months I don't exactly remember tbh. Probably what you guessed, otherwise something related to the very first implementation of the feature. It's probably redundant at this point.
Btw was just looking at line 66, where I wonder if logging "has a plain initial value" is still true for arrays (which now are an option)
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.
No worries, I'm remembering everything around this myself.
looking at line 66, where I wonder if logging "has a plain initial value" is still true for arrays
Is it the wording that confuses you? I'd say it matters little as it's internal logging.
There's an issue with properly typing nested objects in the Ideally, we should recursively reference that type, so to support infinitely nested objects: export type ModelDefinitionValue =
| PrimaryKeyDeclaration
| OneOf<any>
| ManyOf<any>
| (() => PrimitiveValueType)
export type ModelDefinition = {
[propertyName: string]: ModelDefinitionValue | ModelDefinition
} Once we do so, however, there's no way to reliably differentiate between internal types like factory({
user: {
address: {
isPrimaryKey: true,
getValue: () => 'abc-123'
}
}
}) Such a Lines 19 to 24 in 3016d6d
Lines 44 to 49 in 3016d6d
Once we introduce arbitrary objects as values, it'll be impossible to differentiate between them and internal values. That's already evident if we implement the Even if we introduce distinct properties in internal values to clearly detect them (as we have with I think there's a couple of options we have here. Option 1: Use distinct properties on internal valuesWe may use unlikely property names to partially account for this, like Option 2: Treat internal values as class instancesInternal values (relations/primary keys) may be represented as class instances. That way we can leverage class belonging to eliminate unintentional property collisions with internal values' properties. class PrimaryKey { ... }
function primaryKey(...args) {
return new PrimaryKey(...args)
}
// parseModelDefinition.ts
if (value instance of PrimaryKey) {
// treat the value as a primary key
}
if (value instanceof Relation) {
// treat the value as a relation
} This would add a little clutter internally, but should allow us to reliably differentiate between internal and arbitrary values in the context of nested objects support. @gidesan, I'd love to hear your feedback on this. Please do not proceed with implementation until we discuss this in sufficient detail. |
Meanwhile, I've pushed some minor adjustments to the code, mainly around the recursive |
I'm done for today. Here are some of the remaining points to cover (mainly leaving here for myself):
|
Not a strong opinion on that. I'd say option 2 is cleaner, but I also guess collisions for option 1 will be very rare (and they can be notified to the user). Available for help if needed ofc :) |
Primary key and Relations as classesI've rewritten the internal implementation of the primary key and relations and those are now represented as classes (see my post above for the motivation to do so). No user-facing changes were introduced, apart from a new added invariant:
|
I've spotted that |
Added a test for updating a nested property of an entity (no relations). The only thing that remains now is the proper TypeScript support for deeply nested objects. This may be a challenge. |
I've spent some time adjusting the type definitions to support nested objects. Boy, is that a challenge. It comes down to enabling nested objects type-wise for the following areas:
I'm more or less done with everything, covering up some collateral issues. @gidesan, please don't push any substantial changes, especially type-related ones. My mind will explode if I have to rebase what I have. Thanks! |
Everything should be in order now: the feature is implemented, typed, relevant tests updated/added. Thank you for the great work on this, @gidesan! 🎉 |
There's another issue I've found with |
Adds support to nested objects and arrays in model definitions, e.g.
This PR is expected to solve this issue #97 as well.