Skip to content

Commit

Permalink
Move to auto match via student service
Browse files Browse the repository at this point in the history
  • Loading branch information
arcshiftsolutions committed Jan 26, 2024
1 parent 2d0f158 commit 504e98f
Show file tree
Hide file tree
Showing 14 changed files with 427 additions and 274 deletions.
100 changes: 100 additions & 0 deletions api/src/main/java/ca/bc/gov/educ/api/soam/filter/FilterOperation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package ca.bc.gov.educ.api.soam.filter;

import com.fasterxml.jackson.annotation.JsonValue;

import java.util.Optional;

/**
* The enum Filter operation.
*/
public enum FilterOperation {

/**
* Equal filter operation.
*/
EQUAL("eq"),
/**
* Not equal filter operation.
*/
NOT_EQUAL("neq"),
/**
* Greater than filter operation.
*/
GREATER_THAN("gt"),
/**
* Greater than or equal to filter operation.
*/
GREATER_THAN_OR_EQUAL_TO("gte"),
/**
* Less than filter operation.
*/
LESS_THAN("lt"),
/**
* Less than or equal to filter operation.
*/
LESS_THAN_OR_EQUAL_TO("lte"),
/**
* In filter operation.
*/
IN("in"),
/**
* Not in filter operation.
*/
NOT_IN("nin"),
/**
* Between filter operation.
*/
BETWEEN("btn"),
/**
* Contains filter operation.
*/
CONTAINS("like"),
/**
* Contains ignore case filter operation.
*/
CONTAINS_IGNORE_CASE("like_ignore_case"),
/**
* Starts with filter operation.
*/
STARTS_WITH("starts_with"),
/**
* Starts with ignore case filter operation.
*/
STARTS_WITH_IGNORE_CASE("starts_with_ignore_case");

/**
* The Value.
*/
private final String value;

/**
* Instantiates a new Filter operation.
*
* @param value the value
*/
FilterOperation(String value) {
this.value = value;
}

/**
* From value optional.
*
* @param value the value
* @return the optional
*/
public static Optional<FilterOperation> fromValue(String value) {
for (FilterOperation op : FilterOperation.values()) {
if (String.valueOf(op.value).equalsIgnoreCase(value)) {
return Optional.of(op);
}
}
return Optional.empty();
}

@Override
@JsonValue
public String toString() {
return String.valueOf(value);
}

}
151 changes: 125 additions & 26 deletions api/src/main/java/ca/bc/gov/educ/api/soam/rest/RestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@

import ca.bc.gov.educ.api.soam.codetable.CodeTableUtils;
import ca.bc.gov.educ.api.soam.exception.SoamRuntimeException;
import ca.bc.gov.educ.api.soam.filter.FilterOperation;
import ca.bc.gov.educ.api.soam.model.entity.DigitalIDEntity;
import ca.bc.gov.educ.api.soam.model.entity.ServicesCardEntity;
import ca.bc.gov.educ.api.soam.model.entity.StsLoginPrincipalEntity;
import ca.bc.gov.educ.api.soam.model.entity.StudentEntity;
import ca.bc.gov.educ.api.soam.properties.ApplicationProperties;
import ca.bc.gov.educ.api.soam.struct.v1.penmatch.PenMatchResult;
import ca.bc.gov.educ.api.soam.struct.v1.penmatch.PenMatchStudent;
import ca.bc.gov.educ.api.soam.struct.v1.student.Condition;
import ca.bc.gov.educ.api.soam.struct.v1.student.Search;
import ca.bc.gov.educ.api.soam.struct.v1.student.SearchCriteria;
import ca.bc.gov.educ.api.soam.struct.v1.student.ValueType;
import ca.bc.gov.educ.api.soam.struct.v1.tenant.TenantAccess;
import ca.bc.gov.educ.api.soam.util.SoamUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.retry.annotation.Retry;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
Expand All @@ -26,9 +32,16 @@
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

import static ca.bc.gov.educ.api.soam.filter.FilterOperation.EQUAL;
import static ca.bc.gov.educ.api.soam.struct.v1.student.Condition.AND;
import static ca.bc.gov.educ.api.soam.struct.v1.student.ValueType.DATE;
import static ca.bc.gov.educ.api.soam.struct.v1.student.ValueType.STRING;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;

@Component
Expand All @@ -43,6 +56,42 @@ public class RestUtils {
private static final String STUDENT_API = "studentApi";
private static final String STS_API = "stsAPI";

/**
* The constant PAGE_SIZE.
*/
public static final String PAGE_SIZE = "pageSize";
/**
* The constant LEGAL_LAST_NAME.
*/
public static final String LEGAL_LAST_NAME = "legalLastName";
/**
* The constant LEGAL_FIRST_NAME.
*/
public static final String LEGAL_FIRST_NAME = "legalFirstName";
/**
* The constant LEGAL_FIRST_NAME.
*/
public static final String LEGAL_MIDDLE_NAMES = "legalMiddleNames";
/**
* The constant LEGAL_FIRST_NAME.
*/
public static final String GENDER = "gender";
/**
* The constant DOB.
*/
public static final String DOB = "dob";
/**
* The constant DOB_FORMATTER_LONG.
*/
private static final DateTimeFormatter DOB_FORMATTER_LONG = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/**
* The constant status.
*/
public static final String STATUS_CODE = "statusCode";
/**
* The Object mapper.
*/
private final ObjectMapper objectMapper = new ObjectMapper();
private final WebClient webClient;
private final ApplicationProperties props;
private final SoamUtil soamUtil;
Expand Down Expand Up @@ -329,30 +378,6 @@ public DigitalIDEntity createDigitalID(@NonNull final String identifierType, @No
}
}

public Optional<PenMatchResult> postToMatchAPI(PenMatchStudent request) {
try {
val response = this.webClient.post()
.uri(this.props.getPenMatchApiURL())
.header(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.body(Mono.just(request), PenMatchStudent.class)
.retrieve()
.bodyToMono(PenMatchResult.class)
.doOnSuccess(entity -> {
if (entity != null) {
this.logSuccess(entity.toString());
}
})
.block();
if (response == null) {
throw new SoamRuntimeException(this.getErrorMessageString(HttpStatus.INTERNAL_SERVER_ERROR, NULL_BODY_FROM +
"pen match get call."));
}
return Optional.of(response);
} catch (final WebClientResponseException e) {
throw new SoamRuntimeException(this.getErrorMessageString(e.getStatusCode(), e.getResponseBodyAsString()));
}
}

@Bulkhead(name = SERVICES_CARD_API)
@CircuitBreaker(name = SERVICES_CARD_API)
public void createServicesCard(@NonNull final ServicesCardEntity servicesCardEntity, final String correlationID) {
Expand Down Expand Up @@ -408,6 +433,80 @@ public StudentEntity getStudentByStudentID(final String studentID, final String
}
}

@Bulkhead(name = STUDENT_API)
@CircuitBreaker(name = STUDENT_API)
@Retry(name = STUDENT_API)
public List<StudentEntity> getStudentByBCSCDemogs(final ServicesCardEntity servicesCard, final String correlationID) {
try {
val searchCriteria = getSearchCriteria(servicesCard);
val apiResponse = this.webClient.get()
.uri(this.props.getStudentApiURL(), uri -> uri.path("/paginated?pageNumber=1&pageSize=10&searchCriteriaList=" + searchCriteria)
.build())
.header(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(CORRELATION_ID, correlationID)
.retrieve()
.bodyToMono(List.class)
.doOnSuccess(responseEntity -> {
if (responseEntity != null) {
this.logSuccess(responseEntity.toString());
}
})
.block();
if (apiResponse == null) {
throw new SoamRuntimeException(this.getErrorMessageString(HttpStatus.INTERNAL_SERVER_ERROR, NULL_BODY_FROM +
"student get call."));
}
return apiResponse;
} catch (final WebClientResponseException e) {
throw new SoamRuntimeException(this.getErrorMessageString(e.getStatusCode(), e.getResponseBodyAsString()));
} catch (final JsonProcessingException e) {
throw new SoamRuntimeException("Error parsing search criteria: " + e.getMessage());
}
}

public String getSearchCriteria(final ServicesCardEntity servicesCard) throws JsonProcessingException {
final LocalDate dobDate = LocalDate.parse(soamUtil.getBCSCDobString(servicesCard.getBirthDate()), DOB_FORMATTER_LONG);
final List<SearchCriteria> criteriaList = new LinkedList<>();
criteriaList.add(this.getCriteria(DOB, EQUAL, DOB_FORMATTER_LONG.format(dobDate), DATE));
criteriaList.add(this.getCriteriaWithCondition(LEGAL_LAST_NAME, EQUAL, servicesCard.getSurname(), STRING, AND));

if(StringUtils.isNotBlank(servicesCard.getGivenName())) {
criteriaList.add(this.getCriteriaWithCondition(LEGAL_FIRST_NAME, EQUAL, servicesCard.getGivenName(), STRING, AND));
}else{
criteriaList.add(this.getCriteriaWithCondition(LEGAL_FIRST_NAME, EQUAL, null, STRING, AND));
}

if(StringUtils.isNotEmpty(servicesCard.getGivenNames())) {
var middleName = servicesCard.getGivenNames().replaceAll(servicesCard.getGivenNames(), "").trim();
if (StringUtils.isNotBlank(middleName)) {
criteriaList.add(this.getCriteriaWithCondition(LEGAL_MIDDLE_NAMES, EQUAL, middleName, STRING, AND));
} else {
criteriaList.add(this.getCriteriaWithCondition(LEGAL_MIDDLE_NAMES, EQUAL, null, STRING, AND));
}
}

criteriaList.add(this.getCriteriaWithCondition(GENDER, EQUAL, servicesCard.getGender(), STRING, AND));

final List<SearchCriteria> criteriaMergedDeceased = new LinkedList<>();

final SearchCriteria criteriaMandD = SearchCriteria.builder().key(STATUS_CODE).operation(FilterOperation.NOT_IN).value("M,D").valueType(STRING).build();

criteriaMergedDeceased.add(criteriaMandD);

final List<Search> searches = new LinkedList<>();
searches.add(Search.builder().searchCriteriaList(criteriaList).build());
searches.add(Search.builder().condition(AND).searchCriteriaList(criteriaMergedDeceased).build());

return this.objectMapper.writeValueAsString(searches);
}

private SearchCriteria getCriteria(final String key, final FilterOperation operation, final String value, final ValueType valueType) {
return SearchCriteria.builder().key(key).operation(operation).value(value).valueType(valueType).build();
}

private SearchCriteria getCriteriaWithCondition(final String key, final FilterOperation operation, final String value, final ValueType valueType, final Condition condition) {
return SearchCriteria.builder().key(key).operation(operation).value(value).valueType(valueType).condition(condition).build();
}

private String getErrorMessageString(final HttpStatusCode status, final String body) {
return "Unexpected HTTP return code: " + status + " error message: " + body;
Expand Down
Loading

0 comments on commit 504e98f

Please sign in to comment.