diff --git a/Makefile b/Makefile index 9690b5d..43c17d6 100644 --- a/Makefile +++ b/Makefile @@ -93,3 +93,4 @@ generatemocks: cd $(CURDIR)/repository/incidentTeamTagValue && minimock -i IncidentTeamTagValueRepository -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/service/externalTeam && minimock -i ExternalTeamService -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/repository/externalTeamRepo && minimock -i ExternalTeamRepository -s _mock.go -o $(CURDIR)/mocks + cd $(CURDIR)/repository/tagValue && minimock -i TagValueRepository -s _mock.go -o $(CURDIR)/mocks diff --git a/appcontext/app.go b/appcontext/app.go index e0b5a3c..0b04ef8 100644 --- a/appcontext/app.go +++ b/appcontext/app.go @@ -115,6 +115,7 @@ func InitializeServices() { requestStatusService := initRequestStatusService() externalTeamService := initExternalTeamService() incidentJiraService := initIncidentJiraService(externalTeamService) + tagService := initTagService() services = &houstonServices{ logRepo: logRepo, teamRepo: teamRepo, @@ -129,7 +130,7 @@ func InitializeServices() { rcaService: initRCAService(), driveService: initDriveService(), calendarService: initCalendarService(), - tagService: initTagService(), + tagService: tagService, productsService: initProductsService(), incidentProductsService: initIncidentProductsService(), productTeamsService: initProductTeamsService(), @@ -139,7 +140,7 @@ func InitializeServices() { teamUserSeverityService: initTeamUserSeverityService(), incidentStatusService: incidentStatusService, requestStatusService: requestStatusService, - tagValueService: initTagValueService(), + tagValueService: initTagValueService(tagService), externalTeamService: externalTeamService, incidentJiraService: incidentJiraService, } @@ -424,8 +425,8 @@ func GetIncidentUserService() incidentUser.IncidentUserService { return services.incidentUserService } -func initTagValueService() tagValue.TagValueService { - return tagValue.NewTagValueService(tagValueRepo.NewTagValueRepository(GetDB())) +func initTagValueService(tagService tagService.ITagService) tagValue.TagValueService { + return tagValue.NewTagValueService(tagValueRepo.NewTagValueRepository(GetDB()), tagService) } func GetTagValueService() tagValue.TagValueService { diff --git a/cmd/app/handler/tag_value_handler.go b/cmd/app/handler/tag_value_handler.go new file mode 100644 index 0000000..49eaef5 --- /dev/null +++ b/cmd/app/handler/tag_value_handler.go @@ -0,0 +1,88 @@ +package handler + +import ( + "fmt" + "github.com/gin-gonic/gin" + "houston/logger" + "houston/model/customErrors" + tagValueRequest "houston/service/request/tagValue" + commonUtils "houston/service/response/common" + "houston/service/tagValue" + "net/http" +) + +type TagValueHandler struct { + TagValueService tagValue.TagValueService +} + +func NewTagValueHandler(tagValueService tagValue.TagValueService) *TagValueHandler { + return &TagValueHandler{TagValueService: tagValueService} +} + +func (handler *TagValueHandler) HandleAddTagValue(c *gin.Context) { + var request tagValueRequest.AddTagValueRequest + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, err) + return + } + + if err := validateAddTagValueRequest(request); err != nil { + logger.Error(fmt.Sprintf("Received invalid request to add tag value %v", err)) + commonUtils.HandleErrorResponse(c, err) + return + } + + tagValue, err := handler.TagValueService.AddTagValue(request) + if err != nil { + commonUtils.HandleErrorResponse(c, err) + return + } + + c.JSON(http.StatusCreated, commonUtils.SuccessResponse(tagValue, http.StatusCreated)) +} + +func (handler *TagValueHandler) HandleUpdateTagValue(c *gin.Context) { + var request tagValueRequest.UpdateTagValueRequest + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, err) + return + } + + if err := validateUpdateTagValueRequest(request); err != nil { + logger.Error(fmt.Sprintf("Received invalid request to update tag value %v", err)) + commonUtils.HandleErrorResponse(c, err) + return + } + + tagValue, err := handler.TagValueService.UpdateTagValue(request) + if err != nil { + commonUtils.HandleErrorResponse(c, err) + return + } + + c.JSON(http.StatusOK, commonUtils.SuccessResponse(tagValue, http.StatusOK)) +} + +func validateAddTagValueRequest(request tagValueRequest.AddTagValueRequest) error { + if request.TagId == nil { + return customErrors.NewInvalidInputError("Tag id is required") + } + + if request.Value == nil { + return customErrors.NewInvalidInputError("Value is required") + } + + return nil +} + +func validateUpdateTagValueRequest(request tagValueRequest.UpdateTagValueRequest) error { + if request.ID == nil { + return customErrors.NewInvalidInputError("ID is required") + } + + if request.Value == nil && request.TagID == nil && request.Active == nil { + return customErrors.NewInvalidInputError("At least one field is required to update") + } + + return nil +} diff --git a/cmd/app/server.go b/cmd/app/server.go index 1db71a6..4eb9ced 100644 --- a/cmd/app/server.go +++ b/cmd/app/server.go @@ -73,6 +73,7 @@ func (s *Server) Handler(houstonGroup *gin.RouterGroup) { s.filtersHandler(houstonGroup) s.incidentUserHandler(houstonGroup) s.requestStatusHandler(houstonGroup) + s.tagValueHandler(houstonGroup) //this should always be at the end since it opens websocket to connect to slackbot s.houstonHandler() @@ -193,6 +194,13 @@ func (s *Server) reminderHandler(houstonGroup *gin.RouterGroup) { houstonGroup.POST("reminder/user-incidents", reminderHandler.HandleUserIncidents) } +func (s *Server) tagValueHandler(houstonGroup *gin.RouterGroup) { + tagValueHandler := handler.NewTagValueHandler(appcontext.GetTagValueService()) + + houstonGroup.POST("/tag-value", s.authService.IfAdmin(tagValueHandler.HandleAddTagValue)) + houstonGroup.PATCH("/tag-value", s.authService.IfAdmin(tagValueHandler.HandleUpdateTagValue)) +} + func (s *Server) incidentClientHandlerV2(houstonGroup *gin.RouterGroup) { houstonGroup.Use(func(c *gin.Context) { // Add your desired header key-value pair diff --git a/db/migration/000034_add_tag_id_tag_value_unique_constraint.up.sql b/db/migration/000034_add_tag_id_tag_value_unique_constraint.up.sql new file mode 100644 index 0000000..f132109 --- /dev/null +++ b/db/migration/000034_add_tag_id_tag_value_unique_constraint.up.sql @@ -0,0 +1 @@ +ALTER TABLE tag_value ADD CONSTRAINT unique_tag_id_tag_value UNIQUE (tag_id, value); \ No newline at end of file diff --git a/model/tagValue/entity.go b/model/tagValue/entity.go index b5a2b2c..f2a6b84 100644 --- a/model/tagValue/entity.go +++ b/model/tagValue/entity.go @@ -1,18 +1,19 @@ package tagValue import ( + "gorm.io/gorm" "houston/model/tag" "time" ) type TagValueEntity struct { - ID uint `gorm:"primarykey"` - CreatedAt time.Time `gorm:"column:create_at"` - UpdatedAt time.Time `gorm:"column:updated_at"` - DeletedAt time.Time `gorm:"column:deleted_at"` - TagId uint `gorm:"column:tag_id"` - Value string `gorm:"column:value"` - Active bool `gorm:"column:active"` + ID uint `gorm:"primarykey"` + CreatedAt time.Time `gorm:"column:create_at"` + UpdatedAt time.Time `gorm:"column:updated_at"` + DeletedAt gorm.DeletedAt `gorm:"column:deleted_at"` + TagId uint `gorm:"column:tag_id"` + Value string `gorm:"column:value"` + Active bool `gorm:"column:active"` Tag tag.TagEntity `gorm:"foreignKey:TagId"` } diff --git a/repository/tagValue/tag_value_repository_impl.go b/repository/tagValue/tag_value_repository_impl.go index bd8fb41..0936dbf 100644 --- a/repository/tagValue/tag_value_repository_impl.go +++ b/repository/tagValue/tag_value_repository_impl.go @@ -21,3 +21,36 @@ func (repo *tagValueRepositoryImpl) GetTagValuesByTagName(tagName string) ([]tag return tagValues, nil } + +func (repo *tagValueRepositoryImpl) AddTagValue(tagId uint, value string) (*tagValue.TagValueEntity, error) { + tagValue := tagValue.TagValueEntity{ + TagId: tagId, + Value: value, + Active: true, + } + err := repo.gormClient.Create(&tagValue) + if err.Error != nil { + return nil, err.Error + } + + return &tagValue, nil +} + +func (repo *tagValueRepositoryImpl) GetTagValueById(id uint) (*tagValue.TagValueEntity, error) { + var tagValue tagValue.TagValueEntity + result := repo.gormClient.Unscoped().Preload("Tag").First(&tagValue, id) + if result.Error != nil { + return nil, result.Error + } + + return &tagValue, nil +} + +func (repo *tagValueRepositoryImpl) UpdateTagValue(tagValue *tagValue.TagValueEntity) error { + result := repo.gormClient.Preload("Tag").Save(tagValue) + if result.Error != nil { + return result.Error + } + + return nil +} diff --git a/repository/tagValue/tag_value_repository_interface.go b/repository/tagValue/tag_value_repository_interface.go index 7498453..57fa882 100644 --- a/repository/tagValue/tag_value_repository_interface.go +++ b/repository/tagValue/tag_value_repository_interface.go @@ -7,6 +7,9 @@ import ( type TagValueRepository interface { GetTagValuesByTagName(tagName string) ([]tagValue.TagValueEntity, error) + AddTagValue(tagId uint, value string) (*tagValue.TagValueEntity, error) + GetTagValueById(id uint) (*tagValue.TagValueEntity, error) + UpdateTagValue(tagValue *tagValue.TagValueEntity) error } func NewTagValueRepository(db *gorm.DB) TagValueRepository { diff --git a/service/request/tagValue/add_tag_value_request.go b/service/request/tagValue/add_tag_value_request.go new file mode 100644 index 0000000..e445f38 --- /dev/null +++ b/service/request/tagValue/add_tag_value_request.go @@ -0,0 +1,6 @@ +package tagValue + +type AddTagValueRequest struct { + TagId *uint `json:"tag_id"` + Value *string `json:"value"` +} diff --git a/service/request/tagValue/update_tag_value_request.go b/service/request/tagValue/update_tag_value_request.go new file mode 100644 index 0000000..0a089b2 --- /dev/null +++ b/service/request/tagValue/update_tag_value_request.go @@ -0,0 +1,8 @@ +package tagValue + +type UpdateTagValueRequest struct { + ID *uint `json:"id"` + Value *string `json:"value"` + TagID *uint `json:"tag_id"` + Active *bool `json:"active"` +} diff --git a/service/response/common/common_utils.go b/service/response/common/common_utils.go index ac61a10..45c8e14 100644 --- a/service/response/common/common_utils.go +++ b/service/response/common/common_utils.go @@ -53,5 +53,11 @@ func HandleErrorResponse(c *gin.Context, err error) { return } + var invalidInputError *customErrors.InvalidInputError + if errors.As(err, &invalidInputError) { + c.JSON(http.StatusBadRequest, ErrorResponse(err, http.StatusBadRequest, nil)) + return + } + c.JSON(http.StatusInternalServerError, ErrorResponse(err, http.StatusInternalServerError, nil)) } diff --git a/service/response/tagValue/tag_value_response.go b/service/response/tagValue/tag_value_response.go new file mode 100644 index 0000000..275f744 --- /dev/null +++ b/service/response/tagValue/tag_value_response.go @@ -0,0 +1,8 @@ +package tagValue + +type TagValueResponse struct { + ID uint `json:"id"` + TagID uint `json:"tag_id"` + Value string `json:"value"` + Active bool `json:"active"` +} diff --git a/service/tagValue/tag_value_service_impl.go b/service/tagValue/tag_value_service_impl.go index dd28507..19bb62d 100644 --- a/service/tagValue/tag_value_service_impl.go +++ b/service/tagValue/tag_value_service_impl.go @@ -2,14 +2,22 @@ package tagValue import ( "fmt" + "gorm.io/gorm" "houston/common/util/dto" "houston/logger" + "houston/model/customErrors" + tagModel "houston/model/tag" tagValueModel "houston/model/tagValue" "houston/repository/tagValue" + tagValueRequest "houston/service/request/tagValue" + tagValueResponse "houston/service/response/tagValue" + tagService "houston/service/tag" + "time" ) type tagValueServiceImpl struct { tagValueRepository tagValue.TagValueRepository + tagService tagService.ITagService } const logTag = "[tag-value-service]" @@ -25,3 +33,144 @@ func (service *tagValueServiceImpl) GetTagValuesByTagName(tagName string) ([]tag return dto.ToDtoArray[tagValueModel.TagValueEntity, tagValueModel.TagValueDTO](tagValues), nil } + +func (service *tagValueServiceImpl) AddTagValue(request tagValueRequest.AddTagValueRequest) (*tagValueResponse.TagValueResponse, error) { + logger.Info(fmt.Sprintf("%s received request to create tag value: %v", logTag, request)) + + tag, err := service.tagService.FindTagById(*request.TagId) + if err != nil { + logger.Error(fmt.Sprintf("%s Error while fetching tag by id: %d : %v", logTag, *request.TagId, err)) + return nil, err + } else { + err := validateTag(tag) + if err != nil { + logger.Info(fmt.Sprintf("%s Invalid tag: %v : %v", logTag, tag, err)) + return nil, err + } + } + + tagValue, err := service.tagValueRepository.AddTagValue(*request.TagId, *request.Value) + if err != nil { + logger.Info(fmt.Sprintf("%s Error while creating tag value: %v", logTag, err)) + return nil, err + } + + return convertToTagValueResponse(*tagValue), nil +} + +func (service *tagValueServiceImpl) UpdateTagValue(request tagValueRequest.UpdateTagValueRequest) (*tagValueResponse.TagValueResponse, error) { + logger.Info(fmt.Sprintf("%s received request to update tag value: %v", logTag, request)) + + tagValue, err := service.tagValueRepository.GetTagValueById(*request.ID) + if err != nil { + logger.Error(fmt.Sprintf("%s Error while fetching tag value by id: %d : %v", logTag, *request.ID, err)) + return nil, err + } else if !tagValue.Tag.Active { + logger.Info(fmt.Sprintf("%s Tag is inactive: %v", logTag, tagValue.Tag)) + return nil, customErrors.NewInvalidInputError("Current Tag is inactive for given tag value") + } + + if request.TagID != nil { + err := service.updateTagId(tagValue, *request.TagID) + if err != nil { + return nil, err + } + } + + if request.Value != nil { + err := service.updateValue(tagValue, *request.Value) + if err != nil { + return nil, err + } + } + + if request.Active != nil { + err := service.updateActiveStatus(tagValue, *request.Active) + if err != nil { + return nil, err + } + } + + err = service.tagValueRepository.UpdateTagValue(tagValue) + if err != nil { + logger.Error(fmt.Sprintf("%s Error while updating tag value: %v", logTag, err)) + return nil, err + } + + return convertToTagValueResponse(*tagValue), nil +} + +func (service *tagValueServiceImpl) updateTagId(tagValue *tagValueModel.TagValueEntity, tagId uint) error { + if tagValue.TagId == tagId { + return customErrors.NewInvalidInputError("tag id is same as existing tag id") + } else if !tagValue.Active { + return customErrors.NewInvalidInputError("Tag value is inactive. Please activate it first") + } + + tag, err := service.tagService.FindTagById(tagId) + if err != nil { + logger.Error(fmt.Sprintf("%s Error while fetching tag by id: %d : %v", logTag, tagId, err)) + return err + } else { + err := validateTag(tag) + if err != nil { + logger.Info(fmt.Sprintf("%s Invalid tag: %v : %v", logTag, tag, err)) + return err + } + } + + tagValue.TagId = tagId + tagValue.Tag = *tag + tagValue.UpdatedAt = time.Now() + return nil +} + +func (service *tagValueServiceImpl) updateValue(tagValue *tagValueModel.TagValueEntity, value string) error { + if tagValue.Value == value { + return customErrors.NewInvalidInputError("tag value is same as existing value") + } else if !tagValue.Active { + return customErrors.NewInvalidInputError("Tag value is inactive. Please activate it first") + } + + tagValue.Value = value + tagValue.UpdatedAt = time.Now() + + return nil +} + +func (service *tagValueServiceImpl) updateActiveStatus(tagValue *tagValueModel.TagValueEntity, isActive bool) error { + if tagValue.Active == isActive { + return customErrors.NewInvalidInputError("tag value activation status is same as existing status") + } + + tagValue.Active = isActive + tagValue.UpdatedAt = time.Now() + if tagValue.Active { + tagValue.DeletedAt = gorm.DeletedAt{Time: time.Time{}, Valid: false} + } else { + tagValue.DeletedAt = gorm.DeletedAt{Time: time.Now(), Valid: true} + } + + return nil +} + +func validateTag(tag *tagModel.TagEntity) error { + if tag == nil { + return customErrors.NewNotFoundError("Tag not found") + } else if !tag.Active { + return customErrors.NewInvalidInputError("Tag is inactive") + } else if tag.Type == tagModel.FreeText { + return customErrors.NewInvalidInputError("Tag type is free text") + } + + return nil +} + +func convertToTagValueResponse(tagValue tagValueModel.TagValueEntity) *tagValueResponse.TagValueResponse { + return &tagValueResponse.TagValueResponse{ + ID: tagValue.ID, + TagID: tagValue.TagId, + Value: tagValue.Value, + Active: tagValue.Active, + } +} diff --git a/service/tagValue/tag_value_service_interface.go b/service/tagValue/tag_value_service_interface.go index 9462ef4..be07d25 100644 --- a/service/tagValue/tag_value_service_interface.go +++ b/service/tagValue/tag_value_service_interface.go @@ -3,12 +3,17 @@ package tagValue import ( tagValueModel "houston/model/tagValue" "houston/repository/tagValue" + tagValueRequest "houston/service/request/tagValue" + tagValueResponse "houston/service/response/tagValue" + "houston/service/tag" ) type TagValueService interface { GetTagValuesByTagName(tagName string) ([]tagValueModel.TagValueDTO, error) + AddTagValue(request tagValueRequest.AddTagValueRequest) (*tagValueResponse.TagValueResponse, error) + UpdateTagValue(request tagValueRequest.UpdateTagValueRequest) (*tagValueResponse.TagValueResponse, error) } -func NewTagValueService(tagValueRepository tagValue.TagValueRepository) TagValueService { - return &tagValueServiceImpl{tagValueRepository: tagValueRepository} +func NewTagValueService(tagValueRepository tagValue.TagValueRepository, tagService tag.ITagService) TagValueService { + return &tagValueServiceImpl{tagValueRepository: tagValueRepository, tagService: tagService} } diff --git a/service/tagValue/tag_value_service_test.go b/service/tagValue/tag_value_service_test.go new file mode 100644 index 0000000..bbb9c01 --- /dev/null +++ b/service/tagValue/tag_value_service_test.go @@ -0,0 +1,299 @@ +package tagValue + +import ( + "errors" + "github.com/stretchr/testify/suite" + "gorm.io/gorm" + "houston/logger" + "houston/mocks" + "houston/model/tag" + tagValueModel "houston/model/tagValue" + tagValueRequest "houston/service/request/tagValue" + "testing" + "time" +) + +type TagValueServiceSuite struct { + suite.Suite + tagValueRepo mocks.TagValueRepositoryMock + tagService mocks.ITagServiceMock + tagValueService TagValueService +} + +func (suite *TagValueServiceSuite) Test_GetTagValuesByTagName_GetTagValuesByTagNameError() { + suite.tagValueRepo.GetTagValuesByTagNameMock.Return(nil, errors.New("error")) + + response, err := suite.tagValueService.GetTagValuesByTagName("tag") + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_GetTagValuesByTagName_SuccessCase() { + suite.tagValueRepo.GetTagValuesByTagNameMock.Return([]tagValueModel.TagValueEntity{*getMockTagValue()}, nil) + + response, err := suite.tagValueService.GetTagValuesByTagName("tag") + suite.NotNil(response, "response should not be nil") + suite.Nil(err, "error should be nil") +} + +func (suite *TagValueServiceSuite) Test_AddTagValue_FindTagError() { + suite.tagService.FindTagByIdMock.Return(nil, errors.New("error")) + + response, err := suite.tagValueService.AddTagValue(getMockAddTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_AddTagValue_NilTagCase() { + suite.tagService.FindTagByIdMock.Return(nil, nil) + + response, err := suite.tagValueService.AddTagValue(getMockAddTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_AddTagValue_InactiveTagCase() { + suite.tagService.FindTagByIdMock.Return(getMockTag(false, tag.SingleValue), nil) + + response, err := suite.tagValueService.AddTagValue(getMockAddTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_AddTagValue_FreeTextTagTypeCase() { + suite.tagService.FindTagByIdMock.Return(getMockTag(true, tag.FreeText), nil) + + response, err := suite.tagValueService.AddTagValue(getMockAddTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_AddTagValue_AddTagValueError() { + suite.tagService.FindTagByIdMock.Return(getMockTag(true, tag.SingleValue), nil) + suite.tagValueRepo.AddTagValueMock.Return(nil, errors.New("error")) + + response, err := suite.tagValueService.AddTagValue(getMockAddTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_AddTagValue_SuccessCase() { + suite.tagService.FindTagByIdMock.Return(getMockTag(true, tag.SingleValue), nil) + suite.tagValueRepo.AddTagValueMock.Return(getMockTagValue(), nil) + + response, err := suite.tagValueService.AddTagValue(getMockAddTagValueRequest()) + suite.NotNil(response, "response should not be nil") + suite.Nil(err, "error should be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_GetTagValueError() { + suite.tagValueRepo.GetTagValueByIdMock.Return(nil, errors.New("error")) + + response, err := suite.tagValueService.UpdateTagValue(getMockUpdateTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_CurrentInactiveTagCase() { + tagValue := getMockTagValue() + tagValue.Tag.Active = false + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + + response, err := suite.tagValueService.UpdateTagValue(getMockUpdateTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_SameTagIDCase() { + tagValue := getMockTagValue() + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + + tagValueId, tagId := uint(1), uint(2) + response, err := suite.tagValueService.UpdateTagValue(tagValueRequest.UpdateTagValueRequest{ + ID: &tagValueId, TagID: &tagId, + }) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_SameValueCase() { + tagValue := getMockTagValue() + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + + tagValueId, value := uint(1), "test value" + response, err := suite.tagValueService.UpdateTagValue(tagValueRequest.UpdateTagValueRequest{ + ID: &tagValueId, Value: &value, + }) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_SameActivationStateCase() { + tagValue := getMockTagValue() + tagValue.Active = true + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + + tagValueId, active := uint(1), true + response, err := suite.tagValueService.UpdateTagValue(tagValueRequest.UpdateTagValueRequest{ + ID: &tagValueId, Active: &active, + }) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_FindTagError() { + tagValue := getMockTagValue() + tagValue.Active = true + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + suite.tagService.FindTagByIdMock.Return(nil, errors.New("error")) + + response, err := suite.tagValueService.UpdateTagValue(getMockUpdateTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_UpdateValueInactiveTagValueCase() { + tagValue := getMockTagValue() + request := getMockUpdateTagValueRequest() + request.TagID = nil + request.Active = nil + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + suite.tagService.FindTagByIdMock.Return(getMockTag(true, tag.SingleValue), nil) + + response, err := suite.tagValueService.UpdateTagValue(request) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") + +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_NilTagCase() { + tagValue := getMockTagValue() + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + suite.tagService.FindTagByIdMock.Return(nil, nil) + + response, err := suite.tagValueService.UpdateTagValue(getMockUpdateTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_InactiveTagCase() { + tagValue := getMockTagValue() + tagValue.Active = true + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + suite.tagService.FindTagByIdMock.Return(getMockTag(false, tag.SingleValue), nil) + + response, err := suite.tagValueService.UpdateTagValue(getMockUpdateTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_FreeTextTagTypeCase() { + tagValue := getMockTagValue() + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + suite.tagService.FindTagByIdMock.Return(getMockTag(true, tag.FreeText), nil) + + response, err := suite.tagValueService.UpdateTagValue(getMockUpdateTagValueRequest()) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_UpdateTagValueError() { + tagValue := getMockTagValue() + tagValue.Active = true + request := getMockUpdateTagValueRequest() + request.Active = nil + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + suite.tagService.FindTagByIdMock.Return(getMockTag(true, tag.SingleValue), nil) + suite.tagValueRepo.UpdateTagValueMock.Return(errors.New("error")) + + response, err := suite.tagValueService.UpdateTagValue(request) + suite.Nil(response, "response should be nil") + suite.NotNil(err, "error should not be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_SuccessCase() { + tagValue := getMockTagValue() + tagValue.Active = true + mockRequest := getMockUpdateTagValueRequest() + mockRequest.Active = nil + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + suite.tagService.FindTagByIdMock.Return(getMockTag(true, tag.SingleValue), nil) + suite.tagValueRepo.UpdateTagValueMock.Return(nil) + + response, err := suite.tagValueService.UpdateTagValue(mockRequest) + suite.NotNil(response, "response should not be nil") + suite.Nil(err, "error should be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_DeactivationCase() { + tagValue := getMockTagValue() + tagValue.Active = true + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + suite.tagValueRepo.UpdateTagValueMock.Return(nil) + + tagValueId, isActive := uint(1), false + response, err := suite.tagValueService.UpdateTagValue(tagValueRequest.UpdateTagValueRequest{ + ID: &tagValueId, Active: &isActive, + }) + suite.NotNil(response, "response should not be nil") + suite.Nil(err, "error should be nil") +} + +func (suite *TagValueServiceSuite) Test_UpdateTagValue_ReactivationCase() { + tagValue := getMockTagValue() + suite.tagValueRepo.GetTagValueByIdMock.Return(tagValue, nil) + suite.tagValueRepo.UpdateTagValueMock.Return(nil) + + tagValueId, isActive := uint(1), true + response, err := suite.tagValueService.UpdateTagValue(tagValueRequest.UpdateTagValueRequest{ + ID: &tagValueId, Active: &isActive, + }) + suite.NotNil(response, "response should not be nil") + suite.Nil(err, "error should be nil") +} + +func (suite *TagValueServiceSuite) SetupTest() { + logger.InitLogger() + suite.tagValueRepo = *mocks.NewTagValueRepositoryMock(suite.T()) + suite.tagService = *mocks.NewITagServiceMock(suite.T()) + suite.tagValueService = NewTagValueService(&suite.tagValueRepo, &suite.tagService) +} + +func TestTagValueServiceSuite(t *testing.T) { + suite.Run(t, new(TagValueServiceSuite)) +} + +func getMockAddTagValueRequest() tagValueRequest.AddTagValueRequest { + tagId, value := uint(1), "value" + return tagValueRequest.AddTagValueRequest{ + TagId: &tagId, Value: &value, + } +} + +func getMockUpdateTagValueRequest() tagValueRequest.UpdateTagValueRequest { + tagId, value, active := uint(1), "value", true + return tagValueRequest.UpdateTagValueRequest{ + ID: &tagId, TagID: &tagId, Value: &value, Active: &active, + } +} + +func getMockTag(active bool, tagType tag.Type) *tag.TagEntity { + return &tag.TagEntity{ + Name: "tag", Active: active, Type: tagType, + } +} + +func getMockTagValue() *tagValueModel.TagValueEntity { + return &tagValueModel.TagValueEntity{ + ID: 0, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + DeletedAt: gorm.DeletedAt{}, + TagId: 2, + Value: "test value", + Active: false, + Tag: tag.TagEntity{ + Active: true, + }, + } +}