Feature/expose the environment variable REQUEST_TIMEOUT
(#2792)
* Expose the environment variable `REQUEST_TIMEOUT` * Update changelog
This commit is contained in:
parent
0034776b34
commit
34b02210df
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Changed the performance calculation to a time-weighted approach
|
- Changed the performance calculation to a time-weighted approach
|
||||||
|
- Exposed the environment variable `REQUEST_TIMEOUT`
|
||||||
- Used the `HasPermission` annotation in endpoints
|
- Used the `HasPermission` annotation in endpoints
|
||||||
- Upgraded `Nx` from version `17.2.5` to `17.2.7`
|
- Upgraded `Nx` from version `17.2.5` to `17.2.7`
|
||||||
|
|
||||||
|
@ -100,6 +100,7 @@ We provide official container images hosted on [Docker Hub](https://hub.docker.c
|
|||||||
| `REDIS_HOST` | | The host where _Redis_ is running |
|
| `REDIS_HOST` | | The host where _Redis_ is running |
|
||||||
| `REDIS_PASSWORD` | | The password of _Redis_ |
|
| `REDIS_PASSWORD` | | The password of _Redis_ |
|
||||||
| `REDIS_PORT` | | The port where _Redis_ is running |
|
| `REDIS_PORT` | | The port where _Redis_ is running |
|
||||||
|
| `REQUEST_TIMEOUT` | `2000` | The timeout of network requests to data providers in milliseconds |
|
||||||
|
|
||||||
### Run with Docker Compose
|
### Run with Docker Compose
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import { PropertyService } from '@ghostfolio/api/services/property/property.serv
|
|||||||
import { TagService } from '@ghostfolio/api/services/tag/tag.service';
|
import { TagService } from '@ghostfolio/api/services/tag/tag.service';
|
||||||
import {
|
import {
|
||||||
DEFAULT_CURRENCY,
|
DEFAULT_CURRENCY,
|
||||||
DEFAULT_REQUEST_TIMEOUT,
|
|
||||||
PROPERTY_BETTER_UPTIME_MONITOR_ID,
|
PROPERTY_BETTER_UPTIME_MONITOR_ID,
|
||||||
PROPERTY_COUNTRIES_OF_SUBSCRIBERS,
|
PROPERTY_COUNTRIES_OF_SUBSCRIBERS,
|
||||||
PROPERTY_DEMO_USER_ID,
|
PROPERTY_DEMO_USER_ID,
|
||||||
@ -162,7 +161,7 @@ export class InfoService {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const { pull_count } = await got(
|
const { pull_count } = await got(
|
||||||
`https://hub.docker.com/v2/repositories/ghostfolio/ghostfolio`,
|
`https://hub.docker.com/v2/repositories/ghostfolio/ghostfolio`,
|
||||||
@ -187,7 +186,7 @@ export class InfoService {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const { body } = await got('https://github.com/ghostfolio/ghostfolio', {
|
const { body } = await got('https://github.com/ghostfolio/ghostfolio', {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -214,7 +213,7 @@ export class InfoService {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const { stargazers_count } = await got(
|
const { stargazers_count } = await got(
|
||||||
`https://api.github.com/repos/ghostfolio/ghostfolio`,
|
`https://api.github.com/repos/ghostfolio/ghostfolio`,
|
||||||
@ -342,7 +341,7 @@ export class InfoService {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const { data } = await got(
|
const { data } = await got(
|
||||||
`https://uptime.betterstack.com/api/v2/monitors/${monitorId}/sla?from=${format(
|
`https://uptime.betterstack.com/api/v2/monitors/${monitorId}/sla?from=${format(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
||||||
import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config';
|
|
||||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||||
import { HttpException, Injectable } from '@nestjs/common';
|
import { HttpException, Injectable } from '@nestjs/common';
|
||||||
import { DataSource } from '@prisma/client';
|
import { DataSource } from '@prisma/client';
|
||||||
@ -9,6 +9,7 @@ import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class LogoService {
|
export class LogoService {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
private readonly configurationService: ConfigurationService,
|
||||||
private readonly symbolProfileService: SymbolProfileService
|
private readonly symbolProfileService: SymbolProfileService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ export class LogoService {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
return got(
|
return got(
|
||||||
`https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${aUrl}&size=64`,
|
`https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${aUrl}&size=64`,
|
||||||
|
@ -44,6 +44,7 @@ export class ConfigurationService {
|
|||||||
REDIS_HOST: str({ default: 'localhost' }),
|
REDIS_HOST: str({ default: 'localhost' }),
|
||||||
REDIS_PASSWORD: str({ default: '' }),
|
REDIS_PASSWORD: str({ default: '' }),
|
||||||
REDIS_PORT: port({ default: 6379 }),
|
REDIS_PORT: port({ default: 6379 }),
|
||||||
|
REQUEST_TIMEOUT: num({ default: 2000 }),
|
||||||
ROOT_URL: str({ default: DEFAULT_ROOT_URL }),
|
ROOT_URL: str({ default: DEFAULT_ROOT_URL }),
|
||||||
STRIPE_PUBLIC_KEY: str({ default: '' }),
|
STRIPE_PUBLIC_KEY: str({ default: '' }),
|
||||||
STRIPE_SECRET_KEY: str({ default: '' }),
|
STRIPE_SECRET_KEY: str({ default: '' }),
|
||||||
|
@ -5,7 +5,6 @@ import {
|
|||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
IDataProviderResponse
|
IDataProviderResponse
|
||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config';
|
|
||||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
@ -107,7 +106,7 @@ export class AlphaVantageService implements DataProviderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getQuotes({
|
public async getQuotes({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
symbols
|
symbols
|
||||||
}: {
|
}: {
|
||||||
requestTimeout?: number;
|
requestTimeout?: number;
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
||||||
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
|
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
|
||||||
import {
|
import {
|
||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
IDataProviderResponse
|
IDataProviderResponse
|
||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import {
|
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
|
||||||
DEFAULT_CURRENCY,
|
|
||||||
DEFAULT_REQUEST_TIMEOUT
|
|
||||||
} from '@ghostfolio/common/config';
|
|
||||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||||
import { DataProviderInfo } from '@ghostfolio/common/interfaces';
|
import { DataProviderInfo } from '@ghostfolio/common/interfaces';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
@ -25,7 +23,9 @@ import got from 'got';
|
|||||||
export class CoinGeckoService implements DataProviderInterface {
|
export class CoinGeckoService implements DataProviderInterface {
|
||||||
private readonly URL = 'https://api.coingecko.com/api/v3';
|
private readonly URL = 'https://api.coingecko.com/api/v3';
|
||||||
|
|
||||||
public constructor() {}
|
public constructor(
|
||||||
|
private readonly configurationService: ConfigurationService
|
||||||
|
) {}
|
||||||
|
|
||||||
public canHandle(symbol: string) {
|
public canHandle(symbol: string) {
|
||||||
return true;
|
return true;
|
||||||
@ -47,7 +47,7 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const { name } = await got(`${this.URL}/coins/${aSymbol}`, {
|
const { name } = await got(`${this.URL}/coins/${aSymbol}`, {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -59,7 +59,9 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
let message = error;
|
let message = error;
|
||||||
|
|
||||||
if (error?.code === 'ABORT_ERR') {
|
if (error?.code === 'ABORT_ERR') {
|
||||||
message = `RequestError: The operation was aborted because the request to the data provider took more than ${DEFAULT_REQUEST_TIMEOUT}ms`;
|
message = `RequestError: The operation was aborted because the request to the data provider took more than ${this.configurationService.get(
|
||||||
|
'REQUEST_TIMEOUT'
|
||||||
|
)}ms`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.error(message, 'CoinGeckoService');
|
Logger.error(message, 'CoinGeckoService');
|
||||||
@ -95,7 +97,7 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const { prices } = await got(
|
const { prices } = await got(
|
||||||
`${
|
`${
|
||||||
@ -141,7 +143,7 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getQuotes({
|
public async getQuotes({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
symbols
|
symbols
|
||||||
}: {
|
}: {
|
||||||
requestTimeout?: number;
|
requestTimeout?: number;
|
||||||
@ -183,7 +185,9 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
let message = error;
|
let message = error;
|
||||||
|
|
||||||
if (error?.code === 'ABORT_ERR') {
|
if (error?.code === 'ABORT_ERR') {
|
||||||
message = `RequestError: The operation was aborted because the request to the data provider took more than ${DEFAULT_REQUEST_TIMEOUT}ms`;
|
message = `RequestError: The operation was aborted because the request to the data provider took more than ${this.configurationService.get(
|
||||||
|
'REQUEST_TIMEOUT'
|
||||||
|
)}ms`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.error(message, 'CoinGeckoService');
|
Logger.error(message, 'CoinGeckoService');
|
||||||
@ -210,7 +214,7 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const { coins } = await got(`${this.URL}/search?query=${query}`, {
|
const { coins } = await got(`${this.URL}/search?query=${query}`, {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -231,7 +235,9 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
let message = error;
|
let message = error;
|
||||||
|
|
||||||
if (error?.code === 'ABORT_ERR') {
|
if (error?.code === 'ABORT_ERR') {
|
||||||
message = `RequestError: The operation was aborted because the request to the data provider took more than ${DEFAULT_REQUEST_TIMEOUT}ms`;
|
message = `RequestError: The operation was aborted because the request to the data provider took more than ${this.configurationService.get(
|
||||||
|
'REQUEST_TIMEOUT'
|
||||||
|
)}ms`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.error(message, 'CoinGeckoService');
|
Logger.error(message, 'CoinGeckoService');
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
|
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
|
||||||
import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config';
|
|
||||||
import { parseSymbol } from '@ghostfolio/common/helper';
|
import { parseSymbol } from '@ghostfolio/common/helper';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { SymbolProfile } from '@prisma/client';
|
import { SymbolProfile } from '@prisma/client';
|
||||||
@ -15,7 +14,7 @@ export class OpenFigiDataEnhancerService implements DataEnhancerInterface {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async enhance({
|
public async enhance({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
response,
|
response,
|
||||||
symbol
|
symbol
|
||||||
}: {
|
}: {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
|
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
|
||||||
import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config';
|
|
||||||
import { Country } from '@ghostfolio/common/interfaces/country.interface';
|
import { Country } from '@ghostfolio/common/interfaces/country.interface';
|
||||||
import { Sector } from '@ghostfolio/common/interfaces/sector.interface';
|
import { Sector } from '@ghostfolio/common/interfaces/sector.interface';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
@ -21,8 +21,12 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
|
|||||||
'Information Technology': 'Technology'
|
'Information Technology': 'Technology'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private readonly configurationService: ConfigurationService
|
||||||
|
) {}
|
||||||
|
|
||||||
public async enhance({
|
public async enhance({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
response,
|
response,
|
||||||
symbol
|
symbol
|
||||||
}: {
|
}: {
|
||||||
@ -55,7 +59,7 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
return got(
|
return got(
|
||||||
`${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol.split(
|
`${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol.split(
|
||||||
@ -82,7 +86,7 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const holdings = await got(
|
const holdings = await got(
|
||||||
`${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol}.json`,
|
`${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol}.json`,
|
||||||
@ -97,7 +101,7 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
return got(
|
return got(
|
||||||
`${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol.split(
|
`${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol.split(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
||||||
|
|
||||||
import { YahooFinanceDataEnhancerService } from './yahoo-finance.service';
|
import { YahooFinanceDataEnhancerService } from './yahoo-finance.service';
|
||||||
@ -25,13 +26,16 @@ jest.mock(
|
|||||||
);
|
);
|
||||||
|
|
||||||
describe('YahooFinanceDataEnhancerService', () => {
|
describe('YahooFinanceDataEnhancerService', () => {
|
||||||
|
let configurationService: ConfigurationService;
|
||||||
let cryptocurrencyService: CryptocurrencyService;
|
let cryptocurrencyService: CryptocurrencyService;
|
||||||
let yahooFinanceDataEnhancerService: YahooFinanceDataEnhancerService;
|
let yahooFinanceDataEnhancerService: YahooFinanceDataEnhancerService;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
configurationService = new ConfigurationService();
|
||||||
cryptocurrencyService = new CryptocurrencyService();
|
cryptocurrencyService = new CryptocurrencyService();
|
||||||
|
|
||||||
yahooFinanceDataEnhancerService = new YahooFinanceDataEnhancerService(
|
yahooFinanceDataEnhancerService = new YahooFinanceDataEnhancerService(
|
||||||
|
configurationService,
|
||||||
cryptocurrencyService
|
cryptocurrencyService
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
||||||
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
|
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
|
||||||
import {
|
import { DEFAULT_CURRENCY, UNKNOWN_KEY } from '@ghostfolio/common/config';
|
||||||
DEFAULT_CURRENCY,
|
|
||||||
DEFAULT_REQUEST_TIMEOUT,
|
|
||||||
UNKNOWN_KEY
|
|
||||||
} from '@ghostfolio/common/config';
|
|
||||||
import { isCurrency } from '@ghostfolio/common/helper';
|
import { isCurrency } from '@ghostfolio/common/helper';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
@ -22,6 +19,7 @@ import type { Price } from 'yahoo-finance2/dist/esm/src/modules/quoteSummary-ifa
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
|
export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
private readonly configurationService: ConfigurationService,
|
||||||
private readonly cryptocurrencyService: CryptocurrencyService
|
private readonly cryptocurrencyService: CryptocurrencyService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -76,7 +74,7 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async enhance({
|
public async enhance({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
response,
|
response,
|
||||||
symbol
|
symbol
|
||||||
}: {
|
}: {
|
||||||
|
@ -5,10 +5,7 @@ import {
|
|||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
IDataProviderResponse
|
IDataProviderResponse
|
||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import {
|
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
|
||||||
DEFAULT_CURRENCY,
|
|
||||||
DEFAULT_REQUEST_TIMEOUT
|
|
||||||
} from '@ghostfolio/common/config';
|
|
||||||
import { DATE_FORMAT, isCurrency } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, isCurrency } from '@ghostfolio/common/helper';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
@ -82,7 +79,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const response = await got(
|
const response = await got(
|
||||||
`${this.URL}/eod/${symbol}?api_token=${
|
`${this.URL}/eod/${symbol}?api_token=${
|
||||||
@ -132,7 +129,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getQuotes({
|
public async getQuotes({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
symbols
|
symbols
|
||||||
}: {
|
}: {
|
||||||
requestTimeout?: number;
|
requestTimeout?: number;
|
||||||
@ -232,7 +229,9 @@ export class EodHistoricalDataService implements DataProviderInterface {
|
|||||||
let message = error;
|
let message = error;
|
||||||
|
|
||||||
if (error?.code === 'ABORT_ERR') {
|
if (error?.code === 'ABORT_ERR') {
|
||||||
message = `RequestError: The operation was aborted because the request to the data provider took more than ${DEFAULT_REQUEST_TIMEOUT}ms`;
|
message = `RequestError: The operation was aborted because the request to the data provider took more than ${this.configurationService.get(
|
||||||
|
'REQUEST_TIMEOUT'
|
||||||
|
)}ms`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.error(message, 'EodHistoricalDataService');
|
Logger.error(message, 'EodHistoricalDataService');
|
||||||
@ -359,7 +358,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const response = await got(
|
const response = await got(
|
||||||
`${this.URL}/search/${aQuery}?api_token=${this.apiKey}`,
|
`${this.URL}/search/${aQuery}?api_token=${this.apiKey}`,
|
||||||
@ -391,7 +390,9 @@ export class EodHistoricalDataService implements DataProviderInterface {
|
|||||||
let message = error;
|
let message = error;
|
||||||
|
|
||||||
if (error?.code === 'ABORT_ERR') {
|
if (error?.code === 'ABORT_ERR') {
|
||||||
message = `RequestError: The operation was aborted because the request to the data provider took more than ${DEFAULT_REQUEST_TIMEOUT}ms`;
|
message = `RequestError: The operation was aborted because the request to the data provider took more than ${this.configurationService.get(
|
||||||
|
'REQUEST_TIMEOUT'
|
||||||
|
)}ms`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.error(message, 'EodHistoricalDataService');
|
Logger.error(message, 'EodHistoricalDataService');
|
||||||
|
@ -5,10 +5,7 @@ import {
|
|||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
IDataProviderResponse
|
IDataProviderResponse
|
||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import {
|
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
|
||||||
DEFAULT_CURRENCY,
|
|
||||||
DEFAULT_REQUEST_TIMEOUT
|
|
||||||
} from '@ghostfolio/common/config';
|
|
||||||
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
|
||||||
import { DataProviderInfo } from '@ghostfolio/common/interfaces';
|
import { DataProviderInfo } from '@ghostfolio/common/interfaces';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
@ -70,7 +67,7 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const { historical } = await got(
|
const { historical } = await got(
|
||||||
`${this.URL}/historical-price-full/${aSymbol}?apikey=${this.apiKey}`,
|
`${this.URL}/historical-price-full/${aSymbol}?apikey=${this.apiKey}`,
|
||||||
@ -114,7 +111,7 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getQuotes({
|
public async getQuotes({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
symbols
|
symbols
|
||||||
}: {
|
}: {
|
||||||
requestTimeout?: number;
|
requestTimeout?: number;
|
||||||
@ -154,7 +151,9 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
|||||||
let message = error;
|
let message = error;
|
||||||
|
|
||||||
if (error?.code === 'ABORT_ERR') {
|
if (error?.code === 'ABORT_ERR') {
|
||||||
message = `RequestError: The operation was aborted because the request to the data provider took more than ${DEFAULT_REQUEST_TIMEOUT}ms`;
|
message = `RequestError: The operation was aborted because the request to the data provider took more than ${this.configurationService.get(
|
||||||
|
'REQUEST_TIMEOUT'
|
||||||
|
)}ms`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.error(message, 'FinancialModelingPrepService');
|
Logger.error(message, 'FinancialModelingPrepService');
|
||||||
@ -181,7 +180,7 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const result = await got(
|
const result = await got(
|
||||||
`${this.URL}/search?query=${query}&apikey=${this.apiKey}`,
|
`${this.URL}/search?query=${query}&apikey=${this.apiKey}`,
|
||||||
@ -205,7 +204,9 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
|||||||
let message = error;
|
let message = error;
|
||||||
|
|
||||||
if (error?.code === 'ABORT_ERR') {
|
if (error?.code === 'ABORT_ERR') {
|
||||||
message = `RequestError: The operation was aborted because the request to the data provider took more than ${DEFAULT_REQUEST_TIMEOUT}ms`;
|
message = `RequestError: The operation was aborted because the request to the data provider took more than ${this.configurationService.get(
|
||||||
|
'REQUEST_TIMEOUT'
|
||||||
|
)}ms`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.error(message, 'FinancialModelingPrepService');
|
Logger.error(message, 'FinancialModelingPrepService');
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
||||||
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
||||||
import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config';
|
|
||||||
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
@ -101,7 +100,7 @@ export class GoogleSheetsService implements DataProviderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getQuotes({
|
public async getQuotes({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
symbols
|
symbols
|
||||||
}: {
|
}: {
|
||||||
requestTimeout?: number;
|
requestTimeout?: number;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
||||||
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
|
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
|
||||||
import {
|
import {
|
||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
@ -6,7 +7,6 @@ import {
|
|||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
||||||
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
||||||
import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config';
|
|
||||||
import {
|
import {
|
||||||
DATE_FORMAT,
|
DATE_FORMAT,
|
||||||
extractNumberFromString,
|
extractNumberFromString,
|
||||||
@ -23,6 +23,7 @@ import got from 'got';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class ManualService implements DataProviderInterface {
|
export class ManualService implements DataProviderInterface {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
private readonly configurationService: ConfigurationService,
|
||||||
private readonly prismaService: PrismaService,
|
private readonly prismaService: PrismaService,
|
||||||
private readonly symbolProfileService: SymbolProfileService
|
private readonly symbolProfileService: SymbolProfileService
|
||||||
) {}
|
) {}
|
||||||
@ -100,7 +101,7 @@ export class ManualService implements DataProviderInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const { body } = await got(url, {
|
const { body } = await got(url, {
|
||||||
headers,
|
headers,
|
||||||
@ -134,7 +135,7 @@ export class ManualService implements DataProviderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getQuotes({
|
public async getQuotes({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
symbols
|
symbols
|
||||||
}: {
|
}: {
|
||||||
requestTimeout?: number;
|
requestTimeout?: number;
|
||||||
|
@ -5,10 +5,7 @@ import {
|
|||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
IDataProviderResponse
|
IDataProviderResponse
|
||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import {
|
import { ghostfolioFearAndGreedIndexSymbol } from '@ghostfolio/common/config';
|
||||||
DEFAULT_REQUEST_TIMEOUT,
|
|
||||||
ghostfolioFearAndGreedIndexSymbol
|
|
||||||
} from '@ghostfolio/common/config';
|
|
||||||
import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
@ -88,7 +85,7 @@ export class RapidApiService implements DataProviderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getQuotes({
|
public async getQuotes({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
symbols
|
symbols
|
||||||
}: {
|
}: {
|
||||||
requestTimeout?: number;
|
requestTimeout?: number;
|
||||||
@ -146,7 +143,7 @@ export class RapidApiService implements DataProviderInterface {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, DEFAULT_REQUEST_TIMEOUT);
|
}, this.configurationService.get('REQUEST_TIMEOUT'));
|
||||||
|
|
||||||
const { fgi } = await got(
|
const { fgi } = await got(
|
||||||
`https://fear-and-greed-index.p.rapidapi.com/v1/fgi`,
|
`https://fear-and-greed-index.p.rapidapi.com/v1/fgi`,
|
||||||
@ -166,7 +163,9 @@ export class RapidApiService implements DataProviderInterface {
|
|||||||
let message = error;
|
let message = error;
|
||||||
|
|
||||||
if (error?.code === 'ABORT_ERR') {
|
if (error?.code === 'ABORT_ERR') {
|
||||||
message = `RequestError: The operation was aborted because the request to the data provider took more than ${DEFAULT_REQUEST_TIMEOUT}ms`;
|
message = `RequestError: The operation was aborted because the request to the data provider took more than ${this.configurationService.get(
|
||||||
|
'REQUEST_TIMEOUT'
|
||||||
|
)}ms`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.error(message, 'RapidApiService');
|
Logger.error(message, 'RapidApiService');
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
||||||
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
||||||
import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service';
|
import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service';
|
||||||
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
|
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
|
||||||
@ -6,10 +7,7 @@ import {
|
|||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
IDataProviderResponse
|
IDataProviderResponse
|
||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import {
|
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
|
||||||
DEFAULT_CURRENCY,
|
|
||||||
DEFAULT_REQUEST_TIMEOUT
|
|
||||||
} from '@ghostfolio/common/config';
|
|
||||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
@ -22,6 +20,7 @@ import { Quote } from 'yahoo-finance2/dist/esm/src/modules/quote';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class YahooFinanceService implements DataProviderInterface {
|
export class YahooFinanceService implements DataProviderInterface {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
private readonly configurationService: ConfigurationService,
|
||||||
private readonly cryptocurrencyService: CryptocurrencyService,
|
private readonly cryptocurrencyService: CryptocurrencyService,
|
||||||
private readonly yahooFinanceDataEnhancerService: YahooFinanceDataEnhancerService
|
private readonly yahooFinanceDataEnhancerService: YahooFinanceDataEnhancerService
|
||||||
) {}
|
) {}
|
||||||
@ -160,7 +159,7 @@ export class YahooFinanceService implements DataProviderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getQuotes({
|
public async getQuotes({
|
||||||
requestTimeout = DEFAULT_REQUEST_TIMEOUT,
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
|
||||||
symbols
|
symbols
|
||||||
}: {
|
}: {
|
||||||
requestTimeout?: number;
|
requestTimeout?: number;
|
||||||
|
@ -32,6 +32,7 @@ export interface Environment extends CleanedEnvAccessors {
|
|||||||
REDIS_HOST: string;
|
REDIS_HOST: string;
|
||||||
REDIS_PASSWORD: string;
|
REDIS_PASSWORD: string;
|
||||||
REDIS_PORT: number;
|
REDIS_PORT: number;
|
||||||
|
REQUEST_TIMEOUT: number;
|
||||||
ROOT_URL: string;
|
ROOT_URL: string;
|
||||||
STRIPE_PUBLIC_KEY: string;
|
STRIPE_PUBLIC_KEY: string;
|
||||||
STRIPE_SECRET_KEY: string;
|
STRIPE_SECRET_KEY: string;
|
||||||
|
@ -39,7 +39,6 @@ export const DEFAULT_CURRENCY = 'USD';
|
|||||||
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
|
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
|
||||||
export const DEFAULT_LANGUAGE_CODE = 'en';
|
export const DEFAULT_LANGUAGE_CODE = 'en';
|
||||||
export const DEFAULT_PAGE_SIZE = 50;
|
export const DEFAULT_PAGE_SIZE = 50;
|
||||||
export const DEFAULT_REQUEST_TIMEOUT = ms('2 seconds');
|
|
||||||
export const DEFAULT_ROOT_URL = 'http://localhost:4200';
|
export const DEFAULT_ROOT_URL = 'http://localhost:4200';
|
||||||
|
|
||||||
export const EMERGENCY_FUND_TAG_ID = '4452656d-9fa4-4bd0-ba38-70492e31d180';
|
export const EMERGENCY_FUND_TAG_ID = '4452656d-9fa4-4bd0-ba38-70492e31d180';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user