Создан контроллер для CRUD операций с дашбордами

This commit is contained in:
Pavel Gnedov 2023-09-20 10:12:45 +07:00
parent 0e6b64fb27
commit c8e71d3926
8 changed files with 117 additions and 31 deletions

View file

@ -43,7 +43,8 @@
"url": "",
"dbs": {
"users": "",
"issues": ""
"issues": "",
"dashboards": ""
}
}
}

View file

@ -4,8 +4,7 @@
"changes": "",
"userMetaInfo": "",
"eccmDailyReports": "",
"eccmDailyReportsUserComments": "",
"dashboards": ""
"eccmDailyReportsUserComments": ""
}
},
"telegramBotToken": "",

View file

@ -0,0 +1,38 @@
import { Body, Controller, Get, Param, Post, Put } from '@nestjs/common';
import { DashboardsService } from './dashboards.service';
import { BadRequestErrorHandler, getOrAppErrorOrThrow } from '../utils/result';
@Controller('dashboard')
export class DashboardController {
constructor(private dashboardsService: DashboardsService) {}
@Post(':name')
async create(@Param('name') name: string): Promise<string> {
if (name === 'anonymous') {
name = null;
}
const res = await getOrAppErrorOrThrow(
() => this.dashboardsService.create(name),
BadRequestErrorHandler,
);
return res.id;
}
@Get(':name')
async load(@Param('name') name: string): Promise<any> {
const res = await getOrAppErrorOrThrow(
() => this.dashboardsService.load(name),
BadRequestErrorHandler,
);
return res;
}
@Put(':name')
async save(@Param('name') name: string, @Body() data: any): Promise<void> {
const res = await getOrAppErrorOrThrow(
() => this.dashboardsService.save(name, data),
BadRequestErrorHandler,
);
return res;
}
}

View file

@ -1,19 +1,28 @@
import { Injectable } from '@nestjs/common';
import { Injectable, Logger } from '@nestjs/common';
import { Dashboards as DashboardsDb } from '../couchdb-datasources/dashboards';
import { Dashboard as DashboardModel } from '../models/dashboard';
import {
Dashboard as DashboardModel,
Data as DashboardData,
} from '../models/dashboard';
import nano from 'nano';
import { randomUUID } from 'crypto';
import { Result, fail, getOrThrow, success } from '../utils/result';
import { createAppError } from '../utils/result';
@Injectable()
export class DashboardsService {
private logger = new Logger(DashboardsService.name);
constructor(private db: DashboardsDb) {}
async create(name?: string): Promise<Result<DashboardModel, string>> {
async create(name?: string): Promise<DashboardModel> {
if (!name) {
name = randomUUID();
} else if (await this.isExists(name)) {
return fail('ALREADY_EXISTS');
}
this.logger.debug(`Create new dashboard with name - ${name}`);
if (await this.isExists(name)) {
const err = createAppError('ALREADY_EXISTS');
this.logger.error(`Error - ${JSON.stringify(err)}`);
throw err;
}
const ds = await this.db.getDatasource();
const doc: nano.MaybeDocument & DashboardModel = {
@ -22,15 +31,23 @@ export class DashboardsService {
data: null,
};
await ds.insert(doc);
return success(await ds.get(name));
return await ds.get(name);
}
async load(
async loadRawData(
name: string,
): Promise<Result<DashboardModel & nano.MaybeDocument, string>> {
): Promise<DashboardModel & nano.MaybeDocument> {
this.logger.debug(`Load raw data, dashboard name - ${name}`);
const ds = await this.db.getDatasource();
if (!(await this.isExists(name))) return fail('NOT_EXISTS');
return success(await ds.get(name));
if (!(await this.isExists(name))) throw createAppError('NOT_EXISTS');
const res = await ds.get(name);
return res;
}
async load(name: string): Promise<DashboardData> {
this.logger.debug(`Load dashboard name - ${name}`);
const rawData = await this.loadRawData(name);
return rawData.data;
}
async isExists(name: string): Promise<boolean> {
@ -43,14 +60,20 @@ export class DashboardsService {
}
}
async save(name: string, data: any): Promise<Result<any, string>> {
async save(name: string, data: any): Promise<void> {
this.logger.debug(
`Save dashboard name - ${name}, data - ${JSON.stringify(data)}`,
);
const ds = await this.db.getDatasource();
const loadRes = await this.load(name);
if (loadRes.error) return fail(loadRes.error);
const prevValue = await this.loadRawData(name);
const prevValue = getOrThrow(loadRes);
const newValue = { ...prevValue, data: data };
const newValue = {
_id: prevValue._id,
_rev: prevValue._rev,
id: prevValue.id,
data: data,
};
await ds.insert(newValue);
return success(newValue);
return;
}
}

View file

@ -31,6 +31,9 @@ import { IssuesUpdaterService } from './issues-updater/issues-updater.service';
import { CalendarEnhancer } from './issue-enhancers/calendar-enhancer';
import { CalendarService } from './calendar/calendar.service';
import { CalendarController } from './calendar/calendar.controller';
import { Dashboards as DashboardsDs } from './couchdb-datasources/dashboards';
import { DashboardController } from './dashboards/dashboard.controller';
import { DashboardsService } from './dashboards/dashboards.service';
@Module({})
export class EventEmitterModule implements OnModuleInit {
@ -48,6 +51,7 @@ export class EventEmitterModule implements OnModuleInit {
CouchDb,
Users,
Issues,
DashboardsDs,
RedmineUserCacheWriterService,
UsersService,
IssuesService,
@ -96,6 +100,7 @@ export class EventEmitterModule implements OnModuleInit {
},
inject: ['CALENDAR_ENHANCER', IssuesService],
},
DashboardsService,
],
exports: [
EventEmitterService,
@ -105,6 +110,7 @@ export class EventEmitterModule implements OnModuleInit {
CouchDb,
Users,
Issues,
DashboardsDs,
RedmineUserCacheWriterService,
UsersService,
IssuesService,
@ -131,12 +137,14 @@ export class EventEmitterModule implements OnModuleInit {
provide: 'CALENDAR_SERVICE',
useExisting: 'CALENDAR_SERVICE',
},
DashboardsService,
],
controllers: [
MainController,
UsersController,
IssuesController,
CalendarController,
DashboardController,
],
};
}

View file

@ -1,4 +1,6 @@
export type Data = Record<string, any> | null;
export type Dashboard = {
id: string;
data: any;
data: Data;
};

View file

@ -1,3 +1,5 @@
import { BadRequestException } from '@nestjs/common';
export type Result<T, E> = {
result?: T;
error?: E;
@ -39,23 +41,34 @@ export type AppError = Error & {
app: true;
};
export function createAppError(msg: string): AppError {
const err = new Error(msg);
const app: AppError = { ...err, app: true };
return app;
export function createAppError(msg: string | Error): AppError {
let err: any;
if (typeof msg === 'string') {
err = new Error(msg);
} else if (typeof msg === 'object') {
err = msg;
} else {
err = new Error('UNKNOWN_APP_ERROR');
}
err.name = 'ApplicationError';
return err;
}
export async function getOrAppErrorOrThrow<T>(
fn: () => Promise<T>,
onError: (err: AppError) => Promise<void>,
): Promise<T | AppError> {
onAppError?: (err: Error) => Error,
onOtherError?: (err: Error) => Error,
): Promise<T> {
try {
return await fn();
} catch (ex) {
if (ex && ex.app) {
onError(ex);
return ex;
if (ex && ex.name === 'ApplicationError') {
throw onAppError ? onAppError(ex) : ex;
}
throw ex;
throw onOtherError ? onOtherError(ex) : ex;
}
}
export function BadRequestErrorHandler(err: Error): Error {
return new BadRequestException(err.message);
}

View file

@ -51,6 +51,7 @@ import { SimpleIssuesListController } from './dashboards/simple-issues-list.cont
import { TagsManagerController } from './tags-manager/tags-manager.controller';
import { CreateTagManagerServiceProvider } from './tags-manager/tags-manager.service';
import { CalendarEnhancer } from '@app/event-emitter/issue-enhancers/calendar-enhancer';
import { Dashboards as DashboardsDs } from '@app/event-emitter/couchdb-datasources/dashboards';
@Module({
imports: [
@ -145,6 +146,7 @@ export class AppModule implements OnModuleInit {
UserMetaInfo.getDatasource();
DailyEccmReportsDatasource.getDatasource();
DailyEccmReportsUserCommentsDatasource.getDatasource();
DashboardsDs.getDatasource();
this.enhancerService.addEnhancer([
this.timestampEnhancer,