Browse Source
			
			
			Feature/add health check endpoints (#1886)
			
				* Add health check endpoints
* Update changelog
			
			
				pull/1887/head
			
			
		 
		
			
				
					
						 Thomas Kaul
					
					3 years ago
						Thomas Kaul
					
					3 years ago
					
						
							committed by
							
								 GitHub
								GitHub
							
						 
					
				 
				
			 
		 
		
			
				
					
					No known key found for this signature in database
					
						
							GPG Key ID: 4AEE18F83AFDEB23
						
					
				
			
		
		
		
	
		
			
				 14 changed files with 
125 additions and 
1 deletions
			 
			
		 
		
			
				- 
					
					
					 
					CHANGELOG.md
				
- 
					
					
					 
					apps/api/src/app/app.module.ts
				
- 
					
					
					 
					apps/api/src/app/health/health.controller.ts
				
- 
					
					
					 
					apps/api/src/app/health/health.module.ts
				
- 
					
					
					 
					apps/api/src/app/health/health.service.ts
				
- 
					
					
					 
					apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts
				
- 
					
					
					 
					apps/api/src/services/data-provider/coingecko/coingecko.service.ts
				
- 
					
					
					 
					apps/api/src/services/data-provider/data-provider.service.ts
				
- 
					
					
					 
					apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts
				
- 
					
					
					 
					apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts
				
- 
					
					
					 
					apps/api/src/services/data-provider/interfaces/data-provider.interface.ts
				
- 
					
					
					 
					apps/api/src/services/data-provider/manual/manual.service.ts
				
- 
					
					
					 
					apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts
				
- 
					
					
					 
					apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts
				
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | 
			
		
	
		
		
			
				
					|  |  | ### Added |  |  | ### Added | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | - Added a fallback to historical market data if a data provider does not provide live data |  |  | - Added a fallback to historical market data if a data provider does not provide live data | 
			
		
	
		
		
			
				
					|  |  |  |  |  | - Added a general health check endpoint | 
			
		
	
		
		
			
				
					|  |  |  |  |  | - Added health check endpoints for data providers | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | ### Changed |  |  | ### Changed | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | 
 | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -24,6 +24,7 @@ import { CacheModule } from './cache/cache.module'; | 
			
		
	
		
		
			
				
					|  |  | import { ExchangeRateModule } from './exchange-rate/exchange-rate.module'; |  |  | import { ExchangeRateModule } from './exchange-rate/exchange-rate.module'; | 
			
		
	
		
		
			
				
					|  |  | import { ExportModule } from './export/export.module'; |  |  | import { ExportModule } from './export/export.module'; | 
			
		
	
		
		
			
				
					|  |  | import { FrontendMiddleware } from './frontend.middleware'; |  |  | import { FrontendMiddleware } from './frontend.middleware'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { HealthModule } from './health/health.module'; | 
			
		
	
		
		
			
				
					|  |  | import { ImportModule } from './import/import.module'; |  |  | import { ImportModule } from './import/import.module'; | 
			
		
	
		
		
			
				
					|  |  | import { InfoModule } from './info/info.module'; |  |  | import { InfoModule } from './info/info.module'; | 
			
		
	
		
		
			
				
					|  |  | import { LogoModule } from './logo/logo.module'; |  |  | import { LogoModule } from './logo/logo.module'; | 
			
		
	
	
		
		
			
				
					|  | @ -57,6 +58,7 @@ import { UserModule } from './user/user.module'; | 
			
		
	
		
		
			
				
					|  |  |     ExchangeRateModule, |  |  |     ExchangeRateModule, | 
			
		
	
		
		
			
				
					|  |  |     ExchangeRateDataModule, |  |  |     ExchangeRateDataModule, | 
			
		
	
		
		
			
				
					|  |  |     ExportModule, |  |  |     ExportModule, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     HealthModule, | 
			
		
	
		
		
			
				
					|  |  |     ImportModule, |  |  |     ImportModule, | 
			
		
	
		
		
			
				
					|  |  |     InfoModule, |  |  |     InfoModule, | 
			
		
	
		
		
			
				
					|  |  |     LogoModule, |  |  |     LogoModule, | 
			
		
	
	
		
		
			
				
					|  | 
 | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -0,0 +1,44 @@ | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request.interceptor'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   Controller, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   Get, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   HttpException, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   Param, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   UseInterceptors | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } from '@nestjs/common'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { HealthService } from './health.service'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { DataSource } from '@prisma/client'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { StatusCodes, getReasonPhrase } from 'http-status-codes'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  | @Controller('health') | 
			
		
	
		
		
			
				
					|  |  |  |  |  | export class HealthController { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public constructor(private readonly healthService: HealthService) {} | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   @Get() | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public async getHealth() {} | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   @Get('data-provider/:dataSource') | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   @UseInterceptors(TransformDataSourceInRequestInterceptor) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public async getHealthOfDataProvider( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     @Param('dataSource') dataSource: DataSource | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   ) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if (!DataSource[dataSource]) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       throw new HttpException( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         getReasonPhrase(StatusCodes.NOT_FOUND), | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         StatusCodes.NOT_FOUND | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const hasResponse = await this.healthService.hasResponseFromDataProvider( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       dataSource | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if (hasResponse !== true) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       throw new HttpException( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         getReasonPhrase(StatusCodes.SERVICE_UNAVAILABLE), | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         StatusCodes.SERVICE_UNAVAILABLE | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       ); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -0,0 +1,13 @@ | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { Module } from '@nestjs/common'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { HealthController } from './health.controller'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { HealthService } from './health.service'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  | @Module({ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   controllers: [HealthController], | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   imports: [ConfigurationModule, DataProviderModule], | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   providers: [HealthService] | 
			
		
	
		
		
			
				
					|  |  |  |  |  | }) | 
			
		
	
		
		
			
				
					|  |  |  |  |  | export class HealthModule {} | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -0,0 +1,14 @@ | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { Injectable } from '@nestjs/common'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { DataSource } from '@prisma/client'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  | @Injectable() | 
			
		
	
		
		
			
				
					|  |  |  |  |  | export class HealthService { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public constructor( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     private readonly dataProviderService: DataProviderService | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   ) {} | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public async hasResponseFromDataProvider(aDataSource: DataSource) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return this.dataProviderService.checkQuote(aDataSource); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -7,7 +7,7 @@ import { | 
			
		
	
		
		
			
				
					|  |  | } from '@ghostfolio/api/services/interfaces/interfaces'; |  |  | } from '@ghostfolio/api/services/interfaces/interfaces'; | 
			
		
	
		
		
			
				
					|  |  | 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 } from '@nestjs/common'; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  | import { DataSource, SymbolProfile } from '@prisma/client'; |  |  | import { DataSource, SymbolProfile } from '@prisma/client'; | 
			
		
	
		
		
			
				
					|  |  | import { format, isAfter, isBefore, parse } from 'date-fns'; |  |  | import { format, isAfter, isBefore, parse } from 'date-fns'; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | @ -110,6 +110,10 @@ export class AlphaVantageService implements DataProviderInterface { | 
			
		
	
		
		
			
				
					|  |  |     return {}; |  |  |     return {}; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public getTestSymbol() { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return undefined; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { |  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { | 
			
		
	
		
		
			
				
					|  |  |     const result = await this.alphaVantage.data.search(aQuery); |  |  |     const result = await this.alphaVantage.data.search(aQuery); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | 
 | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -160,6 +160,10 @@ export class CoinGeckoService implements DataProviderInterface { | 
			
		
	
		
		
			
				
					|  |  |     return results; |  |  |     return results; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public getTestSymbol() { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return 'bitcoin'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { |  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { | 
			
		
	
		
		
			
				
					|  |  |     let items: LookupItem[] = []; |  |  |     let items: LookupItem[] = []; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | 
 | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -38,6 +38,24 @@ export class DataProviderService { | 
			
		
	
		
		
			
				
					|  |  |       }) ?? {}; |  |  |       }) ?? {}; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public async checkQuote(dataSource: DataSource) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const dataProvider = this.getDataProvider(dataSource); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const symbol = dataProvider.getTestSymbol(); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const quotes = await this.getQuotes([ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         dataSource, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         symbol | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ]); | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if (quotes[symbol]?.marketPrice > 0) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       return true; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return false; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   public async getDividends({ |  |  |   public async getDividends({ | 
			
		
	
		
		
			
				
					|  |  |     dataSource, |  |  |     dataSource, | 
			
		
	
		
		
			
				
					|  |  |     from, |  |  |     from, | 
			
		
	
	
		
		
			
				
					|  | 
 | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -172,6 +172,10 @@ export class EodHistoricalDataService implements DataProviderInterface { | 
			
		
	
		
		
			
				
					|  |  |     return {}; |  |  |     return {}; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public getTestSymbol() { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return 'AAPL.US'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { |  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { | 
			
		
	
		
		
			
				
					|  |  |     const searchResult = await this.getSearchResult(aQuery); |  |  |     const searchResult = await this.getSearchResult(aQuery); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | 
 | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -143,6 +143,10 @@ export class GoogleSheetsService implements DataProviderInterface { | 
			
		
	
		
		
			
				
					|  |  |     return {}; |  |  |     return {}; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public getTestSymbol() { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return 'INDEXSP:.INX'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { |  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { | 
			
		
	
		
		
			
				
					|  |  |     const items = await this.prismaService.symbolProfile.findMany({ |  |  |     const items = await this.prismaService.symbolProfile.findMany({ | 
			
		
	
		
		
			
				
					|  |  |       select: { |  |  |       select: { | 
			
		
	
	
		
		
			
				
					|  | 
 | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -40,5 +40,7 @@ export interface DataProviderInterface { | 
			
		
	
		
		
			
				
					|  |  |     aSymbols: string[] |  |  |     aSymbols: string[] | 
			
		
	
		
		
			
				
					|  |  |   ): Promise<{ [symbol: string]: IDataProviderResponse }>; |  |  |   ): Promise<{ [symbol: string]: IDataProviderResponse }>; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   getTestSymbol(): string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   search(aQuery: string): Promise<{ items: LookupItem[] }>; |  |  |   search(aQuery: string): Promise<{ items: LookupItem[] }>; | 
			
		
	
		
		
			
				
					|  |  | } |  |  | } | 
			
		
	
	
		
		
			
				
					|  | 
 | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -163,6 +163,10 @@ export class ManualService implements DataProviderInterface { | 
			
		
	
		
		
			
				
					|  |  |     return {}; |  |  |     return {}; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public getTestSymbol() { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return undefined; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { |  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { | 
			
		
	
		
		
			
				
					|  |  |     let items = await this.prismaService.symbolProfile.findMany({ |  |  |     let items = await this.prismaService.symbolProfile.findMany({ | 
			
		
	
		
		
			
				
					|  |  |       select: { |  |  |       select: { | 
			
		
	
	
		
		
			
				
					|  | 
 | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -113,6 +113,10 @@ export class RapidApiService implements DataProviderInterface { | 
			
		
	
		
		
			
				
					|  |  |     return {}; |  |  |     return {}; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public getTestSymbol() { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return undefined; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { |  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { | 
			
		
	
		
		
			
				
					|  |  |     return { items: [] }; |  |  |     return { items: [] }; | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
	
		
		
			
				
					|  | 
 | 
			
		
	
								
							
						
					 
					
				 
			 
		
			
			
			
			
			
			
				
				
					
						
							
								
									
	
		
		
			
				
					|  | @ -167,6 +167,7 @@ export class YahooFinanceService implements DataProviderInterface { | 
			
		
	
		
		
			
				
					|  |  |     if (aSymbols.length <= 0) { |  |  |     if (aSymbols.length <= 0) { | 
			
		
	
		
		
			
				
					|  |  |       return {}; |  |  |       return {}; | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     const yahooFinanceSymbols = aSymbols.map((symbol) => |  |  |     const yahooFinanceSymbols = aSymbols.map((symbol) => | 
			
		
	
		
		
			
				
					|  |  |       this.yahooFinanceDataEnhancerService.convertToYahooFinanceSymbol(symbol) |  |  |       this.yahooFinanceDataEnhancerService.convertToYahooFinanceSymbol(symbol) | 
			
		
	
		
		
			
				
					|  |  |     ); |  |  |     ); | 
			
		
	
	
		
		
			
				
					|  | @ -251,6 +252,10 @@ export class YahooFinanceService implements DataProviderInterface { | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public getTestSymbol() { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     return 'AAPL'; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { |  |  |   public async search(aQuery: string): Promise<{ items: LookupItem[] }> { | 
			
		
	
		
		
			
				
					|  |  |     const items: LookupItem[] = []; |  |  |     const items: LookupItem[] = []; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | 
 |