-
Notifications
You must be signed in to change notification settings - Fork 1
feat: Support building & using patches as a list of DTOs #14
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
base: dev
Are you sure you want to change the base?
Conversation
ebe3f7b to
a25fa9c
Compare
| #[DataProvider('validOperationsForDTOsProvider')] | ||
| public function testValidJsonPatchesAsDTOs(string $json, string $patches, string $expected): void |
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.
Potentially this test runs more cases than is strictly necessary to prove the DTOs are correctly read & handled.
However it seemed the most efficient way to have confidence that they support all the expected properties / types and work correctly with all handlers and to keep that in sync with any changes to the pure JSON objects over time.
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 okay with your assumptions here, but I would move the tests into a dedicated class for each operation DTO. I suggest this because I think each DTO also needs a test that ensures it fails when incorrect properties are passed. This is important because the PatchOperationList::fromJson method bases its validation workflow (both for patch validation and invalid DTOs) on the assumption that the constructor throws an exception when invalid input is provided.
Adds a `Handler` suffix to all current final operation handler class names, and moves them to a `handlers` namespace. This is not expected to affect end-users as the in-built handler classes are not generally expected to be used outside the package. This will avoid naming conflicts and confusion when we subsequently introduce support for representing a patch as a set of operation DTOs.
End-users are not expected to reference these classes directly. If users want to customise handling for a particular operation, they should implement the interface and/or extend from the abstract base class.
Renames all remaining classes, interfaces & methods related to patch operation handlers to include the `Handler` suffix. This reinstates consistency between the naming of these concerns, and will provide the cleanest base for implementing patch operation DTOs without naming conflicts or confusion. It will, however, impact any end-users who have implemented & registered custom patch handlers as they will need to update them for the new naming as part of the 3.x upgrade.
a25fa9c to
075e62b
Compare
Build on the existing implementation to allow users to specify patches as a list of operation objects, rather than as a JSON string. Unlike the JSON form, the patch operation DTOs are strict about the parameters they accept. If a user wishes to include custom properties, they can implement a class extending the base `PatchOperation`. Patch DTOs can be serialised to and from JSON, for convenience.
Now that there are explicit operation classes for each standard operation, it feels appropriate to define the expected schema for unserialized operations alongside the DTO and import it to the handler. This more clearly shows the relationship between these objects.
075e62b to
efa8e0d
Compare
|
|
||
| return ['op' => 'replace', 'path' => $patch->path, 'value' => $this->previous]; | ||
| public function __construct( | ||
| public readonly string $path, |
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 PHP 8.1 is reaching EOL on January 1st, I'm considering bumping the minimum required PHP version to 8.2 for the 3.0 release. This would allow us to use the class-level readonly keyword.
What do you think about that?
| JsonHandlerInterface $jsonHandler = new BasicJsonHandler(), | ||
| array $customClasses = [], | ||
| ): self { | ||
| $patches = $jsonHandler->decode($jsonOperations, ['associative' => true]); |
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 JSON decoders may not support the associative array option at all because forcing objects into arrays introduces representation limitations that make it error-prone. Decoding the patch as objects instead is a more robust approach.
| #[DataProvider('validOperationsForDTOsProvider')] | ||
| public function testValidJsonPatchesAsDTOs(string $json, string $patches, string $expected): void |
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 okay with your assumptions here, but I would move the tests into a dedicated class for each operation DTO. I suggest this because I think each DTO also needs a test that ensures it fails when incorrect properties are passed. This is important because the PatchOperationList::fromJson method bases its validation workflow (both for patch validation and invalid DTOs) on the assumption that the constructor throws an exception when invalid input is provided.
Build on the existing implementation to allow users to specify patches as a list of operation objects, rather than as a JSON string.
Unlike the JSON form, the patch operation DTOs are strict about the parameters they accept. If a user wishes to include custom properties, they can implement a class extending the base
PatchOperation.Patch DTOs can be serialised to and from JSON, for convenience.
Completes #12
** note ** I have based this branch off the branch for #13 to avoid merge conflicts. The first 3 commits in this diff can therefore be ignored. Once #13 is merged I will rebase as required.