UNPKG

@minimaltech/node-infra

Version:

Minimal Technology NodeJS Infrastructure - Loopback 4 Framework

463 lines 22.1 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.defineRelationCrudController = exports.defineAssociateController = exports.defineRelationViewController = void 0; const core_1 = require("@loopback/core"); const repository_1 = require("@loopback/repository"); const rest_1 = require("@loopback/rest"); const get_1 = __importDefault(require("lodash/get")); const common_1 = require("../../common"); const utilities_1 = require("../../utilities"); const common_2 = require("./common"); const security_1 = require("@loopback/security"); // -------------------------------------------------------------------------------------------------------------- const defineRelationViewController = (opts) => { const { baseClass, entities, relation, defaultLimit = common_1.App.DEFAULT_QUERY_LIMIT, endPoint = '', schema, } = opts; const restPath = `/{id}/${endPoint ? endPoint : relation.name}`; const BaseClass = baseClass !== null && baseClass !== void 0 ? baseClass : common_2.BaseController; class ViewController extends BaseClass { constructor(sourceRepository, targetRepository, getCurrentUser) { super({ scope: `ViewController.${relation.name}` }); this.defaultLimit = defaultLimit; if (!sourceRepository) { throw (0, utilities_1.getError)({ statusCode: 500, message: '[defineRelationViewController] Invalid source repository!', }); } this.sourceRepository = sourceRepository; if (!targetRepository) { throw (0, utilities_1.getError)({ statusCode: 500, message: '[defineRelationViewController] Invalid target repository!', }); } this.targetRepository = targetRepository; if (getCurrentUser && typeof getCurrentUser !== 'function') { throw (0, utilities_1.getError)({ statusCode: 500, message: '[defineRelationViewController] Invalid getCurrentUser type | Please check again the 3rd parameter in constructor!', }); } this.getCurrentUser = getCurrentUser; } // ----------------------------------------------------------------------------------------------- _getBelongsToRepository(sourceId) { const ref = (0, get_1.default)(this.sourceRepository, relation.name)(sourceId); return ref; } _getHasOneRepository(sourceId) { const ref = (0, get_1.default)(this.sourceRepository, relation.name)(sourceId); return ref; } _getHasManyRepository(sourceId) { const ref = (0, get_1.default)(this.sourceRepository, relation.name)(sourceId); return ref; } _getHasManyThroughRepository(sourceId) { const ref = (0, get_1.default)(this.sourceRepository, relation.name)(sourceId); return ref; } // ---------------------------------------------------------------------------------------------------------- _getContextUser() { return new Promise((resolve, reject) => { if (!this.getCurrentUser) { resolve(null); return; } if (typeof this.getCurrentUser !== 'function') { throw (0, utilities_1.getError)({ statusCode: 500, message: '[defineRelationViewController][_getContextUser] Invalid getCurrentUser type | Please check again the 3rd parameter in constructor!', }); } this.getCurrentUser().then(resolve).catch(reject); }); } // ----------------------------------------------------------------------------------------------- find(id, filter) { switch (relation.type) { case common_1.EntityRelations.BELONGS_TO: { return this._getBelongsToRepository(id).get(); } case common_1.EntityRelations.HAS_ONE: { return this._getHasOneRepository(id).get((0, common_2.applyLimit)(filter)); } case common_1.EntityRelations.HAS_MANY: { return this._getHasManyRepository(id).find((0, common_2.applyLimit)(filter)); } case common_1.EntityRelations.HAS_MANY_THROUGH: { return this._getHasManyThroughRepository(id).find((0, common_2.applyLimit)(filter)); } default: { return Promise.resolve([]); } } } // ----------------------------------------------------------------------------------------------- count(id, where) { switch (relation.type) { case common_1.EntityRelations.BELONGS_TO: { return new Promise(resolve => { this._getBelongsToRepository(id) .get() .then(rs => { if (!rs) { resolve({ count: 0 }); return; } resolve({ count: 1 }); }) .catch(() => { resolve({ count: 0 }); }); }); } case common_1.EntityRelations.HAS_ONE: { return new Promise(resolve => { this._getHasOneRepository(id) .get() .then(rs => { if (!rs) { resolve({ count: 0 }); return; } resolve({ count: 1 }); }) .catch(() => { resolve({ count: 0 }); }); }); } case common_1.EntityRelations.HAS_MANY: { const relationRepository = this._getHasManyRepository(id); const targetConstraint = relationRepository.constraint; const isPrincipalEntity = this.targetRepository.modelClass.definition.properties['principalType'] !== null; const countCondition = Object.assign(Object.assign(Object.assign({}, where), targetConstraint), { principalType: isPrincipalEntity ? entities.source : undefined }); return this.targetRepository.count(countCondition); } case common_1.EntityRelations.HAS_MANY_THROUGH: { return new Promise((resolve, reject) => { const relationRepository = this._getHasManyThroughRepository(id); const throughConstraint = relationRepository.getThroughConstraintFromSource(); relationRepository .getThroughRepository() .then(throughRepository => { throughRepository .find({ where: Object.assign({}, throughConstraint), }) .then(throughInstances => { const targetConstraint = relationRepository.getTargetConstraintFromThroughModels(throughInstances); const condition = Object.assign(Object.assign({}, where), targetConstraint); resolve(this.targetRepository.count(condition)); }) .catch(reject); }) .catch(reject); }); } default: { return { count: 0 }; } } } } __decorate([ (0, rest_1.get)(restPath, { responses: { '200': { description: `Array of target model in relation ${relation.name}`, content: { 'application/json': { schema }, }, }, }, }), __param(0, rest_1.param.path.number('id')), __param(1, rest_1.param.query.object('filter')), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], ViewController.prototype, "find", null); __decorate([ (0, rest_1.get)(`${restPath}/count`, { responses: { '200': { content: { 'application/json': { schema: repository_1.CountSchema, }, }, }, }, }), __param(0, rest_1.param.path.number('id')), __param(1, rest_1.param.query.object('where')), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", void 0) ], ViewController.prototype, "count", null); return ViewController; }; exports.defineRelationViewController = defineRelationViewController; // -------------------------------------------------------------------------------------------------------------- const defineAssociateController = (opts) => { const { baseClass, relation, defaultLimit = common_1.App.DEFAULT_QUERY_LIMIT, endPoint = '', schema, } = opts; const restPath = `/{id}/${endPoint ? endPoint : relation.name}`; class AssociationController extends baseClass { constructor(sourceRepository, targetRepository, getCurrentUser) { super(sourceRepository, targetRepository, getCurrentUser); this.defaultLimit = defaultLimit; } // ----------------------------------------------------------------------------------------------- link(id, linkId) { return __awaiter(this, void 0, void 0, function* () { const isSourceExist = yield this.sourceRepository.exists(id); if (!isSourceExist) { throw (0, utilities_1.getError)({ statusCode: 400, message: 'Invalid association (source model is not existed)', }); } const isTargetExist = yield this.targetRepository.exists(linkId); if (!isTargetExist) { throw (0, utilities_1.getError)({ statusCode: 400, message: 'Invalid association (target model is not existed)', }); } const ref = (0, get_1.default)(this.sourceRepository, relation.name)(id); return ref.link(linkId); }); } // ----------------------------------------------------------------------------------------------- unlink(id, linkId) { return __awaiter(this, void 0, void 0, function* () { const ref = (0, get_1.default)(this.sourceRepository, relation.name)(id); return ref.unlink(linkId); }); } } __decorate([ (0, rest_1.post)(`${restPath}/{link_id}`, { responses: { '200': { description: `Create association between source and target for ${relation.name} relation`, content: { 'application/json': { schema, }, }, }, }, }), __param(0, rest_1.param.path.number('id')), __param(1, rest_1.param.path.number('link_id')), __metadata("design:type", Function), __metadata("design:paramtypes", [Number, Number]), __metadata("design:returntype", Promise) ], AssociationController.prototype, "link", null); __decorate([ (0, rest_1.del)(`${restPath}/{link_id}`, { responses: { '200': { description: `Remove association between source and target for ${relation.name} relation`, content: { 'application/json': {} }, }, }, }), __param(0, rest_1.param.path.number('id')), __param(1, rest_1.param.path.number('link_id')), __metadata("design:type", Function), __metadata("design:paramtypes", [Number, Number]), __metadata("design:returntype", Promise) ], AssociationController.prototype, "unlink", null); return AssociationController; }; exports.defineAssociateController = defineAssociateController; // -------------------------------------------------------------------------------------------------------------- const defineRelationCrudController = (controllerOptions) => { var _a, _b; const { association, schema, options = { useControlTarget: false, defaultLimit: common_1.App.DEFAULT_QUERY_LIMIT, endPoint: '', }, } = controllerOptions; const { entities, repositories, relation } = association; const { target } = schema; if (!common_1.EntityRelations.isValid(relation.type)) { throw (0, utilities_1.getError)({ statusCode: 500, message: `[defineRelationCrudController] Invalid relationType! Valids: ${[...common_1.EntityRelations.SCHEME_SET]}`, }); } const { target: targetSchema } = schema; const { useControlTarget = true, defaultLimit = common_1.App.DEFAULT_QUERY_LIMIT, endPoint = relation.name, } = options; const restPath = `{id}/${endPoint}`; const ViewController = (0, exports.defineRelationViewController)({ baseClass: common_2.BaseController, entities, relation, defaultLimit, endPoint, schema: target, }); const AssociationController = (0, exports.defineAssociateController)({ baseClass: ViewController, relation, defaultLimit, endPoint, schema: target, }); // ----------------------------------------------------------------------------------------------- const ExtendsableClass = relation.type === common_1.EntityRelations.HAS_MANY_THROUGH ? AssociationController : ViewController; if (!useControlTarget) { return ExtendsableClass; } // ----------------------------------------------------------------------------------------------- class Controller extends ExtendsableClass { constructor(sourceRepository, targetRepository, getCurrentUser) { super(sourceRepository, targetRepository, getCurrentUser); } // ----------------------------------------------------------------------------------------------- create(id, mapping) { return new Promise((resolve, reject) => { this._getContextUser() .then((currentUser) => { const ref = (0, get_1.default)(this.sourceRepository, relation.name)(id); ref .create(mapping, { authorId: currentUser === null || currentUser === void 0 ? void 0 : currentUser.userId, }) .then(resolve) .catch(reject); }) .catch(reject); }); } // ----------------------------------------------------------------------------------------------- patch(id, mapping, where) { return new Promise((resolve, reject) => { this._getContextUser() .then((currentUser) => { const ref = (0, get_1.default)(this.sourceRepository, relation.name)(id); ref .patch(mapping, where, { authorId: currentUser === null || currentUser === void 0 ? void 0 : currentUser.userId, }) .then(resolve) .catch(reject); }) .catch(reject); }); } // ----------------------------------------------------------------------------------------------- delete(id, where) { return new Promise((resolve, reject) => { this._getContextUser() .then((currentUser) => { const ref = (0, get_1.default)(this.sourceRepository, relation.name)(id); ref .delete(where, { authorId: currentUser === null || currentUser === void 0 ? void 0 : currentUser.userId, }) .then(resolve) .catch(reject); }) .catch(reject); }); } } __decorate([ (0, rest_1.post)(restPath, { responses: { '200': { description: `Create target model for ${relation.name} relation`, content: { 'application/json': { schema, }, }, }, }, }), __param(0, rest_1.param.path.number('id')), __param(1, (0, rest_1.requestBody)({ required: true, content: { 'application/json': { schema: targetSchema }, }, })), __metadata("design:type", Function), __metadata("design:paramtypes", [Number, Object]), __metadata("design:returntype", Promise) ], Controller.prototype, "create", null); __decorate([ (0, rest_1.patch)(restPath, { responses: { '200': { description: `Patch target model for ${relation.name} relation`, content: { 'application/json': { schema: repository_1.CountSchema } }, }, }, }), __param(0, rest_1.param.path.number('id')), __param(1, (0, rest_1.requestBody)({ required: true, content: { 'application/json': { schema: targetSchema }, }, })), __param(2, rest_1.param.query.object('where')), __metadata("design:type", Function), __metadata("design:paramtypes", [Number, Object, Object]), __metadata("design:returntype", Promise) ], Controller.prototype, "patch", null); __decorate([ (0, rest_1.del)(restPath, { responses: { '200': { description: `Delete target model for ${relation.name} relation`, content: { 'application/json': { schema: repository_1.CountSchema } }, }, }, }), __param(0, rest_1.param.path.number('id')), __param(1, rest_1.param.query.object('where')), __metadata("design:type", Function), __metadata("design:paramtypes", [Number, Object]), __metadata("design:returntype", Promise) ], Controller.prototype, "delete", null); (0, core_1.inject)(`repositories.${(_a = repositories === null || repositories === void 0 ? void 0 : repositories.source) !== null && _a !== void 0 ? _a : `${entities.source}Repository`}`)(Controller, undefined, 0); (0, core_1.inject)(`repositories.${(_b = repositories === null || repositories === void 0 ? void 0 : repositories.target) !== null && _b !== void 0 ? _b : `${entities.target}Repository`}`)(Controller, undefined, 1); if (options.doInjectCurrentUser) { core_1.inject.getter(security_1.SecurityBindings.USER, { optional: true })(Controller, undefined, 2); } return Controller; }; exports.defineRelationCrudController = defineRelationCrudController; //# sourceMappingURL=relational.controller.js.map