Skip to content
Snippets Groups Projects
Commit 35ff1b0f authored by OZGCloud's avatar OZGCloud
Browse files

OZG-6811 remove bescheid-manager except for bescheid grpc service and add document service

parent fc369fe9
No related branches found
No related tags found
No related merge requests found
Showing
with 29 additions and 1058 deletions
...@@ -17,3 +17,4 @@ target/ ...@@ -17,3 +17,4 @@ target/
.idea .idea
*.iml *.iml
*.orig *.orig
.vscode/
...@@ -17,12 +17,18 @@ ...@@ -17,12 +17,18 @@
<properties> <properties>
<vorgang-manager.version>2.13.0</vorgang-manager.version> <vorgang-manager.version>2.13.0</vorgang-manager.version>
<nachrichten-manager.version>2.9.0</nachrichten-manager.version> <nachrichten-manager.version>2.9.0</nachrichten-manager.version>
<document-manager.version>1.0.0-PR-1-SNAPSHOT</document-manager.version>
<api-lib.version>0.12.0</api-lib.version> <api-lib.version>0.12.0</api-lib.version>
<spring-cloud-config-client.version>4.1.3</spring-cloud-config-client.version> <spring-cloud-config-client.version>4.1.3</spring-cloud-config-client.version>
</properties> </properties>
<dependencies> <dependencies>
<!-- ozg-cloud --> <!-- ozg-cloud -->
<dependency>
<groupId>de.ozgcloud.document</groupId>
<artifactId>document-manager-server</artifactId>
<version>${document-manager.version}</version>
</dependency>
<dependency> <dependency>
<groupId>de.ozgcloud.vorgang</groupId> <groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager-interface</artifactId> <artifactId>vorgang-manager-interface</artifactId>
...@@ -109,7 +115,8 @@ ...@@ -109,7 +115,8 @@
</dependency> </dependency>
<!--dev tools--> <!--dev
tools-->
<dependency> <dependency>
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <artifactId>mapstruct</artifactId>
......
package de.ozgcloud.bescheid;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import de.ozgcloud.bescheid.vorgang.VorgangId;
import de.ozgcloud.common.binaryfile.FileId;
import lombok.Builder;
import lombok.Getter;
import lombok.Singular;
@Builder(toBuilder = true)
@Getter
public class Bescheid {
public static final String FIELD_VORGANG_ID = "vorgangId";
public static final String FIELD_CREATED_BY = "createdBy";
public static final String FIELD_BEWILLIGT = "bewilligt";
public static final String FIELD_STATUS = "status";
public static final String FIELD_BESCHEID_DOCUMENT = "bescheidDocument";
public static final String FIELD_BESCHIEDEN_AM = "beschiedenAm";
public static final String FIELD_SEND_BY = "sendBy";
public static final String FIELD_NACHRICHT_TEXT = "nachrichtText";
public static final String FIELD_NACHRICHT_SUBJECT = "nachrichtSubject";
public static final String FIELD_ATTACHMENTS = "attachments";
public static final String FIELD_SENT_INFO = "sentInfo";
private BescheidId id;
private long version;
private VorgangId vorgangId;
private UserId createdBy;
private boolean bewilligt;
private Status status;
private FileId bescheidDocument;
private LocalDate beschiedenAm;
private SendBy sendBy;
private SentInfo sentInfo;
@Builder.Default
private Optional<String> nachrichtSubject = Optional.empty();
@Builder.Default
private Optional<String> nachrichtText = Optional.empty();
@Singular
private List<FileId> attachments;
public enum Status {
DRAFT, SENT;
public boolean not(Object value) {
return !hasValue(value);
}
public boolean hasValue(Object value) {
return this.name().equalsIgnoreCase(String.valueOf(value));
}
}
public enum SendBy {
NACHRICHT, MANUAL;
public boolean notValue(Object sendByValue) {
return !hasValue(sendByValue);
}
public boolean hasValue(Object sendByValue) {
return this.name().equalsIgnoreCase(String.valueOf(sendByValue));
}
}
}
\ No newline at end of file
package de.ozgcloud.bescheid;
import static de.ozgcloud.common.grpc.GrpcUtil.*;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import de.ozgcloud.bescheid.common.callcontext.CurrentUserService;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ForwardingClientCall.SimpleForwardingClientCall;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
@Component("bescheidCallContextInterceptor")
public class BescheidCallContextAttachingInterceptor implements ClientInterceptor {
public static final String BESCHEID_MANAGER_CLIENT_NAME = "OzgCloud_BescheidManager";
@Autowired
private CurrentUserService userService;
// <A> = Request, <B> = Response
@Override
public <A, B> ClientCall<A, B> interceptCall(MethodDescriptor<A, B> method, CallOptions callOptions, Channel next) {
return new CallContextAttachingClientCall<>(next.newCall(method, callOptions));
}
final class CallContextAttachingClientCall<A, B> extends SimpleForwardingClientCall<A, B> {
protected CallContextAttachingClientCall(ClientCall<A, B> delegate) {
super(delegate);
}
@Override
public void start(Listener<B> responseListener, Metadata headers) {
headers.merge(buildCallContextMetadata());
super.start(responseListener, headers);
}
private Metadata buildCallContextMetadata() {
var metadata = new Metadata();
userService.getUser().getUserId().ifPresent(userId -> metadata.put(HEADER_KEY_USER_ID, userId.getBytes()));
metadata.put(HEADER_KEY_CLIENT_NAME, BESCHEID_MANAGER_CLIENT_NAME.getBytes());
metadata.put(HEADER_KEY_REQUEST_ID, generateRequestId().getBytes());
return metadata;
}
// TODO requestId für command handling uebertragen / erstellen
private String generateRequestId() {
return UUID.randomUUID().toString();
}
}
}
package de.ozgcloud.bescheid;
import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandExecutedEvent;
class BescheidCreatedEvent extends CommandExecutedEvent {
public BescheidCreatedEvent(Command command) {
super(command.getId());
}
public BescheidCreatedEvent(Command command, String createdItemId) {
super(command, createdItemId);
}
}
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.bescheid;
import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandExecutedEvent;
class BescheidDeletedEvent extends CommandExecutedEvent {
public BescheidDeletedEvent(Command command) {
super(command);
}
}
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.bescheid;
import static java.util.Objects.*;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.stereotype.Component;
import de.ozgcloud.bescheid.Bescheid.SendBy;
import de.ozgcloud.bescheid.binaryfile.BinaryFileService;
import de.ozgcloud.bescheid.common.callcontext.CurrentUserService;
import de.ozgcloud.bescheid.common.user.UserProfileService;
import de.ozgcloud.bescheid.vorgang.VorgangId;
import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandCreatedEvent;
import de.ozgcloud.command.CommandExecutedEvent;
import de.ozgcloud.command.CommandFailedEvent;
import de.ozgcloud.common.attached_item.AttachedItemService;
import de.ozgcloud.common.errorhandling.TechnicalException;
import de.ozgcloud.document.BescheidDocumentCreatedEvent;
import de.ozgcloud.document.Document;
import de.ozgcloud.document.DocumentService;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Log4j2
@Component
@RequiredArgsConstructor
class BescheidEventListener {
public static final String CREATE_BESCHEID_ORDER = "CREATE_BESCHEID";
public static final String DELETE_BESCHEID_ORDER = "DELETE_BESCHEID";
public static final String UPDATE_BESCHEID_ORDER = "UPDATE_BESCHEID";
public static final String CREATE_BESCHEID_DOCUMENT_ORDER = "CREATE_BESCHEID_DOCUMENT";
public static final String SEND_BESCHEID_ORDER = "SEND_BESCHEID";
public static final Predicate<Command> IS_CREATE_BESCHEID_COMMAND = command -> CREATE_BESCHEID_ORDER.equals(command.getOrder());
private static final String IS_CREATE_BESCHEID = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_CREATE_BESCHEID_COMMAND.test(event.getSource())}";
public static final Predicate<Command> IS_DELETE_BESCHEID_COMMAND = command -> DELETE_BESCHEID_ORDER.equals(command.getOrder());
private static final String IS_DELETE_BESCHEID = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_DELETE_BESCHEID_COMMAND.test(event.getSource())}";
public static final Predicate<Command> IS_UPDATE_BESCHEID_COMMAND = command -> UPDATE_BESCHEID_ORDER.equals(command.getOrder());
private static final String IS_UPDATE_BESCHEID = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_UPDATE_BESCHEID_COMMAND.test(event.getSource())}";
public static final Predicate<Command> IS_CREATE_BESCHEID_DOCUMENT_COMMAND = command -> CREATE_BESCHEID_DOCUMENT_ORDER.equals(command.getOrder());
private static final String IS_CREATE_BESCHEID_DOCUMENT = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_CREATE_BESCHEID_DOCUMENT_COMMAND.test(event.getSource())}";
public static final Predicate<Command> IS_SEND_BESCHEID_COMMAND = command -> nonNull(command) && SEND_BESCHEID_ORDER.equals(command.getOrder());
private static final String IS_SEND_BESCHEID = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_SEND_BESCHEID_COMMAND.test(event.getSource())}";
private static final String IS_SEND_BESCHEID_ORDER = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_SEND_BESCHEID_COMMAND.test(event.getCommand())}";
static final String VORGANG_ID_BODY_KEY = "vorgangId";
static final String BESCHEID_VOM_BODY_KEY = "bescheidVom";
static final String GENEHMIGT_BODY_KEY = "genehmigt";
private static final String LOG_MESSAGE_TEMPLATE = "{}. Command failed.";
private static final String ERROR_MESSAGE_TEMPLATE = "Error on executing %s Command.";
private final BescheidService service;
private final BinaryFileService fileService;
private final AttachedItemService attachedItemService;
private final DocumentService documentService;
private final ApplicationEventPublisher eventPublisher;
private final CurrentUserService userService;
private final UserProfileService userProfileService;
@EventListener(condition = IS_CREATE_BESCHEID)
public void onCreateBescheidCommand(CommandCreatedEvent event) {
runWithSecurityContext(event.getSource(), this::doCreateBescheid);
}
void doCreateBescheid(Command command) {
var createdItemId = service.createBescheidDraft(command);
eventPublisher.publishEvent(new BescheidCreatedEvent(command, createdItemId));
}
@EventListener(condition = IS_DELETE_BESCHEID)
public void onDeleteBescheid(CommandCreatedEvent event) {
runWithSecurityContext(event.getSource(), this::doDeleteBescheid);
}
void doDeleteBescheid(Command command) {
attachedItemService.deleteBescheidDraft(command);
eventPublisher.publishEvent(new BescheidDeletedEvent(command));
}
@EventListener(condition = IS_UPDATE_BESCHEID)
public void onUpdateBescheidCommand(CommandCreatedEvent event) {
runWithSecurityContext(event.getSource(), this::doUpdateBescheid);
}
void doUpdateBescheid(Command command) {
attachedItemService.updateBescheidDraft(command, getDocument(command));
eventPublisher.publishEvent(new BescheidUpdatedEvent(command));
}
Optional<Document> getDocument(Command command) {
return Optional.ofNullable(MapUtils.getString(command.getBodyObject(), Bescheid.FIELD_BESCHEID_DOCUMENT))
.map(StringUtils::trimToNull)
.map(documentService::getDocument);
}
@EventListener(condition = IS_CREATE_BESCHEID_DOCUMENT)
public void onCreatedBescheidDocument(CommandCreatedEvent event) {
runWithSecurityContext(event.getSource(), this::doCreateBescheidDocument);
}
void doCreateBescheidDocument(Command command) {
var bescheid = doCreateBescheidBiz(command);
var bescheidDocument = documentService.createBescheidDocument(command, bescheid);
eventPublisher.publishEvent(new BescheidDocumentCreatedEvent(command, bescheidDocument));
}
BescheidResponse doCreateBescheidBiz(@NonNull Command command) {
var bescheid = service.createBescheid(createRequest(command));
return fileService.uploadBescheidFile(bescheid);
}
@EventListener(condition = IS_SEND_BESCHEID)
public void onSendBescheidCommand(CommandCreatedEvent event) {
runWithSecurityContext(event.getSource(), this::doSendBescheid);
}
void doSendBescheid(Command command) {
var bescheidItem = attachedItemService.getItem(command.getRelationId());
if (SendBy.MANUAL.hasValue(bescheidItem.getItem().get(Bescheid.FIELD_SEND_BY))) {
service.sendBescheidManually(bescheidItem, command);
} else if (SendBy.NACHRICHT.hasValue(bescheidItem.getItem().get(Bescheid.FIELD_SEND_BY))) {
service.sendBescheidPostfachMail(bescheidItem, command);
} else {
throw new TechnicalException("Bescheid has unexpected sendBy value: '%s'. Allowed values are %s."
.formatted(bescheidItem.getItem().get(Bescheid.FIELD_SEND_BY),
Arrays.stream(SendBy.values()).map(SendBy::name).collect(Collectors.joining(","))));
}
eventPublisher.publishEvent(new BescheidSentEvent(command));
}
BescheidRequest createRequest(Command command) {
var eventBody = command.getBodyObject();
var builder = BescheidRequest.builder();
builder.vorgangId(VorgangId.from(command.getVorgangId()));
Optional.ofNullable(MapUtils.getString(eventBody, BESCHEID_VOM_BODY_KEY))
.map(LocalDate::parse).ifPresent(builder::bescheidVom);
Optional.ofNullable(MapUtils.getBoolean(eventBody, GENEHMIGT_BODY_KEY))
.ifPresentOrElse(builder::genehmigt, () -> builder.genehmigt(true));
builder.createFor(userProfileService.getUserProfile());
return builder.build();
}
@EventListener(condition = IS_SEND_BESCHEID_ORDER)
public void onBescheidSent(CommandExecutedEvent event) {
runWithSecurityContext(event.getCommand(), this::setAntragBewilligung);
}
void setAntragBewilligung(Command command) {
service.setAntragBewilligung(attachedItemService.getItem(command.getRelationId()));
}
void runWithSecurityContext(Command command, Consumer<Command> commandExecutor) {
SecurityContext prevContext = null;
try {
prevContext = userService.startSecurityContext(command);
commandExecutor.accept(command);
} catch (Exception e) {
var errorMessage = ERROR_MESSAGE_TEMPLATE.formatted(command.getOrder());
LOG.error(LOG_MESSAGE_TEMPLATE, errorMessage, e);
eventPublisher.publishEvent(new CommandFailedEvent(command.getId(), buildErrorMessage(errorMessage, e)));
} finally {
userService.resetSecurityContext(prevContext);
}
}
private String buildErrorMessage(String message, Exception cause) {
try {
StringBuilder sb = new StringBuilder(message);
if (StringUtils.isNotBlank(cause.getMessage())) {
sb.append(" Cause: ").append(cause.getMessage());
}
return sb.toString();
} catch (Exception e2) {
LOG.error("Error in building Error Message (sick).", e2);
return message;
}
}
}
...@@ -4,13 +4,16 @@ import java.util.Optional; ...@@ -4,13 +4,16 @@ import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import de.ozgcloud.bescheid.BescheidServiceGrpc.BescheidServiceImplBase; import de.ozgcloud.bescheid.BescheidServiceGrpc.BescheidServiceImplBase;
import de.ozgcloud.bescheid.vorgang.VorgangId; import de.ozgcloud.document.bescheid.Bescheid;
import de.ozgcloud.document.bescheid.BescheidService;
import de.ozgcloud.document.bescheid.vorgang.VorgangId;
import io.grpc.stub.StreamObserver; import io.grpc.stub.StreamObserver;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.devh.boot.grpc.server.service.GrpcService; import net.devh.boot.grpc.server.service.GrpcService;
@GrpcService @GrpcService
@RequiredArgsConstructor @RequiredArgsConstructor
@Deprecated
class BescheidGrpcService extends BescheidServiceImplBase { class BescheidGrpcService extends BescheidServiceImplBase {
private final BescheidService service; private final BescheidService service;
......
package de.ozgcloud.bescheid;
import de.ozgcloud.common.datatype.StringBasedValue;
public class BescheidId extends StringBasedValue {
BescheidId(String id) {
super(id);
}
public static BescheidId from(String id) {
return new BescheidId(id);
}
}
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.bescheid;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class BescheidManagerConfig {
private String version;
private String javaVersion;
private Features features;
@Builder
@Getter
static class Features {
private boolean canCreateBescheidDocument;
}
}
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.bescheid;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import de.ozgcloud.apilib.common.command.OzgCloudCommandService;
import de.ozgcloud.apilib.common.command.grpc.CommandMapper;
import de.ozgcloud.apilib.common.command.grpc.GrpcOzgCloudCommandService;
import de.ozgcloud.bescheid.common.callcontext.BescheidManagerCallContextProvider;
import de.ozgcloud.vorgang.grpc.command.CommandServiceGrpc;
import net.devh.boot.grpc.client.inject.GrpcClient;
@Configuration
public class BescheidManagerConfiguration {
public static final String COMMAND_SERVICE_NAME = "bescheid_OzgCloudCommandService";
@GrpcClient("command-manager")
private CommandServiceGrpc.CommandServiceBlockingStub commandServiceStub;
@Bean(COMMAND_SERVICE_NAME)
OzgCloudCommandService grpcOzgCloudCommandService(CommandMapper commandMapper, BescheidManagerCallContextProvider contextProvider) {
return new GrpcOzgCloudCommandService(commandServiceStub, commandMapper, contextProvider,
GrpcOzgCloudCommandService.DEFAULT_COMMAND_REQUEST_THRESHOLD_MILLIS);
}
}
package de.ozgcloud.bescheid; package de.ozgcloud.bescheid;
import static de.ozgcloud.bescheid.Bescheid.*; import static de.ozgcloud.document.bescheid.Bescheid.*;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Collection; import java.util.Collection;
...@@ -21,18 +21,26 @@ import org.mapstruct.NullValuePropertyMappingStrategy; ...@@ -21,18 +21,26 @@ import org.mapstruct.NullValuePropertyMappingStrategy;
import org.mapstruct.ReportingPolicy; import org.mapstruct.ReportingPolicy;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import de.ozgcloud.bescheid.vorgang.VorgangId;
import de.ozgcloud.command.Command; import de.ozgcloud.command.Command;
import de.ozgcloud.common.attached_item.AttachedItem;
import de.ozgcloud.common.attached_item.AttachedItemService;
import de.ozgcloud.common.binaryfile.FileId; import de.ozgcloud.common.binaryfile.FileId;
import de.ozgcloud.common.datatype.StringBasedValue; import de.ozgcloud.common.datatype.StringBasedValue;
import de.ozgcloud.document.bescheid.Bescheid;
import de.ozgcloud.document.bescheid.Bescheid.SendBy;
import de.ozgcloud.document.bescheid.BescheidCallContextAttachingInterceptor;
import de.ozgcloud.document.bescheid.BescheidId;
import de.ozgcloud.document.bescheid.BescheidManagerConfig;
import de.ozgcloud.document.bescheid.SentInfo;
import de.ozgcloud.document.bescheid.UserId;
import de.ozgcloud.document.bescheid.vorgang.VorgangId;
import de.ozgcloud.document.common.attached_item.AttachedItem;
import de.ozgcloud.document.common.attached_item.AttachedItemService;
@Mapper(uses = { SentInfoMapper.class }, // @Mapper(uses = { SentInfoMapper.class }, //
unmappedTargetPolicy = ReportingPolicy.WARN, // unmappedTargetPolicy = ReportingPolicy.WARN, //
collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, // collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, //
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, // nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, //
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) // nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) //
@Deprecated
public abstract class BescheidMapper { public abstract class BescheidMapper {
@Autowired @Autowired
......
package de.ozgcloud.bescheid;
import de.ozgcloud.bescheid.vorgang.Vorgang;
public interface BescheidRemoteService {
BescheidResponse create(BescheidRequest request, Vorgang vorgang);
}
package de.ozgcloud.bescheid;
import java.time.LocalDate;
import de.ozgcloud.bescheid.common.user.UserProfile;
import de.ozgcloud.bescheid.vorgang.VorgangId;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class BescheidRequest {
private VorgangId vorgangId;
private LocalDate bescheidVom;
private boolean genehmigt;
private UserProfile createFor;
}
package de.ozgcloud.bescheid;
import java.io.File;
import java.util.List;
import java.util.Optional;
import de.ozgcloud.bescheid.vorgang.Vorgang.ServiceKonto;
import de.ozgcloud.bescheid.vorgang.VorgangId;
import de.ozgcloud.common.binaryfile.FileId;
import lombok.Builder;
import lombok.Getter;
import lombok.Singular;
import lombok.With;
@Builder(toBuilder = true)
@Getter
public class BescheidResponse {
private VorgangId vorgangId;
private boolean bewilligt;
private UserId createdBy;
private String bescheidFileName;
private File bescheidFile;
@With
private FileId bescheidFileId;
private String contentType;
private long size;
@Builder.Default
private Optional<String> nachrichtSubject = Optional.empty();
@Builder.Default
private Optional<String> nachrichtText = Optional.empty();
@Singular
private List<FileId> attachments;
private ServiceKonto serviceKonto;
}
\ No newline at end of file
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.bescheid;
import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandExecutedEvent;
public class BescheidSentEvent extends CommandExecutedEvent {
public BescheidSentEvent(final Command command) {
super(command);
}
}
package de.ozgcloud.bescheid;
import static java.util.Objects.*;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.annotation.PostConstruct;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.info.BuildProperties;
import org.springframework.stereotype.Service;
import de.ozgcloud.apilib.common.command.OzgCloudCommand;
import de.ozgcloud.apilib.common.command.OzgCloudCommandService;
import de.ozgcloud.apilib.common.command.OzgCloudCreateSubCommandsRequest;
import de.ozgcloud.apilib.common.command.grpc.CommandMapper;
import de.ozgcloud.bescheid.administration.AdministrationService;
import de.ozgcloud.bescheid.attributes.ClientAttributeService;
import de.ozgcloud.bescheid.common.freemarker.TemplateHandler;
import de.ozgcloud.bescheid.common.user.UserProfileService;
import de.ozgcloud.bescheid.vorgang.Vorgang;
import de.ozgcloud.bescheid.vorgang.VorgangId;
import de.ozgcloud.bescheid.vorgang.VorgangService;
import de.ozgcloud.command.Command;
import de.ozgcloud.common.attached_item.AttachedItem;
import de.ozgcloud.common.attached_item.AttachedItemService;
import de.ozgcloud.common.binaryfile.FileId;
import de.ozgcloud.common.errorhandling.TechnicalException;
import de.ozgcloud.document.Document;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Service
@Log4j2
@RequiredArgsConstructor
public class BescheidService {
static final String DEFAULT_NACHRICHT_SUBJECT = "Ihr Bescheid zum Antrag";
public static final String DEFAULT_NACHRICHT_TEMPLATE_FILE = "bescheid.nachrichtTemplate.txt.ftlh";
public static final String KEY_TEMPLATE_SIGNATURE = "signature";
private static final String ERROR_MESSAGE_NO_SERVICE = "No Bescheid Endpoint is configured.";
static final String VORGANG_BESCHEIDEN_ORDER = "VORGANG_BESCHEIDEN";
static final String SUBCOMMANDS_EXECUTION_MODE = "PARALLEL";
static final String SEND_POSTFACH_NACHRICHT_ORDER = "SEND_POSTFACH_NACHRICHT";
static final String FIELD_POSTFACH_ID = "postfachId";
static final String FIELD_REPLY_OPTION = "replyOption";
static final String REPLY_OPTION = "FORBIDDEN";
static final String FIELD_SUBJECT = "subject";
static final String FIELD_MAIL_BODY = "mailBody";
static final String FIELD_ATTACHMENTS = "attachments";
static final String SUBJECT = "Ihr Bescheid zum Antrag";
private final VorgangService vorgangService;
private final AttachedItemService attachedItemService;
private final UserProfileService userProfileService;
@Qualifier(BescheidManagerConfiguration.COMMAND_SERVICE_NAME)
private final OzgCloudCommandService commandService;
private final AdministrationService administrationService;
private final CommandMapper commandMapper;
private final ClientAttributeService bescheidClientAttributeService;
private final BuildProperties buildProperties;
private final Optional<BescheidRemoteService> remoteService;
private final BescheidMapper mapper;
private final TemplateHandler templateHandler;
@PostConstruct
void logStatus() {
remoteService.ifPresentOrElse(
service -> LOG.info("Bescheid-Manager is configured."),
() -> LOG.info("No BescheidRemoteService configured - Bescheid creation is not possible."));
}
public Optional<Bescheid> findDraft(String vorgangId) {
return attachedItemService.findBescheidItem(VorgangId.from(vorgangId)).map(mapper::mapFromAttachedItem);
}
public Stream<Bescheid> findAll(VorgangId vorgangId) {
return attachedItemService.findAllBescheid(vorgangId).stream().map(mapper::mapFromAttachedItem);
}
public String createBescheidDraft(Command createBescheidDraftCommand) {
validateBescheidData(createBescheidDraftCommand.getBodyObject());
return attachedItemService.createBescheidDraft(buildBescheidDraft(createBescheidDraftCommand));
}
void validateBescheidData(Map<String, Object> bodyObject) {
validateBescheidField(bodyObject, Bescheid.FIELD_BESCHIEDEN_AM);
validateBescheidField(bodyObject, Bescheid.FIELD_BEWILLIGT);
validateSendBy(MapUtils.getString(bodyObject, Bescheid.FIELD_SEND_BY));
}
void validateBescheidField(Map<String, Object> bescheid, String field) {
if (isNull(bescheid.get(field))) {
throw new TechnicalException("Fields '%s' is required for bescheid creation".formatted(field));
}
}
void validateSendBy(String sendBy) {
if (isUnknownSendByValue(sendBy)) {
var possibleSendByValues = Arrays.stream(Bescheid.SendBy.values()).map(Bescheid.SendBy::name)
.collect(Collectors.joining(","));
var message = String.format("Unexpected value for field '%s': %s. Allowed are: %s", Bescheid.FIELD_SEND_BY, sendBy,
possibleSendByValues);
throw new TechnicalException(message);
}
}
private boolean isUnknownSendByValue(String sendByValue) {
return Arrays.stream(Bescheid.SendBy.values()).noneMatch(sendBy -> sendBy.hasValue(sendByValue));
}
Bescheid buildBescheidDraft(Command command) {
var bescheid = mapper.mapFromCommand(command);
var resultBuilder = bescheid.toBuilder().id(null).version(0).status(Bescheid.Status.DRAFT);
if (bescheid.getNachrichtSubject().isEmpty()) {
resultBuilder.nachrichtSubject(Optional.of(DEFAULT_NACHRICHT_SUBJECT));
}
if (bescheid.getNachrichtText().isEmpty()) {
resultBuilder.nachrichtText(Optional.of(createNachrichtText()));
}
return resultBuilder.build();
}
String createNachrichtText() {
return templateHandler.fillTemplate(DEFAULT_NACHRICHT_TEMPLATE_FILE, getTemplateParameters());
}
Map<String, Object> getTemplateParameters() {
return administrationService.getNachrichtSignature().map(signature -> Map.<String, Object>of(KEY_TEMPLATE_SIGNATURE, signature))
.orElseGet(Collections::emptyMap);
}
public BescheidResponse createBescheid(BescheidRequest request) {
checkRemoteService();
return doCreateBescheid(request);
}
private BescheidResponse doCreateBescheid(BescheidRequest request) {
var vorgang = vorgangService.getById(request.getVorgangId());
return remoteService.map(service -> service.create(request, vorgang))
.map(bescheid -> updateBescheid(bescheid, vorgang))
.orElseThrow(() -> new TechnicalException(ERROR_MESSAGE_NO_SERVICE));
}
private BescheidResponse updateBescheid(BescheidResponse bescheid, Vorgang vorgang) {
return bescheid.toBuilder().vorgangId(vorgang.getId()).serviceKonto(vorgang.getServiceKonto()).build();
}
private void checkRemoteService() {
if (remoteService.isEmpty()) {
LOG.error(ERROR_MESSAGE_NO_SERVICE);
throw new TechnicalException(ERROR_MESSAGE_NO_SERVICE);
}
}
public void sendBescheidManually(AttachedItem bescheidItem, Command parentCommand) {
validateBescheidSendManually(bescheidItem, parentCommand.getRelationVersion());
var vorgang = vorgangService.getById(VorgangId.from(bescheidItem.getVorgangId()));
addSubCommands(parentCommand.getId(), buildSetBescheidSentSubCommands(bescheidItem, vorgang, getUserId()));
}
void validateBescheidSendManually(AttachedItem bescheidItem, long version) {
validateBescheid(bescheidItem, version);
var sendBy = bescheidItem.getItem().get(Bescheid.FIELD_SEND_BY);
if (Bescheid.SendBy.MANUAL.notValue(sendBy)) {
throw new TechnicalException(
"Bescheid has unexpected sendBy value: '%s'. Expected is %s".formatted(sendBy, Bescheid.SendBy.MANUAL));
}
}
public void sendBescheidPostfachMail(AttachedItem bescheidItem, Command parentCommand) {
validateBescheidSendPostfach(bescheidItem, parentCommand.getRelationVersion());
var vorgang = vorgangService.getById(VorgangId.from(bescheidItem.getVorgangId()));
var userId = getUserId();
var subCommands = buildSetBescheidSentSubCommands(bescheidItem, vorgang, userId);
subCommands.add(buildSendPostfachNachrichtCommand(bescheidItem, vorgang.getServiceKonto(), userId));
addSubCommands(parentCommand.getId(), subCommands);
}
public void validateBescheidSendPostfach(AttachedItem bescheidItem, long version) {
validateBescheid(bescheidItem, version);
var sendBy = bescheidItem.getItem().get(Bescheid.FIELD_SEND_BY);
if (Bescheid.SendBy.NACHRICHT.notValue(sendBy)) {
throw new TechnicalException(
"Bescheid has unexpected sendBy value: '%s'. Expected is %s".formatted(sendBy, Bescheid.SendBy.NACHRICHT));
}
if (getNachrichtSubject(bescheidItem).isEmpty()) {
throw new TechnicalException("Bescheid has no nachricht subject");
}
if (getNachrichtText(bescheidItem).isEmpty()) {
throw new TechnicalException("Bescheid has no nachricht text");
}
}
void validateBescheid(AttachedItem bescheidItem, long version) {
if (bescheidItem.getVersion() != version) {
throw new TechnicalException("Bescheid has different version. Expected: %d, but was: %d".formatted(version, bescheidItem.getVersion()));
}
var status = MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_STATUS);
if (Bescheid.Status.DRAFT.not(status)) {
throw new TechnicalException("Bescheid has status '%s'. Bescheid must have status DRAFT".formatted(status));
}
if (StringUtils.isBlank(MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_BESCHEID_DOCUMENT))) {
throw new TechnicalException("Bescheid has no document");
}
}
Optional<String> getNachrichtSubject(AttachedItem bescheidItem) {
return Optional.ofNullable(MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_NACHRICHT_SUBJECT))
.map(StringUtils::trimToNull);
}
Optional<String> getNachrichtText(AttachedItem bescheidItem) {
return Optional.ofNullable(MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_NACHRICHT_TEXT))
.map(StringUtils::trimToNull);
}
String getUserId() {
return userProfileService.getUserProfile().getId().toString();
}
List<OzgCloudCommand> buildSetBescheidSentSubCommands(AttachedItem bescheidItem, Vorgang vorgang, String userId) {
var subCommands = new ArrayList<OzgCloudCommand>();
subCommands.add(buildVorgangBescheidenCommand(vorgang));
subCommands.add(buildSetBescheidSentStatusCommand(bescheidItem, userId));
return subCommands;
}
OzgCloudCommand buildVorgangBescheidenCommand(Vorgang vorgang) {
return OzgCloudCommand.builder()
.vorgangId(commandMapper.toOzgCloudVorgangId(vorgang.getId().toString()))
.relationId(commandMapper.mapRelationId(vorgang.getId().toString()))
.relationVersion(vorgang.getVersion())
.order(VORGANG_BESCHEIDEN_ORDER)
.build();
}
OzgCloudCommand buildSetBescheidSentStatusCommand(AttachedItem bescheidItem, String userId) {
return OzgCloudCommand.builder()
.relationId(commandMapper.mapRelationId(bescheidItem.getId()))
.relationVersion(bescheidItem.getVersion())
.order(AttachedItemService.PATCH_ATTACHED_ITEM)
.bodyObject(Map.of(
AttachedItem.PROPERTY_ID, bescheidItem.getId(),
AttachedItem.PROPERTY_ITEM, buildBescheidSentStatusItem(userId)))
.build();
}
Map<String, Object> buildBescheidSentStatusItem(String userId) {
return Map.of(
Bescheid.FIELD_STATUS, Bescheid.Status.SENT.name(),
Bescheid.FIELD_SENT_INFO, buildSentInfoMap(userId));
}
Map<String, Object> buildSentInfoMap(String userId) {
return Map.of(
SentInfo.FIELD_SENT_AT, ZonedDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME),
SentInfo.FIELD_SENT_BY, userId);
}
OzgCloudCommand buildSendPostfachNachrichtCommand(AttachedItem bescheidItem, Vorgang.ServiceKonto serviceKonto, String userId) {
var bodyObject = buildSendNachrichtCommandBody(bescheidItem, buildPostfachAddress(serviceKonto.getPostfachAddresses().getFirst(),
serviceKonto.getType()));
return OzgCloudCommand.builder()
.vorgangId(commandMapper.toOzgCloudVorgangId(bescheidItem.getVorgangId()))
.relationId(commandMapper.mapRelationId(bescheidItem.getVorgangId()))
.order(SEND_POSTFACH_NACHRICHT_ORDER)
.createdBy(commandMapper.toOzgCloudUserId(userId))
.bodyObject(bodyObject)
.build();
}
Map<String, Object> buildPostfachAddress(Vorgang.PostfachAddress postfachAddress, String serviceKontoType) {
return Map.of(Vorgang.PostfachAddress.FIELD_TYPE, postfachAddress.getType(),
Vorgang.PostfachAddress.FIELD_VERSION, postfachAddress.getVersion(),
Vorgang.PostfachAddress.FIELD_IDENTIFIER, postfachAddress.getIdentifier(),
Vorgang.ServiceKonto.FIELD_SERVICEKONTO_TYPE, serviceKontoType);
}
Map<String, Object> buildSendNachrichtCommandBody(AttachedItem bescheidItem, Map<String, Object> postfachAddress) {
return Map.of(
FIELD_REPLY_OPTION, REPLY_OPTION,
FIELD_SUBJECT, getNachrichtSubject(bescheidItem).orElse(SUBJECT),
FIELD_MAIL_BODY, getNachrichtText(bescheidItem).orElse(StringUtils.EMPTY),
FIELD_ATTACHMENTS, buildAttachments(bescheidItem),
Vorgang.ServiceKonto.FIELD_POSTFACH_ADDRESS, postfachAddress);
}
List<String> buildAttachments(AttachedItem bescheidItem) {
return Stream.concat(Stream.of(getBescheidFileId(bescheidItem)), getAttachments(bescheidItem).stream()).map(FileId::toString).toList();
}
FileId getBescheidFileId(AttachedItem bescheidItem) {
var bescheidDocument = attachedItemService.getItem(MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_BESCHEID_DOCUMENT));
return FileId.from(MapUtils.getString(bescheidDocument.getItem(), Document.FIELD_DOCUMENT_FILE));
}
List<FileId> getAttachments(AttachedItem bescheidItem) {
var attachmentsObject = bescheidItem.getItem().get(Bescheid.FIELD_ATTACHMENTS);
if (attachmentsObject instanceof Collection<?> attachments) {
return attachments.stream().map(String::valueOf).map(FileId::from).toList();
}
return getStringAsList(attachmentsObject);
}
List<FileId> getStringAsList(Object attachmentsObject) {
if (attachmentsObject instanceof String attachment && StringUtils.isNotBlank(attachment)) {
return Collections.singletonList(FileId.from(attachment));
}
return Collections.emptyList();
}
void addSubCommands(String parentCommandId, List<OzgCloudCommand> subCommands) {
commandService.addSubCommands(buildCreateSubCommandsRequest(parentCommandId, subCommands));
}
OzgCloudCreateSubCommandsRequest buildCreateSubCommandsRequest(String parentCommandId, List<OzgCloudCommand> subCommands) {
return OzgCloudCreateSubCommandsRequest.builder()
.parentId(parentCommandId)
.executionMode(SUBCOMMANDS_EXECUTION_MODE)
.completedIfSubsCompleted(true)
.subCommands(subCommands)
.build();
}
public void setAntragBewilligung(AttachedItem bescheidItem) {
try {
bescheidClientAttributeService.setAntragResult(bescheidItem.getVorgangId(), getBewilligt(bescheidItem));
} catch (Exception e) {
LOG.error("Cannot set antrag result on vorgang (id: {})", bescheidItem.getVorgangId(), e);
}
}
boolean getBewilligt(AttachedItem bescheidItem) {
return MapUtils.getBooleanValue(bescheidItem.getItem(), Bescheid.FIELD_BEWILLIGT, false);
}
public BescheidManagerConfig getConfig() {
return BescheidManagerConfig.builder()
.version(buildProperties.getVersion())
.javaVersion(getJavaVersion())
.features(buildFeatures())
.build();
}
String getJavaVersion() {
return System.getProperty("java.version");
}
BescheidManagerConfig.Features buildFeatures() {
return BescheidManagerConfig.Features.builder()
.canCreateBescheidDocument(remoteService.isPresent())
.build();
}
public Bescheid getBescheid(String id) {
return mapper.mapFromAttachedItem(attachedItemService.getItem(id));
}
}
package de.ozgcloud.bescheid;
import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandExecutedEvent;
class BescheidUpdatedEvent extends CommandExecutedEvent {
public BescheidUpdatedEvent(Command command) {
super(command);
}
}
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.bescheid;
import java.time.ZonedDateTime;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class SentInfo {
public static final String FIELD_SENT_BY = "sentBy";
public static final String FIELD_SENT_AT = "sentAt";
private String sentBy;
private ZonedDateTime sentAt;
}
...@@ -12,7 +12,10 @@ import org.mapstruct.Mapper; ...@@ -12,7 +12,10 @@ import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy; import org.mapstruct.ReportingPolicy;
import de.ozgcloud.document.bescheid.SentInfo;
@Mapper(unmappedTargetPolicy = ReportingPolicy.WARN, collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) @Mapper(unmappedTargetPolicy = ReportingPolicy.WARN, collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED)
@Deprecated
public interface SentInfoMapper { public interface SentInfoMapper {
String GRPC_DATE_TIME_FORMATTER = "yyyy-MM-dd'T'HH:mm:ss'Z'"; String GRPC_DATE_TIME_FORMATTER = "yyyy-MM-dd'T'HH:mm:ss'Z'";
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment