From 6a2bebb62c1678e20e7825e9e08174016f509da2 Mon Sep 17 00:00:00 2001 From: Azizbek Khushvakov <113523904+azizbekxm@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:16:41 +0500 Subject: [PATCH] [MODFISTO-500] - Implement endpoint to return all finance data for FY (#432) * [MODFISTO-500] - Implement endpoint to return all finance data for FY * [MODFISTO-500] - Implement endpoint to return all finance data for FY * Removed unnecessary tests from TestEntities * Minor improvements * added to StorageTestSuite * remove drop part * Update acq-model to master branch --- descriptors/ModuleDescriptor-template.json | 19 +++++- ramls/acq-models | 2 +- ramls/finance-data.raml | 35 ++++++++++ .../org/folio/rest/impl/FinanceDataApi.java | 25 +++++++ .../db_scripts/all_finance_data_view.sql | 27 ++++++++ .../templates/db_scripts/schema.json | 5 ++ src/test/java/org/folio/StorageTestSuite.java | 4 ++ .../folio/rest/impl/FinanceDataApiTest.java | 68 +++++++++++++++++++ .../org/folio/rest/utils/TestEntities.java | 38 +++-------- 9 files changed, 193 insertions(+), 30 deletions(-) create mode 100644 ramls/finance-data.raml create mode 100644 src/main/java/org/folio/rest/impl/FinanceDataApi.java create mode 100644 src/main/resources/templates/db_scripts/all_finance_data_view.sql create mode 100644 src/test/java/org/folio/rest/impl/FinanceDataApiTest.java diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 72bbbe84..e2f8e4d3 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -462,6 +462,17 @@ } ] }, + { + "id": "finance-storage.finance-data", + "version": "1.0", + "handlers": [ + { + "methods": ["GET"], + "pathPattern": "/finance-storage/finance-data", + "permissionsRequired": ["finance-storage.finance-data.collection.get"] + } + ] + }, { "id" : "_tenant", "version" : "2.0", @@ -1005,6 +1016,11 @@ "finance-storage.fund-update-logs.item.delete" ] }, + { + "permissionName": "finance-storage.finance-data.collection.get", + "displayName": "all finance-data for fiscal year", + "description": "Get collection of finance data for particular fiscal year" + }, { "permissionName" : "finance.module.all", "displayName" : "All finance-module perms", @@ -1019,7 +1035,8 @@ "finance-storage.ledgers.all", "finance-storage.transactions.all", "finance-storage.fund-types.all", - "finance-storage.fund-update-logs.all" + "finance-storage.fund-update-logs.all", + "finance-storage.finance-data.collection.get" ] } ], diff --git a/ramls/acq-models b/ramls/acq-models index 680fe63a..1652e69e 160000 --- a/ramls/acq-models +++ b/ramls/acq-models @@ -1 +1 @@ -Subproject commit 680fe63a34fc7b25d9d59013a2758de76cf82b56 +Subproject commit 1652e69e9e98499b805b698e59efd7d2a51d98b5 diff --git a/ramls/finance-data.raml b/ramls/finance-data.raml new file mode 100644 index 00000000..bd605d0c --- /dev/null +++ b/ramls/finance-data.raml @@ -0,0 +1,35 @@ +#%RAML 1.0 +title: "mod-finance-storage" +baseUri: https://github.com/folio-org/mod-finance-storage +version: v1 + +documentation: + - title: mod-finance-storage (Funds) + content: CRUD APIs used to retrieve finance data for a fiscal year + +types: + errors: !include raml-util/schemas/errors.schema + fy-finance-data-collection: !include acq-models/mod-finance/schemas/fy_finance_data_collection.json + UUID: + type: string + pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$ + +traits: + pageable: !include raml-util/traits/pageable.raml + searchable: !include raml-util/traits/searchable.raml + validate: !include raml-util/traits/validation.raml + +resourceTypes: + collection-get: !include raml-util/rtypes/collection-get.raml + +/finance-storage/finance-data: + type: + collection-get: + exampleCollection: !include acq-models/mod-finance/examples/fy_finance_data_collection.sample + schemaCollection: fy-finance-data-collection + get: + description: Get finance data for a fiscal year + is: [ + searchable: { description: "with valid searchable fields: for example fiscalYearId", example: "[\"fiscalYearId\", \"7a4c4d30-3b63-4102-8e2d-3ee5792d7d02\", \"=\"]" }, + pageable + ] diff --git a/src/main/java/org/folio/rest/impl/FinanceDataApi.java b/src/main/java/org/folio/rest/impl/FinanceDataApi.java new file mode 100644 index 00000000..e0c9cd22 --- /dev/null +++ b/src/main/java/org/folio/rest/impl/FinanceDataApi.java @@ -0,0 +1,25 @@ +package org.folio.rest.impl; + + +import javax.ws.rs.core.Response; +import java.util.Map; + +import io.vertx.core.AsyncResult; +import io.vertx.core.Context; +import io.vertx.core.Handler; +import org.folio.rest.jaxrs.model.FyFinanceData; +import org.folio.rest.jaxrs.model.FyFinanceDataCollection; +import org.folio.rest.jaxrs.resource.FinanceStorageFinanceData; +import org.folio.rest.persist.PgUtil; + +public class FinanceDataApi implements FinanceStorageFinanceData { + private static final String FINANCE_DATA_VIEW = "finance_data_view"; + + @Override + public void getFinanceStorageFinanceData(String query, String totalRecords, int offset, int limit, Map okapiHeaders, + Handler> asyncResultHandler, Context vertxContext) { + PgUtil.get(FINANCE_DATA_VIEW, FyFinanceData.class, FyFinanceDataCollection.class, query, offset, limit, + okapiHeaders, vertxContext, GetFinanceStorageFinanceDataResponse.class, asyncResultHandler); + } + +} diff --git a/src/main/resources/templates/db_scripts/all_finance_data_view.sql b/src/main/resources/templates/db_scripts/all_finance_data_view.sql new file mode 100644 index 00000000..f40f7954 --- /dev/null +++ b/src/main/resources/templates/db_scripts/all_finance_data_view.sql @@ -0,0 +1,27 @@ +CREATE OR REPLACE VIEW ${myuniversity}_${mymodule}.finance_data_view AS +SELECT + fiscal_year.id as id, + jsonb_build_object( + 'fiscalYearId', fiscal_year.id, + 'fiscalYearCode', fiscal_year.jsonb ->>'code', + 'fundId', fund.id, + 'fundCode', fund.jsonb ->>'code', + 'fundName', fund.jsonb ->>'name', + 'fundDescription', fund.jsonb ->>'description', + 'fundStatus', fund.jsonb ->>'fundStatus', + 'fundTags', fund.jsonb ->'tags' -> 'tagList', + 'budgetId', budget.id, + 'budgetName', budget.jsonb ->>'name', + 'budgetStatus', budget.jsonb ->>'budgetStatus', + 'budgetInitialAllocation', budget.jsonb ->>'initialAllocation', + 'budgetCurrentAllocation', budget.jsonb ->>'allocated', + 'budgetAllowableExpenditure', budget.jsonb ->>'allowableExpenditure', + 'budgetAllowableEncumbrance', budget.jsonb ->>'allowableEncumbrance' + ) as jsonb +FROM ${myuniversity}_${mymodule}.fiscal_year +LEFT OUTER JOIN ${myuniversity}_${mymodule}.ledger + ON ledger.fiscalyearoneid = fiscal_year.id +LEFT OUTER JOIN ${myuniversity}_${mymodule}.fund + ON fund.ledgerid = ledger.id +LEFT OUTER JOIN ${myuniversity}_${mymodule}.budget + ON fund.id = budget.fundid; diff --git a/src/main/resources/templates/db_scripts/schema.json b/src/main/resources/templates/db_scripts/schema.json index feeb2605..1e92f710 100644 --- a/src/main/resources/templates/db_scripts/schema.json +++ b/src/main/resources/templates/db_scripts/schema.json @@ -95,6 +95,11 @@ "run": "after", "snippetPath": "migration/extract_credits_from_expenditures.sql", "fromModuleVersion": "mod-finance-storage-8.7.0" + }, + { + "run": "after", + "snippetPath": "all_finance_data_view.sql", + "fromModuleVersion": "mod-finance-storage-8.8.0" } ], "tables": [ diff --git a/src/test/java/org/folio/StorageTestSuite.java b/src/test/java/org/folio/StorageTestSuite.java index 82cdd2c6..6df6d059 100644 --- a/src/test/java/org/folio/StorageTestSuite.java +++ b/src/test/java/org/folio/StorageTestSuite.java @@ -22,6 +22,7 @@ import org.folio.rest.core.RestClientTest; import org.folio.rest.impl.BudgetTest; import org.folio.rest.impl.EntitiesCrudTest; +import org.folio.rest.impl.FinanceDataApiTest; import org.folio.rest.impl.GroupBudgetTest; import org.folio.rest.impl.GroupFundFYTest; import org.folio.rest.impl.GroupTest; @@ -216,4 +217,7 @@ class PaymentCreditTestNested extends PaymentCreditTest {} @Nested class PendingPaymentTestNested extends PendingPaymentTest {} + + @Nested + class FinanceDataApiTestNested extends FinanceDataApiTest {} } diff --git a/src/test/java/org/folio/rest/impl/FinanceDataApiTest.java b/src/test/java/org/folio/rest/impl/FinanceDataApiTest.java new file mode 100644 index 00000000..9491007f --- /dev/null +++ b/src/test/java/org/folio/rest/impl/FinanceDataApiTest.java @@ -0,0 +1,68 @@ +package org.folio.rest.impl; + +import static org.folio.rest.RestVerticle.OKAPI_HEADER_TENANT; +import static org.folio.rest.utils.TenantApiTestUtil.deleteTenant; +import static org.folio.rest.utils.TenantApiTestUtil.prepareTenant; +import static org.folio.rest.utils.TenantApiTestUtil.purge; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import io.restassured.http.Header; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.rest.jaxrs.model.FyFinanceDataCollection; +import org.folio.rest.jaxrs.model.TenantJob; +import org.folio.rest.jaxrs.resource.FinanceStorageFinanceData; +import org.folio.rest.persist.HelperUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + + +public class FinanceDataApiTest extends TestBase { + private static final String TENANT_NAME = "fyfinancedata"; + private static final Header TENANT_HEADER = new Header(OKAPI_HEADER_TENANT, TENANT_NAME); + private static final Logger logger = LogManager.getLogger(); + private static final String FINANCE_DATA_ENDPOINT = HelperUtils.getEndpoint(FinanceStorageFinanceData.class); + private static TenantJob tenantJob; + + @BeforeAll + public static void before() { + logger.info("Create a new tenant loading the sample data"); + tenantJob = prepareTenant(TENANT_HEADER, true, true); + } + + @AfterAll + public static void after() { + logger.info("Delete the created \"fyfinancedata\" tenant"); + purge(TENANT_HEADER); + deleteTenant(tenantJob, TENANT_HEADER); + } + + @Test + public void positive_testGetQuery() { + verifyCollectionQuantity(FINANCE_DATA_ENDPOINT + "?query=(fiscalYearId==7a4c4d30-3b63-4102-8e2d-3ee5792d7d02)", 21, TENANT_HEADER); + verifyCollectionQuantity(FINANCE_DATA_ENDPOINT + "?query=(fiscalYearId==9b1d00d1-1f3d-4f1c-8e4b-0f1e3b7b1b1b)", 0, TENANT_HEADER); + } + + @Test + public void positive_testResponseOfGet() { + var response = getData(FINANCE_DATA_ENDPOINT + "?query=(fiscalYearId==7a4c4d30-3b63-4102-8e2d-3ee5792d7d02)", TENANT_HEADER); + var body = response.getBody().as(FyFinanceDataCollection.class); + + var fyFinanceDataExample = body.getFyFinanceData().get(0); + assertEquals("7a4c4d30-3b63-4102-8e2d-3ee5792d7d02", fyFinanceDataExample.getFiscalYearId()); + assertNotNull(fyFinanceDataExample.getFundId()); + assertNotNull(fyFinanceDataExample.getFundCode()); + assertNotNull(fyFinanceDataExample.getFundName()); + assertNotNull(fyFinanceDataExample.getFundDescription()); + assertNotNull(fyFinanceDataExample.getFundStatus()); + assertNotNull(fyFinanceDataExample.getBudgetId()); + assertNotNull(fyFinanceDataExample.getBudgetName()); + assertNotNull(fyFinanceDataExample.getBudgetStatus()); + assertNotNull(fyFinanceDataExample.getBudgetInitialAllocation()); + assertNotNull(fyFinanceDataExample.getBudgetCurrentAllocation()); + assertNotNull(fyFinanceDataExample.getBudgetAllowableExpenditure()); + assertNotNull(fyFinanceDataExample.getBudgetAllowableEncumbrance()); + } +} diff --git a/src/test/java/org/folio/rest/utils/TestEntities.java b/src/test/java/org/folio/rest/utils/TestEntities.java index 0f180f16..f480cfd8 100644 --- a/src/test/java/org/folio/rest/utils/TestEntities.java +++ b/src/test/java/org/folio/rest/utils/TestEntities.java @@ -1,9 +1,11 @@ package org.folio.rest.utils; +import lombok.Getter; import org.folio.rest.jaxrs.model.*; import org.folio.rest.jaxrs.resource.*; import org.folio.rest.persist.HelperUtils; +@Getter public enum TestEntities { //The Order is important because of the foreign key relationships EXPENSE_CLASS(HelperUtils.getEndpoint(FinanceStorageExpenseClasses.class), ExpenseClass.class, "data/expense-classes-4.0.0/", "elec.json", "name", "Electronic", 2, true), @@ -33,44 +35,24 @@ public enum TestEntities { this.isOptLockingEnabled = isOptLockingEnabled; } - private int initialQuantity; - private String endpoint; - private String sampleFileName; + private final int initialQuantity; + private final String endpoint; + private final String sampleFileName; private String sampleId; - private String pathToSamples; - private String updatedFieldName; - private String updatedFieldValue; - private Class clazz; - private boolean isOptLockingEnabled; - - public String getEndpoint() { - return endpoint; - } + private final String pathToSamples; + private final String updatedFieldName; + private final String updatedFieldValue; + private final Class clazz; + private final boolean isOptLockingEnabled; public String getEndpointWithId() { return endpoint + "/{id}"; } - public String getUpdatedFieldName() { - return updatedFieldName; - } - - public String getUpdatedFieldValue() { - return updatedFieldValue; - } - - public int getInitialQuantity() { - return initialQuantity; - } - public String getSampleFileName() { return pathToSamples + sampleFileName; } - public Class getClazz() { - return clazz; - } - public String getPathToSampleFile() { return pathToSamples + sampleFileName; }