Browse Source

Bugfix/fix locale in scraper configuration (#5011)

* Fix locale in scraper configuration

* Update changelog
pull/5031/head
Thomas Kaul 6 days ago
committed by GitHub
parent
commit
a7a33eeb56
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      CHANGELOG.md
  2. 8
      apps/api/src/app/admin/admin.controller.ts
  3. 8
      apps/api/src/services/data-provider/manual/manual.service.ts
  4. 35
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

4
CHANGELOG.md

@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the language localization for Catalan (`ca`) - Improved the language localization for Catalan (`ca`)
### Fixed
- Fixed an issue with the locale in the scraper configuration
## 2.174.0 - 2025-06-24 ## 2.174.0 - 2025-06-24
### Added ### Added

8
apps/api/src/app/admin/admin.controller.ts

@ -17,7 +17,8 @@ import {
AdminData, AdminData,
AdminMarketData, AdminMarketData,
AdminUsers, AdminUsers,
EnhancedSymbolProfile EnhancedSymbolProfile,
ScraperConfiguration
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions'; import { permissions } from '@ghostfolio/common/permissions';
import type { import type {
@ -222,13 +223,12 @@ export class AdminController {
@Post('market-data/:dataSource/:symbol/test') @Post('market-data/:dataSource/:symbol/test')
@UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async testMarketData( public async testMarketData(
@Body() data: { scraperConfiguration: string }, @Body() data: { scraperConfiguration: ScraperConfiguration },
@Param('dataSource') dataSource: DataSource, @Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string @Param('symbol') symbol: string
): Promise<{ price: number }> { ): Promise<{ price: number }> {
try { try {
const scraperConfiguration = JSON.parse(data.scraperConfiguration); const price = await this.manualService.test(data.scraperConfiguration);
const price = await this.manualService.test(scraperConfiguration);
if (price) { if (price) {
return { price }; return { price };

8
apps/api/src/services/data-provider/manual/manual.service.ts

@ -104,7 +104,7 @@ export class ManualService implements DataProviderInterface {
} }
return historical; return historical;
} else if (selector === undefined || url === undefined) { } else if (!selector || !url) {
return {}; return {};
} }
@ -162,7 +162,11 @@ export class ManualService implements DataProviderInterface {
const symbolProfilesWithScraperConfigurationAndInstantMode = const symbolProfilesWithScraperConfigurationAndInstantMode =
symbolProfiles.filter(({ scraperConfiguration }) => { symbolProfiles.filter(({ scraperConfiguration }) => {
return scraperConfiguration?.mode === 'instant'; return (
scraperConfiguration?.mode === 'instant' &&
scraperConfiguration?.selector &&
scraperConfiguration?.url
);
}); });
const scraperResultPromises = const scraperResultPromises =

35
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -11,6 +11,7 @@ import {
AdminMarketDataDetails, AdminMarketDataDetails,
AssetProfileIdentifier, AssetProfileIdentifier,
LineChartItem, LineChartItem,
ScraperConfiguration,
User User
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { translate } from '@ghostfolio/ui/i18n'; import { translate } from '@ghostfolio/ui/i18n';
@ -41,6 +42,7 @@ import {
AssetClass, AssetClass,
AssetSubClass, AssetSubClass,
MarketData, MarketData,
Prisma,
SymbolProfile SymbolProfile
} from '@prisma/client'; } from '@prisma/client';
import { format } from 'date-fns'; import { format } from 'date-fns';
@ -343,7 +345,10 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
public async onSubmitAssetProfileForm() { public async onSubmitAssetProfileForm() {
let countries = []; let countries = [];
let scraperConfiguration = {}; let scraperConfiguration: ScraperConfiguration = {
selector: '',
url: ''
};
let sectors = []; let sectors = [];
let symbolMapping = {}; let symbolMapping = {};
@ -354,9 +359,9 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
try { try {
scraperConfiguration = { scraperConfiguration = {
defaultMarketPrice: defaultMarketPrice:
this.assetProfileForm.controls['scraperConfiguration'].controls[ (this.assetProfileForm.controls['scraperConfiguration'].controls[
'defaultMarketPrice' 'defaultMarketPrice'
].value, ].value as number) || undefined,
headers: JSON.parse( headers: JSON.parse(
this.assetProfileForm.controls['scraperConfiguration'].controls[ this.assetProfileForm.controls['scraperConfiguration'].controls[
'headers' 'headers'
@ -365,10 +370,10 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
locale: locale:
this.assetProfileForm.controls['scraperConfiguration'].controls[ this.assetProfileForm.controls['scraperConfiguration'].controls[
'locale' 'locale'
].value, ].value || undefined,
mode: this.assetProfileForm.controls['scraperConfiguration'].controls[ mode: this.assetProfileForm.controls['scraperConfiguration'].controls[
'mode' 'mode'
].value, ].value as ScraperConfiguration['mode'],
selector: selector:
this.assetProfileForm.controls['scraperConfiguration'].controls[ this.assetProfileForm.controls['scraperConfiguration'].controls[
'selector' 'selector'
@ -377,6 +382,10 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
'url' 'url'
].value ].value
}; };
if (!scraperConfiguration.selector || !scraperConfiguration.url) {
scraperConfiguration = undefined;
}
} catch {} } catch {}
try { try {
@ -391,7 +400,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
const assetProfile: UpdateAssetProfileDto = { const assetProfile: UpdateAssetProfileDto = {
countries, countries,
scraperConfiguration,
sectors, sectors,
symbolMapping, symbolMapping,
assetClass: this.assetProfileForm.get('assetClass').value, assetClass: this.assetProfileForm.get('assetClass').value,
@ -400,6 +408,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
currency: this.assetProfileForm.get('currency').value, currency: this.assetProfileForm.get('currency').value,
isActive: this.assetProfileForm.get('isActive').value, isActive: this.assetProfileForm.get('isActive').value,
name: this.assetProfileForm.get('name').value, name: this.assetProfileForm.get('name').value,
scraperConfiguration:
scraperConfiguration as unknown as Prisma.InputJsonObject,
url: this.assetProfileForm.get('url').value || null url: this.assetProfileForm.get('url').value || null
}; };
@ -493,11 +503,10 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
this.adminService this.adminService
.testMarketData({ .testMarketData({
dataSource: this.data.dataSource, dataSource: this.data.dataSource,
scraperConfiguration: JSON.stringify({ scraperConfiguration: {
defaultMarketPrice: defaultMarketPrice: this.assetProfileForm.controls[
this.assetProfileForm.controls['scraperConfiguration'].controls[ 'scraperConfiguration'
'defaultMarketPrice' ].controls['defaultMarketPrice'].value as number,
].value,
headers: JSON.parse( headers: JSON.parse(
this.assetProfileForm.controls['scraperConfiguration'].controls[ this.assetProfileForm.controls['scraperConfiguration'].controls[
'headers' 'headers'
@ -506,7 +515,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
locale: locale:
this.assetProfileForm.controls['scraperConfiguration'].controls[ this.assetProfileForm.controls['scraperConfiguration'].controls[
'locale' 'locale'
].value, ].value || undefined,
mode: this.assetProfileForm.controls['scraperConfiguration'].controls[ mode: this.assetProfileForm.controls['scraperConfiguration'].controls[
'mode' 'mode'
].value, ].value,
@ -517,7 +526,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
url: this.assetProfileForm.controls['scraperConfiguration'].controls[ url: this.assetProfileForm.controls['scraperConfiguration'].controls[
'url' 'url'
].value ].value
}), },
symbol: this.data.symbol symbol: this.data.symbol
}) })
.pipe( .pipe(

Loading…
Cancel
Save