diff --git a/README.md b/README.md index 9e2d81a9d779ab9b7ea0397ce5850415a1ea9998..16fe92f86e739dbcfc7ad5a9832351f1c36ad6e4 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,157 @@ # OSIv2-Postfach-Anbindung für OZG-Cloud-Nachrichten -Anbindung des OSIv2-Postfachs für die OZG-Cloud. +Senden und Empfangen von Postfach-Nachrichten über die OSI-Postfach-Facade 2.0 (OPF). +Das Maven-Artefakt `osiv2-postach` kann in ein Spring-Boot eingebunden werden. +Die Spring-Bean `osiPostfachRemoteService` mit dem Interface +`PostfachRemoteService` kann mit `ozgcloud.osiv2.enabled: true` aktiviert werden. +## Konfiguration +Um auf die Postfach-Facade zugreifen zu können, muss der Client sich beim Servicekonto anmelden. +Hierzu wird `ozgcloud.osiv2.auth` konfiguriert, wie bspw. hier für Stage-Schleswig-Holstein: +```yaml +client-id: 'OZG-Kopfstelle-SH' +client-secret: 'changeme' +scope: default, access_urn:dataport:osi:sh:stage:ozgkopfstelle +token-uri: 'https://idp.serviceportal-stage.schleswig-holstein.de/webidp2/connect/token' +resource: 'urn:dataport:osi:postfach:rz2:stage:sh' +``` +Für die Postfach-Facade muss zudem die URL, der Tenant, und der SAML-Name-Identifier konfiguriert werden. +Beim Nachschlagen des Postfachs der Antragsteller*in wird ein Postfach mit übereinstimmenden Tenant präferiert. +Zudem werden Tenant und SAML-Name-Identifier beim Upload von Anhängen angegeben. + +Hierzu wird `ozgcloud.osiv2.api` konfiguriert: +```yaml +url: 'https://api-gateway-stage.dataport.de:443/api/osi_postfach/1.0.0' +tenant: 'SH' +name-identifier: 'ozgkopfstelle' +``` +<small>(Der Wert von `name-identifier` wird momentan nicht von der Postfach-Facade geprüft.)</small> + +## Senden einer Postfach-Nachricht +Der Aufruf `sendMessage(PostfachNachricht)` sendet eine Nachricht an die Antragsteller*in. Jede Nachricht ist stets in Bezug zu einem Vorgang. + +1. Mit dem SAML-Name-Identifier der Antragsteller*in wird ein Postfach nachgeschlagen, welches die Nachricht empfangen soll. +2. Vor dem Senden der Nachricht werden alle Anhänge in die Quarantäne hochgeladen und auf Viren geprüft. +3. Nach dem erfolgreichen Hochladen der Anhänge wird die Nachricht an das Postfach gesendet. +```mermaid +%% Senden einer Nachricht + sequenceDiagram + participant N as nachrichten-manager + participant A as file-manager + participant B as Client + participant C as OPF + activate N + N->>B: OsiPostfachRemoteService::sendMessage + activate B + Note left of B: (1) Nachschlagen einer Empfänger-Postfach-Adresse + B->>C: POST /MailboxDirectory/v1/Lookup + activate C + C-->>B: {mailboxId} + deactivate C + Note left of B: (2) Hochladen der Anhänge + B->>A: GRPC findBinaryFilesMetaData + activate A + A-->>B: {Liste an Anhang-Metadaten} + deactivate A + loop Für jeden Anhang + B->>A: GRPC GetBinaryFileContent + activate A + loop Für jeden Daten-Chunk + B->>C: POST /Quarantine/v1/Upload/Chunked (chunk) + activate C + C-->>B: + deactivate C + end + A-->>B: + deactivate A + B->>C: POST /Quarantine/v1/Upload/Chunked (empty chunk) + activate C + C-->>B: + deactivate C + end + loop Regelmäßiges Polling bis alle Anhänge geprüft sind + loop Für jeden Anhang + B->>C: GET /Quarantine/v1/Upload/{guid} + activate C + C-->>B: + deactivate C + end + end + Note left of B: (3) Nachricht senden + B->>C: POST /MessageExchange/v1/Send/{mailboxId} + activate C + C-->>B: + deactivate C + B-->>N: + deactivate B + deactivate N +``` + +## Empfangen von Postfach-Nachrichten +Der Aufruf `getAllMessages()` holt alle Nachrichten, die an das OZG-Cloud-Postfach gerichtet sind ab. + +1. Es wird eine Liste an Nachrichten-Kennungen abgerufen. +2. Für jede Nachricht wird die Nachricht abgerufen und die Anhänge heruntergeladen. +3. Ein Stream an Nachrichten wird zurückgegeben. Anhand der Vorgangs-Kennung lässt sich jede Nachricht stets einem Vorgang zugordnen. +```mermaid +%% Empfangen von Nachrichten + sequenceDiagram + participant N as nachrichten-manager + participant A as file-manager + participant B as Client + participant C as OPF + activate N + N->>B: OsiPostfachRemoteService::getAllMessages + activate B + B->>C: GET /MessageExchange/v1/Receive + activate C + C-->>B: {Liste an Nachrichten-Kennungen (max 100)} + deactivate C + loop Für jede Nachrichten-Kennung (messageId) + Note left of B: (1) Nachricht abrufen + B->>C: GET /MessageExchange/v1/Receive/{messageId} + activate C + C-->>B: {Nachricht mit Anhang-Metadaten} + deactivate C + Note left of B: (2) Herunterladen der Anhänge + loop Für jeden Anhang + B->>C: GET /MessageExchange/v1/Receive/{messageId}/Attachment/{attachmentId} + activate C + B->>A: GRPC UploadBinaryFileAsStream + activate A + A-->>B: + deactivate A + C-->>B: + deactivate C + end + Note left of B: (3) Bereitstellung der Postfach-Nachricht + B-->>N: {Postfach-Nachricht als Stream-Element} + end + deactivate B + deactivate N +``` + +Der nachrichten-manager erhält beim Aufruf von `getAllMessages` einen Stream von Postfach-Nachrichten. Nach der erfolgreichen Verarbeitung einer Nachricht sollte er `deleteMessage` aufrufen: + +```mermaid +%% Empfangen einer Nachricht + sequenceDiagram + participant N as nachrichten-manager + participant B as Client + participant C as OPF + + activate N + N->>B: OsiPostfachRemoteService::deleteMessage + activate B + B->>C: GET /MessageExchange/v1/Delete/{messageId} + activate C + C->>B: + deactivate C + B-->>N: + deactivate B + deactivate N +``` ## Client-Authentifizierung beim Servicekonto @@ -17,7 +166,7 @@ Der Resource-Server liest die Resource-URI aus dem `aud`-Claim (siehe [RFC 9068, curl -v --output auth_response.json \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=client_credentials" \ - --data-urlencode "client_id=OZG-Kopfstelle" \ + --data-urlencode "client_id=OZG-Kopfstelle-SH" \ --data-urlencode "client_secret=${SH_STAGE_CLIENT_SECRET}" \ --data-urlencode "scope=default access_urn:dataport:osi:sh:stage:ozgkopfstelle" \ --data-urlencode "resource=urn:dataport:osi:postfach:rz2:stage:sh" \ diff --git a/spec/postfach-api-facade.yaml b/spec/postfach-api-facade.yaml index 9a67d1cbd7ec9a71af0f96dfbc0512d6a5abd6aa..d494dbb4bf978d68832eb6b2b1eae40eac4677fc 100644 --- a/spec/postfach-api-facade.yaml +++ b/spec/postfach-api-facade.yaml @@ -756,6 +756,7 @@ paths: detail: Der Dienst ist zurzeit nicht verfügbar. /MailboxDirectory/v1/Lookup: post: + operationId: lookupMailboxIds tags: - MailboxDirectory summary: Liefert eine Liste der zu NameIdentifiern gehörenden OSI-Postfächer diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/config/ApiClientConfiguration.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/config/ApiClientConfiguration.java index 6b28b57315c3cce2c26f9e88c6275fafe3f989a4..50496cab096aca2ec5c3bc87bb0f9bd128df3de9 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/config/ApiClientConfiguration.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/config/ApiClientConfiguration.java @@ -29,6 +29,7 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestClient; import de.ozgcloud.nachrichten.postfach.osiv2.gen.ApiClient; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MailboxDirectoryApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MessageExchangeApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.QuarantineApi; import lombok.RequiredArgsConstructor; @@ -57,6 +58,11 @@ public class ApiClientConfiguration { return new QuarantineApi(apiClient); } + @Bean + MailboxDirectoryApi mailboxDirectoryApi(ApiClient apiClient) { + return new MailboxDirectoryApi(apiClient); + } + @Bean @SneakyThrows ApiClient apiClient() { diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2ExceptionHandler.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2ExceptionHandler.java index 395f4272265f5695cb06f9a0cd91ae9ddc396949..f7e42d6e24bdab325a7cfe25083c5f0e3895aebc 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2ExceptionHandler.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2ExceptionHandler.java @@ -19,6 +19,9 @@ public class Osi2ExceptionHandler { if (exception instanceof ResourceAccessException) { return PostfachMessageCode.SERVER_CONNECTION_FAILED_MESSAGE_CODE; } + if (exception instanceof Osi2MailboxNotFoundException) { + return PostfachMessageCode.SEND_FAILED_UNKNOWN_POSTFACH_ID_MESSAGE_CODE; + } return PostfachMessageCode.PROCESS_FAILED_MESSAGE_CODE; } @@ -33,5 +36,4 @@ public class Osi2ExceptionHandler { return restClientResponseException.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(404)); } - } diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2MailboxNotFoundException.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2MailboxNotFoundException.java new file mode 100644 index 0000000000000000000000000000000000000000..5a44f277641c52bb6a77aef30a26f073ba710f81 --- /dev/null +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2MailboxNotFoundException.java @@ -0,0 +1,7 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.exception; + +public class Osi2MailboxNotFoundException extends Osi2RuntimeException { + public Osi2MailboxNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/FileChunkInfo.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/FileChunkInfo.java index a119cbaf62538ee51a8f5c30e680936a06d207f8..f63abe3450e3864847791d12e6e6e85e6a754197 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/FileChunkInfo.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/FileChunkInfo.java @@ -16,6 +16,12 @@ public record FileChunkInfo( ) { public AbstractResource createUploadResource(InputStream fileInputStream) { return new AbstractResource() { + + @Override + public String getFilename() { + return upload.file().getName(); + } + @Override public String getDescription() { return "File chunk " + chunkIndex + " of " + upload.getLoggableString(); diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/Osi2Attachment.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/Osi2Attachment.java index dc41ec7685a33e83990ce954b9097e523dfe57cc..a948a1a1017236c3fba12508d4bcafb242287105 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/Osi2Attachment.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/Osi2Attachment.java @@ -10,7 +10,7 @@ public record Osi2Attachment( String guid, OzgCloudFile file ) { - public static final long CHUNK_SIZE = 100L * (2L << 10); + public static final long CHUNK_SIZE = (2L << 14); public static Osi2Attachment from(OzgCloudFile file) { return Osi2Attachment.builder() diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/Osi2Message.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/Osi2Message.java index f527fceac71309d1bc39f427e1e8e08f367ce89c..ecda0e2648b2ae43cd56cd39013013181a6eedc6 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/Osi2Message.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/Osi2Message.java @@ -3,14 +3,13 @@ package de.ozgcloud.nachrichten.postfach.osiv2.model; import java.time.ZonedDateTime; import java.util.List; -import de.ozgcloud.nachrichten.postfach.PostfachAddress; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import lombok.Builder; @Builder public record Osi2Message( String vorgangId, - PostfachAddress postfachAddress, + String mailboxId, String messageId, ZonedDateTime createdAt, String subject, diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2MessageMapper.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2MessageMapper.java index 7d81f6f6a4a39d30952f08314ce8204ea72d6562..006fe52f3c5822efa48000eacc88887c209ab735 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2MessageMapper.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2MessageMapper.java @@ -1,16 +1,31 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.Named; import org.mapstruct.ReportingPolicy; +import de.ozgcloud.nachrichten.postfach.PostfachAddress; +import de.ozgcloud.nachrichten.postfach.PostfachAddressIdentifier; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; +import de.ozgcloud.nachrichten.postfach.StringBasedIdentifier; +import de.ozgcloud.nachrichten.postfach.osiv2.OsiPostfachRemoteService; +import de.ozgcloud.nachrichten.postfach.osiv2.model.AttachmentInfo; +import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Attachment; import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; @Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) public interface Osi2MessageMapper { + String POSTFACH_ADDRESS_VERSION = "2.0"; + int POSTFACH_ADDRESS_TYPE = 2; + @Mapping(target = "id", ignore = true) @Mapping(target = "referencedNachricht", ignore = true) @@ -20,7 +35,57 @@ public interface Osi2MessageMapper { @Mapping(target = "sentSuccessful", ignore = true) @Mapping(target = "messageCode", ignore = true) + @Mapping(target = "postfachAddress", source = "osi2Message.mailboxId", qualifiedByName = "createPostfachAddressByMailboxId") @Mapping(target = "attachments", source = "attachmentIds") @Mapping(target = "direction", constant = "IN") - PostfachNachricht toPostfachNachricht(Osi2Message osi2Message, List<String> attachmentIds); + PostfachNachricht mapPostfachNachricht(Osi2Message osi2Message, List<String> attachmentIds); + + @Named("createPostfachAddressByMailboxId") + default PostfachAddress createPostfachAddressByMailboxId(String mailBoxId) { + return PostfachAddress.builder() + .type(POSTFACH_ADDRESS_TYPE) + .version(POSTFACH_ADDRESS_VERSION) + .identifier(StringBasedIdentifier.builder() + // Note: this should be a nameIdentifier for sending but is a mailboxGuid/messageBox. + // Thus, the PostfachAddress in this mapped response can not be used for sending + .postfachId(mailBoxId) + .build()) + .serviceKontoType(OsiPostfachRemoteService.POSTFACH_TYPE_OSI) + .build(); + + } + + default String getNameIdentifier(PostfachNachricht nachricht) { + return Optional.ofNullable(nachricht.getPostfachAddress()) + .map(PostfachAddress::getIdentifier) + .map(PostfachAddressIdentifier::getStringRepresentation) + .orElseThrow(() -> new IllegalArgumentException("Missing nameIdentifier!")); + } + + @Mapping(target = "mailboxId", source = "mailboxId") + @Mapping(target = "attachments", expression = "java( mapAttachmentInfosInOriginalOrder(nachricht, uploadAttachments) )") + Osi2Message mapOsi2Message(PostfachNachricht nachricht, String mailboxId, List<Osi2Attachment> uploadAttachments); + + default List<AttachmentInfo> mapAttachmentInfosInOriginalOrder(PostfachNachricht nachricht, List<Osi2Attachment> files) { + var filesById = associateUploadWithAttachmentId(files); + return nachricht.getAttachments() + .stream() + .map(fileId -> Objects.requireNonNull( + filesById.get(fileId), + "Expect all attachmentIds are uploaded!" + )) + .map(this::mapAttachmentInfo) + .toList(); + } + + @Mapping(target = "contentType", source = "file.contentType") + @Mapping(target = "name", source = "file.name") + @Mapping(target = "size", source = "file.size") + AttachmentInfo mapAttachmentInfo(Osi2Attachment file); + + default Map<String, Osi2Attachment> associateUploadWithAttachmentId(List<Osi2Attachment> uploads) { + return uploads.stream() + .filter(upload -> upload.file() != null && upload.file().getId() != null) + .collect(Collectors.toMap(upload -> upload.file().getId().toString(), Function.identity())); + } } diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2PostfachService.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2PostfachService.java index 754a9458fcf09d45e7ea55bd5c18e574b10de913..dc08ac22f05c51102d7dff14801fe153527faa30 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2PostfachService.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2PostfachService.java @@ -19,8 +19,11 @@ public class Osi2PostfachService { public void sendMessage(PostfachNachricht nachricht) { postfachApiFacadeService.sendMessage( - nachricht, - quarantineService.uploadAttachments(nachricht.getAttachments()) + messageMapper.mapOsi2Message( + nachricht, + postfachApiFacadeService.lookupMailboxId(messageMapper.getNameIdentifier(nachricht)), + quarantineService.uploadAttachments(nachricht.getAttachments()) + ) ); } @@ -32,7 +35,7 @@ public class Osi2PostfachService { PostfachNachricht fetchPostfachNachricht(String messageGuid) { var message = postfachApiFacadeService.fetchMessageById(messageGuid); - return messageMapper.toPostfachNachricht( + return messageMapper.mapPostfachNachricht( message, persistAttachmentService.persistAttachments(message) ); diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2RequestMapper.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2RequestMapper.java index 86aa86d13ffd80d919a5f4c8b92ae31c4d39bf22..de83be90b0db68e0949dec8abfbad538ed9cfcee 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2RequestMapper.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2RequestMapper.java @@ -2,67 +2,43 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.Named; import org.mapstruct.ReportingPolicy; -import de.ozgcloud.nachrichten.postfach.PostfachAddress; -import de.ozgcloud.nachrichten.postfach.PostfachAddressIdentifier; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.DomainChunkMetaData; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeFiles; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.OutSendMessageRequestV2; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1References; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyBehavior; +import de.ozgcloud.nachrichten.postfach.osiv2.model.AttachmentInfo; import de.ozgcloud.nachrichten.postfach.osiv2.model.FileChunkInfo; -import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Attachment; +import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; @Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR) public interface Osi2RequestMapper { int MAX_NUMBER_RECEIVED_MESSAGES = 100; - @Mapping(target = "sequencenumber", source = "nachricht.vorgangId") - @Mapping(target = "body", source = "nachricht.mailBody") + @Mapping(target = "sequencenumber", source = "vorgangId") + @Mapping(target = "body", source = "mailBody") @Mapping(target = "displayName", ignore = true) @Mapping(target = "originSender", ignore = true) - @Mapping(target = "replyAction", source = "nachricht.replyOption") + @Mapping(target = "replyAction", source = "replyOption") @Mapping(target = "eidasLevel", constant = "LOW") @Mapping(target = "isObligatory", expression = "java( false )") @Mapping(target = "isHtml", expression = "java( false )") - @Mapping(target = "files", expression = "java( mapMessageExchangeFiles(nachricht, files) )") + @Mapping(target = "files", source = "attachments", qualifiedByName = "mapMessageExchangeFile") @Mapping(target = "references", expression = "java( mapReferences() )") - OutSendMessageRequestV2 mapOutSendMessageRequestV2(PostfachNachricht nachricht, List<Osi2Attachment> files); + OutSendMessageRequestV2 mapOutSendMessageRequestV2(Osi2Message message); - default List<MessageExchangeFiles> mapMessageExchangeFiles(PostfachNachricht nachricht, List<Osi2Attachment> files) { - var filesById = associateUploadWithAttachmentId(files); - return nachricht.getAttachments() - .stream() - .map(fileId -> Objects.requireNonNull( - filesById.get(fileId), - "Expect all attachmentIds are uploaded!" - )) - .map(this::mapMessageExchangeFile) - .toList(); - } - - default Map<String, Osi2Attachment> associateUploadWithAttachmentId(List<Osi2Attachment> uploads) { - return uploads.stream() - .filter(upload -> upload.file() != null && upload.file().getId() != null) - .collect(Collectors.toMap(upload -> upload.file().getId().toString(), Function.identity())); - } - - @Mapping(target = "mimeType", source = "file.contentType") - @Mapping(target = "name", source = "file.name") - @Mapping(target = "size", source = "file.size") + @Mapping(target = "mimeType", source = "contentType") @Mapping(target = "isOriginalMessage", expression = "java( false )") - MessageExchangeFiles mapMessageExchangeFile(Osi2Attachment fileUpload); + @Named("mapMessageExchangeFile") + MessageExchangeFiles mapMessageExchangeFile(AttachmentInfo fileUpload); default List<V1References> mapReferences() { return Collections.emptyList(); @@ -83,12 +59,4 @@ public interface Osi2RequestMapper { @Mapping(target = "totalChunks", expression = "java( (int) fileChunkInfo.upload().numberOfChunks() )") @Mapping(target = "totalFileSize", source = "upload.file.size") DomainChunkMetaData mapDomainChunkMetaData(FileChunkInfo fileChunkInfo); - - default String mapMailboxId(PostfachNachricht nachricht) { - return Optional.ofNullable(nachricht.getPostfachAddress()) - .map(PostfachAddress::getIdentifier) - .map(PostfachAddressIdentifier::getStringRepresentation) - .orElseThrow(() -> new IllegalArgumentException("Missing MailboxId!")); - } - } diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java index 74a34e0487b9e48ef2ee046d8a5f1ac707778fe6..a1ab4aa8615a72f161ae9418cb4f90c892a03936 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java @@ -7,23 +7,26 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import java.util.stream.Stream; + +import jakarta.validation.constraints.NotNull; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Named; import org.mapstruct.ReportingPolicy; -import de.ozgcloud.nachrichten.postfach.PostfachAddress; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; -import de.ozgcloud.nachrichten.postfach.StringBasedIdentifier; -import de.ozgcloud.nachrichten.postfach.osiv2.OsiPostfachRemoteService; +import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2MailboxNotFoundException; import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2RuntimeException; import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2UploadException; -import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeReceiveAttachment; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.DTOMailboxScopeData; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MddiMailboxIndexData; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeReceiveMessage; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeReceiveMessagesResponse; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.QuarantineFileResult; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.QuarantineStatus; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.ResponsesMailboxDirectoryResponse; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyBehavior; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyFiles; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; @@ -33,11 +36,8 @@ import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; @Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, imports = { Osi2HtmlDocument.class }) public interface Osi2ResponseMapper { - String POSTFACH_ADDRESS_VERSION = "2.0"; - int POSTFACH_ADDRESS_TYPE = 2; - @Mapping(target = "vorgangId", source = "sequencenumber") - @Mapping(target = "postfachAddress", source = "messageBox") + @Mapping(target = "mailboxId", source = "messageBox") @Mapping(target = "messageId", source = "guid") @Mapping(target = "createdAt", source = "responseTime", qualifiedByName = "mapOffsetDateTimeToZoned") @Mapping(target = "subject", source = "subject") @@ -66,20 +66,6 @@ public interface Osi2ResponseMapper { : body; } - default PostfachAddress buildPostfachAddressByPostfachId(UUID messageBox) { - return messageBox == null - ? null - : PostfachAddress.builder() - .type(POSTFACH_ADDRESS_TYPE) - .version(POSTFACH_ADDRESS_VERSION) - .identifier(StringBasedIdentifier.builder() - .postfachId(messageBox.toString()) - .build()) - .serviceKontoType(OsiPostfachRemoteService.POSTFACH_TYPE_OSI) - .build(); - - } - default PostfachNachricht.ReplyOption mapReplyAction(V1ReplyBehavior replyOption) { return replyOption == null ? PostfachNachricht.ReplyOption.FORBIDDEN @@ -113,18 +99,29 @@ public interface Osi2ResponseMapper { } default void checkChunkUploadSuccess(QuarantineFileResult quarantineFileResult) { - if (!Boolean.TRUE.equals(quarantineFileResult.getSuccess())) { + if (!Optional.ofNullable(quarantineFileResult.getError()).map(String::isBlank).orElse(true)) { throw new Osi2RuntimeException( "Chunk-Upload of file %s failed: %s".formatted(quarantineFileResult.getFileUid(), quarantineFileResult.getError()), null); } } - @Named("mapMessageExchangeReceiveAttachment") - default String mapMessageExchangeReceiveAttachment(MessageExchangeReceiveAttachment attachment) { - return Optional.ofNullable(attachment) - .map(MessageExchangeReceiveAttachment::getGuid) - .map(UUID::toString) - .orElse(null); + default Optional<String> extractMailboxIdByTenantOrFirst(@NotNull List<MddiMailboxIndexData> mailBoxes, String tenant) { + return mailBoxes.stream() + .filter(mailbox -> tenant.equals(mailbox.getTenant())) + .findFirst() + .or(() -> mailBoxes.stream().findFirst()) + .map(MddiMailboxIndexData::getMailboxguid) + .map(UUID::toString); } + default String getMailboxIdByTenantOrFirst(ResponsesMailboxDirectoryResponse responsesMailboxDirectoryResponse, String tenant) { + return Optional.ofNullable(responsesMailboxDirectoryResponse) + .map(ResponsesMailboxDirectoryResponse::getMailBoxIndex) + .map(Collection::stream) + .flatMap(Stream::findFirst) + .map(DTOMailboxScopeData::getMemberscope) + .flatMap(mailBoxes -> extractMailboxIdByTenantOrFirst(mailBoxes, tenant)) + .orElseThrow(() -> new Osi2MailboxNotFoundException("Empty mailbox lookup response! %s".formatted( + responsesMailboxDirectoryResponse != null ? responsesMailboxDirectoryResponse.toString() : "(response null)"))); + } } diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java index 989ae00cba9f3caa4f564fc9ac37b77e323a6efa..65010e59039cc40e20e6cd08adde2f0897651fa4 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java @@ -8,14 +8,13 @@ import java.util.UUID; import org.springframework.core.io.AbstractResource; import org.springframework.core.io.Resource; -import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.osiv2.ServiceIfOsi2Enabled; import de.ozgcloud.nachrichten.postfach.osiv2.config.Osi2PostfachProperties; import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2UploadException; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MailboxDirectoryApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MessageExchangeApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.QuarantineApi; import de.ozgcloud.nachrichten.postfach.osiv2.model.FileChunkInfo; -import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Attachment; import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -27,14 +26,15 @@ public class PostfachApiFacadeService { private final MessageExchangeApi messageExchangeApi; private final QuarantineApi quarantineApi; + private final MailboxDirectoryApi mailboxDirectoryApi; private final Osi2RequestMapper requestMapper; private final Osi2ResponseMapper responseMapper; private final Osi2PostfachProperties.ApiConfiguration apiConfiguration; - public void sendMessage(PostfachNachricht nachricht, List<Osi2Attachment> attachments) { + public void sendMessage(Osi2Message message) { messageExchangeApi.sendMessage( - requestMapper.mapMailboxId(nachricht), - requestMapper.mapOutSendMessageRequestV2(nachricht, attachments) + message.mailboxId(), + requestMapper.mapOutSendMessageRequestV2(message) ); } @@ -75,4 +75,11 @@ public class PostfachApiFacadeService { public Resource downloadAttachment(String messageGuid, String attachmentGuid) { return messageExchangeApi.getMessageAttachment(UUID.fromString(messageGuid), UUID.fromString(attachmentGuid)); } + + public String lookupMailboxId(String nameIdentifier) { + return responseMapper.getMailboxIdByTenantOrFirst( + mailboxDirectoryApi.lookupMailboxIds(List.of(UUID.fromString(nameIdentifier))), + apiConfiguration.getTenant() + ); + } } diff --git a/src/main/resources/application-stage.yml b/src/main/resources/application-stage.yml index 2618fc516f3b8383f3ad2d4ddfccb4c8c06eeccb..e69cb9d817c200ebe67b7f5827fac3826e273cf0 100644 --- a/src/main/resources/application-stage.yml +++ b/src/main/resources/application-stage.yml @@ -2,7 +2,7 @@ ozgcloud: osiv2: enabled: false auth: - client-id: 'OZG-Kopfstelle' + client-id: 'OZG-Kopfstelle-SH' client-secret: 'changeme' scope: default, access_urn:dataport:osi:sh:stage:ozgkopfstelle token-uri: 'https://idp.serviceportal-stage.schleswig-holstein.de/webidp2/connect/token' diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java index 550d15b32897a7017dd46e2954b9d11379249078..51dd44b370569b53609c2fbd4b558466e40c76ef 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java @@ -35,6 +35,7 @@ import de.ozgcloud.nachrichten.postfach.osiv2.factory.JsonUtil; import de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeReceiveMessagesResponseTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeSendMessageResponseTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.ResponsesMailboxDirectoryResponseTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyFilesTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.QuarantineFileResult; @@ -83,6 +84,7 @@ class OsiPostfachRemoteServiceITCase { @SneakyThrows void shouldSendRequestWithJwt() { var postfachNachricht = PostfachNachrichtTestFactory.create(); + mockLookupResponse(); mockSendResponse(); osiPostfachRemoteService.sendMessage(postfachNachricht); @@ -110,6 +112,7 @@ class OsiPostfachRemoteServiceITCase { var postfachNachrichtWithAttachment = PostfachNachrichtTestFactory.createBuilder() .attachments(List.of(textFileId)) .build(); + mockLookupResponse(); mockSendResponse(); osiPostfachRemoteService.sendMessage(postfachNachrichtWithAttachment); @@ -130,6 +133,10 @@ class OsiPostfachRemoteServiceITCase { exactly(1), postRequestedFor(urlPathTemplate("/MessageExchange/v1/Send/{mailboxId}")) ); + postfachFacadeMockServer.verify( + exactly(1), + postRequestedFor(urlPathTemplate("/MailboxDirectory/v1/Lookup")) + ); } @DisplayName("should throw postfach exception with connection error code") @@ -173,6 +180,7 @@ class OsiPostfachRemoteServiceITCase { var postfachNachrichtWithAttachment = PostfachNachrichtTestFactory.createBuilder() .attachments(List.of(textFileId)) .build(); + mockLookupResponse(); try { osiPostfachRemoteService.sendMessage(postfachNachrichtWithAttachment); @@ -204,6 +212,7 @@ class OsiPostfachRemoteServiceITCase { var postfachNachrichtWithAttachment = PostfachNachrichtTestFactory.createBuilder() .attachments(List.of(textFileId)) .build(); + mockLookupResponse(); try { osiPostfachRemoteService.sendMessage(postfachNachrichtWithAttachment); @@ -217,6 +226,17 @@ class OsiPostfachRemoteServiceITCase { ); } + private void mockLookupResponse() { + // Stub mailboxids lookup response (MessageExchangeApi::lookupMailboxIds) + postfachFacadeMockServer.stubFor(post(urlPathTemplate("/MailboxDirectory/v1/Lookup")) + .willReturn( + okJsonObj( + ResponsesMailboxDirectoryResponseTestFactory.create() + ) + ) + ); + } + private void mockSendResponse() { // Stub message send response (MessageExchangeApi::sendMessage) postfachFacadeMockServer.stubFor(post(urlPathTemplate("/MessageExchange/v1/Send/{mailboxId}")) diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/attachment/Osi2PersistAttachmentServiceTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/attachment/Osi2PersistAttachmentServiceTest.java index 2fcf40037503cf91d20e4ce5206c5c0518e6d72d..92b590f0a2727c7a7cb7890d245ae73d7e76b091 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/attachment/Osi2PersistAttachmentServiceTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/attachment/Osi2PersistAttachmentServiceTest.java @@ -3,7 +3,6 @@ package de.ozgcloud.nachrichten.postfach.osiv2.attachment; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.GrpcOzgFileTestFactory.*; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2FileUploadTestFactory.*; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory.*; -import static de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2ExceptionHandlerTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2ExceptionHandlerTest.java index df51704620bc574bcbf1c1f01c4c5ff7786ab3fc..986571b9fe186418e8cd85d879d044209cfe72d1 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2ExceptionHandlerTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/exception/Osi2ExceptionHandlerTest.java @@ -11,6 +11,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.springframework.http.HttpStatusCode; +import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestClientResponseException; class Osi2ExceptionHandlerTest { @@ -36,6 +37,22 @@ class Osi2ExceptionHandlerTest { assertThat(result).isEqualTo(SERVER_CONNECTION_FAILED_MESSAGE_CODE); } + @DisplayName("should return connection failed message code") + @Test + void shouldReturnConnectionFailedMessageCode() { + var result = handler.deriveMessageCode(new ResourceAccessException("")); + + assertThat(result).isEqualTo(SERVER_CONNECTION_FAILED_MESSAGE_CODE); + } + + @DisplayName("should return unknown postfach id message code") + @Test + void shouldReturnUnknownPostfachIdMessageCode() { + var result = handler.deriveMessageCode(new Osi2MailboxNotFoundException("")); + + assertThat(result).isEqualTo(SEND_FAILED_UNKNOWN_POSTFACH_ID_MESSAGE_CODE); + } + @DisplayName("should return processed failed error code by default") @Test void shouldReturnProcessedFailedErrorCodeByDefault() { diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java index 8aa54916936532edcdc288378702867186469fe6..97cc84d99b4783bb08df5d933bc7fb171ac28c58 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java @@ -12,7 +12,7 @@ import lombok.extern.log4j.Log4j2; @Log4j2 public class AttachmentExampleUploadUtil { - public static final byte[] EXAMPLE_TEXT_DATA = LoremIpsum.getInstance().getParagraphs(5,100).getBytes(); + public static final byte[] EXAMPLE_TEXT_DATA = LoremIpsum.getInstance().getParagraphs(5,10).getBytes(); public static String uploadTextFile(Osi2AttachmentFileService remoteService) { diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/Osi2AttachmentTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/Osi2AttachmentTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..662d3d3e0de6c38b10e7f134b3ba30263b1ca47e --- /dev/null +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/Osi2AttachmentTestFactory.java @@ -0,0 +1,21 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.factory; + +import java.util.UUID; + +import de.ozgcloud.apilib.file.OzgCloudFileTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Attachment; + +public class Osi2AttachmentTestFactory { + public static final String ATTACHMENT_GUID = UUID.randomUUID().toString(); + public static final String ATTACHMENT_GUID_2 = UUID.randomUUID().toString(); + + public static Osi2Attachment create() { + return createBuilder().build(); + } + + public static Osi2Attachment.Osi2AttachmentBuilder createBuilder() { + return Osi2Attachment.builder() + .guid(ATTACHMENT_GUID) + .file(OzgCloudFileTestFactory.create()); + } +} diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/Osi2MessageTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/Osi2MessageTestFactory.java index 8d40f117e113c3c070288228e011c27c3ead4500..b609152b4840b7533ede5c574ee7b0b3de10ac45 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/Osi2MessageTestFactory.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/Osi2MessageTestFactory.java @@ -1,29 +1,31 @@ package de.ozgcloud.nachrichten.postfach.osiv2.factory; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory.*; -import static de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory.*; import java.time.ZonedDateTime; import java.util.List; +import java.util.UUID; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; public class Osi2MessageTestFactory { + public static final String MAILBOX_ID = UUID.randomUUID().toString(); + public static Osi2Message create() { return createBuilder().build(); } public static Osi2Message.Osi2MessageBuilder createBuilder() { return Osi2Message.builder() + .vorgangId(VORGANG_ID) + .mailboxId(MAILBOX_ID) .messageId(MESSAGE_ID) .mailBody(MAIL_BODY) .subject(MAIL_SUBJECT) .replyOption(PostfachNachricht.ReplyOption.FORBIDDEN) .createdAt(ZonedDateTime.now()) - .vorgangId(VORGANG_ID) - .attachments(List.of()) - .postfachAddress(PostfachAddressTestFactory.create()); + .attachments(List.of()); } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachAddressTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachAddressTestFactory.java index de986e02f39c9781e3af9be92741d4d0b64994de..415054d28d7d399f271c100bf16f2b54d1861339 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachAddressTestFactory.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachAddressTestFactory.java @@ -7,7 +7,7 @@ import de.ozgcloud.nachrichten.postfach.StringBasedIdentifier; public class PostfachAddressTestFactory { - public static final String MAILBOX_ID = UUID.randomUUID().toString(); + public static final String NAME_IDENTIFIER = UUID.randomUUID().toString(); public static final String SERVICE_KONTO_TYPE = "TYPE1"; public static PostfachAddress create() { @@ -19,7 +19,7 @@ public class PostfachAddressTestFactory { .type(1) .serviceKontoType(SERVICE_KONTO_TYPE) .identifier(StringBasedIdentifier.builder() - .postfachId(MAILBOX_ID) + .postfachId(NAME_IDENTIFIER) .build()); } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachNachrichtTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachNachrichtTestFactory.java index aaf929ea7d99c7b3262df9458295e8bc7c647e90..3424251bc054764b96b6fbf49454806974b923f2 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachNachrichtTestFactory.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachNachrichtTestFactory.java @@ -12,7 +12,9 @@ public class PostfachNachrichtTestFactory { public static final String MAIL_SUBJECT = "AW: " + LoremIpsum.getInstance().getTitle(2, 6); public static final String VORGANG_ID = "test-vorgang-id"; public static final String USER_ID = "test-user-id"; - + public static final String MESSAGE_ID = UUID.randomUUID().toString(); + public static final ZonedDateTime CREATED_AT = ZonedDateTime.now(); + public static final PostfachNachricht.ReplyOption REPLY_OPTION = PostfachNachricht.ReplyOption.FORBIDDEN; public static PostfachNachricht create() { return createBuilder().build(); @@ -22,8 +24,9 @@ public class PostfachNachrichtTestFactory { return PostfachNachricht.builder() .mailBody(MAIL_BODY) .subject(MAIL_SUBJECT) - .replyOption(PostfachNachricht.ReplyOption.FORBIDDEN) - .createdAt(ZonedDateTime.now()) + .messageId(MESSAGE_ID) + .replyOption(REPLY_OPTION) + .createdAt(CREATED_AT) .createdBy(USER_ID) .vorgangId(VORGANG_ID) .postfachAddress(PostfachAddressTestFactory.create()); diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/ResponsesMailboxDirectoryResponseTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/ResponsesMailboxDirectoryResponseTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..9681f8d86be37e8d7766f77ba287649cc07ba264 --- /dev/null +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/ResponsesMailboxDirectoryResponseTestFactory.java @@ -0,0 +1,56 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.factory; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.DTOMailboxScopeData; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MddiMailboxIndexData; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MddiMailboxType; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.ResponsesMailboxDirectoryResponse; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1Meta; + +public class ResponsesMailboxDirectoryResponseTestFactory { + + public static final String NAME_ID = UUID.randomUUID().toString(); + public static final String MAILBOX_GUID_SH = UUID.randomUUID().toString(); + public static final String MAILBOX_GUID_HH = UUID.randomUUID().toString(); + public static final String TENANT_SH = "SH"; + public static final String TENANT_HH = "HH"; + + public static ResponsesMailboxDirectoryResponse create() { + return createBuilder( + buildMailboxWithSHTenant().build(), + buildMailboxWithHHTenant().build() + ).build(); + } + + public static ResponsesMailboxDirectoryResponse.Builder createBuilder(MddiMailboxIndexData... mailBoxes) { + return ResponsesMailboxDirectoryResponse.builder() + .mailBoxIndex(List.of(createScopeData(mailBoxes))) + .meta(V1Meta.builder().build()); + } + + public static MddiMailboxIndexData.Builder buildMailboxWithSHTenant() { + return MddiMailboxIndexData.builder() + .mailboxguid(UUID.fromString(MAILBOX_GUID_SH)) + .tenant(TENANT_SH) + .mailboxname("") + .mailboxdescription("") + .mailboxtype(MddiMailboxType.PERSONAL); + } + + public static MddiMailboxIndexData.Builder buildMailboxWithHHTenant() { + return MddiMailboxIndexData.builder() + .mailboxguid(UUID.fromString(MAILBOX_GUID_HH)) + .tenant(TENANT_HH) + .mailboxtype(MddiMailboxType.PERSONAL); + } + + private static DTOMailboxScopeData createScopeData(MddiMailboxIndexData... mailBoxes) { + return DTOMailboxScopeData.builder() + .membershipcontext(UUID.fromString(NAME_ID)) + .memberscope(Arrays.asList(mailBoxes)) + .build(); + } +} diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2MessageMapperTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2MessageMapperTest.java index 89d64bac79517999fc840fd5c2857126c879fa51..31584b2b4441a33bda5d98d3bed1a8f40e9963cb 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2MessageMapperTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2MessageMapperTest.java @@ -1,27 +1,45 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; +import static de.ozgcloud.apilib.file.OzgCloudFileTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2AttachmentTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2MessageTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachAddressTestFactory.*; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory.*; -import static java.util.Collections.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.transfer.Osi2MessageMapper.*; import static org.assertj.core.api.Assertions.*; import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mapstruct.factory.Mappers; +import de.ozgcloud.apilib.file.OzgCloudFileId; +import de.ozgcloud.apilib.file.OzgCloudFileTestFactory; +import de.ozgcloud.nachrichten.postfach.PostfachAddress; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; +import de.ozgcloud.nachrichten.postfach.osiv2.OsiPostfachRemoteService; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2AttachmentTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2MessageTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachAddressTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.model.AttachmentInfo; +import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Attachment; import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; class Osi2MessageMapperTest { private final Osi2MessageMapper mapper = Mappers.getMapper(Osi2MessageMapper.class); - @DisplayName("to PostfachNachricht") + @DisplayName("map PostfachNachricht") @Nested - class TestToPostfachNachricht { + class TestMapPostfachNachricht { private final Osi2Message osi2Message = Osi2MessageTestFactory.create(); private final String attachmentId1 = "attachmentId1"; private final String attachmentId2 = "attachmentId2"; @@ -71,7 +89,9 @@ class Osi2MessageMapperTest { void shouldMapPostfachAddress() { var result = doMapping(); - assertThat(result.getPostfachAddress()).isEqualTo(osi2Message.postfachAddress()); + assertThat(result.getPostfachAddress()).usingRecursiveComparison().isEqualTo( + mapper.createPostfachAddressByMailboxId(osi2Message.mailboxId()) + ); } @DisplayName("should map direction") @@ -91,7 +111,218 @@ class Osi2MessageMapperTest { } private PostfachNachricht doMapping() { - return mapper.toPostfachNachricht(osi2Message, List.of(attachmentId1, attachmentId2)); + return mapper.mapPostfachNachricht(osi2Message, List.of(attachmentId1, attachmentId2)); + } + } + + @DisplayName("create postfach address by mailboxId") + @Nested + class TestCreatePostfachAddressByMailboxId { + + @DisplayName("should map postfach address type") + @Test + void shouldMapPostfachAddressType() { + var result = doMapping(); + + assertThat(result.getType()).isEqualTo(POSTFACH_ADDRESS_TYPE); + } + + @DisplayName("should map version") + @Test + void shouldMapVersion() { + var result = doMapping(); + + assertThat(result.getVersion()).isEqualTo(POSTFACH_ADDRESS_VERSION); + } + + @DisplayName("should map postfachId") + @Test + void shouldMapPostfachId() { + var result = doMapping(); + + assertThat(result.getIdentifier().getStringRepresentation()).isEqualTo(MAILBOX_ID); + } + + @DisplayName("should map servicekontotype") + @Test + void shouldMapServicekontotype() { + var result = doMapping(); + + assertThat(result.getServiceKontoType()).isEqualTo(OsiPostfachRemoteService.POSTFACH_TYPE_OSI); + } + + private PostfachAddress doMapping() { + return mapper.createPostfachAddressByMailboxId(MAILBOX_ID); + } + } + + @DisplayName("map osi2 message") + @Nested + class TestMapOsi2Message { + + private static final String FILE_ID2 = UUID.randomUUID().toString(); + private final Osi2Attachment attachment1 = Osi2AttachmentTestFactory.createBuilder() + .build(); + private final Osi2Attachment attachment2 = Osi2AttachmentTestFactory.createBuilder() + .guid(ATTACHMENT_GUID_2) + .file(OzgCloudFileTestFactory.createBuilder() + .id(OzgCloudFileId.from(FILE_ID2)) + .build()) + .build(); + private final PostfachNachricht nachricht = PostfachNachrichtTestFactory.createBuilder() + .attachments(List.of(ID_STR, FILE_ID2)) + .build(); + + @DisplayName("should map vorgangId") + @Test + void shouldMapVorgangId() { + var result = doMapping(); + + assertThat(result.vorgangId()).isEqualTo(VORGANG_ID); + } + + @DisplayName("should map mailboxId") + @Test + void shouldMapMailboxId() { + var result = doMapping(); + + assertThat(result.mailboxId()).isEqualTo(MAILBOX_ID); + } + + @DisplayName("should map messsageId") + @Test + void shouldMapMesssageId() { + var result = doMapping(); + + assertThat(result.messageId()).isEqualTo(MESSAGE_ID); + } + + @DisplayName("should map createdAt") + @Test + void shouldMapCreatedAt() { + var result = doMapping(); + + assertThat(result.createdAt()).isEqualTo(CREATED_AT); + } + + @DisplayName("should map subject") + @Test + void shouldMapSubject() { + var result = doMapping(); + + assertThat(result.subject()).isEqualTo(MAIL_SUBJECT); + } + + @DisplayName("should map mailBody") + @Test + void shouldMapMailBody() { + var result = doMapping(); + + assertThat(result.mailBody()).isEqualTo(MAIL_BODY); + } + + @DisplayName("should map replyOption") + @Test + void shouldMapReplyOption() { + var result = doMapping(); + + assertThat(result.replyOption()).isEqualTo(REPLY_OPTION); + } + + @DisplayName("should map attachments") + @Test + void shouldMapAttachments() { + var result = doMapping(); + + assertThat(result.attachments()).containsExactly( + mapper.mapAttachmentInfo(attachment1), + mapper.mapAttachmentInfo(attachment2) + ); + } + + private Osi2Message doMapping() { + return mapper.mapOsi2Message( + nachricht, + MAILBOX_ID, + List.of(attachment2, attachment1) + ); + } + } + + @DisplayName("map attachment info") + @Nested + class TestMapAttachmentInfo { + + private final Osi2Attachment attachment = Osi2AttachmentTestFactory.create(); + + @DisplayName("should map guid") + @Test + void shouldMapGuid() { + var result = doMapping(); + + assertThat(result.guid()).isEqualTo(ATTACHMENT_GUID); + } + + @DisplayName("should map name") + @Test + void shouldMapName() { + var result = doMapping(); + + assertThat(result.name()).isEqualTo(OzgCloudFileTestFactory.NAME); + } + + @DisplayName("should map contentType") + @Test + void shouldMapContentType() { + var result = doMapping(); + + assertThat(result.contentType()).isEqualTo(OzgCloudFileTestFactory.CONTENT_TYPE); + } + + @DisplayName("should map size") + @Test + void shouldMapSize() { + var result = doMapping(); + + assertThat(result.size()).isEqualTo(OzgCloudFileTestFactory.SIZE); + } + + private AttachmentInfo doMapping() { + return mapper.mapAttachmentInfo(attachment); + } + } + + @DisplayName("get nameIdentifier") + @Nested + class TestGetNameIdentifier { + + @DisplayName("should map postfach address to nameIdentifier") + @Test + void shouldMapPostfachAddressToNameIdentifier() { + var result = mapper.getNameIdentifier(PostfachNachrichtTestFactory.create()); + + assertThat(result).isEqualTo(NAME_IDENTIFIER); + } + + @DisplayName("should throw on missing postfach address") + @ParameterizedTest + @MethodSource("badPostfachAddresses") + void shouldThrowOnMissingPostfachAddress(PostfachAddress badPostfachAddress) { + var nachrichtWithBadPostfachAddress = PostfachNachrichtTestFactory.createBuilder() + .postfachAddress(badPostfachAddress) + .build(); + + assertThatThrownBy(() -> mapper.getNameIdentifier(nachrichtWithBadPostfachAddress)) + .isInstanceOf(IllegalArgumentException.class); + } + + static Stream<Arguments> badPostfachAddresses() { + return Stream.of( + null, + Arguments.of(PostfachAddressTestFactory.createBuilder() + .identifier(null) + .build()) + ); } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2PostfachServiceTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2PostfachServiceTest.java index 7077d02f8f7ad83c5dd3171056079beac450b06e..8f19f2c08e1d15028b897067e39a3e1ec23ebf38 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2PostfachServiceTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2PostfachServiceTest.java @@ -1,6 +1,10 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; +import static de.ozgcloud.apilib.file.OzgCloudFileTestFactory.*; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeReceiveMessagesResponseTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2MessageTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachAddressTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; @@ -16,9 +20,12 @@ import org.mockito.Mock; import org.mockito.Spy; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; +import de.ozgcloud.nachrichten.postfach.osiv2.attachment.Osi2PersistAttachmentService; import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2FileUploadTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2MessageTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Attachment; +import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; class Osi2PostfachServiceTest { @@ -30,33 +37,65 @@ class Osi2PostfachServiceTest { private PostfachApiFacadeService postfachApiFacadeService; @Mock private Osi2QuarantineService quarantineService; + @Mock + private Osi2PersistAttachmentService persistAttachmentService; + @Mock + private Osi2MessageMapper messageMapper; @DisplayName("send message") @Nested class TestSendOsi2Message { private final PostfachNachricht nachricht = PostfachNachrichtTestFactory.create(); + private final Osi2Message osi2Message = Osi2MessageTestFactory.create(); private final List<Osi2Attachment> uploadFiles = List.of(Osi2FileUploadTestFactory.create()); @BeforeEach void mock() { + when(messageMapper.getNameIdentifier(any())).thenReturn(NAME_IDENTIFIER); + when(messageMapper.mapOsi2Message(any(), any(), any())).thenReturn(osi2Message); when(quarantineService.uploadAttachments(any())).thenReturn(uploadFiles); + when(postfachApiFacadeService.lookupMailboxId(any())).thenReturn(MAILBOX_ID); + } + + @DisplayName("should call getNameIdentifier") + @Test + void shouldCallGetNameIdentifier() { + service.sendMessage(nachricht); + + verify(messageMapper).getNameIdentifier(nachricht); + } + + @DisplayName("should call lookupMailboxId") + @Test + void shouldCallLookupMailboxId() { + service.sendMessage(nachricht); + + verify(postfachApiFacadeService).lookupMailboxId(NAME_IDENTIFIER); } - @DisplayName("should send message") + @DisplayName("should call mapOsi2Message") @Test - void shouldSendMessage() { + void shouldCallMapOsi2Message() { service.sendMessage(nachricht); - verify(postfachApiFacadeService).sendMessage(nachricht, uploadFiles); + verify(messageMapper).mapOsi2Message(nachricht, MAILBOX_ID, uploadFiles); } - @DisplayName("should upload attachments of nachricht") + @DisplayName("should call uploadAttachments") @Test - void shouldUploadAttachmentsOfNachricht() { + void shouldCallUploadAttachments() { service.sendMessage(nachricht); verify(quarantineService).uploadAttachments(nachricht.getAttachments()); } + + @DisplayName("should call sendMessage") + @Test + void shouldCallSendMessage() { + service.sendMessage(nachricht); + + verify(postfachApiFacadeService).sendMessage(osi2Message); + } } @DisplayName("receive messages") @@ -93,4 +132,47 @@ class Osi2PostfachServiceTest { } } + @DisplayName("fetch PostfachNachricht") + @Nested + class TestFetchPostfachNachricht { + + private final Osi2Message message = Osi2MessageTestFactory.create(); + private final PostfachNachricht postfachNachricht = PostfachNachrichtTestFactory.create(); + + @BeforeEach + void mock() { + when(postfachApiFacadeService.fetchMessageById(any())).thenReturn(message); + when(messageMapper.mapPostfachNachricht(any(), any())).thenReturn(postfachNachricht); + when(persistAttachmentService.persistAttachments(any())).thenReturn(List.of(ID_STR)); + } + + @DisplayName("should call fetchMessageById") + @Test + void shouldCallFetchMessageById() { + fetchPostfachNachricht(); + + verify(postfachApiFacadeService).fetchMessageById(MESSAGE_ID); + } + + @DisplayName("should call mapPostfachNachricht") + @Test + void shouldCallMapPostfachNachricht() { + fetchPostfachNachricht(); + + verify(messageMapper).mapPostfachNachricht(message, List.of(ID_STR)); + } + + @DisplayName("should return") + @Test + void shouldReturn() { + var result = fetchPostfachNachricht(); + + assertThat(result).isEqualTo(postfachNachricht); + } + + private PostfachNachricht fetchPostfachNachricht() { + return service.fetchPostfachNachricht(MESSAGE_ID); + } + } + } \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2RequestMapperTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2RequestMapperTest.java index bdf27c7d4d9171eb1c1b60da3ede0638510b176b..b6cda51ed4e9722a8ff08f0f04c7f53eb64bce37 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2RequestMapperTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2RequestMapperTest.java @@ -1,80 +1,46 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2FileUploadTestFactory.*; -import static de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachAddressTestFactory.*; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory.*; -import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; import java.util.List; import java.util.UUID; -import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; import org.mapstruct.factory.Mappers; -import de.ozgcloud.apilib.file.OzgCloudFileTestFactory; -import de.ozgcloud.nachrichten.postfach.PostfachAddress; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.AttachmentInfoTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.FileChunkInfoTestFactory; -import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2FileUploadTestFactory; -import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachAddressTestFactory; -import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2MessageTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.DomainChunkMetaData; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeFiles; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.OutSendMessageRequestV2; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1EidasLevel; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1FilestorageTarget; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyBehavior; +import de.ozgcloud.nachrichten.postfach.osiv2.model.AttachmentInfo; import de.ozgcloud.nachrichten.postfach.osiv2.model.FileChunkInfo; +import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; class Osi2RequestMapperTest { private final Osi2RequestMapper mapper = Mappers.getMapper(Osi2RequestMapper.class); - @DisplayName("map mailbox id") - @Nested - class TestMapMailboxId { - - @DisplayName("should map postfach address to mailbox id") - @Test - void shouldMapPostfachAddressToMailboxId() { - var result = mapper.mapMailboxId(PostfachNachrichtTestFactory.create()); - - assertThat(result).isEqualTo(MAILBOX_ID); - } - - @DisplayName("should throw on missing postfach address") - @ParameterizedTest - @MethodSource("badPostfachAddresses") - void shouldThrowOnMissingPostfachAddress(PostfachAddress badPostfachAddress) { - var nachrichtWithBadPostfachAddress = PostfachNachrichtTestFactory.createBuilder() - .postfachAddress(badPostfachAddress) - .build(); - - assertThatThrownBy(() -> mapper.mapMailboxId(nachrichtWithBadPostfachAddress)) - .isInstanceOf(IllegalArgumentException.class); - } - - static Stream<Arguments> badPostfachAddresses() { - return Stream.of( - null, - Arguments.of(PostfachAddressTestFactory.createBuilder() - .identifier(null) - .build()) - ); - } - } - @DisplayName("map OutSendMessageRequestV2") @Nested class TestMapOutSendOsi2MessageRequestV2 { + private final Osi2Message osi2Message = Osi2MessageTestFactory.create(); + + private final AttachmentInfo attachment1 = AttachmentInfoTestFactory.create(); + private final AttachmentInfo attachment2 = AttachmentInfoTestFactory.createBuilder() + .guid(UPLOAD_GUID2) + .build(); + @DisplayName("should map sequence number") @Test void shouldMapSequenceNumber() { @@ -118,11 +84,11 @@ class Osi2RequestMapperTest { @DisplayName("should map possible reply action") @Test void shouldMapPossibleReplyAction() { - var nachricht = PostfachNachrichtTestFactory.createBuilder() + var message = Osi2MessageTestFactory.createBuilder() .replyOption(PostfachNachricht.ReplyOption.POSSIBLE) .build(); - var result = mapper.mapOutSendMessageRequestV2(nachricht, emptyList()); + var result = mapper.mapOutSendMessageRequestV2(message); assertThat(result.getReplyAction()).isEqualTo(V1ReplyBehavior.REPLYPOSSIBLE); } @@ -130,11 +96,11 @@ class Osi2RequestMapperTest { @DisplayName("should map mandatory reply action") @Test void shouldMapMandatoryReplyAction() { - var nachricht = PostfachNachrichtTestFactory.createBuilder() + var message = Osi2MessageTestFactory.createBuilder() .replyOption(PostfachNachricht.ReplyOption.MANDATORY) .build(); - var result = mapper.mapOutSendMessageRequestV2(nachricht, emptyList()); + var result = mapper.mapOutSendMessageRequestV2(message); assertThat(result.getReplyAction()).isEqualTo(V1ReplyBehavior.REPLYMANDATORY); } @@ -171,36 +137,19 @@ class Osi2RequestMapperTest { assertThat(result.getFiles()).isEmpty(); } - @DisplayName("should map two files") + @DisplayName("should map files") @Test - void shouldMapTwoFiles() { - var files = List.of( - Osi2FileUploadTestFactory.create(), - Osi2FileUploadTestFactory.createBuilder() - .file(OzgCloudFileTestFactory.create()) - .guid(UPLOAD_GUID2) - .build() - ); - var nachricht = PostfachNachrichtTestFactory.createBuilder() - .attachments(List.of(UPLOAD_FILE_ID, OzgCloudFileTestFactory.ID_STR)) + void shouldMapFiles() { + var message = Osi2MessageTestFactory.createBuilder() + .attachments(List.of(attachment1, attachment2)) .build(); - var result = mapper.mapOutSendMessageRequestV2(nachricht, files); + var result = mapper.mapOutSendMessageRequestV2(message); - assertThat(result.getFiles()) - .usingRecursiveComparison() - .isEqualTo(files.stream().map(mapper::mapMessageExchangeFile).toList()); - } - - @DisplayName("should map files") - @Test - void shouldMapFiles() { - var result = mapper.mapOutSendMessageRequestV2( - PostfachNachrichtTestFactory.create(), - List.of(Osi2FileUploadTestFactory.create()) + assertThat(result.getFiles()).containsExactly( + mapper.mapMessageExchangeFile(attachment1), + mapper.mapMessageExchangeFile(attachment2) ); - - assertThat(result.getFiles()).isEmpty(); } @DisplayName("should map references") @@ -212,7 +161,7 @@ class Osi2RequestMapperTest { } private OutSendMessageRequestV2 doMapping() { - return mapper.mapOutSendMessageRequestV2(PostfachNachrichtTestFactory.create(), emptyList()); + return mapper.mapOutSendMessageRequestV2(osi2Message); } } @@ -252,7 +201,7 @@ class Osi2RequestMapperTest { } private MessageExchangeFiles mapFile() { - return mapper.mapMessageExchangeFile(Osi2FileUploadTestFactory.create()); + return mapper.mapMessageExchangeFile(AttachmentInfoTestFactory.create()); } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java index ff5a336d4e16028c186505786504021ef711f814..154bf87a8ede99cc7bf7585e02dafe43737da24d 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java @@ -5,6 +5,7 @@ import static de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyFilesTestFac import static de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory.*; import static org.assertj.core.api.Assertions.*; +import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; @@ -18,13 +19,16 @@ import org.mapstruct.factory.Mappers; import org.mockito.InjectMocks; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; +import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2MailboxNotFoundException; import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2RuntimeException; import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2UploadException; import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2FileUploadTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.QuarantineFileResultTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.ResponsesMailboxDirectoryResponseTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyFilesTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.QuarantineStatus; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.ResponsesMailboxDirectoryResponse; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyBehavior; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyFiles; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; @@ -53,8 +57,7 @@ class Osi2ResponseMapperTest { void shouldMapPostfachAddress() { var result = doMapping(); - assertThat(result.postfachAddress().getIdentifier()) - .hasToString(MESSAGE_BOX_ID); + assertThat(result.mailboxId()).isEqualTo(MESSAGE_BOX_ID); } @Test @@ -295,4 +298,67 @@ class Osi2ResponseMapperTest { .hasMessageContaining(Osi2FileUploadTestFactory.UPLOAD_GUID); } } + + @DisplayName("get mailboxId by tenant or first") + @Nested + class TestGetMailboxIdByTenantOrFirst { + + private final ResponsesMailboxDirectoryResponse response = ResponsesMailboxDirectoryResponseTestFactory.create(); + + @DisplayName("should throw with null response") + @Test + void shouldThrowWithNullResponse() { + + assertThatThrownBy(() -> mapper.getMailboxIdByTenantOrFirst(null, "")) + .isInstanceOf(Osi2MailboxNotFoundException.class); + } + + @DisplayName("should throw with missing index") + @Test + void shouldThrowWithMissingIndex() { + var responseWithoutMailboxIds = ResponsesMailboxDirectoryResponseTestFactory.createBuilder() + .mailBoxIndex(List.of()) + .build(); + + assertThatThrownBy( + () -> mapper.getMailboxIdByTenantOrFirst(responseWithoutMailboxIds, ResponsesMailboxDirectoryResponseTestFactory.TENANT_SH)) + .isInstanceOf(Osi2MailboxNotFoundException.class); + } + + @DisplayName("should throw with missing mailboxes") + @Test + void shouldThrowWithMissingMailboxes() { + var responseWithoutMailboxIds = ResponsesMailboxDirectoryResponseTestFactory.createBuilder() + .build(); + + assertThatThrownBy( + () -> mapper.getMailboxIdByTenantOrFirst(responseWithoutMailboxIds, ResponsesMailboxDirectoryResponseTestFactory.TENANT_SH)) + .isInstanceOf(Osi2MailboxNotFoundException.class); + } + + @DisplayName("should return mailboxId for SH") + @Test + void shouldReturnMailboxIdForSH() { + var result = mapper.getMailboxIdByTenantOrFirst(response, ResponsesMailboxDirectoryResponseTestFactory.TENANT_SH); + + assertThat(result).isEqualTo(ResponsesMailboxDirectoryResponseTestFactory.MAILBOX_GUID_SH); + } + + @DisplayName("should return mailboxId for HH") + @Test + void shouldReturnMailboxIdForHh() { + var result = mapper.getMailboxIdByTenantOrFirst(response, ResponsesMailboxDirectoryResponseTestFactory.TENANT_HH); + + assertThat(result).isEqualTo(ResponsesMailboxDirectoryResponseTestFactory.MAILBOX_GUID_HH); + } + + @DisplayName("should return first mailboxId for unknown tenant") + @Test + void shouldReturnFirstMailboxIdForUnknownTenant() { + var result = mapper.getMailboxIdByTenantOrFirst(response, "unknown"); + + assertThat(result).isEqualTo(ResponsesMailboxDirectoryResponseTestFactory.MAILBOX_GUID_SH); + } + + } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java index 0e6e97f9263715e2f246471e0364cf0e351a6e03..eafc3e250a330de08954023c11baa28ea712456d 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java @@ -1,7 +1,10 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeReceiveMessagesResponseTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2FileUploadTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2MessageTestFactory.*; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachAddressTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.ResponsesMailboxDirectoryResponseTestFactory.*; import static de.ozgcloud.nachrichten.postfach.osiv2.transfer.Osi2RequestMapper.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; @@ -18,14 +21,13 @@ import org.mockito.Mock; import org.mockito.Spy; import org.springframework.core.io.AbstractResource; -import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.osiv2.config.Osi2PostfachProperties; import de.ozgcloud.nachrichten.postfach.osiv2.factory.FileChunkInfoTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeReceiveMessagesResponseTestFactory; -import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2FileUploadTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.Osi2MessageTestFactory; -import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.ResponsesMailboxDirectoryResponseTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MailboxDirectoryApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MessageExchangeApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.QuarantineApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.DomainChunkMetaData; @@ -34,9 +36,9 @@ import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeReceiveMe import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeSendMessageResponse; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.OutSendMessageRequestV2; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.QuarantineStatus; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.ResponsesMailboxDirectoryResponse; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; import de.ozgcloud.nachrichten.postfach.osiv2.model.FileChunkInfo; -import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Attachment; import de.ozgcloud.nachrichten.postfach.osiv2.model.Osi2Message; import lombok.SneakyThrows; @@ -52,6 +54,9 @@ class PostfachApiFacadeServiceTest { @Mock QuarantineApi quarantineApi; + @Mock + MailboxDirectoryApi mailboxDirectoryApi; + @Mock Osi2RequestMapper osi2RequestMapper; @@ -71,37 +76,26 @@ class PostfachApiFacadeServiceTest { @Mock MessageExchangeSendMessageResponse messageExchangeSendMessageResponse; - private final List<Osi2Attachment> files = List.of(Osi2FileUploadTestFactory.create()); - - private final PostfachNachricht nachricht = PostfachNachrichtTestFactory.create(); + private final Osi2Message message = Osi2MessageTestFactory.create(); @BeforeEach void mock() { - when(osi2RequestMapper.mapMailboxId(any())).thenReturn(MAILBOX_ID); - when(osi2RequestMapper.mapOutSendMessageRequestV2(any(), any())).thenReturn(outSendMessageRequestV2); + when(osi2RequestMapper.mapOutSendMessageRequestV2(any())).thenReturn(outSendMessageRequestV2); when(messageExchangeApi.sendMessage(any(), any())).thenReturn(messageExchangeSendMessageResponse); } - @DisplayName("should call mapMailboxId") - @Test - void shouldCallMapMailboxId() { - service.sendMessage(nachricht, files); - - verify(osi2RequestMapper).mapMailboxId(nachricht); - } - @DisplayName("should call mapOutSendMessageRequestV2") @Test void shouldCallMapOutSendMessageRequestV2() { - service.sendMessage(nachricht, files); + service.sendMessage(message); - verify(osi2RequestMapper).mapOutSendMessageRequestV2(nachricht, files); + verify(osi2RequestMapper).mapOutSendMessageRequestV2(message); } @DisplayName("should call sendMessage") @Test void shouldCallSendMessage() { - service.sendMessage(nachricht, files); + service.sendMessage(message); verify(messageExchangeApi).sendMessage(MAILBOX_ID, outSendMessageRequestV2); } @@ -323,4 +317,74 @@ class PostfachApiFacadeServiceTest { } } + @DisplayName("download attachment") + @Nested + class TestDownloadAttachment { + @Mock + AbstractResource resource; + + @BeforeEach + void mock() { + when(messageExchangeApi.getMessageAttachment(any(), any())).thenReturn(resource); + } + + @DisplayName("should call getMessageAttachment") + @Test + void shouldCallGetMessageAttachment() { + service.downloadAttachment(MESSAGE_ID_1, UPLOAD_GUID); + + verify(messageExchangeApi).getMessageAttachment(UUID.fromString(MESSAGE_ID_1), UUID.fromString(UPLOAD_GUID)); + } + + @DisplayName("should return resource") + @Test + void shouldReturnResource() { + var result = service.downloadAttachment(MESSAGE_ID_1, UPLOAD_GUID); + + assertThat(result).isEqualTo(resource); + } + } + + @DisplayName("lookup mailboxId") + @Nested + class TestLookupMailboxId { + + private final ResponsesMailboxDirectoryResponse response = ResponsesMailboxDirectoryResponseTestFactory.create(); + + @BeforeEach + void mock() { + when(osi2ResponseMapper.getMailboxIdByTenantOrFirst(any(), any())).thenReturn(MAILBOX_ID); + when(mailboxDirectoryApi.lookupMailboxIds(any())).thenReturn(response); + when(apiConfiguration.getTenant()).thenReturn(TENANT_SH); + } + + @DisplayName("should call lookupMailboxIds") + @Test + void shouldCallLookupMailboxIds() { + lookupMailboxId(); + + verify(mailboxDirectoryApi).lookupMailboxIds(List.of(UUID.fromString(NAME_IDENTIFIER))); + } + + @DisplayName("should call getMailBoxIdByTenantOrFirst") + @Test + void shouldCallGetMailBoxIdByTenantOrFirst() { + lookupMailboxId(); + + verify(osi2ResponseMapper).getMailboxIdByTenantOrFirst(response, TENANT_SH); + } + + @DisplayName("should return") + @Test + void shouldReturn() { + var result = lookupMailboxId(); + + assertThat(result).isEqualTo(MAILBOX_ID); + } + + private String lookupMailboxId() { + return service.lookupMailboxId(NAME_IDENTIFIER); + } + } + } \ No newline at end of file