diff --git a/src/main/java/dev/sorn/fmp4j/clients/FmpBulkClient.java b/src/main/java/dev/sorn/fmp4j/clients/FmpBulkClient.java index 57a1313..d4e8763 100644 --- a/src/main/java/dev/sorn/fmp4j/clients/FmpBulkClient.java +++ b/src/main/java/dev/sorn/fmp4j/clients/FmpBulkClient.java @@ -4,9 +4,11 @@ import dev.sorn.fmp4j.http.FmpHttpClient; import dev.sorn.fmp4j.models.FmpBalanceSheetStatement; import dev.sorn.fmp4j.models.FmpCashFlowStatement; +import dev.sorn.fmp4j.models.FmpCashFlowStatementGrowth; import dev.sorn.fmp4j.models.FmpCompanies; import dev.sorn.fmp4j.services.FmpBulkBalanceSheetStatementService; import dev.sorn.fmp4j.services.FmpBulkCashFlowStatementService; +import dev.sorn.fmp4j.services.FmpBulkCashFlowStatementGrowthService; import dev.sorn.fmp4j.services.FmpBulkCompaniesService; import dev.sorn.fmp4j.services.FmpService; import dev.sorn.fmp4j.types.FmpPart; @@ -19,11 +21,14 @@ public class FmpBulkClient { protected final FmpService fmpBulkCompaniesService; protected final FmpService fmpBulkBalanceSheetService; protected final FmpService fmpBulkCashFlowService; + protected final FmpService fmpBulkCashFlowStatementGrowthService; public FmpBulkClient(FmpConfig fmpConfig, FmpHttpClient fmpHttpClient) { this.fmpBulkCompaniesService = new FmpBulkCompaniesService(fmpConfig, fmpHttpClient); this.fmpBulkBalanceSheetService = new FmpBulkBalanceSheetStatementService(fmpConfig, fmpHttpClient); this.fmpBulkCashFlowService = new FmpBulkCashFlowStatementService(fmpConfig, fmpHttpClient); + this.fmpBulkCashFlowStatementGrowthService = + new FmpBulkCashFlowStatementGrowthService(fmpConfig, fmpHttpClient); } public synchronized FmpCompanies[] companies(FmpPart part) { @@ -36,11 +41,16 @@ public synchronized FmpBalanceSheetStatement[] balanceSheetStatements(FmpYear ye fmpBulkBalanceSheetService.param("period", period); return fmpBulkBalanceSheetService.download(); } - + public synchronized FmpCashFlowStatement[] cashFlowStatements(FmpYear year, FmpPeriod period) { fmpBulkCashFlowService.param("year", year); fmpBulkCashFlowService.param("period", period); - return fmpBulkCashFlowService.download(); } + + public synchronized FmpCashFlowStatementGrowth[] cashFlowStatementGrowth(FmpYear year, FmpPeriod period) { + fmpBulkCashFlowStatementGrowthService.param("year", year); + fmpBulkCashFlowStatementGrowthService.param("period", period); + return fmpBulkCashFlowStatementGrowthService.download(); + } } diff --git a/src/main/java/dev/sorn/fmp4j/services/FmpBulkCashFlowStatementGrowthService.java b/src/main/java/dev/sorn/fmp4j/services/FmpBulkCashFlowStatementGrowthService.java new file mode 100644 index 0000000..94b054f --- /dev/null +++ b/src/main/java/dev/sorn/fmp4j/services/FmpBulkCashFlowStatementGrowthService.java @@ -0,0 +1,36 @@ +package dev.sorn.fmp4j.services; + +import static dev.sorn.fmp4j.json.FmpJsonUtils.typeRef; + +import dev.sorn.fmp4j.cfg.FmpConfig; +import dev.sorn.fmp4j.http.FmpHttpClient; +import dev.sorn.fmp4j.models.FmpCashFlowStatementGrowth; +import dev.sorn.fmp4j.types.FmpPeriod; +import dev.sorn.fmp4j.types.FmpYear; +import java.util.Map; + +public class FmpBulkCashFlowStatementGrowthService extends FmpService { + public FmpBulkCashFlowStatementGrowthService(FmpConfig cfg, FmpHttpClient http) { + super(cfg, http, typeRef(FmpCashFlowStatementGrowth[].class)); + } + + @Override + protected String relativeUrl() { + return "/cash-flow-statement-growth-bulk"; + } + + @Override + protected Map> requiredParams() { + return Map.of("year", FmpYear.class, "period", FmpPeriod.class); + } + + @Override + protected Map> optionalParams() { + return Map.of(); + } + + @Override + protected Map headers() { + return Map.of("Content-Type", "text/csv"); + } +} diff --git a/src/test/java/dev/sorn/fmp4j/FmpClientTest.java b/src/test/java/dev/sorn/fmp4j/FmpClientTest.java index b56161b..d6d444d 100644 --- a/src/test/java/dev/sorn/fmp4j/FmpClientTest.java +++ b/src/test/java/dev/sorn/fmp4j/FmpClientTest.java @@ -181,6 +181,27 @@ void searchByName() { assertValidResult(result, 5, FmpSearchByName.class); } + @ParameterizedTest + @ValueSource(strings = {"quarter"}) + void cashFlowStatementGrowthBulk(String p) { + // given + var year = year("2025"); + var period = period(p); + var typeRef = typeRef(FmpCashFlowStatementGrowth[].class); + var endpoint = "cash-flow-statement-growth-bulk"; + var uri = buildUri(endpoint); + var headers = Map.of("Content-Type", "text/csv"); + var params = buildParams(Map.of("year", year, "period", period)); + var file = format("stable/%s/?year=%s&period=%s.csv", endpoint, year, period); + + // when + mockHttpGet(uri, headers, params, file, typeRef); + var result = fmpClient.bulk().cashFlowStatementGrowth(year, period); + + // then + assertValidResult(result, 1, FmpCashFlowStatementGrowth.class); + } + @Test void searchByCusip() { // given diff --git a/src/test/java/dev/sorn/fmp4j/services/FmpBulkCashFlowStatementGrowthServiceTest.java b/src/test/java/dev/sorn/fmp4j/services/FmpBulkCashFlowStatementGrowthServiceTest.java new file mode 100644 index 0000000..9068c64 --- /dev/null +++ b/src/test/java/dev/sorn/fmp4j/services/FmpBulkCashFlowStatementGrowthServiceTest.java @@ -0,0 +1,74 @@ +package dev.sorn.fmp4j.services; + +import static dev.sorn.fmp4j.HttpClientStub.httpClientStub; +import static dev.sorn.fmp4j.TestUtils.assertAllFieldsNonNull; +import static dev.sorn.fmp4j.TestUtils.testResource; +import static dev.sorn.fmp4j.json.FmpJsonDeserializer.FMP_JSON_DESERIALIZER; +import static dev.sorn.fmp4j.types.FmpPeriod.period; +import static dev.sorn.fmp4j.types.FmpYear.year; +import static java.util.stream.IntStream.range; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import dev.sorn.fmp4j.HttpClientStub; +import dev.sorn.fmp4j.cfg.FmpConfigImpl; +import dev.sorn.fmp4j.http.FmpHttpClient; +import dev.sorn.fmp4j.http.FmpHttpClientImpl; +import dev.sorn.fmp4j.models.FmpCashFlowStatementGrowth; +import dev.sorn.fmp4j.types.FmpPeriod; +import dev.sorn.fmp4j.types.FmpYear; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class FmpBulkCashFlowStatementGrowthServiceTest { + private final HttpClientStub httpStub = httpClientStub(); + private final FmpHttpClient http = new FmpHttpClientImpl(httpStub, FMP_JSON_DESERIALIZER); + private final FmpService service = + new FmpBulkCashFlowStatementGrowthService(new FmpConfigImpl(), http); + + @Test + void relative_url() { + // when + var relativeUrl = service.relativeUrl(); + + // then + assertEquals("/cash-flow-statement-growth-bulk", relativeUrl); + } + + @Test + void required_params() { + // when + var params = service.requiredParams(); + + // then + assertEquals(Map.of("year", FmpYear.class, "period", FmpPeriod.class), params); + } + + @Test + void optional_params() { + // when + var params = service.optionalParams(); + + // then + assertEquals(Map.of(), params); + } + + @Test + void successful_download() { + // given + var year = year("2025"); + var period = period("quarter"); + service.param("year", year); + service.param("period", period); + httpStub.configureResponse() + .body(testResource("stable/cash-flow-statement-growth-bulk/?year=%s&period=%s.csv", year, period)) + .statusCode(200) + .apply(); + + // when + var result = service.download(); + + // then + assertEquals(1, result.length); + range(0, result.length).forEach(i -> assertAllFieldsNonNull(result[i])); + } +} diff --git a/src/testFixtures/resources/stable/cash-flow-statement-growth-bulk/%3Fyear=2025&period=quarter.csv b/src/testFixtures/resources/stable/cash-flow-statement-growth-bulk/%3Fyear=2025&period=quarter.csv new file mode 100644 index 0000000..d845ab3 --- /dev/null +++ b/src/testFixtures/resources/stable/cash-flow-statement-growth-bulk/%3Fyear=2025&period=quarter.csv @@ -0,0 +1,2 @@ +symbol, date, fiscalYear, period, reportedCurrency, growthNetIncome, growthDepreciationAndAmortization, growthDeferredIncomeTax, growthStockBasedCompensation, growthChangeInWorkingCapital, growthAccountsReceivables, growthInventory, growthAccountsPayables, growthOtherWorkingCapital, growthOtherNonCashItems, growthNetCashProvidedByOperatingActivites, growthInvestmentsInPropertyPlantAndEquipment, growthAcquisitionsNet, growthPurchasesOfInvestments, growthSalesMaturitiesOfInvestments, growthOtherInvestingActivites, growthNetCashUsedForInvestingActivites, growthDebtRepayment, growthCommonStockIssued, growthCommonStockRepurchased, growthDividendsPaid, growthOtherFinancingActivites, growthNetCashUsedProvidedByFinancingActivities, growthEffectOfForexChangesOnCash, growthNetChangeInCash, growthCashAtEndOfPeriod, growthCashAtBeginningOfPeriod, growthOperatingCashFlow, growthCapitalExpenditure, growthFreeCashFlow, growthNetDebtIssuance, growthLongTermNetDebtIssuance, growthShortTermNetDebtIssuance, growthNetStockIssuance, growthPreferredDividendsPaid, growthIncomeTaxesPaid, growthInterestPaid +"000001.SZ","2025-03-31","2025","Q1","CNY","0","0","0","0","0","0","0","0","0","3.2072823819457614","3.2072823819457614","0.7332280978689818","0","-0.12254537395030414","0.3847853673478318","-0.8417721518987342","2.1699343339587243","1","0","0","0.6798284344644885","-1.7077146619443309","-3.2122934677858628","-1.0731570061902083","2.348938711752274","0.11426914604625096","-0.07809495106059301","3.2072823819457614","0.7332280978689818","3.16553689621649","1","1","0","0","0.6798284344644885","0","0",