UNPKG

dt-common-device

Version:

A secure and robust device management library for IoT applications

643 lines (642 loc) 29.4 kB
"use strict"; var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); var _, done = false; for (var i = decorators.length - 1; i >= 0; i--) { var context = {}; for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; for (var p in contextIn.access) context.access[p] = contextIn.access[p]; context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); if (kind === "accessor") { if (result === void 0) continue; if (result === null || typeof result !== "object") throw new TypeError("Object expected"); if (_ = accept(result.get)) descriptor.get = _; if (_ = accept(result.set)) descriptor.set = _; if (_ = accept(result.init)) initializers.unshift(_); } else if (_ = accept(result)) { if (kind === "field") initializers.unshift(_); else descriptor[key] = _; } } if (target) Object.defineProperty(target, contextIn.name, descriptor); done = true; }; var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { var useValue = arguments.length > 2; for (var i = 0; i < initializers.length; i++) { value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); } return useValue ? value : void 0; }; var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) { if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.IssueService = void 0; const typedi_1 = require("typedi"); const Issue_model_1 = require("../models/Issue.model"); const issue_types_1 = require("../../../types/issue.types"); const IssueBuilder_1 = require("../entities/IssueBuilder"); let IssueService = (() => { let _classDecorators = [(0, typedi_1.Service)()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; var IssueService = _classThis = class { constructor(issueRepository) { this.issueRepository = issueRepository; } /** * Create a readiness issue using IssueBuilder */ async createReadinessIssue(propertyId, title, description, createdBy, entityId, entityType, assignedTo, dueDate) { const issueBuilder = IssueBuilder_1.IssueBuilder.createReadinessIssue() .setPropertyId(propertyId) .setTitle(title) .setDescription(description) .setCreatedBy(createdBy); if (entityId) issueBuilder.setEntityId(entityId); if (entityType) issueBuilder.setEntityType(entityType); if (assignedTo) issueBuilder.setAssignedTo(assignedTo); if (dueDate) issueBuilder.setDueDate(dueDate); return await this.createIssue(issueBuilder); } /** * Create an operations issue using IssueBuilder */ async createOperationsIssue(propertyId, title, description, createdBy, entityId, entityType, assignedTo, dueDate) { const issueBuilder = IssueBuilder_1.IssueBuilder.createOperationsIssue() .setPropertyId(propertyId) .setTitle(title) .setDescription(description) .setCreatedBy(createdBy); if (entityId) issueBuilder.setEntityId(entityId); if (entityType) issueBuilder.setEntityType(entityType); if (assignedTo) issueBuilder.setAssignedTo(assignedTo); if (dueDate) issueBuilder.setDueDate(dueDate); return await this.createIssue(issueBuilder); } /** * Create a security issue using IssueBuilder */ async createSecurityIssue(propertyId, title, description, createdBy, entityId, entityType, assignedTo, dueDate) { const issueBuilder = IssueBuilder_1.IssueBuilder.createSecurityIssue() .setPropertyId(propertyId) .setTitle(title) .setDescription(description) .setCreatedBy(createdBy); if (entityId) issueBuilder.setEntityId(entityId); if (entityType) issueBuilder.setEntityType(entityType); if (assignedTo) issueBuilder.setAssignedTo(assignedTo); if (dueDate) issueBuilder.setDueDate(dueDate); return await this.createIssue(issueBuilder); } /** * Create an energy issue using IssueBuilder */ async createEnergyIssue(propertyId, title, description, createdBy, entityId, entityType, assignedTo, dueDate) { const issueBuilder = IssueBuilder_1.IssueBuilder.createEnergyIssue() .setPropertyId(propertyId) .setTitle(title) .setDescription(description) .setCreatedBy(createdBy); if (entityId) issueBuilder.setEntityId(entityId); if (entityType) issueBuilder.setEntityType(entityType); if (assignedTo) issueBuilder.setAssignedTo(assignedTo); if (dueDate) issueBuilder.setDueDate(dueDate); return await this.createIssue(issueBuilder); } /** * Create a device-specific issue using IssueBuilder */ async createDeviceIssue(deviceId, propertyId, title, description, createdBy, category, priority, assignedTo, dueDate) { const issueBuilder = IssueBuilder_1.IssueBuilder.createDeviceIssue(deviceId, propertyId) .setTitle(title) .setDescription(description) .setCreatedBy(createdBy); if (category) issueBuilder.setCategory(category); if (priority) issueBuilder.setPriority(priority); if (assignedTo) issueBuilder.setAssignedTo(assignedTo); if (dueDate) issueBuilder.setDueDate(dueDate); return await this.createIssue(issueBuilder); } /** * Create a hub-specific issue using IssueBuilder */ async createHubIssue(hubId, propertyId, title, description, createdBy, category, priority, assignedTo, dueDate) { const issueBuilder = IssueBuilder_1.IssueBuilder.createHubIssue(hubId, propertyId) .setTitle(title) .setDescription(description) .setCreatedBy(createdBy); if (category) issueBuilder.setCategory(category); if (priority) issueBuilder.setPriority(priority); if (assignedTo) issueBuilder.setAssignedTo(assignedTo); if (dueDate) issueBuilder.setDueDate(dueDate); return await this.createIssue(issueBuilder); } /** * Create a user-specific issue using IssueBuilder */ async createUserIssue(userId, propertyId, title, description, createdBy, category, priority, assignedTo, dueDate) { const issueBuilder = IssueBuilder_1.IssueBuilder.createUserIssue(userId, propertyId) .setTitle(title) .setDescription(description) .setCreatedBy(createdBy); if (category) issueBuilder.setCategory(category); if (priority) issueBuilder.setPriority(priority); if (assignedTo) issueBuilder.setAssignedTo(assignedTo); if (dueDate) issueBuilder.setDueDate(dueDate); return await this.createIssue(issueBuilder); } /** * Create a maintenance issue using IssueBuilder */ async createMaintenanceIssue(propertyId, title, description, createdBy, entityId, entityType, assignedTo, dueDate) { const issueBuilder = IssueBuilder_1.IssueBuilder.createMaintenanceIssue(propertyId, entityId, entityType) .setTitle(title) .setDescription(description) .setCreatedBy(createdBy); if (assignedTo) issueBuilder.setAssignedTo(assignedTo); if (dueDate) issueBuilder.setDueDate(dueDate); return await this.createIssue(issueBuilder); } /** * Create an urgent issue using IssueBuilder */ async createUrgentIssue(propertyId, title, description, createdBy, entityId, entityType, assignedTo, dueDate) { const issueBuilder = IssueBuilder_1.IssueBuilder.createUrgentIssue(propertyId, entityId, entityType) .setTitle(title) .setDescription(description) .setCreatedBy(createdBy); if (assignedTo) issueBuilder.setAssignedTo(assignedTo); if (dueDate) issueBuilder.setDueDate(dueDate); return await this.createIssue(issueBuilder); } /** * Create a new issue with business logic validation * Accepts either a CreateIssueData object or an IssueBuilder instance */ async createIssue(issueData) { let processedIssueData; // Handle IssueBuilder instance if (issueData instanceof IssueBuilder_1.IssueBuilder) { processedIssueData = issueData.build(); } else { processedIssueData = issueData; } // Business logic: Validate issue data this.validateIssueData(processedIssueData); // Business logic: Set default priority if not provided if (!processedIssueData.priority) { processedIssueData.priority = this.determineDefaultPriority(processedIssueData.category); } // Business logic: Validate due date is in the future if (processedIssueData.dueDate && processedIssueData.dueDate <= new Date()) { throw new Error("Due date must be in the future"); } return await this.issueRepository.create(processedIssueData); } /** * Get issue by ID with business logic */ async getIssueById(id, includeDeleted = false) { if (!id) { throw new Error("Issue ID is required"); } const issue = await this.issueRepository.findById(id, includeDeleted); // Business logic: Check if issue is overdue if (issue?.dueDate && issue.dueDate < new Date() && issue.status !== issue_types_1.IssueStatus.RESOLVED) { // You could add a flag or handle overdue logic here console.warn(`Issue ${id} is overdue`); } return issue; } /** * Get all issues with business logic filtering */ async getIssues(filters = {}) { // Business logic: Validate filters this.validateFilters(filters); // Business logic: Apply business rules to filters const enhancedFilters = this.applyBusinessRules(filters); return await this.issueRepository.findAll(enhancedFilters); } /** * Update an issue with business logic validation */ async updateIssue(id, updateData) { if (!id) { throw new Error("Issue ID is required"); } // Business logic: Validate update data this.validateUpdateData(updateData); // Business logic: Check if issue exists and is not deleted const existingIssue = await this.issueRepository.findById(id); if (!existingIssue) { throw new Error("Issue not found"); } // Business logic: Handle status transitions if (updateData.status) { this.validateStatusTransition(existingIssue.status, updateData.status); } // Business logic: Handle priority changes if (updateData.priority) { this.validatePriorityChange(existingIssue.priority, updateData.priority); } return await this.issueRepository.update(id, updateData); } /** * Soft delete an issue with business logic */ async deleteIssue(id, deletedBy) { if (!id || !deletedBy) { throw new Error("Issue ID and deleted by user are required"); } // Business logic: Check if issue can be deleted const issue = await this.issueRepository.findById(id); if (!issue) { throw new Error("Issue not found"); } // Business logic: Prevent deletion of resolved issues (optional rule) if (issue.status === issue_types_1.IssueStatus.RESOLVED) { throw new Error("Cannot delete resolved issues"); } return await this.issueRepository.softDelete(id, deletedBy); } /** * Permanently delete an issue */ async permanentlyDeleteIssue(id) { return await this.issueRepository.hardDelete(id); } /** * Add a comment with business logic */ async addComment(issueId, commentData) { if (!issueId || !commentData.userId || !commentData.content) { throw new Error("Issue ID, user ID, and comment content are required"); } // Business logic: Check if issue exists and is active const issue = await this.issueRepository.findById(issueId); if (!issue) { throw new Error("Issue not found"); } if (issue.isDeleted) { throw new Error("Cannot add comment to deleted issue"); } // Business logic: Update issue status based on comment (optional) const shouldUpdateStatus = this.shouldUpdateStatusOnComment(commentData.content); if (shouldUpdateStatus && issue.status === issue_types_1.IssueStatus.PENDING) { await this.issueRepository.update(issueId, { status: issue_types_1.IssueStatus.IN_PROGRESS, updatedBy: commentData.userId, }); } // Add comment using the model instance methods const issueModel = await Issue_model_1.IssueModel.findById(issueId); if (issueModel) { issueModel.addComment(commentData); return await issueModel.save(); } return null; } /** * Update a comment on an issue */ async updateComment(issueId, commentId, content, userId) { const issueModel = await Issue_model_1.IssueModel.findById(issueId); if (!issueModel) return false; const success = issueModel.updateComment(commentId, content, userId); if (success) { await issueModel.save(); } return success; } /** * Remove a comment from an issue */ async removeComment(issueId, commentId) { const issueModel = await Issue_model_1.IssueModel.findById(issueId); if (!issueModel) return false; const success = issueModel.removeComment(commentId); if (success) { await issueModel.save(); } return success; } /** * Resolve an issue with business logic */ async resolveIssue(id, resolvedBy) { if (!id || !resolvedBy) { throw new Error("Issue ID and resolved by user are required"); } // Business logic: Check if issue can be resolved const issue = await this.issueRepository.findById(id); if (!issue) { throw new Error("Issue not found"); } if (issue.status === issue_types_1.IssueStatus.RESOLVED) { throw new Error("Issue is already resolved"); } if (issue.status === issue_types_1.IssueStatus.CANCELLED) { throw new Error("Cannot resolve cancelled issue"); } // Business logic: Auto-assign if not assigned if (!issue.assignedTo) { await this.issueRepository.update(id, { assignedTo: resolvedBy, updatedBy: resolvedBy, }); } // Resolve the issue using model instance methods const issueModel = await Issue_model_1.IssueModel.findById(id); if (issueModel) { issueModel.resolve(resolvedBy); return await issueModel.save(); } return null; } /** * Reopen a resolved issue */ async reopenIssue(id, reopenedBy) { const issueModel = await Issue_model_1.IssueModel.findById(id); if (!issueModel) return null; issueModel.reopen(reopenedBy); return await issueModel.save(); } /** * Assign an issue with business logic */ async assignIssue(id, userId, assignedBy) { if (!id || !userId || !assignedBy) { throw new Error("Issue ID, assignee user ID, and assigned by user are required"); } // Business logic: Check if issue can be assigned const issue = await this.issueRepository.findById(id); if (!issue) { throw new Error("Issue not found"); } if (issue.status === issue_types_1.IssueStatus.RESOLVED || issue.status === issue_types_1.IssueStatus.CLOSED) { throw new Error("Cannot assign resolved or closed issue"); } // Business logic: Update status to IN_PROGRESS when assigned const updateData = { assignedTo: userId, updatedBy: assignedBy, }; if (issue.status === issue_types_1.IssueStatus.PENDING) { updateData.status = issue_types_1.IssueStatus.IN_PROGRESS; } return await this.issueRepository.update(id, updateData); } /** * Unassign an issue */ async unassignIssue(id, unassignedBy) { const issueModel = await Issue_model_1.IssueModel.findById(id); if (!issueModel) return null; issueModel.unassign(unassignedBy); return await issueModel.save(); } /** * Get issues by property with business logic */ async getIssuesByProperty(propertyId, includeDeleted = false) { if (!propertyId) { throw new Error("Property ID is required"); } return await this.issueRepository.findByProperty(propertyId, includeDeleted); } /** * Get issues assigned to a user with business logic */ async getIssuesByAssignee(assignedTo, includeDeleted = false) { if (!assignedTo) { throw new Error("Assignee user ID is required"); } return await this.issueRepository.findByAssignee(assignedTo, includeDeleted); } /** * Get issues by entity */ async getIssuesByEntity(entityId, entityType, includeDeleted = false) { return await this.issueRepository.findByEntity(entityId, entityType, includeDeleted); } /** * Get issues by status */ async getIssuesByStatus(status, includeDeleted = false) { return await this.issueRepository.findByStatus(status, includeDeleted); } /** * Get issues by priority */ async getIssuesByPriority(priority, includeDeleted = false) { return await this.issueRepository.findByPriority(priority, includeDeleted); } /** * Get overdue issues with business logic */ async getOverdueIssues(includeDeleted = false) { const overdueIssues = await this.issueRepository.findOverdue(includeDeleted); // Business logic: Log overdue issues for monitoring if (overdueIssues.length > 0) { console.warn(`Found ${overdueIssues.length} overdue issues`); } return overdueIssues; } /** * Get upcoming issues (due within specified days) */ async getUpcomingIssues(days = 7, includeDeleted = false) { return await this.issueRepository.findUpcoming(days, includeDeleted); } /** * Get issue statistics with business logic */ async getIssueStatistics(propertyId) { const stats = await this.issueRepository.getStatistics(propertyId); // Business logic: Calculate additional metrics const responseTime = this.calculateAverageResponseTime(stats); const resolutionRate = this.calculateResolutionRate(stats); // Log resolution rate for monitoring console.log(`Resolution rate: ${resolutionRate.toFixed(2)}%`); console.log(`Response time: ${responseTime.toFixed(2)} days`); // Business logic: Add alerts for critical metrics if (stats.overdue > 0) { console.warn(`Alert: ${stats.overdue} overdue issues detected`); } if (stats.byPriority[issue_types_1.IssuePriority.CRITICAL] > 0) { console.error(`Alert: ${stats.byPriority[issue_types_1.IssuePriority.CRITICAL]} critical issues require immediate attention`); } return stats; } /** * Search issues with business logic */ async searchIssues(searchTerm, filters = {}) { if (!searchTerm || searchTerm.trim().length < 2) { throw new Error("Search term must be at least 2 characters long"); } return await this.issueRepository.search(searchTerm, filters); } // Private business logic methods validateIssueData(data) { if (!data.title || data.title.trim().length < 5) { throw new Error("Issue title must be at least 5 characters long"); } if (!data.description || data.description.trim().length < 10) { throw new Error("Issue description must be at least 10 characters long"); } if (!data.propertyId) { throw new Error("Property ID is required"); } if (!data.createdBy) { throw new Error("Created by user ID is required"); } } validateFilters(filters) { if (filters.limit && (filters.limit < 1 || filters.limit > 100)) { throw new Error("Limit must be between 1 and 100"); } if (filters.skip && filters.skip < 0) { throw new Error("Skip must be non-negative"); } } validateUpdateData(data) { if (data.title && data.title.trim().length < 5) { throw new Error("Issue title must be at least 5 characters long"); } if (data.description && data.description.trim().length < 10) { throw new Error("Issue description must be at least 10 characters long"); } } validateStatusTransition(currentStatus, newStatus) { const validTransitions = { [issue_types_1.IssueStatus.PENDING]: [ issue_types_1.IssueStatus.IN_PROGRESS, issue_types_1.IssueStatus.CANCELLED, issue_types_1.IssueStatus.ON_HOLD, ], [issue_types_1.IssueStatus.IN_PROGRESS]: [ issue_types_1.IssueStatus.RESOLVED, issue_types_1.IssueStatus.CANCELLED, issue_types_1.IssueStatus.ON_HOLD, ], [issue_types_1.IssueStatus.RESOLVED]: [issue_types_1.IssueStatus.CLOSED, issue_types_1.IssueStatus.PENDING], // Reopen [issue_types_1.IssueStatus.CLOSED]: [issue_types_1.IssueStatus.PENDING], // Reopen [issue_types_1.IssueStatus.CANCELLED]: [issue_types_1.IssueStatus.PENDING], // Reopen [issue_types_1.IssueStatus.ON_HOLD]: [issue_types_1.IssueStatus.PENDING, issue_types_1.IssueStatus.IN_PROGRESS], }; if (!validTransitions[currentStatus]?.includes(newStatus)) { throw new Error(`Invalid status transition from ${currentStatus} to ${newStatus}`); } } validatePriorityChange(currentPriority, newPriority) { // Business rule: Only allow priority escalation, not de-escalation for critical issues if (currentPriority === issue_types_1.IssuePriority.CRITICAL && newPriority !== issue_types_1.IssuePriority.CRITICAL) { throw new Error("Cannot de-escalate priority of critical issues"); } } determineDefaultPriority(category) { // Business logic: Determine default priority based on category const categoryPriorities = { [issue_types_1.IssuesCategory.READINESS]: issue_types_1.IssuePriority.MEDIUM, [issue_types_1.IssuesCategory.OPERATIONS]: issue_types_1.IssuePriority.HIGH, [issue_types_1.IssuesCategory.SECURITY]: issue_types_1.IssuePriority.CRITICAL, [issue_types_1.IssuesCategory.ENERGY]: issue_types_1.IssuePriority.LOW, [issue_types_1.IssuesCategory.OTHER]: issue_types_1.IssuePriority.MEDIUM, }; return categoryPriorities[category] || issue_types_1.IssuePriority.MEDIUM; } applyBusinessRules(filters) { // Business logic: Apply additional filters based on business rules const enhancedFilters = { ...filters }; // Example: Always exclude cancelled issues unless explicitly requested if (!enhancedFilters.status || enhancedFilters.status !== issue_types_1.IssueStatus.CANCELLED) { enhancedFilters.status = { $ne: issue_types_1.IssueStatus.CANCELLED }; } return enhancedFilters; } shouldUpdateStatusOnComment(content) { // Business logic: Determine if comment should trigger status change const statusKeywords = [ "working on", "investigating", "fixing", "resolving", ]; return statusKeywords.some((keyword) => content.toLowerCase().includes(keyword)); } calculateAverageResponseTime(stats) { // Business logic: Calculate average response time (placeholder) return 0; } calculateResolutionRate(stats) { // Business logic: Calculate resolution rate if (stats.total === 0) return 0; return ((stats.resolved + stats.closed) / stats.total) * 100; } }; __setFunctionName(_classThis, "IssueService"); (() => { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); IssueService = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); __runInitializers(_classThis, _classExtraInitializers); })(); return IssueService = _classThis; })(); exports.IssueService = IssueService;