mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
fix: accounts TS interfaces.
This commit is contained in:
@@ -83,8 +83,9 @@ export default class AccountsController extends BaseController{
|
|||||||
this.catchServiceErrors,
|
this.catchServiceErrors,
|
||||||
);
|
);
|
||||||
router.delete(
|
router.delete(
|
||||||
'/',
|
'/', [
|
||||||
this.bulkDeleteSchema,
|
...this.bulkDeleteSchema,
|
||||||
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.deleteBulkAccounts.bind(this)),
|
asyncMiddleware(this.deleteBulkAccounts.bind(this)),
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors,
|
||||||
@@ -279,8 +280,9 @@ export default class AccountsController extends BaseController{
|
|||||||
const { ids: accountsIds } = req.query;
|
const { ids: accountsIds } = req.query;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const isActive = (type === 'activate' ? 1 : 0);
|
const isActive = (type === 'activate' ? true : false);
|
||||||
await this.accountsService.activateAccounts(tenantId, accountsIds, isActive)
|
await this.accountsService.activateAccounts(tenantId, accountsIds, isActive);
|
||||||
|
|
||||||
return res.status(200).send({ ids: accountsIds });
|
return res.status(200).send({ ids: accountsIds });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
|
|||||||
217
server/src/api/controllers/Views.ts
Normal file
217
server/src/api/controllers/Views.ts
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { Router, Request, NextFunction, Response } from 'express';
|
||||||
|
import {
|
||||||
|
check,
|
||||||
|
query,
|
||||||
|
param,
|
||||||
|
oneOf,
|
||||||
|
validationResult,
|
||||||
|
} from 'express-validator';
|
||||||
|
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||||
|
import {
|
||||||
|
validateViewRoles,
|
||||||
|
} from 'lib/ViewRolesBuilder';
|
||||||
|
import ViewsService from 'services/Views/ViewsService';
|
||||||
|
import BaseController from './BaseController';
|
||||||
|
import { IViewDTO } from 'interfaces';
|
||||||
|
import { ServiceError } from 'exceptions';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class ViewsController extends BaseController{
|
||||||
|
@Inject()
|
||||||
|
viewsService: ViewsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router constructor.
|
||||||
|
*/
|
||||||
|
router() {
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/', [
|
||||||
|
...this.viewDTOSchemaValidation,
|
||||||
|
],
|
||||||
|
asyncMiddleware(this.listViews)
|
||||||
|
);
|
||||||
|
router.post('/', [
|
||||||
|
...this.viewDTOSchemaValidation,
|
||||||
|
],
|
||||||
|
asyncMiddleware(this.createView)
|
||||||
|
);
|
||||||
|
|
||||||
|
router.post('/:view_id', [
|
||||||
|
...this.viewDTOSchemaValidation,
|
||||||
|
],
|
||||||
|
asyncMiddleware(this.editView)
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete('/:view_id', [
|
||||||
|
...this.viewParamSchemaValidation
|
||||||
|
],
|
||||||
|
asyncMiddleware(this.deleteView));
|
||||||
|
|
||||||
|
router.get('/:view_id', [
|
||||||
|
...this.viewParamSchemaValidation
|
||||||
|
]
|
||||||
|
asyncMiddleware(this.getView)
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get('/:view_id/resource', [
|
||||||
|
...this.viewParamSchemaValidation
|
||||||
|
],
|
||||||
|
asyncMiddleware(this.getViewResource)
|
||||||
|
);
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
|
||||||
|
get viewDTOSchemaValidation() {
|
||||||
|
return [
|
||||||
|
check('resource_name').exists().escape().trim(),
|
||||||
|
check('name').exists().escape().trim(),
|
||||||
|
check('logic_expression').exists().trim().escape(),
|
||||||
|
check('roles').isArray({ min: 1 }),
|
||||||
|
check('roles.*.field_key').exists().escape().trim(),
|
||||||
|
check('roles.*.comparator').exists(),
|
||||||
|
check('roles.*.value').exists(),
|
||||||
|
check('roles.*.index').exists().isNumeric().toInt(),
|
||||||
|
check('columns').exists().isArray({ min: 1 }),
|
||||||
|
check('columns.*.key').exists().escape().trim(),
|
||||||
|
check('columns.*.index').exists().isNumeric().toInt(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
get viewParamSchemaValidation() {
|
||||||
|
return [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all views that associated with the given resource.
|
||||||
|
* @param {Request} req -
|
||||||
|
* @param {Response} res -
|
||||||
|
* @param {NextFunction} next -
|
||||||
|
*/
|
||||||
|
listViews(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const filter = req.query;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const views = this.viewsService.listViews(tenantId, filter);
|
||||||
|
return res.status(200).send({ views });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param {NextFunction} next
|
||||||
|
*/
|
||||||
|
getView(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const view = this.viewsService.getView(tenantId, viewId);
|
||||||
|
return res.status(200).send({ view });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new view.
|
||||||
|
* @param {Request} req -
|
||||||
|
* @param {Response} res -
|
||||||
|
* @param {NextFunction} next -
|
||||||
|
*/
|
||||||
|
createView(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const viewDTO: IViewDTO = this.matchedBodyData(req);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.viewsService.newView(tenantId, viewDTO);
|
||||||
|
return res.status(200).send({ id: 1 });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits views metadata.
|
||||||
|
* @param {Request} req -
|
||||||
|
* @param {Response} res -
|
||||||
|
* @param {NextFunction} next -
|
||||||
|
*/
|
||||||
|
editView(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { id: viewId } = req.params;
|
||||||
|
const { body: viewEditDTO } = req;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.viewsService.editView(tenantId, viewId, viewEditDTO);
|
||||||
|
return res.status(200).send({ id: viewId });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given view.
|
||||||
|
* @param {Request} req -
|
||||||
|
* @param {Response} res -
|
||||||
|
* @param {NextFunction} next -
|
||||||
|
*/
|
||||||
|
deleteView(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { id: viewId } = req.params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.viewsService.deleteView(tenantId, viewId);
|
||||||
|
return res.status(200).send();
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles service errors.
|
||||||
|
* @param {Error} error
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Response} res
|
||||||
|
* @param {NextFunction} next
|
||||||
|
*/
|
||||||
|
handlerServiceErrors(error: Error, req: Request, res: Response, next: NextFunction) {
|
||||||
|
if (error instanceof ServiceError) {
|
||||||
|
if (error.errorType === 'INVALID_LOGIC_EXPRESSION') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'VIEW.ROLES.LOGIC.EXPRESSION.INVALID', code: 400 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === '') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'RESOURCE_FIELDS_NOT_EXIST', code: 100 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === '') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'COLUMNS_NOT_EXIST', code: 200 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === 'VIEW_NOT_FOUND') {
|
||||||
|
return res.boom.notFound(null, {
|
||||||
|
errors: [{ type: 'VIEW_NOT_FOUND', code: 100 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.errorType === 'VIEW_PREDEFINED') {
|
||||||
|
return res.boom.badRequest(null, {
|
||||||
|
errors: [{ type: 'PREDEFINED_VIEW', code: 200 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -4,14 +4,22 @@ export interface IAccountDTO {
|
|||||||
name: string,
|
name: string,
|
||||||
code: string,
|
code: string,
|
||||||
description: string,
|
description: string,
|
||||||
accountTypeNumber: number,
|
accountTypeId: number,
|
||||||
|
parentAccountId: number,
|
||||||
|
active: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IAccount {
|
export interface IAccount {
|
||||||
name: string,
|
name: string,
|
||||||
|
slug: string,
|
||||||
code: string,
|
code: string,
|
||||||
description: string,
|
description: string,
|
||||||
accountTypeNumber: number,
|
accountTypeId: number,
|
||||||
|
parentAccountId: number,
|
||||||
|
active: boolean,
|
||||||
|
predefined: boolean,
|
||||||
|
amount: number,
|
||||||
|
currencyCode: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IAccountsFilter extends IDynamicListFilterDTO {
|
export interface IAccountsFilter extends IDynamicListFilterDTO {
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ export default class AccountsService {
|
|||||||
* @param {IAccount} account
|
* @param {IAccount} account
|
||||||
*/
|
*/
|
||||||
private throwErrorIfAccountPredefined(account: IAccount) {
|
private throwErrorIfAccountPredefined(account: IAccount) {
|
||||||
if (account.prefined) {
|
if (account.predefined) {
|
||||||
throw new ServiceError('account_predefined');
|
throw new ServiceError('account_predefined');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,6 +344,11 @@ export default class AccountsService {
|
|||||||
return storedAccounts;
|
return storedAccounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate whether one of the given accounts is predefined.
|
||||||
|
* @param {IAccount[]} accounts -
|
||||||
|
* @return {IAccount[]} - Predefined accounts
|
||||||
|
*/
|
||||||
private validatePrefinedAccounts(accounts: IAccount[]) {
|
private validatePrefinedAccounts(accounts: IAccount[]) {
|
||||||
const predefined = accounts.filter((account: IAccount) => account.predefined);
|
const predefined = accounts.filter((account: IAccount) => account.predefined);
|
||||||
|
|
||||||
@@ -405,7 +410,7 @@ export default class AccountsService {
|
|||||||
*/
|
*/
|
||||||
public async activateAccounts(tenantId: number, accountsIds: number[], activate: boolean = true) {
|
public async activateAccounts(tenantId: number, accountsIds: number[], activate: boolean = true) {
|
||||||
const { Account } = this.tenancy.models(tenantId);
|
const { Account } = this.tenancy.models(tenantId);
|
||||||
const accounts = await this.getAccountsOrThrowError(tenantId, accountsIds);
|
await this.getAccountsOrThrowError(tenantId, accountsIds);
|
||||||
|
|
||||||
this.logger.info('[account] trying activate/inactive the given accounts ids.', { accountsIds });
|
this.logger.info('[account] trying activate/inactive the given accounts ids.', { accountsIds });
|
||||||
await Account.query().whereIn('id', accountsIds)
|
await Account.query().whereIn('id', accountsIds)
|
||||||
@@ -438,7 +443,7 @@ export default class AccountsService {
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {IAccountsFilter} accountsFilter
|
* @param {IAccountsFilter} accountsFilter
|
||||||
*/
|
*/
|
||||||
async getAccountsList(tenantId: number, filter: IAccountsFilter) {
|
public async getAccountsList(tenantId: number, filter: IAccountsFilter) {
|
||||||
const { Account } = this.tenancy.models(tenantId);
|
const { Account } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const dynamicList = await this.dynamicListService.dynamicList(tenantId, Account, filter);
|
const dynamicList = await this.dynamicListService.dynamicList(tenantId, Account, filter);
|
||||||
|
|||||||
288
server/src/services/Views/ViewsService.ts
Normal file
288
server/src/services/Views/ViewsService.ts
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
import { Service, Inject } from "typedi";
|
||||||
|
import { pick, difference } from 'lodash';
|
||||||
|
import { ServiceError } from 'exceptions';
|
||||||
|
import {
|
||||||
|
IViewsService,
|
||||||
|
IViewDTO,
|
||||||
|
IView,
|
||||||
|
IViewRole,
|
||||||
|
IViewHasColumn,
|
||||||
|
} from 'interfaces';
|
||||||
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
|
import { validateRolesLogicExpression } from 'lib/ViewRolesBuilder';
|
||||||
|
|
||||||
|
const ERRORS = {
|
||||||
|
VIEW_NOT_FOUND: 'VIEW_NOT_FOUND',
|
||||||
|
VIEW_PREDEFINED: 'VIEW_PREDEFINED',
|
||||||
|
INVALID_LOGIC_EXPRESSION: 'INVALID_LOGIC_EXPRESSION',
|
||||||
|
};
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class ViewsService implements IViewsService {
|
||||||
|
@Inject()
|
||||||
|
tenancy: TenancyService;
|
||||||
|
|
||||||
|
@Inject('logger')
|
||||||
|
logger: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listing resource views.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} resourceModel
|
||||||
|
*/
|
||||||
|
public async listViews(tenantId: number, resourceModel: string) {
|
||||||
|
const { View } = this.tenancy.models(tenantId);
|
||||||
|
return View.query().where('resource_model', resourceModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
validateResourceFieldsExistance() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
validateResourceColumnsExistance() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getView(tenantId: number, viewId: number) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Precedures.
|
||||||
|
* ----––––––
|
||||||
|
* - Validate resource fields existance.
|
||||||
|
* - Validate resource columns existance.
|
||||||
|
* - Validate view logic expression.
|
||||||
|
* - Store view to the storage.
|
||||||
|
* - Store view columns to the storage.
|
||||||
|
* - Store view roles/conditions to the storage.
|
||||||
|
* ---------
|
||||||
|
* @param {number} tenantId - Tenant id.
|
||||||
|
* @param {IViewDTO} viewDTO - View DTO.
|
||||||
|
*/
|
||||||
|
async newView(tenantId: number, viewDTO: IViewDTO): Promise<void> {
|
||||||
|
const { View, ViewColumn, ViewRole } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
this.logger.info('[views] trying to create a new view.', { tenantId, viewDTO });
|
||||||
|
// Validates the view conditional logic expression.
|
||||||
|
if (!validateRolesLogicExpression(viewDTO.logicExpression, viewDTO.roles)) {
|
||||||
|
throw new ServiceError(ERRORS.INVALID_LOGIC_EXPRESSION);
|
||||||
|
}
|
||||||
|
// Save view details.
|
||||||
|
const view = await View.query().insert({
|
||||||
|
name: viewDTO.name,
|
||||||
|
predefined: false,
|
||||||
|
rolesLogicExpression: viewDTO.logicExpression,
|
||||||
|
});
|
||||||
|
this.logger.info('[views] inserted to the storage.', { tenantId, viewDTO });
|
||||||
|
|
||||||
|
// Save view roles async operations.
|
||||||
|
const saveViewRolesOpers = [];
|
||||||
|
|
||||||
|
viewDTO.roles.forEach((role) => {
|
||||||
|
const saveViewRoleOper = ViewRole.query().insert({
|
||||||
|
...pick(role, ['fieldKey', 'comparator', 'value', 'index']),
|
||||||
|
viewId: view.id,
|
||||||
|
});
|
||||||
|
saveViewRolesOpers.push(saveViewRoleOper);
|
||||||
|
});
|
||||||
|
|
||||||
|
viewDTO.columns.forEach((column) => {
|
||||||
|
const saveViewColumnOper = ViewColumn.query().insert({
|
||||||
|
viewId: view.id,
|
||||||
|
index: column.index,
|
||||||
|
});
|
||||||
|
saveViewRolesOpers.push(saveViewColumnOper);
|
||||||
|
});
|
||||||
|
this.logger.info('[views] roles and columns inserted to the storage.', { tenantId, viewDTO });
|
||||||
|
|
||||||
|
await Promise.all(saveViewRolesOpers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} viewId
|
||||||
|
* @param {IViewEditDTO}
|
||||||
|
*/
|
||||||
|
async editView(tenantId: number, viewId: number, viewEditDTO: IViewEditDTO) {
|
||||||
|
const { View, ViewRole, ViewColumn } = req.models;
|
||||||
|
const view = await View.query().where('id', viewId)
|
||||||
|
.withGraphFetched('roles.field')
|
||||||
|
.withGraphFetched('columns')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
const errorReasons = [];
|
||||||
|
const fieldsSlugs = viewEditDTO.roles.map((role) => role.field_key);
|
||||||
|
const resourceFieldsKeys = resource.fields.map((f) => f.key);
|
||||||
|
const resourceFieldsKeysMap = new Map(resource.fields.map((field) => [field.key, field]));
|
||||||
|
const columnsKeys = viewEditDTO.columns.map((c) => c.key);
|
||||||
|
|
||||||
|
// The difference between the stored resource fields and submit fields keys.
|
||||||
|
const notFoundFields = difference(fieldsSlugs, resourceFieldsKeys);
|
||||||
|
|
||||||
|
// Validate not found resource fields keys.
|
||||||
|
if (notFoundFields.length > 0) {
|
||||||
|
errorReasons.push({
|
||||||
|
type: 'RESOURCE_FIELDS_NOT_EXIST', code: 100, fields: notFoundFields,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// The difference between the stored resource fields and the submit columns keys.
|
||||||
|
const notFoundColumns = difference(columnsKeys, resourceFieldsKeys);
|
||||||
|
|
||||||
|
// Validate not found view columns.
|
||||||
|
if (notFoundColumns.length > 0) {
|
||||||
|
errorReasons.push({ type: 'RESOURCE_COLUMNS_NOT_EXIST', code: 200, columns: notFoundColumns });
|
||||||
|
}
|
||||||
|
// Validates the view conditional logic expression.
|
||||||
|
if (!validateViewRoles(viewEditDTO.roles, viewEditDTO.logicExpression)) {
|
||||||
|
errorReasons.push({ type: 'VIEW.ROLES.LOGIC.EXPRESSION.INVALID', code: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewRolesIds = view.roles.map((r) => r.id);
|
||||||
|
const viewColumnsIds = view.columns.map((c) => c.id);
|
||||||
|
|
||||||
|
const formUpdatedRoles = viewEditDTO.roles.filter((r) => r.id);
|
||||||
|
const formInsertRoles = viewEditDTO.roles.filter((r) => !r.id);
|
||||||
|
|
||||||
|
const formRolesIds = formUpdatedRoles.map((r) => r.id);
|
||||||
|
|
||||||
|
const formUpdatedColumns = viewEditDTO.columns.filter((r) => r.id);
|
||||||
|
const formInsertedColumns = viewEditDTO.columns.filter((r) => !r.id);
|
||||||
|
const formColumnsIds = formUpdatedColumns.map((r) => r.id);
|
||||||
|
|
||||||
|
const rolesIdsShouldDeleted = difference(viewRolesIds, formRolesIds);
|
||||||
|
const columnsIdsShouldDelete = difference(viewColumnsIds, formColumnsIds);
|
||||||
|
|
||||||
|
const notFoundViewRolesIds = difference(formRolesIds, viewRolesIds);
|
||||||
|
const notFoundViewColumnsIds = difference(viewColumnsIds, viewColumnsIds);
|
||||||
|
|
||||||
|
// Validate the not found view roles ids.
|
||||||
|
if (notFoundViewRolesIds.length) {
|
||||||
|
errorReasons.push({ type: 'VIEW.ROLES.IDS.NOT.FOUND', code: 500, ids: notFoundViewRolesIds });
|
||||||
|
}
|
||||||
|
// Validate the not found view columns ids.
|
||||||
|
if (notFoundViewColumnsIds.length) {
|
||||||
|
errorReasons.push({ type: 'VIEW.COLUMNS.IDS.NOT.FOUND', code: 600, ids: notFoundViewColumnsIds });
|
||||||
|
}
|
||||||
|
if (errorReasons.length > 0) {
|
||||||
|
return res.status(400).send({ errors: errorReasons });
|
||||||
|
}
|
||||||
|
const asyncOpers = [];
|
||||||
|
|
||||||
|
// Save view details.
|
||||||
|
await View.query()
|
||||||
|
.where('id', view.id)
|
||||||
|
.patch({
|
||||||
|
name: viewEditDTO.name,
|
||||||
|
roles_logic_expression: viewEditDTO.logicExpression,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update view roles.
|
||||||
|
if (formUpdatedRoles.length > 0) {
|
||||||
|
formUpdatedRoles.forEach((role) => {
|
||||||
|
const fieldModel = resourceFieldsKeysMap.get(role.field_key);
|
||||||
|
const updateOper = ViewRole.query()
|
||||||
|
.where('id', role.id)
|
||||||
|
.update({
|
||||||
|
...pick(role, ['comparator', 'value', 'index']),
|
||||||
|
field_id: fieldModel.id,
|
||||||
|
});
|
||||||
|
asyncOpers.push(updateOper);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Insert a new view roles.
|
||||||
|
if (formInsertRoles.length > 0) {
|
||||||
|
formInsertRoles.forEach((role) => {
|
||||||
|
const fieldModel = resourceFieldsKeysMap.get(role.field_key);
|
||||||
|
const insertOper = ViewRole.query()
|
||||||
|
.insert({
|
||||||
|
...pick(role, ['comparator', 'value', 'index']),
|
||||||
|
field_id: fieldModel.id,
|
||||||
|
view_id: view.id,
|
||||||
|
});
|
||||||
|
asyncOpers.push(insertOper);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Delete view roles.
|
||||||
|
if (rolesIdsShouldDeleted.length > 0) {
|
||||||
|
const deleteOper = ViewRole.query()
|
||||||
|
.whereIn('id', rolesIdsShouldDeleted)
|
||||||
|
.delete();
|
||||||
|
asyncOpers.push(deleteOper);
|
||||||
|
}
|
||||||
|
// Insert a new view columns to the storage.
|
||||||
|
if (formInsertedColumns.length > 0) {
|
||||||
|
formInsertedColumns.forEach((column) => {
|
||||||
|
const fieldModel = resourceFieldsKeysMap.get(column.key);
|
||||||
|
const insertOper = ViewColumn.query()
|
||||||
|
.insert({
|
||||||
|
field_id: fieldModel.id,
|
||||||
|
index: column.index,
|
||||||
|
view_id: view.id,
|
||||||
|
});
|
||||||
|
asyncOpers.push(insertOper);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Update the view columns on the storage.
|
||||||
|
if (formUpdatedColumns.length > 0) {
|
||||||
|
formUpdatedColumns.forEach((column) => {
|
||||||
|
const updateOper = ViewColumn.query()
|
||||||
|
.where('id', column.id)
|
||||||
|
.update({
|
||||||
|
index: column.index,
|
||||||
|
});
|
||||||
|
asyncOpers.push(updateOper);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Delete the view columns from the storage.
|
||||||
|
if (columnsIdsShouldDelete.length > 0) {
|
||||||
|
const deleteOper = ViewColumn.query()
|
||||||
|
.whereIn('id', columnsIdsShouldDelete)
|
||||||
|
.delete();
|
||||||
|
asyncOpers.push(deleteOper);
|
||||||
|
}
|
||||||
|
await Promise.all(asyncOpers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve views details of the given id or throw not found error.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} viewId
|
||||||
|
*/
|
||||||
|
private async getViewByIdOrThrowError(tenantId: number, viewId: number): Promise<IView> {
|
||||||
|
const { View } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
this.logger.info('[views] get stored view.', { tenantId, viewId });
|
||||||
|
const view = await View.query().findById(viewId);
|
||||||
|
|
||||||
|
if (!view) {
|
||||||
|
this.logger.info('[views] the given id not found.', { tenantId, viewId });
|
||||||
|
throw new ServiceError(ERRORS.VIEW_NOT_FOUND);
|
||||||
|
}
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given view with associated roles and columns.
|
||||||
|
* @param {number} tenantId - Tenant id.
|
||||||
|
* @param {number} viewId - View id.
|
||||||
|
*/
|
||||||
|
public async deleteView(tenantId: number, viewId: number): Promise<void> {
|
||||||
|
const { View } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
this.logger.info('[views] trying to delete the given view.', { tenantId, viewId });
|
||||||
|
const view = await this.getViewByIdOrThrowError(tenantId, viewId);
|
||||||
|
|
||||||
|
if (view.predefined) {
|
||||||
|
this.logger.info('[views] cannot delete predefined.', { tenantId, viewId });
|
||||||
|
throw new ServiceError(ERRORS.VIEW_PREDEFINED);
|
||||||
|
}
|
||||||
|
await Promise.all([
|
||||||
|
view.$relatedQuery('roles').delete(),
|
||||||
|
view.$relatedQuery('columns').delete(),
|
||||||
|
]);
|
||||||
|
await View.query().where('id', view.id).delete();
|
||||||
|
this.logger.info('[views] deleted successfully.', { tenantId, viewId });
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user