Skip to content
This repository has been archived by the owner on May 16, 2023. It is now read-only.

Commit

Permalink
Added pocnat endpoint (#121)
Browse files Browse the repository at this point in the history
* * added /pocnat endpoints and converted results for /app

* * fixed checkstyle

* * added tests for PoC-NAT

* * added test coverage

* * removed some code smells
  • Loading branch information
Morphyum authored Feb 23, 2022
1 parent 7c1cf05 commit 671d158
Show file tree
Hide file tree
Showing 9 changed files with 499 additions and 22 deletions.
78 changes: 73 additions & 5 deletions src/main/java/app/coronawarn/testresult/TestResultController.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@

package app.coronawarn.testresult;

import app.coronawarn.testresult.model.PocNatResultList;
import app.coronawarn.testresult.model.QuickTestResultList;
import app.coronawarn.testresult.model.TestResult;
import app.coronawarn.testresult.model.TestResultList;
import app.coronawarn.testresult.model.TestResultRequest;
import app.coronawarn.testresult.model.TestResultResponse;
import app.coronawarn.testresult.model.TestType;
import app.coronawarn.testresult.service.TestResultService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
Expand Down Expand Up @@ -79,10 +81,10 @@ public ResponseEntity<TestResultResponse> result(
) {
log.info("Received test result request from app.");

TestResult result = testResultService.getOrCreate(request.getId(), false, null);
TestResult result = testResultService.getOrCreate(request.getId(), TestType.PCR, null);
return ResponseEntity.ok(new TestResultResponse()
.setLabId(result.getLabId())
.setTestResult(result.getResult(), result.getSc())
.setTestResult(testResultService.conversionCheck(result.getResult()), result.getSc())
);
}

Expand All @@ -108,7 +110,7 @@ public ResponseEntity<TestResultResponse> result(
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<?> results(
public ResponseEntity<Object> results(
@org.springframework.web.bind.annotation.RequestBody @NotNull @Valid TestResultList list
) {
log.info("Received {} test results to insert or update from lab.", list.getTestResults().size());
Expand Down Expand Up @@ -146,7 +148,7 @@ public ResponseEntity<TestResultResponse> quickTestResult(
@org.springframework.web.bind.annotation.RequestBody @Valid TestResultRequest request
) {
log.info("Received test result request from Quicktest.");
TestResult result = testResultService.getOrCreate(request.getId(), true, request.getSc());
TestResult result = testResultService.getOrCreate(request.getId(), TestType.QUICKTEST, request.getSc());
return ResponseEntity.ok(new TestResultResponse()
.setLabId(result.getLabId())
.setTestResult(result.getResult()));
Expand Down Expand Up @@ -174,7 +176,7 @@ public ResponseEntity<TestResultResponse> quickTestResult(
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<?> quicktestResults(
public ResponseEntity<Object> quicktestResults(
@org.springframework.web.bind.annotation.RequestBody @NotNull @Valid QuickTestResultList list
) {
log.info("Received {} test result to insert or update from Quicktests. ", list.getTestResults().size());
Expand All @@ -185,4 +187,70 @@ public ResponseEntity<?> quicktestResults(

return ResponseEntity.noContent().build();
}

/**
* Get the test result response from a request containing the id.
*
* @param request the test result request with id
* @return the test result response
*/
@Operation(
description = "The result and the sc (sample collection) timestamp of a PoC-NAT can be set.",
summary = "Set the testresult for a PoC-NAT.",
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = TestResultRequest.class))),
responses = {
@ApiResponse(
responseCode = "200",
description = "Ok, PoC-NAT result inserted successfully."
)
}
)
@PostMapping(
value = "/api/v1/pocnat/result",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<TestResultResponse> pocnatResult(
@org.springframework.web.bind.annotation.RequestBody @Valid TestResultRequest request
) {
log.info("Received test result request from PoC-NAT.");
TestResult result = testResultService.getOrCreate(request.getId(), TestType.POCNAT, request.getSc());
return ResponseEntity.ok(new TestResultResponse()
.setLabId(result.getLabId())
.setTestResult(result.getResult()));
}

/**
* Insert or update the PoC-NAT.
*
* @param list the test result list request
* @return the response
*/
@Operation(
description = "The result and the sc (sample collection) timestamp of a PoC-NAT can be set.",
summary = "Set multiple testresults for a PoC-NAT as an array.",
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = TestResultRequest.class))),
responses = {
@ApiResponse(
responseCode = "204",
description = "No content, PoC-NAT result(s) inserted successfully."
)
}
)
@PostMapping(
value = "/api/v1/pocnat/results",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Object> pocnatResults(
@org.springframework.web.bind.annotation.RequestBody @NotNull @Valid PocNatResultList list
) {
log.info("Received {} test result to insert or update from PoC-NATs. ", list.getTestResults().size());

list.getTestResults().stream()
.map(tr -> testResultService.convertPocNat(tr, list.getLabId()))
.forEach(testResultService::createOrUpdate);

return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public TestResultEntity setLabId(String labId) {

public enum Result {
PENDING, NEGATIVE, POSITIVE, INVALID, REDEEMED,
QUICK_PENDING, QUICK_NEGATIVE, QUICK_POSITIVE, QUICK_INVALID, QUICK_REDEEMED
QUICK_PENDING, QUICK_NEGATIVE, QUICK_POSITIVE, QUICK_INVALID, QUICK_REDEEMED,
POCNAT_PENDING, POCNAT_NEGATIVE, POCNAT_POSITIVE, POCNAT_INVALID, POCNAT_REDEEMED
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ public class TestResultExceptionHandler {
MethodArgumentNotValidException.class,
ConstraintViolationException.class
})
public ResponseEntity<?> handleValidationExceptions() {
public ResponseEntity<Object> handleValidationExceptions() {
log.warn("Request contains invalid arguments or constraint violations in body.");
return ResponseEntity.badRequest().build();
}

@ExceptionHandler(TestResultException.class)
public ResponseEntity<?> handleTestResultExceptions(
public ResponseEntity<Object> handleTestResultExceptions(
TestResultException exception
) {
log.warn("Request produced a test result exception with status {}.", exception.getStatus());
Expand Down
77 changes: 77 additions & 0 deletions src/main/java/app/coronawarn/testresult/model/PocNatResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Corona-Warn-App / cwa-testresult-server
*
* (C) 2020, T-Systems International GmbH
*
* Deutsche Telekom AG and all other contributors /
* copyright owners license this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package app.coronawarn.testresult.model;

import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;

/**
* Model of the test result.
*/
@Schema(
description = "The PoC-NAT result model."
)
@Getter
@Setter
@ToString
@EqualsAndHashCode
@Accessors(chain = true)
public class PocNatResult {

/**
* Hash (SHA256) of test result id (aka QR-Code, GUID) encoded as hex string.
*/
@NotBlank
@Pattern(regexp = "^[XxA-Fa-f0-9]([A-Fa-f0-9]{63})$")
@Schema(description = "the testId (Hashed GUID")
private String id;

/**
* The test result.
* 10: PoC-NAT-Pending
* 11: PoC-NAT-Negative
* 12: PoC-NAT-Positive
* 13: PoC-NAT-Invalid
* 14: PoC-NAT-Redeemed
*/
@Min(10)
@Max(14)
@NotNull
@Schema(description = "the result of the PoC-NAT", required = true)
private Integer result;

/**
* Timestamp of the SampleCollection (sc).
*/
@Schema(description = "the timestamp of the sample collection (sc) in unix epoch format. If not set,"
+ " the time of insertion will be used instead")
private Long sc;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Corona-Warn-App / cwa-testresult-server
*
* (C) 2020, T-Systems International GmbH
*
* Deutsche Telekom AG and all other contributors /
* copyright owners license this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package app.coronawarn.testresult.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;



/**
* Model of the test result list.
*/
@Schema(
description = "The PoC-NAT result list model."
)
@Getter
@Setter
@ToString
@EqualsAndHashCode
@Accessors(chain = true)
public class PocNatResultList {

/**
* The test result entries.
*/
@NotNull
@NotEmpty
@Schema(description = "array of PoC-NAT results", required = true)
private List<@Valid PocNatResult> testResults;

/**
* The labId of the uploader.
*/
@Schema(description = "The id that identifies a lab. Every lab can choose its own labid, "
+ "but it must be unique over all labs, should be generated once via cryptographic hash function",
required = true, maxLength = 64)
@JsonInclude(JsonInclude.Include.NON_NULL)
private String labId;
}
7 changes: 7 additions & 0 deletions src/main/java/app/coronawarn/testresult/model/TestType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package app.coronawarn.testresult.model;

public enum TestType {
PCR,
QUICKTEST,
POCNAT
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
import app.coronawarn.testresult.TestResultRepository;
import app.coronawarn.testresult.entity.TestResultEntity;
import app.coronawarn.testresult.exception.TestResultException;
import app.coronawarn.testresult.model.PocNatResult;
import app.coronawarn.testresult.model.QuickTestResult;
import app.coronawarn.testresult.model.TestResult;
import app.coronawarn.testresult.model.TestType;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
Expand Down Expand Up @@ -97,7 +99,7 @@ public TestResult createOrUpdate(final TestResult result) {
LocalDateTime sc = LocalDateTime.now();
if (result.getSc() != null) {
log.warn("Set Sc for Lab {}", result.getLabId());
sc = LocalDateTime.ofEpochSecond(result.getSc(),0, ZoneOffset.UTC);
sc = LocalDateTime.ofEpochSecond(result.getSc(), 0, ZoneOffset.UTC);
}
entity.setResult(result.getResult())
.setResultDate(sc);
Expand All @@ -118,23 +120,26 @@ public TestResult createOrUpdate(final TestResult result) {
* @param id the test result id
* @return the test result
*/
public TestResult getOrCreate(final String id, boolean quicktest, Long sc) {
public TestResult getOrCreate(final String id, TestType testtype, Long sc) {
try {
TestResultEntity entity = testResultRepository.findByResultId(id)
.orElseGet(() -> {
log.info("Get failed now creating test result in database.");
TestResultEntity resultEntity = new TestResultEntity();
if (quicktest) {
if (testtype == TestType.QUICKTEST) {
resultEntity.setResult(TestResultEntity.Result.QUICK_PENDING.ordinal());
resultEntity.setResultId(DigestUtils.sha256Hex(id));
} else if (testtype == TestType.POCNAT) {
resultEntity.setResult(TestResultEntity.Result.POCNAT_PENDING.ordinal());
resultEntity.setResultId(id);
} else {
resultEntity.setResult(TestResultEntity.Result.PENDING.ordinal());
resultEntity.setResultId(id);
}
if (sc == null) {
log.info("Set Sc during get or create");
resultEntity.setResultDate(LocalDateTime.now());
} else {
} else {
resultEntity.setResultDate(LocalDateTime.ofEpochSecond(sc, 0, ZoneOffset.UTC));
}
return testResultRepository.save(resultEntity);
Expand Down Expand Up @@ -162,4 +167,31 @@ public TestResult convertQuickTest(QuickTestResult quickTestResult, String labId
return testResult;
}

/**
* Converting a PoCNATResult to Testresult for saving.
*
* @param pocnatResult the Result to convert
* @return the converted test result
*/
public TestResult convertPocNat(PocNatResult pocnatResult, String labId) {
TestResult testResult = new TestResult();
testResult.setResult(pocnatResult.getResult());
testResult.setLabId(labId);
testResult.setId(DigestUtils.sha256Hex(pocnatResult.getId()));
testResult.setSc(pocnatResult.getSc());
return testResult;
}

/**
* Checks and converts PoC-NAT results to PCR results for REST reponse.
*
* @param result the Result to check and possibly convert
* @return either the original result or the converted result
*/
public Integer conversionCheck(Integer result) {
if (result >= 10 && result <= 14) {
result -= 10;
}
return result;
}
}
Loading

0 comments on commit 671d158

Please sign in to comment.