-
-
Notifications
You must be signed in to change notification settings - Fork 11
Support for bulk operations #149
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: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,8 +4,15 @@ | |
|
|
||
| from pydantic import Field | ||
| from pydantic import PlainSerializer | ||
| from pydantic import ValidationInfo | ||
| from pydantic import model_validator | ||
| from typing_extensions import Self | ||
|
|
||
| from ..annotations import Required | ||
| from ..annotations import Returned | ||
| from ..attributes import ComplexAttribute | ||
| from ..context import Context | ||
| from ..exceptions import InvalidValueException | ||
| from ..path import URN | ||
| from ..utils import _int_to_str | ||
| from .message import Message | ||
|
|
@@ -18,7 +25,7 @@ class Method(str, Enum): | |
| patch = "PATCH" | ||
| delete = "DELETE" | ||
|
|
||
| method: Method | None = None | ||
| method: Annotated[Method | None, Required.true] = None | ||
| """The HTTP method of the current operation.""" | ||
|
|
||
| bulk_id: str | None = None | ||
|
|
@@ -28,10 +35,10 @@ class Method(str, Enum): | |
| version: str | None = None | ||
| """The current resource version.""" | ||
|
|
||
| path: str | None = None | ||
| path: Annotated[str | None, Returned.never] = None | ||
| """The resource's relative path to the SCIM service provider's root.""" | ||
|
|
||
| data: Any | None = None | ||
| data: Annotated[Any | None, Returned.never] = None | ||
| """The resource data as it would appear for a single SCIM POST, PUT, or | ||
| PATCH operation.""" | ||
|
|
||
|
|
@@ -44,14 +51,63 @@ class Method(str, Enum): | |
| status: Annotated[int | None, PlainSerializer(_int_to_str)] = None | ||
| """The HTTP response status code for the requested operation.""" | ||
|
|
||
| @model_validator(mode="after") | ||
| def validate_operation_requirements(self, info: ValidationInfo) -> Self: | ||
| """Validate operation requirements according to RFC 7644.""" | ||
| scim_ctx = info.context.get("scim") if info.context else None | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please return early when scim_ctx is None to align the behavior to the rest of the library. |
||
| if scim_ctx and Context.is_request(scim_ctx) or scim_ctx == Context.DEFAULT: | ||
| # RFC 7644 Section 3.7: "path [...] REQUIRED in a request." | ||
| if self.path is None: | ||
| raise InvalidValueException( | ||
| detail="path is required for request operations" | ||
| ).as_pydantic_error() | ||
| if self.method in ( | ||
| BulkOperation.Method.post, | ||
| BulkOperation.Method.put, | ||
| BulkOperation.Method.patch, | ||
| ): | ||
| # RFC 7644 Section 3.7: "data The resource data as it would appear for a single SCIM POST, | ||
| # PUT, or PATCH operation. REQUIRED in a request when "method" is "POST", "PUT", or "PATCH"." | ||
| if self.data is None: | ||
| raise InvalidValueException( | ||
| detail="data is required for POST, PUT, or PATCH request operations" | ||
| ).as_pydantic_error() | ||
| elif scim_ctx and Context.is_response(scim_ctx): # pragma: no branch | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why |
||
| # RFC 7644 Section 3.7: "location The resource endpoint URL. REQUIRED in a response, | ||
| # except in the event of a POST failure." | ||
| if self.location is None and not ( | ||
| self.method == BulkOperation.Method.post | ||
| and self.status is not None | ||
| and self.status >= 400 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The RFC says: So technically that would include the 1** and 3** error responses |
||
| ): | ||
| raise InvalidValueException( | ||
| detail="location is required for response" | ||
| ).as_pydantic_error() | ||
|
|
||
| # RFC 7644 Section 3.7: "When indicating a response with an HTTP status | ||
| # other than a 200-series response, the response body MUST be included. | ||
| # [...] When indicating an error, the "response" attribute MUST contain | ||
| # the detail error response | ||
| if ( | ||
| self.status is not None | ||
| and self.status >= 400 | ||
| and not (self.response and self.response.get("detail")) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure about this one, the formulation is ambiguous.
I think the What do you think? |
||
| ): | ||
| raise InvalidValueException( | ||
| detail="response error detail is required" | ||
| ).as_pydantic_error() | ||
|
|
||
| # RFC 7644 Section 3.7: "bulkId [...] REQUIRED when "method" is "POST"." | ||
| if self.method == BulkOperation.Method.post and self.bulk_id is None: | ||
| raise InvalidValueException( | ||
| detail="bulkId is required for POST operations" | ||
| ).as_pydantic_error() | ||
|
|
||
| return self | ||
|
|
||
| class BulkRequest(Message): | ||
| """Bulk request as defined in :rfc:`RFC7644 §3.7 <7644#section-3.7>`. | ||
|
|
||
| .. todo:: | ||
|
|
||
| The models for Bulk operations are defined, but their behavior is not implemented nor tested yet. | ||
| """ | ||
| class BulkRequest(Message): | ||
| """Bulk request as defined in :rfc:`RFC7644 §3.7 <7644#section-3.7>`.""" | ||
|
|
||
| __schema__ = URN("urn:ietf:params:scim:api:messages:2.0:BulkRequest") | ||
|
|
||
|
|
@@ -60,23 +116,18 @@ class BulkRequest(Message): | |
| will accept before the operation is terminated and an error response is | ||
| returned.""" | ||
|
|
||
| operations: list[BulkOperation] | None = Field( | ||
| operations: Annotated[list[BulkOperation] | None, Required.true] = Field( | ||
| None, serialization_alias="Operations" | ||
| ) | ||
| """Defines operations within a bulk job.""" | ||
|
|
||
|
|
||
| class BulkResponse(Message): | ||
| """Bulk response as defined in :rfc:`RFC7644 §3.7 <7644#section-3.7>`. | ||
|
|
||
| .. todo:: | ||
|
|
||
| The models for Bulk operations are defined, but their behavior is not implemented nor tested yet. | ||
| """ | ||
| """Bulk response as defined in :rfc:`RFC7644 §3.7 <7644#section-3.7>`.""" | ||
|
|
||
| __schema__ = URN("urn:ietf:params:scim:api:messages:2.0:BulkResponse") | ||
|
|
||
| operations: list[BulkOperation] | None = Field( | ||
| operations: Annotated[list[BulkOperation] | None, Required.true] = Field( | ||
| None, serialization_alias="Operations" | ||
| ) | ||
| """Defines operations within a bulk job.""" | ||
Uh oh!
There was an error while loading. Please reload this page.
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 RFC says:
path and data are required in requests, but not strictly forbidden in responses. I think
Required.neveris too strong.