Skip to content

Commit 4d05422

Browse files
committed
doc: Meta.location integration examples
1 parent 8d3d82f commit 4d05422

File tree

3 files changed

+33
-14
lines changed

3 files changed

+33
-14
lines changed

doc/guides/_examples/django_example.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.http import HttpResponse
55
from django.urls import path
66
from django.urls import register_converter
7+
from django.urls import reverse
78
from django.utils.decorators import method_decorator
89
from django.views import View
910
from django.views.decorators.csrf import csrf_exempt
@@ -43,6 +44,13 @@ def scim_response(payload, status=HTTPStatus.OK):
4344
status=status,
4445
content_type="application/scim+json",
4546
)
47+
48+
49+
def resource_location(request, app_record):
50+
"""Return the canonical URL for a user record."""
51+
return request.build_absolute_uri(
52+
reverse("scim_user", kwargs={"app_record": app_record})
53+
)
4654
# -- setup-end --
4755

4856

@@ -115,7 +123,7 @@ def get(self, request, app_record):
115123
if if_none_match and etag in [t.strip() for t in if_none_match.split(",")]:
116124
return HttpResponse(status=HTTPStatus.NOT_MODIFIED)
117125

118-
scim_user = to_scim_user(app_record)
126+
scim_user = to_scim_user(app_record, resource_location(request, app_record))
119127
resp = scim_response(
120128
scim_user.model_dump_json(
121129
scim_ctx=Context.RESOURCE_QUERY_RESPONSE,
@@ -139,7 +147,7 @@ def put(self, request, app_record):
139147
check_etag(app_record, request.META.get("HTTP_IF_MATCH"))
140148
except PreconditionFailed:
141149
return scim_precondition_error()
142-
existing_user = to_scim_user(app_record)
150+
existing_user = to_scim_user(app_record, resource_location(request, app_record))
143151
try:
144152
replacement = User.model_validate(
145153
json.loads(request.body),
@@ -156,7 +164,7 @@ def put(self, request, app_record):
156164
except ValueError as error:
157165
return scim_uniqueness_error(error)
158166

159-
response_user = to_scim_user(updated_record)
167+
response_user = to_scim_user(updated_record, resource_location(request, updated_record))
160168
resp = scim_response(
161169
response_user.model_dump_json(
162170
scim_ctx=Context.RESOURCE_REPLACEMENT_RESPONSE
@@ -178,7 +186,7 @@ def patch(self, request, app_record):
178186
except ValidationError as error:
179187
return scim_validation_error(error)
180188

181-
scim_user = to_scim_user(app_record)
189+
scim_user = to_scim_user(app_record, resource_location(request, app_record))
182190
patch.patch(scim_user)
183191

184192
updated_record = from_scim_user(scim_user)
@@ -207,7 +215,7 @@ def get(self, request):
207215
return scim_validation_error(error)
208216

209217
total, page = list_records(req.start_index_0, req.stop_index_0)
210-
resources = [to_scim_user(record) for record in page]
218+
resources = [to_scim_user(record, resource_location(request, record)) for record in page]
211219
response = ListResponse[User](
212220
total_results=total,
213221
start_index=req.start_index or 1,
@@ -237,7 +245,7 @@ def post(self, request):
237245
except ValueError as error:
238246
return scim_uniqueness_error(error)
239247

240-
response_user = to_scim_user(app_record)
248+
response_user = to_scim_user(app_record, resource_location(request, app_record))
241249
resp = scim_response(
242250
response_user.model_dump_json(scim_ctx=Context.RESOURCE_CREATION_RESPONSE),
243251
HTTPStatus.CREATED,

doc/guides/_examples/flask_example.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from flask import Blueprint
44
from flask import make_response
55
from flask import request
6+
from flask import url_for
67
from pydantic import ValidationError
78
from werkzeug.routing import BaseConverter
89
from werkzeug.routing import ValidationError as RoutingValidationError
@@ -42,6 +43,11 @@ def add_scim_content_type(response):
4243
"""Expose every endpoint with the SCIM media type."""
4344
response.headers["Content-Type"] = "application/scim+json"
4445
return response
46+
47+
48+
def resource_location(app_record):
49+
"""Return the canonical URL for a user record."""
50+
return url_for("scim.get_user", app_record=app_record, _external=True)
4551
# -- setup-end --
4652

4753

@@ -104,7 +110,7 @@ def handle_precondition_failed(error):
104110
def get_user(app_record):
105111
"""Return one SCIM user."""
106112
req = ResponseParameters.model_validate(request.args.to_dict())
107-
scim_user = to_scim_user(app_record)
113+
scim_user = to_scim_user(app_record, resource_location(app_record))
108114
resp = make_response(
109115
scim_user.model_dump_json(
110116
scim_ctx=Context.RESOURCE_QUERY_RESPONSE,
@@ -123,7 +129,7 @@ def get_user(app_record):
123129
def patch_user(app_record):
124130
"""Apply a SCIM PatchOp to an existing user."""
125131
check_etag(app_record, request.headers.get("If-Match"))
126-
scim_user = to_scim_user(app_record)
132+
scim_user = to_scim_user(app_record, resource_location(app_record))
127133
patch = PatchOp[User].model_validate(
128134
request.get_json(),
129135
scim_ctx=Context.RESOURCE_PATCH_REQUEST,
@@ -146,7 +152,7 @@ def patch_user(app_record):
146152
def replace_user(app_record):
147153
"""Replace an existing user with a full SCIM resource."""
148154
check_etag(app_record, request.headers.get("If-Match"))
149-
existing_user = to_scim_user(app_record)
155+
existing_user = to_scim_user(app_record, resource_location(app_record))
150156
replacement = User.model_validate(
151157
request.get_json(),
152158
scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST,
@@ -157,7 +163,7 @@ def replace_user(app_record):
157163
updated_record = from_scim_user(replacement)
158164
save_record(updated_record)
159165

160-
response_user = to_scim_user(updated_record)
166+
response_user = to_scim_user(updated_record, resource_location(updated_record))
161167
return (
162168
response_user.model_dump_json(
163169
scim_ctx=Context.RESOURCE_REPLACEMENT_RESPONSE
@@ -186,7 +192,7 @@ def list_users():
186192
"""Return one page of users as a SCIM ListResponse."""
187193
req = SearchRequest.model_validate(request.args.to_dict())
188194
total, page = list_records(req.start_index_0, req.stop_index_0)
189-
resources = [to_scim_user(record) for record in page]
195+
resources = [to_scim_user(record, resource_location(record)) for record in page]
190196
response = ListResponse[User](
191197
total_results=total,
192198
start_index=req.start_index or 1,
@@ -215,7 +221,7 @@ def create_user():
215221
app_record = from_scim_user(request_user)
216222
save_record(app_record)
217223

218-
response_user = to_scim_user(app_record)
224+
response_user = to_scim_user(app_record, resource_location(app_record))
219225
return (
220226
response_user.model_dump_json(scim_ctx=Context.RESOURCE_CREATION_RESPONSE),
221227
HTTPStatus.CREATED,

doc/guides/_examples/integrations.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,12 @@ def delete_record(record_id):
5959

6060

6161
# -- mapping-start --
62-
def to_scim_user(record):
63-
"""Convert an application record into a SCIM User resource."""
62+
def to_scim_user(record, location=None):
63+
"""Convert an application record into a SCIM User resource.
64+
65+
:param record: The application record.
66+
:param location: Canonical URL of the resource, set in :attr:`~scim2_models.Meta.location`.
67+
"""
6468
return User(
6569
id=record["id"],
6670
user_name=record["user_name"],
@@ -72,6 +76,7 @@ def to_scim_user(record):
7276
version=make_etag(record),
7377
created=record["created_at"],
7478
last_modified=record["updated_at"],
79+
location=location,
7580
),
7681
)
7782

0 commit comments

Comments
 (0)