diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/config/EducGradBusinessApiConfig.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/config/EducGradBusinessApiConfig.java index bac2109..6391f55 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/config/EducGradBusinessApiConfig.java +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/config/EducGradBusinessApiConfig.java @@ -5,14 +5,12 @@ import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.context.annotation.Profile; import org.springframework.web.client.RestTemplate; -import org.springframework.web.reactive.function.client.ExchangeStrategies; -import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import reactor.netty.http.client.HttpClient; +@Profile("!test") @Configuration public class EducGradBusinessApiConfig implements WebMvcConfigurer { @@ -30,21 +28,7 @@ public void addInterceptors(InterceptorRegistry registry) { @Bean public ModelMapper modelMapper() { - ModelMapper modelMapper = new ModelMapper(); - return modelMapper; - } - - @Bean - public WebClient webClient() { - HttpClient client = HttpClient.create(); - client.warmup().block(); - return WebClient.builder() - .clientConnector(new ReactorClientHttpConnector(HttpClient.newConnection().compress(true))) - .exchangeStrategies(ExchangeStrategies.builder() - .codecs(configurer -> configurer - .defaultCodecs() - .maxInMemorySize(300 * 1024 * 1024)) - .build()).build(); + return new ModelMapper(); } @Bean diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/config/RestWebClient.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/config/RestWebClient.java new file mode 100644 index 0000000..245e8af --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/config/RestWebClient.java @@ -0,0 +1,87 @@ +package ca.bc.gov.educ.api.gradbusiness.config; + +import ca.bc.gov.educ.api.gradbusiness.util.EducGradBusinessApiConstants; +import ca.bc.gov.educ.api.gradbusiness.util.LogHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; +import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.ExchangeStrategies; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +@Profile("!test") +public class RestWebClient { + + EducGradBusinessApiConstants constants; + + LogHelper logHelper; + + @Autowired + public RestWebClient(EducGradBusinessApiConstants constants, LogHelper logHelper) { + this.constants = constants; + this.logHelper = logHelper; + } + + @Bean("gradBusinessClient") + public WebClient getGraduationClientWebClient(OAuth2AuthorizedClientManager authorizedClientManager) { + ServletOAuth2AuthorizedClientExchangeFilterFunction filter = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); + filter.setDefaultClientRegistrationId("grad-business-client"); + return WebClient.builder() + .exchangeStrategies(ExchangeStrategies + .builder() + .codecs(codecs -> codecs + .defaultCodecs() + .maxInMemorySize(50 * 1024 * 1024)) + .build()) + .apply(filter.oauth2Configuration()) + .filter(this.log()) + .build(); + } + @Bean + public OAuth2AuthorizedClientManager authorizedClientManager( + ClientRegistrationRepository clientRegistrationRepository, + OAuth2AuthorizedClientRepository authorizedClientRepository) { + OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() + .clientCredentials() + .build(); + DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( + clientRegistrationRepository, authorizedClientRepository); + authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); + return authorizedClientManager; + } + + /** + * Old web client. You can use a @Qualifier('default') to summon it. + */ + @Bean + public WebClient webClient() { + return WebClient.builder().exchangeStrategies(ExchangeStrategies.builder() + .codecs(configurer -> configurer + .defaultCodecs() + .maxInMemorySize(300 * 1024 * 1024)) // 300MB + .build()) + .filter(this.log()) + .build(); + } + + private ExchangeFilterFunction log() { + return (clientRequest, next) -> next + .exchange(clientRequest) + .doOnNext((clientResponse -> LogHelper.logClientHttpReqResponseDetails( + clientRequest.method(), + clientRequest.url().toString(), + clientResponse.statusCode().value(), + clientRequest.headers().get(EducGradBusinessApiConstants.CORRELATION_ID), + constants.isSplunkLogHelperEnabled()) + )); + } +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/controller/GradBusinessController.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/controller/GradBusinessController.java index 0be003d..1839b94 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/controller/GradBusinessController.java +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/controller/GradBusinessController.java @@ -124,8 +124,8 @@ public ResponseEntity certificateReportDataFromGraduation(@RequestBody S @PreAuthorize("hasAuthority('SCOPE_GET_GRADUATION_DATA')") @Operation(summary = "Get School Report pdf from graduation by mincode and report type", description = "Get School Report pdf from graduation by mincode and report type", tags = { "Graduation Data" }) @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")}) - public ResponseEntity schoolReportByMincode(@PathVariable String mincode,@RequestParam(name = "type") String type, @RequestHeader(name="Authorization") String accessToken) { - return gradBusinessService.getSchoolReportPDFByMincode(mincode, type, accessToken.replace(BEARER, "")); + public ResponseEntity schoolReportByMincode(@PathVariable String mincode,@RequestParam(name = "type") String type) { + return gradBusinessService.getSchoolReportPDFByMincode(mincode, type); } @GetMapping(EducGraduationApiConstants.STUDENT_CREDENTIAL_PDF) diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/exception/ServiceException.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/exception/ServiceException.java index 29f3f31..01d7e57 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/exception/ServiceException.java +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/exception/ServiceException.java @@ -12,4 +12,8 @@ public ServiceException(String message, int value) { this.statusCode = value; } + public ServiceException(String s, int value, Exception e) { + super(s, e); + this.statusCode = value; + } } diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/model/dto/School.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/model/dto/School.java new file mode 100644 index 0000000..c5546f1 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/model/dto/School.java @@ -0,0 +1,36 @@ +package ca.bc.gov.educ.api.gradbusiness.model.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.*; +import org.springframework.stereotype.Component; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode +@Component("instituteSchool") +@JsonIgnoreProperties(ignoreUnknown = true) +public class School { + + private String schoolId; + private String districtId; + private String mincode; + private String independentAuthorityId; + private String schoolNumber; + private String faxNumber; + private String phoneNumber; + private String email; + private String website; + private String displayName; + private String displayNameNoSpecialChars; + private String schoolReportingRequirementCode; + private String schoolOrganizationCode; + private String schoolCategoryCode; + private String facilityTypeCode; + private String openedDate; + private String closedDate; + private boolean canIssueTranscripts; + private boolean canIssueCertificates; + +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/service/GradBusinessService.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/service/GradBusinessService.java index 4382f96..48a8f69 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/service/GradBusinessService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/service/GradBusinessService.java @@ -1,11 +1,9 @@ package ca.bc.gov.educ.api.gradbusiness.service; import ca.bc.gov.educ.api.gradbusiness.exception.ServiceException; +import ca.bc.gov.educ.api.gradbusiness.model.dto.School; import ca.bc.gov.educ.api.gradbusiness.model.dto.Student; -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 ca.bc.gov.educ.api.gradbusiness.util.*; import io.github.resilience4j.retry.annotation.Retry; import jakarta.transaction.Transactional; import org.apache.commons.collections4.ListUtils; @@ -63,17 +61,24 @@ public class GradBusinessService { */ final EducGraduationApiConstants educGraduationApiConstants; + final SchoolService schoolService; + final RESTService restService; + final JsonTransformer jsonTransformer; + /** * Instantiates a new Grad business service. * * @param webClient the web client */ @Autowired - public GradBusinessService(WebClient webClient, TokenUtils tokenUtils, EducGradBusinessApiConstants educGradStudentApiConstants, EducGraduationApiConstants educGraduationApiConstants) { + public GradBusinessService(WebClient webClient, TokenUtils tokenUtils, EducGradBusinessApiConstants educGradStudentApiConstants, EducGraduationApiConstants educGraduationApiConstants, SchoolService schoolService, RESTService restService, JsonTransformer jsonTransformer) { this.webClient = webClient; this.tokenUtils = tokenUtils; this.educGradStudentApiConstants = educGradStudentApiConstants; this.educGraduationApiConstants = educGraduationApiConstants; + this.schoolService = schoolService; + this.restService = restService; + this.jsonTransformer = jsonTransformer; } /** @@ -179,22 +184,26 @@ public ResponseEntity getStudentDemographicsByPen(String pen, String acc } } - public ResponseEntity getSchoolReportPDFByMincode(String mincode, String type, String accessToken) { + public ResponseEntity getSchoolReportPDFByMincode(String mincode, String type) { Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("PST"), Locale.CANADA); int year = cal.get(Calendar.YEAR); String month = String.format("%02d", cal.get(Calendar.MONTH) + 1); try { - HttpHeaders headers = new HttpHeaders(); - headers.put(HttpHeaders.AUTHORIZATION, Collections.singletonList(BEARER + accessToken)); - headers.put(HttpHeaders.ACCEPT, Collections.singletonList(APPLICATION_PDF)); - headers.put(HttpHeaders.CONTENT_TYPE, Collections.singletonList(APPLICATION_PDF)); - InputStreamResource result = webClient.get().uri(String.format(educGraduationApiConstants.getSchoolReportByMincode(), mincode,type)).headers(h -> h.setBearerAuth(accessToken)).retrieve().bodyToMono(InputStreamResource.class).block(); - byte[] res = new byte[0]; - if(result != null) { - res = result.getInputStream().readAllBytes(); + List schoolDetails = schoolService.getSchoolDetails(mincode); + if (schoolDetails.isEmpty()) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } + String schoolOfRecordId = schoolDetails.get(0).getSchoolId(); + + var result = restService.get(String.format(educGraduationApiConstants.getSchoolReportBySchoolIdAndReportType(), schoolOfRecordId,type), InputStreamResource.class); + byte[] response = new byte[0]; + if (result != null) { + response = result.getInputStream().readAllBytes(); } - return handleBinaryResponse(res, EducGradBusinessUtil.getFileNameSchoolReports(mincode,year,month,type,MediaType.APPLICATION_PDF), MediaType.APPLICATION_PDF); + + return handleBinaryResponse(response, EducGradBusinessUtil.getFileNameSchoolReports(mincode,year,month,type,MediaType.APPLICATION_PDF), MediaType.APPLICATION_PDF); } catch (Exception e) { + logger.error("Error getting school report PDF by mincode: {}", e.getMessage()); return getInternalServerErrorResponse(e); } } diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/service/RESTService.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/service/RESTService.java new file mode 100644 index 0000000..7a69010 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/service/RESTService.java @@ -0,0 +1,87 @@ +package ca.bc.gov.educ.api.gradbusiness.service; + +import ca.bc.gov.educ.api.gradbusiness.exception.ServiceException; +import ca.bc.gov.educ.api.gradbusiness.util.EducGradBusinessApiConstants; +import ca.bc.gov.educ.api.gradbusiness.util.ThreadLocalStateUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; + +import java.time.Duration; + +@Service +public class RESTService { + private final WebClient graduationServiceWebClient; + + private static final String SERVER_ERROR = "5xx error."; + private static final String SERVICE_FAILED_ERROR = "Service failed to process after max retries."; + + @Autowired + public RESTService(@Qualifier("gradBusinessClient") WebClient graduationServiceWebClient) { + this.graduationServiceWebClient = graduationServiceWebClient; + } + + public T get(String url, Class clazz) { + T obj; + try { + obj = graduationServiceWebClient + .get() + .uri(url) + .headers(h -> h.set(EducGradBusinessApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID())) + .retrieve() + // if 5xx errors, throw Service error + .onStatus(HttpStatusCode::is5xxServerError, + clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, SERVER_ERROR), clientResponse.statusCode().value()))) + .bodyToMono(clazz) + // only does retry if initial error was 5xx as service may be temporarily down + // 4xx errors will always happen if 404, 401, 403 etc, so does not retry + .retryWhen(Retry.backoff(3, Duration.ofSeconds(2)) + .filter(ServiceException.class::isInstance) + .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> { + throw new ServiceException(getErrorMessage(url, SERVICE_FAILED_ERROR), HttpStatus.SERVICE_UNAVAILABLE.value()); + })) + .block(); + } catch (Exception e) { + // catches IOExceptions and the like + throw new ServiceException( + getErrorMessage(url, e.getLocalizedMessage()), + (e instanceof WebClientResponseException exception) ? exception.getStatusCode().value() : HttpStatus.SERVICE_UNAVAILABLE.value(), + e); + } + return obj; + } + + public T post(String url, Object body, Class clazz) { + T obj; + try { + obj = graduationServiceWebClient.post() + .uri(url) + .headers(h -> h.set(EducGradBusinessApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID())) + .body(BodyInserters.fromValue(body)) + .retrieve() + .onStatus(HttpStatusCode::is5xxServerError, + clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, SERVER_ERROR), clientResponse.statusCode().value()))) + .bodyToMono(clazz) + .retryWhen(Retry.backoff(3, Duration.ofSeconds(2)) + .filter(ServiceException.class::isInstance) + .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> { + throw new ServiceException(getErrorMessage(url, SERVICE_FAILED_ERROR), HttpStatus.SERVICE_UNAVAILABLE.value()); + })) + .block(); + } catch (Exception e) { + throw new ServiceException(getErrorMessage(url, e.getLocalizedMessage()), HttpStatus.SERVICE_UNAVAILABLE.value(), e); + } + return obj; + } + + private String getErrorMessage(String url, String errorMessage) { + return "Service failed to process at url: " + url + " due to: " + errorMessage; + } +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/service/SchoolService.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/service/SchoolService.java new file mode 100644 index 0000000..1d0325e --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/service/SchoolService.java @@ -0,0 +1,30 @@ +package ca.bc.gov.educ.api.gradbusiness.service; + +import ca.bc.gov.educ.api.gradbusiness.model.dto.School; +import ca.bc.gov.educ.api.gradbusiness.util.EducGradBusinessApiConstants; +import ca.bc.gov.educ.api.gradbusiness.util.JsonTransformer; +import com.fasterxml.jackson.core.type.TypeReference; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class SchoolService { + EducGradBusinessApiConstants educGraduationApiConstants; + RESTService restService; + + JsonTransformer jsonTransformer; + @Autowired + public SchoolService(EducGradBusinessApiConstants educGraduationApiConstants, RESTService restService, JsonTransformer jsonTransformer) { + this.educGraduationApiConstants = educGraduationApiConstants; + this.restService = restService; + this.jsonTransformer = jsonTransformer; + } + + public List getSchoolDetails(String mincode) { + var response = this.restService.get(String.format(educGraduationApiConstants.getSchoolDetails(),mincode), List.class); + return jsonTransformer.convertValue(response, new TypeReference<>() {}); + } +} + diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/EducGradBusinessApiConstants.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/EducGradBusinessApiConstants.java index d0464c4..6f710af 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/EducGradBusinessApiConstants.java +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/EducGradBusinessApiConstants.java @@ -103,6 +103,9 @@ public class EducGradBusinessApiConstants { @Value("${endpoint.grad-student-api.amalgamated-students.url}") private String studentsForAmalgamatedReport; + @Value("${endpoint.grad-trax-api.search-schools-by-min-code.url}") + private String schoolDetails; + // Splunk LogHelper Enabled @Value("${splunk.log-helper.enabled}") private boolean splunkLogHelperEnabled; diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/EducGraduationApiConstants.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/EducGraduationApiConstants.java index 703e4ec..51cb154 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/EducGraduationApiConstants.java +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/EducGraduationApiConstants.java @@ -45,8 +45,8 @@ public class EducGraduationApiConstants { @Value("${endpoint.grad-report-api.report-data-by-xml.url}") private String xmlTranscriptReportData; - @Value("${endpoint.grad-graduation-report-api.school-report-by-mincode.url}") - private String schoolReportByMincode; + @Value("${endpoint.grad-graduation-report-api.school-report-by-school-id-and-report-type.url}") + private String schoolReportBySchoolIdAndReportType; @Value("${endpoint.grad-graduation-report-api.student-credential-by-type.url}") private String studentCredentialByType; diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/JsonTransformer.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/JsonTransformer.java new file mode 100644 index 0000000..41142a8 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/JsonTransformer.java @@ -0,0 +1,103 @@ +package ca.bc.gov.educ.api.gradbusiness.util; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.io.InputStream; + +@Component +public class JsonTransformer implements Transformer { + + private static final Logger log = LoggerFactory.getLogger(JsonTransformer.class); + private static final String MARSHALLING_MSG = "Time taken for unmarshalling response from String to {} is {} ms"; + + final ObjectMapper objectMapper; + + @Autowired + public JsonTransformer(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public Object unmarshall(byte[] input, Class clazz) { + Object result = null; + long start = System.currentTimeMillis(); + try { + result = objectMapper.readValue(input, clazz); + } catch (IOException e) { + log.error(e.getLocalizedMessage(), e); + } + log.debug("Time taken for unmarshalling response from bytes to {} is {} ms", clazz.getName(), (System.currentTimeMillis() - start)); + return result; + } + + + @Override + public Object unmarshall(String input, Class clazz) { + Object result = null; + long start = System.currentTimeMillis(); + try { + result = objectMapper.readValue(input, clazz); + } catch (IOException e) { + log.error(e.getLocalizedMessage(), e); + } + log.debug(MARSHALLING_MSG, clazz.getName(), (System.currentTimeMillis() - start)); + return result; + } + + @Override + public Object unmarshall(String input, TypeReference valueTypeRef) { + Object result = null; + long start = System.currentTimeMillis(); + try { + result = objectMapper.readValue(input, valueTypeRef); + } catch (IOException e) { + log.error(e.getLocalizedMessage(), e); + } + log.debug(MARSHALLING_MSG, valueTypeRef.getType().getTypeName(), (System.currentTimeMillis() - start)); + return result; + } + + @Override + public Object unmarshall(InputStream input, Class clazz) { + Object result = null; + long start = System.currentTimeMillis(); + try { + result = objectMapper.readValue(input, clazz); + } catch (IOException e) { + log.error(e.getLocalizedMessage(), e); + } + log.debug("Time taken for unmarshalling response from stream to {} is {} ms", clazz.getName(), (System.currentTimeMillis() - start)); + return result; + } + + @Override + public String marshall(Object input) { + String result = null; + try { + result = objectMapper.writeValueAsString(input); + } catch (IOException e) { + log.error(e.getLocalizedMessage(), e); + } + return result; + } + + @Override + public String getAccept() { + return "application/json"; + } + + @Override + public String getContentType() { + return "application/json"; + } + + public T convertValue(Object fromValue, TypeReference toValueTypeRef) throws IllegalArgumentException { + return objectMapper.convertValue(fromValue, toValueTypeRef); + } +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/LogHelper.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/LogHelper.java index a004c2c..322df66 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/LogHelper.java +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/LogHelper.java @@ -10,12 +10,15 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; + import java.time.Instant; import java.util.HashMap; import java.util.List; import java.util.Map; @Slf4j +@Component public final class LogHelper { private static final ObjectMapper mapper = new ObjectMapper(); private static final String EXCEPTION = "Exception "; diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/ThreadLocalStateUtil.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/ThreadLocalStateUtil.java new file mode 100644 index 0000000..653cac7 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/ThreadLocalStateUtil.java @@ -0,0 +1,14 @@ +package ca.bc.gov.educ.api.gradbusiness.util; + +public class ThreadLocalStateUtil { + private static ThreadLocal transaction = new ThreadLocal<>(); + public static void setCorrelationID(String correlationID){ + transaction.set(correlationID); + } + public static String getCorrelationID() { + return transaction.get(); + } + public static void clear() { + transaction.remove(); + } +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/Transformer.java b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/Transformer.java new file mode 100644 index 0000000..39502c9 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/gradbusiness/util/Transformer.java @@ -0,0 +1,23 @@ +package ca.bc.gov.educ.api.gradbusiness.util; + +import com.fasterxml.jackson.core.type.TypeReference; + +import java.io.InputStream; + +public interface Transformer { + + public Object unmarshall(byte[] input, Class clazz); + + public Object unmarshall(String input, Class clazz); + + public Object unmarshall(InputStream input, Class clazz); + + public Object unmarshall(String input, TypeReference valueTypeRef); + + public String marshall(Object input); + + public String getAccept(); + + public String getContentType(); + +} diff --git a/api/src/main/resources/application.yaml b/api/src/main/resources/application.yaml index 1089cb5..070d82f 100644 --- a/api/src/main/resources/application.yaml +++ b/api/src/main/resources/application.yaml @@ -21,6 +21,16 @@ spring: jwt: issuer-uri: ${TOKEN_ISSUER_URL} jwk-set-uri: ${TOKEN_ISSUER_URL}/protocol/openid-connect/certs + client: + registration: + grad-business-client: + client-id: ${GRAD_BUSINESS_API_CLIENT_NAME} + client-secret: ${GRAD_BUSINESS_API_CLIENT_SECRET} + authorization-grant-type: client_credentials + provider: + batch-client: + issuer-uri: ${TOKEN_ISSUER_URL} + token-uri: ${TOKEN_ISSUER_URL}/protocol/openid-connect/token #Logging properties logging: @@ -146,10 +156,13 @@ endpoint: amalgamated-students: url: ${GRAD_STUDENT_API}api/v1/student/amalgamated/schoolreport/%s/type/%s grad-graduation-report-api: - school-report-by-mincode: - url: ${GRAD_GRADUATION_REPORT_API}api/v1/graduationreports/schoolreport?mincode=%s&reportType=%s + school-report-by-school-id-and-report-type: + url: ${GRAD_GRADUATION_REPORT_API}api/v2/graduationreports/schoolreport?schoolOfRecordId=%s&reportTypeCode=%s student-credential-by-type: url: ${GRAD_GRADUATION_REPORT_API}api/v1/graduationreports/business/studentcredential/%s/%s + grad-trax-api: + search-schools-by-min-code: + url: ${GRAD_TRAX_API}api/v2/trax/school/search?mincode=%s #Splunk LogHelper splunk: diff --git a/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/EducGradBusinessApiApplicationTests.java b/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/EducGradBusinessApiApplicationTests.java index 1a0f2ec..1100d01 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/EducGradBusinessApiApplicationTests.java +++ b/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/EducGradBusinessApiApplicationTests.java @@ -1,8 +1,11 @@ package ca.bc.gov.educ.api.gradbusiness; import ca.bc.gov.educ.api.gradbusiness.exception.ServiceException; +import ca.bc.gov.educ.api.gradbusiness.model.dto.School; import ca.bc.gov.educ.api.gradbusiness.model.dto.Student; import ca.bc.gov.educ.api.gradbusiness.service.GradBusinessService; +import ca.bc.gov.educ.api.gradbusiness.service.RESTService; +import ca.bc.gov.educ.api.gradbusiness.service.SchoolService; import ca.bc.gov.educ.api.gradbusiness.util.EducGradBusinessApiConstants; import ca.bc.gov.educ.api.gradbusiness.util.EducGraduationApiConstants; import ca.bc.gov.educ.api.gradbusiness.util.TokenUtils; @@ -10,7 +13,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; @@ -18,9 +20,9 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.io.InputStreamResource; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; @@ -30,18 +32,18 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.function.Consumer; -import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.any; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.openMocks; @FixMethodOrder(MethodSorters.NAME_ASCENDING) -@RunWith(SpringRunner.class) -@SpringBootTest +@SpringBootTest(classes = {EducGradBusinessApiApplication.class}) @ActiveProfiles("test") class EducGradBusinessApiApplicationTests { @@ -70,18 +72,17 @@ class EducGradBusinessApiApplicationTests { @Autowired private GradBusinessService gradBusinessService; + @MockBean + private SchoolService schoolService; + @MockBean + private RESTService restService; @BeforeEach public void setUp() { openMocks(this); } - @AfterEach - public void tearDown() { - - } - - @org.junit.jupiter.api.Test + @Test void testReportDataByPen() throws Exception { String studentGradData = readFile("json/gradstatus.json"); @@ -274,17 +275,14 @@ void testSchoolReportPDFByMincode() throws Exception { byte[] samplePdf = readBinaryFile("data/sample.pdf"); InputStreamResource pdf = new InputStreamResource(new ByteArrayInputStream(samplePdf)); - when(this.tokenUtils.getAccessToken()).thenReturn("accessToken"); - - when(this.webClient.get()).thenReturn(this.requestHeadersUriMock); - when(this.requestHeadersUriMock.uri(String.format(educGraduationApiConstants.getSchoolReportByMincode(),mincode,type))).thenReturn(this.requestHeadersMock); - when(this.requestHeadersMock.headers(any(Consumer.class))).thenReturn(this.requestHeadersMock); - when(this.requestHeadersMock.retrieve()).thenReturn(this.responseMock); - when(this.responseMock.bodyToMono(InputStreamResource.class)).thenReturn(Mono.just(pdf)); + var schoolList = List.of(School.builder().mincode(mincode).schoolId(String.valueOf(UUID.randomUUID())).build()); + when(schoolService.getSchoolDetails(anyString())).thenReturn(schoolList); + when(this.restService.get(any(String.class), any())).thenReturn(pdf); - ResponseEntity byteData = gradBusinessService.getSchoolReportPDFByMincode(mincode, type, "accessToken"); + ResponseEntity byteData = gradBusinessService.getSchoolReportPDFByMincode(mincode, type); assertNotNull(byteData); - assertTrue(byteData.getBody().length > 0); + assertEquals(HttpStatus.OK, byteData.getStatusCode()); + assertNotNull(byteData.getBody()); } @Test @@ -330,7 +328,7 @@ void testgetAmalgamatedSchoolReportPDFByMincode() throws Exception { } @Test - void testSchoolReportPDFByMincode_NotFound() throws Exception { + void testSchoolReportPDFByMincode_NotFound() { String mincode = "128385861"; String type = "NONGRADPRJ"; @@ -338,37 +336,25 @@ void testSchoolReportPDFByMincode_NotFound() throws Exception { byte[] samplePdf = new byte[0]; InputStreamResource pdf = new InputStreamResource(new ByteArrayInputStream(samplePdf)); - when(this.tokenUtils.getAccessToken()).thenReturn("accessToken"); + when(schoolService.getSchoolDetails(anyString())).thenReturn(Collections.emptyList()); + when(this.restService.get(any(String.class), any())).thenReturn(pdf); - when(this.webClient.get()).thenReturn(this.requestHeadersUriMock); - when(this.requestHeadersUriMock.uri(String.format(educGraduationApiConstants.getSchoolReportByMincode(),mincode,type))).thenReturn(this.requestHeadersMock); - when(this.requestHeadersMock.headers(any(Consumer.class))).thenReturn(this.requestHeadersMock); - when(this.requestHeadersMock.retrieve()).thenReturn(this.responseMock); - when(this.responseMock.bodyToMono(InputStreamResource.class)).thenReturn(Mono.just(pdf)); - - ResponseEntity byteData = gradBusinessService.getSchoolReportPDFByMincode(mincode, type, "accessToken"); + ResponseEntity byteData = gradBusinessService.getSchoolReportPDFByMincode(mincode, type); assertNotNull(byteData); + assertEquals(HttpStatus.NOT_FOUND, byteData.getStatusCode()); assertNull(byteData.getBody()); } @Test - void testSchoolReportPDFByMincode_Error500() throws Exception { + void testSchoolReportPDFByMincode_Error500() { String mincode = "128385861"; String type = "NONGRADPRJ"; - InputStream is = getClass().getClassLoader().getResourceAsStream("json/xmlTranscriptReportRequest.json"); - when(this.tokenUtils.getAccessToken()).thenReturn("accessToken"); + when(schoolService.getSchoolDetails(anyString())).thenThrow(new RuntimeException()); - when(this.webClient.get()).thenReturn(this.requestHeadersUriMock); - when(this.requestHeadersUriMock.uri(String.format(educGraduationApiConstants.getSchoolReportByMincode(),mincode,type))).thenReturn(this.requestHeadersMock); - when(this.requestHeadersMock.headers(any(Consumer.class))).thenReturn(this.requestHeadersMock); - when(this.requestHeadersMock.retrieve()).thenReturn(this.responseMock); - when(this.responseMock.bodyToMono(InputStreamResource.class)).thenThrow(); - - ResponseEntity byteData = gradBusinessService.getSchoolReportPDFByMincode(mincode, type, "accessToken"); + ResponseEntity byteData = gradBusinessService.getSchoolReportPDFByMincode(mincode, type); assertNotNull(byteData); - assertTrue(byteData.getBody().length > 0); assertTrue(byteData.getStatusCode().is5xxServerError()); } @@ -505,7 +491,7 @@ void testStudentTranscriptPDFByTypeByPen() throws Exception { } @Test - void testStudentCredentialPDFByType_NotFound() throws Exception { + void testStudentCredentialPDFByType_NotFound() { String pen = "128385861"; String type = "NONGRADREG"; @@ -542,15 +528,12 @@ void testStudentCredentialPDFByType_NotFound() throws Exception { } @Test - void testStudentCredentialPDFByType_Error500() throws Exception { + void testStudentCredentialPDFByType_Error500() { String pen = "128385861"; String type = "TRAN"; String studentID = UUID.randomUUID().toString(); - byte[] samplePdf = readBinaryFile("data/sample.pdf"); - InputStreamResource pdf = new InputStreamResource(new ByteArrayInputStream(samplePdf)); - Student sObj = new Student(); sObj.setStudentID(studentID); sObj.setPen(pen); diff --git a/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/EducGradBusinessApiControllerTests.java b/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/EducGradBusinessApiControllerTests.java index e40b22b..b5990c5 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/EducGradBusinessApiControllerTests.java +++ b/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/EducGradBusinessApiControllerTests.java @@ -143,7 +143,7 @@ void testXmlTranscriptData() throws Exception { } @Test - void testGetGradStudentByPenFromStudentAPI() throws Exception { + void testGetGradStudentByPenFromStudentAPI() { Student obj = new Student(); obj.setPen("12312321"); @@ -156,7 +156,7 @@ void testGetGradStudentByPenFromStudentAPI() throws Exception { } @Test - void testGetGradStudentDemographicsByPen() throws Exception { + void testGetGradStudentDemographicsByPen() { byte[] greBPack = "Any String you want".getBytes(); HttpHeaders headers = new HttpHeaders(); @@ -185,14 +185,14 @@ void testSchoolReportByMincode() throws Exception { .contentType(MediaType.APPLICATION_XML) .body(greBPack); - Mockito.when(gradBusinessService.getSchoolReportPDFByMincode("12312321", "GRAD","accessToken")).thenReturn(response); - gradBusinessController.schoolReportByMincode("12312321","GRAD", "accessToken"); - Mockito.verify(gradBusinessService).getSchoolReportPDFByMincode("12312321","GRAD", "accessToken"); + Mockito.when(gradBusinessService.getSchoolReportPDFByMincode("12312321", "GRAD")).thenReturn(response); + gradBusinessController.schoolReportByMincode("12312321","GRAD"); + Mockito.verify(gradBusinessService).getSchoolReportPDFByMincode("12312321","GRAD"); } @Test - void testAmalgamatedSchoolReportByMincode() throws Exception { + void testAmalgamatedSchoolReportByMincode() { byte[] greBPack = "Any String you want".getBytes(); HttpHeaders headers = new HttpHeaders(); @@ -210,7 +210,7 @@ void testAmalgamatedSchoolReportByMincode() throws Exception { } @Test - void testStudentCredentialByType() throws Exception { + void testStudentCredentialByType() { byte[] greBPack = "Any String you want".getBytes(); HttpHeaders headers = new HttpHeaders(); @@ -228,7 +228,7 @@ void testStudentCredentialByType() throws Exception { } @Test - void testStudentTranscriptPDFByType() throws Exception { + void testStudentTranscriptPDFByType() { byte[] greBPack = "Any String you want".getBytes(); HttpHeaders headers = new HttpHeaders(); diff --git a/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/service/RESTServiceGETTest.java b/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/service/RESTServiceGETTest.java new file mode 100644 index 0000000..497c0e8 --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/service/RESTServiceGETTest.java @@ -0,0 +1,86 @@ +package ca.bc.gov.educ.api.gradbusiness.service; + +import ca.bc.gov.educ.api.gradbusiness.exception.ServiceException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.util.function.Consumer; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@SpringBootTest +@RunWith(SpringRunner.class) +public class RESTServiceGETTest { + + @Autowired + private RESTService restService; + + @MockBean + private WebClient.RequestHeadersSpec requestHeadersMock; + @MockBean + private WebClient.RequestHeadersUriSpec requestHeadersUriMock; + @MockBean + private WebClient.RequestBodySpec requestBodyMock; + @MockBean + private WebClient.RequestBodyUriSpec requestBodyUriMock; + @MockBean + private WebClient.ResponseSpec responseMock; + @MockBean(name = "webClient") + WebClient webClient; + + @MockBean + @Qualifier("gradBusinessClient") + WebClient graduationClientWebClient; + + @MockBean + private ClientRegistrationRepository clientRegistrationRepositoryMock; + + @MockBean + private OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepositoryMock; + + private static final String TEST_URL_200 = "https://httpstat.us/200"; + private static final String TEST_URL_403 = "https://httpstat.us/403"; + private static final String TEST_URL_503 = "https://httpstat.us/503"; + private static final String OK_RESPONSE = "200 OK"; + + @Before + public void setUp(){ + when(this.webClient.get()).thenReturn(this.requestHeadersUriMock); + when(this.graduationClientWebClient.get()).thenReturn(this.requestHeadersUriMock); + when(this.requestHeadersUriMock.uri(any(String.class))).thenReturn(this.requestHeadersMock); + when(this.requestHeadersMock.headers(any(Consumer.class))).thenReturn(this.requestHeadersMock); + when(this.requestHeadersMock.retrieve()).thenReturn(this.responseMock); + when(this.responseMock.onStatus(any(), any())).thenReturn(this.responseMock); + } + + @Test + public void testGetOverride_GivenProperData_Expect200Response(){ + when(this.responseMock.bodyToMono(String.class)).thenReturn(Mono.just(OK_RESPONSE)); + String response = this.restService.get(TEST_URL_200, String.class); + Assert.assertEquals(OK_RESPONSE, response); + } + + @Test(expected = ServiceException.class) + public void testGetOverride_Given5xxErrorFromService_ExpectServiceError(){ + when(this.responseMock.bodyToMono(ServiceException.class)).thenReturn(Mono.just(new ServiceException("Error", 500))); + this.restService.get(TEST_URL_503, String.class); + } + + @Test(expected = ServiceException.class) + public void testGetOverride_Given4xxErrorFromService_ExpectServiceError(){ + when(this.responseMock.bodyToMono(ServiceException.class)).thenReturn(Mono.just(new ServiceException("Error", 500))); + this.restService.get(TEST_URL_403, String.class); + } +} diff --git a/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/service/RESTServicePOSTTest.java b/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/service/RESTServicePOSTTest.java new file mode 100644 index 0000000..fc676f5 --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/service/RESTServicePOSTTest.java @@ -0,0 +1,84 @@ +package ca.bc.gov.educ.api.gradbusiness.service; + +import ca.bc.gov.educ.api.gradbusiness.exception.ServiceException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.BodyInserter; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.util.function.Consumer; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@SpringBootTest +@RunWith(SpringRunner.class) +@ExtendWith(MockitoExtension.class) +public class RESTServicePOSTTest { + + @Autowired + private RESTService restService; + + @MockBean + private WebClient.RequestHeadersSpec requestHeadersMock; + @MockBean + private WebClient.RequestBodySpec requestBodyMock; + @MockBean + private WebClient.RequestBodyUriSpec requestBodyUriMock; + @MockBean + private WebClient.ResponseSpec responseMock; + @MockBean(name = "webClient") + WebClient webClient; + + @MockBean + @Qualifier("gradBusinessClient") + WebClient graduationClientWebClient; + + @MockBean + ClientRegistrationRepository clientRegistrationRepository; + + @MockBean + OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository; + + private static final byte[] TEST_BYTES = "The rain in Spain stays mainly on the plain.".getBytes(); + private static final String TEST_BODY = "{test:test}"; + private static final String TEST_URL = "https://fake.url.com"; + + @Before + public void setUp(){ + when(this.webClient.post()).thenReturn(this.requestBodyUriMock); + when(this.graduationClientWebClient.post()).thenReturn(this.requestBodyUriMock); + when(this.requestBodyUriMock.uri(any(String.class))).thenReturn(this.requestBodyUriMock); + when(this.requestBodyUriMock.headers(any(Consumer.class))).thenReturn(this.requestBodyMock); + when(this.requestBodyMock.contentType(any())).thenReturn(this.requestBodyMock); + when(this.requestBodyMock.body(any(BodyInserter.class))).thenReturn(this.requestHeadersMock); + when(this.requestHeadersMock.retrieve()).thenReturn(this.responseMock); + when(this.responseMock.bodyToMono(byte[].class)).thenReturn(Mono.just(TEST_BYTES)); + } + + @Test + public void testPostOverride_GivenProperData_Expect200Response(){ + when(this.responseMock.onStatus(any(), any())).thenReturn(this.responseMock); + byte[] response = this.restService.post(TEST_URL, TEST_BODY, byte[].class); + Assert.assertArrayEquals(TEST_BYTES, response); + } + + @Test(expected = ServiceException.class) + public void testPostOverride_Given4xxErrorFromService_ExpectServiceError() { + when(this.responseMock.onStatus(any(), any())).thenThrow(new ServiceException("Error", 500)); + this.restService.post(TEST_URL, TEST_BODY, byte[].class); + } + +} diff --git a/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/support/MockConfiguration.java b/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/support/MockConfiguration.java new file mode 100644 index 0000000..4b19baa --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/gradbusiness/support/MockConfiguration.java @@ -0,0 +1,62 @@ +package ca.bc.gov.educ.api.gradbusiness.support; + +import org.mockito.Mockito; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * The type Mock configuration. + */ +@Profile("test") +@Configuration +@ContextConfiguration(classes = {MockConfiguration.class}) +public class MockConfiguration { + + @Bean + @Primary + public RestTemplate restTemplate() { + return Mockito.mock(RestTemplate.class); + } + + @Bean + @Primary + public WebClient webClient() { + return Mockito.mock(WebClient.class); + } + + @Bean + @Qualifier("gradBusinessClient") + public WebClient getGraduationClientWebClient() { + return Mockito.mock(WebClient.class); + } + + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } + + @Bean + public ClientRegistrationRepository clientRegistrationRepository() { + return Mockito.mock(ClientRegistrationRepository.class); + } + + @Bean + public OAuth2AuthorizedClientService authorizedClientService() { + return Mockito.mock(OAuth2AuthorizedClientService.class); + } + + @Bean + public OAuth2AuthorizedClientRepository authorizedClientRepository() { + return Mockito.mock(OAuth2AuthorizedClientRepository.class); + } +} diff --git a/api/src/test/resources/application.yaml b/api/src/test/resources/application.yaml index c11267e..c7d3e4c 100644 --- a/api/src/test/resources/application.yaml +++ b/api/src/test/resources/application.yaml @@ -25,6 +25,16 @@ spring: jwt: issuer-uri: https://soam-dev.apps.silver.devops.gov.bc.ca/auth/realms/master jwk-set-uri: https://soam-dev.apps.silver.devops.gov.bc.ca/auth/realms/master/protocol/openid-connect/certs + client: + registration: + grad-business-client: + client-id: my-client + client-secret: 123 + authorization-grant-type: client_credentials + provider: + grad-business-client: + issuer-uri: http://test + token-uri: http://test #Logging properties logging: @@ -79,8 +89,8 @@ endpoint: keycloak: getToken: https://soam-dev.apps.silver.devops.gov.bc.ca/auth/realms/master/protocol/openid-connect/token grad-graduation-report-api: - school-report-by-mincode: - url: https://educ-grad-graduation-report-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/graduationreports/schoolreport?mincode=%s&reportType=%s + school-report-by-school-id-and-report-type: + url: http://test student-credential-by-type: url: https://educ-grad-graduation-report-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/graduationreports/business/studentcredential/%s/%s pen-student-api: @@ -107,6 +117,9 @@ endpoint: url: https://educ-grad-student-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/student/demog/pen/%s amalgamated-students: url: https://educ-grad-student-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/student/amalgamated/schoolreport/%s/type/%s + grad-trax-api: + search-schools-by-min-code: + url: http://test #Splunk LogHelper splunk: diff --git a/tools/config/update-configmap.sh b/tools/config/update-configmap.sh index 362f25d..851d060 100644 --- a/tools/config/update-configmap.sh +++ b/tools/config/update-configmap.sh @@ -8,6 +8,8 @@ COMMON_NAMESPACE=$4 BUSINESS_NAMESPACE=$5 SPLUNK_TOKEN=$6 APP_LOG_LEVEL=$7 +CLIENT_ID=$8 +CLIENT_SECRET_NAME=grad-business-api-client-secret SPLUNK_URL="gww.splunk.educ.gov.bc.ca" FLB_CONFIG="[SERVICE] @@ -62,6 +64,7 @@ oc create -n "$GRAD_NAMESPACE"-"$envValue" configmap "$APP_NAME"-config-map \ --from-literal=GRAD_GRADUATION_API="http://educ-grad-graduation-api.$GRAD_NAMESPACE-$envValue.svc.cluster.local:8080/" \ --from-literal=KEYCLOAK_TOKEN_URL="https://soam-$envValue.apps.silver.devops.gov.bc.ca/" \ --from-literal=GRAD_STUDENT_GRADUATION_API="http://educ-grad-student-graduation-api.$GRAD_NAMESPACE-$envValue.svc.cluster.local:8080/" \ + --from-literal=GRAD_TRAX_API="http://educ-grad-trax-api.$GRAD_NAMESPACE-$envValue.svc.cluster.local:8080/" \ --dry-run=client -o yaml | oc apply -f - echo Creating config map "$APP_NAME"-flb-sc-config-map @@ -69,3 +72,22 @@ oc create -n "$GRAD_NAMESPACE"-"$envValue" configmap "$APP_NAME"-flb-sc-config-m --from-literal=fluent-bit.conf="$FLB_CONFIG" \ --from-literal=parsers.conf="$PARSER_CONFIG" \ --dry-run=client -o yaml | oc apply -f - + +echo Retrieving client ID for grad-business-api-service +CLIENT_UUID=$(curl -sX GET "https://$SOAM_KC/auth/admin/realms/$SOAM_KC_REALM_ID/clients" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TKN" | + jq '.[] | select(.clientId=="'"$CLIENT_ID"'")' | jq -r '.id') + +echo +echo Retrieving client secret for grad-business-api-service +SERVICE_CLIENT_SECRET=$(curl -sX GET "https://$SOAM_KC/auth/admin/realms/$SOAM_KC_REALM_ID/clients/$CLIENT_UUID/client-secret" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TKN" | + jq -r '.value') + +echo Creating secret for client +oc create -n "$GRAD_NAMESPACE"-"$envValue" secret generic $CLIENT_SECRET_NAME \ + --from-literal=GRAD_BUSINESS_API_CLIENT_NAME="$CLIENT_ID" \ + --from-literal=GRAD_BUSINESS_API_CLIENT_SECRET="$SERVICE_CLIENT_SECRET" \ + --dry-run=client -o yaml | oc apply -f - \ No newline at end of file