Feature/setup api versioning (#783)

* Setup API versioning

* Update changelog
pull/786/head
Thomas Kaul 3 years ago committed by GitHub
parent eb77652d6a
commit 6762572658
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added API versioning
- Added more durations in the coupon system
### Changed

@ -1,4 +1,4 @@
import { Logger, ValidationPipe } from '@nestjs/common';
import { Logger, ValidationPipe, VersioningType } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app/app.module';
@ -7,8 +7,11 @@ import { environment } from './environments/environment';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors();
const globalPrefix = 'api';
app.setGlobalPrefix(globalPrefix);
app.enableVersioning({
defaultVersion: '1',
type: VersioningType.URI
});
app.setGlobalPrefix('api');
app.useGlobalPipes(
new ValidationPipe({
forbidNonWhitelisted: true,

@ -8,7 +8,7 @@
<div>
<ng-container *ngIf="data.hasPermissionToUseSocialLogin">
<div class="text-center">
<a color="accent" href="/api/auth/google" mat-flat-button
<a color="accent" href="/api/v1/auth/google" mat-flat-button
><ion-icon class="mr-1" name="logo-google"></ion-icon
><span i18n>Sign in with Google</span></a
>

@ -35,7 +35,7 @@
>
or
</div>
<a color="accent" href="/api/auth/google" mat-flat-button
<a color="accent" href="/api/v1/auth/google" mat-flat-button
><ion-icon class="mr-1" name="logo-google"></ion-icon
><span i18n>Continue with Google</span></a
>

@ -19,7 +19,7 @@ export class AdminService {
public deleteProfileData({ dataSource, symbol }: UniqueAsset) {
return this.http.delete<void>(
`/api/admin/profile-data/${dataSource}/${symbol}`
`/api/v1/admin/profile-data/${dataSource}/${symbol}`
);
}
@ -31,7 +31,7 @@ export class AdminService {
symbol: string;
}): Observable<AdminMarketDataDetails> {
return this.http
.get<any>(`/api/admin/market-data/${dataSource}/${symbol}`)
.get<any>(`/api/v1/admin/market-data/${dataSource}/${symbol}`)
.pipe(
map((data) => {
for (const item of data.marketData) {
@ -43,16 +43,16 @@ export class AdminService {
}
public gatherMax() {
return this.http.post<void>(`/api/admin/gather/max`, {});
return this.http.post<void>(`/api/v1/admin/gather/max`, {});
}
public gatherProfileData() {
return this.http.post<void>(`/api/admin/gather/profile-data`, {});
return this.http.post<void>(`/api/v1/admin/gather/profile-data`, {});
}
public gatherProfileDataBySymbol({ dataSource, symbol }: UniqueAsset) {
return this.http.post<void>(
`/api/admin/gather/profile-data/${dataSource}/${symbol}`,
`/api/v1/admin/gather/profile-data/${dataSource}/${symbol}`,
{}
);
}
@ -64,7 +64,7 @@ export class AdminService {
}: UniqueAsset & {
date?: Date;
}) {
let url = `/api/admin/gather/${dataSource}/${symbol}`;
let url = `/api/v1/admin/gather/${dataSource}/${symbol}`;
if (date) {
url = `${url}/${format(date, DATE_FORMAT)}`;
@ -82,7 +82,7 @@ export class AdminService {
date: Date;
symbol: string;
}) {
const url = `/api/symbol/${dataSource}/${symbol}/${format(
const url = `/api/v1/symbol/${dataSource}/${symbol}/${format(
date,
DATE_FORMAT
)}`;
@ -101,7 +101,7 @@ export class AdminService {
marketData: UpdateMarketDataDto;
symbol: string;
}) {
const url = `/api/admin/market-data/${dataSource}/${symbol}/${format(
const url = `/api/v1/admin/market-data/${dataSource}/${symbol}/${format(
date,
DATE_FORMAT
)}`;

@ -8,6 +8,6 @@ export class CacheService {
public constructor(private http: HttpClient) {}
public flush() {
return this.http.post<any>(`/api/cache/flush`, {});
return this.http.post<any>(`/api/v1/cache/flush`, {});
}
}

@ -23,12 +23,10 @@ import {
PortfolioChart,
PortfolioDetails,
PortfolioInvestments,
PortfolioPerformance,
PortfolioPerformanceResponse,
PortfolioPublicDetails,
PortfolioReport,
PortfolioSummary,
UniqueAsset,
User
} from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
@ -52,46 +50,46 @@ export class DataService {
couponId?: string;
priceId: string;
}) {
return this.http.post('/api/subscription/stripe/checkout-session', {
return this.http.post('/api/v1/subscription/stripe/checkout-session', {
couponId,
priceId
});
}
public fetchAccounts() {
return this.http.get<Accounts>('/api/account');
return this.http.get<Accounts>('/api/v1/account');
}
public fetchAdminData() {
return this.http.get<AdminData>('/api/admin');
return this.http.get<AdminData>('/api/v1/admin');
}
public fetchAdminMarketData() {
return this.http.get<AdminMarketData>('/api/admin/market-data');
return this.http.get<AdminMarketData>('/api/v1/admin/market-data');
}
public deleteAccess(aId: string) {
return this.http.delete<any>(`/api/access/${aId}`);
return this.http.delete<any>(`/api/v1/access/${aId}`);
}
public deleteAccount(aId: string) {
return this.http.delete<any>(`/api/account/${aId}`);
return this.http.delete<any>(`/api/v1/account/${aId}`);
}
public deleteOrder(aId: string) {
return this.http.delete<any>(`/api/order/${aId}`);
return this.http.delete<any>(`/api/v1/order/${aId}`);
}
public deleteUser(aId: string) {
return this.http.delete<any>(`/api/user/${aId}`);
return this.http.delete<any>(`/api/v1/user/${aId}`);
}
public fetchAccesses() {
return this.http.get<Access[]>('/api/access');
return this.http.get<Access[]>('/api/v1/access');
}
public fetchChart({ range }: { range: DateRange }) {
return this.http.get<PortfolioChart>('/api/portfolio/chart', {
return this.http.get<PortfolioChart>('/api/v1/portfolio/chart', {
params: { range }
});
}
@ -103,7 +101,7 @@ export class DataService {
params = params.append('activityIds', activityIds.join(','));
}
return this.http.get<Export>('/api/export', {
return this.http.get<Export>('/api/v1/export', {
params
});
}
@ -121,7 +119,7 @@ export class DataService {
}
public fetchInvestments(): Observable<PortfolioInvestments> {
return this.http.get<any>('/api/portfolio/investments').pipe(
return this.http.get<any>('/api/v1/portfolio/investments').pipe(
map((response) => {
if (response.firstOrderDate) {
response.firstOrderDate = parseISO(response.firstOrderDate);
@ -147,7 +145,7 @@ export class DataService {
params = params.append('includeHistoricalData', includeHistoricalData);
}
return this.http.get<SymbolItem>(`/api/symbol/${dataSource}/${symbol}`, {
return this.http.get<SymbolItem>(`/api/v1/symbol/${dataSource}/${symbol}`, {
params
});
}
@ -157,14 +155,14 @@ export class DataService {
}: {
range: DateRange;
}): Observable<PortfolioPositions> {
return this.http.get<PortfolioPositions>('/api/portfolio/positions', {
return this.http.get<PortfolioPositions>('/api/v1/portfolio/positions', {
params: { range }
});
}
public fetchSymbols(aQuery: string) {
return this.http
.get<{ items: LookupItem[] }>(`/api/symbol/lookup?query=${aQuery}`)
.get<{ items: LookupItem[] }>(`/api/v1/symbol/lookup?query=${aQuery}`)
.pipe(
map((respose) => {
return respose.items;
@ -173,7 +171,7 @@ export class DataService {
}
public fetchOrders(): Observable<Activities> {
return this.http.get<any>('/api/order').pipe(
return this.http.get<any>('/api/v1/order').pipe(
map(({ activities }) => {
for (const activity of activities) {
activity.createdAt = parseISO(activity.createdAt);
@ -185,14 +183,14 @@ export class DataService {
}
public fetchPortfolioDetails(aParams: { [param: string]: any }) {
return this.http.get<PortfolioDetails>('/api/portfolio/details', {
return this.http.get<PortfolioDetails>('/api/v1/portfolio/details', {
params: aParams
});
}
public fetchPortfolioPerformance(params: { [param: string]: any }) {
return this.http.get<PortfolioPerformanceResponse>(
'/api/portfolio/performance',
'/api/v1/portfolio/performance',
{
params
}
@ -201,16 +199,16 @@ export class DataService {
public fetchPortfolioPublic(aId: string) {
return this.http.get<PortfolioPublicDetails>(
`/api/portfolio/public/${aId}`
`/api/v1/portfolio/public/${aId}`
);
}
public fetchPortfolioReport() {
return this.http.get<PortfolioReport>('/api/portfolio/report');
return this.http.get<PortfolioReport>('/api/v1/portfolio/report');
}
public fetchPortfolioSummary(): Observable<PortfolioSummary> {
return this.http.get<any>('/api/portfolio/summary').pipe(
return this.http.get<any>('/api/v1/portfolio/summary').pipe(
map((summary) => {
if (summary.firstOrderDate) {
summary.firstOrderDate = parseISO(summary.firstOrderDate);
@ -229,7 +227,7 @@ export class DataService {
symbol: string;
}) {
return this.http
.get<any>(`/api/portfolio/position/${dataSource}/${symbol}`)
.get<any>(`/api/v1/portfolio/position/${dataSource}/${symbol}`)
.pipe(
map((data) => {
if (data.orders) {
@ -245,47 +243,47 @@ export class DataService {
}
public loginAnonymous(accessToken: string) {
return this.http.get<any>(`/api/auth/anonymous/${accessToken}`);
return this.http.get<any>(`/api/v1/auth/anonymous/${accessToken}`);
}
public postAccess(aAccess: CreateAccessDto) {
return this.http.post<OrderModel>(`/api/access`, aAccess);
return this.http.post<OrderModel>(`/api/v1/access`, aAccess);
}
public postAccount(aAccount: CreateAccountDto) {
return this.http.post<OrderModel>(`/api/account`, aAccount);
return this.http.post<OrderModel>(`/api/v1/account`, aAccount);
}
public postOrder(aOrder: CreateOrderDto) {
return this.http.post<OrderModel>(`/api/order`, aOrder);
return this.http.post<OrderModel>(`/api/v1/order`, aOrder);
}
public postUser() {
return this.http.post<UserItem>(`/api/user`, {});
return this.http.post<UserItem>(`/api/v1/user`, {});
}
public putAccount(aAccount: UpdateAccountDto) {
return this.http.put<UserItem>(`/api/account/${aAccount.id}`, aAccount);
return this.http.put<UserItem>(`/api/v1/account/${aAccount.id}`, aAccount);
}
public putAdminSetting(key: string, aData: PropertyDto) {
return this.http.put<void>(`/api/admin/settings/${key}`, aData);
return this.http.put<void>(`/api/v1/admin/settings/${key}`, aData);
}
public putOrder(aOrder: UpdateOrderDto) {
return this.http.put<UserItem>(`/api/order/${aOrder.id}`, aOrder);
return this.http.put<UserItem>(`/api/v1/order/${aOrder.id}`, aOrder);
}
public putUserSetting(aData: UpdateUserSettingDto) {
return this.http.put<User>(`/api/user/setting`, aData);
return this.http.put<User>(`/api/v1/user/setting`, aData);
}
public putUserSettings(aData: UpdateUserSettingsDto) {
return this.http.put<User>(`/api/user/settings`, aData);
return this.http.put<User>(`/api/v1/user/settings`, aData);
}
public redeemCoupon(couponCode: string) {
return this.http.post('/api/subscription/redeem-coupon', {
return this.http.post('/api/v1/subscription/redeem-coupon', {
couponCode
});
}

@ -282,6 +282,6 @@ export class ImportTransactionsService {
}
private postImport(aImportData: { orders: CreateOrderDto[] }) {
return this.http.post<void>('/api/import', aImportData);
return this.http.post<void>('/api/v1/import', aImportData);
}
}

@ -36,7 +36,7 @@ export class UserService extends ObservableStore<UserStoreState> {
}
private fetchUser() {
return this.http.get<User>('/api/user').pipe(
return this.http.get<User>('/api/v1/user').pipe(
map((user) => {
this.setState({ user }, UserStoreActions.GetUser);
return user;

@ -35,7 +35,7 @@ export class WebAuthnService {
public register() {
return this.http
.get<PublicKeyCredentialCreationOptionsJSON>(
`/api/auth/webauthn/generate-registration-options`,
`/api/v1/auth/webauthn/generate-registration-options`,
{}
)
.pipe(
@ -48,7 +48,7 @@ export class WebAuthnService {
}),
switchMap((attResp) => {
return this.http.post<AuthDeviceDto>(
`/api/auth/webauthn/verify-attestation`,
`/api/v1/auth/webauthn/verify-attestation`,
{
credential: attResp
}
@ -65,31 +65,33 @@ export class WebAuthnService {
public deregister() {
const deviceId = this.getDeviceId();
return this.http.delete<AuthDeviceDto>(`/api/auth-device/${deviceId}`).pipe(
catchError((error) => {
console.warn(`Could not deregister device ${deviceId}`, error);
return of(null);
}),
tap(() =>
this.settingsStorageService.removeSetting(
WebAuthnService.WEB_AUTH_N_DEVICE_ID
return this.http
.delete<AuthDeviceDto>(`/api/v1/auth-device/${deviceId}`)
.pipe(
catchError((error) => {
console.warn(`Could not deregister device ${deviceId}`, error);
return of(null);
}),
tap(() =>
this.settingsStorageService.removeSetting(
WebAuthnService.WEB_AUTH_N_DEVICE_ID
)
)
)
);
);
}
public login() {
const deviceId = this.getDeviceId();
return this.http
.post<PublicKeyCredentialRequestOptionsJSON>(
`/api/auth/webauthn/generate-assertion-options`,
`/api/v1/auth/webauthn/generate-assertion-options`,
{ deviceId }
)
.pipe(
switchMap(startAuthentication),
switchMap((assertionResponse) => {
return this.http.post<{ authToken: string }>(
`/api/auth/webauthn/verify-assertion`,
`/api/v1/auth/webauthn/verify-assertion`,
{
credential: assertionResponse,
deviceId

@ -8,7 +8,7 @@ import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
(async () => {
const response = await fetch('/api/info');
const response = await fetch('/api/v1/info');
const info: InfoItem = await response.json();
if (window.localStorage.getItem('utm_source') === 'trusted-web-activity') {

Loading…
Cancel
Save