Skip to content

Commit

Permalink
feat(FSADI1-1650): added new search endpoint (#269)
Browse files Browse the repository at this point in the history
Co-authored-by: Maria Martinez <[email protected]>
  • Loading branch information
paulushcgcj and mamartinezmejia authored Nov 25, 2024
1 parent e48d6c3 commit 46826de
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,87 @@ public Flux<ClientPublicViewDto> searchClients(
.putIfAbsent(ApplicationConstants.X_TOTAL_COUNT, List.of(dto.getCount().toString())));
}

/**
* Searches for clients based on the provided parameters using a fuzzy match algorithm.
* The search is case-insensitive and has a threshold cutout of 0.8 for the fuzzy match.
*
* @param page the one-based page number to retrieve, defaults to 0 if not provided.
* @param size the number of results per page, defaults to 10 if not provided.
* @param name the name of the client to search for (optional).
* @param acronym the acronym of the client to search for (optional).
* @param number the unique number of the client to search for (optional).
* @param serverResponse the {@link ServerHttpResponse} to include response headers.
* @return a reactive stream of {@link ClientPublicViewDto} objects representing matching
* clients.

Check warning on line 130 in src/main/java/ca/bc/gov/api/oracle/legacy/controller/ClientSearchController.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagContinuationIndentationCheck

Line continuation have incorrect indentation level, expected level should be 4.
*
* @apiNote This method provides a paginated, fuzzy search for client details. Results
* include a total record count in the response headers under {@code X-Total-Count}.

Check warning on line 133 in src/main/java/ca/bc/gov/api/oracle/legacy/controller/ClientSearchController.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagContinuationIndentationCheck

Line continuation have incorrect indentation level, expected level should be 4.
*/
@GetMapping("/by")
@Operation(
summary = "Search for clients",
description = """
Search for clients based on the provided parameters.
It uses a fuzzy match to search for the client name.
The cutout for the fuzzy match is 0.8. The search is case insensitive.""",
tags = {"Client Search API"},
responses = {
@ApiResponse(
responseCode = "200",
description = "Successfully retrieved clients",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
array = @ArraySchema(
schema = @Schema(
name = "ClientView",
implementation = ClientPublicViewDto.class
)
)
),
headers = {
@Header(
name = ApplicationConstants.X_TOTAL_COUNT,
description = "Total number of records found"
)
}
)
}
)
public Flux<ClientPublicViewDto> searchByAcronymNameNumber(
@Parameter(description = "The one index page number, defaults to 0", example = "0")
@RequestParam(value = "page", required = false, defaultValue = "0")
Integer page,

@Parameter(description = "The amount of data to be returned per page, defaults to 10",
example = "10")
@RequestParam(value = "size", required = false, defaultValue = "10")
Integer size,

@Parameter(description = "The name of the client you're searching", example = "Western Forest Products")

Check warning on line 175 in src/main/java/ca/bc/gov/api/oracle/legacy/controller/ClientSearchController.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck

Line is longer than 100 characters (found 110).
@RequestParam(value = "name", required = false)
String name,

@Parameter(description = "The acronym of the client you're searching", example = "WFPS")
@RequestParam(value = "acronym", required = false)
String acronym,

@Parameter(description = "The number of the client you're searching", example = "00000001")
@RequestParam(value = "number", required = false)
String number,

ServerHttpResponse serverResponse
) {

log.info("Searching for clients with name {}, acronym {}, number {}", name, acronym, number);
return
clientSearchService
.searchByAcronymNameNumber(name, acronym, number)
.flatMapMany(criteria -> clientSearchService.searchClientByQuery(criteria, page, size))
.doOnNext(client -> log.info("Found client with id {}", client.getClientNumber()))
.doOnNext(dto -> serverResponse.getHeaders()
.putIfAbsent(ApplicationConstants.X_TOTAL_COUNT,
List.of(dto.getCount().toString())));

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ca.bc.gov.api.oracle.legacy.entity.ForestClientEntity;
import org.springframework.data.domain.Pageable;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
Expand All @@ -25,4 +26,20 @@ Flux<ForestClientEntity> findByClientNumberContainingOrClientNameContaining(

Mono<Long> countByClientNumberContainingOrClientNameContaining(String clientNumber,
String clientName);


@Query(value = """

Check warning on line 31 in src/main/java/ca/bc/gov/api/oracle/legacy/repository/ForestClientRepository.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.whitespace.FileTabCharacterCheck

Line contains a tab character.

Check warning on line 31 in src/main/java/ca/bc/gov/api/oracle/legacy/repository/ForestClientRepository.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.indentation.IndentationCheck

'method def modifier' has incorrect indentation level 8, expected level should be 2.
SELECT
CLIENT_NUMBER
FROM THE.FOREST_CLIENT
WHERE
UTL_MATCH.JARO_WINKLER_SIMILARITY(CLIENT_NAME, :clientName) >= 80
OR UTL_MATCH.JARO_WINKLER_SIMILARITY(LEGAL_FIRST_NAME, :clientName) >= 80
OR UTL_MATCH.JARO_WINKLER_SIMILARITY(LEGAL_MIDDLE_NAME, :clientName) >= 80
OR UTL_MATCH.JARO_WINKLER_SIMILARITY(TRIM(COALESCE(LEGAL_FIRST_NAME || ' ', '') || TRIM(COALESCE(LEGAL_MIDDLE_NAME || ' ', '')) || COALESCE(CLIENT_NAME, '')), :clientName) >= 80

Check warning on line 39 in src/main/java/ca/bc/gov/api/oracle/legacy/repository/ForestClientRepository.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck

Line is longer than 100 characters (found 183).
OR CLIENT_ACRONYM = :acronym
OR CLIENT_NUMBER = :clientNumber
ORDER BY CLIENT_NUMBER""")
Flux<String> searchNumberByNameAcronymNumber(String clientName, String acronym, String clientNumber);

Check warning on line 43 in src/main/java/ca/bc/gov/api/oracle/legacy/repository/ForestClientRepository.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck

Line is longer than 100 characters (found 109).

Check warning on line 43 in src/main/java/ca/bc/gov/api/oracle/legacy/repository/ForestClientRepository.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.whitespace.FileTabCharacterCheck

Line contains a tab character.

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,28 @@

import ca.bc.gov.api.oracle.legacy.dto.ClientPublicViewDto;
import ca.bc.gov.api.oracle.legacy.entity.ForestClientEntity;
import ca.bc.gov.api.oracle.legacy.repository.ForestClientRepository;
import ca.bc.gov.api.oracle.legacy.util.ClientMapper;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service

Check warning on line 22 in src/main/java/ca/bc/gov/api/oracle/legacy/service/ClientSearchService.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocTypeCheck

Missing a Javadoc comment.
@RequiredArgsConstructor
@Slf4j
public class ClientSearchService {

private final R2dbcEntityTemplate template;
private final ForestClientRepository forestClientRepository;

/**
* This method is used to create a search criteria based on a list of client IDs. It first logs
Expand Down Expand Up @@ -104,4 +108,51 @@ public Flux<ClientPublicViewDto> searchClientByQuery(
)
.doOnNext(client -> log.info("Found client with id {}", client.getClientNumber()));
}

/**
* Constructs a search {@link Criteria} object based on the provided client name, acronym, or
* number.
* This method normalizes input values for case-insensitive searches and validates the client
* number.
*
* @param name the name of the client to search for, or null if not specified.
* @param acronym the acronym of the client to search for, or null if not specified.
* @param number the unique number of the client to search for, or null if not specified.
* @return a {@link Mono} emitting the constructed {@link Criteria} object for the search.
*
* @implNote Input values are transformed to uppercase for case-insensitivity. The client
* number is validated using {@link #checkClientNumber(String)}. Repository results are

Check warning on line 124 in src/main/java/ca/bc/gov/api/oracle/legacy/service/ClientSearchService.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagContinuationIndentationCheck

Line continuation have incorrect indentation level, expected level should be 4.
* mapped to a search criteria object.

Check warning on line 125 in src/main/java/ca/bc/gov/api/oracle/legacy/service/ClientSearchService.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagContinuationIndentationCheck

Line continuation have incorrect indentation level, expected level should be 4.
*/
public Mono<Criteria> searchByAcronymNameNumber(String name, String acronym, String number) {
log.info("Searching for clients with name {}, acronym {} or number {}", name, acronym, number);

String searchName = StringUtils.isNotBlank(name) ? name.toUpperCase() : null;
String searchAcronym = StringUtils.isNotBlank(acronym) ? acronym.toUpperCase() : null;
String searchNumber = StringUtils.isNotBlank(number) ? checkClientNumber(number) : null;

return
forestClientRepository
.searchNumberByNameAcronymNumber(
searchName,
searchAcronym,
searchNumber
)
.collectList()
.map(this::searchById);

}

private String checkClientNumber(String clientNumber) {
if(StringUtils.isBlank(clientNumber)) {

Check warning on line 147 in src/main/java/ca/bc/gov/api/oracle/legacy/service/ClientSearchService.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.whitespace.WhitespaceAfterCheck

'if' is not followed by whitespace.

Check warning on line 147 in src/main/java/ca/bc/gov/api/oracle/legacy/service/ClientSearchService.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.whitespace.WhitespaceAroundCheck

WhitespaceAround: 'if' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
return clientNumber;
}

try {
Integer parsed = Integer.parseInt(clientNumber);
return String.format("%08d", parsed);
} catch (NumberFormatException nfe) {
return clientNumber;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import ca.bc.gov.api.oracle.legacy.AbstractTestContainerIntegrationTest;
import ca.bc.gov.api.oracle.legacy.dto.ClientPublicViewDto;
import java.util.Optional;
import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
Expand Down Expand Up @@ -36,6 +38,39 @@ void shouldSearchClientsById(Integer returnSize, Object[] ids) {
.hasSize(returnSize);
}

@ParameterizedTest
@MethodSource("searchByNameAcronymNumber")
@DisplayName("Search clients by name, acronym, or number")
void shouldSearchByNameAcronymOrNumber(Integer returnSize, String name, String acronym, String number) {
webTestClient
.get()
.uri(uriBuilder -> uriBuilder
.path("/api/clients/search/by")
.queryParamIfPresent("name", Optional.ofNullable(name))
.queryParamIfPresent("acronym", Optional.ofNullable(acronym))
.queryParamIfPresent("number", Optional.ofNullable(number))
.build()
)
.exchange()
.expectStatus().isOk()
.expectBodyList(ClientPublicViewDto.class)
.hasSize(returnSize);
}


private static Stream<Arguments> searchByNameAcronymNumber() {
return Stream.of(
Arguments.of(1, "INDIA",null,null),
Arguments.of(8, null,"SAMPLIBC",null),
Arguments.of(1, null,null,"00000001"),
Arguments.of(1, null,null,"1"),

Arguments.of(0, "XXAABBDA",null,null),
Arguments.of(0, null,"XXAABB",null),
Arguments.of(0, null,null,"12345678")
);
}


private static Stream<Arguments> searchById() {
return Stream.of(
Expand Down

0 comments on commit 46826de

Please sign in to comment.