Skip to content

Commit 6aac844

Browse files
committed
Purpose: Create /request-storage/anonymization-settings GET and POST endpoints to retrieve and update the configuration for request anonymization.
Requirements/Scope: GET: Verify that the user has the UI-Circulation Settings Request Anonymization View capability Returns current request anonymization configuration Returns an appropriate error if the user is unauthorized POST Verify that the user has the UI-Circulation Settings Request Anonymization Edit capability Updates request anonymization configuration in the database Returns 201 if successful Returns an appropriate error if the user is unauthorized or if the payload is invalid Acceptance Criteria: A /request-storage/anonymization-settings GET endpoint is created A /request-storage/anonymization-settings POST endpoint is created
1 parent 1ee57f4 commit 6aac844

8 files changed

Lines changed: 321 additions & 0 deletions

File tree

descriptors/ModuleDescriptor-template.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,14 @@
222222
"methods": ["DELETE"],
223223
"pathPattern": "/request-storage/requests",
224224
"permissionsRequired": ["circulation-storage.requests.collection.delete"]
225+
},{
226+
"methods": ["GET"],
227+
"pathPattern": "/request-storage/anonymization-settings",
228+
"permissionsRequired": ["circulation-storage.request-anonymization-settings.collection.get"]
229+
},{
230+
"methods": ["POST"],
231+
"pathPattern": "/request-storage/anonymization-settings",
232+
"permissionsRequired": ["circulation-storage.request-anonymization-settings.item.post"]
225233
}
226234
]
227235
},
@@ -1089,6 +1097,16 @@
10891097
"displayName": "Circulation storage - delete circulation setting by id",
10901098
"description": "Delete circulation setting by id"
10911099
},
1100+
{
1101+
"permissionName": "circulation-storage.request-anonymization-settings.collection.get",
1102+
"displayName": "Circulation storage - get request anonymization settings",
1103+
"description": "Get request anonymization settings from storage"
1104+
},
1105+
{
1106+
"permissionName": "circulation-storage.request-anonymization-settings.item.post",
1107+
"displayName": "Circulation storage - create/update request anonymization settings",
1108+
"description": "Create or update request anonymization settings in storage"
1109+
},
10921110
{
10931111
"permissionName": "circulation-storage.all",
10941112
"displayName": "Circulation storage module - all permissions",
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "AnonymizationSettingsResponse",
4+
"type": "object",
5+
"description": "Response payload representing the stored request anonymization configuration.",
6+
"additionalProperties": false,
7+
"properties": {
8+
"id": {
9+
"description": "Identifier of this settings record.",
10+
"type": "string",
11+
"pattern": "^[0-9a-fA-F-]{36}$"
12+
},
13+
"enabled": {
14+
"type": "boolean",
15+
"description": "Whether anonymization of requests is enabled."
16+
},
17+
"retentionDays": {
18+
"type": "integer",
19+
"minimum": 0,
20+
"description": "Number of days to retain request data before it becomes eligible for anonymization."
21+
},
22+
"fields": {
23+
"type": "array",
24+
"uniqueItems": true,
25+
"description": "List of request fields that will be anonymized when the policy triggers.",
26+
"items": {
27+
"items": {
28+
"type": "string",
29+
"description": "A field name from the request record to anonymize."
30+
}
31+
},
32+
"metadata": {
33+
"description": "Optional audit metadata.",
34+
"type": "object",
35+
"additionalProperties": false,
36+
"properties": {
37+
"createdDate": {
38+
"type": "string",
39+
"format": "date-time",
40+
"description": "ISO-8601 timestamp when the record was created."
41+
},
42+
"createdByUserId": {
43+
"type": "string",
44+
"pattern": "^[0-9a-fA-F-]{36}$",
45+
"description": "UUID of the user who created the record."
46+
},
47+
"updatedDate": {
48+
"type": "string",
49+
"format": "date-time",
50+
"description": "ISO-8601 timestamp when the record was last updated."
51+
}
52+
},
53+
"updatedByUserId": {
54+
"type": "string",
55+
"pattern": "^[0-9a-fA-F-]{36}$",
56+
"description": "UUID of the user who last updated the record."
57+
}
58+
}
59+
}
60+
},
61+
"required": ["id", "enabled"]
62+
}

ramls/anonymization-settings.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "AnonymizationSettings",
4+
"description": "Input payload to create or update the request anonymization configuration.",
5+
"type": "object",
6+
"additionalProperties": false,
7+
"properties": {
8+
"enabled": {
9+
"description": "Turn request anonymization on/off.",
10+
"type": "boolean"
11+
},
12+
"retentionDays": {
13+
"description": "Keep request data for this many days before anonymizing.",
14+
"type": "integer",
15+
"minimum": 0
16+
},
17+
"fields": {
18+
"description": "Request fields to anonymize when policy triggers.",
19+
"type": "array",
20+
"uniqueItems": true,
21+
"items": {
22+
"type": "string",
23+
"description": "A field name from the request record to anonymize."
24+
},
25+
"examples": [
26+
["requesterId", "proxyUserId", "pickupServicePointId"]
27+
]
28+
}
29+
},
30+
"required": ["enabled"]
31+
}

ramls/request-storage.raml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ types:
1515
parameters: !include raml-util/schemas/parameters.schema
1616
location: !include locations/location.json
1717
locations: !include locations/locations.json
18+
anonymize-requests-request: !include anonymize-storage-requests-request.json
19+
anonymize-requests-response: !include anonymize-storage-requests-response.json
20+
AnonymizationSettings: !include anonymization-settings.json
21+
AnonymizationSettingsResponse: !include anonymization-settings-response.json
1822

1923
traits:
2024
pageable: !include raml-util/traits/pageable.raml
@@ -76,3 +80,39 @@ resourceTypes:
7680
responses:
7781
501:
7882
description: "Not implemented yet"
83+
84+
/anonymization-settings:
85+
get:
86+
description: Get current request anonymization configuration
87+
responses:
88+
200:
89+
body:
90+
application/json:
91+
type: AnonymizationSettingsResponse
92+
401:
93+
description: Not Authenticated
94+
403:
95+
description: Not authorized
96+
404:
97+
description: No configuration found
98+
post:
99+
description: Create requestion anonymization configuration
100+
body:
101+
application/json:
102+
type: AnonymizationSettings
103+
responses:
104+
201:
105+
body:
106+
application/json:
107+
type: AnonymizationSettingsResponse
108+
400:
109+
description: Bad Request
110+
401:
111+
description: Not Authenticated
112+
403:
113+
description: Not authorized
114+
422:
115+
body:
116+
application/json:
117+
type: errors
118+
example: !include raml-util/examples/errors.sample

src/main/java/org/folio/rest/impl/RequestsAPI.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,28 @@ public void putRequestStorageRequestsByRequestId(String requestId, Request reque
7373
.onComplete(asyncResultHandler);
7474
}
7575

76+
@Validate
77+
@Override
78+
public void getRequestStorageAnonymizationSettings(
79+
Map<String, String> okapiHeaders,
80+
Handler<AsyncResult<Response>> asyncResultHandler,
81+
Context vertxContext) {
82+
83+
new org.folio.service.request.RequestService(vertxContext, okapiHeaders)
84+
.getAnonymizationSettings()
85+
.onComplete(asyncResultHandler);
86+
}
87+
88+
@Validate
89+
@Override
90+
public void postRequestStorageAnonymizationSettings(
91+
org.folio.rest.jaxrs.model.AnonymizationSettings entity,
92+
Map<String,String> okapiHeaders,
93+
Handler<AsyncResult<Response>> h,
94+
Context ctx) {
95+
new org.folio.service.request.RequestService(ctx, okapiHeaders)
96+
.createAnonymizationSettings(entity)
97+
.onComplete(h);
98+
}
99+
76100
}

src/main/java/org/folio/service/request/RequestService.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import org.folio.persist.RequestRepository;
2121
import org.folio.rest.impl.util.OkapiResponseUtil;
2222
import org.folio.rest.impl.util.RequestsApiUtil;
23+
import org.folio.rest.jaxrs.model.AnonymizationSettings;
24+
import org.folio.rest.jaxrs.model.AnonymizationSettingsResponse;
2325
import org.folio.rest.jaxrs.model.Errors;
2426
import org.folio.rest.jaxrs.model.Request;
2527
import org.folio.rest.jaxrs.model.Requests;
@@ -38,6 +40,7 @@
3840
public class RequestService {
3941

4042
private static final Logger log = LogManager.getLogger(RequestService.class);
43+
private static final String ANON_SETTINGS_TABLE = "anonymization_settings";
4144

4245
private final Context vertxContext;
4346
private final Map<String, String> okapiHeaders;
@@ -146,6 +149,28 @@ public Future<Response> deleteAll() {
146149
.compose(eventPublisher.publishAllRemoved());
147150
}
148151

152+
public Future<Response> getAnonymizationSettings() {
153+
return PgUtil.get(
154+
ANON_SETTINGS_TABLE,
155+
AnonymizationSettingsResponse.class,
156+
null, null,
157+
0, 1,
158+
okapiHeaders,
159+
vertxContext,
160+
RequestStorage.GetRequestStorageAnonymizationSettingsResponse.class
161+
);
162+
}
163+
164+
public Future<Response> createAnonymizationSettings(AnonymizationSettings body) {
165+
return PgUtil.post(
166+
ANON_SETTINGS_TABLE,
167+
body,
168+
okapiHeaders,
169+
vertxContext,
170+
RequestStorage.PostRequestStorageAnonymizationSettingsResponse.class
171+
);
172+
}
173+
149174
private boolean isSamePositionInQueueError(AsyncResult<Response> reply) {
150175
String message = OkapiResponseUtil.getErrorMessage(reply);
151176
return RequestsApiUtil.hasSamePositionConstraintViolated(message);

src/main/resources/templates/db_scripts/schema.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,18 @@
595595
"sqlExpressionQuery": "$"
596596
}
597597
]
598+
},
599+
{
600+
"tableName": "anonymization_settings",
601+
"withMetadata": true,
602+
"withAuditing": false,
603+
"uniqueIndex": [
604+
{
605+
"fieldName": "name",
606+
"tOps": "ADD",
607+
"caseSensitive": false
608+
}
609+
]
598610
}
599611
],
600612
"scripts": [

0 commit comments

Comments
 (0)