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

MODTLR-10: Support for cross-tenant requests #10

Merged
merged 16 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"methods": ["POST"],
"pathPattern": "/tlr/ecs-tlr",
"permissionsRequired": ["tlr.ecs-tlr.post"],
"modulePermissions": []
"modulePermissions": [
"circulation.requests.item.post"
]
}
]
},
Expand Down Expand Up @@ -71,9 +73,22 @@
}
},
"env": [
{ "name": "JAVA_OPTIONS",
{
"name": "JAVA_OPTIONS",
"value": "-XX:MaxRAMPercentage=66.0"
},
{
"name": "OKAPI_URL",
"value": "http://okapi:9130"
},
{
"name": "KAFKA_HOST",
"value": "kafka"
},
{
"name": "KAFKA_PORT",
"value": "9092"
},
OleksandrVidinieiev marked this conversation as resolved.
Show resolved Hide resolved
{ "name": "DB_HOST", "value": "postgres" },
{ "name": "DB_PORT", "value": "5432" },
{ "name": "DB_USERNAME", "value": "folio_admin" },
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/folio/client/CirculationClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.folio.client;

import org.folio.domain.dto.Request;
import org.folio.spring.config.FeignClientConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;

@FeignClient(name = "circulation", url = "${folio.okapi-url}", configuration = FeignClientConfiguration.class)
public interface CirculationClient {

@PostMapping("/circulation/requests")
Request createRequest(Request request);
alexanderkurash marked this conversation as resolved.
Show resolved Hide resolved
}
1 change: 1 addition & 0 deletions src/main/java/org/folio/domain/entity/EcsTlrEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class EcsTlrEntity {
private String requestType;
private String requestLevel;
private Date requestExpirationDate;
private Date requestDate;
private String patronComments;
private String fulfillmentPreference;
private UUID pickupServicePointId;
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/folio/domain/mapper/EcsTlrMapper.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.folio.domain.mapper;

import org.folio.domain.dto.EcsTlr;
import org.folio.domain.dto.Request;
import org.folio.domain.entity.EcsTlrEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
Expand Down Expand Up @@ -49,4 +50,6 @@ default String mapRequestLevelToString(EcsTlr.RequestLevelEnum requestLevelEnum)
default String mapFulfillmentPreferenceToString(EcsTlr.FulfillmentPreferenceEnum fulfillmentPreferenceEnum) {
return fulfillmentPreferenceEnum != null ? fulfillmentPreferenceEnum.getValue() : null;
}

Request mapDtoToRequest(EcsTlr ecsTlr);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.folio.exception;

import lombok.Getter;

@Getter
public class TenantScopedExecutionException extends RuntimeException {
private final String tenantId;

public TenantScopedExecutionException(Exception cause, String tenantId) {
super(cause);
this.tenantId = tenantId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.folio.service;

import java.util.concurrent.Callable;

public interface TenantScopedExecutionService {

<T> T execute(String tenantId, Callable<T> action);
}
17 changes: 17 additions & 0 deletions src/main/java/org/folio/service/impl/EcsTlrServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import java.util.Optional;
import java.util.UUID;

import org.folio.client.CirculationClient;
import org.folio.domain.dto.EcsTlr;
import org.folio.domain.dto.Request;
import org.folio.domain.mapper.EcsTlrMapper;
import org.folio.repository.EcsTlrRepository;
import org.folio.service.TenantScopedExecutionService;
import org.folio.service.EcsTlrService;
import org.springframework.stereotype.Service;

Expand All @@ -19,6 +22,8 @@ public class EcsTlrServiceImpl implements EcsTlrService {

private final EcsTlrRepository ecsTlrRepository;
private final EcsTlrMapper requestsMapper;
private final CirculationClient circulationClient;
private final TenantScopedExecutionService tenantScopedExecutionService;

@Override
public Optional<EcsTlr> get(UUID id) {
Expand All @@ -31,8 +36,20 @@ public Optional<EcsTlr> get(UUID id) {
@Override
public EcsTlr post(EcsTlr ecsTlr) {
log.debug("post:: parameters ecsTlr: {}", () -> ecsTlr);
createRemoteRequest(ecsTlr, "university"); // TODO: replace with real tenantId

return requestsMapper.mapEntityToDto(ecsTlrRepository.save(
requestsMapper.mapDtoToEntity(ecsTlr)));
}

private Request createRemoteRequest(EcsTlr ecsTlr, String tenantId) {
log.info("createRemoteRequest:: creating remote request for ECS TLR {} and tenant {}", ecsTlr.getId(), tenantId);
Request mappedRequest = requestsMapper.mapDtoToRequest(ecsTlr);
Request createdRequest = tenantScopedExecutionService.execute(tenantId,
() -> circulationClient.createRequest(mappedRequest));
log.info("createRemoteRequest:: request created: {}", createdRequest.getId());
log.debug("createRemoteRequest:: request: {}", () -> createdRequest);

return createdRequest;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.folio.service.impl;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

import org.folio.exception.TenantScopedExecutionException;
import org.folio.service.TenantScopedExecutionService;
import org.folio.spring.FolioExecutionContext;
import org.folio.spring.FolioModuleMetadata;
import org.folio.spring.integration.XOkapiHeaders;
import org.folio.spring.scope.FolioExecutionContextSetter;
import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Service
@RequiredArgsConstructor
@Log4j2
public class TenantScopedExecutionServiceImpl implements TenantScopedExecutionService {

private final FolioModuleMetadata moduleMetadata;
private final FolioExecutionContext executionContext;

@Override
public <T> T execute(String tenantId, Callable<T> action) {
log.info("execute:: tenantId: {}", tenantId);
Map<String, Collection<String>> headers = executionContext.getAllHeaders();
headers.put(XOkapiHeaders.TENANT, List.of(tenantId));

try (var x = new FolioExecutionContextSetter(moduleMetadata, headers)) {
return action.call();
} catch (Exception e) {
log.error("execute:: tenantId: {}", tenantId, e);
throw new TenantScopedExecutionException(e, tenantId);
}
}
}
4 changes: 4 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ folio:
enabled: true
environment: ${ENV:folio}
okapi-url: ${OKAPI_URL:http://okapi:9130}
logging:
feign:
enabled: true
level: full
alexanderkurash marked this conversation as resolved.
Show resolved Hide resolved
management:
endpoints:
web:
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/db/changelog/changes/initial_schema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<column name="requester_id" type="uuid"/>
<column name="request_type" type="varchar(255)"/>
<column name="request_level" type="varchar(255)"/>
<column name="request_date" type="timestamp with time zone"/>
<column name="request_expiration_date" type="timestamp with time zone"/>
<column name="patron_comments" type="varchar(255)"/>
<column name="fulfillment_preference" type="varchar(255)"/>
Expand Down
15 changes: 15 additions & 0 deletions src/main/resources/log4j2.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
status = error
name = PropertiesConfig
packages = org.folio.spring.logging

appenders = console

appender.console.type = Console
appender.console.name = STDOUT

appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{HH:mm:ss} [$${folio:requestid:-}] [$${folio:tenantid:-}] [$${folio:userid:-}] [$${folio:moduleid:-}] %-5p %-20.20C{1} %m%n

rootLogger.level = debug
rootLogger.appenderRefs = debug
rootLogger.appenderRef.stdout.ref = STDOUT
4 changes: 3 additions & 1 deletion src/main/resources/swagger.api/ecs-tlr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ components:
ecs-tlr:
$ref: 'schemas/EcsTlr.yaml#/EcsTlr'
errorResponse:
$ref: schemas/errors.json
$ref: 'schemas/errors.json'
request:
$ref: 'schemas/request.json'
parameters:
requestId:
name: requestId
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/swagger.api/schemas/EcsTlr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ EcsTlr:
description: "Date when the request expires"
type: string
format: date-time
requestDate:
OleksandrVidinieiev marked this conversation as resolved.
Show resolved Hide resolved
description: "Date when the request was placed"
type: string
format: date-time
patronComments:
description: "Comments made by the patron"
type: string
Expand All @@ -42,3 +46,4 @@ EcsTlr:
- requestType
- requestLevel
- fulfillmentPreference
- requestDate
57 changes: 57 additions & 0 deletions src/main/resources/swagger.api/schemas/override-blocks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"description": "Blocks to override (e.g. during checkout or renewal)",
"properties": {
"itemNotLoanableBlock": {
"description": "'Item not loanable' block",
"type": "object",
"properties": {
"dueDate": {
"description": "Due date for a new loan",
"type": "string",
"format": "date-time"
}
},
"additionalProperties": false,
"required": [
"dueDate"
]
},
"patronBlock": {
"description": "Automated patron block",
"type": "object",
"additionalProperties": false
},
"itemLimitBlock": {
"description": "Item limit block",
"type": "object",
"additionalProperties": false
},
"renewalBlock": {
"description": "Renewal block",
"type": "object",
"additionalProperties": false
},
"renewalDueDateRequiredBlock": {
"description": "Override renewal block which requires due date field",
"type": "object",
"properties": {
"dueDate": {
"description": "Due date for a new loan",
"type": "string",
"format": "date-time"
}
},
"additionalProperties": false,
"required": [
"dueDate"
]
},
"comment": {
"description": "Reason for override",
"type": "string"
}
},
"additionalProperties": false
}
35 changes: 35 additions & 0 deletions src/main/resources/swagger.api/schemas/request-search-index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Request fields used for search",
"type": "object",
"properties": {
"callNumberComponents": {
"type": "object",
"description": "Effective call number components",
"properties": {
"callNumber": {
"type": "string",
"description": "Effective Call Number is an identifier assigned to an item or its holding and associated with the item."
},
"prefix": {
"type": "string",
"description": "Effective Call Number Prefix is the prefix of the identifier assigned to an item or its holding and associated with the item."
},
"suffix": {
"type": "string",
"description": "Effective Call Number Suffix is the suffix of the identifier assigned to an item or its holding and associated with the item."
}
},
"additionalProperties": false
},
"shelvingOrder": {
"type": "string",
"description": "A system generated normalization of the call number that allows for call number sorting in reports and search results"
},
"pickupServicePointName": {
"description": "The name of the request pickup service point",
"type": "string"
}
},
"additionalProperties": false
}
Loading