Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/maven/cucumber.version-7.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
pinsondg authored Mar 20, 2023
2 parents 3a4d5e4 + 2b34314 commit 76bf415
Show file tree
Hide file tree
Showing 84 changed files with 15,538 additions and 224 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ jobs:
build_deploy:

runs-on: ubuntu-latest
env:
TEST_BUCKET_NAME: ${{ secrets.TEST_BUCKET_NAME }}
TEST_KEY_OPTION_CSV: ${{ secrets.TEST_KEY_OPTION_CSV }}
TEST_KEY_FEAR_GREED_JSON: ${{ secrets.TEST_KEY_FEAR_GREED_JSON }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

steps:
- name: Set Timezone
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ target/
.sts4-cache

### IntelliJ IDEA ###
.run
.idea
*.iws
*.iml
Expand All @@ -36,4 +37,7 @@ build/
### Local DBs ###
*.db

venv
venv

src/main/resources/data/fear-greed-files/
fear-greed-json/
44 changes: 35 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<version>2.6.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dpgrandslam</groupId>
Expand All @@ -15,15 +15,28 @@
<description>MicroService for getting stock and options data</description>
<properties>
<java.version>11</java.version>
<feign.version>11.7</feign.version>
<jsoup.version>1.14.3</jsoup.version>
<feign.version>11.8</feign.version>
<jsoup.version>1.15.3</jsoup.version>
<jasypt.version>1.9.3</jasypt.version>
<caffine.version>3.0.4</caffine.version>
<caffine.version>3.0.6</caffine.version>
<validation-api.version>2.0.1.Final</validation-api.version>
<liquibase-hibernate5.version>3.10.2</liquibase-hibernate5.version>
<cucumber.version>7.0.0</cucumber.version>
<jacoco.line-coverage>0.80</jacoco.line-coverage>
<log4j2.version>2.15.0</log4j2.version>
<spring-cloud-aws.version>2.2.6.RELEASE</spring-cloud-aws.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-bom</artifactId>
<version>1.12.186</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand All @@ -45,6 +58,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
Expand Down Expand Up @@ -172,9 +189,14 @@
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.9.6</version>
<version>3.10.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</dependency>

</dependencies>
<build>
<plugins>
Expand All @@ -201,6 +223,9 @@
<goal>check</goal>
</goals>
<configuration>
<excludes>
<exclude>com/dpgrandslam/stockdataservice/domain/util/*</exclude>
</excludes>
<rules>
<rule>
<element>PACKAGE</element>
Expand All @@ -216,6 +241,7 @@
<excludes>
<exclude>**/util*</exclude>
<exclude>**/tiingo*</exclude>
<exclude>**/error*</exclude>
</excludes>
</configuration>
</execution>
Expand Down Expand Up @@ -260,17 +286,17 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.13</version>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.6.0</version>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.29</version>
<version>1.33</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
Expand All @@ -291,7 +317,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>3.0.0-M5</version>
<version>3.0.0-M7</version>
</plugin>
</plugins>
</reporting>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.dpgrandslam.stockdataservice.adapter.api;

import com.dpgrandslam.stockdataservice.domain.model.JobRunRequest;
import com.dpgrandslam.stockdataservice.domain.model.JobRunResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.*;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Controller
@RequestMapping("/job")
public class JobController {

@Autowired
private JobLauncher jobLauncher;

@Autowired
private List<Job> batchJobs;

@Autowired
private JobExplorer jobExplorer;

@PostMapping("/run")
public ResponseEntity<JobRunResponse> runOptionCSVLoadJob(@RequestBody JobRunRequest runRequest) throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
Map<String, JobParameter> jobParameterMap = runRequest.getJobParams().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, x -> new JobParameter(x.getValue())));
Optional<Job> jobToRun = batchJobs.stream().filter(job -> job.getName().equals(runRequest.getJobName())).findFirst();
if (jobToRun.isEmpty()) {
return ResponseEntity.badRequest().build();
}
log.info("Batch job {} started through API call.", runRequest.getJobName());
JobExecution jobExecution = jobLauncher.run(jobToRun.get(), new JobParameters(jobParameterMap));
JobRunResponse jobRunResponse = new JobRunResponse();
jobRunResponse.setJobId(jobExecution.getJobId());
jobRunResponse.setJobExecutionId(jobExecution.getId());
jobRunResponse.setJobStatus(jobExecution.getStatus().name());
return ResponseEntity.ok(jobRunResponse);
}

@GetMapping("/status")
public ResponseEntity<JobRunResponse> getJobStatus(@RequestParam Long executionId) {
JobRunResponse jobRunResponse = new JobRunResponse();
JobExecution jobExecution = jobExplorer.getJobExecution(executionId);
jobRunResponse.setJobStatus(jobExecution.getStatus().name());
String message = jobExecution.getAllFailureExceptions().stream().findFirst().map(Throwable::getMessage).orElse(null);
jobRunResponse.setMessage(message);
jobRunResponse.setJobId(jobExecution.getJobId());
jobRunResponse.setJobExecutionId(jobExecution.getId());
return ResponseEntity.ok(jobRunResponse);
}

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package com.dpgrandslam.stockdataservice.adapter.api;

import com.dpgrandslam.stockdataservice.domain.error.OptionsChainLoadException;
import com.dpgrandslam.stockdataservice.domain.model.FearGreedIndex;
import com.dpgrandslam.stockdataservice.domain.model.options.Option;
import com.dpgrandslam.stockdataservice.domain.model.options.OptionsChain;
import com.dpgrandslam.stockdataservice.domain.model.stock.*;
import com.dpgrandslam.stockdataservice.domain.service.OptionsChainLoadService;
import com.dpgrandslam.stockdataservice.domain.service.StockDataLoadService;
import com.dpgrandslam.stockdataservice.domain.service.TenYearTreasuryYieldService;
import com.dpgrandslam.stockdataservice.domain.service.TrackedStockService;
import com.dpgrandslam.stockdataservice.domain.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.jni.Local;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -19,7 +18,9 @@

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

@CrossOrigin(origins = "http://localhost:3000")
@Controller
@RequestMapping("/data")
@Slf4j
Expand All @@ -39,6 +40,13 @@ public class StockDataServiceController {
@Autowired
private TenYearTreasuryYieldService treasuryYieldService;

@Autowired
@Qualifier("CNNFearGreedDataLoadAPIService")
private FearGreedDataLoadService fearGreedDataLoadService;

@Autowired
private VIXLoadService vixLoadService;

@GetMapping("/option/{ticker}")
public ResponseEntity<List<OptionsChain>> getOptionsChain(@PathVariable(name = "ticker") String ticker,
@RequestParam(name = "expirationDate") Optional<String> expirationDate,
Expand Down Expand Up @@ -125,8 +133,30 @@ public ResponseEntity addTrackedStocks(@RequestBody List<String> tickers) {
}

@GetMapping("/treasury-yield")
public ResponseEntity<YahooFinanceTenYearTreasuryYield> getTreasuryYield(@RequestParam Optional<String> date) {
return ResponseEntity.ok(treasuryYieldService.getTreasuryYieldForDate(date.map(LocalDate::parse)
.orElse(LocalDate.now())));
public ResponseEntity<List<YahooFinanceQuote>> getTreasuryYield(@RequestParam String startDate, @RequestParam Optional<String> endDate) {
return ResponseEntity.ok(treasuryYieldService.getTreasuryYieldForDate(LocalDate.parse(startDate),
endDate.map(LocalDate::parse).orElse(LocalDate.now())));
}

@GetMapping("/fear-greed")
public ResponseEntity<List<FearGreedIndex>> getFearGreedIndexBetweenDates(@RequestParam Optional<String> startDate,
@RequestParam Optional<String> endDate) {
LocalDate sd = startDate.map(LocalDate::parse).orElse(LocalDate.now());
LocalDate ed = endDate.map(LocalDate::parse).orElse(LocalDate.now());

if (sd.equals(LocalDate.now()) && ed.equals(LocalDate.now())) {
return ResponseEntity.ok(fearGreedDataLoadService.loadCurrentFearGreedIndex().stream()
.sorted(Comparator.comparing(FearGreedIndex::getTradeDate))
.collect(Collectors.toList()));
} else if (sd.equals(ed)) {
Optional<FearGreedIndex> fgIndex = fearGreedDataLoadService.getFearGreedIndexOfDay(sd);
return fgIndex.map(fearGreedIndex -> ResponseEntity.ok(Collections.singletonList(fearGreedIndex))).orElseGet(() -> ResponseEntity.notFound().build());
}
return ResponseEntity.ok(fearGreedDataLoadService.loadFearGreedDataBetweenDates(sd, ed));
}

@GetMapping("/vix")
public ResponseEntity<List<YahooFinanceQuote>> getVixForDates(@RequestParam String startDate, @RequestParam Optional<String> endDate) {
return ResponseEntity.ok(vixLoadService.loadVIXBetweenDates(LocalDate.parse(startDate), endDate.map(LocalDate::parse).orElse(LocalDate.parse(startDate))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.dpgrandslam.stockdataservice.adapter.apiclient;

import com.dpgrandslam.stockdataservice.domain.model.CNNFearGreedResponse;
import feign.Headers;
import feign.Param;
import feign.RequestLine;

import java.time.LocalDate;

@Headers({"Accept: application/json", "if-none-match: W/2005698896447632232"})
public interface CNNFearGreedClient {

@RequestLine("GET /index/fearandgreed/graphdata/{date}")
CNNFearGreedResponse getFearGreedData(@Param("date") String date);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.dpgrandslam.stockdataservice.adapter.repository;

import com.dpgrandslam.stockdataservice.domain.model.FearGreedIndex;
import org.springframework.data.jpa.repository.JpaRepository;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

public interface FearGreedIndexRepository extends JpaRepository<FearGreedIndex, Long> {

Optional<FearGreedIndex> findFearGreedIndexByTradeDate(LocalDate tradeDate);

List<FearGreedIndex> findFearGreedIndexByTradeDateBetween(LocalDate startDate, LocalDate endDate);

List<FearGreedIndex> findFearGreedIndicesByTradeDateGreaterThanEqual(LocalDate tradeDate);

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

import com.dpgrandslam.stockdataservice.domain.model.stock.TrackedStock;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface TrackedStocksRepository extends JpaRepository<TrackedStock, String> {

List<TrackedStock> findAllByActiveIsTrue();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.dpgrandslam.stockdataservice.domain.config;

import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* A simple security configuration for the api without having to use spring security
*/
@Configuration
public class APISecurityConfig implements WebMvcConfigurer {

@Bean
@ConfigurationProperties(prefix = "api.security")
public APISecurityConfigurationProperties apiSecurityConfigurationProperties() {
return new APISecurityConfigurationProperties();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
WebMvcConfigurer.super.addInterceptors(registry);
registry.addInterceptor(new HandlerInterceptor() {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
APISecurityConfigurationProperties securityConfigurationProperties = apiSecurityConfigurationProperties();
if (securityConfigurationProperties.getEnabled()
&& (StringUtils.isBlank(securityConfigurationProperties.getPassword())
|| !securityConfigurationProperties.getPassword().equals(request.getHeader("stock-data-password")))) {
response.setStatus(401);
return false;
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.dpgrandslam.stockdataservice.domain.config;

import lombok.Data;

@Data
public class APISecurityConfigurationProperties {

private Boolean enabled = false;
private String password = "";

}
Loading

0 comments on commit 76bf415

Please sign in to comment.