Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop/alex grad2 2410 #119

Merged
merged 4 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</parent>
<groupId>ca.bc.gov</groupId>
<artifactId>educ-grad-business-api</artifactId>
<version>1.8.25</version>
<version>1.8.26</version>
<name>educ-grad-business-api</name>
<description>GRAD Business API for external clients</description>

Expand Down Expand Up @@ -170,6 +170,11 @@
<artifactId>pdfbox</artifactId>
<version>2.0.26</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220320</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public WebClient webClient() {
return WebClient.builder().exchangeStrategies(ExchangeStrategies.builder()
.codecs(configurer -> configurer
.defaultCodecs()
.maxInMemorySize(20 * 1024 * 1024))
.maxInMemorySize(100 * 1024 * 1024))
.build()).build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ca.bc.gov.educ.api.gradbusiness.config;

import ca.bc.gov.educ.api.gradbusiness.model.dto.ResponseObjCache;
import ca.bc.gov.educ.api.gradbusiness.util.EducGraduationApiConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TokenUtilsConfig {

EducGraduationApiConstants constants;

@Autowired
public TokenUtilsConfig(EducGraduationApiConstants constants) {
this.constants = constants;
}

@Bean
public ResponseObjCache createResponseObjCache() {
return new ResponseObjCache(constants.getTokenExpiryOffset());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ca.bc.gov.educ.api.gradbusiness.model.dto;

import lombok.Data;

@Data
public class ResponseObj {

private String access_token;
private String refresh_token;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ca.bc.gov.educ.api.gradbusiness.model.dto;

import org.json.JSONObject;

import java.util.Base64;

public class ResponseObjCache {

private long tokenExpiry = 0;
private ResponseObj responseObj;

// tokenExpiry-[seconds] provides a slight offset, if token WILL expire in
// [seconds], obtain a new one
private int offset;

public ResponseObjCache(int offset) {
this.offset = offset;
}

public ResponseObj getResponseObj() {
return responseObj;
}

public void setResponseObj(ResponseObj responseObj) {
this.setTokenExpiry(responseObj);
this.responseObj = responseObj;
}

public boolean isExpired(){
// tokenExpiry-[seconds] provides a slight offset, if token WILL expire in
// 10 seconds, obtain a new one
return (responseObj == null) || (tokenExpiry-offset) < (System.currentTimeMillis() / 1000);
}

private void setTokenExpiry(ResponseObj responseObj){
String[] parts = responseObj.getAccess_token().split("\\.");
JSONObject payload = new JSONObject(new String(Base64.getUrlDecoder().decode(parts[1])));
this.tokenExpiry = payload.getLong("exp");
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import ca.bc.gov.educ.api.gradbusiness.util.EducGradBusinessApiConstants;
import ca.bc.gov.educ.api.gradbusiness.util.EducGradBusinessUtil;
import ca.bc.gov.educ.api.gradbusiness.util.EducGraduationApiConstants;
import ca.bc.gov.educ.api.gradbusiness.util.TokenUtils;
import io.github.resilience4j.retry.annotation.Retry;
import jakarta.transaction.Transactional;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -23,6 +24,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
* The type Grad business service.
Expand All @@ -41,26 +43,32 @@ public class GradBusinessService {
*/
final WebClient webClient;

/**
* Utility service to obtain access token
* */
final TokenUtils tokenUtils;

/**
* The Educ grad student api constants.
*/
@Autowired
EducGradBusinessApiConstants educGradStudentApiConstants;
final EducGradBusinessApiConstants educGradStudentApiConstants;

/**
* The Educ graduation api constants.
*/
@Autowired
EducGraduationApiConstants educGraduationApiConstants;
final EducGraduationApiConstants educGraduationApiConstants;

/**
* Instantiates a new Grad business service.
*
* @param webClient the web client
*/
@Autowired
public GradBusinessService(WebClient webClient) {
public GradBusinessService(WebClient webClient, TokenUtils tokenUtils, EducGradBusinessApiConstants educGradStudentApiConstants, EducGraduationApiConstants educGraduationApiConstants) {
this.webClient = webClient;
this.tokenUtils = tokenUtils;
this.educGradStudentApiConstants = educGradStudentApiConstants;
this.educGraduationApiConstants = educGraduationApiConstants;
}

/**
Expand Down Expand Up @@ -166,48 +174,6 @@ public ResponseEntity<byte[]> getStudentDemographicsByPen(String pen, String acc
}
}

/**
* Gets internal server error response.
*
* @param t the t
* @return the internal server error response
*/
protected ResponseEntity<byte[]> getInternalServerErrorResponse(Throwable t) {
ResponseEntity<byte[]> result;

Throwable tmp = t;
String message;
if (tmp.getCause() != null) {
tmp = tmp.getCause();
message = tmp.getMessage();
} else {
message = tmp.getMessage();
}
if(message == null) {
message = tmp.getClass().getName();
}

result = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(message.getBytes());
return result;
}

private ResponseEntity<byte[]> handleBinaryResponse(byte[] resultBinary, String reportFile, MediaType contentType) {
ResponseEntity<byte[]> response;

if(resultBinary.length > 0) {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "inline; filename=" + reportFile);
response = ResponseEntity
.ok()
.headers(headers)
.contentType(contentType)
.body(resultBinary);
} else {
response = ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
return response;
}

public ResponseEntity<byte[]> getSchoolReportPDFByMincode(String mincode, String type, String accessToken) {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("PST"), Locale.CANADA);
int year = cal.get(Calendar.YEAR);
Expand All @@ -229,29 +195,21 @@ public ResponseEntity<byte[]> getSchoolReportPDFByMincode(String mincode, String
}

public ResponseEntity<byte[]> getAmalgamatedSchoolReportPDFByMincode(String mincode, String type, String accessToken) {
logger.debug("******** Retrieve List of Students for Amalgamated School Report ******");
List<UUID> studentList = webClient.get().uri(String.format(educGradStudentApiConstants.getStudentsForAmalgamatedReport(), mincode, type)).headers(h -> h.setBearerAuth(accessToken)).retrieve().bodyToMono(new ParameterizedTypeReference<List<UUID>>() {
}).block();
logger.debug("******** Fetched Student List ******");
List<InputStream> locations = new ArrayList<>();
if (studentList != null && !studentList.isEmpty()) {
for (UUID studentID : studentList) {
InputStreamResource result = webClient.get().uri(String.format(educGraduationApiConstants.getStudentCredentialByType(), studentID, "ACHV")).headers(h -> h.setBearerAuth(accessToken)).retrieve().bodyToMono(InputStreamResource.class).block();
if (result != null) {
try {
locations.add(result.getInputStream());
} catch (IOException e) {
logger.debug("Error {}",e.getLocalizedMessage());
}
}
}
logger.debug("******** Fetched Achievement Reports ******");
logger.debug("******** Fetched {} students ******", studentList.size());
getStudentAchievementReports(studentList, locations);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("PST"), Locale.CANADA);
int year = cal.get(Calendar.YEAR);
String month = "00";
String fileName = EducGradBusinessUtil.getFileNameSchoolReports(mincode, year, month, type);
try {
logger.debug("******** Merged Documents ******");
logger.debug("******** Merging Documents Started ******");
byte[] res = EducGradBusinessUtil.mergeDocuments(locations);
logger.debug("******** Merged {} Documents ******", locations.size());
HttpHeaders headers = new HttpHeaders();
headers.put(HttpHeaders.AUTHORIZATION, Collections.singletonList(BEARER + accessToken));
headers.put(HttpHeaders.ACCEPT, Collections.singletonList(APPLICATION_PDF));
Expand Down Expand Up @@ -284,7 +242,6 @@ public ResponseEntity<byte[]> getStudentCredentialPDFByType(String pen, String t
}
}


@Transactional
public ResponseEntity<byte[]> getStudentTranscriptPDFByType(String pen, String type, String interim, String accessToken) {
try {
Expand Down Expand Up @@ -316,4 +273,73 @@ public ResponseEntity<byte[]> getStudentTranscriptPDFByType(String pen, String t
return getInternalServerErrorResponse(e);
}
}

private void getStudentAchievementReports(List<UUID> studentList, List<InputStream> locations) {
logger.debug("******** Getting Student Achievement Reports ******");
List<CompletableFuture<InputStream>> futures = studentList.stream()
.map(studentGuid -> CompletableFuture.supplyAsync(() -> getStudentAchievementReport(studentGuid)))
.toList();
CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
CompletableFuture<List<InputStream>> result = allFutures.thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.toList());
locations.addAll(result.join());
logger.debug("******** Fetched All Student Achievement Reports ******");
}

private InputStream getStudentAchievementReport(UUID studentGuid) {
String accessTokenNext = tokenUtils.getAccessToken();
InputStreamResource result = webClient.get().uri(String.format(educGraduationApiConstants.getStudentCredentialByType(), studentGuid, "ACHV")).headers(h -> h.setBearerAuth(accessTokenNext)).retrieve().bodyToMono(InputStreamResource.class).block();
if (result != null) {
try {
logger.debug("******** Fetched Achievement Report for {} ******", studentGuid);
return result.getInputStream();
} catch (IOException e) {
logger.debug("Error extracting report binary from stream: {}", e.getLocalizedMessage());
}
}
return null;
}

/**
* Gets internal server error response.
*
* @param t the t
* @return the internal server error response
*/
protected ResponseEntity<byte[]> getInternalServerErrorResponse(Throwable t) {
ResponseEntity<byte[]> result;

Throwable tmp = t;
String message;
if (tmp.getCause() != null) {
tmp = tmp.getCause();
message = tmp.getMessage();
} else {
message = tmp.getMessage();
}
if(message == null) {
message = tmp.getClass().getName();
}

result = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(message.getBytes());
return result;
}

private ResponseEntity<byte[]> handleBinaryResponse(byte[] resultBinary, String reportFile, MediaType contentType) {
ResponseEntity<byte[]> response;

if(resultBinary.length > 0) {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "inline; filename=" + reportFile);
response = ResponseEntity
.ok()
.headers(headers)
.contentType(contentType)
.body(resultBinary);
} else {
response = ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
return response;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,15 @@ public class EducGraduationApiConstants {
@Value("${endpoint.grad-report-api.transcript-by-request.url}")
private String studentTranscriptReportByRequest;


@Value("${authorization.user}")
private String userName;

@Value("${authorization.password}")
private String password;

@Value("${endpoint.keycloak.getToken}")
private String tokenUrl;

@Value("${authorization.token-expiry-offset}")
private int tokenExpiryOffset;
}
Loading
Loading