kontenhumas-be/app/module/approval_workflows/service/approval_workflows.service.go

508 lines
16 KiB
Go

package service
import (
"errors"
"fmt"
"netidhub-saas-be/app/database/entity"
stepRepo "netidhub-saas-be/app/module/approval_workflow_steps/repository"
"netidhub-saas-be/app/module/approval_workflows/repository"
"netidhub-saas-be/app/module/approval_workflows/request"
usersRepo "netidhub-saas-be/app/module/users/repository"
"netidhub-saas-be/utils/paginator"
utilSvc "netidhub-saas-be/utils/service"
"github.com/google/uuid"
"github.com/rs/zerolog"
)
type approvalWorkflowsService struct {
ApprovalWorkflowsRepository repository.ApprovalWorkflowsRepository
ApprovalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository
UsersRepository usersRepo.UsersRepository
Log zerolog.Logger
}
// ApprovalWorkflowsService define interface of IApprovalWorkflowsService
type ApprovalWorkflowsService interface {
// Basic CRUD
GetAll(authToken string, req request.ApprovalWorkflowsQueryRequest) (workflows []*entity.ApprovalWorkflows, paging paginator.Pagination, err error)
FindOne(authToken string, id uint) (workflow *entity.ApprovalWorkflows, err error)
Create(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error)
Update(authToken string, id uint, workflow *entity.ApprovalWorkflows) (err error)
Delete(authToken string, id uint) (err error)
// Workflow management
GetDefault(authToken string) (workflow *entity.ApprovalWorkflows, err error)
SetDefault(authToken string, id uint) (err error)
ActivateWorkflow(authToken string, id uint) (err error)
DeactivateWorkflow(authToken string, id uint) (err error)
// Workflow with steps
GetWorkflowWithSteps(authToken string, id uint) (workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps, err error)
CreateWorkflowWithSteps(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error)
UpdateWorkflowWithSteps(authToken string, id uint, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (err error)
// Validation
ValidateWorkflow(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error)
CanDeleteWorkflow(authToken string, id uint) (canDelete bool, reason string, err error)
}
func NewApprovalWorkflowsService(
approvalWorkflowsRepository repository.ApprovalWorkflowsRepository,
approvalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository,
usersRepository usersRepo.UsersRepository,
log zerolog.Logger,
) ApprovalWorkflowsService {
return &approvalWorkflowsService{
ApprovalWorkflowsRepository: approvalWorkflowsRepository,
ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository,
UsersRepository: usersRepository,
Log: log,
}
}
// Basic CRUD implementations
func (_i *approvalWorkflowsService) GetAll(authToken string, req request.ApprovalWorkflowsQueryRequest) (workflows []*entity.ApprovalWorkflows, paging paginator.Pagination, err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return nil, paginator.Pagination{}, errors.New("clientId not found in auth token")
}
return _i.ApprovalWorkflowsRepository.GetAll(clientId, req)
}
func (_i *approvalWorkflowsService) FindOne(authToken string, id uint) (workflow *entity.ApprovalWorkflows, err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return nil, errors.New("clientId not found in auth token")
}
return _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
}
func (_i *approvalWorkflowsService) Create(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return nil, errors.New("clientId not found in auth token")
}
// Validate workflow and steps
isValid, validationErrors, err := _i.ValidateWorkflow(authToken, workflow, steps)
if err != nil {
return nil, err
}
if !isValid {
return nil, errors.New(fmt.Sprintf("Validation failed: %v", validationErrors))
}
// Create workflow
workflowReturn, err = _i.ApprovalWorkflowsRepository.Create(clientId, workflow)
if err != nil {
return nil, err
}
// Create steps
for i, step := range steps {
step.WorkflowId = workflowReturn.ID
step.StepOrder = i + 1
_, err = _i.ApprovalWorkflowStepsRepository.Create(clientId, step)
if err != nil {
// Rollback workflow creation if step creation fails
_i.ApprovalWorkflowsRepository.Delete(clientId, workflowReturn.ID)
return nil, err
}
}
return workflowReturn, nil
}
func (_i *approvalWorkflowsService) Update(authToken string, id uint, workflow *entity.ApprovalWorkflows) (err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return errors.New("clientId not found in auth token")
}
// Check if workflow exists
existingWorkflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
if err != nil {
return err
}
if existingWorkflow == nil {
return errors.New("workflow not found")
}
return _i.ApprovalWorkflowsRepository.Update(clientId, id, workflow)
}
func (_i *approvalWorkflowsService) Delete(authToken string, id uint) (err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return errors.New("clientId not found in auth token")
}
// Check if workflow can be deleted
canDelete, reason, err := _i.CanDeleteWorkflow(authToken, id)
if err != nil {
return err
}
if !canDelete {
return errors.New(reason)
}
return _i.ApprovalWorkflowsRepository.Delete(clientId, id)
}
// Workflow management
func (_i *approvalWorkflowsService) GetDefault(authToken string) (workflow *entity.ApprovalWorkflows, err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return nil, errors.New("clientId not found in auth token")
}
return _i.ApprovalWorkflowsRepository.FindDefault(clientId)
}
func (_i *approvalWorkflowsService) SetDefault(authToken string, id uint) (err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return errors.New("clientId not found in auth token")
}
// Check if workflow exists and is active
workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
if err != nil {
return err
}
if workflow == nil {
return errors.New("workflow not found")
}
if workflow.IsActive == nil || !*workflow.IsActive {
return errors.New("cannot set inactive workflow as default")
}
return _i.ApprovalWorkflowsRepository.SetDefault(clientId, id)
}
func (_i *approvalWorkflowsService) ActivateWorkflow(authToken string, id uint) (err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return errors.New("clientId not found in auth token")
}
// Validate workflow before activation
workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
if err != nil {
return err
}
if workflow == nil {
return errors.New("workflow not found")
}
// Get workflow steps and validate
steps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id)
if err != nil {
return err
}
isValid, validationErrors, err := _i.ValidateWorkflow(authToken, workflow, steps)
if err != nil {
return err
}
if !isValid {
return errors.New(fmt.Sprintf("Cannot activate invalid workflow: %v", validationErrors))
}
// Activate workflow
isActive := true
updateData := &entity.ApprovalWorkflows{IsActive: &isActive}
return _i.ApprovalWorkflowsRepository.Update(clientId, id, updateData)
}
func (_i *approvalWorkflowsService) DeactivateWorkflow(authToken string, id uint) (err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return errors.New("clientId not found in auth token")
}
// Check if this is the default workflow
defaultWorkflow, err := _i.ApprovalWorkflowsRepository.FindDefault(clientId)
if err != nil {
return err
}
if defaultWorkflow != nil && defaultWorkflow.ID == id {
return errors.New("cannot deactivate default workflow")
}
// Check if workflow is being used in active approval flows
canDelete, reason, err := _i.CanDeleteWorkflow(authToken, id)
if err != nil {
return err
}
if !canDelete {
return errors.New(fmt.Sprintf("Cannot deactivate workflow: %s", reason))
}
// Deactivate workflow
isActive := false
updateData := &entity.ApprovalWorkflows{IsActive: &isActive}
return _i.ApprovalWorkflowsRepository.Update(clientId, id, updateData)
}
// Workflow with steps
func (_i *approvalWorkflowsService) GetWorkflowWithSteps(authToken string, id uint) (workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps, err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return nil, nil, errors.New("clientId not found in auth token")
}
workflow, err = _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
if err != nil {
return nil, nil, err
}
steps, err = _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id)
if err != nil {
return nil, nil, err
}
return workflow, steps, nil
}
func (_i *approvalWorkflowsService) CreateWorkflowWithSteps(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return nil, errors.New("clientId not found in auth token")
}
return _i.Create(authToken, workflow, steps)
}
func (_i *approvalWorkflowsService) UpdateWorkflowWithSteps(authToken string, id uint, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return errors.New("clientId not found in auth token")
}
// Update workflow
err = _i.Update(authToken, id, workflow)
if err != nil {
return err
}
// Get existing steps
existingSteps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id)
if err != nil {
return err
}
// Delete existing steps (simplified approach - in production, you might want to update/merge)
for _, existingStep := range existingSteps {
err = _i.ApprovalWorkflowStepsRepository.Delete(clientId, existingStep.ID)
if err != nil {
return err
}
}
// Create new steps
for i, step := range steps {
step.WorkflowId = id
step.StepOrder = i + 1
_, err = _i.ApprovalWorkflowStepsRepository.Create(clientId, step)
if err != nil {
return err
}
}
return nil
}
// Validation
func (_i *approvalWorkflowsService) ValidateWorkflow(authToken string, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error) {
errors = make([]string, 0)
// Validate workflow
if workflow.Name == "" {
errors = append(errors, "Workflow name is required")
}
// Validate steps
if len(steps) == 0 {
errors = append(errors, "Workflow must have at least one step")
} else {
// Check for duplicate step orders
stepOrderMap := make(map[int]bool)
for i, step := range steps {
expectedOrder := i + 1
if step.StepOrder != 0 && step.StepOrder != expectedOrder {
errors = append(errors, fmt.Sprintf("Step %d has incorrect order %d, expected %d", i+1, step.StepOrder, expectedOrder))
}
if stepOrderMap[step.StepOrder] {
errors = append(errors, fmt.Sprintf("Duplicate step order: %d", step.StepOrder))
}
stepOrderMap[step.StepOrder] = true
if step.StepName == "" {
errors = append(errors, fmt.Sprintf("Step %d name is required", i+1))
}
if step.RequiredUserLevelId == 0 {
errors = append(errors, fmt.Sprintf("Step %d must have a required user level", i+1))
}
}
}
isValid = len(errors) == 0
return isValid, errors, nil
}
func (_i *approvalWorkflowsService) CanDeleteWorkflow(authToken string, id uint) (canDelete bool, reason string, err error) {
// Extract clientId from authToken
var clientId *uuid.UUID
if authToken != "" {
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
if user != nil && user.ClientId != nil {
clientId = user.ClientId
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
}
}
if clientId == nil {
return false, "clientId not found in auth token", errors.New("clientId not found in auth token")
}
// Check if workflow is default
defaultWorkflow, err := _i.ApprovalWorkflowsRepository.FindDefault(clientId)
if err != nil {
return false, "", err
}
if defaultWorkflow != nil && defaultWorkflow.ID == id {
return false, "Cannot delete default workflow", nil
}
// Check if workflow is being used in active approval flows
// This would require a method in ArticleApprovalFlowsRepository
// For now, we'll assume it can be deleted
// TODO: Implement check for active approval flows
return true, "", nil
}