Добавлен сервис для работы с дашбордами

This commit is contained in:
Pavel Gnedov 2023-09-19 07:34:49 +07:00
parent b8c15c0dd0
commit 0e6b64fb27
7 changed files with 171 additions and 4 deletions

View file

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

View file

@ -0,0 +1,36 @@
import { Injectable, Logger } from '@nestjs/common';
import nano from 'nano';
import * as DashboardModel from '../models/dashboard';
import { CouchDb } from './couchdb';
import configuration from '../configs/main-config';
const config = configuration();
@Injectable()
export class Dashboards {
private static logger = new Logger(Dashboards.name);
private static dashboardsDb = null;
private static initialized = false;
static async getDatasource(): Promise<
nano.DocumentScope<DashboardModel.Dashboard>
> {
if (Dashboards.initialized) {
return Dashboards.dashboardsDb;
}
Dashboards.initialized = true;
const n = CouchDb.getCouchDb();
const dashboardsDbName = config.couchDb?.dbs?.dashboards;
const dbs = await n.db.list();
if (!dbs.includes(dashboardsDbName)) {
await n.db.create(dashboardsDbName);
}
Dashboards.dashboardsDb = await n.db.use(dashboardsDbName);
Dashboards.logger.log(`Connected to dashboards db - ${dashboardsDbName}`);
return Dashboards.dashboardsDb;
}
async getDatasource(): Promise<nano.DocumentScope<DashboardModel.Dashboard>> {
return await Dashboards.getDatasource();
}
}

View file

@ -0,0 +1,56 @@
import { Injectable } from '@nestjs/common';
import { Dashboards as DashboardsDb } from '../couchdb-datasources/dashboards';
import { Dashboard as DashboardModel } from '../models/dashboard';
import nano from 'nano';
import { randomUUID } from 'crypto';
import { Result, fail, getOrThrow, success } from '../utils/result';
@Injectable()
export class DashboardsService {
constructor(private db: DashboardsDb) {}
async create(name?: string): Promise<Result<DashboardModel, string>> {
if (!name) {
name = randomUUID();
} else if (await this.isExists(name)) {
return fail('ALREADY_EXISTS');
}
const ds = await this.db.getDatasource();
const doc: nano.MaybeDocument & DashboardModel = {
_id: name,
id: name,
data: null,
};
await ds.insert(doc);
return success(await ds.get(name));
}
async load(
name: string,
): Promise<Result<DashboardModel & nano.MaybeDocument, string>> {
const ds = await this.db.getDatasource();
if (!(await this.isExists(name))) return fail('NOT_EXISTS');
return success(await ds.get(name));
}
async isExists(name: string): Promise<boolean> {
const ds = await this.db.getDatasource();
try {
await ds.get(name);
return true;
} catch (ex) {
return false;
}
}
async save(name: string, data: any): Promise<Result<any, string>> {
const ds = await this.db.getDatasource();
const loadRes = await this.load(name);
if (loadRes.error) return fail(loadRes.error);
const prevValue = getOrThrow(loadRes);
const newValue = { ...prevValue, data: data };
await ds.insert(newValue);
return success(newValue);
}
}

View file

@ -87,11 +87,14 @@ export class EventEmitterModule implements OnModuleInit {
},
{
provide: 'CALENDAR_SERVICE',
useFactory: (calendarEnhancer: CalendarEnhancer, issuesService: IssuesService): CalendarService => {
useFactory: (
calendarEnhancer: CalendarEnhancer,
issuesService: IssuesService,
): CalendarService => {
const calendarEventsKey = calendarEnhancer.calendarEventsKey;
return new CalendarService(calendarEventsKey, issuesService);
},
inject: ['CALENDAR_ENHANCER', IssuesService]
inject: ['CALENDAR_ENHANCER', IssuesService],
},
],
exports: [
@ -129,7 +132,12 @@ export class EventEmitterModule implements OnModuleInit {
useExisting: 'CALENDAR_SERVICE',
},
],
controllers: [MainController, UsersController, IssuesController, CalendarController],
controllers: [
MainController,
UsersController,
IssuesController,
CalendarController,
],
};
}

View file

@ -0,0 +1,4 @@
export type Dashboard = {
id: string;
data: any;
};

View file

@ -16,6 +16,7 @@ export type MainConfigModel = {
dbs: {
users: string;
issues: string;
dashboards: string;
};
};
webhooks: WebhookConfigItemModel[];

View file

@ -0,0 +1,61 @@
export type Result<T, E> = {
result?: T;
error?: E;
};
export function success<T, E>(res: T): Result<T, E> {
return {
result: res,
};
}
export function fail<T, E>(error: E): Result<T, E> {
return {
error: error,
};
}
export function getOrThrow<T, E>(res: Result<T, E>): T {
if (res.result) return res.result;
throw res.error ? res.error : 'UNKNOWN_ERROR';
}
export async function successOrError<T, E>(
cb: () => Promise<T>,
): Promise<Result<T, E>> {
try {
const res = await cb();
return {
result: res,
};
} catch (ex) {
return {
error: ex,
};
}
}
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 async function getOrAppErrorOrThrow<T>(
fn: () => Promise<T>,
onError: (err: AppError) => Promise<void>,
): Promise<T | AppError> {
try {
return await fn();
} catch (ex) {
if (ex && ex.app) {
onError(ex);
return ex;
}
throw ex;
}
}