Browse Source
Merge branch 'main' into bugfix/data-gathering-blocked-by-failed-jobs
pull/7720/head
Thomas Kaul
5 days ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with
54 additions and
9 deletions
-
CHANGELOG.md
-
apps/api/src/app/admin/admin.controller.ts
-
apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts
-
apps/api/src/services/queues/data-gathering/data-gathering.service.spec.ts
-
apps/api/src/services/queues/data-gathering/data-gathering.service.ts
-
apps/client/src/app/components/access-table/access-table.component.html
-
apps/client/src/app/components/access-table/access-table.component.ts
|
|
|
@ -9,12 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 |
|
|
|
|
|
|
|
### Changed |
|
|
|
|
|
|
|
- Harmonized the icons and labels in the access table to share the portfolio |
|
|
|
- Migrated the create and edit access dialogs to dedicated routes |
|
|
|
- Improved the response of the historical market data gathering endpoint for a specific date |
|
|
|
- Introduced a timeout for the asset profile and the historical market data gathering jobs |
|
|
|
- Reduced the number of attempts of the asset profile and the historical market data gathering jobs |
|
|
|
|
|
|
|
### Fixed |
|
|
|
|
|
|
|
- Fixed the country mapping of Macau in the _Financial Modeling Prep_ service |
|
|
|
- Fixed the asset profile and historical market data gathering of a symbol getting blocked permanently by a failed job by discarding the failed jobs |
|
|
|
- Fixed the asset profile data gathering of a symbol in the admin control panel by removing an existing job before enqueueing a new one |
|
|
|
|
|
|
|
|
|
|
|
@ -218,11 +218,20 @@ export class AdminController { |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
return this.dataGatheringService.gatherSymbolForDate({ |
|
|
|
const marketData = await this.dataGatheringService.gatherSymbolForDate({ |
|
|
|
dataSource, |
|
|
|
date, |
|
|
|
symbol |
|
|
|
}); |
|
|
|
|
|
|
|
if (!marketData) { |
|
|
|
throw new HttpException( |
|
|
|
getReasonPhrase(StatusCodes.NOT_FOUND), |
|
|
|
StatusCodes.NOT_FOUND |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
return marketData; |
|
|
|
} |
|
|
|
|
|
|
|
@HasPermission(permissions.accessAdminControl) |
|
|
|
|
|
|
|
@ -56,6 +56,7 @@ export class FinancialModelingPrepService |
|
|
|
private static countriesMapping = { |
|
|
|
'Czech Republic': 'CZ', |
|
|
|
'Korea (the Republic of)': 'KR', |
|
|
|
Macau: 'MO', |
|
|
|
'Russian Federation': 'RU', |
|
|
|
'Taiwan (Province of China)': 'TW', |
|
|
|
Turkey: 'TR' |
|
|
|
|
|
|
|
@ -219,5 +219,34 @@ describe('DataGatheringService', () => { |
|
|
|
}) |
|
|
|
); |
|
|
|
}); |
|
|
|
|
|
|
|
it('returns the upserted market data', async () => { |
|
|
|
dataProviderService.getHistoricalRaw.mockResolvedValue({ |
|
|
|
'YAHOO-AAPL': { |
|
|
|
'2026-08-22': { marketPrice: 100 } |
|
|
|
} |
|
|
|
}); |
|
|
|
prismaService.marketData.upsert.mockResolvedValue({ marketPrice: 100 }); |
|
|
|
|
|
|
|
const marketData = await dataGatheringService.gatherSymbolForDate({ |
|
|
|
dataSource: 'YAHOO', |
|
|
|
date: parseDate('2026-08-22'), |
|
|
|
symbol: 'AAPL' |
|
|
|
}); |
|
|
|
|
|
|
|
expect(marketData).toEqual({ marketPrice: 100 }); |
|
|
|
}); |
|
|
|
|
|
|
|
it('returns undefined if the data provider has no market price', async () => { |
|
|
|
dataProviderService.getHistoricalRaw.mockResolvedValue({}); |
|
|
|
|
|
|
|
const marketData = await dataGatheringService.gatherSymbolForDate({ |
|
|
|
dataSource: 'YAHOO', |
|
|
|
date: parseDate('2026-08-22'), |
|
|
|
symbol: 'AAPL' |
|
|
|
}); |
|
|
|
|
|
|
|
expect(marketData).toBeUndefined(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
@ -348,10 +348,10 @@ export class DataGatheringService { |
|
|
|
}); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
this.logger.error(error.message); |
|
|
|
} finally { |
|
|
|
return undefined; |
|
|
|
this.logger.error(error?.message); |
|
|
|
} |
|
|
|
|
|
|
|
return undefined; |
|
|
|
} |
|
|
|
|
|
|
|
public async gatherSymbols({ |
|
|
|
|
|
|
|
@ -15,7 +15,7 @@ |
|
|
|
} @else if (element.type === 'PUBLIC') { |
|
|
|
<span i18n>Public</span> |
|
|
|
} @else if (element.type === 'MCP') { |
|
|
|
<span>MCP</span> |
|
|
|
<span>Model Context Protocol (MCP)</span> |
|
|
|
} |
|
|
|
</td> |
|
|
|
</ng-container> |
|
|
|
@ -112,10 +112,11 @@ |
|
|
|
} |
|
|
|
<button mat-menu-item (click)="onDeleteAccess(element.id)"> |
|
|
|
<span class="align-items-center d-flex"> |
|
|
|
<ion-icon class="mr-2" name="remove-circle-outline" /> |
|
|
|
@if (isReceivedAccess()) { |
|
|
|
<span i18n>Remove</span> |
|
|
|
<ion-icon class="mr-2" name="trash-outline" /> |
|
|
|
<span i18n>Delete</span> |
|
|
|
} @else { |
|
|
|
<ion-icon class="mr-2" name="remove-circle-outline" /> |
|
|
|
<span i18n>Revoke</span> |
|
|
|
} |
|
|
|
</span> |
|
|
|
|
|
|
|
@ -31,7 +31,8 @@ import { |
|
|
|
createOutline, |
|
|
|
ellipsisHorizontal, |
|
|
|
linkOutline, |
|
|
|
removeCircleOutline |
|
|
|
removeCircleOutline, |
|
|
|
trashOutline |
|
|
|
} from 'ionicons/icons'; |
|
|
|
import ms from 'ms'; |
|
|
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; |
|
|
|
@ -107,7 +108,8 @@ export class GfAccessTableComponent { |
|
|
|
createOutline, |
|
|
|
ellipsisHorizontal, |
|
|
|
linkOutline, |
|
|
|
removeCircleOutline |
|
|
|
removeCircleOutline, |
|
|
|
trashOutline |
|
|
|
}); |
|
|
|
|
|
|
|
this.hasPermissionToEnableMcp = hasPermission( |
|
|
|
|