@minimaltech/node-infra
Version:
Minimal Technology NodeJS Infrastructure - Loopback 4 Framework
463 lines • 22.1 kB
JavaScript
;
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