From e1b099f6a2e4f34d754ae73509454689dd2ce2c6 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 2 Oct 2025 22:51:49 +0700 Subject: [PATCH] feat: update approval workflow --- .../approval_workflows.module.go | 1 + .../approval_workflows.controller.go | 44 +++++ .../request/approval_workflows.request.go | 50 ++++++ .../service/approval_workflows.service.go | 151 ++++++++++++++++++ docs/swagger/docs.go | 97 +++++++++++ docs/swagger/swagger.json | 97 +++++++++++ docs/swagger/swagger.yaml | 64 ++++++++ 7 files changed, 504 insertions(+) diff --git a/app/module/approval_workflows/approval_workflows.module.go b/app/module/approval_workflows/approval_workflows.module.go index f71c5a9..ab26ec4 100644 --- a/app/module/approval_workflows/approval_workflows.module.go +++ b/app/module/approval_workflows/approval_workflows.module.go @@ -64,6 +64,7 @@ func (_i *ApprovalWorkflowsRouter) RegisterApprovalWorkflowsRoutes() { router.Post("/", approvalWorkflowsController.Save) router.Post("/with-steps", approvalWorkflowsController.SaveWithSteps) router.Post("/with-client-settings", approvalWorkflowsController.SaveWithClientSettings) + router.Put("/with-client-settings", approvalWorkflowsController.UpdateWithClientSettings) router.Post("/comprehensive-details", approvalWorkflowsController.GetComprehensiveDetails) router.Put("/:id", approvalWorkflowsController.Update) router.Put("/:id/with-steps", approvalWorkflowsController.UpdateWithSteps) diff --git a/app/module/approval_workflows/controller/approval_workflows.controller.go b/app/module/approval_workflows/controller/approval_workflows.controller.go index d38985f..59b6a2d 100644 --- a/app/module/approval_workflows/controller/approval_workflows.controller.go +++ b/app/module/approval_workflows/controller/approval_workflows.controller.go @@ -33,6 +33,7 @@ type ApprovalWorkflowsController interface { SaveWithSteps(c *fiber.Ctx) error UpdateWithSteps(c *fiber.Ctx) error SaveWithClientSettings(c *fiber.Ctx) error + UpdateWithClientSettings(c *fiber.Ctx) error GetComprehensiveDetails(c *fiber.Ctx) error } @@ -509,6 +510,8 @@ func (_i *approvalWorkflowsController) SaveWithClientSettings(c *fiber.Ctx) erro return err } + _i.Log.Info().Interface("workflowData", workflowData).Interface("clientSettingsData", clientSettingsData).Msg("Comprehensive workflow with client settings created successfully") + // Combine workflow, steps, and client settings data responseData := map[string]interface{}{ "workflow": workflowData, @@ -556,3 +559,44 @@ func (_i *approvalWorkflowsController) GetComprehensiveDetails(c *fiber.Ctx) err Data: details, }) } + +// UpdateWithClientSettings ApprovalWorkflows +// @Summary Update comprehensive ApprovalWorkflows with client settings +// @Description API for updating ApprovalWorkflows with workflow steps and client approval settings in a single call +// @Tags ApprovalWorkflows +// @Security Bearer +// @Param Authorization header string true "Insert the Authorization" +// @Param req body request.UpdateApprovalWorkflowWithClientSettingsRequest true "Comprehensive approval workflow update data" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /approval-workflows/with-client-settings [put] +func (_i *approvalWorkflowsController) UpdateWithClientSettings(c *fiber.Ctx) error { + req := new(request.UpdateApprovalWorkflowWithClientSettingsRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + // Get authToken from context + authToken := c.Get("Authorization") + + // Update comprehensive workflow with client settings + workflowData, clientSettingsData, err := _i.approvalWorkflowsService.UpdateWorkflowWithClientSettings(authToken, *req) + if err != nil { + return err + } + + // Combine workflow, steps, and client settings data + responseData := map[string]interface{}{ + "workflow": workflowData, + "clientSettings": clientSettingsData, + "message": "Comprehensive approval workflow with client settings updated successfully", + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Comprehensive approval workflow with client settings successfully updated"}, + Data: responseData, + }) +} diff --git a/app/module/approval_workflows/request/approval_workflows.request.go b/app/module/approval_workflows/request/approval_workflows.request.go index afea5ed..0ae0df3 100644 --- a/app/module/approval_workflows/request/approval_workflows.request.go +++ b/app/module/approval_workflows/request/approval_workflows.request.go @@ -304,3 +304,53 @@ func (req ApprovalWorkflowsQueryRequestContext) ToParamRequest() ApprovalWorkflo type ComprehensiveWorkflowDetailRequest struct { WorkflowId uint `json:"workflowId" validate:"required"` } + +// UpdateApprovalWorkflowWithClientSettingsRequest - Request for updating approval workflow with client settings +type UpdateApprovalWorkflowWithClientSettingsRequest struct { + WorkflowId uint `json:"workflowId" validate:"required"` + + // Workflow details + Name string `json:"name" validate:"required"` + Description string `json:"description"` + IsActive *bool `json:"isActive"` + IsDefault *bool `json:"isDefault"` + + // Workflow steps + Steps []ApprovalWorkflowStepRequest `json:"steps" validate:"required"` + + // Client approval settings + ClientSettings ClientApprovalSettingsRequest `json:"clientSettings"` +} + +// ToWorkflowEntity converts UpdateApprovalWorkflowWithClientSettingsRequest to ApprovalWorkflows entity +func (req UpdateApprovalWorkflowWithClientSettingsRequest) ToWorkflowEntity() *entity.ApprovalWorkflows { + return &entity.ApprovalWorkflows{ + Name: req.Name, + Description: &req.Description, + IsActive: req.IsActive, + IsDefault: req.IsDefault, + } +} + +// ToStepsEntity converts UpdateApprovalWorkflowWithClientSettingsRequest to ApprovalWorkflowSteps entities +func (req UpdateApprovalWorkflowWithClientSettingsRequest) ToStepsEntity() []*entity.ApprovalWorkflowSteps { + steps := make([]*entity.ApprovalWorkflowSteps, len(req.Steps)) + for i, stepReq := range req.Steps { + steps[i] = stepReq.ToEntity(req.WorkflowId) + } + return steps +} + +// ToClientApprovalSettingsEntity converts UpdateApprovalWorkflowWithClientSettingsRequest to ClientApprovalSettings entity +func (req UpdateApprovalWorkflowWithClientSettingsRequest) ToClientApprovalSettingsEntity() *entity.ClientApprovalSettings { + return &entity.ClientApprovalSettings{ + RequiresApproval: req.ClientSettings.RequiresApproval, + AutoPublishArticles: req.ClientSettings.AutoPublishArticles, + ApprovalExemptUsers: req.ClientSettings.ApprovalExemptUsers, + ApprovalExemptRoles: req.ClientSettings.ApprovalExemptRoles, + ApprovalExemptCategories: req.ClientSettings.ApprovalExemptCategories, + RequireApprovalFor: req.ClientSettings.RequireApprovalFor, + SkipApprovalFor: req.ClientSettings.SkipApprovalFor, + IsActive: req.ClientSettings.IsActive, + } +} diff --git a/app/module/approval_workflows/service/approval_workflows.service.go b/app/module/approval_workflows/service/approval_workflows.service.go index 97ad228..d97a275 100644 --- a/app/module/approval_workflows/service/approval_workflows.service.go +++ b/app/module/approval_workflows/service/approval_workflows.service.go @@ -62,6 +62,9 @@ type ApprovalWorkflowsService interface { // Comprehensive workflow creation with client settings CreateWorkflowWithClientSettings(authToken string, req request.CreateApprovalWorkflowWithClientSettingsRequest) (workflow *entity.ApprovalWorkflows, clientSettings *entity.ClientApprovalSettings, err error) + // Comprehensive workflow update with client settings + UpdateWorkflowWithClientSettings(authToken string, req request.UpdateApprovalWorkflowWithClientSettingsRequest) (workflow *entity.ApprovalWorkflows, clientSettings *entity.ClientApprovalSettings, err error) + // Comprehensive workflow details GetComprehensiveWorkflowDetails(authToken string, workflowId uint) (details *response.ComprehensiveWorkflowDetailResponse, err error) } @@ -1001,3 +1004,151 @@ func (_i *approvalWorkflowsService) GetComprehensiveWorkflowDetails(authToken st return details, nil } + +// UpdateWorkflowWithClientSettings updates a comprehensive approval workflow with client settings in a single transaction +func (_i *approvalWorkflowsService) UpdateWorkflowWithClientSettings(authToken string, req request.UpdateApprovalWorkflowWithClientSettingsRequest) (workflow *entity.ApprovalWorkflows, clientSettings *entity.ClientApprovalSettings, 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") + } + + _i.Log.Info(). + Uint("workflowId", req.WorkflowId). + Interface("clientId", clientId). + Msg("Updating comprehensive approval workflow with client settings") + + // Check if workflow exists + existingWorkflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, req.WorkflowId) + if err != nil { + return nil, nil, fmt.Errorf("failed to find workflow: %w", err) + } + if existingWorkflow == nil { + return nil, nil, errors.New("workflow not found") + } + + // Convert request to entities + workflowEntity := req.ToWorkflowEntity() + stepsEntity := req.ToStepsEntity() + clientSettingsEntity := req.ToClientApprovalSettingsEntity() + + // Validate workflow and steps + isValid, validationErrors, err := _i.ValidateWorkflow(authToken, workflowEntity, stepsEntity) + if err != nil { + return nil, nil, err + } + if !isValid { + return nil, nil, fmt.Errorf("validation failed: %v", validationErrors) + } + + // Start transaction-like operations with rollback capability + // Update workflow + err = _i.ApprovalWorkflowsRepository.Update(clientId, req.WorkflowId, workflowEntity) + if err != nil { + _i.Log.Error().Err(err).Uint("workflowId", req.WorkflowId).Msg("Failed to update workflow") + return nil, nil, fmt.Errorf("failed to update workflow: %w", err) + } + _i.Log.Info().Uint("workflowId", req.WorkflowId).Msg("Workflow updated successfully") + + // Delete existing steps and create new ones + existingSteps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, req.WorkflowId) + if err != nil { + _i.Log.Error().Err(err).Uint("workflowId", req.WorkflowId).Msg("Failed to get existing workflow steps") + return nil, nil, fmt.Errorf("failed to get existing workflow steps: %w", err) + } + + for _, step := range existingSteps { + err = _i.ApprovalWorkflowStepsRepository.Delete(clientId, step.ID) + if err != nil { + _i.Log.Error().Err(err).Uint("workflowId", req.WorkflowId).Uint("stepId", step.ID).Msg("Failed to delete existing workflow step") + return nil, nil, fmt.Errorf("failed to delete existing workflow step %d: %w", step.ID, err) + } + } + + // Create new workflow steps + for i, step := range stepsEntity { + step.WorkflowId = req.WorkflowId + step.StepOrder = i + 1 + step.ClientId = clientId + _, err = _i.ApprovalWorkflowStepsRepository.Create(clientId, step) + if err != nil { + _i.Log.Error().Err(err).Uint("workflowId", req.WorkflowId).Int("stepOrder", i+1).Msg("Failed to create workflow step") + // Rollback: delete the workflow + _i.ApprovalWorkflowsRepository.Delete(clientId, req.WorkflowId) + return nil, nil, fmt.Errorf("failed to create workflow step %d: %w", i+1, err) + } + } + _i.Log.Info().Uint("workflowId", req.WorkflowId).Int("stepsCount", len(stepsEntity)).Msg("Workflow steps updated successfully") + + // Set the workflow as default in client settings if specified + if req.IsDefault != nil && *req.IsDefault { + clientSettingsEntity.DefaultWorkflowId = &req.WorkflowId + } + + // Update or create client approval settings + existingClientSettings, err := _i.ClientApprovalSettingsRepository.FindByClientId(*clientId) + if err != nil { + _i.Log.Warn().Err(err).Interface("clientId", clientId).Msg("Failed to find existing client approval settings, will create new") + // Create new client settings + clientSettingsEntity.ClientId = *clientId + clientSettings, err = _i.ClientApprovalSettingsRepository.Create(clientId, clientSettingsEntity) + if err != nil { + _i.Log.Error().Err(err).Interface("clientId", clientId).Msg("Failed to create client approval settings") + // Rollback: delete the workflow and steps + rollbackSteps, _ := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, req.WorkflowId) + for _, step := range rollbackSteps { + _i.ApprovalWorkflowStepsRepository.Delete(clientId, step.ID) + } + _i.ApprovalWorkflowsRepository.Delete(clientId, req.WorkflowId) + return nil, nil, fmt.Errorf("failed to create client approval settings: %w", err) + } + } else { + // Update existing client settings + clientSettingsEntity.ID = existingClientSettings.ID + clientSettingsEntity.ClientId = *clientId + clientSettingsEntity.CreatedAt = existingClientSettings.CreatedAt + clientSettings, err = _i.ClientApprovalSettingsRepository.Update(clientId, clientSettingsEntity) + if err != nil { + _i.Log.Error().Err(err).Uint("clientSettingsId", existingClientSettings.ID).Msg("Failed to update client approval settings") + // Rollback: delete the workflow and steps + rollbackSteps, _ := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, req.WorkflowId) + for _, step := range rollbackSteps { + _i.ApprovalWorkflowStepsRepository.Delete(clientId, step.ID) + } + _i.ApprovalWorkflowsRepository.Delete(clientId, req.WorkflowId) + return nil, nil, fmt.Errorf("failed to update client approval settings: %w", err) + } + } + _i.Log.Info().Interface("clientId", clientId).Msg("Client approval settings updated successfully") + + // If this workflow is set as default, update the default workflow setting + if req.IsDefault != nil && *req.IsDefault { + err = _i.ApprovalWorkflowsRepository.SetDefault(clientId, req.WorkflowId) + if err != nil { + _i.Log.Warn().Err(err).Uint("workflowId", req.WorkflowId).Msg("Failed to set workflow as default") + // Don't fail the entire operation for this + } else { + _i.Log.Info().Uint("workflowId", req.WorkflowId).Msg("Workflow set as default successfully") + } + } + + workflow, err = _i.ApprovalWorkflowsRepository.FindOne(clientId, req.WorkflowId) + if err != nil { + return nil, nil, fmt.Errorf("failed to get workflow: %w", err) + } + + _i.Log.Info(). + Uint("workflowId", workflow.ID). + Interface("clientId", clientId). + Msg("Comprehensive approval workflow with client settings updated successfully") + + return workflow, clientSettings, nil +} diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index 692d891..4b25ffe 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -1814,6 +1814,62 @@ const docTemplate = `{ } }, "/approval-workflows/with-client-settings": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for updating ApprovalWorkflows with workflow steps and client approval settings in a single call", + "tags": [ + "ApprovalWorkflows" + ], + "summary": "Update comprehensive ApprovalWorkflows with client settings", + "parameters": [ + { + "type": "string", + "description": "Insert the Authorization", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Comprehensive approval workflow update data", + "name": "req", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateApprovalWorkflowWithClientSettingsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, "post": { "security": [ { @@ -18087,6 +18143,47 @@ const docTemplate = `{ } } }, + "request.UpdateApprovalWorkflowWithClientSettingsRequest": { + "type": "object", + "required": [ + "name", + "steps", + "workflowId" + ], + "properties": { + "clientSettings": { + "description": "Client approval settings", + "allOf": [ + { + "$ref": "#/definitions/request.ClientApprovalSettingsRequest" + } + ] + }, + "description": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "isDefault": { + "type": "boolean" + }, + "name": { + "description": "Workflow details", + "type": "string" + }, + "steps": { + "description": "Workflow steps", + "type": "array", + "items": { + "$ref": "#/definitions/request.ApprovalWorkflowStepRequest" + } + }, + "workflowId": { + "type": "integer" + } + } + }, "request.UpdateClientApprovalSettingsRequest": { "type": "object", "properties": { diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 89b65a2..7776703 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -1803,6 +1803,62 @@ } }, "/approval-workflows/with-client-settings": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for updating ApprovalWorkflows with workflow steps and client approval settings in a single call", + "tags": [ + "ApprovalWorkflows" + ], + "summary": "Update comprehensive ApprovalWorkflows with client settings", + "parameters": [ + { + "type": "string", + "description": "Insert the Authorization", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Comprehensive approval workflow update data", + "name": "req", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateApprovalWorkflowWithClientSettingsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, "post": { "security": [ { @@ -18076,6 +18132,47 @@ } } }, + "request.UpdateApprovalWorkflowWithClientSettingsRequest": { + "type": "object", + "required": [ + "name", + "steps", + "workflowId" + ], + "properties": { + "clientSettings": { + "description": "Client approval settings", + "allOf": [ + { + "$ref": "#/definitions/request.ClientApprovalSettingsRequest" + } + ] + }, + "description": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "isDefault": { + "type": "boolean" + }, + "name": { + "description": "Workflow details", + "type": "string" + }, + "steps": { + "description": "Workflow steps", + "type": "array", + "items": { + "$ref": "#/definitions/request.ApprovalWorkflowStepRequest" + } + }, + "workflowId": { + "type": "integer" + } + } + }, "request.UpdateClientApprovalSettingsRequest": { "type": "object", "properties": { diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 23e95ae..9c23617 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -1272,6 +1272,33 @@ definitions: minimum: 1 type: integer type: object + request.UpdateApprovalWorkflowWithClientSettingsRequest: + properties: + clientSettings: + allOf: + - $ref: '#/definitions/request.ClientApprovalSettingsRequest' + description: Client approval settings + description: + type: string + isActive: + type: boolean + isDefault: + type: boolean + name: + description: Workflow details + type: string + steps: + description: Workflow steps + items: + $ref: '#/definitions/request.ApprovalWorkflowStepRequest' + type: array + workflowId: + type: integer + required: + - name + - steps + - workflowId + type: object request.UpdateClientApprovalSettingsRequest: properties: approvalExemptCategories: @@ -3145,6 +3172,43 @@ paths: summary: Create comprehensive ApprovalWorkflows with client settings tags: - ApprovalWorkflows + put: + description: API for updating ApprovalWorkflows with workflow steps and client + approval settings in a single call + parameters: + - description: Insert the Authorization + in: header + name: Authorization + required: true + type: string + - description: Comprehensive approval workflow update data + in: body + name: req + required: true + schema: + $ref: '#/definitions/request.UpdateApprovalWorkflowWithClientSettingsRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update comprehensive ApprovalWorkflows with client settings + tags: + - ApprovalWorkflows /approval-workflows/with-steps: post: description: API for creating ApprovalWorkflows with steps