Skip to content
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

Move attribute and etag logic into Repository #411

Merged
merged 3 commits into from
Dec 1, 2023

Conversation

bdemers
Copy link
Member

@bdemers bdemers commented Nov 13, 2023

Breaks up the current update method into new update and patch methods.
(Breaking change)

  • ETag/Version logic must be managed by Repositories, except for the case of ServiceProviderConfiguration (which is likely stored in memory)
    • Implementations should set scimResource.meta.version, how this version is defined is up to the implementation
  • The UpdateRequest class has been removed, the getPatchOperations method has been moved into a new class PatchGenerator
  • Current examples have been updated and demo simple in memory patch application. e.g. Get/apply patch/persist.
    • Other implementations may want generate update queries directly from the list of PatchOperations (skipping intermediate get step)

T update(String id, String version, T resource, Set<AttributeReference> includedAttributes, Set<AttributeReference> excludedAttributes) throws ResourceException;

/**
* Allows the SCIM server's REST implementation to update and existing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Allows the SCIM server's REST implementation to update and existing
* Allows the SCIM server's REST implementation to update an existing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Copy link
Contributor

@joshuapsteele joshuapsteele left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this is looking good to me. Will definitely help us optimize database operations on our end when IdPs like Azure send us a bunch of single-user PATCH /Groups requests!

However, can we get some better comments/documentation about what, specifically, about the incoming PUT or PATCH requests determines whether the update() or patch() flows are used? In general, I tend to get lost in SCIMple when looking for the endpoint/controller-level logic. In this PR as well, I've missed (at least on a first pass) where the main logic is for SCIMple to decide whether to use update() or patch() based on the incoming request that it's handling.

@bdemers
Copy link
Member Author

bdemers commented Nov 15, 2023

However, can we get some better comments/documentation about what, specifically, about the incoming PUT or PATCH requests determines whether the update() or patch() flows are used? In general, I tend to get lost in SCIMple when looking for the endpoint/controller-level logic. In this PR as well, I've missed (at least on a first pass) where the main logic is for SCIMple to decide whether to use update() or patch() based on the incoming request that it's handling.

Absolutely!

IMHO, the high level goal is that folks shouldn't need to know any of the detail of the REST layer, and only need to implement the Repository API.

This will need to be expanded on in the javadoc and other docs, but here are some key points:

  • There are two ways to modify an existing resource:
    • a replacement of a resource, PUT request, which calls repository.update(...)
    • a partial update of a resource, PATCH request which calls repository.patch(...)

Maybe it would be helpful to have a table mapping the SCIM protocol requests back to the repository method:

(not completely accurate, mostly thinking out loud for how this might look)

REST Endpoint REST method Repository method
/Users GET repository.query()
/Users/{id} ` PUT repository.update()
/Users/{id} ` PATCH repository.patch()
...

As an aside, while talking about the javadocs, I'm not sure the Swagger/OpenAPI annotations in the REST layer add value to the user. (As the OpenAPI spec for SCIM could be defined by the body managing the RFC, or other project, that OpenAPI spec doesn't change unless the RFC is updated)
Anyway, I've wondered if removing it would make it easier to read/understand the code. (Or if I'm just over thinking it and that's not a problem at all)

Copy link
Contributor

@joshuapsteele joshuapsteele left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple nits, really (typo, remove commented-out lines). Otherwise, this approach looks good to me. And it would really help us optimize group membership updates on our end!


@Override
public ScimUser patch(String id, String version, List<PatchOperation> patchOperations, Set<AttributeReference> includedAttributeReferences, Set<AttributeReference> excludedAttributeReferences) throws ResourceException {
PatchHandler patchHandler = new PatchHandlerImpl(schemaRegistry);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make sense to inject PatchHandlerImpl to the Repositories instead of instantiating per request?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just pushed an updated, and injected the PatchHandler, please take a look!

import java.util.*;
import java.util.stream.Collectors;

public class PatchGenerator {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this class used anywhere, so just making sure I understand - what exactly is the purpose of PatchGenerator? is it meant to be used optionally by anyone who wants to convert an update request into a patch request in their repo implementation?

Perhaps a short comment on this class and its intended use could be helpful for future adopters?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯
Added Javadoc comments!

Copy link
Contributor

@kjthorpe18 kjthorpe18 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Just a few small nits. Nice work here!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This covers ScimUser resources very well, but some basic tests for ScimGroup resources would be useful, too.

  • Modify displayName
  • add member(s)
  • remove member(s)
  • replace member(s) - when should this be used over multiple add/remove operations? e.g. 1 replace = 1 add + 1 remove

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are great!

The first 3 are pretty easy, the last one is a bit more complex, and will require a bit more work (and likely changes as to the JSON diff lib is invoked)

Since this logic didn't change in this PR (it was moved from a different class)
I'll create a separate issue to track this (and I'll stick the current tests i have in a new PR, as a starting point)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #427

Comment on lines +345 to +351
if (patchOpType == PatchOperation.Type.REPLACE && (parseData.resourceObject == null ||
(parseData.resourceObject instanceof Collection && ((Collection<?>)parseData.resourceObject).isEmpty()))) {
patchOpType = PatchOperation.Type.REMOVE;
valueNode = null;
}

if (patchOpType == PatchOperation.Type.REPLACE && parseData.originalObject == null) {
patchOpType = PatchOperation.Type.ADD;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this is part what I mentioned in another comment - a replace operation actually being an add/remove depending on the original or target resource having null

Copy link
Contributor

@joshuapsteele joshuapsteele left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bdemers Still think these changes look great and I'm eager to see them incorporated into the project! Who else do we need to bug so that they can review this PR? :)

Breaks up the current `update` method into new `update` and `patch` methods.
(Breaking change)

- ETag/Version logic must be managed by Repositories, except for the case of `ServiceProviderConfiguration` (which is likely stored in memory)
  - Implementations should set `scimResource.meta.version`, how this version is defined is up to the implementation
- The `UpdateRequest` class has been removed, the `getPatchOperations` method has been moved into a new class `PatchGenerator`
- Current examples have been updated and demo simple in memory patch application. e.g. Get/apply patch/persist.
  - Other implementations may want generate update queries directly from the list of PatchOperations (skipping intermediate get step)
Rename PatchHandlerImpl to DefaultPatchHandler (users of SCIMple could create different implementations)
Update examples to inject PatchHandler
NOTE: This section does not describe the supporting endpoints that SCIMple handles automatically
@bdemers bdemers force-pushed the update-patch-attributes branch from ffcebe3 to 630d2f3 Compare November 29, 2023 15:58
@bdemers
Copy link
Member Author

bdemers commented Nov 29, 2023

@joshuapsteele All the reviews here are great!
I just cleaned up the git history a little, I'll merge it once CI passes!

@joshuapsteele
Copy link
Contributor

@joshuapsteele All the reviews here are great! I just cleaned up the git history a little, I'll merge it once CI passes!

@bdemers Sounds good! @kjthorpe18 @zalbert02 and I are all eagerly anticipating this improvement! BTW, how can we get added as contributors to this SCIMple project? We're quite motivated to see it succeed and continue to be developed over time.

@bdemers bdemers merged commit 65e5f13 into develop Dec 1, 2023
2 checks passed
@bdemers bdemers deleted the update-patch-attributes branch December 1, 2023 16:09
@bdemers
Copy link
Member Author

bdemers commented Dec 1, 2023

@joshuapsteele keep doing what you doing, I'll try to get that ball rolling!
In the short term, if you haven't seen this article take a look!
https://community.apache.org/contributors/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants