-
Notifications
You must be signed in to change notification settings - Fork 44
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
feat: Add PatchCollection #2402
feat: Add PatchCollection #2402
Conversation
This was in the wrong place, the folders are named after patch actions, and 'index' is not a patch action
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #2402 +/- ##
===========================================
+ Coverage 74.99% 75.13% +0.14%
===========================================
Files 268 269 +1
Lines 26017 26334 +317
===========================================
+ Hits 19511 19785 +274
- Misses 5181 5213 +32
- Partials 1325 1336 +11
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 9 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
cli/collection_patch.go
Outdated
@@ -0,0 +1,68 @@ | |||
// Copyright 2023 Democratized Data Foundation |
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.
todo:
// Copyright 2023 Democratized Data Foundation | |
// Copyright 2024 Democratized Data Foundation |
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.
😁 Will do
- 2024
cli/collection_patch.go
Outdated
case len(args) >= 1: | ||
patch = args[0] |
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.
question: In what case can len(args)
> 1?
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 don't really care :) But I've added a param limit, arg length is now 0 or 1.
// | ||
// It will also update the GQL types used by the query system. It will error and not apply any of the | ||
// requested, valid updates should the net result of the patch result in an invalid state. The | ||
// individual operations defined in the patch do not need to result in a valid state, only the net result |
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.
question: what is meant by valid state
here.
// individual operations defined in the patch do not need to result in a valid state, only the net result | |
// individual operations defined in the patch do not need to result in a `valid state`, only the net result |
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.
A valid CollectionDescription mutation, at the moment that means one that only changes the name (or tests, or does nothing).
// It will also update the GQL types used by the query system. It will error and not apply any of the | ||
// requested, valid updates should the net result of the patch result in an invalid state. The |
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.
todo: This sentence reads very odd to me "It will error and not apply any of the requested, valid updates should the net result of the patch result in an invalid state."
Please reword.
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 is odd about it? Do you have a rough alternative in mind?
I think it is important to highlight that the full patch will be rolled back if an given patch item fails - especially given that SQLs do not do this and require manual rollback when given multiple DDL statements.
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 sounds more clear when you said: full patch will be rolled back if an given patch item fails
This is very wordy: ... apply any of the requested, valid updates should the net result...
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.
rolled back
is incorrect/lazy lol - the patches are aggregated before being applied - hence the use of net result of the patch
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.
The individual operations defined in the patch do not need to result in a valid state, only the net result of the full patch.
I though I understood the bahaviour from the prior sentence but this one gets me confused. How can the net result be valid if an individual operation creates an invalid state?
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.
the patches are aggregated before being applied
This will be nice to document somewhere too
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 though I understood the bahaviour from the prior sentence but this one gets me confused. How can the net result be valid if an individual operation creates an invalid state?
There are many reasons why we want this to be, and many of the tests cover this, for example:
{ "op": "copy", "from": "/Users/Fields/1", "path": "/Users/Fields/2" },
{ "op": "replace", "path": "/Users/Fields/2/Name", "value": "age" },
The first field is copied (invalid as it is now a duplicate), and then renamed (net is now valid). This allows existing stuff (including full schema/collections) to be used as templates. There are other use cases too.
This will be nice to document somewhere too
It is, we are talking about the documentation that currently documents it.
"strconv" | ||
"strings" | ||
|
||
jsonpatch "github.com/evanphx/json-patch/v5" |
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.
question: I forgot if we cared about camel casing package import alias names. If we do then jsonPatch
?
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 don't know either, my IDE did this, and I don't see it as being unreadable
@@ -526,6 +529,283 @@ func validateUpdateSchemaFields( | |||
return hasChanged, nil | |||
} | |||
|
|||
func (db *db) patchCollection( |
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.
suggestion: I would much rather than patch stuff be moved to a new file, like: db/collection_patch.go
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.
All of core functions to mutating schema and collections currently live in this file, I agree that it should be reorganized, but not in this PR.
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've opened a ticket: #2407
continue | ||
} | ||
|
||
// DeepEqual is temporary, as this validation is temporary |
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.
question: Why is this temporary? Will the use of reflection package be removed?
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.
The validation (function) is temporary, which means that if this function is removed, the call to DeepEqual will be removed with it.
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.
The validation (function) is temporary, which means that if this function is removed, the call to DeepEqual will be removed with it.
Why is it temporary?
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.
Because the function is validating that indexes have not been modified, and we very much want them to be mutable in the future.
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.
Thanks that makes so much sense.
Please document this
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.
Please document this
It is.
db/collection.go
Outdated
for _, oldCol := range oldColsByID { | ||
for _, newCol := range newColsByID { | ||
// It is not enough to just match by the map index, in case the index does not pair | ||
// up with the ID (this can happen if a user moves it) |
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.
question: moves it
moves what?
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.
The collection relative to it's index in the map, I'll replace it
- Replace
it
{ "op": "add", "path": "/2", "value": {"Name": "Dogs"} } | ||
] | ||
`, | ||
ExpectedError: "collection ID cannot be zero", |
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.
Would add another test where it is explicitly 0
:
{ "op": "add", "path": "/2", "value": {"ID": 0, "Name": "Dogs"} }
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.
Will add
- Add explicit zero add test
// If a value is not provided the patch will be applied to all nodes. | ||
NodeID immutable.Option[int] | ||
|
||
Patch string |
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.
todo: document please
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.
My bad :) Of course :)
- Doc test action Patch prop
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.
One minor fix from me.
return store.PatchCollection(cmd.Context(), patch) | ||
}, | ||
} | ||
cmd.Flags().StringVarP(&patchFile, "patch-file", "p", "", "File to load a patch from") |
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.
todo: This flag doesn't match the example above.
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.
Nice catch, copy-pasted issue from PatchSchema, I've fixed both.
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.
Don't all other file consumption commands use --file |-f
why do we want to have a different flag name for this arg (does it clash with -f
somewhere?)
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.
PatchSchema
uses p
because it it takes two kinds of files. I think it is more important to be consistent with PatchSchema
than anywhere else here.
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.
How does it take two files? I see 1 so far with -p
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.
PatchSchema
takes two files, I assume you are looking at PatchCollection
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 we should change the flag for patch schema to also be -f and make it consistent throughout. The lens file can stay with -t or maybe -l would make more sense.
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.
f
and file
is ambiguous for a command that takes two different files. I do not like that.
PatchSchema
used to be f
, but when the lens stuff got added it was changed to avoid this confusion.
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.
@nasdf your opinion would be appreciated here when you have a chance.
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 its fine to keep it -p
for consistency with the other patch method. Using -f
when there are multiple files is worse in my opinion.
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.
Just a few comments to resolve before approval :)
func MakeCollectionPatchCommand() *cobra.Command { | ||
var patchFile string | ||
var cmd = &cobra.Command{ | ||
Use: "patch [patch]", |
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.
thought: I've notice that is some commands, the optional flags aren't shown in the Use
field. I'm wondering if we should start showing all the optional flags here.
// It will also update the GQL types used by the query system. It will error and not apply any of the | ||
// requested, valid updates should the net result of the patch result in an invalid state. The |
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.
The individual operations defined in the patch do not need to result in a valid state, only the net result of the full patch.
I though I understood the bahaviour from the prior sentence but this one gets me confused. How can the net result be valid if an individual operation creates an invalid state?
// It is not enough to just match by the map index, in case the index does not pair | ||
// up with the ID (this can happen if a user moves the collection within the map) |
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.
question: Can you explain how this can be possible?
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.
{ "op": "move", "from": "/1", "path": "/2" }
This moves the collection within the map, without mutating the ID.
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.
Thanks for clarifying.
{ "op": "copy", "from": "/1/Name", "path": "/2/Name" }, | ||
{ "op": "remove", "path": "/1/Name" } |
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.
todo: Can you please add a test showing that if only a copy is applied, we will get a duplicated name error or something like that.
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 the value is low, as duplicate names are tested elsewhere, but I will add
- add copy name test
{ "op": "replace", "path": "/2/ID", "value": 1 } | ||
] | ||
`, | ||
ExpectedError: "collection sources cannot be mutated.", |
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.
thought: The error is a little weird here. It's talking about mutating sources when the patch is dealing with the collection ID.
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.
Yes, unfortunately when mutating IDs it becomes impossible for us to tell what the original object was (and that the ID was mutated), so it will often get caught by other validation rules.
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.
LGTM assuming you will resolve other comments
Relevant issue(s)
Resolves #2389
Description
Adds the PatchCollection command.
Mutating anything but the collection name is currently disabled, we can expand this as we see fit, but for now I'd prefer to keep the initial PR small.
This change means that the Collection Name is no longer always going to be the same as the Schema Name.
I've manually tested the OpenApi stuff via playground.