Browse Source
Feature/improve currency validation in getAssetProfiles() functionality of data provider service (#5747)
* Improve currency validation
* Update changelog
pull/5752/head
Thomas Kaul
3 weeks ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with
42 additions and
14 deletions
-
CHANGELOG.md
-
apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.controller.ts
-
apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts
-
apps/api/src/services/data-provider/data-provider.service.ts
-
apps/api/src/services/data-provider/errors/asset-profile-invalid.error.ts
-
apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts
|
|
|
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 |
|
|
|
### Changed |
|
|
|
|
|
|
|
- Disabled the zoom functionality in the _Progressive Web App_ (PWA) |
|
|
|
- Improved the currency validation in the get asset profiles functionality of the data provider service |
|
|
|
- Improved the currency validation in the search functionality of the data provider service |
|
|
|
- Optimized the get quotes functionality by utilizing the asset profile resolutions in the _Financial Modeling Prep_ service |
|
|
|
- Extracted the footer to a component |
|
|
|
|
|
|
|
@ -1,5 +1,6 @@ |
|
|
|
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; |
|
|
|
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; |
|
|
|
import { AssetProfileInvalidError } from '@ghostfolio/api/services/data-provider/errors/asset-profile-invalid.error'; |
|
|
|
import { parseDate } from '@ghostfolio/common/helper'; |
|
|
|
import { |
|
|
|
DataProviderGhostfolioAssetProfileResponse, |
|
|
|
@ -66,7 +67,14 @@ export class GhostfolioController { |
|
|
|
}); |
|
|
|
|
|
|
|
return assetProfile; |
|
|
|
} catch { |
|
|
|
} catch (error) { |
|
|
|
if (error instanceof AssetProfileInvalidError) { |
|
|
|
throw new HttpException( |
|
|
|
getReasonPhrase(StatusCodes.NOT_FOUND), |
|
|
|
StatusCodes.NOT_FOUND |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
throw new HttpException( |
|
|
|
getReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR), |
|
|
|
StatusCodes.INTERNAL_SERVER_ERROR |
|
|
|
|
|
|
|
@ -40,10 +40,7 @@ export class GhostfolioService { |
|
|
|
private readonly propertyService: PropertyService |
|
|
|
) {} |
|
|
|
|
|
|
|
public async getAssetProfile({ |
|
|
|
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), |
|
|
|
symbol |
|
|
|
}: GetAssetProfileParams) { |
|
|
|
public async getAssetProfile({ symbol }: GetAssetProfileParams) { |
|
|
|
let result: DataProviderGhostfolioAssetProfileResponse = {}; |
|
|
|
|
|
|
|
try { |
|
|
|
@ -51,12 +48,15 @@ export class GhostfolioService { |
|
|
|
|
|
|
|
for (const dataProviderService of this.getDataProviderServices()) { |
|
|
|
promises.push( |
|
|
|
dataProviderService |
|
|
|
.getAssetProfile({ |
|
|
|
requestTimeout, |
|
|
|
symbol |
|
|
|
}) |
|
|
|
.then(async (assetProfile) => { |
|
|
|
this.dataProviderService |
|
|
|
.getAssetProfiles([ |
|
|
|
{ |
|
|
|
symbol, |
|
|
|
dataSource: dataProviderService.getName() |
|
|
|
} |
|
|
|
]) |
|
|
|
.then(async (assetProfiles) => { |
|
|
|
const assetProfile = assetProfiles[symbol]; |
|
|
|
const dataSourceOrigin = DataSource.GHOSTFOLIO; |
|
|
|
|
|
|
|
if (assetProfile) { |
|
|
|
|
|
|
|
@ -35,6 +35,8 @@ import { eachDayOfInterval, format, isValid } from 'date-fns'; |
|
|
|
import { groupBy, isEmpty, isNumber, uniqWith } from 'lodash'; |
|
|
|
import ms from 'ms'; |
|
|
|
|
|
|
|
import { AssetProfileInvalidError } from './errors/asset-profile-invalid.error'; |
|
|
|
|
|
|
|
@Injectable() |
|
|
|
export class DataProviderService implements OnModuleInit { |
|
|
|
private dataProviderMapping: { [dataProviderName: string]: string }; |
|
|
|
@ -106,9 +108,9 @@ export class DataProviderService implements OnModuleInit { |
|
|
|
); |
|
|
|
|
|
|
|
promises.push( |
|
|
|
promise.then((symbolProfile) => { |
|
|
|
if (symbolProfile) { |
|
|
|
response[symbol] = symbolProfile; |
|
|
|
promise.then((assetProfile) => { |
|
|
|
if (isCurrency(assetProfile?.currency)) { |
|
|
|
response[symbol] = assetProfile; |
|
|
|
} |
|
|
|
}) |
|
|
|
); |
|
|
|
@ -117,6 +119,12 @@ export class DataProviderService implements OnModuleInit { |
|
|
|
|
|
|
|
try { |
|
|
|
await Promise.all(promises); |
|
|
|
|
|
|
|
if (isEmpty(response)) { |
|
|
|
throw new AssetProfileInvalidError( |
|
|
|
'No valid asset profiles have been found' |
|
|
|
); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
Logger.error(error, 'DataProviderService'); |
|
|
|
|
|
|
|
|
|
|
|
@ -0,0 +1,7 @@ |
|
|
|
export class AssetProfileInvalidError extends Error { |
|
|
|
public constructor(message: string) { |
|
|
|
super(message); |
|
|
|
|
|
|
|
this.name = 'AssetProfileInvalidError'; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -102,6 +102,10 @@ export class FinancialModelingPrepService implements DataProviderInterface { |
|
|
|
} |
|
|
|
).then((res) => res.json()); |
|
|
|
|
|
|
|
if (!assetProfile) { |
|
|
|
throw new Error(`${symbol} not found`); |
|
|
|
} |
|
|
|
|
|
|
|
const { assetClass, assetSubClass } = |
|
|
|
this.parseAssetClass(assetProfile); |
|
|
|
|
|
|
|
|