Skip to content

Commit

Permalink
[MODFIN-388] - Implement endpoint to return all finance data for FY t…
Browse files Browse the repository at this point in the history
…hrough acqUnitRestriciton
  • Loading branch information
azizbekxm committed Nov 19, 2024
1 parent 3d1ae11 commit 2ce1ef8
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,17 @@ public FinanceDataService(RestClient restClient, AcqUnitsService acqUnitsService

public Future<FyFinanceDataCollection> getFinanceDataWithAcqUnitsRestriction(String query, int offset, int limit,
RequestContext requestContext) {
return restClient.get(new RequestEntry(resourcesPath(FINANCE_DATA_STORAGE))
return acqUnitsService.buildAcqUnitsCqlClauseForFinanceData(requestContext)
.map(clause -> StringUtils.isEmpty(query) ? clause : combineCqlExpressions("and", clause, query))
.compose(effectiveQuery -> getFinanceData(effectiveQuery, offset, limit, requestContext));
}

private Future<FyFinanceDataCollection> getFinanceData(String query, int offset, int limit, RequestContext requestContext) {
var requestEntry = new RequestEntry(resourcesPath(FINANCE_DATA_STORAGE))
.withOffset(offset)
.withLimit(limit)
.withQuery(query)
.buildEndpoint(), FyFinanceDataCollection.class, requestContext);
// return acqUnitsService.buildAcqUnitsCqlClause(requestContext)
// .map(clause -> StringUtils.isEmpty(query) ? clause : combineCqlExpressions("and", clause, query))
// .map(effectiveQuery -> new RequestEntry(resourcesPath(FINANCE_DATA_STORAGE))
// .withOffset(offset)
// .withLimit(limit)
// .withQuery(effectiveQuery)
// )
// .compose(requestEntry -> restClient.get(requestEntry.buildEndpoint(), FyFinanceDataCollection.class, requestContext));
.withQuery(query);
return restClient.get(requestEntry.buildEndpoint(), FyFinanceDataCollection.class, requestContext);
}
}

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

public final class AcqUnitConstants {
public static final String ACQUISITIONS_UNIT_IDS = "acqUnitIds";
public static final String FD_FUND_ACQUISITIONS_UNIT_IDS = "fundAcqUnitIds"; // for finance data view table
public static final String FD_BUDGET_ACQUISITIONS_UNIT_IDS = "budgetAcqUnitIds"; // for finance data view table
public static final String NO_ACQ_UNIT_ASSIGNED_CQL = "cql.allRecords=1 not " + ACQUISITIONS_UNIT_IDS + " <> []";
public static final String FD_NO_ACQ_UNIT_ASSIGNED_CQL = "cql.allRecords=1 not " +
FD_BUDGET_ACQUISITIONS_UNIT_IDS + " <> []" + FD_FUND_ACQUISITIONS_UNIT_IDS + " <> []";
public static final String IS_DELETED_PROP = "isDeleted";
public static final String ALL_UNITS_CQL = IS_DELETED_PROP + "=*";
public static final String ACTIVE_UNITS_CQL = IS_DELETED_PROP + "==false";
Expand Down
20 changes: 19 additions & 1 deletion src/main/java/org/folio/services/protection/AcqUnitsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import static org.folio.rest.util.ResourcePathResolver.resourcesPath;
import static org.folio.services.protection.AcqUnitConstants.ACQUISITIONS_UNIT_IDS;
import static org.folio.services.protection.AcqUnitConstants.ACTIVE_UNITS_CQL;
import static org.folio.services.protection.AcqUnitConstants.FD_BUDGET_ACQUISITIONS_UNIT_IDS;
import static org.folio.services.protection.AcqUnitConstants.FD_FUND_ACQUISITIONS_UNIT_IDS;
import static org.folio.services.protection.AcqUnitConstants.FD_NO_ACQ_UNIT_ASSIGNED_CQL;
import static org.folio.services.protection.AcqUnitConstants.IS_DELETED_PROP;
import static org.folio.services.protection.AcqUnitConstants.NO_ACQ_UNIT_ASSIGNED_CQL;

Expand Down Expand Up @@ -60,7 +63,22 @@ public Future<String> buildAcqUnitsCqlClause(RequestContext requestContext) {
if (ids.isEmpty()) {
return NO_ACQ_UNIT_ASSIGNED_CQL;
}
return String.format("%s or (%s)", convertIdsToCqlQuery(ids, ACQUISITIONS_UNIT_IDS, false), NO_ACQ_UNIT_ASSIGNED_CQL);
return String.format("%s or (%s)",
convertIdsToCqlQuery(ids, ACQUISITIONS_UNIT_IDS, false),
NO_ACQ_UNIT_ASSIGNED_CQL);
});
}

public Future<String> buildAcqUnitsCqlClauseForFinanceData(RequestContext requestContext) {
return getAcqUnitIdsForSearch(requestContext)
.map(ids -> {
if (ids.isEmpty()) {
return NO_ACQ_UNIT_ASSIGNED_CQL;
}
return String.format("(%s and %s) or (%s)",
convertIdsToCqlQuery(ids, FD_FUND_ACQUISITIONS_UNIT_IDS, false),
convertIdsToCqlQuery(ids, FD_BUDGET_ACQUISITIONS_UNIT_IDS, false),
FD_NO_ACQ_UNIT_ASSIGNED_CQL);
});
}

Expand Down
19 changes: 6 additions & 13 deletions src/test/java/org/folio/rest/impl/EntitiesCrudBasicsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import static org.folio.rest.util.TestConstants.TOTAL_RECORDS;
import static org.folio.rest.util.TestConstants.VALID_UUID;
import static org.folio.rest.util.TestEntities.BUDGET;
import static org.folio.rest.util.TestEntities.FINANCE_DATA;
import static org.folio.rest.util.TestEntities.FISCAL_YEAR;
import static org.folio.rest.util.TestEntities.FUND;
import static org.folio.rest.util.TestEntities.GROUP;
Expand All @@ -42,7 +41,6 @@
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -129,16 +127,14 @@ static Stream<TestEntities> getTestEntitiesWithGetEndpointWithoutGroup() {
*/
static Stream<TestEntities> getTestEntitiesWithGetByIdEndpoint() {
return getTestEntitiesWithGetEndpoint()
.filter(e -> !e.equals(GROUP_FUND_FISCAL_YEAR))
.filter(e -> !e.equals(FINANCE_DATA));
.filter(e -> !e.equals(GROUP_FUND_FISCAL_YEAR));
}

static Stream<TestEntities> getTestEntitiesWithPostEndpoint() {
return getTestEntities()
.filter(e -> !e.equals(TRANSACTIONS))
.filter(e -> !e.equals(TRANSACTIONS_ALLOCATION))
.filter(e -> !e.equals(TRANSACTIONS_TRANSFER))
.filter(e -> !e.equals(FINANCE_DATA));
.filter(e -> !e.equals(TRANSACTIONS_TRANSFER));
}

/**
Expand All @@ -148,8 +144,7 @@ static Stream<TestEntities> getTestEntitiesWithPostEndpoint() {
*/
static Stream<TestEntities> getTestEntitiesWithPutEndpoint() {
return getTestEntitiesWithGetByIdEndpoint()
.filter(e -> !e.equals(TRANSACTIONS))
.filter(e -> !e.equals(FINANCE_DATA));
.filter(e -> !e.equals(TRANSACTIONS));
}

/**
Expand All @@ -158,9 +153,7 @@ static Stream<TestEntities> getTestEntitiesWithPutEndpoint() {
* @return stream of test entities
*/
static Stream<TestEntities> getTestEntitiesWithDeleteEndpoint() {
return getTestEntitiesWithGetEndpoint()
.filter(e -> !e.equals(TRANSACTIONS))
.filter(e -> !e.equals(FINANCE_DATA));
return getTestEntitiesWithGetEndpoint().filter(e -> !e.equals(TRANSACTIONS));
}

/**
Expand Down Expand Up @@ -271,7 +264,7 @@ void testGetRecordByIdNotFound(TestEntities testEntity) {

@ParameterizedTest
@MethodSource("getTestEntitiesWithPostEndpoint")
void testPostRecord(TestEntities testEntity) throws IOException {
void testPostRecord(TestEntities testEntity) {
logger.info("=== Test create {} record ===", testEntity.name());

JsonObject record = testEntity.getMockObject();
Expand Down Expand Up @@ -299,7 +292,7 @@ record = JsonObject.mapFrom(t);

@ParameterizedTest
@MethodSource("getTestEntitiesWithPostEndpoint")
void testPostRecordServerError(TestEntities testEntity) throws IOException {
void testPostRecordServerError(TestEntities testEntity) {
logger.info("=== Test create {} record - Internal Server Error ===", testEntity.name());

Headers headers = RestTestUtils.prepareHeaders(TestConfig.X_OKAPI_URL, ERROR_X_OKAPI_TENANT);
Expand Down
117 changes: 43 additions & 74 deletions src/test/java/org/folio/rest/impl/FinanceDataApiTest.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
package org.folio.rest.impl;

import static io.vertx.core.Future.succeededFuture;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static javax.ws.rs.core.Response.Status.OK;
import static org.folio.rest.util.MockServer.addMockEntry;
import static org.folio.rest.util.ErrorCodes.GENERIC_ERROR_CODE;
import static org.folio.rest.util.RestTestUtils.verifyGet;
import static org.folio.rest.util.TestConfig.autowireDependencies;
import static org.folio.rest.util.TestConfig.clearVertxContext;
import static org.folio.rest.util.TestConfig.initSpringContext;
import static org.folio.rest.util.TestConfig.isVerticleNotDeployed;
import static org.folio.rest.util.TestEntities.FINANCE_DATA;
import static org.folio.services.protection.AcqUnitConstants.NO_ACQ_UNIT_ASSIGNED_CQL;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.core.Is.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;

import io.vertx.core.Context;
import java.util.Map;
import java.util.UUID;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import io.vertx.core.Future;
import io.vertx.ext.web.handler.HttpException;
import org.folio.ApiTestSuite;
import org.folio.config.ApplicationConfig;
import org.folio.rest.core.models.RequestContext;
import org.folio.rest.jaxrs.model.Errors;
import org.folio.rest.jaxrs.model.FyFinanceData;
import org.folio.rest.jaxrs.model.FyFinanceDataCollection;
import org.folio.rest.jaxrs.resource.FinanceFinanceData;
import org.folio.rest.util.HelperUtils;
import org.folio.rest.util.RestTestUtils;
import org.folio.services.financedata.FinanceDataService;
import org.folio.services.protection.AcqUnitsService;
Expand All @@ -42,102 +43,71 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;

class FinanceDataApiTest {
public class FinanceDataApiTest {

@Autowired
private FinanceDataService financeDataService;
public FinanceDataService financeDataService;
@Autowired
private AcqUnitsService acqUnitsService;
public AcqUnitsService acqUnitsService;

private Context vertxContext;
private Map<String, String> okapiHeaders;
private static boolean runningOnOwn;
private static final String FINANCE_DATA_ENDPOINT = "/finance-storage/finance-data";

@BeforeAll
static void before() throws InterruptedException, ExecutionException, TimeoutException {
static void init() throws InterruptedException, ExecutionException, TimeoutException {
if (isVerticleNotDeployed()) {
ApiTestSuite.before();
runningOnOwn = true;
}
initSpringContext(ApplicationConfig.class);
initSpringContext(FinanceDataApiTest.ContextConfiguration.class);
}

@BeforeEach
void setUp() {
void beforeEach() {
autowireDependencies(this);
vertxContext = mock(Context.class);
okapiHeaders = mock(Map.class);
}

@AfterEach
void resetMocks() {
reset(financeDataService);
reset(acqUnitsService);
}

@AfterAll
static void after() {
static void afterAll() {
if (runningOnOwn) {
ApiTestSuite.after();
}
clearVertxContext();
}

@AfterEach
void resetMocks() {
reset(financeDataService);
reset(acqUnitsService);
}

@Test
void testGetFinanceFinanceDataSuccess() {
var fiscalYearId = "123e4567-e89b-12d3-a456-426614174004";
var getFinanceDataWithFiscalYearId = FINANCE_DATA.getEndpoint() + "?query=fiscalYearId==" + fiscalYearId + "&offset=0&limit=10";
var financeDataCollection = new FyFinanceDataCollection()
.withFyFinanceData(List.of(new FyFinanceData().withFiscalYearId(fiscalYearId)))
.withTotalRecords(1);

addMockEntry(FINANCE_DATA.name(), FINANCE_DATA.getMockObject());
// when(acqUnitsService.buildAcqUnitsCqlClause(any()))
// .thenReturn(io.vertx.core.Future.succeededFuture(""));
when(financeDataService.getFinanceDataWithAcqUnitsRestriction(any(), anyInt(), anyInt(), any(RequestContext.class)));
var financeData = RestTestUtils.verifyGet(FINANCE_DATA.getEndpoint(), APPLICATION_JSON, OK.getStatusCode())
.as(FyFinanceData.class);
when(financeDataService.getFinanceDataWithAcqUnitsRestriction(any(), anyInt(), anyInt(), any(RequestContext.class)))
.thenReturn(succeededFuture(financeDataCollection));
when(acqUnitsService.buildAcqUnitsCqlClause(any())).thenReturn(succeededFuture(NO_ACQ_UNIT_ASSIGNED_CQL));
var actualFinanceDataCollection = RestTestUtils.verifyGet(FINANCE_DATA_ENDPOINT, APPLICATION_JSON, OK.getStatusCode())
.as(FyFinanceDataCollection.class);

assertThat(fiscalYearId, equalTo(financeData.getFiscalYearId()));
assertThat(fiscalYearId, equalTo(actualFinanceDataCollection.getFyFinanceData().get(0).getFiscalYearId()));
}

// @Test
// void getFinanceFinanceDataReturnsEmptyCollectionWhenNoData() {
// var fiscalYearId = "123e4567-e89b-12d3-a456-426614174004";
// var getFinanceDataWithFiscalYearId = FINANCE_DATA.getEndpoint() + "?query=fiscalYearId==" + fiscalYearId + "&offset=0&limit=10";
//
// var financeData = RestTestUtils.verifyGet(FINANCE_DATA.getEndpoint(), APPLICATION_JSON, OK.getStatusCode())
// .as(FyFinanceDataCollection.class);
//
// assertThat(financeData.getTotalRecords(), is(0));
// assertThat(financeData.getFyFinanceData(), is(empty()));
// }
//
// @Test
// void testGetFinanceFinanceDataFailure() {
// String query = "query";
// int offset = 0;
// int limit = 10;
// Throwable throwable = new RuntimeException("Error");
//
// when(financeDataService.getFinanceDataWithAcqUnitsRestriction(any(), anyInt(), anyInt(), any(RequestContext.class)))
// .thenReturn(io.vertx.core.Future.failedFuture(throwable));
//
// Handler<AsyncResult<Response>> asyncResultHandler = asyncResult -> {
// assertThat(asyncResult.failed(), equalTo(true));
// assertThat(asyncResult.cause(), equalTo(throwable));
// };
//
//// financeDataApi.getFinanceFinanceData(query, null, offset, limit, okapiHeaders, asyncResultHandler, vertxContext);
//
// verify(financeDataService).getFinanceDataWithAcqUnitsRestriction(query, offset, limit, new RequestContext(vertxContext, okapiHeaders));
// }

@Test
void testGetCollectionGffyNotFound() {
var collection = verifyGet(
HelperUtils.getEndpoint(FinanceFinanceData.class) + RestTestUtils.buildQueryParam("id==(" + UUID.randomUUID() + ")"),
APPLICATION_JSON, NOT_FOUND.getStatusCode()).as(FyFinanceDataCollection.class);
void testGetFinanceFinanceDataFailure() {
Future<FyFinanceDataCollection> financeDataFuture = Future.failedFuture(new HttpException(500, INTERNAL_SERVER_ERROR.getReasonPhrase()));

when(financeDataService.getFinanceDataWithAcqUnitsRestriction(any(), anyInt(), anyInt(), any(RequestContext.class)))
.thenReturn(financeDataFuture);

assertThat(collection.getTotalRecords(), is(0));
assertThat(collection.getTotalRecords(), is(0));
var errors = verifyGet(FINANCE_DATA_ENDPOINT, APPLICATION_JSON, INTERNAL_SERVER_ERROR.getStatusCode()).as(Errors.class);

assertThat(errors.getErrors(), hasSize(1));
assertThat(errors.getErrors().get(0).getCode(), is(GENERIC_ERROR_CODE.getCode()));
}

static class ContextConfiguration {
Expand All @@ -147,8 +117,7 @@ public FinanceDataService financeDataService() {
return mock(FinanceDataService.class);
}

@Bean
public AcqUnitsService acqUnitsService() {
@Bean AcqUnitsService acqUnitsService() {
return mock(AcqUnitsService.class);
}
}
Expand Down
Loading

0 comments on commit 2ce1ef8

Please sign in to comment.