diff --git a/CHANGELOG.md b/CHANGELOG.md index 0faaca8d0..4103287cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue where symbols with special characters caused API request failures by URL encoding the symbol - Fixed the disabled state of the delete action in the asset profiles actions menu of the historical market data table in the admin control panel - Fixed the persistence of an empty `locale` string in the scraper configuration diff --git a/apps/client/src/app/pages/api/api-page.component.ts b/apps/client/src/app/pages/api/api-page.component.ts index e75a51c73..b949cfb0f 100644 --- a/apps/client/src/app/pages/api/api-page.component.ts +++ b/apps/client/src/app/pages/api/api-page.component.ts @@ -94,7 +94,7 @@ export class GfApiPageComponent implements OnInit { private fetchAssetProfile({ symbol }: { symbol: string }) { return this.http .get( - `/api/v1/data-providers/ghostfolio/asset-profile/${symbol}`, + `/api/v1/data-providers/ghostfolio/asset-profile/${encodeURIComponent(symbol)}`, { headers: this.getHeaders() } ) .pipe(this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef)); @@ -107,7 +107,7 @@ export class GfApiPageComponent implements OnInit { return this.http .get( - `/api/v2/data-providers/ghostfolio/dividends/${symbol}`, + `/api/v2/data-providers/ghostfolio/dividends/${encodeURIComponent(symbol)}`, { params, headers: this.getHeaders() @@ -129,7 +129,7 @@ export class GfApiPageComponent implements OnInit { return this.http .get( - `/api/v2/data-providers/ghostfolio/historical/${symbol}`, + `/api/v2/data-providers/ghostfolio/historical/${encodeURIComponent(symbol)}`, { params, headers: this.getHeaders() diff --git a/libs/ui/src/lib/services/admin.service.ts b/libs/ui/src/lib/services/admin.service.ts index c767c4f48..4d2f6e48f 100644 --- a/libs/ui/src/lib/services/admin.service.ts +++ b/libs/ui/src/lib/services/admin.service.ts @@ -36,7 +36,7 @@ export class AdminService { public addAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) { return this.http.post( - `/api/v1/admin/profile-data/${dataSource}/${symbol}`, + `/api/v1/admin/profile-data/${dataSource}/${encodeURIComponent(symbol)}`, null ); } @@ -63,7 +63,7 @@ export class AdminService { public deleteProfileData({ dataSource, symbol }: AssetProfileIdentifier) { return this.http.delete( - `/api/v1/admin/profile-data/${dataSource}/${symbol}` + `/api/v1/admin/profile-data/${dataSource}/${encodeURIComponent(symbol)}` ); } @@ -140,7 +140,7 @@ export class AdminService { symbol }: AssetProfileIdentifier) { return this.http.post( - `/api/v1/admin/gather/profile-data/${dataSource}/${symbol}`, + `/api/v1/admin/gather/profile-data/${dataSource}/${encodeURIComponent(symbol)}`, {} ); } @@ -162,7 +162,7 @@ export class AdminService { params = params.append('range', range); } - const url = `/api/v1/admin/gather/${dataSource}/${symbol}`; + const url = `/api/v1/admin/gather/${dataSource}/${encodeURIComponent(symbol)}`; return this.http.post(url, undefined, { params }); } @@ -172,7 +172,7 @@ export class AdminService { dateString, symbol }: { dateString: string } & AssetProfileIdentifier) { - const url = `/api/v1/symbol/${dataSource}/${symbol}/${dateString}`; + const url = `/api/v1/symbol/${dataSource}/${encodeURIComponent(symbol)}/${dateString}`; return this.http.get(url); } @@ -197,7 +197,7 @@ export class AdminService { }: UpdateAssetProfileDto ) { return this.http.patch( - `/api/v1/admin/profile-data/${dataSource}/${symbol}`, + `/api/v1/admin/profile-data/${dataSource}/${encodeURIComponent(symbol)}`, { assetClass, assetSubClass, @@ -238,7 +238,7 @@ export class AdminService { symbol }: AssetProfileIdentifier & UpdateAssetProfileDto['scraperConfiguration']) { return this.http.post<{ price: number }>( - `/api/v1/admin/market-data/${dataSource}/${symbol}/test`, + `/api/v1/admin/market-data/${dataSource}/${encodeURIComponent(symbol)}/test`, { scraperConfiguration } diff --git a/libs/ui/src/lib/services/data.service.ts b/libs/ui/src/lib/services/data.service.ts index 8d16c6d35..f49beab23 100644 --- a/libs/ui/src/lib/services/data.service.ts +++ b/libs/ui/src/lib/services/data.service.ts @@ -301,7 +301,7 @@ export class DataService { public fetchDividendsImport({ dataSource, symbol }: AssetProfileIdentifier) { return this.http.get( - `/api/v1/import/dividends/${dataSource}/${symbol}` + `/api/v1/import/dividends/${dataSource}/${encodeURIComponent(symbol)}` ); } @@ -313,7 +313,7 @@ export class DataService { symbol: string; }) { return this.http.get( - `/api/v1/exchange-rate/${symbol}/${format(date, DATE_FORMAT, { in: utc })}` + `/api/v1/exchange-rate/${encodeURIComponent(symbol)}/${format(date, DATE_FORMAT, { in: utc })}` ); } @@ -341,7 +341,7 @@ export class DataService { public deleteBenchmark({ dataSource, symbol }: AssetProfileIdentifier) { return this.http.delete>( - `/api/v1/benchmarks/${dataSource}/${symbol}` + `/api/v1/benchmarks/${dataSource}/${encodeURIComponent(symbol)}` ); } @@ -358,7 +358,9 @@ export class DataService { } public deleteWatchlistItem({ dataSource, symbol }: AssetProfileIdentifier) { - return this.http.delete(`/api/v1/watchlist/${dataSource}/${symbol}`); + return this.http.delete( + `/api/v1/watchlist/${dataSource}/${encodeURIComponent(symbol)}` + ); } public fetchAccesses() { @@ -369,14 +371,16 @@ export class DataService { dataSource, symbol }: AssetProfileIdentifier): Observable { - return this.http.get(`/api/v1/asset/${dataSource}/${symbol}`).pipe( - map((data) => { - for (const item of data.marketData) { - item.date = parseISO(item.date); - } - return data; - }) - ); + return this.http + .get(`/api/v1/asset/${dataSource}/${encodeURIComponent(symbol)}`) + .pipe( + map((data) => { + for (const item of data.marketData) { + item.date = parseISO(item.date); + } + return data; + }) + ); } public fetchAssetProfiles({ @@ -437,7 +441,7 @@ export class DataService { } return this.http.get( - `/api/v1/benchmarks/${dataSource}/${symbol}/${format(startDate, DATE_FORMAT, { in: utc })}`, + `/api/v1/benchmarks/${dataSource}/${encodeURIComponent(symbol)}/${format(startDate, DATE_FORMAT, { in: utc })}`, { params } ); } @@ -486,7 +490,7 @@ export class DataService { > { return this.http .get( - `/api/v1/portfolio/holding/${dataSource}/${symbol}` + `/api/v1/portfolio/holding/${dataSource}/${encodeURIComponent(symbol)}` ) .pipe( map((response) => { @@ -540,7 +544,9 @@ export class DataService { symbol }: AssetProfileIdentifier): Observable { return this.http - .get(`/api/v1/asset-profiles/${dataSource}/${symbol}`) + .get( + `/api/v1/asset-profiles/${dataSource}/${encodeURIComponent(symbol)}` + ) .pipe( map((data) => { for (const item of data.marketData) { @@ -778,9 +784,12 @@ export class DataService { params = params.append('includeHistoricalData', includeHistoricalData); } - return this.http.get(`/api/v1/symbol/${dataSource}/${symbol}`, { - params - }); + return this.http.get( + `/api/v1/symbol/${dataSource}/${encodeURIComponent(symbol)}`, + { + params + } + ); } public fetchSymbols({ @@ -851,7 +860,7 @@ export class DataService { marketData, symbol }: { marketData: UpdateBulkMarketDataDto } & AssetProfileIdentifier) { - const url = `/api/v1/market-data/${dataSource}/${symbol}`; + const url = `/api/v1/market-data/${dataSource}/${encodeURIComponent(symbol)}`; return this.http.post(url, marketData); } @@ -890,7 +899,7 @@ export class DataService { tags }: { tags: Tag[] } & AssetProfileIdentifier) { return this.http.put( - `/api/v1/portfolio/holding/${dataSource}/${symbol}/tags`, + `/api/v1/portfolio/holding/${dataSource}/${encodeURIComponent(symbol)}/tags`, { tags } ); }