Skip to content

Commit

Permalink
MODINVSTOR-1222 Implement Subject sources management (#1056)
Browse files Browse the repository at this point in the history
* MODINVSTOR-1222 Implement Subject sources management

* fix checkstyle

* small fixes

* fix test
  • Loading branch information
JavokhirAbdullayev authored Aug 7, 2024
1 parent 5d3e753 commit f3fe256
Show file tree
Hide file tree
Showing 23 changed files with 664 additions and 9 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Required sourceId field in holdings record ([MODINVSTOR-1161](https://folio-org.atlassian.net/browse/MODINVSTOR-1161))

### New APIs versions
* Provides `subject-source 1.0`
* Provides `subject-types 1.0`
* Provides `instance-date-types 1.0`
* Provides `instance-storage 10.1`
Expand All @@ -19,6 +20,8 @@
* Implement Subject types management ([MODINVSTOR-1221](https://folio-org.atlassian.net/browse/MODINVSTOR-1221))
* Implement endpoint to publish reindex event for the range of instance/item/holding records ([MODINVSTOR-1230](https://folio-org.atlassian.net/browse/MODINVSTOR-1230))
* Info, not warn, about expected 403 from /user-tenants ([MODINVSTOR-1237](https://folio-org.atlassian.net/browse/MODINVSTOR-1237))
* Implement Subject sources management ([MODINVSTOR-1222](https://folio-org.atlassian.net/browse/MODINVSTOR-1222))


### Bug fixes
* Unintended update of instance records \_version (optimistic locking) whenever any of its holdings or items are created, updated or deleted. ([MODINVSTOR-1186](https://folio-org.atlassian.net/browse/MODINVSTOR-1186))
Expand Down
4 changes: 4 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ These properties can be changed by setting env variable.
* `KAFKA_INSTITUTION_TOPIC_NUM_PARTITIONS` Default value - `1`
* `KAFKA_SUBJECT_TYPE_TOPIC_NUM_PARTITIONS` Default value - `1`
* `KAFKA_REINDEX_RECORDS_TOPIC_NUM_PARTITIONS` Default value - `16`
* `KAFKA_SUBJECT_SOURCE_TOPIC_NUM_PARTITIONS` Default value - `1`

# Building

Expand All @@ -129,6 +130,8 @@ These environment variables configure Kafka, for details see [Kafka](#kafka):
* `KAFKA_CLASSIFICATION_TYPE_TOPIC_NUM_PARTITIONS`
* `KAFKA_SUBJECT_TYPE_TOPIC_NUM_PARTITIONS`
* `KAFKA_REINDEX_RECORDS_TOPIC_NUM_PARTITIONS`
* `KAFKA_SUBJECT_SOURCE_TOPIC_NUM_PARTITIONS`


These environment variables configure Kafka topic for specific business-related topics
* `KAFKA_CLASSIFICATION_TYPE_TOPIC_NUM_PARTITIONS`
Expand All @@ -138,6 +141,7 @@ These environment variables configure Kafka topic for specific business-related
* `KAFKA_INSTITUTION_TOPIC_NUM_PARTITIONS`
* `KAFKA_SUBJECT_TYPE_TOPIC_NUM_PARTITIONS`
* `KAFKA_REINDEX_RECORDS_TOPIC_NUM_PARTITIONS`
* `KAFKA_SUBJECT_SOURCE_TOPIC_NUM_PARTITIONS`

mod-inventory-storage also supports all Raml Module Builder (RMB) environment variables,
for details see [RMB](https://github.com/folio-org/raml-module-builder#environment-variables):
Expand Down
58 changes: 58 additions & 0 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,33 @@
}
]
},
{
"id": "subject-sources",
"version": "1.0",
"handlers": [
{
"methods": ["GET"],
"pathPattern": "/subject-sources",
"permissionsRequired": ["inventory-storage.subject-sources.collection.get"]
}, {
"methods": ["GET"],
"pathPattern": "/subject-sources/{id}",
"permissionsRequired": ["inventory-storage.subject-sources.item.get"]
}, {
"methods": ["POST"],
"pathPattern": "/subject-sources",
"permissionsRequired": ["inventory-storage.subject-sources.item.post"]
}, {
"methods": ["PUT"],
"pathPattern": "/subject-sources/{id}",
"permissionsRequired": ["inventory-storage.subject-sources.item.put"]
}, {
"methods": ["DELETE"],
"pathPattern": "/subject-sources/{id}",
"permissionsRequired": ["inventory-storage.subject-sources.item.delete"]
}
]
},
{
"id": "contributor-types",
"version": "2.0",
Expand Down Expand Up @@ -1988,6 +2015,31 @@
"displayName": "inventory storage - delete individual subject type",
"description": "delete subject type in storage"
},
{
"permissionName": "inventory-storage.subject-sources.collection.get",
"displayName": "inventory storage - get subject sources collection",
"description": "get subject-sources collection from storage"
},
{
"permissionName": "inventory-storage.subject-sources.item.get",
"displayName": "inventory storage - get individual subject source",
"description": "get individual subject source from storage"
},
{
"permissionName": "inventory-storage.subject-sources.item.post",
"displayName": "inventory storage - create individual subject source",
"description": "create individual subject source in storage"
},
{
"permissionName": "inventory-storage.subject-sources.item.put",
"displayName": "inventory storage - modify subject source",
"description": "modify subject source in storage"
},
{
"permissionName": "inventory-storage.subject-sources.item.delete",
"displayName": "inventory storage - delete individual subject source",
"description": "delete subject source in storage"
},
{
"permissionName": "inventory-storage.instance-types.collection.get",
"displayName": "inventory storage - get instance types collection",
Expand Down Expand Up @@ -2661,6 +2713,11 @@
"inventory-storage.subject-types.item.post",
"inventory-storage.subject-types.item.put",
"inventory-storage.subject-types.item.delete",
"inventory-storage.subject-sources.collection.get",
"inventory-storage.subject-sources.item.get",
"inventory-storage.subject-sources.item.post",
"inventory-storage.subject-sources.item.put",
"inventory-storage.subject-sources.item.delete",
"inventory-storage.instance-types.collection.get",
"inventory-storage.instance-types.item.get",
"inventory-storage.instance-types.item.post",
Expand Down Expand Up @@ -2832,6 +2889,7 @@
{ "name": "KAFKA_CAMPUS_TOPIC_NUM_PARTITIONS", "value": "1"},
{ "name": "KAFKA_INSTITUTION_TOPIC_NUM_PARTITIONS", "value": "1"},
{ "name": "KAFKA_SUBJECT_TYPE_TOPIC_NUM_PARTITIONS", "value": "1"},
{ "name": "KAFKA_SUBJECT_SOURCE_TOPIC_NUM_PARTITIONS", "value": "1"},
{ "name": "KAFKA_REINDEX_RECORDS_TOPIC_NUM_PARTITIONS", "value": "16"}
]
}
Expand Down
5 changes: 5 additions & 0 deletions ramls/examples/subject-source.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"id": "535e3160-763a-42f9-b0c0-d8ed7df6e2a3",
"name": "Library of Congress Subject Headings",
"source": "folio"
}
20 changes: 20 additions & 0 deletions ramls/examples/subject-sources.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"subjectSources": [
{
"id": "06b2cbd8-66bf-4956-9d90-97c9776365b8",
"name": "Library of Congress Subject Headings",
"source": "folio"
},
{
"id": "f9e5b41b-8d5b-47d3-91d0-ca9004796400",
"name": "Library of Congress Children's and Young Adults' Subject Headings",
"source": "folio"
},
{
"id": "6e09d47d-95e2-4d8a-831b-f777b8ef6d99",
"name": "Non-medical Subject Headings",
"source": "local"
}
],
"totalRecords": 3
}
30 changes: 30 additions & 0 deletions ramls/subject-source.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "A subject source",
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"description": "label for the subject source",
"type": "string"
},
"source": {
"type": "string",
"description": "label indicating where the subject source entry originates from, i.e. 'folio' or 'local'",
"enum": [
"folio",
"local"
]
},
"metadata": {
"type": "object",
"$ref": "raml-util/schemas/metadata.schema",
"readonly": true
}
},
"required": [
"name"
]
}
47 changes: 47 additions & 0 deletions ramls/subject-source.raml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#%RAML 1.0
title: Subject Sources API
version: v1.0
protocols: [ HTTP, HTTPS ]
baseUri: http://localhost

documentation:
- title: Subject Sources API
content: This documents the API calls that can be made to query and manage subject types

types:
subjectSource: !include subject-source.json
subjectSources: !include subject-sources.json
errors: !include raml-util/schemas/errors.schema

traits:
pageable: !include raml-util/traits/pageable.raml
searchable: !include raml-util/traits/searchable.raml
validate: !include raml-util/traits/validation.raml

resourceTypes:
collection: !include raml-util/rtypes/collection.raml
collection-item: !include raml-util/rtypes/item-collection.raml
get-delete-only: !include raml-util/rtypes/get-delete.raml

/subject-sources:
type:
collection:
exampleCollection: !include examples/subject-sources.json
exampleItem: !include examples/subject-source.json
schemaCollection: subjectSources
schemaItem: subjectSource
get:
is: [
searchable: {description: "with valid searchable fields", example: "name=aaa"},
pageable
]
description: Return a list of subject sources
post:
description: Create a new subject source
is: [validate]
/{subjectSourceId}:
description: Pass in the subject source id
type:
collection-item:
exampleItem: !include examples/subject-source.json
schema: subjectSource
24 changes: 24 additions & 0 deletions ramls/subject-sources.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "A collection of subject sources",
"type": "object",
"properties": {
"subjectSources": {
"description": "List of subject sources",
"id": "subjectSource",
"type": "array",
"items": {
"type": "object",
"$ref": "subject-source.json"
}
},
"totalRecords": {
"description": "Estimated or exact total number of records",
"type": "integer"
}
},
"required": [
"subjectSources",
"totalRecords"
]
}
4 changes: 2 additions & 2 deletions ramls/subject-type.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
"type": "string"
},
"name": {
"description": "label for the identifier type",
"description": "label for the subject type",
"type": "string"
},
"source": {
"type": "string",
"description": "label indicating where the identifier type entry originates from, i.e. 'folio' or 'local'",
"description": "label indicating where the subject type entry originates from, i.e. 'folio' or 'local'",
"enum": [
"folio",
"local"
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/org/folio/InventoryKafkaTopic.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public enum InventoryKafkaTopic implements KafkaTopic {
CAMPUS("campus"),
SUBJECT_TYPE("subject-types"),
INSTITUTION("institution"),
REINDEX_RECORDS("reindex-records");
REINDEX_RECORDS("reindex-records"),
SUBJECT_SOURCE("subject-sources");

private static final String DEFAULT_NUM_PARTITIONS_PROPERTY = "KAFKA_DOMAIN_TOPIC_NUM_PARTITIONS";
private static final String DEFAULT_NUM_PARTITIONS_VALUE = "50";
Expand All @@ -37,7 +38,8 @@ public enum InventoryKafkaTopic implements KafkaTopic {
CAMPUS, Pair.of("KAFKA_CAMPUS_TOPIC_NUM_PARTITIONS", "1"),
INSTITUTION, Pair.of("KAFKA_INSTITUTION_TOPIC_NUM_PARTITIONS", "1"),
SUBJECT_TYPE, Pair.of("KAFKA_SUBJECT_TYPE_TOPIC_NUM_PARTITIONS", "1"),
REINDEX_RECORDS, Pair.of("KAFKA_REINDEX_RECORDS_TOPIC_NUM_PARTITIONS", "16")
REINDEX_RECORDS, Pair.of("KAFKA_REINDEX_RECORDS_TOPIC_NUM_PARTITIONS", "16"),
SUBJECT_SOURCE, Pair.of("KAFKA_SUBJECT_SOURCE_TOPIC_NUM_PARTITIONS", "1")
);

private final String topic;
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/org/folio/persist/SubjectSourceRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.folio.persist;

import static org.folio.rest.persist.PgUtil.postgresClient;
import static org.folio.services.subjectsource.SubjectSourceService.SUBJECT_SOURCE;

import io.vertx.core.Context;
import java.util.Map;
import org.folio.rest.jaxrs.model.SubjectSource;

public class SubjectSourceRepository extends AbstractRepository<SubjectSource> {

public SubjectSourceRepository(Context context, Map<String, String> okapiHeaders) {
super(postgresClient(context, okapiHeaders), SUBJECT_SOURCE, SubjectSource.class);
}
}
73 changes: 73 additions & 0 deletions src/main/java/org/folio/rest/impl/SubjectSourceApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.folio.rest.impl;

import static io.vertx.core.Future.succeededFuture;
import static org.folio.rest.support.EndpointFailureHandler.handleFailure;

import io.vertx.core.AsyncResult;
import io.vertx.core.Context;
import io.vertx.core.Handler;
import java.util.Map;
import javax.ws.rs.core.Response;
import org.folio.rest.jaxrs.model.SubjectSource;
import org.folio.rest.jaxrs.resource.SubjectSources;
import org.folio.services.subjectsource.SubjectSourceService;

public class SubjectSourceApi implements SubjectSources {

@Override
public void getSubjectSources(String query, String totalRecords, int offset,
int limit,
Map<String, String> okapiHeaders,
Handler<AsyncResult<Response>> asyncResultHandler,
Context vertxContext) {
new SubjectSourceService(vertxContext, okapiHeaders)
.getByQuery(query, offset, limit)
.onSuccess(response -> asyncResultHandler.handle(succeededFuture(response)))
.onFailure(handleFailure(asyncResultHandler));
}

@Override
public void postSubjectSources(SubjectSource entity,
Map<String, String> okapiHeaders,
Handler<AsyncResult<Response>> asyncResultHandler,
Context vertxContext) {
new SubjectSourceService(vertxContext, okapiHeaders)
.create(entity)
.onSuccess(response -> asyncResultHandler.handle(succeededFuture(response)))
.onFailure(handleFailure(asyncResultHandler));
}

@Override
public void getSubjectSourcesBySubjectSourceId(String subjectSourceId,
Map<String, String> okapiHeaders,
Handler<AsyncResult<Response>> asyncResultHandler,
Context vertxContext) {
new SubjectSourceService(vertxContext, okapiHeaders)
.getById(subjectSourceId)
.onSuccess(response -> asyncResultHandler.handle(succeededFuture(response)))
.onFailure(handleFailure(asyncResultHandler));
}

@Override
public void deleteSubjectSourcesBySubjectSourceId(String subjectSourceId,
Map<String, String> okapiHeaders,
Handler<AsyncResult<Response>> asyncResultHandler,
Context vertxContext) {
new SubjectSourceService(vertxContext, okapiHeaders)
.delete(subjectSourceId)
.onSuccess(response -> asyncResultHandler.handle(succeededFuture(response)))
.onFailure(handleFailure(asyncResultHandler));
}

@Override
public void putSubjectSourcesBySubjectSourceId(String subjectSourceId,
SubjectSource entity,
Map<String, String> okapiHeaders,
Handler<AsyncResult<Response>> asyncResultHandler,
Context vertxContext) {
new SubjectSourceService(vertxContext, okapiHeaders)
.update(subjectSourceId, entity)
.onSuccess(response -> asyncResultHandler.handle(succeededFuture(response)))
.onFailure(handleFailure(asyncResultHandler));
}
}
6 changes: 6 additions & 0 deletions src/main/java/org/folio/rest/support/ResponseUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
import org.folio.HttpStatus;

public final class ResponseUtil {

public static final String SOURCE_CANNOT_BE_FOLIO =
"Illegal operation: Source field cannot be set to folio";
public static final String SOURCE_CANNOT_BE_UPDATED =
"Illegal operation: Source field cannot be updated";

private ResponseUtil() { }

public static boolean isUpdateSuccessResponse(Response response) {
Expand Down
Loading

0 comments on commit f3fe256

Please sign in to comment.