Browse Source

Bugfix/encode symbols with special characters in API request urls (#7110)

* Encode symbols in API request urls

* Update changelog
pull/7107/head
Thomas Kaul 6 days ago
committed by GitHub
parent
commit
f53998f071
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 6
      apps/client/src/app/pages/api/api-page.component.ts
  3. 14
      libs/ui/src/lib/services/admin.service.ts
  4. 49
      libs/ui/src/lib/services/data.service.ts

1
CHANGELOG.md

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed ### 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 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 - Fixed the persistence of an empty `locale` string in the scraper configuration

6
apps/client/src/app/pages/api/api-page.component.ts

@ -94,7 +94,7 @@ export class GfApiPageComponent implements OnInit {
private fetchAssetProfile({ symbol }: { symbol: string }) { private fetchAssetProfile({ symbol }: { symbol: string }) {
return this.http return this.http
.get<DataProviderGhostfolioAssetProfileResponse>( .get<DataProviderGhostfolioAssetProfileResponse>(
`/api/v1/data-providers/ghostfolio/asset-profile/${symbol}`, `/api/v1/data-providers/ghostfolio/asset-profile/${encodeURIComponent(symbol)}`,
{ headers: this.getHeaders() } { headers: this.getHeaders() }
) )
.pipe(this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef)); .pipe(this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef));
@ -107,7 +107,7 @@ export class GfApiPageComponent implements OnInit {
return this.http return this.http
.get<DividendsResponse>( .get<DividendsResponse>(
`/api/v2/data-providers/ghostfolio/dividends/${symbol}`, `/api/v2/data-providers/ghostfolio/dividends/${encodeURIComponent(symbol)}`,
{ {
params, params,
headers: this.getHeaders() headers: this.getHeaders()
@ -129,7 +129,7 @@ export class GfApiPageComponent implements OnInit {
return this.http return this.http
.get<HistoricalResponse>( .get<HistoricalResponse>(
`/api/v2/data-providers/ghostfolio/historical/${symbol}`, `/api/v2/data-providers/ghostfolio/historical/${encodeURIComponent(symbol)}`,
{ {
params, params,
headers: this.getHeaders() headers: this.getHeaders()

14
libs/ui/src/lib/services/admin.service.ts

@ -36,7 +36,7 @@ export class AdminService {
public addAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) { public addAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) {
return this.http.post<void>( return this.http.post<void>(
`/api/v1/admin/profile-data/${dataSource}/${symbol}`, `/api/v1/admin/profile-data/${dataSource}/${encodeURIComponent(symbol)}`,
null null
); );
} }
@ -63,7 +63,7 @@ export class AdminService {
public deleteProfileData({ dataSource, symbol }: AssetProfileIdentifier) { public deleteProfileData({ dataSource, symbol }: AssetProfileIdentifier) {
return this.http.delete<void>( return this.http.delete<void>(
`/api/v1/admin/profile-data/${dataSource}/${symbol}` `/api/v1/admin/profile-data/${dataSource}/${encodeURIComponent(symbol)}`
); );
} }
@ -140,7 +140,7 @@ export class AdminService {
symbol symbol
}: AssetProfileIdentifier) { }: AssetProfileIdentifier) {
return this.http.post<void>( return this.http.post<void>(
`/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); 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<MarketData | void>(url, undefined, { params }); return this.http.post<MarketData | void>(url, undefined, { params });
} }
@ -172,7 +172,7 @@ export class AdminService {
dateString, dateString,
symbol symbol
}: { dateString: string } & AssetProfileIdentifier) { }: { dateString: string } & AssetProfileIdentifier) {
const url = `/api/v1/symbol/${dataSource}/${symbol}/${dateString}`; const url = `/api/v1/symbol/${dataSource}/${encodeURIComponent(symbol)}/${dateString}`;
return this.http.get<DataProviderHistoricalResponse>(url); return this.http.get<DataProviderHistoricalResponse>(url);
} }
@ -197,7 +197,7 @@ export class AdminService {
}: UpdateAssetProfileDto }: UpdateAssetProfileDto
) { ) {
return this.http.patch<EnhancedSymbolProfile>( return this.http.patch<EnhancedSymbolProfile>(
`/api/v1/admin/profile-data/${dataSource}/${symbol}`, `/api/v1/admin/profile-data/${dataSource}/${encodeURIComponent(symbol)}`,
{ {
assetClass, assetClass,
assetSubClass, assetSubClass,
@ -238,7 +238,7 @@ export class AdminService {
symbol symbol
}: AssetProfileIdentifier & UpdateAssetProfileDto['scraperConfiguration']) { }: AssetProfileIdentifier & UpdateAssetProfileDto['scraperConfiguration']) {
return this.http.post<{ price: number }>( return this.http.post<{ price: number }>(
`/api/v1/admin/market-data/${dataSource}/${symbol}/test`, `/api/v1/admin/market-data/${dataSource}/${encodeURIComponent(symbol)}/test`,
{ {
scraperConfiguration scraperConfiguration
} }

49
libs/ui/src/lib/services/data.service.ts

@ -301,7 +301,7 @@ export class DataService {
public fetchDividendsImport({ dataSource, symbol }: AssetProfileIdentifier) { public fetchDividendsImport({ dataSource, symbol }: AssetProfileIdentifier) {
return this.http.get<ImportResponse>( return this.http.get<ImportResponse>(
`/api/v1/import/dividends/${dataSource}/${symbol}` `/api/v1/import/dividends/${dataSource}/${encodeURIComponent(symbol)}`
); );
} }
@ -313,7 +313,7 @@ export class DataService {
symbol: string; symbol: string;
}) { }) {
return this.http.get<DataProviderHistoricalResponse>( return this.http.get<DataProviderHistoricalResponse>(
`/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) { public deleteBenchmark({ dataSource, symbol }: AssetProfileIdentifier) {
return this.http.delete<Partial<SymbolProfile>>( return this.http.delete<Partial<SymbolProfile>>(
`/api/v1/benchmarks/${dataSource}/${symbol}` `/api/v1/benchmarks/${dataSource}/${encodeURIComponent(symbol)}`
); );
} }
@ -358,7 +358,9 @@ export class DataService {
} }
public deleteWatchlistItem({ dataSource, symbol }: AssetProfileIdentifier) { public deleteWatchlistItem({ dataSource, symbol }: AssetProfileIdentifier) {
return this.http.delete<void>(`/api/v1/watchlist/${dataSource}/${symbol}`); return this.http.delete<void>(
`/api/v1/watchlist/${dataSource}/${encodeURIComponent(symbol)}`
);
} }
public fetchAccesses() { public fetchAccesses() {
@ -369,14 +371,16 @@ export class DataService {
dataSource, dataSource,
symbol symbol
}: AssetProfileIdentifier): Observable<AssetResponse> { }: AssetProfileIdentifier): Observable<AssetResponse> {
return this.http.get<any>(`/api/v1/asset/${dataSource}/${symbol}`).pipe( return this.http
map((data) => { .get<any>(`/api/v1/asset/${dataSource}/${encodeURIComponent(symbol)}`)
for (const item of data.marketData) { .pipe(
item.date = parseISO(item.date); map((data) => {
} for (const item of data.marketData) {
return data; item.date = parseISO(item.date);
}) }
); return data;
})
);
} }
public fetchAssetProfiles({ public fetchAssetProfiles({
@ -437,7 +441,7 @@ export class DataService {
} }
return this.http.get<BenchmarkMarketDataDetailsResponse>( return this.http.get<BenchmarkMarketDataDetailsResponse>(
`/api/v1/benchmarks/${dataSource}/${symbol}/${format(startDate, DATE_FORMAT, { in: utc })}`, `/api/v1/benchmarks/${dataSource}/${encodeURIComponent(symbol)}/${format(startDate, DATE_FORMAT, { in: utc })}`,
{ params } { params }
); );
} }
@ -486,7 +490,7 @@ export class DataService {
> { > {
return this.http return this.http
.get<PortfolioHoldingResponse>( .get<PortfolioHoldingResponse>(
`/api/v1/portfolio/holding/${dataSource}/${symbol}` `/api/v1/portfolio/holding/${dataSource}/${encodeURIComponent(symbol)}`
) )
.pipe( .pipe(
map((response) => { map((response) => {
@ -540,7 +544,9 @@ export class DataService {
symbol symbol
}: AssetProfileIdentifier): Observable<AssetProfileResponse> { }: AssetProfileIdentifier): Observable<AssetProfileResponse> {
return this.http return this.http
.get<any>(`/api/v1/asset-profiles/${dataSource}/${symbol}`) .get<any>(
`/api/v1/asset-profiles/${dataSource}/${encodeURIComponent(symbol)}`
)
.pipe( .pipe(
map((data) => { map((data) => {
for (const item of data.marketData) { for (const item of data.marketData) {
@ -778,9 +784,12 @@ export class DataService {
params = params.append('includeHistoricalData', includeHistoricalData); params = params.append('includeHistoricalData', includeHistoricalData);
} }
return this.http.get<SymbolItem>(`/api/v1/symbol/${dataSource}/${symbol}`, { return this.http.get<SymbolItem>(
params `/api/v1/symbol/${dataSource}/${encodeURIComponent(symbol)}`,
}); {
params
}
);
} }
public fetchSymbols({ public fetchSymbols({
@ -851,7 +860,7 @@ export class DataService {
marketData, marketData,
symbol symbol
}: { marketData: UpdateBulkMarketDataDto } & AssetProfileIdentifier) { }: { marketData: UpdateBulkMarketDataDto } & AssetProfileIdentifier) {
const url = `/api/v1/market-data/${dataSource}/${symbol}`; const url = `/api/v1/market-data/${dataSource}/${encodeURIComponent(symbol)}`;
return this.http.post<MarketData>(url, marketData); return this.http.post<MarketData>(url, marketData);
} }
@ -890,7 +899,7 @@ export class DataService {
tags tags
}: { tags: Tag[] } & AssetProfileIdentifier) { }: { tags: Tag[] } & AssetProfileIdentifier) {
return this.http.put<void>( return this.http.put<void>(
`/api/v1/portfolio/holding/${dataSource}/${symbol}/tags`, `/api/v1/portfolio/holding/${dataSource}/${encodeURIComponent(symbol)}/tags`,
{ tags } { tags }
); );
} }

Loading…
Cancel
Save