You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ghostfolio/apps/client/src/app/services/data.service.ts

386 lines
9.9 KiB

import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { CreateAccessDto } from '@ghostfolio/api/app/access/create-access.dto';
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activities } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
import { PortfolioPositionDetail } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
import { PortfolioPositions } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-positions.interface';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { SymbolItem } from '@ghostfolio/api/app/symbol/interfaces/symbol-item.interface';
import { UserItem } from '@ghostfolio/api/app/user/interfaces/user-item.interface';
import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto';
import { PropertyDto } from '@ghostfolio/api/services/property/property.dto';
import { DATE_FORMAT } from '@ghostfolio/common/helper';
import {
Access,
Accounts,
AdminData,
AdminMarketData,
BenchmarkMarketDataDetails,
BenchmarkResponse,
Export,
Filter,
InfoItem,
OAuthResponse,
PortfolioDetails,
PortfolioInvestments,
PortfolioPerformanceResponse,
PortfolioPublicDetails,
PortfolioReport,
UniqueAsset,
User
} from '@ghostfolio/common/interfaces';
import { filterGlobalPermissions } from '@ghostfolio/common/permissions';
import { AccountWithValue, DateRange } from '@ghostfolio/common/types';
import { DataSource, Order as OrderModel } from '@prisma/client';
import { format, parseISO } from 'date-fns';
import { cloneDeep, groupBy } from 'lodash';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class DataService {
public constructor(private http: HttpClient) {}
public createCheckoutSession({
couponId,
priceId
}: {
couponId?: string;
priceId: string;
}) {
return this.http.post('/api/v1/subscription/stripe/checkout-session', {
couponId,
priceId
});
}
public fetchAccount(aAccountId: string) {
return this.http.get<AccountWithValue>(`/api/v1/account/${aAccountId}`);
}
public fetchAccounts() {
return this.http.get<Accounts>('/api/v1/account');
}
public fetchActivities({
filters
}: {
filters?: Filter[];
}): Observable<Activities> {
return this.http
.get<any>('/api/v1/order', {
params: this.buildFiltersAsQueryParams({ filters })
})
.pipe(
map(({ activities }) => {
for (const activity of activities) {
activity.createdAt = parseISO(activity.createdAt);
activity.date = parseISO(activity.date);
}
return { activities };
})
);
}
public fetchAdminData() {
return this.http.get<AdminData>('/api/v1/admin');
}
public fetchAdminMarketData({ filters }: { filters?: Filter[] }) {
return this.http.get<AdminMarketData>('/api/v1/admin/market-data', {
params: this.buildFiltersAsQueryParams({ filters })
});
}
public deleteAccess(aId: string) {
return this.http.delete<any>(`/api/v1/access/${aId}`);
}
public deleteAccount(aId: string) {
return this.http.delete<any>(`/api/v1/account/${aId}`);
}
public deleteOrder(aId: string) {
return this.http.delete<any>(`/api/v1/order/${aId}`);
}
public deleteUser(aId: string) {
return this.http.delete<any>(`/api/v1/user/${aId}`);
}
public fetchAccesses() {
return this.http.get<Access[]>('/api/v1/access');
}
public fetchBenchmarkBySymbol({
dataSource,
startDate,
symbol
}: {
startDate: Date;
} & UniqueAsset): Observable<BenchmarkMarketDataDetails> {
return this.http.get<BenchmarkMarketDataDetails>(
`/api/v1/benchmark/${dataSource}/${symbol}/${format(
startDate,
DATE_FORMAT
)}`
);
}
public fetchBenchmarks() {
return this.http.get<BenchmarkResponse>('/api/v1/benchmark');
}
public fetchExport(activityIds?: string[]) {
let params = new HttpParams();
if (activityIds) {
params = params.append('activityIds', activityIds.join(','));
}
return this.http.get<Export>('/api/v1/export', {
params
});
}
public fetchInfo(): InfoItem {
const info = cloneDeep((window as any).info);
const utmSource = <'ios' | 'trusted-web-activity'>(
window.localStorage.getItem('utm_source')
);
info.globalPermissions = filterGlobalPermissions(
info.globalPermissions,
utmSource
);
return info;
}
public fetchInvestments({
groupBy,
range
}: {
groupBy?: 'month';
range: DateRange;
}) {
return this.http.get<PortfolioInvestments>(
'/api/v1/portfolio/investments',
{ params: { groupBy, range } }
);
}
public fetchSymbolItem({
dataSource,
includeHistoricalData,
symbol
}: {
dataSource: DataSource | string;
includeHistoricalData?: number;
symbol: string;
}) {
let params = new HttpParams();
if (includeHistoricalData) {
params = params.append('includeHistoricalData', includeHistoricalData);
}
return this.http.get<SymbolItem>(`/api/v1/symbol/${dataSource}/${symbol}`, {
params
});
}
public fetchPositions({
range
}: {
range: DateRange;
}): Observable<PortfolioPositions> {
return this.http.get<PortfolioPositions>('/api/v1/portfolio/positions', {
params: { range }
});
}
public fetchSymbols(aQuery: string) {
return this.http
.get<{ items: LookupItem[] }>(`/api/v1/symbol/lookup?query=${aQuery}`)
.pipe(
map((respose) => {
return respose.items;
})
);
}
public fetchPortfolioDetails({
filters
}: {
filters?: Filter[];
}): Observable<PortfolioDetails> {
return this.http
.get<any>('/api/v1/portfolio/details', {
params: this.buildFiltersAsQueryParams({ filters })
})
.pipe(
map((response) => {
if (response.summary?.firstOrderDate) {
response.summary.firstOrderDate = parseISO(
response.summary.firstOrderDate
);
}
return response;
})
);
}
public fetchPortfolioPerformance({
range
}: {
range: DateRange;
}): Observable<PortfolioPerformanceResponse> {
return this.http
.get<any>(`/api/v2/portfolio/performance`, { params: { range } })
.pipe(
map((response) => {
if (response.firstOrderDate) {
response.firstOrderDate = parseISO(response.firstOrderDate);
}
return response;
})
);
}
public fetchPortfolioPublic(aId: string) {
return this.http.get<PortfolioPublicDetails>(
`/api/v1/portfolio/public/${aId}`
);
}
public fetchPortfolioReport() {
return this.http.get<PortfolioReport>('/api/v1/portfolio/report');
}
public fetchPositionDetail({
dataSource,
symbol
}: {
dataSource: DataSource;
symbol: string;
}) {
return this.http
.get<PortfolioPositionDetail>(
`/api/v1/portfolio/position/${dataSource}/${symbol}`
)
.pipe(
map((data) => {
if (data.orders) {
for (const order of data.orders) {
order.createdAt = parseISO(<string>(<unknown>order.createdAt));
order.date = parseISO(<string>(<unknown>order.date));
}
}
return data;
})
);
}
public loginAnonymous(accessToken: string) {
return this.http.get<OAuthResponse>(
`/api/v1/auth/anonymous/${accessToken}`
);
}
public postAccess(aAccess: CreateAccessDto) {
return this.http.post<OrderModel>(`/api/v1/access`, aAccess);
}
public postAccount(aAccount: CreateAccountDto) {
return this.http.post<OrderModel>(`/api/v1/account`, aAccount);
}
public postOrder(aOrder: CreateOrderDto) {
return this.http.post<OrderModel>(`/api/v1/order`, aOrder);
}
public postUser() {
return this.http.post<UserItem>(`/api/v1/user`, {});
}
public putAccount(aAccount: UpdateAccountDto) {
return this.http.put<UserItem>(`/api/v1/account/${aAccount.id}`, aAccount);
}
public putAdminSetting(key: string, aData: PropertyDto) {
return this.http.put<void>(`/api/v1/admin/settings/${key}`, aData);
}
public putOrder(aOrder: UpdateOrderDto) {
return this.http.put<UserItem>(`/api/v1/order/${aOrder.id}`, aOrder);
}
public putUserSetting(aData: UpdateUserSettingDto) {
return this.http.put<User>(`/api/v1/user/setting`, aData);
}
public redeemCoupon(couponCode: string) {
return this.http.post('/api/v1/subscription/redeem-coupon', {
couponCode
});
}
private buildFiltersAsQueryParams({ filters }: { filters?: Filter[] }) {
let params = new HttpParams();
if (filters?.length > 0) {
const {
ACCOUNT: filtersByAccount,
ASSET_CLASS: filtersByAssetClass,
TAG: filtersByTag
} = groupBy(filters, (filter) => {
return filter.type;
});
if (filtersByAccount) {
params = params.append(
'accounts',
filtersByAccount
.map(({ id }) => {
return id;
})
.join(',')
);
}
if (filtersByAssetClass) {
params = params.append(
'assetClasses',
filtersByAssetClass
.map(({ id }) => {
return id;
})
.join(',')
);
}
if (filtersByTag) {
params = params.append(
'tags',
filtersByTag
.map(({ id }) => {
return id;
})
.join(',')
);
}
}
return params;
}
}