Skip to content

Commit 301fa3d

Browse files
authored
Merge pull request #516 from CMSgov/story-503
Story 503
2 parents 0874b3b + 400ac83 commit 301fa3d

File tree

10 files changed

+280
-29
lines changed

10 files changed

+280
-29
lines changed

rest-api/src/main/java/gov/cms/qpp/conversion/api/controllers/v1/CpcFileControllerV1.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.springframework.http.HttpStatus;
1414
import org.springframework.http.MediaType;
1515
import org.springframework.http.ResponseEntity;
16+
import org.springframework.web.bind.annotation.CrossOrigin;
1617
import org.springframework.web.bind.annotation.PathVariable;
1718
import org.springframework.web.bind.annotation.RequestMapping;
1819
import org.springframework.web.bind.annotation.RequestMethod;
@@ -26,6 +27,7 @@
2627
*/
2728
@RestController
2829
@RequestMapping("/cpc")
30+
@CrossOrigin
2931
public class CpcFileControllerV1 {
3032

3133
private static final Logger API_LOG = LoggerFactory.getLogger(Constants.API_LOG);
@@ -69,7 +71,7 @@ public ResponseEntity<List<UnprocessedCpcFileData>> getUnprocessedCpcPlusFiles()
6971
headers = {"Accept=" + Constants.V1_API_ACCEPT})
7072
public ResponseEntity<InputStreamResource> getFileById(@PathVariable("fileId") String fileId)
7173
throws IOException {
72-
API_LOG.info("CPC+ file request received");
74+
API_LOG.info("CPC+ file retrieval request received");
7375

7476
if (blockCpcPlusApi()) {
7577
API_LOG.info("CPC+ file request blocked by feature flag");
@@ -78,20 +80,44 @@ public ResponseEntity<InputStreamResource> getFileById(@PathVariable("fileId") S
7880

7981
InputStreamResource content = cpcFileService.getFileById(fileId);
8082

81-
API_LOG.info("CPC+ file request succeeded");
83+
API_LOG.info("CPC+ file retrieval request succeeded");
8284

8385
HttpHeaders httpHeaders = new HttpHeaders();
8486
httpHeaders.setContentType(MediaType.APPLICATION_XML);
8587

8688
return new ResponseEntity<>(content, httpHeaders, HttpStatus.OK);
8789
}
8890

91+
/**
92+
* Updates a file's status to processed in the database
93+
*
94+
* @param fileId Identifier of the file needing to be updated
95+
* @return Message if the file was updated or not
96+
*/
97+
@RequestMapping(method = RequestMethod.PUT, value = "/file/{fileId}",
98+
headers = {"Accept=" + Constants.V1_API_ACCEPT} )
99+
public ResponseEntity<String> markFileProcessed(@PathVariable("fileId") String fileId) {
100+
if (blockCpcPlusApi()) {
101+
API_LOG.info("CPC+ unprocessed files request blocked by feature flag");
102+
return new ResponseEntity<>(null, null, HttpStatus.FORBIDDEN);
103+
}
104+
105+
API_LOG.info("CPC+ update file as processed request received");
106+
String message = cpcFileService.processFileById(fileId);
107+
API_LOG.info("CPC+ update file as processed request succeeded");
108+
HttpHeaders httpHeaders = new HttpHeaders();
109+
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
110+
111+
return new ResponseEntity<>(message, httpHeaders, HttpStatus.OK);
112+
}
113+
89114
/**
90115
* Checks whether the the CPC+ APIs should not be allowed to execute.
91116
*
92117
* @return Whether the CPC+ APIs should be blocked.
93118
*/
94119
private boolean blockCpcPlusApi() {
95120
return EnvironmentHelper.isPresent(Constants.NO_CPC_PLUS_API_ENV_VARIABLE);
121+
96122
}
97123
}

rest-api/src/main/java/gov/cms/qpp/conversion/api/controllers/v1/ExceptionHandlerControllerV1.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package gov.cms.qpp.conversion.api.controllers.v1;
22

3+
import gov.cms.qpp.conversion.api.exceptions.InvalidFileTypeException;
34
import gov.cms.qpp.conversion.api.exceptions.NoFileInDatabaseException;
45
import gov.cms.qpp.conversion.api.model.Constants;
56
import gov.cms.qpp.conversion.api.services.AuditService;
@@ -60,7 +61,7 @@ ResponseEntity<AllErrors> handleQppValidationException(QppValidationException ex
6061

6162
/**
6263
* "Catch" the {@link NoFileInDatabaseException}.
63-
* Return the {@link AllErrors} with an HTTP status 422.
64+
* Return the {@link AllErrors} with an HTTP status 404.
6465
*
6566
* @param exception The NoFileInDatabaseException that was "caught".
6667
* @return The NoFileInDatabaseException message
@@ -74,6 +75,23 @@ ResponseEntity<String> handleFileNotFoundException(NoFileInDatabaseException exc
7475

7576
return new ResponseEntity<>(exception.getMessage(), httpHeaders, HttpStatus.NOT_FOUND);
7677
}
78+
79+
/**
80+
* "Catch" the {@link InvalidFileTypeException}.
81+
* Return the {@link AllErrors} with an HTTP status 404.
82+
*
83+
* @param exception The InvalidFileTypeException that was "caught".
84+
* @return The InvalidFileTypeException message
85+
*/
86+
@ExceptionHandler(InvalidFileTypeException.class)
87+
@ResponseBody
88+
ResponseEntity<String> handleInvalidFileTypeException(InvalidFileTypeException exception) {
89+
API_LOG.error("A file type error occurred", exception);
90+
HttpHeaders httpHeaders = new HttpHeaders();
91+
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
92+
93+
return new ResponseEntity<>(exception.getMessage(), httpHeaders, HttpStatus.NOT_FOUND);
94+
}
7795

7896
private ResponseEntity<AllErrors> cope(TransformException exception) {
7997
HttpHeaders httpHeaders = new HttpHeaders();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package gov.cms.qpp.conversion.api.exceptions;
2+
3+
/**
4+
* Exception for handling an invalid file type
5+
*/
6+
public class InvalidFileTypeException extends RuntimeException {
7+
8+
/**
9+
* Constructor to call RuntimeException
10+
* @param message Error response
11+
*/
12+
public InvalidFileTypeException(String message) {
13+
super(message);
14+
}
15+
}

rest-api/src/main/java/gov/cms/qpp/conversion/api/services/CpcFileService.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@ public interface CpcFileService {
2525
* @throws IOException for invalid IOUtils usage
2626
*/
2727
InputStreamResource getFileById(String fileId) throws IOException;
28+
29+
/**
30+
* Marks a CPC File as processed by id
31+
*
32+
* @param fileId Identifier of the CPC+ file
33+
* @return Success or failure message
34+
*/
35+
String processFileById(String fileId);
2836
}

rest-api/src/main/java/gov/cms/qpp/conversion/api/services/CpcFileServiceImpl.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
package gov.cms.qpp.conversion.api.services;
22

3+
import gov.cms.qpp.conversion.api.exceptions.InvalidFileTypeException;
34
import gov.cms.qpp.conversion.api.exceptions.NoFileInDatabaseException;
45
import gov.cms.qpp.conversion.api.model.Metadata;
56
import gov.cms.qpp.conversion.api.model.UnprocessedCpcFileData;
7+
import java.util.List;
8+
import java.util.concurrent.CompletableFuture;
9+
import java.util.stream.Collectors;
610
import org.springframework.beans.factory.annotation.Autowired;
711
import org.springframework.core.io.InputStreamResource;
812
import org.springframework.stereotype.Service;
913

10-
import java.util.List;
11-
import java.util.stream.Collectors;
12-
1314
/**
1415
* Service for handling Cpc File meta data
1516
*/
1617
@Service
1718
public class CpcFileServiceImpl implements CpcFileService {
1819

1920
public static final String FILE_NOT_FOUND = "File not found!";
21+
protected static final String INVALID_FILE = "The file was not a CPC+ file.";
22+
protected static final String FILE_FOUND = "The file was found and will be updated as processed.";
2023

2124
@Autowired
2225
private DbService dbService;
@@ -44,13 +47,35 @@ public List<UnprocessedCpcFileData> getUnprocessedCpcPlusFiles() {
4447
*/
4548
public InputStreamResource getFileById(String fileId) {
4649
Metadata metadata = dbService.getMetadataById(fileId);
47-
if (metadata != null && metadata.getCpc() != null && !metadata.getCpcProcessed()) {
50+
if (isAnUnprocessedCpcFile(metadata)) {
4851
return new InputStreamResource(storageService.getFileByLocationId(metadata.getSubmissionLocator()));
4952
} else {
5053
throw new NoFileInDatabaseException(FILE_NOT_FOUND);
5154
}
5255
}
5356

57+
/**
58+
* Process to ensure the file is an unprocessed cpc+ file and marks the file as processed
59+
*
60+
* @param fileId Identifier of the CPC+ file
61+
* @return Success or failure message.
62+
*/
63+
public String processFileById(String fileId) {
64+
Metadata metadata = dbService.getMetadataById(fileId);
65+
if (metadata == null) {
66+
throw new NoFileInDatabaseException(FILE_NOT_FOUND);
67+
} else if (metadata.getCpc() == null) {
68+
throw new InvalidFileTypeException(INVALID_FILE);
69+
} else if (metadata.getCpcProcessed()) {
70+
return FILE_FOUND;
71+
} else {
72+
metadata.setCpcProcessed(true);
73+
CompletableFuture<Metadata> metadataFuture = dbService.write(metadata);
74+
metadataFuture.join();
75+
return FILE_FOUND;
76+
}
77+
}
78+
5479
/**
5580
* Service to transform a {@link Metadata} list into the {@link UnprocessedCpcFileData}
5681
*
@@ -60,4 +85,14 @@ public InputStreamResource getFileById(String fileId) {
6085
private List<UnprocessedCpcFileData> transformMetaDataToUnprocessedCpcFileData(List<Metadata> metadataList) {
6186
return metadataList.stream().map(UnprocessedCpcFileData::new).collect(Collectors.toList());
6287
}
88+
89+
/**
90+
* Determines if the file is unprocessed and is CPC+
91+
*
92+
* @param metadata Data to be determined valid or invalid
93+
* @return result of the check
94+
*/
95+
private boolean isAnUnprocessedCpcFile(Metadata metadata) {
96+
return metadata != null && metadata.getCpc() != null && !metadata.getCpcProcessed();
97+
}
6398
}

rest-api/src/test/java/gov/cms/qpp/conversion/api/controllers/v1/CpcFileControllerV1Test.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import static com.google.common.truth.Truth.assertThat;
2727
import static org.mockito.ArgumentMatchers.anyString;
28+
import static org.mockito.Mockito.times;
2829
import static org.mockito.Mockito.verify;
2930
import static org.mockito.Mockito.when;
3031

@@ -71,6 +72,28 @@ void testGetFileById() throws IOException {
7172
.isEqualTo("1234");
7273
}
7374

75+
@Test
76+
void testMarkFileAsProcessedReturnsSuccess() {
77+
when(cpcFileService.processFileById(anyString())).thenReturn("success!");
78+
79+
ResponseEntity<String> response = cpcFileControllerV1.markFileProcessed("meep");
80+
81+
verify(cpcFileService, times(1)).processFileById("meep");
82+
83+
assertThat(response.getBody()).isEqualTo("success!");
84+
}
85+
86+
@Test
87+
void testMarkFileAsProcessedHttpStatusOk() {
88+
when(cpcFileService.processFileById(anyString())).thenReturn("success!");
89+
90+
ResponseEntity<String> response = cpcFileControllerV1.markFileProcessed("meep");
91+
92+
verify(cpcFileService, times(1)).processFileById("meep");
93+
94+
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
95+
}
96+
7497
@Test
7598
void testEndpoint1WithFeatureFlagDisabled() {
7699
System.setProperty(Constants.NO_CPC_PLUS_API_ENV_VARIABLE, "trueOrWhatever");
@@ -91,6 +114,16 @@ void testEndpoint2WithFeatureFlagDisabled() throws IOException {
91114
assertThat(cpcResponse.getBody()).isNull();
92115
}
93116

117+
@Test
118+
void testEndpoint3WithFeatureFlagDisabled() throws IOException {
119+
System.setProperty(Constants.NO_CPC_PLUS_API_ENV_VARIABLE, "trueOrWhatever");
120+
121+
ResponseEntity<String> cpcResponse = cpcFileControllerV1.markFileProcessed("meep");
122+
123+
assertThat(cpcResponse.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
124+
assertThat(cpcResponse.getBody()).isNull();
125+
}
126+
94127
List<UnprocessedCpcFileData> createMockedUnprocessedDataList() {
95128
Metadata metadata = new Metadata();
96129
metadata.setSubmissionLocator("Test");

rest-api/src/test/java/gov/cms/qpp/conversion/api/controllers/v1/ExceptionHandlerControllerV1Test.java

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package gov.cms.qpp.conversion.api.controllers.v1;
22

3-
import static com.google.common.truth.Truth.assertThat;
4-
import static com.google.common.truth.Truth.assertWithMessage;
5-
import static org.mockito.ArgumentMatchers.any;
6-
import static org.powermock.api.mockito.PowerMockito.when;
7-
3+
import gov.cms.qpp.conversion.Converter;
4+
import gov.cms.qpp.conversion.PathSource;
5+
import gov.cms.qpp.conversion.api.exceptions.InvalidFileTypeException;
6+
import gov.cms.qpp.conversion.api.exceptions.NoFileInDatabaseException;
7+
import gov.cms.qpp.conversion.api.services.AuditService;
8+
import gov.cms.qpp.conversion.api.services.CpcFileServiceImpl;
9+
import gov.cms.qpp.conversion.model.error.AllErrors;
10+
import gov.cms.qpp.conversion.model.error.QppValidationException;
11+
import gov.cms.qpp.conversion.model.error.TransformException;
12+
import gov.cms.qpp.test.MockitoExtension;
813
import java.nio.file.Path;
914
import java.nio.file.Paths;
1015
import java.util.concurrent.CompletableFuture;
11-
1216
import org.junit.jupiter.api.BeforeAll;
1317
import org.junit.jupiter.api.BeforeEach;
1418
import org.junit.jupiter.api.Test;
@@ -19,15 +23,10 @@
1923
import org.springframework.http.MediaType;
2024
import org.springframework.http.ResponseEntity;
2125

22-
import gov.cms.qpp.conversion.Converter;
23-
import gov.cms.qpp.conversion.PathSource;
24-
import gov.cms.qpp.conversion.api.exceptions.NoFileInDatabaseException;
25-
import gov.cms.qpp.conversion.api.services.AuditService;
26-
import gov.cms.qpp.conversion.api.services.CpcFileServiceImpl;
27-
import gov.cms.qpp.conversion.model.error.AllErrors;
28-
import gov.cms.qpp.conversion.model.error.QppValidationException;
29-
import gov.cms.qpp.conversion.model.error.TransformException;
30-
import gov.cms.qpp.test.MockitoExtension;
26+
import static com.google.common.truth.Truth.assertThat;
27+
import static com.google.common.truth.Truth.assertWithMessage;
28+
import static org.mockito.ArgumentMatchers.any;
29+
import static org.powermock.api.mockito.PowerMockito.when;
3130

3231
@ExtendWith(MockitoExtension.class)
3332
class ExceptionHandlerControllerV1Test {
@@ -149,4 +148,36 @@ void testFileNotFoundExceptionBody() {
149148
ResponseEntity<String> responseEntity = objectUnderTest.handleFileNotFoundException(exception);
150149
assertThat(responseEntity.getBody()).isEqualTo(CpcFileServiceImpl.FILE_NOT_FOUND);
151150
}
151+
152+
@Test
153+
void testInvalidFileTypeExceptionStatusCode() {
154+
InvalidFileTypeException exception =
155+
new InvalidFileTypeException(CpcFileServiceImpl.FILE_NOT_FOUND);
156+
157+
ResponseEntity<String> responseEntity = objectUnderTest.handleInvalidFileTypeException(exception);
158+
159+
assertWithMessage("The response entity's status code must be 422.")
160+
.that(responseEntity.getStatusCode())
161+
.isEquivalentAccordingToCompareTo(HttpStatus.NOT_FOUND);
162+
}
163+
164+
@Test
165+
void testInvalidFileTypeExceptionHeaderContentType() {
166+
InvalidFileTypeException exception =
167+
new InvalidFileTypeException(CpcFileServiceImpl.FILE_NOT_FOUND);
168+
169+
ResponseEntity<String> responseEntity = objectUnderTest.handleInvalidFileTypeException(exception);
170+
171+
assertThat(responseEntity.getHeaders().getContentType())
172+
.isEquivalentAccordingToCompareTo(MediaType.TEXT_PLAIN);
173+
}
174+
175+
@Test
176+
void testInvalidFileTypeExceptionBody() {
177+
InvalidFileTypeException exception =
178+
new InvalidFileTypeException(CpcFileServiceImpl.FILE_NOT_FOUND);
179+
180+
ResponseEntity<String> responseEntity = objectUnderTest.handleInvalidFileTypeException(exception);
181+
assertThat(responseEntity.getBody()).isEqualTo(CpcFileServiceImpl.FILE_NOT_FOUND);
182+
}
152183
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package gov.cms.qpp.conversion.api.exceptions;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static com.google.common.truth.Truth.assertThat;
6+
7+
class InvalidFileTypeExceptionTest {
8+
9+
@Test
10+
void testConstructor() {
11+
InvalidFileTypeException invalidFileTypeException = new InvalidFileTypeException("test");
12+
13+
14+
assertThat(invalidFileTypeException).hasMessageThat().isEqualTo("test");
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package gov.cms.qpp.conversion.api.exceptions;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static com.google.common.truth.Truth.assertThat;
6+
7+
class NoFileInDatabaseExceptionTest {
8+
9+
@Test
10+
void testConstructor() {
11+
NoFileInDatabaseException noFileInDatabaseException = new NoFileInDatabaseException("test");
12+
13+
14+
assertThat(noFileInDatabaseException).hasMessageThat().isEqualTo("test");
15+
}
16+
}

0 commit comments

Comments
 (0)