diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg
index 34ea3cb..0a979ea 100644
--- a/.github/badges/jacoco.svg
+++ b/.github/badges/jacoco.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/main/java/ca/bc/gov/backendstartapi/endpoint/FundingSourceEndpoint.java b/src/main/java/ca/bc/gov/backendstartapi/endpoint/FundingSourceEndpoint.java
new file mode 100644
index 0000000..6fcfb53
--- /dev/null
+++ b/src/main/java/ca/bc/gov/backendstartapi/endpoint/FundingSourceEndpoint.java
@@ -0,0 +1,67 @@
+package ca.bc.gov.backendstartapi.endpoint;
+
+import ca.bc.gov.backendstartapi.entity.FundingSource;
+import ca.bc.gov.backendstartapi.repository.FundingSourceRepository;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.time.LocalDate;
+import java.time.temporal.TemporalUnit;
+import java.util.List;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/** This class exposes funding sources resources API. */
+@Setter
+@RestController
+@NoArgsConstructor
+@RequestMapping("/api/funding-sources")
+@Tag(
+ name = "FundingSourceEndpoint",
+ description = "Resource to retrieve Funding Source to Owners Agencies")
+public class FundingSourceEndpoint {
+
+ private FundingSourceRepository fundingSourceRepository;
+
+ @Autowired
+ public FundingSourceEndpoint(FundingSourceRepository fundingSourceRepository) {
+ this.fundingSourceRepository = fundingSourceRepository;
+ }
+
+ /**
+ * Retrieve all funding sources.
+ *
+ * @return A list of {@link FundingSource} with all found result.
+ */
+ @GetMapping(produces = "application/json")
+ @PreAuthorize("hasRole('user_read')")
+ @Operation(
+ summary = "Retrieve non-expired funding sources",
+ description = "Retrieve all valid (non expired) funding source based on effectiveDate "
+ + "and expiryDate, where 'today >= effectiveDate' and 'today < expiryDate'.")
+ @ApiResponses(
+ value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "Returns a list containing all valid (non expired) funding sources",
+ content =
+ @Content(
+ mediaType = "application/json",
+ schema = @Schema(implementation = FundingSource.class))),
+ @ApiResponse(
+ responseCode = "401",
+ description = "Access token is missing or invalid",
+ content = @Content(schema = @Schema(implementation = Void.class)))
+ })
+ public List getAllValidFundingSources() {
+ return fundingSourceRepository.findAllValid();
+ }
+}
diff --git a/src/main/java/ca/bc/gov/backendstartapi/entity/FundingSource.java b/src/main/java/ca/bc/gov/backendstartapi/entity/FundingSource.java
new file mode 100644
index 0000000..3e2a600
--- /dev/null
+++ b/src/main/java/ca/bc/gov/backendstartapi/entity/FundingSource.java
@@ -0,0 +1,44 @@
+package ca.bc.gov.backendstartapi.entity;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import java.time.LocalDate;
+import lombok.Getter;
+import lombok.Setter;
+
+/** This class presents a funding source to an agency seedlot owner. */
+@Getter
+@Setter
+@Entity
+@Table(name = "SPAR_FUND_SRCE_CODE")
+@Schema(description = "Represents a Funding Source object in the database")
+public class FundingSource {
+
+ @Id
+ @Column(name = "SPAR_FUND_SRCE_CODE")
+ @Schema(description = "Funding source's code, from SPAR_FUND_SRCE_CODE column", example = "BCT")
+ private String code;
+
+ @Column(name = "DESCRIPTION")
+ @Schema(
+ description = "Funding source's description, from DESCRIPTION column",
+ example = "BC Timber Sales")
+ private String description;
+
+ @Column(name = "EFFECTIVE_DATE")
+ @Schema(
+ description = "Funding source's effective date.",
+ type = "string",
+ format = "date")
+ private LocalDate effectiveDate;
+
+ @Column(name = "EXPIRY_DATE")
+ @Schema(
+ description = "Funding source's expiry date.",
+ type = "string",
+ format = "date")
+ private LocalDate expiryDate;
+}
diff --git a/src/main/java/ca/bc/gov/backendstartapi/repository/FundingSourceRepository.java b/src/main/java/ca/bc/gov/backendstartapi/repository/FundingSourceRepository.java
new file mode 100644
index 0000000..5cf6a2f
--- /dev/null
+++ b/src/main/java/ca/bc/gov/backendstartapi/repository/FundingSourceRepository.java
@@ -0,0 +1,16 @@
+package ca.bc.gov.backendstartapi.repository;
+
+import ca.bc.gov.backendstartapi.entity.FundingSource;
+import java.util.List;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+/** This interface enables the funding source entity to be retrieved from the database. */
+public interface FundingSourceRepository extends JpaRepository {
+
+ @Query(
+ value =
+ "select fs from FundingSource fs WHERE CURRENT_DATE >= fs.effectiveDate "
+ + "AND CURRENT_DATE < fs.expiryDate ORDER BY fs.code")
+ List findAllValid();
+}
diff --git a/src/test/java/ca/bc/gov/backendstartapi/endpoint/FundingSourceEndpointTest.java b/src/test/java/ca/bc/gov/backendstartapi/endpoint/FundingSourceEndpointTest.java
new file mode 100644
index 0000000..d374c82
--- /dev/null
+++ b/src/test/java/ca/bc/gov/backendstartapi/endpoint/FundingSourceEndpointTest.java
@@ -0,0 +1,97 @@
+package ca.bc.gov.backendstartapi.endpoint;
+
+import static org.mockito.Mockito.when;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import ca.bc.gov.backendstartapi.entity.FundingSource;
+import ca.bc.gov.backendstartapi.repository.FundingSourceRepository;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.web.servlet.MockMvc;
+
+@ExtendWith(SpringExtension.class)
+@WebMvcTest(FundingSourceEndpoint.class)
+class FundingSourceEndpointTest {
+
+ @Autowired private MockMvc mockMvc;
+
+ @MockBean private FundingSourceRepository fundingSourceRepository;
+
+ @Test
+ @DisplayName("findAllSuccessTest")
+ @WithMockUser(roles = "user_read")
+ void findAllSuccessTest() throws Exception {
+ FundingSource fundingSourceBct = new FundingSource();
+ fundingSourceBct.setCode("BCT");
+ fundingSourceBct.setDescription("BC Timber Sales");
+ fundingSourceBct.setEffectiveDate(LocalDate.parse("2003-04-01"));
+ fundingSourceBct.setExpiryDate(LocalDate.parse("9999-12-31"));
+
+ FundingSource fundingSourceCbi = new FundingSource();
+ fundingSourceCbi.setCode("CBI");
+ fundingSourceCbi.setDescription("Carbon Offset Investment");
+ fundingSourceCbi.setEffectiveDate(LocalDate.parse("2013-08-01"));
+ fundingSourceCbi.setExpiryDate(LocalDate.parse("9999-12-31"));
+
+ FundingSource fundingSourceCl = new FundingSource();
+ fundingSourceCl.setCode("CL");
+ fundingSourceCl.setDescription("Catastrophic Losses");
+ fundingSourceCl.setEffectiveDate(LocalDate.parse("1905-01-01"));
+ fundingSourceCl.setExpiryDate(LocalDate.parse("2099-09-30"));
+
+ List sources = new ArrayList<>();
+ sources.add(fundingSourceBct);
+ sources.add(fundingSourceCbi);
+ sources.add(fundingSourceCl);
+
+ when(fundingSourceRepository.findAllValid()).thenReturn(sources);
+
+ mockMvc
+ .perform(
+ get("/api/funding-sources")
+ .with(csrf().asHeader())
+ .header("Content-Type", "application/json")
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$[0].code").value("BCT"))
+ .andExpect(jsonPath("$[0].description").value("BC Timber Sales"))
+ .andExpect(jsonPath("$[0].effectiveDate").value("2003-04-01"))
+ .andExpect(jsonPath("$[0].expiryDate").value("9999-12-31"))
+ .andExpect(jsonPath("$[1].code").value("CBI"))
+ .andExpect(jsonPath("$[1].description").value("Carbon Offset Investment"))
+ .andExpect(jsonPath("$[1].effectiveDate").value("2013-08-01"))
+ .andExpect(jsonPath("$[1].expiryDate").value("9999-12-31"))
+ .andExpect(jsonPath("$[2].code").value("CL"))
+ .andExpect(jsonPath("$[2].description").value("Catastrophic Losses"))
+ .andExpect(jsonPath("$[2].effectiveDate").value("1905-01-01"))
+ .andExpect(jsonPath("$[2].expiryDate").value("2099-09-30"))
+ .andReturn();
+ }
+
+ @Test
+ @DisplayName("findAllNoAuthorizedTest")
+ void findAllNoAuthorizedTest() throws Exception {
+ mockMvc
+ .perform(
+ get("/api/funding-sources")
+ .with(csrf().asHeader())
+ .header("Content-Type", "application/json")
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().is(401))
+ .andReturn();
+ }
+
+}
diff --git a/src/test/java/ca/bc/gov/backendstartapi/repository/FundingSourceRepositoryTest.java b/src/test/java/ca/bc/gov/backendstartapi/repository/FundingSourceRepositoryTest.java
new file mode 100644
index 0000000..1841298
--- /dev/null
+++ b/src/test/java/ca/bc/gov/backendstartapi/repository/FundingSourceRepositoryTest.java
@@ -0,0 +1,60 @@
+package ca.bc.gov.backendstartapi.repository;
+
+import ca.bc.gov.backendstartapi.entity.FundingSource;
+import java.time.LocalDate;
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
+import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.test.context.jdbc.Sql;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@DataJpaTest
+@AutoConfigureTestDatabase(replace = Replace.NONE)
+class FundingSourceRepositoryTest {
+
+ @Autowired private FundingSourceRepository fundingSourceRepository;
+
+ private boolean isValid(FundingSource fundingSource) {
+ LocalDate today = LocalDate.now();
+
+ // Effective date - Should be before or same as today
+ if (fundingSource.getEffectiveDate().isAfter(today)) {
+ return false;
+ }
+
+ // Expiry date - Should be after today
+ return fundingSource.getExpiryDate().isAfter(today);
+ }
+
+ @Test
+ @DisplayName("findAllTest")
+ @Sql(scripts = {"classpath:scripts/FundingSourceRepositoryTest_findAllTest.sql"})
+ void findAllTest() {
+ List sources = fundingSourceRepository.findAllValid();
+
+ Assertions.assertFalse(sources.isEmpty());
+ Assertions.assertEquals(3, sources.size());
+
+ FundingSource fundingSourceBct = sources.get(0);
+ Assertions.assertEquals("BCT", fundingSourceBct.getCode());
+ Assertions.assertEquals("BC Timber Sales", fundingSourceBct.getDescription());
+ Assertions.assertTrue(isValid(fundingSourceBct));
+
+ FundingSource fundingSourceCbi = sources.get(1);
+ Assertions.assertEquals("CBI", fundingSourceCbi.getCode());
+ Assertions.assertEquals("Carbon Offset Investment", fundingSourceCbi.getDescription());
+ Assertions.assertTrue(isValid(fundingSourceCbi));
+
+ FundingSource fundingSourceCl = sources.get(2);
+ Assertions.assertEquals("CL", fundingSourceCl.getCode());
+ Assertions.assertEquals("Catastrophic Losses", fundingSourceCl.getDescription());
+ Assertions.assertTrue(isValid(fundingSourceCl));
+ }
+}
diff --git a/src/test/resources/scripts/FundingSourceRepositoryTest_findAllTest.sql b/src/test/resources/scripts/FundingSourceRepositoryTest_findAllTest.sql
new file mode 100644
index 0000000..035b9b9
--- /dev/null
+++ b/src/test/resources/scripts/FundingSourceRepositoryTest_findAllTest.sql
@@ -0,0 +1,13 @@
+INSERT INTO SPAR_FUND_SRCE_CODE (SPAR_FUND_SRCE_CODE, DESCRIPTION, EFFECTIVE_DATE, EXPIRY_DATE)
+ VALUES ('BCT', 'BC Timber Sales', '2003-04-01', '9999-12-31');
+INSERT INTO SPAR_FUND_SRCE_CODE (SPAR_FUND_SRCE_CODE, DESCRIPTION, EFFECTIVE_DATE, EXPIRY_DATE)
+ VALUES ('CBI', 'Carbon Offset Investment', '2013-08-01', '9999-12-31');
+INSERT INTO SPAR_FUND_SRCE_CODE (SPAR_FUND_SRCE_CODE, DESCRIPTION, EFFECTIVE_DATE, EXPIRY_DATE)
+ VALUES ('CL', 'Catastrophic Losses', '1905-01-01', '2099-09-30');
+
+-- Not effective yet
+INSERT INTO SPAR_FUND_SRCE_CODE (SPAR_FUND_SRCE_CODE, DESCRIPTION, EFFECTIVE_DATE, EXPIRY_DATE)
+ VALUES ('CI', 'Catastrophic Issues', '2033-01-01', '2099-09-30');
+-- Expired
+INSERT INTO SPAR_FUND_SRCE_CODE (SPAR_FUND_SRCE_CODE, DESCRIPTION, EFFECTIVE_DATE, EXPIRY_DATE)
+ VALUES ('CO', 'Catastrophic Others', '2002-01-01', '2022-09-30');
\ No newline at end of file