Browse Source

Transform data source

pull/658/head
Thomas 3 years ago
parent
commit
a0d5beaa06
  1. 5
      apps/api/src/app/order/order.controller.ts
  2. 79
      apps/api/src/app/portfolio/portfolio.controller.ts
  3. 14
      apps/api/src/app/subscription/subscription.controller.ts
  4. 37
      apps/api/src/interceptors/transform-data-source-in-request.interceptor.ts
  5. 52
      apps/api/src/interceptors/transform-data-source-in-response.interceptor.ts

5
apps/api/src/app/order/order.controller.ts

@ -1,5 +1,6 @@
import { UserService } from '@ghostfolio/api/app/user/user.service'; import { UserService } from '@ghostfolio/api/app/user/user.service';
import { nullifyValuesInObjects } from '@ghostfolio/api/helper/object.helper'; import { nullifyValuesInObjects } from '@ghostfolio/api/helper/object.helper';
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response.interceptor';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types'; import type { RequestWithUser } from '@ghostfolio/common/types';
@ -14,7 +15,8 @@ import {
Param, Param,
Post, Post,
Put, Put,
UseGuards UseGuards,
UseInterceptors
} from '@nestjs/common'; } from '@nestjs/common';
import { REQUEST } from '@nestjs/core'; import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
@ -57,6 +59,7 @@ export class OrderController {
} }
@Get() @Get()
@UseInterceptors(TransformDataSourceInResponseInterceptor)
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
public async getAllOrders( public async getAllOrders(
@Headers('impersonation-id') impersonationId @Headers('impersonation-id') impersonationId

79
apps/api/src/app/portfolio/portfolio.controller.ts

@ -4,9 +4,12 @@ import {
hasNotDefinedValuesInObject, hasNotDefinedValuesInObject,
nullifyValuesInObject nullifyValuesInObject
} from '@ghostfolio/api/helper/object.helper'; } from '@ghostfolio/api/helper/object.helper';
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request.interceptor';
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response.interceptor';
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { baseCurrency } from '@ghostfolio/common/config'; import { baseCurrency } from '@ghostfolio/common/config';
import { parseDate } from '@ghostfolio/common/helper';
import { import {
PortfolioChart, PortfolioChart,
PortfolioDetails, PortfolioDetails,
@ -25,13 +28,11 @@ import {
Inject, Inject,
Param, Param,
Query, Query,
Res, UseGuards,
UseGuards UseInterceptors
} from '@nestjs/common'; } from '@nestjs/common';
import { REQUEST } from '@nestjs/core'; import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { DataSource } from '@prisma/client';
import { Response } from 'express';
import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { PortfolioPositionDetail } from './interfaces/portfolio-position-detail.interface'; import { PortfolioPositionDetail } from './interfaces/portfolio-position-detail.interface';
@ -53,8 +54,7 @@ export class PortfolioController {
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
public async getChart( public async getChart(
@Headers('impersonation-id') impersonationId: string, @Headers('impersonation-id') impersonationId: string,
@Query('range') range, @Query('range') range
@Res() res: Response
): Promise<PortfolioChart> { ): Promise<PortfolioChart> {
const historicalDataContainer = await this.portfolioServiceStrategy const historicalDataContainer = await this.portfolioServiceStrategy
.get() .get()
@ -90,27 +90,29 @@ export class PortfolioController {
}); });
} }
return <any>res.json({ return {
hasError, hasError,
chart: chartData, chart: chartData,
isAllTimeHigh: historicalDataContainer.isAllTimeHigh, isAllTimeHigh: historicalDataContainer.isAllTimeHigh,
isAllTimeLow: historicalDataContainer.isAllTimeLow isAllTimeLow: historicalDataContainer.isAllTimeLow
}); };
} }
@Get('details') @Get('details')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
@UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getDetails( public async getDetails(
@Headers('impersonation-id') impersonationId: string, @Headers('impersonation-id') impersonationId: string,
@Query('range') range, @Query('range') range
@Res() res: Response ): Promise<PortfolioDetails & { hasError: boolean }> {
): Promise<PortfolioDetails> {
if ( if (
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') &&
this.request.user.subscription.type === 'Basic' this.request.user.subscription.type === 'Basic'
) { ) {
res.status(StatusCodes.FORBIDDEN); throw new HttpException(
return <any>res.json({ accounts: {}, holdings: {} }); getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
} }
let hasError = false; let hasError = false;
@ -159,21 +161,22 @@ export class PortfolioController {
} }
} }
return <any>res.json({ accounts, hasError, holdings }); return { accounts, hasError, holdings };
} }
@Get('investments') @Get('investments')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
public async getInvestments( public async getInvestments(
@Headers('impersonation-id') impersonationId: string, @Headers('impersonation-id') impersonationId: string
@Res() res: Response
): Promise<PortfolioInvestments> { ): Promise<PortfolioInvestments> {
if ( if (
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') &&
this.request.user.subscription.type === 'Basic' this.request.user.subscription.type === 'Basic'
) { ) {
res.status(StatusCodes.FORBIDDEN); throw new HttpException(
return <any>res.json({}); getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
} }
let investments = await this.portfolioServiceStrategy let investments = await this.portfolioServiceStrategy
@ -195,15 +198,14 @@ export class PortfolioController {
})); }));
} }
return <any>res.json({ firstOrderDate: investments[0]?.date, investments }); return { firstOrderDate: parseDate(investments[0]?.date), investments };
} }
@Get('performance') @Get('performance')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
public async getPerformance( public async getPerformance(
@Headers('impersonation-id') impersonationId: string, @Headers('impersonation-id') impersonationId: string,
@Query('range') range, @Query('range') range
@Res() res: Response
): Promise<{ hasErrors: boolean; performance: PortfolioPerformance }> { ): Promise<{ hasErrors: boolean; performance: PortfolioPerformance }> {
const performanceInformation = await this.portfolioServiceStrategy const performanceInformation = await this.portfolioServiceStrategy
.get() .get()
@ -219,15 +221,15 @@ export class PortfolioController {
); );
} }
return <any>res.json(performanceInformation); return performanceInformation;
} }
@Get('positions') @Get('positions')
@UseInterceptors(TransformDataSourceInResponseInterceptor)
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
public async getPositions( public async getPositions(
@Headers('impersonation-id') impersonationId: string, @Headers('impersonation-id') impersonationId: string,
@Query('range') range, @Query('range') range
@Res() res: Response
): Promise<PortfolioPositions> { ): Promise<PortfolioPositions> {
const result = await this.portfolioServiceStrategy const result = await this.portfolioServiceStrategy
.get() .get()
@ -247,13 +249,12 @@ export class PortfolioController {
}); });
} }
return <any>res.json(result); return result;
} }
@Get('public/:accessId') @Get('public/:accessId')
public async getPublic( public async getPublic(
@Param('accessId') accessId, @Param('accessId') accessId
@Res() res: Response
): Promise<PortfolioPublicDetails> { ): Promise<PortfolioPublicDetails> {
const access = await this.accessService.access({ id: accessId }); const access = await this.accessService.access({ id: accessId });
const user = await this.userService.user({ const user = await this.userService.user({
@ -261,8 +262,10 @@ export class PortfolioController {
}); });
if (!access) { if (!access) {
res.status(StatusCodes.NOT_FOUND); throw new HttpException(
return <any>res.json({ accounts: {}, holdings: {} }); getReasonPhrase(StatusCodes.NOT_FOUND),
StatusCodes.NOT_FOUND
);
} }
let hasDetails = true; let hasDetails = true;
@ -305,7 +308,7 @@ export class PortfolioController {
} }
} }
return <any>res.json(portfolioPublicDetails); return portfolioPublicDetails;
} }
@Get('summary') @Get('summary')
@ -339,6 +342,7 @@ export class PortfolioController {
} }
@Get('position/:dataSource/:symbol') @Get('position/:dataSource/:symbol')
@UseInterceptors(TransformDataSourceInRequestInterceptor)
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
public async getPosition( public async getPosition(
@Headers('impersonation-id') impersonationId: string, @Headers('impersonation-id') impersonationId: string,
@ -376,21 +380,18 @@ export class PortfolioController {
@Get('report') @Get('report')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
public async getReport( public async getReport(
@Headers('impersonation-id') impersonationId: string, @Headers('impersonation-id') impersonationId: string
@Res() res: Response
): Promise<PortfolioReport> { ): Promise<PortfolioReport> {
if ( if (
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') &&
this.request.user.subscription.type === 'Basic' this.request.user.subscription.type === 'Basic'
) { ) {
res.status(StatusCodes.FORBIDDEN); throw new HttpException(
return <any>res.json({ rules: [] }); getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
} }
return <any>( return await this.portfolioServiceStrategy.get().getReport(impersonationId);
res.json(
await this.portfolioServiceStrategy.get().getReport(impersonationId)
)
);
} }
} }

14
apps/api/src/app/subscription/subscription.controller.ts

@ -7,6 +7,7 @@ import {
Body, Body,
Controller, Controller,
Get, Get,
HttpCode,
HttpException, HttpException,
Inject, Inject,
Logger, Logger,
@ -17,7 +18,6 @@ import {
} from '@nestjs/common'; } from '@nestjs/common';
import { REQUEST } from '@nestjs/core'; import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { Response } from 'express';
import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { SubscriptionService } from './subscription.service'; import { SubscriptionService } from './subscription.service';
@ -32,11 +32,9 @@ export class SubscriptionController {
) {} ) {}
@Post('redeem-coupon') @Post('redeem-coupon')
@HttpCode(StatusCodes.OK)
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
public async redeemCoupon( public async redeemCoupon(@Body() { couponCode }: { couponCode: string }) {
@Body() { couponCode }: { couponCode: string },
@Res() res: Response
) {
if (!this.request.user) { if (!this.request.user) {
throw new HttpException( throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN), getReasonPhrase(StatusCodes.FORBIDDEN),
@ -74,12 +72,10 @@ export class SubscriptionController {
`Subscription for user '${this.request.user.id}' has been created with coupon` `Subscription for user '${this.request.user.id}' has been created with coupon`
); );
res.status(StatusCodes.OK); return {
return <any>res.json({
message: getReasonPhrase(StatusCodes.OK), message: getReasonPhrase(StatusCodes.OK),
statusCode: StatusCodes.OK statusCode: StatusCodes.OK
}); };
} }
@Get('stripe/callback') @Get('stripe/callback')

37
apps/api/src/interceptors/transform-data-source-in-request.interceptor.ts

@ -0,0 +1,37 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { ConfigurationService } from '../services/configuration.service';
@Injectable()
export class TransformDataSourceInRequestInterceptor<T>
implements NestInterceptor<T, any>
{
public constructor(
private readonly configurationService: ConfigurationService
) {}
public intercept(
context: ExecutionContext,
next: CallHandler<T>
): Observable<any> {
const http = context.switchToHttp();
const request = http.getRequest();
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') === true) {
// Decode data source
if (request.params.dataSource) {
request.params.dataSource = Buffer.from(
request.params.dataSource,
'hex'
).toString();
}
}
return next.handle();
}
}

52
apps/api/src/interceptors/transform-data-source-in-response.interceptor.ts

@ -0,0 +1,52 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor
} from '@nestjs/common';
import { DataSource } from '@prisma/client';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ConfigurationService } from '../services/configuration.service';
@Injectable()
export class TransformDataSourceInResponseInterceptor<T>
implements NestInterceptor<T, any>
{
public constructor(
private readonly configurationService: ConfigurationService
) {}
public intercept(
context: ExecutionContext,
next: CallHandler<T>
): Observable<any> {
return next.handle().pipe(
map((data: any) => {
if (
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') === true
) {
if (data.activities) {
data.activities.map((activity) => {
activity.dataSource = this.encodeDataSource(activity.dataSource);
return activity;
});
}
if (data.positions) {
data.positions.map((position) => {
position.dataSource = this.encodeDataSource(position.dataSource);
return position;
});
}
}
return data;
})
);
}
private encodeDataSource(aDataSource: DataSource) {
return Buffer.from(aDataSource, 'utf-8').toString('hex');
}
}
Loading…
Cancel
Save