diff --git a/Makefile b/Makefile index 35820a5..4f3983d 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ generatemocks: cd $(CURDIR)/repository/rcaInput && minimock -i IRcaInputRepository -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/service/teamService && minimock -i ITeamServiceV2 -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/model/user && minimock -i IUserRepositoryInterface -s _mock.go -o $(CURDIR)/mocks - cd $(CURDIR)/model/tag && minimock -i ITagRepository -s _mock.go -o $(CURDIR)/mocks + cd $(CURDIR)/repository/tag && minimock -i ITagRepository -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/pkg/monitoringService && minimock -i MonitoringServiceActions -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/service/krakatoa && minimock -i IKrakatoaService -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/pkg/socketModeClient && minimock -i ISocketModeClientWrapper -s _mock.go -o $(CURDIR)/mocks @@ -87,3 +87,7 @@ generatemocks: cd $(CURDIR)/repository/incidentUser && minimock -i IncidentUserRepository -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/repository/requestStatus && minimock -i RequestStatusRepository -s _mock.go -o $(CURDIR)/mocks cd $(CURDIR)/service/requestStatus && minimock -i RequestStatusService -s _mock.go -o $(CURDIR)/mocks + cd $(CURDIR)/service/incidentTeam && minimock -i IncidentTeamService -s _mock.go -o $(CURDIR)/mocks + cd $(CURDIR)/service/incidentTeamTagValue && minimock -i IncidentTeamTagValueService -s _mock.go -o $(CURDIR)/mocks + cd $(CURDIR)/repository/incidentTeam && minimock -i IncidentTeamRepository -s _mock.go -o $(CURDIR)/mocks + cd $(CURDIR)/repository/incidentTeamTagValue && minimock -i IncidentTeamTagValueRepository -s _mock.go -o $(CURDIR)/mocks diff --git a/appcontext/app.go b/appcontext/app.go index 4364320..733ec29 100644 --- a/appcontext/app.go +++ b/appcontext/app.go @@ -9,7 +9,6 @@ import ( "houston/model/log" productModel "houston/model/product" "houston/model/products_teams" - "houston/model/tag" "houston/model/team" "houston/model/user" conferenceActions "houston/pkg/conference" @@ -23,6 +22,8 @@ import ( "houston/repository/rcaInput" requestStatusRepo "houston/repository/requestStatus" "houston/repository/severity" + "houston/repository/tag" + tagValueRepo "houston/repository/tagValue" teamSeverityRepo "houston/repository/teamSeverity" teamUserRepo "houston/repository/teamUser" teamUserSeverityRepo "houston/repository/teamUserSeverity" @@ -42,6 +43,7 @@ import ( severityServiceImpl "houston/service/severity/impl" "houston/service/slack" tagService "houston/service/tag" + "houston/service/tagValue" "houston/service/teamService" "houston/service/teamSeverity" teamSeverityServiceImpl "houston/service/teamSeverity/impl" @@ -85,6 +87,7 @@ type houstonServices struct { incidentStatusService incidentStatus.IncidentStatusService incidentUserService incidentUser.IncidentUserService requestStatusService requestStatus.RequestStatusService + tagValueService tagValue.TagValueService } var appContext *applicationContext @@ -129,6 +132,7 @@ func InitializeServices() { teamUserSeverityService: initTeamUserSeverityService(), incidentStatusService: incidentStatusService, requestStatusService: requestStatusService, + tagValueService: initTagValueService(), } services.userService = initUserService() services.teamService = initTeamService() @@ -410,3 +414,11 @@ func initIncidentUserService( func GetIncidentUserService() incidentUser.IncidentUserService { return services.incidentUserService } + +func initTagValueService() tagValue.TagValueService { + return tagValue.NewTagValueService(tagValueRepo.NewTagValueRepository(GetDB())) +} + +func GetTagValueService() tagValue.TagValueService { + return services.tagValueService +} diff --git a/cmd/app/handler/slack_handler.go b/cmd/app/handler/slack_handler.go index b82801f..6f252e8 100644 --- a/cmd/app/handler/slack_handler.go +++ b/cmd/app/handler/slack_handler.go @@ -9,7 +9,6 @@ import ( "houston/internal/resolver" "houston/model/incident" "houston/model/log" - "houston/model/tag" "houston/model/team" "houston/model/user" "houston/pkg/rest" @@ -17,6 +16,7 @@ import ( rcaRepository "houston/repository/rca/impl" "houston/repository/rcaInput" "houston/repository/severity" + "houston/repository/tag" "houston/service/documentService" incidentServiceV2 "houston/service/incident/impl" "houston/service/orchestration" @@ -72,6 +72,7 @@ func NewSlackHandler( ) productTeamService := appcontext.GetProductTeamsService() slashCommandProcessor := processor.NewSlashCommandProcessor(socketModeClient, slackbotClient, rcaService) + tagValueService := appcontext.GetTagValueService() return &slackHandler{ socketModeClient: socketModeClient, @@ -92,6 +93,7 @@ func NewSlackHandler( productsService, productTeamService, orchestrator, + tagValueService, ), viewSubmissionProcessor: processor.NewViewSubmissionProcessor( socketModeClient, @@ -107,12 +109,13 @@ func NewSlackHandler( rcaService, incidentServiceV2, orchestrator, + tagValueService, ), userChangeEventProcessor: processor.NewUserChangeEventProcessor( socketModeClient, userService, ), houstonCommandResolver: resolver.NewHoustonCommandResolver( - socketModeClient, slackbotClient, rcaService, productsService, orchestrator, + socketModeClient, slackbotClient, rcaService, productsService, orchestrator, tagValueService, ), memberLeftChannelEventProcessor: processor.NewMemberLeftChannelCallbackEventProcessor(socketModeClient, *incidentServiceV2, appcontext.GetUserService()), } diff --git a/common/util/environment_constant.go b/common/util/env/environment_constant.go similarity index 83% rename from common/util/environment_constant.go rename to common/util/env/environment_constant.go index 2d6964f..d8c505a 100644 --- a/common/util/environment_constant.go +++ b/common/util/env/environment_constant.go @@ -1,4 +1,4 @@ -package util +package env const ( DefaultZendutyTimeout = "DEFAULT_ZENDUTY_TIMEOUT" @@ -9,3 +9,8 @@ const ( AlertIntegrationEnabled = "ALERT_INTEGRATION_ENABLED" AlertEnabledSeverity = "ALERT_ENABLED_SEVERITY" ) + +const ( + APP_NAME = "APP_NAME" + QA_APP_NAME = "qa-houston" +) diff --git a/common/util/slack/slack_constants.go b/common/util/slack/slack_constants.go index eab2827..61da995 100644 --- a/common/util/slack/slack_constants.go +++ b/common/util/slack/slack_constants.go @@ -16,3 +16,8 @@ const ( const ( RCAGenerationErrorMessage = "Some issue occurred while generating RCA." ) + +const ( + IncidentIssueTypeModalRequest = "incident_issue_type_modal_request" + IncidentIssueTypeModalRequestInput = "incident_issue_type_modal_request_input" +) diff --git a/common/util/slack/slack_helpers.go b/common/util/slack/slack_helpers.go index 354d9f6..fd9472b 100644 --- a/common/util/slack/slack_helpers.go +++ b/common/util/slack/slack_helpers.go @@ -209,3 +209,18 @@ func BuildSlackTextMessageFromMetaData(metadata []byte, isCodeBlock bool) (slack return slack.MsgOptionText(textMessage, false), nil } + +func GetIssueTypesFromInput(blockAction map[string]slack.BlockAction) []uint { + var issueTypes []uint + + for _, selectedOption := range blockAction[IncidentIssueTypeModalRequest].SelectedOptions { + issueTypeId, err := strconv.ParseUint(selectedOption.Value, 10, 64) + if err == nil { + issueTypes = append(issueTypes, uint(issueTypeId)) + } else { + logger.Error("Error in converting issue type id to uint", zap.Error(err)) + } + } + + return issueTypes +} diff --git a/common/util/tag/tag_constants.go b/common/util/tag/tag_constants.go new file mode 100644 index 0000000..075b892 --- /dev/null +++ b/common/util/tag/tag_constants.go @@ -0,0 +1,3 @@ +package tag + +const ISSUE_TYPES_TAG_NAME = "issue_types" diff --git a/db/migration/000031_add_incident_team_and_incident_team_tag_value_tables.up.sql b/db/migration/000031_add_incident_team_and_incident_team_tag_value_tables.up.sql new file mode 100644 index 0000000..8228845 --- /dev/null +++ b/db/migration/000031_add_incident_team_and_incident_team_tag_value_tables.up.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS incident_team ( + id SERIAL PRIMARY KEY, + incident_id integer NOT NULL REFERENCES incident(id), + team_id integer NOT NULL REFERENCES team(id), + created_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT incident_team_incident_id_team_id_key UNIQUE (incident_id, team_id) +); + +CREATE TABLE IF NOT EXISTS incident_team_tag_value ( + id SERIAL PRIMARY KEY, + incident_team_id integer NOT NULL REFERENCES incident_team(id), + tag_value_id integer NOT NULL REFERENCES tag_value(id), + created_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT incident_team_incident_team_id_tag_value_id_key UNIQUE (incident_team_id, tag_value_id) +); \ No newline at end of file diff --git a/internal/processor/action/incident_mark_duplicate_action.go b/internal/processor/action/incident_mark_duplicate_action.go index 03b11ca..8bd1dca 100644 --- a/internal/processor/action/incident_mark_duplicate_action.go +++ b/internal/processor/action/incident_mark_duplicate_action.go @@ -11,9 +11,9 @@ import ( "houston/logger" "houston/model/customErrors" "houston/model/incident" - "houston/model/tag" "houston/model/team" "houston/repository/severity" + "houston/repository/tag" "houston/service/conference" incidentService "houston/service/incident" "strconv" diff --git a/internal/processor/action/incident_rca_details_action.go b/internal/processor/action/incident_rca_details_action.go index 1604537..da21ec6 100644 --- a/internal/processor/action/incident_rca_details_action.go +++ b/internal/processor/action/incident_rca_details_action.go @@ -11,9 +11,9 @@ import ( "houston/internal/processor/action/view" "houston/logger" "houston/model/incident" - "houston/model/tag" "houston/model/team" "houston/repository/severity" + "houston/repository/tag" incidentService "houston/service/incident" incidentServiceImpl "houston/service/incident/impl" rcaService "houston/service/rca/impl" diff --git a/internal/processor/action/incident_tags_action.go b/internal/processor/action/incident_tags_action.go index e7520fe..770531b 100644 --- a/internal/processor/action/incident_tags_action.go +++ b/internal/processor/action/incident_tags_action.go @@ -9,16 +9,18 @@ import ( "houston/logger" "houston/model/incident" "houston/model/tag" + "houston/model/tagValue" + tagRepo "houston/repository/tag" "reflect" "strconv" ) type IncidentTagsAction struct { incidentRepository incident.IIncidentRepository - tagRepository tag.ITagRepository + tagRepository tagRepo.ITagRepository } -func NewIncidentTagsAction(incidentService incident.IIncidentRepository, tagService tag.ITagRepository) *IncidentTagsAction { +func NewIncidentTagsAction(incidentService incident.IIncidentRepository, tagService tagRepo.ITagRepository) *IncidentTagsAction { return &IncidentTagsAction{ incidentRepository: incidentService, tagRepository: tagService, @@ -113,7 +115,7 @@ func (action *IncidentTagsAction) getTagsBlock(incidentId uint) []slack.InputBlo } block = view.CreateInputBlockForTag(tagDTO, nil, initialValue, tagDTO.Optional) } else { - var initialTags []tag.TagValueEntity + var initialTags []tagValue.TagValueEntity if tagValues == nil { logger.Error(fmt.Sprintf("no tag values are present for tag: %v", tagDTO.Id)) diff --git a/internal/processor/action/incident_update_product_action.go b/internal/processor/action/incident_update_product_action.go index 5e1197e..78c9d71 100644 --- a/internal/processor/action/incident_update_product_action.go +++ b/internal/processor/action/incident_update_product_action.go @@ -4,8 +4,11 @@ import ( "fmt" "github.com/slack-go/slack" "github.com/slack-go/slack/socketmode" + "github.com/spf13/viper" "go.uber.org/zap" "houston/common/util" + "houston/common/util/env" + slackUtil "houston/common/util/slack" stringUtil "houston/common/util/string" "houston/internal" "houston/internal/processor/action/view" @@ -18,6 +21,7 @@ import ( "houston/service/products" "houston/service/productsTeams" service "houston/service/request" + "houston/service/tagValue" "strconv" "time" ) @@ -31,6 +35,7 @@ type IncidentUpdateProductAction struct { slackbotClient *slackbot.Client incidentServiceV2 *incidentV2.IncidentServiceV2 incidentOrchestrator orchestration.IncidentOrchestrator + tagValueService tagValue.TagValueService } func NewIncidentUpdateProductAction( @@ -42,6 +47,7 @@ func NewIncidentUpdateProductAction( slackbotClient *slackbot.Client, incidentServiceV2 *incidentV2.IncidentServiceV2, incidentOrchestrator orchestration.IncidentOrchestrator, + tagValueService tagValue.TagValueService, ) *IncidentUpdateProductAction { return &IncidentUpdateProductAction{ socketModeClient: client, @@ -52,6 +58,7 @@ func NewIncidentUpdateProductAction( slackbotClient: slackbotClient, incidentServiceV2: incidentServiceV2, incidentOrchestrator: incidentOrchestrator, + tagValueService: tagValueService, } } @@ -91,7 +98,7 @@ func (action *IncidentUpdateProductAction) IncidentUpdateProductSelectionRequest responderTeams := action.incidentOrchestrator.GetResponderTeamsForUpdate(channelID, productIDs) - modalRequest := view.BuildIncidentUpdateResponderTeamModal(channelID, responderTeams) + modalRequest := view.BuildIncidentUpdateResponderTeamModal(channelID, responderTeams, action.tagValueService) slackPrivateMetadata := internal.NewSlackPrivateMetadata(). SetChannel(callback.View.PrivateMetadata). SetTriggerId(callback.TriggerID). @@ -139,14 +146,19 @@ func (action *IncidentUpdateProductAction) IncidentUpdateProductRequestProcess( action.socketModeClient.Ack(*request, payload) productIDs := slackPrivateMetadata.GetProductIds() responderTeamID := action.getResponderTeamID(callback.View.State.Values) + updateIncidentRequest := service.UpdateIncidentRequest{ + Id: incidentEntity.ID, + ProductIDs: productIDs, + TeamId: fmt.Sprintf("%d", responderTeamID), + MetaData: nil, + } + + if viper.GetString(env.APP_NAME) == env.QA_APP_NAME { + updateIncidentRequest.IssueTypes = slackUtil.GetIssueTypesFromInput(callback.View.State.Values[slackUtil.IncidentIssueTypeModalRequestInput]) + } if err := action.incidentServiceV2.UpdateProductID( - service.UpdateIncidentRequest{ - Id: incidentEntity.ID, - ProductIDs: productIDs, - TeamId: fmt.Sprintf("%d", responderTeamID), - MetaData: nil, - }, + updateIncidentRequest, user.ID, incidentEntity, severityEntity, diff --git a/internal/processor/action/incident_update_status_action.go b/internal/processor/action/incident_update_status_action.go index b742aea..01c26a4 100644 --- a/internal/processor/action/incident_update_status_action.go +++ b/internal/processor/action/incident_update_status_action.go @@ -10,9 +10,9 @@ import ( "houston/internal/processor/action/view" "houston/logger" "houston/model/incident" - "houston/model/tag" "houston/model/team" "houston/repository/severity" + "houston/repository/tag" incidentV2 "houston/service/incident/impl" "houston/service/incidentStatus" service "houston/service/request" diff --git a/internal/processor/action/incident_update_type_action.go b/internal/processor/action/incident_update_type_action.go index 6cb8495..9ca9885 100644 --- a/internal/processor/action/incident_update_type_action.go +++ b/internal/processor/action/incident_update_type_action.go @@ -4,8 +4,11 @@ import ( "fmt" "github.com/slack-go/slack" "github.com/slack-go/slack/socketmode" + "github.com/spf13/viper" "go.uber.org/zap" "houston/common/metrics" + "houston/common/util/env" + slackUtil "houston/common/util/slack" "houston/internal/processor/action/view" "houston/logger" "houston/model/incident" @@ -15,6 +18,7 @@ import ( incidentV2 "houston/service/incident/impl" "houston/service/orchestration" service "houston/service/request" + "houston/service/tagValue" "strconv" ) @@ -26,6 +30,7 @@ type IncidentUpdateTypeAction struct { slackbotClient *slackbot.Client incidentServiceV2 *incidentV2.IncidentServiceV2 incidentOrchestrator orchestration.IncidentOrchestrator + tagValueService tagValue.TagValueService } func NewIncidentUpdateTypeAction( @@ -36,6 +41,7 @@ func NewIncidentUpdateTypeAction( slackbotClient *slackbot.Client, incidentServiceV2 *incidentV2.IncidentServiceV2, incidentOrchestrator orchestration.IncidentOrchestrator, + tagValueService tagValue.TagValueService, ) *IncidentUpdateTypeAction { return &IncidentUpdateTypeAction{ socketModeClient: client, @@ -45,12 +51,13 @@ func NewIncidentUpdateTypeAction( slackbotClient: slackbotClient, incidentServiceV2: incidentServiceV2, incidentOrchestrator: incidentOrchestrator, + tagValueService: tagValueService, } } func (incidentUpdateTypeAction *IncidentUpdateTypeAction) IncidentUpdateTypeRequestProcess(callback slack.InteractionCallback, request *socketmode.Request) { responderTeams := incidentUpdateTypeAction.incidentOrchestrator.GetResponderTeamsForUpdate(callback.Channel.ID, nil) - modalRequest := view.BuildIncidentUpdateResponderTeamModal(callback.Channel.ID, responderTeams) + modalRequest := view.BuildIncidentUpdateResponderTeamModal(callback.Channel.ID, responderTeams, incidentUpdateTypeAction.tagValueService) _, err := incidentUpdateTypeAction.socketModeClient.OpenView(callback.TriggerID, modalRequest) if err != nil { @@ -88,13 +95,18 @@ func (incidentUpdateTypeAction *IncidentUpdateTypeAction) IncidentUpdateType( incidentUpdateTypeAction.socketModeClient.Ack(*request, payload) teamID := incidentUpdateTypeAction.buildUpdateIncidentResponderTeamRequest(callback.View.State.Values) + updateIncidentRequest := service.UpdateIncidentRequest{ + Id: incidentEntity.ID, + TeamId: fmt.Sprintf("%d", teamID), + MetaData: nil, + } + + if viper.GetString(env.APP_NAME) == env.QA_APP_NAME { + updateIncidentRequest.IssueTypes = slackUtil.GetIssueTypesFromInput(callback.View.State.Values[slackUtil.IncidentIssueTypeModalRequestInput]) + } if err := incidentUpdateTypeAction.incidentServiceV2.UpdateTeamId( - service.UpdateIncidentRequest{ - Id: incidentEntity.ID, - TeamId: fmt.Sprintf("%d", teamID), - MetaData: nil, - }, + updateIncidentRequest, user.ID, incidentEntity, incidentStatusEntity, diff --git a/internal/processor/action/open_set_team_view_modal_command_action.go b/internal/processor/action/open_set_team_view_modal_command_action.go index 2459435..9b8d688 100644 --- a/internal/processor/action/open_set_team_view_modal_command_action.go +++ b/internal/processor/action/open_set_team_view_modal_command_action.go @@ -10,6 +10,7 @@ import ( "houston/logger" "houston/pkg/slackbot" "houston/service/orchestration" + "houston/service/tagValue" ) const openSetTeamViewModalActionLogTag = "[open_set_team_view_modal_command_action]" @@ -18,17 +19,20 @@ type OpenSetTeamViewModalCommandAction struct { socketModeClient *socketmode.Client slackBot *slackbot.Client incidentOrchestrator orchestration.IncidentOrchestrator + tagValueService tagValue.TagValueService } func NewOpenSetTeamViewModalCommandAction( socketModeClient *socketmode.Client, slackBot *slackbot.Client, incidentOrchestrator orchestration.IncidentOrchestrator, + tagValueService tagValue.TagValueService, ) *OpenSetTeamViewModalCommandAction { return &OpenSetTeamViewModalCommandAction{ socketModeClient: socketModeClient, slackBot: slackBot, incidentOrchestrator: incidentOrchestrator, + tagValueService: tagValueService, } } @@ -56,7 +60,7 @@ func (action *OpenSetTeamViewModalCommandAction) openSetTeamViewModal(cmd slack. logger.Info("opening set team view modal") return executeForHoustonChannel(cmd, func() error { responderTeams := action.incidentOrchestrator.GetResponderTeamsForUpdate(cmd.ChannelID, nil) - modalRequest := view.BuildIncidentUpdateResponderTeamModal(cmd.ChannelID, responderTeams) + modalRequest := view.BuildIncidentUpdateResponderTeamModal(cmd.ChannelID, responderTeams, action.tagValueService) _, err := action.socketModeClient.OpenView(cmd.TriggerID, modalRequest) if err != nil { diff --git a/internal/processor/action/view/incident_rca_details_section.go b/internal/processor/action/view/incident_rca_details_section.go index fa98388..a46563b 100644 --- a/internal/processor/action/view/incident_rca_details_section.go +++ b/internal/processor/action/view/incident_rca_details_section.go @@ -7,6 +7,7 @@ import ( slackUtil "houston/common/util/slack" "houston/model/incident" "houston/model/tag" + "houston/model/tagValue" "strconv" "strings" ) @@ -84,7 +85,7 @@ func buildJiraLinksBlock(entity *incident.IncidentEntity) *slack.SectionBlock { return block } -func CreateInputBlockForTag(tagEntity tag.TagDTO, tagValues []tag.TagValueEntity, initialTagValues interface{}, isOptional bool) *slack.InputBlock { +func CreateInputBlockForTag(tagEntity tag.TagDTO, tagValues []tagValue.TagValueEntity, initialTagValues interface{}, isOptional bool) *slack.InputBlock { text := slack.NewTextBlockObject(slack.PlainTextType, tagEntity.Label, false, false) var element slack.BlockElement switch tagEntity.Type { @@ -94,11 +95,11 @@ func CreateInputBlockForTag(tagEntity tag.TagDTO, tagValues []tag.TagValueEntity } case tag.SingleValue: { - element = createOptionsSelectBlockElementForTag(tagEntity, tagValues, initialTagValues.([]tag.TagValueEntity)) + element = createOptionsSelectBlockElementForTag(tagEntity, tagValues, initialTagValues.([]tagValue.TagValueEntity)) } case tag.MultiValue: { - element = createMultiOptionsSelectBlockElementForTag(tagEntity, tagValues, initialTagValues.([]tag.TagValueEntity)) + element = createMultiOptionsSelectBlockElementForTag(tagEntity, tagValues, initialTagValues.([]tagValue.TagValueEntity)) } default: { @@ -125,7 +126,7 @@ func createPlainTextInputBlockElementForTag(tagEntity tag.TagDTO, value interfac return element } -func createOptionsSelectBlockElementForTag(tagEntity tag.TagDTO, tagValues []tag.TagValueEntity, initialTagValues []tag.TagValueEntity) *slack.SelectBlockElement { +func createOptionsSelectBlockElementForTag(tagEntity tag.TagDTO, tagValues []tagValue.TagValueEntity, initialTagValues []tagValue.TagValueEntity) *slack.SelectBlockElement { blockOptions := createTagOptions(tagValues) placeholder := slack.NewTextBlockObject(slack.PlainTextType, tagEntity.PlaceHolder, false, false) element := slack.NewOptionsSelectBlockElement(slack.OptTypeStatic, placeholder, tagEntity.ActionId, blockOptions...) @@ -135,7 +136,7 @@ func createOptionsSelectBlockElementForTag(tagEntity tag.TagDTO, tagValues []tag return element } -func createMultiOptionsSelectBlockElementForTag(tagEntity tag.TagDTO, tagValues []tag.TagValueEntity, initialTagValues []tag.TagValueEntity) *slack.MultiSelectBlockElement { +func createMultiOptionsSelectBlockElementForTag(tagEntity tag.TagDTO, tagValues []tagValue.TagValueEntity, initialTagValues []tagValue.TagValueEntity) *slack.MultiSelectBlockElement { blockOptions := createTagOptions(tagValues) placeholder := slack.NewTextBlockObject(slack.PlainTextType, tagEntity.PlaceHolder, false, false) element := slack.NewOptionsMultiSelectBlockElement(slack.MultiOptTypeStatic, placeholder, tagEntity.ActionId, blockOptions...) @@ -145,7 +146,7 @@ func createMultiOptionsSelectBlockElementForTag(tagEntity tag.TagDTO, tagValues return element } -func createTagOptions(tagValues []tag.TagValueEntity) []*slack.OptionBlockObject { +func createTagOptions(tagValues []tagValue.TagValueEntity) []*slack.OptionBlockObject { optionBlockObjects := make([]*slack.OptionBlockObject, 0, len(tagValues)) for _, o := range tagValues { txt := fmt.Sprintf("%s", o.Value) diff --git a/internal/processor/action/view/incident_update_responder_team.go b/internal/processor/action/view/incident_update_responder_team.go index fbba9af..97a8691 100644 --- a/internal/processor/action/view/incident_update_responder_team.go +++ b/internal/processor/action/view/incident_update_responder_team.go @@ -2,15 +2,29 @@ package view import ( "fmt" + "github.com/spf13/viper" "houston/common/util" + "houston/common/util/env" + slackUtil "houston/common/util/slack" + "houston/common/util/tag" + "houston/logger" + "houston/model/tagValue" "houston/model/team" service "houston/service/response" + tagValueService "houston/service/tagValue" "strconv" "github.com/slack-go/slack" ) -func BuildIncidentUpdateResponderTeamModal(channelID string, responderTeams *service.ProductAndUserTeams) slack.ModalViewRequest { +func BuildIncidentUpdateResponderTeamModal( + channelID string, responderTeams *service.ProductAndUserTeams, tagValueService tagValueService.TagValueService, +) slack.ModalViewRequest { + issueTypes, err := tagValueService.GetTagValuesByTagName(tag.ISSUE_TYPES_TAG_NAME) + if err != nil { + logger.Error(fmt.Sprintf("error while getting issue types: %v", err)) + } + titleText := slack.NewTextBlockObject(slack.PlainTextType, "Set type of incident", false, false) closeText := slack.NewTextBlockObject(slack.PlainTextType, "Close", false, false) submitText := slack.NewTextBlockObject(slack.PlainTextType, "Submit", false, false) @@ -32,6 +46,10 @@ func BuildIncidentUpdateResponderTeamModal(channelID string, responderTeams *ser }, } + if viper.GetString(env.APP_NAME) == env.QA_APP_NAME && issueTypes != nil && len(issueTypes) > 0 { + blocks.BlockSet = append(blocks.BlockSet, createIssueTypeBlock(issueTypes)) + } + return slack.ModalViewRequest{ Type: slack.VTModal, Title: titleText, @@ -53,3 +71,19 @@ func createIncidentTypeBlock(options []team.TeamDTO) []*slack.OptionBlockObject } return optionBlockObjects } + +func createIssueTypeBlock(issueTypes []tagValue.TagValueDTO) slack.Block { + issueTypeText := slack.NewTextBlockObject(slack.PlainTextType, issueTypes[0].Tag.Label, false, false) + optionBlockObjects := make([]*slack.OptionBlockObject, 0, len(issueTypes)) + for _, issue := range issueTypes { + optionText := slack.NewTextBlockObject(slack.PlainTextType, issue.Value, false, false) + optionBlockObjects = append( + optionBlockObjects, + slack.NewOptionBlockObject(strconv.FormatUint(uint64(issue.ID), 10), optionText, nil), + ) + } + + issueTypeOption := slack.NewOptionsSelectBlockElement(slack.MultiOptTypeStatic, issueTypeText, slackUtil.IncidentIssueTypeModalRequest, optionBlockObjects...) + hintText := slack.NewTextBlockObject(slack.PlainTextType, "Select issue type(s) that the current team solved.", false, false) + return slack.NewInputBlock(slackUtil.IncidentIssueTypeModalRequestInput, issueTypeText, hintText, issueTypeOption) +} diff --git a/internal/processor/event_type_interactive_processor.go b/internal/processor/event_type_interactive_processor.go index d1f2515..a0549d3 100644 --- a/internal/processor/event_type_interactive_processor.go +++ b/internal/processor/event_type_interactive_processor.go @@ -8,16 +8,17 @@ import ( "houston/internal/processor/action" "houston/logger" "houston/model/incident" - "houston/model/tag" "houston/model/team" "houston/pkg/slackbot" "houston/repository/severity" + "houston/repository/tag" incidentService "houston/service/incident/impl" "houston/service/orchestration" "houston/service/products" "houston/service/productsTeams" rcaService "houston/service/rca/impl" slack2 "houston/service/slack" + "houston/service/tagValue" "github.com/slack-go/slack" "github.com/slack-go/slack/socketmode" @@ -61,6 +62,7 @@ func NewBlockActionProcessor( productsService products.ProductService, productTeamService productsTeams.ProductTeamsService, orchestrator orchestration.IncidentOrchestrator, + tagValueService tagValue.TagValueService, ) *BlockActionProcessor { return &BlockActionProcessor{ @@ -75,7 +77,7 @@ func NewBlockActionProcessor( incidentUpdateAction: action.NewIncidentUpdateAction(socketModeClient, incidentRepository, tagService, teamService, severityService, incidentServiceV2), incidentUpdateTypeAction: action.NewIncidentUpdateTypeAction(socketModeClient, incidentRepository, - teamService, severityService, slackbotClient, incidentServiceV2, orchestrator), + teamService, severityService, slackbotClient, incidentServiceV2, orchestrator, tagValueService), incidentUpdateProductAction: action.NewIncidentUpdateProductAction( socketModeClient, incidentRepository, @@ -85,6 +87,7 @@ func NewBlockActionProcessor( slackbotClient, incidentServiceV2, orchestrator, + tagValueService, ), incidentUpdateSeverityAction: action.NewIncidentUpdateSeverityAction(socketModeClient, incidentRepository, severityService, teamService, slackbotClient, incidentServiceV2), @@ -250,6 +253,7 @@ func NewViewSubmissionProcessor( rcaService *rcaService.RcaService, incidentServiceV2 *incidentService.IncidentServiceV2, orchestrator orchestration.IncidentOrchestrator, + tagValueService tagValue.TagValueService, ) *ViewSubmissionProcessor { slackService := slack2.NewSlackService() return &ViewSubmissionProcessor{ @@ -276,9 +280,10 @@ func NewViewSubmissionProcessor( slackbotClient, incidentServiceV2, orchestrator, + tagValueService, ), incidentUpdateTypeAction: action.NewIncidentUpdateTypeAction(socketModeClient, incidentRepository, - teamService, severityService, slackbotClient, incidentServiceV2, orchestrator), + teamService, severityService, slackbotClient, incidentServiceV2, orchestrator, tagValueService), showIncidentSubmitAction: action.NewShowIncidentsSubmitAction(socketModeClient, incidentRepository, teamRepository), incidentDuplicateAction: action.NewDuplicateIncidentProcessor(socketModeClient, incidentRepository, diff --git a/internal/processor/open_set_team_view_modal_command_processor.go b/internal/processor/open_set_team_view_modal_command_processor.go index 5ceda87..ddc790b 100644 --- a/internal/processor/open_set_team_view_modal_command_processor.go +++ b/internal/processor/open_set_team_view_modal_command_processor.go @@ -7,6 +7,7 @@ import ( "houston/logger" "houston/pkg/slackbot" "houston/service/orchestration" + "houston/service/tagValue" ) type OpenSetTeamViewModalCommandProcessor struct { @@ -20,10 +21,11 @@ func NewOpenSetTeamViewModalCommandProcessor( socketModeClient *socketmode.Client, slackBot *slackbot.Client, incidentOrchestrator orchestration.IncidentOrchestrator, + tagValueService tagValue.TagValueService, ) *OpenSetTeamViewModalCommandProcessor { return &OpenSetTeamViewModalCommandProcessor{ socketModeClient: socketModeClient, - openSetTeamViewModalCommandAction: action.NewOpenSetTeamViewModalCommandAction(socketModeClient, slackBot, incidentOrchestrator), + openSetTeamViewModalCommandAction: action.NewOpenSetTeamViewModalCommandAction(socketModeClient, slackBot, incidentOrchestrator, tagValueService), } } diff --git a/internal/resolver/houston_command_resolver.go b/internal/resolver/houston_command_resolver.go index 4220328..22c4db4 100644 --- a/internal/resolver/houston_command_resolver.go +++ b/internal/resolver/houston_command_resolver.go @@ -12,6 +12,7 @@ import ( "houston/service/orchestration" "houston/service/products" rcaService "houston/service/rca/impl" + "houston/service/tagValue" "strings" ) @@ -21,6 +22,7 @@ type HoustonCommandResolver struct { rcaService *rcaService.RcaService productsService products.ProductService incidentOrchestrator orchestration.IncidentOrchestrator + tagValueService tagValue.TagValueService } func NewHoustonCommandResolver( @@ -29,6 +31,7 @@ func NewHoustonCommandResolver( rcaService *rcaService.RcaService, productsService products.ProductService, incidentOrchestrator orchestration.IncidentOrchestrator, + tagValueService tagValue.TagValueService, ) *HoustonCommandResolver { return &HoustonCommandResolver{ socketModeClient: socketModeClient, @@ -36,6 +39,7 @@ func NewHoustonCommandResolver( rcaService: rcaService, productsService: productsService, incidentOrchestrator: incidentOrchestrator, + tagValueService: tagValueService, } } @@ -79,6 +83,7 @@ func (resolver *HoustonCommandResolver) Resolve(evt *socketmode.Event) processor resolver.socketModeClient, resolver.slackBotClient, resolver.incidentOrchestrator, + resolver.tagValueService, ) case strings.HasPrefix(params, internal.SetTeamParam): diff --git a/model/incidentTeam/entity.go b/model/incidentTeam/entity.go new file mode 100644 index 0000000..86e1c38 --- /dev/null +++ b/model/incidentTeam/entity.go @@ -0,0 +1,33 @@ +package incidentTeam + +import ( + "houston/model/incident" + "houston/model/team" + "time" +) + +type IncidentTeamEntity struct { + ID uint `gorm:"primaryKey"` + IncidentID uint `gorm:"column:incident_id;not null;uniqueIndex:incident_team_incident_id_team_id_key"` + TeamID uint `gorm:"column:team_id;not null;uniqueIndex:incident_team_incident_id_team_id_key"` + CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"` + + // Add foreign key constraints + Incident incident.IncidentEntity `gorm:"foreignKey:IncidentID"` + Team team.TeamEntity `gorm:"foreignKey:TeamID"` +} + +func (IncidentTeamEntity) TableName() string { + return "incident_team" +} + +func (entity IncidentTeamEntity) ToDTO() IncidentTeamDTO { + return IncidentTeamDTO{ + ID: entity.ID, + IncidentID: entity.IncidentID, + TeamID: entity.TeamID, + CreatedAt: entity.CreatedAt, + Incident: entity.Incident.ToDTO(), + Team: *entity.Team.ToDTO(), + } +} diff --git a/model/incidentTeam/model.go b/model/incidentTeam/model.go new file mode 100644 index 0000000..0673e56 --- /dev/null +++ b/model/incidentTeam/model.go @@ -0,0 +1,17 @@ +package incidentTeam + +import ( + incidentModel "houston/model/incident" + "houston/model/team" + "time" +) + +type IncidentTeamDTO struct { + ID uint `json:"id"` + IncidentID uint `json:"incident_id"` + TeamID uint `json:"team_id"` + CreatedAt time.Time `json:"created_at"` + + Incident incidentModel.IncidentDTO `json:"incident"` + Team team.TeamDTO `json:"team"` +} diff --git a/model/incidentTeamTagValue/entity.go b/model/incidentTeamTagValue/entity.go new file mode 100644 index 0000000..688a7d7 --- /dev/null +++ b/model/incidentTeamTagValue/entity.go @@ -0,0 +1,32 @@ +package incidentTeamTagValue + +import ( + "houston/model/incidentTeam" + "houston/model/tagValue" + "time" +) + +type IncidentTeamTagValueEntity struct { + ID uint `json:"id"` + IncidentTeamID uint `gorm:"column:incident_team_id;not null;uniqueIndex:incident_team_incident_team_id_tag_value_id_key"` + TagValueID uint `gorm:"column:tag_value_id;not null;uniqueIndex:incident_team_incident_team_id_tag_value_id_key"` + CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"` + + IncidentTeam incidentTeam.IncidentTeamEntity `gorm:"foreignKey:IncidentTeamID"` + TagValue tagValue.TagValueEntity `gorm:"foreignKey:TagValueID"` +} + +func (IncidentTeamTagValueEntity) TableName() string { + return "incident_team_tag_value" +} + +func (entity IncidentTeamTagValueEntity) ToDTO() IncidentTeamTagValueDTO { + return IncidentTeamTagValueDTO{ + ID: entity.ID, + IncidentTeamID: entity.IncidentTeamID, + TagValueID: entity.TagValueID, + CreatedAt: entity.CreatedAt, + IncidentTeam: entity.IncidentTeam.ToDTO(), + TagValue: entity.TagValue.ToDTO(), + } +} diff --git a/model/incidentTeamTagValue/model.go b/model/incidentTeamTagValue/model.go new file mode 100644 index 0000000..cfda912 --- /dev/null +++ b/model/incidentTeamTagValue/model.go @@ -0,0 +1,17 @@ +package incidentTeamTagValue + +import ( + "houston/model/incidentTeam" + "houston/model/tagValue" + "time" +) + +type IncidentTeamTagValueDTO struct { + ID uint `json:"id"` + IncidentTeamID uint `json:"incident_team_id"` + TagValueID uint `json:"tag_value_id"` + CreatedAt time.Time `json:"created_at"` + + IncidentTeam incidentTeam.IncidentTeamDTO `json:"incident_team"` + TagValue tagValue.TagValueDTO `json:"tag_value"` +} diff --git a/model/tag/entity.go b/model/tag/entity.go index 1a80d1b..8faa796 100644 --- a/model/tag/entity.go +++ b/model/tag/entity.go @@ -1,6 +1,8 @@ package tag -import "gorm.io/gorm" +import ( + "gorm.io/gorm" +) type Type string @@ -26,13 +28,14 @@ func (TagEntity) TableName() string { return "tag" } -type TagValueEntity struct { - gorm.Model - TagId uint `gorm:"column:tag_id"` - Value string `gorm:"column:value"` - Active bool `gorm:"column:active"` -} - -func (TagValueEntity) TableName() string { - return "tag_value" +func (entity TagEntity) ToDTO() TagDTO { + return TagDTO{ + Id: entity.ID, + Name: entity.Name, + Type: entity.Type, + Label: entity.Label, + PlaceHolder: entity.PlaceHolder, + ActionId: entity.ActionId, + Optional: entity.Optional, + } } diff --git a/model/tagValue/entity.go b/model/tagValue/entity.go new file mode 100644 index 0000000..b5a2b2c --- /dev/null +++ b/model/tagValue/entity.go @@ -0,0 +1,32 @@ +package tagValue + +import ( + "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"` + + Tag tag.TagEntity `gorm:"foreignKey:TagId"` +} + +func (TagValueEntity) TableName() string { + return "tag_value" +} + +func (entity TagValueEntity) ToDTO() TagValueDTO { + return TagValueDTO{ + ID: entity.ID, + TagID: entity.TagId, + Value: entity.Value, + Active: entity.Active, + Tag: entity.Tag.ToDTO(), + } +} diff --git a/model/tagValue/model.go b/model/tagValue/model.go new file mode 100644 index 0000000..a3cec0c --- /dev/null +++ b/model/tagValue/model.go @@ -0,0 +1,11 @@ +package tagValue + +import "houston/model/tag" + +type TagValueDTO struct { + ID uint `json:"id"` + TagID uint `json:"tag_id"` + Value string `json:"value"` + Active bool `json:"active"` + Tag tag.TagDTO `json:"tag"` +} diff --git a/pkg/alertClient/zenduty_client_impl.go b/pkg/alertClient/zenduty_client_impl.go index ed9f4c7..a9e7ab4 100644 --- a/pkg/alertClient/zenduty_client_impl.go +++ b/pkg/alertClient/zenduty_client_impl.go @@ -7,7 +7,7 @@ import ( "fmt" "github.com/spf13/viper" "go.uber.org/zap" - "houston/common/util" + "houston/common/util/env" "houston/logger" "houston/pkg/rest" request "houston/service/request" @@ -26,16 +26,16 @@ type ZendutyClient struct { func NewZendutyClient() *ZendutyClient { return &ZendutyClient{ RestClient: rest.NewHttpRestClient(), - DefaultTimeout: viper.GetDuration(util.DefaultZendutyTimeout), - BaseURL: viper.GetString(util.ZendutyBaseUrl), - AuthorizationToken: viper.GetString(util.ZendutyAuthorizationToken), + DefaultTimeout: viper.GetDuration(env.DefaultZendutyTimeout), + BaseURL: viper.GetString(env.ZendutyBaseUrl), + AuthorizationToken: viper.GetString(env.ZendutyAuthorizationToken), } } const createLogTag = "[create-zenduty-alert]" func (zendutyClient *ZendutyClient) CreateIncident(alertRequest request.AlertRequest) error { - fullURL := zendutyClient.BaseURL + viper.GetString(util.ZendutyIncidentUrl) + fullURL := zendutyClient.BaseURL + viper.GetString(env.ZendutyIncidentUrl) incidentName := alertRequest.Name requestHeaders := map[string]string{"Authorization": zendutyClient.AuthorizationToken} diff --git a/repository/incidentTeam/incident_team_repository_impl.go b/repository/incidentTeam/incident_team_repository_impl.go new file mode 100644 index 0000000..0ee7fcc --- /dev/null +++ b/repository/incidentTeam/incident_team_repository_impl.go @@ -0,0 +1,38 @@ +package incidentTeam + +import ( + "fmt" + "gorm.io/gorm" + "houston/logger" + "houston/model/incidentTeam" +) + +type incidentTeamRepositoryImpl struct { + gormClient *gorm.DB +} + +func (i *incidentTeamRepositoryImpl) Insert(incidentId uint, teamId uint) (*incidentTeam.IncidentTeamEntity, error) { + incidentTeam := &incidentTeam.IncidentTeamEntity{ + IncidentID: incidentId, + TeamID: teamId, + } + result := i.gormClient.Create(incidentTeam) + + if result.Error != nil { + logger.Error(fmt.Sprintf("failed to add incident-team mapping with incidentId %d and teamId %d: %v", incidentId, teamId, result.Error)) + return nil, result.Error + } + + return incidentTeam, nil +} + +func (i *incidentTeamRepositoryImpl) Fetch(incidentId uint, teamId uint) (*incidentTeam.IncidentTeamEntity, error) { + var incidentTeamEntity incidentTeam.IncidentTeamEntity + result := i.gormClient.Where("incident_id = ? AND team_id = ?", incidentId, teamId).First(&incidentTeamEntity) + + if result.Error != nil { + return nil, result.Error + } + + return &incidentTeamEntity, nil +} diff --git a/repository/incidentTeam/incident_team_repository_interface.go b/repository/incidentTeam/incident_team_repository_interface.go new file mode 100644 index 0000000..a6d05f8 --- /dev/null +++ b/repository/incidentTeam/incident_team_repository_interface.go @@ -0,0 +1,15 @@ +package incidentTeam + +import ( + "gorm.io/gorm" + "houston/model/incidentTeam" +) + +type IncidentTeamRepository interface { + Insert(incidentId uint, teamId uint) (*incidentTeam.IncidentTeamEntity, error) + Fetch(incidentId uint, teamId uint) (*incidentTeam.IncidentTeamEntity, error) +} + +func NewIncidentTeamRepository(gormClient *gorm.DB) IncidentTeamRepository { + return &incidentTeamRepositoryImpl{gormClient: gormClient} +} diff --git a/repository/incidentTeamTagValue/incident_team_tag_value_repository_impl.go b/repository/incidentTeamTagValue/incident_team_tag_value_repository_impl.go new file mode 100644 index 0000000..0ab06ea --- /dev/null +++ b/repository/incidentTeamTagValue/incident_team_tag_value_repository_impl.go @@ -0,0 +1,29 @@ +package incidentTeamTagValue + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + incidentTeamTagValueModel "houston/model/incidentTeamTagValue" +) + +type incidentTeamTagValueRepositoryImpl struct { + gormClient *gorm.DB +} + +func (i *incidentTeamTagValueRepositoryImpl) Insert( + incidentTeamID uint, tagValueIDs []uint, +) ([]incidentTeamTagValueModel.IncidentTeamTagValueEntity, error) { + var entities []incidentTeamTagValueModel.IncidentTeamTagValueEntity + for _, tagValueID := range tagValueIDs { + entities = append(entities, incidentTeamTagValueModel.IncidentTeamTagValueEntity{ + IncidentTeamID: incidentTeamID, + TagValueID: tagValueID, + }) + } + + if err := i.gormClient.Clauses(clause.OnConflict{DoNothing: true}).Create(&entities).Error; err != nil { + return nil, err + } + + return entities, nil +} diff --git a/repository/incidentTeamTagValue/incident_team_tag_value_repository_interface.go b/repository/incidentTeamTagValue/incident_team_tag_value_repository_interface.go new file mode 100644 index 0000000..0e68202 --- /dev/null +++ b/repository/incidentTeamTagValue/incident_team_tag_value_repository_interface.go @@ -0,0 +1,16 @@ +package incidentTeamTagValue + +import ( + "gorm.io/gorm" + incidentTeamTagValueModel "houston/model/incidentTeamTagValue" +) + +type IncidentTeamTagValueRepository interface { + Insert( + incidentTeamID uint, tagValueIDs []uint, + ) ([]incidentTeamTagValueModel.IncidentTeamTagValueEntity, error) +} + +func NewIncidentTeamTagValueRepository(gormClient *gorm.DB) IncidentTeamTagValueRepository { + return &incidentTeamTagValueRepositoryImpl{gormClient: gormClient} +} diff --git a/model/tag/tag.go b/repository/tag/tag.go similarity index 55% rename from model/tag/tag.go rename to repository/tag/tag.go index 1cd8bab..4ca2e13 100644 --- a/model/tag/tag.go +++ b/repository/tag/tag.go @@ -2,16 +2,18 @@ package tag import ( "gorm.io/gorm" + "houston/model/tag" + "houston/model/tagValue" ) type ITagRepository interface { - FindById(id uint) (*TagEntity, error) - FindTagValuesByTagId(id uint) (*[]TagValueEntity, error) - FindTagValuesByIds(ids []int32) (*[]TagValueEntity, error) - FindTagsByTeamId(teamId uint) (*[]TagDTO, error) - GetActiveTags() (*[]TagDTO, error) + FindById(id uint) (*tag.TagEntity, error) + FindTagValuesByTagId(id uint) (*[]tagValue.TagValueEntity, error) + FindTagValuesByIds(ids []int32) (*[]tagValue.TagValueEntity, error) + FindTagsByTeamId(teamId uint) (*[]tag.TagDTO, error) + GetActiveTags() (*[]tag.TagDTO, error) GetTagValuesByTagValueId(tagValueIds []uint) ([]string, error) - GetMandatoryActiveTags() (*[]TagDTO, error) + GetMandatoryActiveTags() (*[]tag.TagDTO, error) } type Repository struct { @@ -24,8 +26,8 @@ func NewTagRepository(gormClient *gorm.DB) *Repository { } } -func (r *Repository) FindById(id uint) (*TagEntity, error) { - tag := TagEntity{} +func (r *Repository) FindById(id uint) (*tag.TagEntity, error) { + tag := tag.TagEntity{} tx := r.gormClient.Raw("select * from tag where id = ?", id).Scan(&tag) if tx.Error != nil { @@ -38,8 +40,8 @@ func (r *Repository) FindById(id uint) (*TagEntity, error) { return &tag, nil } -func (r *Repository) FindTagValuesByTagId(id uint) (*[]TagValueEntity, error) { - var tagValues []TagValueEntity +func (r *Repository) FindTagValuesByTagId(id uint) (*[]tagValue.TagValueEntity, error) { + var tagValues []tagValue.TagValueEntity tx := r.gormClient.Raw("select tv.* from tag_value tv inner join tag t on t.id = tv.tag_id where t.id = ? and tv.active=?", id, true).Scan(&tagValues) if tx.Error != nil { @@ -51,8 +53,8 @@ func (r *Repository) FindTagValuesByTagId(id uint) (*[]TagValueEntity, error) { return &tagValues, nil } -func (r *Repository) FindTagValuesByIds(ids []int32) (*[]TagValueEntity, error) { - var tagValues []TagValueEntity +func (r *Repository) FindTagValuesByIds(ids []int32) (*[]tagValue.TagValueEntity, error) { + var tagValues []tagValue.TagValueEntity tx := r.gormClient.Raw("select * from tag_value where id in ?", ids).Scan(&tagValues) if tx.Error != nil { @@ -64,8 +66,8 @@ func (r *Repository) FindTagValuesByIds(ids []int32) (*[]TagValueEntity, error) return &tagValues, nil } -func (r *Repository) FindTagsByTeamId(teamId uint) (*[]TagDTO, error) { - var tags []TagDTO +func (r *Repository) FindTagsByTeamId(teamId uint) (*[]tag.TagDTO, error) { + var tags []tag.TagDTO tx := r.gormClient.Raw("select t.*, tt.optional from tag t inner join team_tag tt on t.id = tt.tag_id where tt.team_id = ?", teamId).Scan(&tags) if tx.Error != nil { @@ -77,9 +79,9 @@ func (r *Repository) FindTagsByTeamId(teamId uint) (*[]TagDTO, error) { return &tags, nil } -func (r *Repository) GetActiveTags() (*[]TagDTO, error) { - var tags []TagDTO - tx := r.gormClient.Table(TagEntity{}.TableName()).Where("active = ?", true).Order("display_order").Find(&tags) +func (r *Repository) GetActiveTags() (*[]tag.TagDTO, error) { + var tags []tag.TagDTO + tx := r.gormClient.Table(tag.TagEntity{}.TableName()).Where("active = ?", true).Order("display_order").Find(&tags) if tx.Error != nil { return nil, tx.Error } @@ -89,9 +91,9 @@ func (r *Repository) GetActiveTags() (*[]TagDTO, error) { return &tags, nil } -func (r *Repository) GetMandatoryActiveTags() (*[]TagDTO, error) { - var tags []TagDTO - tx := r.gormClient.Table(TagEntity{}.TableName()).Where("active = ? and optional = ?", true, false).Order("display_order").Find(&tags) +func (r *Repository) GetMandatoryActiveTags() (*[]tag.TagDTO, error) { + var tags []tag.TagDTO + tx := r.gormClient.Table(tag.TagEntity{}.TableName()).Where("active = ? and optional = ?", true, false).Order("display_order").Find(&tags) if tx.Error != nil { return nil, tx.Error } @@ -103,7 +105,7 @@ func (r *Repository) GetMandatoryActiveTags() (*[]TagDTO, error) { func (r *Repository) GetTagValuesByTagValueId(tagValueIds []uint) ([]string, error) { var tagValues []string - tx := r.gormClient.Model(TagValueEntity{}).Where("id in ?", tagValueIds).Where("active = ?", true).Pluck("value", &tagValues) + tx := r.gormClient.Model(tagValue.TagValueEntity{}).Where("id in ?", tagValueIds).Where("active = ?", true).Pluck("value", &tagValues) if tx.Error != nil { return nil, tx.Error diff --git a/repository/tagValue/tag_value_repository_impl.go b/repository/tagValue/tag_value_repository_impl.go new file mode 100644 index 0000000..bd8fb41 --- /dev/null +++ b/repository/tagValue/tag_value_repository_impl.go @@ -0,0 +1,23 @@ +package tagValue + +import ( + "gorm.io/gorm" + "houston/model/tagValue" +) + +type tagValueRepositoryImpl struct { + gormClient *gorm.DB +} + +func (repo *tagValueRepositoryImpl) GetTagValuesByTagName(tagName string) ([]tagValue.TagValueEntity, error) { + var tagValues []tagValue.TagValueEntity + result := repo.gormClient.Preload("Tag"). + Joins("JOIN tag ON tag.id = tag_value.tag_id"). + Where("tag.name = ? and tag_value.active = ?", tagName, true).Find(&tagValues) + + if result.Error != nil { + return nil, result.Error + } + + return tagValues, nil +} diff --git a/repository/tagValue/tag_value_repository_interface.go b/repository/tagValue/tag_value_repository_interface.go new file mode 100644 index 0000000..7498453 --- /dev/null +++ b/repository/tagValue/tag_value_repository_interface.go @@ -0,0 +1,14 @@ +package tagValue + +import ( + "gorm.io/gorm" + "houston/model/tagValue" +) + +type TagValueRepository interface { + GetTagValuesByTagName(tagName string) ([]tagValue.TagValueEntity, error) +} + +func NewTagValueRepository(db *gorm.DB) TagValueRepository { + return &tagValueRepositoryImpl{gormClient: db} +} diff --git a/service/alertService/alert_service_impl.go b/service/alertService/alert_service_impl.go index dfb96bd..e2bd5fa 100644 --- a/service/alertService/alert_service_impl.go +++ b/service/alertService/alert_service_impl.go @@ -7,7 +7,7 @@ import ( "github.com/spf13/viper" "go.uber.org/zap" "houston/common/metrics" - "houston/common/util" + "houston/common/util/env" "houston/logger" "houston/model/externalTeam" "houston/pkg/alertClient" @@ -39,7 +39,7 @@ func (alertService *AlertService) CreateIncidentAlert(incidentDTO response.Incid logger.Error(fmt.Sprintf("Error while unmarshalling metadata of external team for teamId: %d", teamId), zap.Error(err)) return err } - slackChannelUrl := viper.GetString(util.SlackChannelBaseUrl) + incidentDTO.SlackChannel + slackChannelUrl := viper.GetString(env.SlackChannelBaseUrl) + incidentDTO.SlackChannel updatedDescription := fmt.Sprintf("%s, channel - %s \n %s", incidentDTO.IncidentName, slackChannelUrl, incidentDTO.Description) alertRequest := request.AlertRequest{ Name: incidentDTO.IncidentName, diff --git a/service/incident/impl/incident_service_test.go b/service/incident/impl/incident_service_test.go index 84aa9d0..3b944ae 100644 --- a/service/incident/impl/incident_service_test.go +++ b/service/incident/impl/incident_service_test.go @@ -26,35 +26,37 @@ import ( type IncidentServiceSuite struct { suite.Suite - slackService mocks.ISlackServiceMock - incidentService *IncidentServiceV2 - incidentRepository mocks.IIncidentRepositoryMock - teamRepository mocks.ITeamRepositoryMock - userRepository mocks.IUserRepositoryMock - teamUserService mocks.ITeamUserServiceMock - severityRepository mocks.ISeverityRepositoryMock - krakatoaService mocks.IKrakatoaServiceMock - incidentChannelService mocks.IIncidentChannelServiceMock - incidentJiraService mocks.IncidentJiraServiceMock - tagService mocks.ITagServiceMock - calendarService mocks.ICalendarServiceMock - rcaService mocks.IRcaServiceMock - severityService mocks.ISeverityServiceMock - teamServiceV2 mocks.ITeamServiceV2Mock - incidentStatusService mocks.IncidentStatusServiceMock - mockDirectory string - mockPngName string - mockCsvName string - mockIncidentId uint - mockIncidentSlackChannelID string - mockUserEmail string - previousSeverityId uint - updatedSeverityId uint - previousStatusId uint - updatedStatusId uint - previousTeamId uint - updatedTeamId uint - mockValidJiraLink string + slackService mocks.ISlackServiceMock + incidentService *IncidentServiceV2 + incidentRepository mocks.IIncidentRepositoryMock + teamRepository mocks.ITeamRepositoryMock + userRepository mocks.IUserRepositoryMock + teamUserService mocks.ITeamUserServiceMock + severityRepository mocks.ISeverityRepositoryMock + krakatoaService mocks.IKrakatoaServiceMock + incidentChannelService mocks.IIncidentChannelServiceMock + incidentJiraService mocks.IncidentJiraServiceMock + tagService mocks.ITagServiceMock + calendarService mocks.ICalendarServiceMock + rcaService mocks.IRcaServiceMock + severityService mocks.ISeverityServiceMock + teamServiceV2 mocks.ITeamServiceV2Mock + incidentStatusService mocks.IncidentStatusServiceMock + incidentTeamService mocks.IncidentTeamServiceMock + incidentTeamTagValueService mocks.IncidentTeamTagValueServiceMock + mockDirectory string + mockPngName string + mockCsvName string + mockIncidentId uint + mockIncidentSlackChannelID string + mockUserEmail string + previousSeverityId uint + updatedSeverityId uint + previousStatusId uint + updatedStatusId uint + previousTeamId uint + updatedTeamId uint + mockValidJiraLink string } func (suite *IncidentServiceSuite) Test_UpdateIncident_Success() { @@ -597,23 +599,27 @@ func (suite *IncidentServiceSuite) SetupTest() { suite.severityService = *mocks.NewISeverityServiceMock(suite.T()) suite.teamServiceV2 = *mocks.NewITeamServiceV2Mock(suite.T()) suite.incidentStatusService = *mocks.NewIncidentStatusServiceMock(suite.T()) + suite.incidentTeamService = *mocks.NewIncidentTeamServiceMock(suite.T()) + suite.incidentTeamTagValueService = *mocks.NewIncidentTeamTagValueServiceMock(suite.T()) suite.incidentService = &IncidentServiceV2{ - db: nil, - slackService: &suite.slackService, - teamRepository: &suite.teamRepository, - severityRepository: &suite.severityRepository, - incidentRepository: &suite.incidentRepository, - userRepository: &suite.userRepository, - incidentChannelService: &suite.incidentChannelService, - krakatoaService: &suite.krakatoaService, - tagService: &suite.tagService, - incidentJiraService: &suite.incidentJiraService, - calendarService: &suite.calendarService, - rcaService: &suite.rcaService, - teamUserService: &suite.teamUserService, - severityService: &suite.severityService, - teamServiceV2: &suite.teamServiceV2, - incidentStatusService: &suite.incidentStatusService, + db: nil, + slackService: &suite.slackService, + teamRepository: &suite.teamRepository, + severityRepository: &suite.severityRepository, + incidentRepository: &suite.incidentRepository, + userRepository: &suite.userRepository, + incidentChannelService: &suite.incidentChannelService, + krakatoaService: &suite.krakatoaService, + tagService: &suite.tagService, + incidentJiraService: &suite.incidentJiraService, + calendarService: &suite.calendarService, + rcaService: &suite.rcaService, + teamUserService: &suite.teamUserService, + severityService: &suite.severityService, + teamServiceV2: &suite.teamServiceV2, + incidentStatusService: &suite.incidentStatusService, + incidentTeamService: &suite.incidentTeamService, + incidentTeamTagValueService: &suite.incidentTeamTagValueService, } suite.mockDirectory = "test_file" diff --git a/service/incident/impl/incident_service_v2.go b/service/incident/impl/incident_service_v2.go index 31a793f..4b7a41f 100644 --- a/service/incident/impl/incident_service_v2.go +++ b/service/incident/impl/incident_service_v2.go @@ -13,6 +13,7 @@ import ( "houston/common/metrics" "houston/common/util" "houston/common/util/dto" + "houston/common/util/env" houstonSlackUtil "houston/common/util/slack" stringUtil "houston/common/util/string" "houston/internal/processor/action/view" @@ -34,6 +35,8 @@ import ( "houston/pkg/rest" "houston/repository/externalTeamRepo" "houston/repository/incidentStatus" + incidentTeamModel "houston/repository/incidentTeam" + incidentTeamTagValueRepo "houston/repository/incidentTeamTagValue" rcaRepository "houston/repository/rca/impl" "houston/repository/rcaInput" severityRepo "houston/repository/severity" @@ -46,6 +49,8 @@ import ( "houston/service/google" incidentService "houston/service/incident" incidentStatusRepo "houston/service/incidentStatus" + "houston/service/incidentTeam" + "houston/service/incidentTeamTagValue" incidentChannel "houston/service/incident_channel" "houston/service/incident_jira" "houston/service/krakatoa" @@ -77,23 +82,25 @@ import ( ) type IncidentServiceV2 struct { - db *gorm.DB - slackService slack.ISlackService - incidentChannelService incidentChannel.IIncidentChannelService - teamRepository team.ITeamRepository - severityRepository severityRepo.ISeverityRepository - incidentRepository incident.IIncidentRepository - userRepository user.IUserRepository - krakatoaService krakatoa.IKrakatoaService - calendarService conferenceService.ICalendarService - incidentJiraService incident_jira.IncidentJiraService - tagService tag.ITagService - rcaService rcaService.IRcaService - teamServiceV2 teamService.ITeamServiceV2 - alertService alertService.IAlertService - teamUserService teamUser2.ITeamUserService - severityService severityService.ISeverityService - incidentStatusService incidentStatusRepo.IncidentStatusService + db *gorm.DB + slackService slack.ISlackService + incidentChannelService incidentChannel.IIncidentChannelService + teamRepository team.ITeamRepository + severityRepository severityRepo.ISeverityRepository + incidentRepository incident.IIncidentRepository + userRepository user.IUserRepository + krakatoaService krakatoa.IKrakatoaService + calendarService conferenceService.ICalendarService + incidentJiraService incident_jira.IncidentJiraService + tagService tag.ITagService + rcaService rcaService.IRcaService + teamServiceV2 teamService.ITeamServiceV2 + alertService alertService.IAlertService + teamUserService teamUser2.ITeamUserService + severityService severityService.ISeverityService + incidentStatusService incidentStatusRepo.IncidentStatusService + incidentTeamService incidentTeam.IncidentTeamService + incidentTeamTagValueService incidentTeamTagValue.IncidentTeamTagValueService } /* @@ -149,6 +156,10 @@ func NewIncidentServiceV2(db *gorm.DB) *IncidentServiceV2 { teamUserService: teamUserService, severityService: severityService, incidentStatusService: incidentStatusService, + incidentTeamService: incidentTeam.NewIncidentTeamService(incidentTeamModel.NewIncidentTeamRepository(db)), + incidentTeamTagValueService: incidentTeamTagValue.NewIncidentTeamTagValueService( + incidentTeamTagValueRepo.NewIncidentTeamTagValueRepository(db), + ), } driveActions, _ := googleDrive.NewGoogleDriveActions() incidentService.rcaService = rcaServiceImpl.NewRcaService( @@ -697,6 +708,21 @@ func createIncidentWorkflow( return err } i.createConferenceAndPostMessageInSlack(incidentEntity) + + err = i.addIncidentTeam(incidentEntity.ID, incidentEntity.TeamId) + if err != nil { + return err + } + + return nil +} + +func (i *IncidentServiceV2) addIncidentTeam(incidentID, teamId uint) error { + if viper.GetString(env.APP_NAME) == env.QA_APP_NAME { + _, err := i.incidentTeamService.AddIncidentTeam(incidentID, teamId) + return err + } + return nil } @@ -1802,7 +1828,8 @@ func (i *IncidentServiceV2) UpdateTeamId( zap.String("TeamId", request.TeamId), zap.Error(err)) return fmt.Errorf("Invalid team ID: %s", request.TeamId) } - if incidentEntity.TeamId != uint(teamIDToUpdate) { + originalTeamId := incidentEntity.TeamId + if originalTeamId != uint(teamIDToUpdate) { teamEntity, err := i.teamRepository.FindTeamById(uint(teamIDToUpdate)) if err != nil || teamEntity == nil { logger.Error(fmt.Sprintf("%s error in fetching team by id", updateLogTag), @@ -1818,7 +1845,7 @@ func (i *IncidentServiceV2) UpdateTeamId( } err = i.UpdateTeamIdWorkflow( - userId, incidentEntity.ID, teamEntity, severityEntity, incidentStatus, incidentChannels, + userId, incidentEntity.ID, teamEntity, severityEntity, incidentStatus, incidentChannels, originalTeamId, request.IssueTypes, ) if err != nil { logger.Error(fmt.Sprintf("%s error in update team id workflow", updateLogTag), zap.Error(err)) @@ -1863,9 +1890,11 @@ func (i *IncidentServiceV2) postTeamUpdateFlows( incidentStatus *incidentStatusModel.IncidentStatusDTO, incidentChannels []incident_channel.IncidentChannelEntity, teamEntity *team.TeamEntity, + issueTypes []uint, + originalTeamId uint, ) error { err := i.UpdateTeamIdWorkflow( - userId, incidentEntity.ID, teamEntity, severityEntity, incidentStatus, incidentChannels, + userId, incidentEntity.ID, teamEntity, severityEntity, incidentStatus, incidentChannels, originalTeamId, issueTypes, ) if err != nil { logger.Error(fmt.Sprintf("%s error in update team id workflow", updateLogTag), zap.Error(err)) @@ -1876,6 +1905,22 @@ func (i *IncidentServiceV2) postTeamUpdateFlows( return nil } +func (i *IncidentServiceV2) processIncidentTeamAndTagValueFlow(incidentId, originalTeamId uint, tagValueIds []uint) error { + if viper.GetString(env.APP_NAME) == env.QA_APP_NAME && len(tagValueIds) != 0 { + originalIncidentTeam, err := i.incidentTeamService.GetIncidentTeam(incidentId, originalTeamId) + if err != nil { + return err + } + + _, err = i.incidentTeamTagValueService.AddTagValuesForIncidentTeam(originalIncidentTeam.ID, tagValueIds) + if err != nil { + return err + } + } + + return nil +} + func (i *IncidentServiceV2) postProductUpdateFlows( userId string, incidentEntity *incident.IncidentEntity, @@ -1919,8 +1964,9 @@ func (i *IncidentServiceV2) UpdateProductID( } incidentEntity.Products = productEntities + originalTeamId := incidentEntity.TeamId - isTeamUpdateRequired := fmt.Sprintf("%d", incidentEntity.TeamId) != request.TeamId + isTeamUpdateRequired := fmt.Sprintf("%d", originalTeamId) != request.TeamId var teamEntity *team.TeamEntity var err error @@ -1937,7 +1983,10 @@ func (i *IncidentServiceV2) UpdateProductID( } if isTeamUpdateRequired { - err = i.postTeamUpdateFlows(userId, incidentEntity, severityEntity, incidentStatus, incidentChannels, teamEntity) + err = i.postTeamUpdateFlows( + userId, incidentEntity, severityEntity, incidentStatus, + incidentChannels, teamEntity, request.IssueTypes, originalTeamId, + ) if err != nil { logger.Error(fmt.Sprintf("%s error in running post team update workflow", updateLogTag), zap.Error(err)) return err @@ -1966,8 +2015,10 @@ func (i *IncidentServiceV2) UpdateTeamIdWorkflow( severityEntity *severity.SeverityEntity, incidentStatus *incidentStatusModel.IncidentStatusDTO, incidentChannels []incident_channel.IncidentChannelEntity, + originalTeamId uint, + issueTypes []uint, ) error { - var slackErrors []error + var flowErrors []error incidentEntity, err := i.incidentRepository.FindIncidentById(incidentID) if err != nil { @@ -1980,7 +2031,7 @@ func (i *IncidentServiceV2) UpdateTeamIdWorkflow( logger.Error( fmt.Sprintf("%s error while adding default users to incident", updateLogTag), zap.Error(err), ) - slackErrors = append(slackErrors, err) + flowErrors = append(flowErrors, err) } channelTopic := ChannelTopic{ @@ -1992,6 +2043,18 @@ func (i *IncidentServiceV2) UpdateTeamIdWorkflow( Title: incidentEntity.Title, } + err = i.addIncidentTeam(incidentEntity.ID, incidentEntity.TeamId) + if err != nil { + logger.Error(fmt.Sprintf("%s error in adding incident team", updateLogTag), zap.Error(err)) + flowErrors = append(flowErrors, err) + } + + err = i.processIncidentTeamAndTagValueFlow(incidentEntity.ID, originalTeamId, issueTypes) + if err != nil { + logger.Error(fmt.Sprintf("%s error in processing incident team and tag value flow", updateLogTag), zap.Error(err)) + flowErrors = append(flowErrors, err) + } + var waitGroup sync.WaitGroup waitGroup.Add(updateTeamSlackActionCount) @@ -2012,7 +2075,7 @@ func (i *IncidentServiceV2) UpdateTeamIdWorkflow( logger.Error( fmt.Sprintf("%s post response failed for IncidentUpdateType", updateLogTag), zap.Error(err), ) - slackErrors = append(slackErrors, err) + flowErrors = append(flowErrors, err) } }) @@ -2041,7 +2104,7 @@ func (i *IncidentServiceV2) UpdateTeamIdWorkflow( ), zap.Error(err), ) - slackErrors = append(slackErrors, err) + flowErrors = append(flowErrors, err) } }) @@ -2059,13 +2122,13 @@ func (i *IncidentServiceV2) UpdateTeamIdWorkflow( fmt.Sprintf("%s %d error while setting channel topic", updateLogTag, incidentEntity.TeamId), zap.Error(err), ) - slackErrors = append(slackErrors, err) + flowErrors = append(flowErrors, err) } }) waitGroup.Wait() - if slackErrors != nil && len(slackErrors) != 0 { - return slackErrors[0] + if flowErrors != nil && len(flowErrors) != 0 { + return flowErrors[0] } return nil @@ -2376,7 +2439,7 @@ func (i *IncidentServiceV2) addBookMarkAndPostMessageInSlack(incidentName string logger.Info(fmt.Sprintf("%s [%s] Conference link posted to the incidentChannel %s", logTag, incidentName, conferenceLink)) } func (i *IncidentServiceV2) SendAlert(incidentEntity *incident.IncidentEntity) { - if !viper.GetBool(util.AlertIntegrationEnabled) || incidentEntity.SeverityId > viper.GetUint(util.AlertEnabledSeverity) { + if !viper.GetBool(env.AlertIntegrationEnabled) || incidentEntity.SeverityId > viper.GetUint(env.AlertEnabledSeverity) { return } externalTeamDTO, err := i.teamServiceV2.GetExternalTeam(incidentEntity.TeamId, util.ZendutyProvider) diff --git a/service/incident/impl/incident_update_status.go b/service/incident/impl/incident_update_status.go index 624e1cc..fefae50 100644 --- a/service/incident/impl/incident_update_status.go +++ b/service/incident/impl/incident_update_status.go @@ -12,12 +12,14 @@ import ( "houston/common/metrics" tagUtil "houston/common/tag" "houston/common/util" + "houston/common/util/env" houstonSlackUtil "houston/common/util/slack" stringUtil "houston/common/util/string" "houston/logger" "houston/model/customErrors" "houston/model/incident" tagModel "houston/model/tag" + "houston/model/tagValue" "houston/model/team" "houston/model/user" rcaService "houston/service/rca/impl" @@ -226,6 +228,12 @@ func (i *IncidentServiceV2) ExecuteIncidentResolveFlow( return err } + err = i.processIncidentTeamAndTagValueFlow(incidentEntity.ID, incidentEntity.TeamId, request.IssueTypes) + if err != nil { + logger.Error(fmt.Sprintf("%s error while processing incident team and tag value flow", resolveLogTag), zap.Error(err)) + return err + } + err = i.updateIncidentRCASummary(request.RcaSummary, incidentEntity, userID) if err != nil { logger.Error( @@ -542,7 +550,7 @@ func (i *IncidentServiceV2) updateIncidentJiraLinks( return nil } -func (i *IncidentServiceV2) getTagInfo(tagValID uint) (*tagModel.TagValueEntity, *tagModel.TagEntity, error) { +func (i *IncidentServiceV2) getTagInfo(tagValID uint) (*tagValue.TagValueEntity, *tagModel.TagEntity, error) { tagValue, err := i.tagService.FindTagValueById(tagValID) if err != nil || tagValue == nil { errMsg := fmt.Sprintf("Could not get tag value by id %d", tagValID) @@ -563,20 +571,11 @@ func (i *IncidentServiceV2) getTagInfo(tagValID uint) (*tagModel.TagValueEntity, func (i *IncidentServiceV2) getTagsAsMap(request service.ResolveIncidentRequest) (map[string]tagUtil.TagMapValue, error) { tagMap := make(map[string]tagUtil.TagMapValue) if request.BusinessAffected != nil && len(request.BusinessAffected) > 0 { - var businessAffectedValues []tagUtil.SelectedOption - var actionId, label string - for _, tagValID := range request.BusinessAffected { - tagValue, tag, err := i.getTagInfo(tagValID) - if err != nil { - return nil, err - } - actionId = tag.ActionId - label = tag.Label - businessAffectedValues = append( - businessAffectedValues, tagUtil.SelectedOption{Value: tagValue.Value, ID: tagValue.ID}, - ) + err := i.buildTagMapValueForMultiSelectOptions(request.BusinessAffected, &tagMap) + if err != nil { + logger.Info(fmt.Sprintf("%s error while building tag map value for business affected", resolveLogTag), zap.Error(err)) + return tagMap, err } - tagMap[actionId] = tagUtil.TagMapValue{Label: label, MultiSelectedOptions: businessAffectedValues} } if request.ContributingFactors != 0 { @@ -591,25 +590,47 @@ func (i *IncidentServiceV2) getTagsAsMap(request service.ResolveIncidentRequest) } if request.AdditionalTags != nil && len(request.AdditionalTags) > 0 { - var additionalTagValues []tagUtil.SelectedOption - var actionId, label string - for _, tagValID := range request.AdditionalTags { - tagValue, tag, err := i.getTagInfo(tagValID) - if err != nil { - return nil, err - } - actionId = tag.ActionId - label = tag.Label - additionalTagValues = append( - additionalTagValues, tagUtil.SelectedOption{Value: tagValue.Value, ID: tagValue.ID}, - ) + err := i.buildTagMapValueForMultiSelectOptions(request.AdditionalTags, &tagMap) + if err != nil { + logger.Info(fmt.Sprintf("%s error while building tag map value for additional tags", resolveLogTag), zap.Error(err)) + return tagMap, err + } + } + + if request.IssueTypes != nil && len(request.IssueTypes) > 0 { + err := i.buildTagMapValueForMultiSelectOptions(request.IssueTypes, &tagMap) + if err != nil { + logger.Error(fmt.Sprintf("%s error while building tag map value for issue types", resolveLogTag), zap.Error(err)) + return tagMap, err } - tagMap[actionId] = tagUtil.TagMapValue{Label: label, MultiSelectedOptions: additionalTagValues} } return tagMap, nil } +func (i *IncidentServiceV2) buildTagMapValueForMultiSelectOptions( + tagValIDs []uint, tagMap *map[string]tagUtil.TagMapValue, +) error { + var selectedOptions []tagUtil.SelectedOption + var actionId, label string + + for _, tagValID := range tagValIDs { + tagValue, tag, err := i.getTagInfo(tagValID) + if err != nil { + return err + } + actionId = tag.ActionId + label = tag.Label + selectedOptions = append( + selectedOptions, tagUtil.SelectedOption{Value: tagValue.Value, ID: tagValue.ID}, + ) + } + + (*tagMap)[actionId] = tagUtil.TagMapValue{Label: label, MultiSelectedOptions: selectedOptions} + + return nil +} + func (i *IncidentServiceV2) postRCADetailsBlock( tagsMap map[string]tagUtil.TagMapValue, channelId string, @@ -891,7 +912,7 @@ func (i *IncidentServiceV2) processArchivalTime(severityID uint, channelID strin } func (i *IncidentServiceV2) processIncidentRCAFlow(incidentEntity *incident.IncidentEntity) { - if incidentEntity.IsPrivate { + if incidentEntity.IsPrivate || viper.GetString(env.APP_NAME) == env.QA_APP_NAME { return } diff --git a/service/incident/impl/incident_update_status_test.go b/service/incident/impl/incident_update_status_test.go index 1eea89c..573e6e2 100644 --- a/service/incident/impl/incident_update_status_test.go +++ b/service/incident/impl/incident_update_status_test.go @@ -7,6 +7,7 @@ import ( "houston/model/incident" "houston/model/incidentStatus" "houston/model/tag" + "houston/model/tagValue" service "houston/service/request" ) @@ -603,8 +604,8 @@ func (suite *IncidentServiceSuite) TestResolveIncident_HappyFlow() { suite.NoError(err, "No error must be thrown") } -func GetMockTagValue() *tag.TagValueEntity { - return &tag.TagValueEntity{ +func GetMockTagValue() *tagValue.TagValueEntity { + return &tagValue.TagValueEntity{ TagId: 1, Value: "mockTagValue", Active: true, diff --git a/service/incidentTeam/incident_team_service_impl.go b/service/incidentTeam/incident_team_service_impl.go new file mode 100644 index 0000000..7f727f7 --- /dev/null +++ b/service/incidentTeam/incident_team_service_impl.go @@ -0,0 +1,53 @@ +package incidentTeam + +import ( + "fmt" + "houston/logger" + incidentTeamModel "houston/model/incidentTeam" + "houston/repository/incidentTeam" +) + +type incidentTeamServiceImpl struct { + incidentTeamRepository incidentTeam.IncidentTeamRepository +} + +const logTag = "[incident-team-service]" + +func (service *incidentTeamServiceImpl) AddIncidentTeam(incidentId uint, teamId uint) (*incidentTeamModel.IncidentTeamDTO, error) { + existingIncidentTeam, _ := service.GetIncidentTeam(incidentId, teamId) + if existingIncidentTeam != nil { + logger.Info(fmt.Sprintf("could not add incident-team mapping with incidentId %d and teamId %d as it already exists", incidentId, teamId)) + return existingIncidentTeam, nil + } + + incidentTeam, err := service.incidentTeamRepository.Insert(incidentId, teamId) + if err != nil { + logger.Info(fmt.Sprintf("%s failed to add incident-team mapping with incidentId %d and teamId %d: %v", logTag, incidentId, teamId, err)) + return nil, err + } + + if incidentTeam == nil { + logger.Info(fmt.Sprintf("%s could not add incident-team mapping with incidentId %d and teamId %d as it already exists", logTag, incidentId, teamId)) + return nil, nil + } + + incidentTeamDTO := incidentTeam.ToDTO() + + return &incidentTeamDTO, err +} + +func (service *incidentTeamServiceImpl) GetIncidentTeam(incidentId, teamId uint) (*incidentTeamModel.IncidentTeamDTO, error) { + incidentTeam, err := service.incidentTeamRepository.Fetch(incidentId, teamId) + if err != nil { + logger.Info(fmt.Sprintf("%s failed to get incident-team mapping with incidentId %d and teamId %d: %v", logTag, incidentId, teamId, err)) + return nil, err + } + + if incidentTeam == nil { + logger.Info(fmt.Sprintf("%s could not get incident-team mapping with incidentId %d and teamId %d as it does not exist", logTag, incidentId, teamId)) + return nil, nil + } + + incidentTeamDTO := incidentTeam.ToDTO() + return &incidentTeamDTO, nil +} diff --git a/service/incidentTeam/incident_team_service_interface.go b/service/incidentTeam/incident_team_service_interface.go new file mode 100644 index 0000000..5abc8ab --- /dev/null +++ b/service/incidentTeam/incident_team_service_interface.go @@ -0,0 +1,17 @@ +package incidentTeam + +import ( + incidentTeamModel "houston/model/incidentTeam" + "houston/repository/incidentTeam" +) + +type IncidentTeamService interface { + AddIncidentTeam(incidentId uint, teamId uint) (*incidentTeamModel.IncidentTeamDTO, error) + GetIncidentTeam(incidentId, teamId uint) (*incidentTeamModel.IncidentTeamDTO, error) +} + +func NewIncidentTeamService(incidentTeamRepository incidentTeam.IncidentTeamRepository) IncidentTeamService { + return &incidentTeamServiceImpl{ + incidentTeamRepository: incidentTeamRepository, + } +} diff --git a/service/incidentTeam/incident_team_service_test.go b/service/incidentTeam/incident_team_service_test.go new file mode 100644 index 0000000..c9c9568 --- /dev/null +++ b/service/incidentTeam/incident_team_service_test.go @@ -0,0 +1,94 @@ +package incidentTeam + +import ( + "errors" + "github.com/stretchr/testify/suite" + "houston/logger" + "houston/mocks" + "houston/model/incidentTeam" + "testing" +) + +type IncidentTeamServiceSuite struct { + suite.Suite + incidentTeamRepository mocks.IncidentTeamRepositoryMock + incidentTeamService IncidentTeamService +} + +func (suite *IncidentTeamServiceSuite) Test_AddIncidentTeam_AlreadyExistingTeam() { + mockIncidentTeam := getMockIncidentTeam() + suite.incidentTeamRepository.FetchMock.Return(&mockIncidentTeam, nil) + + incidentTeamDTO, err := suite.incidentTeamService.AddIncidentTeam(1, 1) + suite.Nil(err, "Error should be nil") + suite.NotNil(incidentTeamDTO, "Incident Team DTO should not be nil") +} + +func (suite *IncidentTeamServiceSuite) Test_AddIncidentTeam_InsertionFailure() { + suite.incidentTeamRepository.FetchMock.Return(nil, nil) + suite.incidentTeamRepository.InsertMock.Return(nil, errors.New("error")) + + incidentTeamDTO, err := suite.incidentTeamService.AddIncidentTeam(1, 1) + suite.NotNil(err, "Error should not be nil") + suite.Nil(incidentTeamDTO, "Incident Team DTO should be nil") +} + +func (suite *IncidentTeamServiceSuite) Test_AddIncidentTeam_NilResponse() { + suite.incidentTeamRepository.FetchMock.Return(nil, nil) + suite.incidentTeamRepository.InsertMock.Return(nil, nil) + + incidentTeamDTO, err := suite.incidentTeamService.AddIncidentTeam(1, 1) + suite.Nil(err, "Error should be nil") + suite.Nil(incidentTeamDTO, "Incident Team DTO should be nil") +} + +func (suite *IncidentTeamServiceSuite) Test_AddIncidentTeam_SuccessCase() { + mockIncidentTeam := getMockIncidentTeam() + suite.incidentTeamRepository.FetchMock.Return(nil, nil) + suite.incidentTeamRepository.InsertMock.Return(&mockIncidentTeam, nil) + + incidentTeamDTO, err := suite.incidentTeamService.AddIncidentTeam(1, 1) + suite.Nil(err, "Error should be nil") + suite.NotNil(incidentTeamDTO, "Incident Team DTO should not be nil") +} + +func (suite *IncidentTeamServiceSuite) Test_GetIncidentTeam_FetchFailure() { + suite.incidentTeamRepository.FetchMock.Return(nil, errors.New("error")) + + incidentTeamDTO, err := suite.incidentTeamService.GetIncidentTeam(1, 1) + suite.NotNil(err, "Error should not be nil") + suite.Nil(incidentTeamDTO, "Incident Team DTO should be nil") +} + +func (suite *IncidentTeamServiceSuite) Test_GetIncidentTeam_NilResponse() { + suite.incidentTeamRepository.FetchMock.Return(nil, nil) + + incidentTeamDTO, err := suite.incidentTeamService.GetIncidentTeam(1, 1) + suite.Nil(err, "Error should be nil") + suite.Nil(incidentTeamDTO, "Incident Team DTO should be nil") +} + +func (suite *IncidentTeamServiceSuite) Test_GetIncidentTeam_SuccessCase() { + mockIncidentTeam := getMockIncidentTeam() + suite.incidentTeamRepository.FetchMock.Return(&mockIncidentTeam, nil) + + incidentTeamDTO, err := suite.incidentTeamService.GetIncidentTeam(1, 1) + suite.Nil(err, "Error should be nil") + suite.NotNil(incidentTeamDTO, "Incident Team DTO should not be nil") +} + +func (suite *IncidentTeamServiceSuite) SetupTest() { + logger.InitLogger() + suite.incidentTeamRepository = *mocks.NewIncidentTeamRepositoryMock(suite.T()) + suite.incidentTeamService = &incidentTeamServiceImpl{incidentTeamRepository: &suite.incidentTeamRepository} +} + +func getMockIncidentTeam() incidentTeam.IncidentTeamEntity { + return incidentTeam.IncidentTeamEntity{ + IncidentID: 1, + TeamID: 1, + } +} +func TestIncidentTeamService(t *testing.T) { + suite.Run(t, new(IncidentTeamServiceSuite)) +} diff --git a/service/incidentTeamTagValue/incident_team_tag_value_service_impl.go b/service/incidentTeamTagValue/incident_team_tag_value_service_impl.go new file mode 100644 index 0000000..0b05092 --- /dev/null +++ b/service/incidentTeamTagValue/incident_team_tag_value_service_impl.go @@ -0,0 +1,41 @@ +package incidentTeamTagValue + +import ( + "fmt" + "houston/common/util/dto" + "houston/logger" + incidentTeamTagValueModel "houston/model/incidentTeamTagValue" + "houston/repository/incidentTeamTagValue" +) + +type incidentTeamTagValueServiceImpl struct { + incidentTeamTagValueRepository incidentTeamTagValue.IncidentTeamTagValueRepository +} + +const logType = "[incident-team-tag-value-service]" + +func (service *incidentTeamTagValueServiceImpl) AddTagValuesForIncidentTeam( + incidentTeamId uint, tagValueIds []uint, +) ([]incidentTeamTagValueModel.IncidentTeamTagValueDTO, error) { + logger.Info( + fmt.Sprintf( + "%s adding tag values for incident team with incidentTeamId %d and tagValueIds %v", + logType, incidentTeamId, tagValueIds, + ), + ) + + incidentTeamTagValueEntities, err := service.incidentTeamTagValueRepository.Insert(incidentTeamId, tagValueIds) + if err != nil { + logger.Info( + fmt.Sprintf( + "%s failed to add tag values for incident team with incidentTeamId %d and tagValueIds %v: %v", + logType, incidentTeamId, tagValueIds, err, + ), + ) + return nil, err + } + + return dto.ToDtoArray[ + incidentTeamTagValueModel.IncidentTeamTagValueEntity, incidentTeamTagValueModel.IncidentTeamTagValueDTO, + ](incidentTeamTagValueEntities), nil +} diff --git a/service/incidentTeamTagValue/incident_team_tag_value_service_interface.go b/service/incidentTeamTagValue/incident_team_tag_value_service_interface.go new file mode 100644 index 0000000..ff541d2 --- /dev/null +++ b/service/incidentTeamTagValue/incident_team_tag_value_service_interface.go @@ -0,0 +1,18 @@ +package incidentTeamTagValue + +import ( + incidentTeamTagValueModel "houston/model/incidentTeamTagValue" + "houston/repository/incidentTeamTagValue" +) + +type IncidentTeamTagValueService interface { + AddTagValuesForIncidentTeam( + incidentTeamId uint, tagValueIds []uint, + ) ([]incidentTeamTagValueModel.IncidentTeamTagValueDTO, error) +} + +func NewIncidentTeamTagValueService( + incidentTeamTagValueRepository incidentTeamTagValue.IncidentTeamTagValueRepository, +) IncidentTeamTagValueService { + return &incidentTeamTagValueServiceImpl{incidentTeamTagValueRepository: incidentTeamTagValueRepository} +} diff --git a/service/incidentTeamTagValue/incident_team_tag_value_service_test.go b/service/incidentTeamTagValue/incident_team_tag_value_service_test.go new file mode 100644 index 0000000..2fff93b --- /dev/null +++ b/service/incidentTeamTagValue/incident_team_tag_value_service_test.go @@ -0,0 +1,46 @@ +package incidentTeamTagValue + +import ( + "errors" + "github.com/stretchr/testify/suite" + "houston/logger" + "houston/mocks" + incidentTeamTagValueModel "houston/model/incidentTeamTagValue" + "testing" +) + +type IncidentTeamTagValueServiceSuite struct { + suite.Suite + incidentTeamTagValueRepository mocks.IncidentTeamTagValueRepositoryMock + incidentTeamTagValueService IncidentTeamTagValueService +} + +func (suite *IncidentTeamTagValueServiceSuite) Test_AddTagValuesForIncidentTeam_RepoErrorCase() { + suite.incidentTeamTagValueRepository.InsertMock.Return(nil, errors.New("error")) + + result, err := suite.incidentTeamTagValueService.AddTagValuesForIncidentTeam(1, []uint{1, 2, 3}) + suite.NotNil(err, "Error should not be nil") + suite.Nil(result, "Result should be nil") +} + +func (suite *IncidentTeamTagValueServiceSuite) Test_AddTagValuesForIncidentTeam_SuccessCase() { + incidentTeamTagValues := []incidentTeamTagValueModel.IncidentTeamTagValueEntity{ + {ID: 1, IncidentTeamID: 1}, {ID: 1, IncidentTeamID: 2}, {ID: 1, IncidentTeamID: 3}, + } + suite.incidentTeamTagValueRepository.InsertMock.Return(incidentTeamTagValues, nil) + + result, err := suite.incidentTeamTagValueService.AddTagValuesForIncidentTeam(1, []uint{1, 2, 3}) + suite.Nil(err, "Error should be nil") + suite.NotNil(result, "Result should not be nil") + suite.Equal(len(incidentTeamTagValues), len(result), "Result length should be equal to incidentTeamTagValues length") +} + +func (suite *IncidentTeamTagValueServiceSuite) SetupTest() { + logger.InitLogger() + suite.incidentTeamTagValueRepository = *mocks.NewIncidentTeamTagValueRepositoryMock(suite.T()) + suite.incidentTeamTagValueService = NewIncidentTeamTagValueService(&suite.incidentTeamTagValueRepository) +} + +func TestIncidentTeamTagValueService(t *testing.T) { + suite.Run(t, new(IncidentTeamTagValueServiceSuite)) +} diff --git a/service/request/resolve_incident_request.go b/service/request/resolve_incident_request.go index d81a2de..49fd13d 100644 --- a/service/request/resolve_incident_request.go +++ b/service/request/resolve_incident_request.go @@ -2,7 +2,9 @@ package service import ( "github.com/slack-go/slack" + "github.com/spf13/viper" "go.uber.org/zap" + "houston/common/util/env" slackUtil "houston/common/util/slack" stringUtil "houston/common/util/string" "houston/logger" @@ -16,6 +18,7 @@ type ResolveIncidentRequest struct { AdditionalTags []uint `json:"additional_tags"` RcaSummary string `json:"rca_summary"` JiraLinks []string `json:"jira_links"` + IssueTypes []uint `json:"issue_types"` } func ConvertSlackActionToResolveIncidentRequest( @@ -45,6 +48,14 @@ func ConvertSlackActionToResolveIncidentRequest( } resolveIncidentRequest.AdditionalTags = additionalTags + if viper.GetString(env.APP_NAME) == env.QA_APP_NAME { + issueTypes, err := slackUtil.GetMultiValueArrayFromSlackOptionBlock(actions["issue_types_action_id"].SelectedOptions) + if err != nil { + logger.Error("Error while getting issue types", zap.Error(err)) + } + resolveIncidentRequest.IssueTypes = issueTypes + } + resolveIncidentRequest.RcaSummary = actions["set_rca_summary"].Value resolveIncidentRequest.JiraLinks = stringUtil.ExtractCommaSeparatedTrimmedArrayFromString(actions["set_jira_links"].Value) diff --git a/service/request/update_incident.go b/service/request/update_incident.go index c84e353..1cda399 100644 --- a/service/request/update_incident.go +++ b/service/request/update_incident.go @@ -11,4 +11,5 @@ type UpdateIncidentRequest struct { DuplicateOfId uint `json:"duplicateOfId,omitempty"` Justification string `json:"justification,omitempty"` ProductIDs []uint `json:"productIds,omitempty"` + IssueTypes []uint `json:"issueTypes,omitempty"` } diff --git a/service/response/resolve_tag_response.go b/service/response/resolve_tag_response.go index 52cbc60..a1ff9e7 100644 --- a/service/response/resolve_tag_response.go +++ b/service/response/resolve_tag_response.go @@ -1,6 +1,8 @@ package service -import "houston/model/tag" +import ( + "houston/model/tagValue" +) type ResolveTagResponse struct { Tags []Tag `json:"tags"` @@ -18,7 +20,7 @@ type TagValue struct { TagValueName string `json:"tag_value_name"` } -func ConvertTagValueEntityToTagValueResponse(tagValues *[]tag.TagValueEntity) []TagValue { +func ConvertTagValueEntityToTagValueResponse(tagValues *[]tagValue.TagValueEntity) []TagValue { var tagValueResponse []TagValue if tagValues != nil && len(*tagValues) > 0 { for _, tagValue := range *tagValues { diff --git a/service/tag/tag_service.go b/service/tag/tag_service.go index 1e7cb66..6f1e111 100644 --- a/service/tag/tag_service.go +++ b/service/tag/tag_service.go @@ -8,16 +8,18 @@ import ( "houston/logger" "houston/model/customErrors" "houston/model/tag" + "houston/model/tagValue" + tagRepo "houston/repository/tag" serviceResponse "houston/service/response" ) type TagService struct { - tagRepository tag.ITagRepository + tagRepository tagRepo.ITagRepository } func NewTagService(db *gorm.DB) *TagService { return &TagService{ - tagRepository: tag.NewTagRepository(db), + tagRepository: tagRepo.NewTagRepository(db), } } @@ -67,7 +69,7 @@ func (service *TagService) FindTagById(tagId uint) (*tag.TagEntity, error) { return tag, nil } -func (service *TagService) FindTagValueById(tagValueId uint) (*tag.TagValueEntity, error) { +func (service *TagService) FindTagValueById(tagValueId uint) (*tagValue.TagValueEntity, error) { tagValues, err := service.tagRepository.FindTagValuesByIds([]int32{int32(tagValueId)}) if err != nil { logger.Error(fmt.Sprintf("%s Error while getting tag values by id %d", logTag, tagValueId), zap.Error(err)) diff --git a/service/tag/tag_service_interface.go b/service/tag/tag_service_interface.go index 94ee011..b2c51b3 100644 --- a/service/tag/tag_service_interface.go +++ b/service/tag/tag_service_interface.go @@ -2,13 +2,14 @@ package tag import ( "houston/model/tag" + "houston/model/tagValue" serviceResponse "houston/service/response" ) type ITagService interface { GetTagsForIncidentResolution() (*serviceResponse.ResolveTagResponse, error) FindTagById(tagId uint) (*tag.TagEntity, error) - FindTagValueById(tagValueId uint) (*tag.TagValueEntity, error) + FindTagValueById(tagValueId uint) (*tagValue.TagValueEntity, error) GetActiveTags() (*[]tag.TagDTO, error) GetMandatoryActiveTags() (*[]tag.TagDTO, error) } diff --git a/service/tag/tag_service_test.go b/service/tag/tag_service_test.go index 15fa1f2..4a12aad 100644 --- a/service/tag/tag_service_test.go +++ b/service/tag/tag_service_test.go @@ -7,6 +7,7 @@ import ( "houston/mocks" "houston/model/customErrors" "houston/model/tag" + "houston/model/tagValue" "testing" ) @@ -104,7 +105,7 @@ func (suite *TagServiceSuite) Test_FindTagValueById_NilTagValues() { } func (suite *TagServiceSuite) Test_FindTagValueById_EmptyTagValues() { - suite.TagRepository.FindTagValuesByIdsMock.Return(&[]tag.TagValueEntity{}, nil) + suite.TagRepository.FindTagValuesByIdsMock.Return(&[]tagValue.TagValueEntity{}, nil) tagValues, err := suite.TagService.FindTagValueById(1) suite.Nil(tagValues, "tag values must be nil") suite.Error(err, "service must throw error") @@ -165,8 +166,8 @@ func getMockTagDTO() *[]tag.TagDTO { } } -func getMockTagValues() *[]tag.TagValueEntity { - return &[]tag.TagValueEntity{ +func getMockTagValues() *[]tagValue.TagValueEntity { + return &[]tagValue.TagValueEntity{ { TagId: 1, Value: "Mock Value 1", diff --git a/service/tagValue/tag_value_service_impl.go b/service/tagValue/tag_value_service_impl.go new file mode 100644 index 0000000..dd28507 --- /dev/null +++ b/service/tagValue/tag_value_service_impl.go @@ -0,0 +1,27 @@ +package tagValue + +import ( + "fmt" + "houston/common/util/dto" + "houston/logger" + tagValueModel "houston/model/tagValue" + "houston/repository/tagValue" +) + +type tagValueServiceImpl struct { + tagValueRepository tagValue.TagValueRepository +} + +const logTag = "[tag-value-service]" + +func (service *tagValueServiceImpl) GetTagValuesByTagName(tagName string) ([]tagValueModel.TagValueDTO, error) { + logger.Info(fmt.Sprintf("%s fetching tag values for tag name: %s", logTag, tagName)) + + tagValues, err := service.tagValueRepository.GetTagValuesByTagName(tagName) + if err != nil { + logger.Error(fmt.Sprintf("%s Error while fetching tag values for tag name: %s : %v", logTag, tagName, err)) + return nil, err + } + + return dto.ToDtoArray[tagValueModel.TagValueEntity, tagValueModel.TagValueDTO](tagValues), nil +} diff --git a/service/tagValue/tag_value_service_interface.go b/service/tagValue/tag_value_service_interface.go new file mode 100644 index 0000000..9462ef4 --- /dev/null +++ b/service/tagValue/tag_value_service_interface.go @@ -0,0 +1,14 @@ +package tagValue + +import ( + tagValueModel "houston/model/tagValue" + "houston/repository/tagValue" +) + +type TagValueService interface { + GetTagValuesByTagName(tagName string) ([]tagValueModel.TagValueDTO, error) +} + +func NewTagValueService(tagValueRepository tagValue.TagValueRepository) TagValueService { + return &tagValueServiceImpl{tagValueRepository: tagValueRepository} +} diff --git a/service/utils/channel_name_util.go b/service/utils/channel_name_util.go index d93d3b0..b93f9a9 100644 --- a/service/utils/channel_name_util.go +++ b/service/utils/channel_name_util.go @@ -3,6 +3,7 @@ package service import ( "fmt" "github.com/spf13/viper" + envUtil "houston/common/util/env" "strconv" "strings" "unicode" @@ -10,9 +11,16 @@ import ( func getBotNameFromEnv() string { env := viper.GetString("env") + appName := viper.GetString(envUtil.APP_NAME) if env == "prod" { + if appName == envUtil.QA_APP_NAME { + return "qa-tracker" + } return "houston" } else if env == "qa" { + if appName == envUtil.QA_APP_NAME { + return "np-qa-tracker" + } return "test-issue" }