diff --git a/pom.xml b/pom.xml index 81d76b6a..40e2e727 100644 --- a/pom.xml +++ b/pom.xml @@ -273,6 +273,19 @@ 2.16.0 test + + + com.squareup.okhttp3 + okhttp + 4.0.1 + test + + + com.squareup.okhttp3 + mockwebserver + 4.0.1 + test + diff --git a/src/main/java/ch/admin/bag/covidcertificate/api/Constants.java b/src/main/java/ch/admin/bag/covidcertificate/api/Constants.java index 4f4c3dcf..3708cbee 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/api/Constants.java +++ b/src/main/java/ch/admin/bag/covidcertificate/api/Constants.java @@ -13,13 +13,13 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class Constants { - public static final String VERSION = "1.2.1"; + public static final String VERSION = "1.3.0"; public static final String DEFAULT_DISEASE_OR_AGENT_TARGETED = "840539006"; public static final String DEFAULT_DISEASE_OR_AGENT_SYSTEM = "2.16.840.1.113883.6.96"; public static final String ISSUER = "Bundesamt für Gesundheit (BAG)"; public static final String PCR_TYPE_CODE = "LP6464-4"; public static final String NONE_PCR_TYPE_CODE = "LP217198-3"; - public static final int MAX_STRING_LENGTH = 50; + public static final int MAX_STRING_LENGTH = 80; public static final int DAYS_UNTIL_RECOVERY_VALID = 10; public static final int RECOVERY_CERTIFICATE_VALIDITY_IN_DAYS = 179; @@ -27,6 +27,7 @@ public class Constants { public static final String KPI_TYPE_VACCINATION = "v"; public static final String KPI_TYPE_TEST = "t"; public static final String KPI_TYPE_RECOVERY = "r"; + public static final String KPI_TYPE_INAPP_DELIVERY = "ad"; public static final String USER_EXT_ID_CLAIM_KEY = "userExtId"; public static final String KPI_UUID_KEY = "uuid"; public static final String KPI_TIMESTAMP_KEY = "ts"; @@ -49,8 +50,8 @@ public class Constants { public static final CreateCertificateError INVALID_DOSES = new CreateCertificateError(455, "Invalid number of doses", HttpStatus.BAD_REQUEST); public static final CreateCertificateError INVALID_VACCINATION_DATE = new CreateCertificateError(456, "Invalid vaccination date! Date cannot be in the future", HttpStatus.BAD_REQUEST); public static final CreateCertificateError INVALID_COUNTRY_OF_VACCINATION = new CreateCertificateError(457, "Invalid country of vaccination", HttpStatus.BAD_REQUEST); - public static final CreateCertificateError INVALID_GIVEN_NAME = new CreateCertificateError(458, "Invalid given name! Must not exceed 50 chars", HttpStatus.BAD_REQUEST); - public static final CreateCertificateError INVALID_FAMILY_NAME = new CreateCertificateError(459, "Invalid family name! Must not exceed 50 chars", HttpStatus.BAD_REQUEST); + public static final CreateCertificateError INVALID_GIVEN_NAME = new CreateCertificateError(458, "Invalid given name! Must not exceed 80 chars", HttpStatus.BAD_REQUEST); + public static final CreateCertificateError INVALID_FAMILY_NAME = new CreateCertificateError(459, "Invalid family name! Must not exceed 80 chars", HttpStatus.BAD_REQUEST); public static final CreateCertificateError NO_TEST_DATA = new CreateCertificateError(460, "No test data was specified", HttpStatus.BAD_REQUEST); public static final CreateCertificateError INVALID_MEMBER_STATE_OF_TEST = new CreateCertificateError(461, "Invalid member state of test", HttpStatus.BAD_REQUEST); public static final CreateCertificateError INVALID_TYP_OF_TEST = new CreateCertificateError(462, "Invalid type of test and manufacturer code combination! Must either be a PCR Test type and no manufacturer code or give a manufacturer code and the antigen test type code.", HttpStatus.BAD_REQUEST); @@ -62,6 +63,10 @@ public class Constants { public static final CreateCertificateError INVALID_LANGUAGE = new CreateCertificateError(469, "The given language does not match any of the supported languages: de, it, fr, rm!", HttpStatus.BAD_REQUEST); public static final RevocationError INVALID_UVCI = new RevocationError(470, "Invalid UVCI format.", HttpStatus.BAD_REQUEST); public static final CreateCertificateError INVALID_ADDRESS = new CreateCertificateError(474, "Paper-based delivery requires a valid address.", HttpStatus.BAD_REQUEST); + public static final CreateCertificateError DUPLICATE_DELIVERY_METHOD = new CreateCertificateError(475, "Delivery method can either be InApp or print, but not both.", HttpStatus.BAD_REQUEST); + public static final CreateCertificateError INVALID_IN_APP_CODE = new CreateCertificateError(476, "InAppDelivery-Code is invalid.", HttpStatus.NOT_FOUND); + public static final CreateCertificateError INVALID_STANDARDISED_GIVEN_NAME = new CreateCertificateError(477, "Invalid given name! The standardised given name exceeds 80 chars", HttpStatus.BAD_REQUEST); + public static final CreateCertificateError INVALID_STANDARDISED_FAMILY_NAME = new CreateCertificateError(478, "Invalid family name! The standardised family name exceeds 80 chars", HttpStatus.BAD_REQUEST); public static final RevocationError DUPLICATE_UVCI = new RevocationError(480, "Duplicate UVCI.", HttpStatus.CONFLICT); @@ -79,4 +84,5 @@ public class Constants { public static final CreateCertificateError CREATE_BARCODE_FAILED = new CreateCertificateError(555, "Creating barcode failed.", HttpStatus.INTERNAL_SERVER_ERROR); public static final CreateCertificateError PRINTING_FAILED = new CreateCertificateError(556, "Printing failed.", HttpStatus.INTERNAL_SERVER_ERROR); public static final CreateCertificateError WRITING_RETURN_CSV_FAILED = new CreateCertificateError(557, "Write CSV failed", HttpStatus.INTERNAL_SERVER_ERROR); + public static final CreateCertificateError INAPP_DELIVERY_FAILED = new CreateCertificateError(558, "InApp delivery failed.", HttpStatus.INTERNAL_SERVER_ERROR); } diff --git a/src/main/java/ch/admin/bag/covidcertificate/api/mapper/CovidCertificatePersonMapper.java b/src/main/java/ch/admin/bag/covidcertificate/api/mapper/CovidCertificatePersonMapper.java index 4bbb16b4..30242fb3 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/api/mapper/CovidCertificatePersonMapper.java +++ b/src/main/java/ch/admin/bag/covidcertificate/api/mapper/CovidCertificatePersonMapper.java @@ -1,5 +1,6 @@ package ch.admin.bag.covidcertificate.api.mapper; +import ch.admin.bag.covidcertificate.api.exception.CreateCertificateException; import ch.admin.bag.covidcertificate.api.request.CovidCertificatePersonDto; import ch.admin.bag.covidcertificate.api.request.CovidCertificatePersonNameDto; import ch.admin.bag.covidcertificate.service.domain.CovidCertificatePerson; @@ -8,6 +9,8 @@ import lombok.NoArgsConstructor; import se.digg.dgc.transliteration.MrzEncoder; +import static ch.admin.bag.covidcertificate.api.Constants.*; + @NoArgsConstructor(access = AccessLevel.PRIVATE) public class CovidCertificatePersonMapper { @@ -19,7 +22,15 @@ public static CovidCertificatePerson toCovidCertificatePerson(CovidCertificatePe } private static CovidCertificatePersonName toCovidCertificatePersonName(CovidCertificatePersonNameDto name) { - return new CovidCertificatePersonName(name.getFamilyName(), standardiseName(name.getFamilyName()), name.getGivenName(), standardiseName(name.getGivenName())); + String standardisedFamilyName = standardiseName(name.getFamilyName()); + if (standardisedFamilyName.length() > MAX_STRING_LENGTH) { + throw new CreateCertificateException(INVALID_STANDARDISED_FAMILY_NAME); + } + String standardisedGivenName = standardiseName(name.getGivenName()); + if (standardisedGivenName.length() > MAX_STRING_LENGTH) { + throw new CreateCertificateException(INVALID_STANDARDISED_GIVEN_NAME); + } + return new CovidCertificatePersonName(name.getFamilyName(), standardisedFamilyName, name.getGivenName(), standardisedGivenName); } private static String standardiseName(String name) { diff --git a/src/main/java/ch/admin/bag/covidcertificate/api/mapper/TestCertificateQrCodeMapper.java b/src/main/java/ch/admin/bag/covidcertificate/api/mapper/TestCertificateQrCodeMapper.java index 65c4a0cd..04576653 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/api/mapper/TestCertificateQrCodeMapper.java +++ b/src/main/java/ch/admin/bag/covidcertificate/api/mapper/TestCertificateQrCodeMapper.java @@ -15,8 +15,7 @@ import java.util.List; import java.util.stream.Collectors; -import static ch.admin.bag.covidcertificate.api.Constants.ISSUER; -import static ch.admin.bag.covidcertificate.api.Constants.VERSION; +import static ch.admin.bag.covidcertificate.api.Constants.*; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class TestCertificateQrCodeMapper { @@ -50,7 +49,7 @@ private static TestCertificateData toTestCertificateData( return new TestCertificateData( CovidCertificateDiseaseOrAgentTargeted.getStandardInstance().getCode(), testValueSet.getTypeCode(), - testValueSet.getName(), + getTrimmedTestName(testValueSet.getName()), testValueSet.getManufacturerCodeEu(), testCertificateDataDto.getSampleDateTime().truncatedTo(ChronoUnit.SECONDS), NegativeTestResult.CODE, @@ -60,4 +59,8 @@ private static TestCertificateData toTestCertificateData( UVCI.generateUVCI(testCertificateDataDto.toString()) ); } + + private static String getTrimmedTestName(String testName) { + return testName.substring(0, Math.min(testName.length(), MAX_STRING_LENGTH)); + } } diff --git a/src/main/java/ch/admin/bag/covidcertificate/api/request/CertificateCreateDto.java b/src/main/java/ch/admin/bag/covidcertificate/api/request/CertificateCreateDto.java index 8ad6c816..3d74a31d 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/api/request/CertificateCreateDto.java +++ b/src/main/java/ch/admin/bag/covidcertificate/api/request/CertificateCreateDto.java @@ -4,19 +4,34 @@ import ch.admin.bag.covidcertificate.api.valueset.AcceptedLanguages; import com.fasterxml.jackson.annotation.JsonUnwrapped; import lombok.*; +import org.springframework.util.StringUtils; -import static ch.admin.bag.covidcertificate.api.Constants.INVALID_LANGUAGE; -import static ch.admin.bag.covidcertificate.api.Constants.NO_PERSON_DATA; +import static ch.admin.bag.covidcertificate.api.Constants.*; @Getter @ToString @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor public abstract class CertificateCreateDto { @JsonUnwrapped private CovidCertificatePersonDto personData; private String language; private CovidCertificateAddressDto address; + private String appCode; + + public CertificateCreateDto(CovidCertificatePersonDto personData, String language, CovidCertificateAddressDto address, String appCode) { + this.personData = personData; + this.language = language; + this.address = address; + this.appCode = appCode != null ? appCode.toUpperCase() : null; + } + + public boolean sendToPrint() { + return this.address != null; + } + + public boolean sendToApp() { + return this.appCode != null; + } public void validate() { if (personData == null) { @@ -27,8 +42,23 @@ public void validate() { if (!AcceptedLanguages.isAcceptedLanguage(language)) { throw new CreateCertificateException(INVALID_LANGUAGE); } - if (address != null) { - address.validate(); + this.validateDeliveryMethod(); + } + + private void validateDeliveryMethod() { + if (this.address != null && StringUtils.hasText(this.appCode)) { + throw new CreateCertificateException(DUPLICATE_DELIVERY_METHOD); + } else { + if (this.address != null) { + this.address.validate(); + } + if (StringUtils.hasText(this.appCode)) { + var isAlphaNumeric = org.apache.commons.lang3.StringUtils.isAlphanumeric(this.appCode); + var isNineCharsLong = this.appCode.length() == 9; + if (!isAlphaNumeric || !isNineCharsLong) { + throw new CreateCertificateException(INVALID_IN_APP_CODE); + } + } } } } diff --git a/src/main/java/ch/admin/bag/covidcertificate/api/request/CertificateCsvBean.java b/src/main/java/ch/admin/bag/covidcertificate/api/request/CertificateCsvBean.java index ed0e1fb2..18fde728 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/api/request/CertificateCsvBean.java +++ b/src/main/java/ch/admin/bag/covidcertificate/api/request/CertificateCsvBean.java @@ -38,6 +38,8 @@ public abstract class CertificateCsvBean { private String cantonCodeSender; @CsvBindByName(column = "error") private String error; + @CsvBindByName(column = "inAppDeliveryCode") + private String inAppDeliveryCode; public abstract CertificateCreateDto mapToCreateDto(); @@ -50,7 +52,8 @@ protected VaccinationCertificateCreateDto mapToCreateDto(VaccinationCertificateD mapToPersonDto(), List.of(dataDto), getLanguage(), - mapToAddressDto() + mapToAddressDto(), + getInAppDeliveryCode() ); } @@ -59,7 +62,8 @@ protected TestCertificateCreateDto mapToCreateDto(TestCertificateDataDto dataDto mapToPersonDto(), List.of(dataDto), getLanguage(), - mapToAddressDto() + mapToAddressDto(), + getInAppDeliveryCode() ); } @@ -68,7 +72,8 @@ protected RecoveryCertificateCreateDto mapToCreateDto(RecoveryCertificateDataDto mapToPersonDto(), List.of(dataDto), getLanguage(), - mapToAddressDto() + mapToAddressDto(), + getInAppDeliveryCode() ); } diff --git a/src/main/java/ch/admin/bag/covidcertificate/api/request/RecoveryCertificateCreateDto.java b/src/main/java/ch/admin/bag/covidcertificate/api/request/RecoveryCertificateCreateDto.java index 983152ce..8836b376 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/api/request/RecoveryCertificateCreateDto.java +++ b/src/main/java/ch/admin/bag/covidcertificate/api/request/RecoveryCertificateCreateDto.java @@ -21,9 +21,10 @@ public RecoveryCertificateCreateDto( CovidCertificatePersonDto personData, List recoveryInfo, String language, - CovidCertificateAddressDto address + CovidCertificateAddressDto address, + String inAppDeliveryCode ) { - super(personData, language, address); + super(personData, language, address, inAppDeliveryCode); this.recoveryInfo = recoveryInfo; } diff --git a/src/main/java/ch/admin/bag/covidcertificate/api/request/TestCertificateCreateDto.java b/src/main/java/ch/admin/bag/covidcertificate/api/request/TestCertificateCreateDto.java index f3d1cc18..25303943 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/api/request/TestCertificateCreateDto.java +++ b/src/main/java/ch/admin/bag/covidcertificate/api/request/TestCertificateCreateDto.java @@ -21,9 +21,10 @@ public TestCertificateCreateDto( CovidCertificatePersonDto personData, List testInfo, String language, - CovidCertificateAddressDto address + CovidCertificateAddressDto address, + String inAppDeliveryCode ) { - super(personData, language, address); + super(personData, language, address, inAppDeliveryCode); this.testInfo = testInfo; } diff --git a/src/main/java/ch/admin/bag/covidcertificate/api/request/VaccinationCertificateCreateDto.java b/src/main/java/ch/admin/bag/covidcertificate/api/request/VaccinationCertificateCreateDto.java index 96c73052..92a389b9 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/api/request/VaccinationCertificateCreateDto.java +++ b/src/main/java/ch/admin/bag/covidcertificate/api/request/VaccinationCertificateCreateDto.java @@ -21,9 +21,10 @@ public VaccinationCertificateCreateDto( CovidCertificatePersonDto personData, List vaccinationInfo, String language, - CovidCertificateAddressDto address + CovidCertificateAddressDto address, + String inAppDeliveryCode ) { - super(personData, language, address); + super(personData, language, address, inAppDeliveryCode); this.vaccinationInfo = vaccinationInfo; } diff --git a/src/main/java/ch/admin/bag/covidcertificate/api/request/VaccinationCertificateDataDto.java b/src/main/java/ch/admin/bag/covidcertificate/api/request/VaccinationCertificateDataDto.java index 98326d7c..a0b7b9db 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/api/request/VaccinationCertificateDataDto.java +++ b/src/main/java/ch/admin/bag/covidcertificate/api/request/VaccinationCertificateDataDto.java @@ -26,9 +26,7 @@ public class VaccinationCertificateDataDto { public void validate() { if (numberOfDoses == null || numberOfDoses < 1 || - numberOfDoses > 9 || totalNumberOfDoses == null || - totalNumberOfDoses > 9 || numberOfDoses > totalNumberOfDoses) { throw new CreateCertificateException(INVALID_DOSES); } diff --git a/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/InAppDeliveryClient.java b/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/InAppDeliveryClient.java new file mode 100644 index 00000000..483e2d85 --- /dev/null +++ b/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/InAppDeliveryClient.java @@ -0,0 +1,13 @@ +package ch.admin.bag.covidcertificate.client.inapp_delivery; + +import ch.admin.bag.covidcertificate.api.exception.CreateCertificateException; +import ch.admin.bag.covidcertificate.client.inapp_delivery.domain.InAppDeliveryRequestDto; + +public interface InAppDeliveryClient { + /** + * Sends an InApp delivery request to the app backend. If the request fails a CreateCertificateException is thrown. + * + * @param requestDto - data to be sent to the app. + */ + void deliverToApp(InAppDeliveryRequestDto requestDto) throws CreateCertificateException; +} diff --git a/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/domain/InAppDeliveryRequestDto.java b/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/domain/InAppDeliveryRequestDto.java new file mode 100644 index 00000000..2a50c8dc --- /dev/null +++ b/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/domain/InAppDeliveryRequestDto.java @@ -0,0 +1,23 @@ +package ch.admin.bag.covidcertificate.client.inapp_delivery.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@AllArgsConstructor +public class InAppDeliveryRequestDto { + /** + * Code of the App the certificate should be sent to. Must be 9 characters, alphanumeric and upper case. + */ + String code; + /** + * Payload of the QRCode. Starts with 'HC1:' + */ + String hcert; + /** + * Base64 encoded String of the PDF. + */ + String pdf; +} diff --git a/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/DefaultInAppDeliveryClient.java b/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/DefaultInAppDeliveryClient.java new file mode 100644 index 00000000..43d32b05 --- /dev/null +++ b/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/DefaultInAppDeliveryClient.java @@ -0,0 +1,89 @@ +package ch.admin.bag.covidcertificate.client.inapp_delivery.internal; + +import ch.admin.bag.covidcertificate.api.exception.CreateCertificateException; +import ch.admin.bag.covidcertificate.client.inapp_delivery.InAppDeliveryClient; +import ch.admin.bag.covidcertificate.client.inapp_delivery.domain.InAppDeliveryRequestDto; +import ch.admin.bag.covidcertificate.config.ProfileRegistry; +import ch.admin.bag.covidcertificate.config.security.authentication.ServletJeapAuthorization; +import ch.admin.bag.covidcertificate.domain.KpiData; +import ch.admin.bag.covidcertificate.service.KpiDataService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import org.springframework.web.util.UriComponentsBuilder; +import reactor.core.publisher.Mono; + +import java.time.LocalDateTime; + +import static ch.admin.bag.covidcertificate.api.Constants.*; +import static net.logstash.logback.argument.StructuredArguments.kv; + +@Service +@RequiredArgsConstructor +@Slf4j +@Profile("!" + ProfileRegistry.INAPP_DELIVERY_SERVICE_MOCK) +public class DefaultInAppDeliveryClient implements InAppDeliveryClient { + + @Value("${cc-inapp-delivery-service.url}") + private String serviceUri; + + private final WebClient defaultWebClient; + private final ServletJeapAuthorization jeapAuthorization; + private final KpiDataService kpiLogService; + + @Override + public void deliverToApp(InAppDeliveryRequestDto requestDto) { + var builder = UriComponentsBuilder.fromHttpUrl(serviceUri); + + var uri = builder.toUriString(); + log.debug("Call the InApp Delivery Backend with url {}", serviceUri); + try { + var response = defaultWebClient.post() + .uri(uri) + .body(Mono.just(requestDto), requestDto.getClass()) + .retrieve() + .toBodilessEntity() + .block(); + log.trace("InApp Delivery Backend Response: {}", response); + if (response != null && response.getStatusCode().is2xxSuccessful()) { + logKpi(); + } else { + throw new CreateCertificateException(INAPP_DELIVERY_FAILED); + } + } catch (WebClientResponseException e) { + log.error("Received error message", e); + this.handleErrorResponse(e); + } catch (WebClientRequestException e) { + log.error("Request to {} failed", serviceUri, e); + throw new CreateCertificateException(INAPP_DELIVERY_FAILED); + } + } + + private void handleErrorResponse(WebClientResponseException exception) { + if (exception != null) { + var statusCode = exception.getStatusCode(); + if (statusCode == HttpStatus.BAD_REQUEST || statusCode == HttpStatus.NOT_FOUND) { + throw new CreateCertificateException(INVALID_IN_APP_CODE); + } else { + throw new CreateCertificateException(INAPP_DELIVERY_FAILED); + } + } else { + throw new CreateCertificateException(INAPP_DELIVERY_FAILED); + } + } + + private void logKpi() { + String extId = jeapAuthorization.getExtIdInAuthentication(); + if (extId != null) { + LocalDateTime kpiTimestamp = LocalDateTime.now(); + log.info("kpi: {} {} {}", kv(KPI_TIMESTAMP_KEY, kpiTimestamp.format(LOG_FORMAT)), kv(KPI_TYPE_KEY, KPI_TYPE_INAPP_DELIVERY), kv(KPI_UUID_KEY, extId)); + kpiLogService.log(new KpiData(kpiTimestamp, KPI_TYPE_INAPP_DELIVERY, extId)); + } + } +} diff --git a/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/MockInAppDeliveryClient.java b/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/MockInAppDeliveryClient.java new file mode 100644 index 00000000..cc2cf1bd --- /dev/null +++ b/src/main/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/MockInAppDeliveryClient.java @@ -0,0 +1,19 @@ +package ch.admin.bag.covidcertificate.client.inapp_delivery.internal; + +import ch.admin.bag.covidcertificate.api.exception.CreateCertificateException; +import ch.admin.bag.covidcertificate.client.inapp_delivery.InAppDeliveryClient; +import ch.admin.bag.covidcertificate.client.inapp_delivery.domain.InAppDeliveryRequestDto; +import ch.admin.bag.covidcertificate.config.ProfileRegistry; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@Profile(ProfileRegistry.INAPP_DELIVERY_SERVICE_MOCK) +public class MockInAppDeliveryClient implements InAppDeliveryClient { + @Override + public void deliverToApp(InAppDeliveryRequestDto requestDto) throws CreateCertificateException { + log.info("Call the mock InApp delivery service"); + } +} diff --git a/src/main/java/ch/admin/bag/covidcertificate/config/ProfileRegistry.java b/src/main/java/ch/admin/bag/covidcertificate/config/ProfileRegistry.java index 40530d5c..34f2a808 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/config/ProfileRegistry.java +++ b/src/main/java/ch/admin/bag/covidcertificate/config/ProfileRegistry.java @@ -7,4 +7,5 @@ public class ProfileRegistry { public static final String SIGNING_SERVICE_MOCK = "mock-signing-service"; public static final String PRINTING_SERVICE_MOCK = "mock-printing-service"; + public static final String INAPP_DELIVERY_SERVICE_MOCK = "mock-inapp-delivery-service"; } diff --git a/src/main/java/ch/admin/bag/covidcertificate/config/security/authentication/ServletJeapAuthorization.java b/src/main/java/ch/admin/bag/covidcertificate/config/security/authentication/ServletJeapAuthorization.java index 762dc0a8..38de0778 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/config/security/authentication/ServletJeapAuthorization.java +++ b/src/main/java/ch/admin/bag/covidcertificate/config/security/authentication/ServletJeapAuthorization.java @@ -2,6 +2,8 @@ import org.springframework.security.core.context.SecurityContextHolder; +import static ch.admin.bag.covidcertificate.api.Constants.USER_EXT_ID_CLAIM_KEY; + /** * This class provides methods to support authorization needs based on the current security context for Spring WebMvc applications. */ @@ -16,4 +18,18 @@ public JeapAuthenticationToken getJeapAuthenticationToken() { return (JeapAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); } + /** + * Fetch the JeapAuthenticationToken from the current security context. + * + * @return The JeapAuthenticationToken extracted from the current security context. + */ + public String getExtIdInAuthentication() { + var authentication = (JeapAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + var jwt = authentication.getToken(); + if (jwt != null) { + return jwt.getClaimAsString(USER_EXT_ID_CLAIM_KEY); + } + return null; + } + } diff --git a/src/main/java/ch/admin/bag/covidcertificate/service/COSETime.java b/src/main/java/ch/admin/bag/covidcertificate/service/COSETime.java index df4ab66f..f8a9656b 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/service/COSETime.java +++ b/src/main/java/ch/admin/bag/covidcertificate/service/COSETime.java @@ -10,7 +10,7 @@ @Component @RequiredArgsConstructor public class COSETime { - private static final Integer EXPIRATION_PERIOD = 12; + private static final Integer EXPIRATION_PERIOD = 24; private final Clock clock; diff --git a/src/main/java/ch/admin/bag/covidcertificate/service/CovidCertificateGenerationService.java b/src/main/java/ch/admin/bag/covidcertificate/service/CovidCertificateGenerationService.java index 0951ef46..93fce408 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/service/CovidCertificateGenerationService.java +++ b/src/main/java/ch/admin/bag/covidcertificate/service/CovidCertificateGenerationService.java @@ -6,6 +6,8 @@ import ch.admin.bag.covidcertificate.api.request.TestCertificateCreateDto; import ch.admin.bag.covidcertificate.api.request.VaccinationCertificateCreateDto; import ch.admin.bag.covidcertificate.api.response.CovidCertificateCreateResponseDto; +import ch.admin.bag.covidcertificate.client.inapp_delivery.InAppDeliveryClient; +import ch.admin.bag.covidcertificate.client.inapp_delivery.domain.InAppDeliveryRequestDto; import ch.admin.bag.covidcertificate.client.printing.PrintQueueClient; import ch.admin.bag.covidcertificate.service.document.CovidPdfCertificateGenerationService; import ch.admin.bag.covidcertificate.service.domain.*; @@ -16,12 +18,15 @@ import org.springframework.stereotype.Service; import se.digg.dgc.encoding.Barcode; +import java.util.Base64; + @Service @Slf4j @RequiredArgsConstructor public class CovidCertificateGenerationService { private final BarcodeService barcodeService; private final PrintQueueClient printQueueClient; + private final InAppDeliveryClient inAppDeliveryClient; private final ObjectMapper objectMapper; private final CovidPdfCertificateGenerationService covidPdfCertificateGenerationService; private final CovidCertificateDtoMapperService covidCertificateDtoMapperService; @@ -52,8 +57,11 @@ private CovidCertificateCreateResponseDto generateCovidCertificate(AbstractCerti byte[] pdf = covidPdfCertificateGenerationService.generateCovidCertificate(pdfData, code); CovidCertificateCreateResponseDto responseDto = new CovidCertificateCreateResponseDto(pdf, code.getImage(), uvci); - if (createDto.getAddress() != null) { + if (createDto.sendToPrint()) { printQueueClient.sendPrintJob(CertificatePrintRequestDtoMapper.toCertificatePrintRequestDto(pdf, uvci, createDto)); + } else if (createDto.sendToApp()) { + var inAppDeliveryDto = new InAppDeliveryRequestDto(createDto.getAppCode(), code.getPayload(), Base64.getEncoder().encodeToString(pdf)); + this.inAppDeliveryClient.deliverToApp(inAppDeliveryDto); } return responseDto; } diff --git a/src/main/java/ch/admin/bag/covidcertificate/service/document/CovidPdfCertificateGenerationService.java b/src/main/java/ch/admin/bag/covidcertificate/service/document/CovidPdfCertificateGenerationService.java index 46a4465f..03abe5b3 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/service/document/CovidPdfCertificateGenerationService.java +++ b/src/main/java/ch/admin/bag/covidcertificate/service/document/CovidPdfCertificateGenerationService.java @@ -77,16 +77,16 @@ public class CovidPdfCertificateGenerationService { public CovidPdfCertificateGenerationService(ConfigurableEnvironment env) throws URISyntaxException, IOException, DocumentException { final BaseFont baseFont = BaseFont.createFont("arial.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, - Files.readAllBytes(Path.of(Objects.requireNonNull(this.getClass().getClassLoader().getResource("templates/fonts/arial.ttf")).toURI())), - null); + Files.readAllBytes(Path.of(Objects.requireNonNull(this.getClass().getClassLoader().getResource("templates/fonts/arial.ttf")).toURI())), + null); final BaseFont baseFontBold = BaseFont.createFont("arialbd.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, - Files.readAllBytes(Path.of(Objects.requireNonNull(this.getClass().getClassLoader().getResource("templates/fonts/arialbd.ttf")).toURI())), - null); + Files.readAllBytes(Path.of(Objects.requireNonNull(this.getClass().getClassLoader().getResource("templates/fonts/arialbd.ttf")).toURI())), + null); final BaseFont baseFontItalic = BaseFont.createFont("ariali.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, - Files.readAllBytes(Path.of(Objects.requireNonNull(this.getClass().getClassLoader().getResource("templates/fonts/ariali.ttf")).toURI())), - null); + Files.readAllBytes(Path.of(Objects.requireNonNull(this.getClass().getClassLoader().getResource("templates/fonts/ariali.ttf")).toURI())), + null); fontRow = new Font(baseFont, 10, Font.NORMAL, BaseColor.BLACK); @@ -109,7 +109,6 @@ public CovidPdfCertificateGenerationService(ConfigurableEnvironment env) throws logoApp = getLogo("appicon.png", 100); addDraftWatermark = Arrays.stream(env.getActiveProfiles()).noneMatch("prod"::equals); - } private ResourceBundleMessageSource messageSource() { @@ -129,6 +128,8 @@ public byte[] generateCovidCertificate(AbstractCertificatePdf data, Barcode barc final Locale locale = getLocale(data.getLanguage()); + boolean isPartialVaccination = data instanceof VaccinationCertificatePdf && ((VaccinationCertificatePdf) data).isPartialVaccination(); + PdfWriter writer = PdfWriter.getInstance(document, stream); document.open(); @@ -137,13 +138,13 @@ public byte[] generateCovidCertificate(AbstractCertificatePdf data, Barcode barc document.setMargins(MARGIN_LEFT, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOTTOM); - document.add(headerTable(locale)); + document.add(headerTable(locale, isPartialVaccination)); Image qrCode = renderQRCode(writer, barcode.getPayload()); - document.add(mainTable(locale, data, qrCode)); + document.add(mainTable(locale, data, qrCode, isPartialVaccination)); - document.add(issuerTable(locale)); + document.add(issuerTable(locale, isPartialVaccination)); document.add(infoTable(locale)); @@ -182,7 +183,7 @@ protected void addMetadata(Document document) { document.addCreator(METADATA); } - private PdfPTable headerTable(Locale locale) { + private PdfPTable headerTable(Locale locale, boolean isPartialVaccination) { PdfPTable table = new PdfPTable(2); table.setWidthPercentage(100); table.setPaddingTop(0); @@ -197,27 +198,31 @@ private PdfPTable headerTable(Locale locale) { cell.setVerticalAlignment(Rectangle.TOP); table.addCell(cell); - PdfPCell valueCell = new PdfPCell(new Phrase(messageSource.getMessage("document.title", null, locale), fontHeaderRed)); + String headerKey = isPartialVaccination ? "evidence.document.title" : "document.title"; + + PdfPCell valueCell = new PdfPCell(new Phrase(messageSource.getMessage(headerKey, null, locale), fontHeaderRed)); valueCell.setBorder(Rectangle.NO_BORDER); valueCell.setVerticalAlignment(Rectangle.TOP); valueCell.setPaddingTop(0); + valueCell.setPaddingLeft(0); table.addCell(valueCell); - PdfPCell valueCell2 = new PdfPCell(new Phrase(messageSource.getMessage("document.title", null, Locale.ENGLISH), fontHeaderBlack)); + PdfPCell valueCell2 = new PdfPCell(new Phrase(messageSource.getMessage(headerKey, null, Locale.ENGLISH), fontHeaderBlack)); valueCell2.setBorder(Rectangle.NO_BORDER); valueCell2.setVerticalAlignment(Rectangle.TOP); valueCell2.setPaddingTop(0); + valueCell2.setPaddingLeft(0); table.addCell(valueCell2); return table; } - private PdfPTable mainTable(Locale locale, AbstractCertificatePdf data, Image qrCode) { + private PdfPTable mainTable(Locale locale, AbstractCertificatePdf data, Image qrCode, boolean isPartialVaccination) { float[] pointColumnWidths = {50F, 20F, 30F}; PdfPTable table = new PdfPTable(pointColumnWidths); table.setWidthPercentage(100); - PdfPCell cell = new PdfPCell(addLeftColumn(locale, qrCode, data)); + PdfPCell cell = new PdfPCell(addLeftColumn(locale, qrCode, data, isPartialVaccination)); cell.setVerticalAlignment(Element.ALIGN_TOP); cell.setBorder(Rectangle.NO_BORDER); cell.setRowspan(30); @@ -225,7 +230,7 @@ private PdfPTable mainTable(Locale locale, AbstractCertificatePdf data, Image qr table.addCell(cell); if (data instanceof VaccinationCertificatePdf) { - addVaccineData(locale, (VaccinationCertificatePdf) data, table); + addVaccineData(locale, (VaccinationCertificatePdf) data, table, isPartialVaccination); } else if (data instanceof RecoveryCertificatePdf) { addRecoveryData(locale, (RecoveryCertificatePdf) data, table); } else { @@ -234,8 +239,10 @@ private PdfPTable mainTable(Locale locale, AbstractCertificatePdf data, Image qr return table; } - private void addVaccineData(Locale locale, VaccinationCertificatePdf data, PdfPTable table) { - addIssuerRow(table, locale, "vaccination.title", 0, true, 0); + private void addVaccineData(Locale locale, VaccinationCertificatePdf data, PdfPTable table, boolean isPartialVaccination) { + String titleKey = isPartialVaccination ? "evidence.title" : "vaccination.title"; + + addIssuerRow(table, locale, titleKey, 0, true, 0); addRow(table, locale, VACCINATION_DISEASE_LABEL_KEY, messageSource.getMessage(VACCINATION_DISEASE_MESSAGE_CODE, null, locale)); addRow(table, locale, "vaccination.dosis.label", data.getNumberOfDoses() + "/" + data.getTotalNumberOfDoses()); addRow(table, locale, "vaccination.type.label", data.getVaccineProphylaxis()); @@ -268,7 +275,7 @@ private void addTestData(Locale locale, TestCertificatePdf data, PdfPTable table addLocaleAndEnglishRow(table, locale, "test.country.label", data.getMemberStateOfTest(), data.getMemberStateOfTestEn()); } - private PdfPTable addLeftColumn(Locale locale, Image qrCode, AbstractCertificatePdf data) { + private PdfPTable addLeftColumn(Locale locale, Image qrCode, AbstractCertificatePdf data, boolean isPartialVaccination) { float[] pointColumnWidths = {50F, 50F}; PdfPTable table = new PdfPTable(pointColumnWidths); @@ -289,7 +296,7 @@ private PdfPTable addLeftColumn(Locale locale, Image qrCode, AbstractCertificate cell2.setPaddingTop(5); table.addCell(cell2); - addQrLabelCell(table, locale, LocalDateTime.now()); + addQrLabelCell(table, locale, LocalDateTime.now(), isPartialVaccination); addIssuerRow(table, locale, "personalData.title", 20, true, PADDING_LEFT); addNameRow(table, locale, "personalData.name.label", data.getFamilyName() + " " + data.getGivenName(), PADDING_LEFT); @@ -377,8 +384,9 @@ private void addLocaleAndEnglishRow(PdfPTable table, Locale locale, String key, table.addCell(valueEnglishCell); } - private void addIssuerRow(PdfPTable table, Locale locale) { - addIssuerRow(table, locale, "issuer.title", 15, false, (float) CovidPdfCertificateGenerationService.PADDING_LEFT); + private void addIssuerRow(PdfPTable table, Locale locale, boolean isPartialVaccination) { + String issuerLabel = isPartialVaccination ? "evidence.issuer" : "issuer.title"; + addIssuerRow(table, locale, issuerLabel, 15, false, (float) CovidPdfCertificateGenerationService.PADDING_LEFT); } private void addIssuerRow(PdfPTable table, Locale locale, String key, int padding, boolean title, float paddingLeft) { @@ -398,16 +406,17 @@ private void addIssuerRow(PdfPTable table, Locale locale, String key, int paddin table.addCell(issuerCell); } - private void addQrLabelCell(PdfPTable table, Locale locale, LocalDateTime dateTime) { + private void addQrLabelCell(PdfPTable table, Locale locale, LocalDateTime dateTime, boolean isPartialVaccination) { + String labelKey = isPartialVaccination ? "evidence.qrCode.label" : "qrCode.label"; String date = dateTime.format(LOCAL_DATE_FORMAT); String time = dateTime.format(DateTimeFormatter.ofPattern("HH:mm")); - PdfPCell first = new PdfPCell(new Phrase(messageSource.getMessage("qrCode.label", new String[]{date, time}, locale), font8Row)); + PdfPCell first = new PdfPCell(new Phrase(messageSource.getMessage(labelKey, new String[]{date, time}, locale), font8Row)); first.setBorder(Rectangle.NO_BORDER); first.setColspan(2); first.setPaddingLeft(PADDING_LEFT); table.addCell(first); - PdfPCell second = new PdfPCell(new Phrase(messageSource.getMessage("qrCode.label", new String[]{date, time}, Locale.ENGLISH), font8English)); + PdfPCell second = new PdfPCell(new Phrase(messageSource.getMessage(labelKey, new String[]{date, time}, Locale.ENGLISH), font8English)); second.setBorder(Rectangle.NO_BORDER); second.setPaddingTop(0); second.setColspan(2); @@ -437,11 +446,11 @@ private Chunk getLogo(String name, int scale) { return new Chunk(logo, 0, 0); } - private PdfPTable issuerTable(Locale locale) { + private PdfPTable issuerTable(Locale locale, boolean isPartialVaccination) { PdfPTable table = new PdfPTable(1); table.setWidthPercentage(100); - addIssuerRow(table, locale); + addIssuerRow(table, locale, isPartialVaccination); addIssuerRow(table, locale, "issuer.issuer", 5, true, PADDING_LEFT); return table; diff --git a/src/main/java/ch/admin/bag/covidcertificate/service/domain/VaccinationCertificatePdf.java b/src/main/java/ch/admin/bag/covidcertificate/service/domain/VaccinationCertificatePdf.java index 1def0a3e..b4bde537 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/service/domain/VaccinationCertificatePdf.java +++ b/src/main/java/ch/admin/bag/covidcertificate/service/domain/VaccinationCertificatePdf.java @@ -54,4 +54,8 @@ public VaccinationCertificatePdf( this.issuer = issuer; } + public boolean isPartialVaccination() { + return this.numberOfDoses < this.totalNumberOfDoses; + } + } diff --git a/src/main/java/ch/admin/bag/covidcertificate/web/controller/ResponseStatusExceptionHandler.java b/src/main/java/ch/admin/bag/covidcertificate/web/controller/ResponseStatusExceptionHandler.java index 733df6f4..c912d2d7 100644 --- a/src/main/java/ch/admin/bag/covidcertificate/web/controller/ResponseStatusExceptionHandler.java +++ b/src/main/java/ch/admin/bag/covidcertificate/web/controller/ResponseStatusExceptionHandler.java @@ -18,7 +18,12 @@ public class ResponseStatusExceptionHandler { protected ResponseEntity handleCreateCertificateException(CreateCertificateException ex) { if (ex.getError().getHttpStatus() == HttpStatus.INTERNAL_SERVER_ERROR) { log.error(ex.getError().getErrorMessage(), ex); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + var error = ex.getError(); + if (error != null) { + return new ResponseEntity<>(ex.getError(), ex.getError().getHttpStatus()); + } else { + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } } else { log.warn("Create certificate exception, errorCode: {}", ex.getError().getErrorCode(), ex); return new ResponseEntity<>(ex.getError(), ex.getError().getHttpStatus()); diff --git a/src/main/resources/application-abn.yml b/src/main/resources/application-abn.yml index 4ef37ce3..695bde2c 100644 --- a/src/main/resources/application-abn.yml +++ b/src/main/resources/application-abn.yml @@ -40,6 +40,9 @@ cc-signing-service: cc-printing-service: url: "https://cc-printing-service.abn.app.cfap02.atlantica.admin.ch/api/v1/print" +cc-inapp-delivery-service: + url: "https://ch-covidcertificate-backend-delivery-ws.abn.app.cfap02.atlantica.admin.ch/cgs/delivery/v1/covidcert" + ## Mutual-SSL configuration app: conn: diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 48b1f12f..421258a2 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -41,6 +41,9 @@ cc-signing-service: cc-printing-service: url: "https://cc-printing-service.dev.app.cfap02.atlantica.admin.ch/api/v1/print" +cc-inapp-delivery-service: + url: "https://ch-covidcertificate-backend-delivery-ws.dev.app.cfap02.atlantica.admin.ch/cgs/delivery/v1/covidcert" + ## Mutual-SSL configuration app: conn: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 4025dca0..a9117202 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -41,6 +41,9 @@ cc-signing-service: cc-printing-service: url: "http://localhost:8124/api/v1/print" +cc-inapp-delivery-service: + url: "https://ch-covidcertificate-backend-delivery-ws.dev.app.cfap02.atlantica.admin.ch/cgs/delivery/v1/covidcert" + ## Mutual-SSL configuration app: conn: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 7b77ebec..54bba02f 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -40,6 +40,9 @@ cc-signing-service: cc-printing-service: url: "https://cc-printing-service.app.cfap02.atlantica.admin.ch/api/v1/print" +cc-inapp-delivery-service: + url: "https://ch-covidcertificate-backend-delivery-ws.app.cfap02.atlantica.admin.ch/cgs/delivery/v1/covidcert" + ## Mutual-SSL configuration app: conn: diff --git a/src/main/resources/templates/messages/document-messages_de.properties b/src/main/resources/templates/messages/document-messages_de.properties index 24a65d08..16c0f72e 100644 --- a/src/main/resources/templates/messages/document-messages_de.properties +++ b/src/main/resources/templates/messages/document-messages_de.properties @@ -34,3 +34,7 @@ vaccination.title=Impfung vaccination.type.label=Art des Impfstoffs test.result.value=Nicht erkannt (Negativ) qrCode.date.label=Date format used: dd.mm.yyyy +evidence.document.title=Nachweis +evidence.title=Unvollständige Impfung +evidence.issuer=Der Nachweis wurde herausgegeben durch +evidence.qrCode.label=Nachweis erstellt am {0} um {1} diff --git a/src/main/resources/templates/messages/document-messages_en.properties b/src/main/resources/templates/messages/document-messages_en.properties index 3989f4a8..19fb9dfc 100644 --- a/src/main/resources/templates/messages/document-messages_en.properties +++ b/src/main/resources/templates/messages/document-messages_en.properties @@ -34,3 +34,7 @@ vaccination.title=Vaccination vaccination.type.label=Vaccine type test.result.value=Not detected (Negative) qrCode.date.label=Date format used: dd.mm.yyyy +evidence.document.title=Evidence +evidence.title=Vaccination incomplete +evidence.issuer=The evidence was issued by +evidence.qrCode.label=Evidence created on {0} at {1} diff --git a/src/main/resources/templates/messages/document-messages_fr.properties b/src/main/resources/templates/messages/document-messages_fr.properties index 5022c674..09758c79 100644 --- a/src/main/resources/templates/messages/document-messages_fr.properties +++ b/src/main/resources/templates/messages/document-messages_fr.properties @@ -34,3 +34,7 @@ vaccination.title=Vaccination vaccination.type.label=Type de vaccin test.result.value=Non détecté (Négatif) qrCode.date.label=Date format used: dd.mm.yyyy +evidence.document.title=Justificatif +evidence.title=Vaccination incomplète +evidence.issuer=Le justificatif a été délivré par +evidence.qrCode.label=Justificatif établi le {0} à {1} diff --git a/src/main/resources/templates/messages/document-messages_it.properties b/src/main/resources/templates/messages/document-messages_it.properties index 31b16a46..e44daabe 100644 --- a/src/main/resources/templates/messages/document-messages_it.properties +++ b/src/main/resources/templates/messages/document-messages_it.properties @@ -34,3 +34,7 @@ vaccination.title=Vaccinazione vaccination.type.label=Tipo di vaccino test.result.value=Non rilevato (Negativo) qrCode.date.label=Date format used: dd.mm.yyyy +evidence.document.title=Documentazione +evidence.title=Vaccinazione incompleta +evidence.issuer=La documentazione è stata emessa da +evidence.qrCode.label=Documentazione emessa il {0} alle {1} diff --git a/src/main/resources/templates/messages/document-messages_rm.properties b/src/main/resources/templates/messages/document-messages_rm.properties index 8a4e604a..34288345 100644 --- a/src/main/resources/templates/messages/document-messages_rm.properties +++ b/src/main/resources/templates/messages/document-messages_rm.properties @@ -34,3 +34,7 @@ vaccination.title=Vaccinaziun vaccination.type.label=Tip dal vaccin test.result.value=Betg identifitgà (Negativ) qrCode.date.label=Date format used: dd.mm.yyyy +evidence.document.title=Cumprova +evidence.title=Vaccinaziun incumpletta +evidence.issuer=La cumprova è vegnida emessa da +evidence.qrCode.label=Cumprova emessa ils {0} a las {1} diff --git a/src/test/java/ch/admin/bag/covidcertificate/TestModelProvider.java b/src/test/java/ch/admin/bag/covidcertificate/TestModelProvider.java index 125e8951..ff5a4aad 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/TestModelProvider.java +++ b/src/test/java/ch/admin/bag/covidcertificate/TestModelProvider.java @@ -17,16 +17,39 @@ public static VaccinationCertificateCreateDto getVaccinationCertificateCreateDto getCovidCertificatePersonDto(), List.of(getVaccinationCertificateDataDto(medicalProductCode)), language, - getCovidCertificateAddressDto() + getCovidCertificateAddressDto(), + null ); } + public static VaccinationCertificateCreateDto getVaccinationCertificateCreateDto(String medicalProductCode, String language, String inAppCode) { + return new VaccinationCertificateCreateDto( + getCovidCertificatePersonDto(), + List.of(getVaccinationCertificateDataDto(medicalProductCode)), + language, + null, + inAppCode + ); + } + + public static TestCertificateCreateDto getTestCertificateCreateDto(String typeCode, String manufacturerCode, String language) { return new TestCertificateCreateDto( getCovidCertificatePersonDto(), List.of(getTestCertificateDataDto(typeCode, manufacturerCode)), language, - getCovidCertificateAddressDto() + getCovidCertificateAddressDto(), + null + ); + } + + public static TestCertificateCreateDto getTestCertificateCreateDto(String typeCode, String manufacturerCode, String language, String inAppCode) { + return new TestCertificateCreateDto( + getCovidCertificatePersonDto(), + List.of(getTestCertificateDataDto(typeCode, manufacturerCode)), + language, + null, + inAppCode ); } @@ -35,7 +58,18 @@ public static RecoveryCertificateCreateDto getRecoveryCertificateCreateDto(Strin getCovidCertificatePersonDto(), List.of(getRecoveryCertificateDataDto()), language, - getCovidCertificateAddressDto() + getCovidCertificateAddressDto(), + null + ); + } + + public static RecoveryCertificateCreateDto getRecoveryCertificateCreateDto(String language, String inAppCode) { + return new RecoveryCertificateCreateDto( + getCovidCertificatePersonDto(), + List.of(getRecoveryCertificateDataDto()), + language, + null, + inAppCode ); } diff --git a/src/test/java/ch/admin/bag/covidcertificate/api/CertificateCreateDtoTest.java b/src/test/java/ch/admin/bag/covidcertificate/api/CertificateCreateDtoTest.java index 03f91543..d45703d1 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/api/CertificateCreateDtoTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/api/CertificateCreateDtoTest.java @@ -4,6 +4,9 @@ import ch.admin.bag.covidcertificate.api.request.CertificateCreateDto; import ch.admin.bag.covidcertificate.api.request.CovidCertificateAddressDto; import ch.admin.bag.covidcertificate.api.request.CovidCertificatePersonDto; +import com.flextrade.jfixture.JFixture; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.Test; import static ch.admin.bag.covidcertificate.api.Constants.*; @@ -17,13 +20,36 @@ public class CertificateCreateDtoTest { private final CovidCertificateAddressDto addressDto = new CovidCertificateAddressDto("street", 1000, "city", "BE"); private static class CertificateCreateDtoIml extends CertificateCreateDto { + public CertificateCreateDtoIml( + CovidCertificatePersonDto personData, + String language + ) { + super(personData, language, null, null); + } public CertificateCreateDtoIml( CovidCertificatePersonDto personData, String language, CovidCertificateAddressDto address ) { - super(personData, language, address); + super(personData, language, address, null); + } + + public CertificateCreateDtoIml( + CovidCertificatePersonDto personData, + String language, + String inAppDeliveryCode + ) { + super(personData, language, null, inAppDeliveryCode); + } + + public CertificateCreateDtoIml( + CovidCertificatePersonDto personData, + String language, + CovidCertificateAddressDto address, + String inAppDeliveryCode + ) { + super(personData, language, address, inAppDeliveryCode); } } @@ -76,8 +102,52 @@ public void doesValidateSuccessfully() { CertificateCreateDto testee = new CertificateCreateDtoIml( personDto, "de", - new CovidCertificateAddressDto("street", 1000, "city", "BE") + addressDto ); assertDoesNotThrow(testee::validate); } + + @Test + public void throwsException__ifAddressAndInAppDeliveryCodeArePassed() { + CertificateCreateDto testee = new CertificateCreateDtoIml(personDto, "de", addressDto, RandomStringUtils.randomAlphanumeric(9)); + + CreateCertificateException exception = assertThrows(CreateCertificateException.class, testee::validate); + assertEquals(DUPLICATE_DELIVERY_METHOD, exception.getError()); + } + + @Test + public void throwsException__ifInvalidInAppDeliveryCode() { + // test too long + CertificateCreateDto testee = new CertificateCreateDtoIml(personDto, "de", RandomStringUtils.randomAlphanumeric(10)); + CreateCertificateException exception = assertThrows(CreateCertificateException.class, testee::validate); + assertEquals(INVALID_IN_APP_CODE, exception.getError()); + + // test too short + testee = new CertificateCreateDtoIml(personDto, "de", RandomStringUtils.randomAlphanumeric(8)); + exception = assertThrows(CreateCertificateException.class, testee::validate); + assertEquals(INVALID_IN_APP_CODE, exception.getError()); + + // test not alphanumeric + testee = new CertificateCreateDtoIml(personDto, "de", RandomStringUtils.random(9)); + exception = assertThrows(CreateCertificateException.class, testee::validate); + assertEquals(INVALID_IN_APP_CODE, exception.getError()); + } + + @Test + public void validatesSuccessfully__ifAddressOnlyIsPassed() { + CertificateCreateDto testee = new CertificateCreateDtoIml(personDto, "de", addressDto); + assertDoesNotThrow(testee::validate); + } + + @Test + public void validatesSuccessfully__ifInAppDeliveryCodeOnlyIsPassed() { + CertificateCreateDto testee = new CertificateCreateDtoIml(personDto, "de", RandomStringUtils.randomAlphanumeric(9)); + assertDoesNotThrow(testee::validate); + } + + @Test + public void validatesSuccessfully__ifNoDeliveryIsPassed() { + CertificateCreateDto testee = new CertificateCreateDtoIml(personDto, "de"); + assertDoesNotThrow(testee::validate); + } } diff --git a/src/test/java/ch/admin/bag/covidcertificate/api/CovidCertificatePersonMapperTest.java b/src/test/java/ch/admin/bag/covidcertificate/api/CovidCertificatePersonMapperTest.java index e1c7f4f8..b438c2fb 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/api/CovidCertificatePersonMapperTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/api/CovidCertificatePersonMapperTest.java @@ -1,17 +1,26 @@ package ch.admin.bag.covidcertificate.api; +import ch.admin.bag.covidcertificate.api.exception.CreateCertificateException; import ch.admin.bag.covidcertificate.api.mapper.CovidCertificatePersonMapper; import ch.admin.bag.covidcertificate.api.request.CovidCertificatePersonDto; +import ch.admin.bag.covidcertificate.api.request.CovidCertificatePersonNameDto; import ch.admin.bag.covidcertificate.service.domain.CovidCertificatePerson; import com.flextrade.jfixture.JFixture; import org.junit.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import java.time.LocalDate; + +import static ch.admin.bag.covidcertificate.api.Constants.INVALID_STANDARDISED_FAMILY_NAME; +import static ch.admin.bag.covidcertificate.api.Constants.INVALID_STANDARDISED_GIVEN_NAME; +import static org.junit.jupiter.api.Assertions.*; public class CovidCertificatePersonMapperTest { private final JFixture jFixture = new JFixture(); + private final String givenName = "GivenName"; + private final String familyName = "FamilyName"; + private final LocalDate dateOfBirth = jFixture.create(LocalDate.class); @Test public void mapsFamilyName() { @@ -34,4 +43,32 @@ public void mapsDateOfBirth() { CovidCertificatePerson actual = CovidCertificatePersonMapper.toCovidCertificatePerson(incoming); assertEquals(incoming.getDateOfBirth(), actual.getDateOfBirth()); } -} \ No newline at end of file + + @Test + public void testInvalidStandardisedGivenName() { + final var personDto = new CovidCertificatePersonDto( + new CovidCertificatePersonNameDto( + familyName, + "ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜabcdefghijklmnopqrstuvwxyzäöüABCDEFGHIJKLMNOPQRSTUV" + ), + dateOfBirth + ); + var exception = assertThrows(CreateCertificateException.class, + () -> CovidCertificatePersonMapper.toCovidCertificatePerson(personDto)); + assertEquals(INVALID_STANDARDISED_GIVEN_NAME, exception.getError()); + } + + @Test + public void testInvalidStandardisedFamilyName() { + final var personDto = new CovidCertificatePersonDto( + new CovidCertificatePersonNameDto( + "ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜabcdefghijklmnopqrstuvwxyzäöüABCDEFGHIJKLMNOPQRSTUV", + givenName + ), + dateOfBirth + ); + var exception = assertThrows(CreateCertificateException.class, + () -> CovidCertificatePersonMapper.toCovidCertificatePerson(personDto)); + assertEquals(INVALID_STANDARDISED_FAMILY_NAME, exception.getError()); + } +} diff --git a/src/test/java/ch/admin/bag/covidcertificate/api/RecoveryCertificateCreateDtoTest.java b/src/test/java/ch/admin/bag/covidcertificate/api/RecoveryCertificateCreateDtoTest.java index a064990d..a3179864 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/api/RecoveryCertificateCreateDtoTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/api/RecoveryCertificateCreateDtoTest.java @@ -25,7 +25,8 @@ public void testNoRecoveryData() { personDto, null, language, - TestModelProvider.getCovidCertificateAddressDto() + TestModelProvider.getCovidCertificateAddressDto(), + null ); CreateCertificateException exception = assertThrows(CreateCertificateException.class, testee::validate); assertEquals(NO_RECOVERY_DATA, exception.getError()); @@ -34,7 +35,8 @@ public void testNoRecoveryData() { personDto, List.of(), language, - TestModelProvider.getCovidCertificateAddressDto() + TestModelProvider.getCovidCertificateAddressDto(), + null ); exception = assertThrows(CreateCertificateException.class, testee::validate); assertEquals(NO_RECOVERY_DATA, exception.getError()); @@ -43,7 +45,8 @@ public void testNoRecoveryData() { personDto, List.of(dataDto), language, - TestModelProvider.getCovidCertificateAddressDto() + TestModelProvider.getCovidCertificateAddressDto(), + null ); assertDoesNotThrow(testee::validate); } diff --git a/src/test/java/ch/admin/bag/covidcertificate/api/TestCertificateCreateDtoTest.java b/src/test/java/ch/admin/bag/covidcertificate/api/TestCertificateCreateDtoTest.java index 1e535423..a41c879d 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/api/TestCertificateCreateDtoTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/api/TestCertificateCreateDtoTest.java @@ -25,7 +25,8 @@ public void testNoTestData() { personDto, null, language, - getCovidCertificateAddressDto() + getCovidCertificateAddressDto(), + null ); CreateCertificateException exception = assertThrows(CreateCertificateException.class, testee::validate); assertEquals(NO_TEST_DATA, exception.getError()); @@ -34,7 +35,8 @@ public void testNoTestData() { personDto, List.of(), language, - getCovidCertificateAddressDto() + getCovidCertificateAddressDto(), + null ); exception = assertThrows(CreateCertificateException.class, testee::validate); assertEquals(NO_TEST_DATA, exception.getError()); @@ -43,7 +45,8 @@ public void testNoTestData() { personDto, List.of(dataDto), language, - getCovidCertificateAddressDto() + getCovidCertificateAddressDto(), + null ); assertDoesNotThrow(testee::validate); } diff --git a/src/test/java/ch/admin/bag/covidcertificate/api/TestCertificateQrCodeMapperTest.java b/src/test/java/ch/admin/bag/covidcertificate/api/TestCertificateQrCodeMapperTest.java index 5f1d95a3..39de42df 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/api/TestCertificateQrCodeMapperTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/api/TestCertificateQrCodeMapperTest.java @@ -5,12 +5,14 @@ import ch.admin.bag.covidcertificate.api.valueset.TestValueSet; import ch.admin.bag.covidcertificate.service.domain.TestCertificateQrCode; import com.flextrade.jfixture.JFixture; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.LocalDateTime; import java.time.Month; import java.time.ZonedDateTime; +import static ch.admin.bag.covidcertificate.FixtureCustomization.customizeTestValueSet; import static ch.admin.bag.covidcertificate.TestModelProvider.getTestCertificateCreateDto; import static ch.admin.bag.covidcertificate.api.Constants.*; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -18,9 +20,16 @@ public class TestCertificateQrCodeMapperTest { - private final JFixture jFixture = new JFixture(); - private final TestCertificateCreateDto incoming = jFixture.create(TestCertificateCreateDto.class); - private final TestValueSet testValueSet = jFixture.create(TestValueSet.class); + private final JFixture fixture = new JFixture(); + private TestCertificateCreateDto incoming; + private TestValueSet testValueSet; + + @BeforeEach + public void setUp() { + customizeTestValueSet(fixture); + incoming = fixture.create(TestCertificateCreateDto.class); + testValueSet = fixture.create(TestValueSet.class); + } @Test public void mapsFamilyName() { diff --git a/src/test/java/ch/admin/bag/covidcertificate/api/VaccinationCertificateCreateDtoTest.java b/src/test/java/ch/admin/bag/covidcertificate/api/VaccinationCertificateCreateDtoTest.java index 70c8a08e..a94e696d 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/api/VaccinationCertificateCreateDtoTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/api/VaccinationCertificateCreateDtoTest.java @@ -25,7 +25,8 @@ public void testNoVaccinationData() { personDto, null, language, - TestModelProvider.getCovidCertificateAddressDto() + TestModelProvider.getCovidCertificateAddressDto(), + null ); CreateCertificateException exception = assertThrows(CreateCertificateException.class, testee::validate); assertEquals(NO_VACCINATION_DATA, exception.getError()); @@ -34,7 +35,8 @@ public void testNoVaccinationData() { personDto, List.of(), language, - TestModelProvider.getCovidCertificateAddressDto() + TestModelProvider.getCovidCertificateAddressDto(), + null ); exception = assertThrows(CreateCertificateException.class, testee::validate); assertEquals(NO_VACCINATION_DATA, exception.getError()); @@ -43,7 +45,8 @@ public void testNoVaccinationData() { personDto, List.of(dataDto), language, - TestModelProvider.getCovidCertificateAddressDto() + TestModelProvider.getCovidCertificateAddressDto(), + null ); assertDoesNotThrow(testee::validate); } diff --git a/src/test/java/ch/admin/bag/covidcertificate/api/VaccinationCertificateDataDtoTest.java b/src/test/java/ch/admin/bag/covidcertificate/api/VaccinationCertificateDataDtoTest.java index 44780f8c..187a0584 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/api/VaccinationCertificateDataDtoTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/api/VaccinationCertificateDataDtoTest.java @@ -60,26 +60,6 @@ public void testInvalidDoses() { exception = assertThrows(CreateCertificateException.class, testee::validate); assertEquals(INVALID_DOSES, exception.getError()); - testee = new VaccinationCertificateDataDto( - medicinalProduct, - 10, - 8, - vaccinationDate, - countryOfVaccination - ); - exception = assertThrows(CreateCertificateException.class, testee::validate); - assertEquals(INVALID_DOSES, exception.getError()); - - testee = new VaccinationCertificateDataDto( - medicinalProduct, - 9, - 10, - vaccinationDate, - countryOfVaccination - ); - exception = assertThrows(CreateCertificateException.class, testee::validate); - assertEquals(INVALID_DOSES, exception.getError()); - testee = new VaccinationCertificateDataDto( medicinalProduct, numberOfDoses, diff --git a/src/test/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/DefaultInAppDeliveryClientITTest.java b/src/test/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/DefaultInAppDeliveryClientITTest.java new file mode 100644 index 00000000..fce8de03 --- /dev/null +++ b/src/test/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/DefaultInAppDeliveryClientITTest.java @@ -0,0 +1,47 @@ +package ch.admin.bag.covidcertificate.client.inapp_delivery.internal; + +import ch.admin.bag.covidcertificate.api.exception.CreateCertificateException; +import ch.admin.bag.covidcertificate.client.inapp_delivery.domain.InAppDeliveryRequestDto; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; + +import static ch.admin.bag.covidcertificate.api.Constants.INAPP_DELIVERY_FAILED; +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest(properties = { + "spring.jpa.hibernate.ddl-auto=create", + "spring.datasource.driver-class-name=org.h2.Driver", + "spring.datasource.url=jdbc:h2:~/test;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE", + "spring.datasource.username=sa", + "spring.datasource.password=sa", + "spring.flyway.clean-on-validation-error=true" +}) +@ActiveProfiles("local") +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) +@Disabled("Only runs manually") +class DefaultInAppDeliveryClientITTest { + + private static final String validTestCode = "BITBITBIT"; + + @Autowired + private DefaultInAppDeliveryClient client; + + @Test + void deliverToApp_invalid() { + var requestDto = new InAppDeliveryRequestDto("test", "test", "test"); + CreateCertificateException exception = assertThrows(CreateCertificateException.class, + () -> client.deliverToApp(requestDto)); + + assertEquals(INAPP_DELIVERY_FAILED,exception.getError()); + } + + @Test + void deliverToApp_valid() { + var requestDto = new InAppDeliveryRequestDto(validTestCode, "test", "test"); + assertDoesNotThrow(() -> client.deliverToApp(requestDto)); + } +} diff --git a/src/test/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/DefaultInAppDeliveryClientTest.java b/src/test/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/DefaultInAppDeliveryClientTest.java new file mode 100644 index 00000000..f3818372 --- /dev/null +++ b/src/test/java/ch/admin/bag/covidcertificate/client/inapp_delivery/internal/DefaultInAppDeliveryClientTest.java @@ -0,0 +1,100 @@ +package ch.admin.bag.covidcertificate.client.inapp_delivery.internal; + +import ch.admin.bag.covidcertificate.api.exception.CreateCertificateException; +import ch.admin.bag.covidcertificate.client.inapp_delivery.domain.InAppDeliveryRequestDto; +import ch.admin.bag.covidcertificate.config.security.authentication.ServletJeapAuthorization; +import ch.admin.bag.covidcertificate.service.KpiDataService; +import com.flextrade.jfixture.JFixture; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.reactive.function.client.WebClient; + +import java.io.IOException; + +import static ch.admin.bag.covidcertificate.api.Constants.INAPP_DELIVERY_FAILED; +import static ch.admin.bag.covidcertificate.api.Constants.INVALID_IN_APP_CODE; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class DefaultInAppDeliveryClientTest { + + static final JFixture fixture = new JFixture(); + static MockWebServer mockInAppDeliveryService; + + static ServletJeapAuthorization jeapAuthorization; + static KpiDataService kpiLogService; + + @InjectMocks + private DefaultInAppDeliveryClient inAppDeliveryClient; + + private InAppDeliveryRequestDto requestDto; + + @BeforeAll + static void setUp() throws IOException { + jeapAuthorization = mock(ServletJeapAuthorization.class); + kpiLogService = mock(KpiDataService.class); + mockInAppDeliveryService = new MockWebServer(); + mockInAppDeliveryService.start(); + } + + @BeforeEach + void initialize() { + reset(jeapAuthorization, kpiLogService); + this.requestDto = fixture.create(InAppDeliveryRequestDto.class); + this.inAppDeliveryClient = new DefaultInAppDeliveryClient(WebClient.create(), jeapAuthorization, kpiLogService); + ReflectionTestUtils.setField(this.inAppDeliveryClient, "serviceUri", + String.format("http://localhost:%s/", mockInAppDeliveryService.getPort())); + } + + @Test + void doesSendInAppDeliverySuccessfully() { + mockInAppDeliveryService.enqueue(new MockResponse().setResponseCode(200)); + + assertDoesNotThrow(() -> this.inAppDeliveryClient.deliverToApp(this.requestDto)); + } + + @Test + void throwsException__ifResponseCode404() { + mockInAppDeliveryService.enqueue(new MockResponse().setResponseCode(404)); + + var exception = assertThrows(CreateCertificateException.class, () -> this.inAppDeliveryClient.deliverToApp(this.requestDto)); + assertEquals(INVALID_IN_APP_CODE, exception.getError()); + } + + @Test + void throwsException__ifResponseCode500() { + mockInAppDeliveryService.enqueue(new MockResponse().setResponseCode(500)); + + var exception = assertThrows(CreateCertificateException.class, () -> this.inAppDeliveryClient.deliverToApp(this.requestDto)); + assertEquals(INAPP_DELIVERY_FAILED, exception.getError()); + } + + @Test + void throwsException__ifServiceUnreachable() { + ReflectionTestUtils.setField(this.inAppDeliveryClient, "serviceUri", "http://127.0.0.1"); + + var exception = assertThrows(CreateCertificateException.class, () -> this.inAppDeliveryClient.deliverToApp(this.requestDto)); + assertEquals(INAPP_DELIVERY_FAILED, exception.getError()); + } + + @Test + void logsKpi__ifDeliverySuccessful() { + when(jeapAuthorization.getExtIdInAuthentication()).thenReturn("test_ext_id"); + mockInAppDeliveryService.enqueue(new MockResponse().setResponseCode(200)); + + assertDoesNotThrow(() -> this.inAppDeliveryClient.deliverToApp(this.requestDto)); + verify(this.kpiLogService, times(1)).log(any()); + } + + @AfterAll + static void tearDown() throws Throwable { + mockInAppDeliveryService.shutdown(); + } +} \ No newline at end of file diff --git a/src/test/java/ch/admin/bag/covidcertificate/service/CBORServiceTest.java b/src/test/java/ch/admin/bag/covidcertificate/service/CBORServiceTest.java index ea7afb17..41901366 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/service/CBORServiceTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/service/CBORServiceTest.java @@ -53,7 +53,7 @@ void whenGetPayload_thenOk() { CBORInstantConverter instantConverter = new CBORInstantConverter(); // given Instant issuedAt = Instant.parse("2021-01-01T00:00:00Z"); - Instant expiration = Instant.parse("2022-01-01T00:00:00Z"); + Instant expiration = Instant.parse("2023-01-01T00:00:00Z"); when(coseTime.getIssuedAt()).thenReturn(issuedAt); when(coseTime.getExpiration()).thenReturn(expiration); byte[] hcert = CBORObject.FromJSONString("{\"hello\": \"world\"}").EncodeToBytes(); diff --git a/src/test/java/ch/admin/bag/covidcertificate/service/COSETimeTest.java b/src/test/java/ch/admin/bag/covidcertificate/service/COSETimeTest.java new file mode 100644 index 00000000..21d3cb1b --- /dev/null +++ b/src/test/java/ch/admin/bag/covidcertificate/service/COSETimeTest.java @@ -0,0 +1,51 @@ +package ch.admin.bag.covidcertificate.service; + +import com.flextrade.jfixture.JFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Clock; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@ExtendWith(MockitoExtension.class) +public class COSETimeTest { + private static final ZoneId ZONE_ID = ZoneId.of("Europe/Zurich"); + private final JFixture jFixture = new JFixture(); + private Instant instant; + private COSETime coseTime; + + @BeforeEach + public void init() { + // given + instant = jFixture.create(Instant.class); + coseTime = new COSETime(Clock.fixed(instant, ZONE_ID)); + } + + @Test + public void whenGetIssuedAt_thenOk() { + // when + Instant result = coseTime.getIssuedAt(); + // then + assertEquals(instant, result); + } + + @Test + public void whenGetExpiration_thenOk() { + // when + Instant result = coseTime.getExpiration(); + // then + long diff = ChronoUnit.MONTHS.between(getLocalDateTime(instant), getLocalDateTime(result)); + assertEquals(diff, 24); + } + + private LocalDateTime getLocalDateTime(Instant instant) { + return instant.atZone(ZONE_ID).toLocalDateTime(); + } +} diff --git a/src/test/java/ch/admin/bag/covidcertificate/service/CovidCertificateDtoMapperServiceTest.java b/src/test/java/ch/admin/bag/covidcertificate/service/CovidCertificateDtoMapperServiceTest.java index df6b7ef8..9c0da678 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/service/CovidCertificateDtoMapperServiceTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/service/CovidCertificateDtoMapperServiceTest.java @@ -21,6 +21,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.jupiter.MockitoExtension; +import static ch.admin.bag.covidcertificate.FixtureCustomization.customizeTestValueSet; import static ch.admin.bag.covidcertificate.api.Constants.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -40,6 +41,7 @@ class CovidCertificateDtoMapperServiceTest { @BeforeEach public void setUp() { + customizeTestValueSet(fixture); lenient().when(valueSetsService.getVaccinationValueSet(any())).thenReturn(fixture.create(VaccinationValueSet.class)); lenient().when(valueSetsService.getTestValueSet(any())).thenReturn(fixture.create(TestValueSet.class)); lenient().when(valueSetsService.getCountryCode(any(), any())).thenReturn(fixture.create(CountryCode.class)); diff --git a/src/test/java/ch/admin/bag/covidcertificate/service/CovidCertificateGenerationServiceTest.java b/src/test/java/ch/admin/bag/covidcertificate/service/CovidCertificateGenerationServiceTest.java index e8015e72..5d0bccb5 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/service/CovidCertificateGenerationServiceTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/service/CovidCertificateGenerationServiceTest.java @@ -1,6 +1,7 @@ package ch.admin.bag.covidcertificate.service; import ch.admin.bag.covidcertificate.api.exception.CreateCertificateException; +import ch.admin.bag.covidcertificate.client.inapp_delivery.InAppDeliveryClient; import ch.admin.bag.covidcertificate.client.printing.PrintQueueClient; import ch.admin.bag.covidcertificate.service.document.CovidPdfCertificateGenerationService; import ch.admin.bag.covidcertificate.service.domain.*; @@ -41,6 +42,8 @@ class CovidCertificateGenerationServiceTest { private ObjectMapper objectMapper; @Mock private PrintQueueClient printQueueClient; + @Mock + private InAppDeliveryClient inAppDeliveryClient; private final JFixture fixture = new JFixture(); @@ -159,6 +162,22 @@ void shouldUVCI() throws IOException { assertNotNull(actual.getUvci()); } + + @Test + void shouldSendInAppDelivery__whenCodeIsPassed() { + var createDto = getVaccinationCertificateCreateDto("EU/1/20/1507", "de", "BITBITBIT"); + + assertDoesNotThrow(() -> service.generateCovidCertificate(createDto)); + verify(inAppDeliveryClient, times(1)).deliverToApp(any()); + } + + @Test + void shouldCallPrintingService__whenAddressPassed() { + var createDto = getVaccinationCertificateCreateDto("EU/1/20/1507", "de"); + + assertDoesNotThrow(() -> service.generateCovidCertificate(createDto)); + verify(printQueueClient, times(1)).sendPrintJob(any()); + } } @Nested @@ -259,6 +278,22 @@ void shouldUVCI() throws IOException { assertNotNull(actual.getUvci()); } + + @Test + void shouldSendInAppDelivery__whenCodeIsPassed() { + var createDto = getTestCertificateCreateDto(null, "1833", "de", "BITBITBIT"); + + assertDoesNotThrow(() -> service.generateCovidCertificate(createDto)); + verify(inAppDeliveryClient, times(1)).deliverToApp(any()); + } + + @Test + void shouldCallPrintingService__whenAddressPassed() { + var createDto = getTestCertificateCreateDto(null, "1833", "de"); + + assertDoesNotThrow(() -> service.generateCovidCertificate(createDto)); + verify(printQueueClient, times(1)).sendPrintJob(any()); + } } @Nested @@ -359,5 +394,21 @@ void shouldUVCI() throws IOException { assertNotNull(actual.getUvci()); } + + @Test + void shouldSendInAppDelivery__whenCodeIsPassed() { + var createDto = getRecoveryCertificateCreateDto("de", "BITBITBIT"); + + assertDoesNotThrow(() -> service.generateCovidCertificate(createDto)); + verify(inAppDeliveryClient, times(1)).deliverToApp(any()); + } + + @Test + void shouldCallPrintingService__whenAddressPassed() { + var createDto = getRecoveryCertificateCreateDto("de"); + + assertDoesNotThrow(() -> service.generateCovidCertificate(createDto)); + verify(printQueueClient, times(1)).sendPrintJob(any()); + } } } diff --git a/src/test/java/ch/admin/bag/covidcertificate/service/CsvServiceTest.java b/src/test/java/ch/admin/bag/covidcertificate/service/CsvServiceTest.java index 413ead65..5caee133 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/service/CsvServiceTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/service/CsvServiceTest.java @@ -124,7 +124,7 @@ private CertificateCreateDtoFamilyNameMatcher(String familyName) { @Override public boolean matches(T t) { - if(t == null) return false; + if (t == null) return false; var actual = t.getPersonData().getName().getFamilyName(); return familyName.equals(actual); } @@ -253,5 +253,6 @@ void massTest(String path) throws IOException { assertNotNull(response.getZip()); inputStream.close(); } + } } diff --git a/src/test/java/ch/admin/bag/covidcertificate/service/document/CovidPdfCertificateGenerationServiceTest.java b/src/test/java/ch/admin/bag/covidcertificate/service/document/CovidPdfCertificateGenerationServiceTest.java index f6e29cc1..c91ae5a6 100644 --- a/src/test/java/ch/admin/bag/covidcertificate/service/document/CovidPdfCertificateGenerationServiceTest.java +++ b/src/test/java/ch/admin/bag/covidcertificate/service/document/CovidPdfCertificateGenerationServiceTest.java @@ -51,8 +51,7 @@ void setup() throws Exception { } - private void generateDocument_vaccine(String language, String familyName, String givenName) throws Exception { - VaccinationCertificateCreateDto createDto = getVaccinationCertificateCreateDto("1119349007", language); + private void generateDocument_vaccine(VaccinationCertificateCreateDto createDto, String language, String familyName, String givenName, String fileName) throws Exception { VaccinationValueSet vaccinationValueSet = new VaccinationValueSet(); ReflectionTestUtils.setField(vaccinationValueSet, "prophylaxis", "SARS-CoV-2 mRNA vaccine"); ReflectionTestUtils.setField(vaccinationValueSet, "medicinalProduct", "COVID-19 Vaccine Moderna"); @@ -64,10 +63,21 @@ private void generateDocument_vaccine(String language, String familyName, String VaccinationCertificateQrCode qrCodeData = VaccinationCertificateQrCodeMapper.toVaccinationCertificateQrCode(createDto, vaccinationValueSet); VaccinationCertificatePdf pdfData = VaccinationCertificatePdfMapper.toVaccinationCertificatePdf(createDto, vaccinationValueSet, qrCodeData, country, countryEn); - doTest(pdfData, "vaccine", language); + doTest(pdfData, fileName, language); } + private void generateDocument_vaccine(String language, String familyName, String givenName) throws Exception { + VaccinationCertificateCreateDto createDto = getVaccinationCertificateCreateDto("1119349007", language); + this.generateDocument_vaccine(createDto, language, familyName, givenName, "vaccine"); + } + + private void generateDocument_partialVaccination(String language, String familyName, String givenName) throws Exception { + VaccinationCertificateCreateDto createDto = getVaccinationCertificateCreateDto("1119349007", language); + ReflectionTestUtils.setField(createDto.getVaccinationInfo().get(0), "numberOfDoses", 1); + this.generateDocument_vaccine(createDto, language, familyName, givenName, "partial-vaccine"); + } + private void generateDocument_test(String language, String familyName, String givenName) throws Exception { TestCertificateCreateDto createDto = getTestCertificateCreateDto("test", "test", language); TestValueSet testValueSet = new TestValueSet(); @@ -122,6 +132,10 @@ private void generateAllDocuments(String familyName, String givenName) throws Ex generateDocument_test("it", familyName, givenName); generateDocument_test("rm", familyName, givenName); + generateDocument_partialVaccination("de", familyName, givenName); + generateDocument_partialVaccination("fr", familyName, givenName); + generateDocument_partialVaccination("it", familyName, givenName); + generateDocument_partialVaccination("rm", familyName, givenName); } diff --git a/src/test/resources/csv/recovery_csv_error_response.csv b/src/test/resources/csv/recovery_csv_error_response.csv new file mode 100644 index 00000000..72892060 --- /dev/null +++ b/src/test/resources/csv/recovery_csv_error_response.csv @@ -0,0 +1,2 @@ +"CANTONCODESENDER","CITY","COUNTRYOFTEST","DATEOFBIRTH","DATEOFFIRSTPOSITIVETESTRESULT","ERROR","FAMILYNAME","GIVENNAME","ID","LANGUAGE","STREETANDNR","ZIPCODE" +"BE","Bern","CH","1985-09-20","2020-12-20","{""errorCode"":474,""errorMessage"":""Paper-based delivery requires a valid address."",""httpStatus"":""BAD_REQUEST""}","Muster","Max","1","de","MusterStrasse 1","998" diff --git a/src/test/resources/csv/vaccination_csv_multiple_invalid.csv b/src/test/resources/csv/vaccination_csv_multiple_invalid.csv index 4b358b03..2f083cb1 100644 --- a/src/test/resources/csv/vaccination_csv_multiple_invalid.csv +++ b/src/test/resources/csv/vaccination_csv_multiple_invalid.csv @@ -1,5 +1,5 @@ givenName;familyName;dateOfBirth;language;streetAndNr;zipCode;city;cantonCodeSender;medicinalProductCode;numberOfDoses;totalNumberOfDoses;vaccinationDate;countryOfVaccination Max;Muster;1985-09-20;de;MusterStrasse 1;1234;Bern;BE;EU/1/20/1507;2;2;2021-05-28;CH Max;TooLongdklsfjsodfjlksdjflkdsjflksjflkdsjflksdjlfkjd;1985-09-20;de;MusterStrasse 1;1234;Bern;BE;EU/1/20/1507;2;2;2021-05-28;CH -TooLongdklsfjsodfjlksdjflkdsjflksjflkdsjflksdjlfkjd;Muster;1985-09-20;de;MusterStrasse 1;1234;Bern;BE;EU/1/20/1507;2;2;2021-05-28;CH +TooLongdklsfjsodfjlksdjflkdsjflksjflkdsjflksdjlfkjdafkldjaöfidoaifhdisoaöfidhsaoöfdihsaiodifhdiosaöoisdidoöaoisdhfidosöaihdfi;Muster;1985-09-20;de;MusterStrasse 1;1234;Bern;BE;EU/1/20/1507;2;2;2021-05-28;CH Max;Muster;2085-09-20;de;MusterStrasse 1;1234;Bern;BE;EU/1/20/1507;2;2;2021-05-28;CH