diff --git a/mocks/mock_client.go b/mocks/mock_client.go index b72a6e25..c725dfb6 100644 --- a/mocks/mock_client.go +++ b/mocks/mock_client.go @@ -112,38 +112,6 @@ func (mr *MockClientMockRecorder) GenerateOAuthToken(arg0 interface{}) *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateOAuthToken", reflect.TypeOf((*MockClient)(nil).GenerateOAuthToken), arg0) } -// GetGitRepositories mocks base method -func (m *MockClient) GetGitRepositories(arg0, arg1, arg2 string) (*serializers.GitRepositoriesResponse, int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetGitRepositories", arg0, arg1, arg2) - ret0, _ := ret[0].(*serializers.GitRepositoriesResponse) - ret1, _ := ret[1].(int) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetGitRepositories indicates an expected call of GetGitRepositories -func (mr *MockClientMockRecorder) GetGitRepositories(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGitRepositories", reflect.TypeOf((*MockClient)(nil).GetGitRepositories), arg0, arg1, arg2) -} - -// GetGitRepositoryBranches mocks base method -func (m *MockClient) GetGitRepositoryBranches(arg0, arg1, arg2, arg3 string) (*serializers.GitBranchesResponse, int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetGitRepositoryBranches", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*serializers.GitBranchesResponse) - ret1, _ := ret[1].(int) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetGitRepositoryBranches indicates an expected call of GetGitRepositoryBranches -func (mr *MockClientMockRecorder) GetGitRepositoryBranches(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGitRepositoryBranches", reflect.TypeOf((*MockClient)(nil).GetGitRepositoryBranches), arg0, arg1, arg2, arg3) -} - // GetBuildDetails mocks base method func (m *MockClient) GetBuildDetails(arg0, arg1, arg2, arg3 string) (*serializers.BuildDetails, int, error) { m.ctrl.T.Helper() @@ -154,6 +122,12 @@ func (m *MockClient) GetBuildDetails(arg0, arg1, arg2, arg3 string) (*serializer return ret0, ret1, ret2 } +// GetBuildDetails indicates an expected call of GetBuildDetails +func (mr *MockClientMockRecorder) GetBuildDetails(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBuildDetails", reflect.TypeOf((*MockClient)(nil).GetBuildDetails), arg0, arg1, arg2, arg3) +} + // GetPullRequest mocks base method func (m *MockClient) GetPullRequest(arg0, arg1, arg2, arg3 string) (*serializers.PullRequest, int, error) { m.ctrl.T.Helper() diff --git a/server/constants/routes.go b/server/constants/routes.go index 76dade22..7de94010 100644 --- a/server/constants/routes.go +++ b/server/constants/routes.go @@ -16,8 +16,6 @@ const ( PathGetSubscriptions = "/subscriptions/{team_id:[A-Za-z0-9]+}" PathSubscriptionNotifications = "/notification" PathGetUserChannelsForTeam = "/channels/{team_id:[A-Za-z0-9]+}" - PathGetGitRepositories = "/{organization:[A-Za-z0-9-/ ]+}/{project:[A-Za-z0-9-/ ]+}/repositories" - PathGetGitRepositoryBranches = "/{organization:[A-Za-z0-9-/ ]+}/{project:[A-Za-z0-9-/ ]+}/repositories/{repository:[A-Za-z0-9-/]+}/branches" PathGetSubscriptionFilterPossibleValues = "/subscriptions/filters" // Azure API paths diff --git a/server/plugin/api.go b/server/plugin/api.go index 05e77406..515ba7ed 100644 --- a/server/plugin/api.go +++ b/server/plugin/api.go @@ -50,8 +50,6 @@ func (p *Plugin) InitRoutes() { s.HandleFunc(constants.PathSubscriptionNotifications, p.handleSubscriptionNotifications).Methods(http.MethodPost) s.HandleFunc(constants.PathSubscriptions, p.handleAuthRequired(p.checkOAuth(p.handleDeleteSubscriptions))).Methods(http.MethodDelete) s.HandleFunc(constants.PathGetUserChannelsForTeam, p.handleAuthRequired(p.getUserChannelsForTeam)).Methods(http.MethodGet) - s.HandleFunc(constants.PathGetGitRepositories, p.handleAuthRequired(p.checkOAuth(p.handleGetGitRepositories))).Methods(http.MethodGet) - s.HandleFunc(constants.PathGetGitRepositoryBranches, p.handleAuthRequired(p.checkOAuth(p.handleGetGitRepositoryBranches))).Methods(http.MethodGet) s.HandleFunc(constants.PathGetSubscriptionFilterPossibleValues, p.handleAuthRequired(p.checkOAuth(p.handleGetSubscriptionFilterPossibleValues))).Methods(http.MethodPost) } @@ -297,6 +295,13 @@ func (p *Plugin) handleCreateSubscription(w http.ResponseWriter, r *http.Request MergeResult: body.MergeResult, NotificationType: body.NotificationType, AreaPath: body.AreaPath, + BuildStatus: body.BuildStatus, + BuildPipeline: body.BuildPipeline, + StageName: body.StageName, + ReleasePipeline: body.ReleasePipeline, + ReleaseStatus: body.ReleaseStatus, + ApprovalType: body.ApprovalType, + ApprovalStatus: body.ApprovalStatus, }); isSubscriptionPresent { p.API.LogError(constants.SubscriptionAlreadyPresent, "Error") p.handleError(w, r, &serializers.Error{Code: http.StatusBadRequest, Message: constants.SubscriptionAlreadyPresent}) @@ -354,6 +359,19 @@ func (p *Plugin) handleCreateSubscription(w http.ResponseWriter, r *http.Request NotificationType: body.NotificationType, NotificationTypeName: body.NotificationTypeName, AreaPath: body.AreaPath, + BuildStatus: body.BuildStatus, + BuildPipeline: body.BuildPipeline, + StageName: body.StageName, + ReleasePipeline: body.ReleasePipeline, + ReleaseStatus: body.ReleaseStatus, + ApprovalType: body.ApprovalType, + ApprovalStatus: body.ApprovalStatus, + BuildStatusName: body.BuildStatusName, + StageNameValue: body.StageNameValue, + ReleasePipelineName: body.ReleasePipelineName, + ReleaseStatusName: body.ReleaseStatusName, + ApprovalTypeName: body.ApprovalTypeName, + ApprovalStatusName: body.ApprovalStatusName, }); storeErr != nil { p.API.LogError("Error in creating a subscription", "Error", storeErr.Error()) p.handleError(w, r, &serializers.Error{Code: http.StatusInternalServerError, Message: storeErr.Error()}) @@ -437,7 +455,14 @@ func (p *Plugin) handleGetSubscriptions(w http.ResponseWriter, r *http.Request) subscriptionByProject[i].PushedByName+ subscriptionByProject[i].MergeResultName+ subscriptionByProject[i].NotificationTypeName+ - subscriptionByProject[i].AreaPath < + subscriptionByProject[i].AreaPath+ + subscriptionByProject[i].ReleasePipelineName+ + subscriptionByProject[i].BuildPipeline+ + subscriptionByProject[i].BuildStatusName+ + subscriptionByProject[i].ApprovalStatusName+ + subscriptionByProject[i].ApprovalTypeName+ + subscriptionByProject[i].StageNameValue+ + subscriptionByProject[i].ReleaseStatusName < subscriptionByProject[j].ChannelName+ subscriptionByProject[j].EventType+ subscriptionByProject[j].TargetBranch+ @@ -446,7 +471,14 @@ func (p *Plugin) handleGetSubscriptions(w http.ResponseWriter, r *http.Request) subscriptionByProject[j].PushedByName+ subscriptionByProject[j].MergeResultName+ subscriptionByProject[j].NotificationTypeName+ - subscriptionByProject[j].AreaPath + subscriptionByProject[j].AreaPath+ + subscriptionByProject[i].ReleasePipelineName+ + subscriptionByProject[i].BuildPipeline+ + subscriptionByProject[i].BuildStatusName+ + subscriptionByProject[i].ApprovalStatusName+ + subscriptionByProject[i].ApprovalTypeName+ + subscriptionByProject[i].StageNameValue+ + subscriptionByProject[i].ReleaseStatusName }) filteredSubscriptionList, filteredSubscriptionErr := p.GetSubscriptionsForAccessibleChannelsOrProjects(subscriptionByProject, teamID, mattermostUserID) @@ -881,6 +913,13 @@ func (p *Plugin) handleDeleteSubscriptions(w http.ResponseWriter, r *http.Reques MergeResult: body.MergeResult, NotificationType: body.NotificationType, AreaPath: body.AreaPath, + BuildStatus: body.BuildStatus, + BuildPipeline: body.BuildPipeline, + StageName: body.StageName, + ReleasePipeline: body.ReleasePipeline, + ReleaseStatus: body.ReleaseStatus, + ApprovalType: body.ApprovalType, + ApprovalStatus: body.ApprovalStatus, }) if !isSubscriptionPresent { p.API.LogError(constants.SubscriptionNotFound) @@ -1039,60 +1078,6 @@ func (p *Plugin) handleGetUserAccountDetails(w http.ResponseWriter, r *http.Requ p.writeJSON(w, &userDetails) } -func (p *Plugin) handleGetGitRepositories(w http.ResponseWriter, r *http.Request) { - mattermostUserID := r.Header.Get(constants.HeaderMattermostUserID) - - pathParams := mux.Vars(r) - organization := pathParams[constants.PathParamOrganization] - project := pathParams[constants.PathParamProject] - - if len(strings.TrimSpace(organization)) == 0 || len(strings.TrimSpace(project)) == 0 { - p.API.LogError(constants.ErrorOrganizationOrProjectQueryParam) - p.handleError(w, r, &serializers.Error{Code: http.StatusBadRequest, Message: constants.ErrorOrganizationOrProjectQueryParam}) - return - } - - response, statusCode, err := p.Client.GetGitRepositories(organization, project, mattermostUserID) - if err != nil { - p.API.LogError("Error in fetching git repositories", err.Error()) - p.handleError(w, r, &serializers.Error{Code: statusCode, Message: err.Error()}) - return - } - - p.writeJSON(w, response.Value) -} - -func (p *Plugin) handleGetGitRepositoryBranches(w http.ResponseWriter, r *http.Request) { - mattermostUserID := r.Header.Get(constants.HeaderMattermostUserID) - - pathParams := mux.Vars(r) - organization := strings.TrimSpace(pathParams[constants.PathParamOrganization]) - project := strings.TrimSpace(pathParams[constants.PathParamProject]) - repository := strings.TrimSpace(pathParams[constants.PathParamRepository]) - - if len(organization) == 0 || len(project) == 0 || len(repository) == 0 { - p.API.LogError(constants.ErrorRepositoryPathParam) - p.handleError(w, r, &serializers.Error{Code: http.StatusBadRequest, Message: constants.ErrorRepositoryPathParam}) - return - } - - response, statusCode, err := p.Client.GetGitRepositoryBranches(organization, project, repository, mattermostUserID) - if err != nil { - p.API.LogError("Error in fetching git repository branches", err.Error()) - p.handleError(w, r, &serializers.Error{Code: statusCode, Message: err.Error()}) - return - } - - // Azure DevOps returns branch name as "refs/heads/", but we need to use only "" so, remove unused part from the name - for _, value := range response.Value { - if strings.Contains(value.Name, "refs/heads/") && len(value.Name) > 11 { - value.Name = value.Name[11:] - } - } - - p.writeJSON(w, response.Value) -} - func (p *Plugin) handleGetSubscriptionFilterPossibleValues(w http.ResponseWriter, r *http.Request) { mattermostUserID := r.Header.Get(constants.HeaderMattermostUserID) diff --git a/server/plugin/api_test.go b/server/plugin/api_test.go index 18afd693..21ce06b8 100644 --- a/server/plugin/api_test.go +++ b/server/plugin/api_test.go @@ -23,7 +23,6 @@ import ( "github.com/mattermost/mattermost-plugin-azure-devops/mocks" "github.com/mattermost/mattermost-plugin-azure-devops/server/constants" "github.com/mattermost/mattermost-plugin-azure-devops/server/serializers" - "github.com/mattermost/mattermost-plugin-azure-devops/server/testutils" ) type panicHandler struct { @@ -961,154 +960,3 @@ func TestGetUserChannelsForTeam(t *testing.T) { }) } } - -func TestHandleGetGitRepositories(t *testing.T) { - defer monkey.UnpatchAll() - mockAPI := &plugintest.API{} - mockCtrl := gomock.NewController(t) - mockedClient := mocks.NewMockClient(mockCtrl) - p := setupMockPlugin(mockAPI, nil, mockedClient) - for _, testCase := range []struct { - description string - organization string - project string - getGitRepositoriesErr error - statusCode int - }{ - { - description: "HandleGetGitRepositories: valid", - organization: "mockOrganization", - project: "mockProject", - statusCode: http.StatusOK, - }, - { - description: "HandleGetGitRepositories: Invalid organization or project name", - organization: "mockOrganization", - statusCode: http.StatusBadRequest, - }, - { - description: "HandleGetGitRepositories: GetGitRepositories returns error", - organization: "mockOrganization", - project: "mockProject", - getGitRepositoriesErr: errors.New("failed to get git repository branches"), - statusCode: http.StatusInternalServerError, - }, - } { - t.Run(testCase.description, func(t *testing.T) { - mockAPI.On("LogError", testutils.GetMockArgumentsWithType("string", 3)...) - - if testCase.statusCode == http.StatusInternalServerError || testCase.statusCode == http.StatusOK { - mockedClient.EXPECT().GetGitRepositories(gomock.Any(), gomock.Any(), gomock.Any()).Return(&serializers.GitRepositoriesResponse{}, testCase.statusCode, testCase.getGitRepositoriesErr) - } - - req := httptest.NewRequest(http.MethodGet, "/mockPath", bytes.NewBufferString(`{}`)) - req.Header.Add(constants.HeaderMattermostUserID, "test-userID") - - pathParams := map[string]string{ - "organization": testCase.organization, - "project": testCase.project, - } - - req = mux.SetURLVars(req, pathParams) - - w := httptest.NewRecorder() - p.handleGetGitRepositories(w, req) - resp := w.Result() - assert.Equal(t, testCase.statusCode, resp.StatusCode) - }) - } -} - -func TestHandleGetGitRepositoryBranches(t *testing.T) { - defer monkey.UnpatchAll() - mockAPI := &plugintest.API{} - mockCtrl := gomock.NewController(t) - mockedClient := mocks.NewMockClient(mockCtrl) - p := setupMockPlugin(mockAPI, nil, mockedClient) - for _, testCase := range []struct { - description string - organization string - repository string - project string - getGitRepositoryBranchesErr error - statusCode int - getGitRepositoryBranchesResponse *serializers.GitBranchesResponse - expectedResponse []*serializers.GitBranch - expectedErrorResponse interface{} - }{ - { - description: "HandleGetGitRepositoryBranches: valid", - organization: "mockOrganization", - project: "mockProject", - repository: "mockRepository", - statusCode: http.StatusOK, - getGitRepositoryBranchesResponse: &serializers.GitBranchesResponse{ - Value: testutils.GetGitBranchesPayload(), - }, - expectedResponse: []*serializers.GitBranch{ - { - ID: "mockID-1", - Name: "mockName-1", - }, - { - ID: "mockID-2", - Name: "mockName-2", - }, - }, - }, - { - description: "HandleGetGitRepositoryBranches: Invalid organization, project or repository params", - organization: "mockOrganization", - project: "mockProject", - statusCode: http.StatusBadRequest, - expectedErrorResponse: map[string]interface{}{"Error": constants.ErrorRepositoryPathParam}, - }, - { - description: "HandleGetGitRepositoryBranches: GetGitRepositoryBranches returns error", - organization: "mockOrganization", - project: "mockProject", - repository: "mockRepository", - getGitRepositoryBranchesErr: errors.New("failed to get the git repository branches for a project"), - statusCode: http.StatusInternalServerError, - expectedErrorResponse: map[string]interface{}{"Error": "failed to get the git repository branches for a project"}, - }, - } { - t.Run(testCase.description, func(t *testing.T) { - mockAPI.On("LogError", testutils.GetMockArgumentsWithType("string", 3)...) - - if testCase.statusCode == http.StatusInternalServerError || testCase.statusCode == http.StatusOK { - mockedClient.EXPECT().GetGitRepositoryBranches(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testCase.getGitRepositoryBranchesResponse, testCase.statusCode, testCase.getGitRepositoryBranchesErr) - } - - req := httptest.NewRequest(http.MethodGet, "/mockPath", bytes.NewBufferString(`{}`)) - req.Header.Add(constants.HeaderMattermostUserID, "mockUserID") - - pathParams := map[string]string{ - "organization": testCase.organization, - "project": testCase.project, - "repository": testCase.repository, - } - - req = mux.SetURLVars(req, pathParams) - - w := httptest.NewRecorder() - p.handleGetGitRepositoryBranches(w, req) - resp := w.Result() - assert.Equal(t, testCase.statusCode, resp.StatusCode) - - if testCase.expectedErrorResponse != nil { - var actualResponse interface{} - err := json.NewDecoder(resp.Body).Decode(&actualResponse) - require.Nil(t, err) - assert.Equal(t, testCase.expectedErrorResponse, actualResponse) - } - - if testCase.expectedResponse != nil { - var actualResponse []*serializers.GitBranch - err := json.NewDecoder(resp.Body).Decode(&actualResponse) - require.Nil(t, err) - assert.Equal(t, testCase.expectedResponse, actualResponse) - } - }) - } -} diff --git a/server/plugin/client.go b/server/plugin/client.go index 3e40881a..820acd86 100644 --- a/server/plugin/client.go +++ b/server/plugin/client.go @@ -25,8 +25,6 @@ type Client interface { CreateSubscription(body *serializers.CreateSubscriptionRequestPayload, project *serializers.ProjectDetails, channelID, pluginURL, mattermostUserID string) (*serializers.SubscriptionValue, int, error) DeleteSubscription(organization, subscriptionID, mattermostUserID string) (int, error) CheckIfUserIsProjectAdmin(organizationName, projectID, pluginURL, mattermostUserID string) (int, error) - GetGitRepositories(organization, projectName, mattermostUserID string) (*serializers.GitRepositoriesResponse, int, error) - GetGitRepositoryBranches(organization, projectName, repository, mattermostUserID string) (*serializers.GitBranchesResponse, int, error) GetBuildDetails(organization, projectName, buildID, mattermostUserID string) (*serializers.BuildDetails, int, error) GetSubscriptionFilterPossibleValues(request *serializers.GetSubscriptionFilterPossibleValuesRequestPayload, mattermostUserID string) (*serializers.SubscriptionFilterPossibleValuesResponseFromClient, int, error) } @@ -197,6 +195,13 @@ func (c *client) CreateSubscription(body *serializers.CreateSubscriptionRequestP PullRequestCreatedBy: body.PullRequestCreatedBy, PullRequestReviewersContains: body.PullRequestReviewersContains, NotificationType: body.NotificationType, + BuildStatus: body.BuildStatus, + DefinitionName: body.BuildPipeline, + ReleaseEnvironmentID: body.StageName, + ReleaseDefinitionID: body.ReleasePipeline, + ReleaseEnvironmentStatus: body.ReleaseStatus, + ReleaseApprovalType: body.ApprovalType, + ReleaseApprovalStatus: body.ApprovalStatus, }, } @@ -251,30 +256,6 @@ func (c *client) DeleteSubscription(organization, subscriptionID, mattermostUser return statusCode, nil } -func (c *client) GetGitRepositories(organization, projectName, mattermostUserID string) (*serializers.GitRepositoriesResponse, int, error) { - getGitRepositoriesURL := fmt.Sprintf(constants.GetGitRepositories, organization, projectName) - - var gitRepositories *serializers.GitRepositoriesResponse - _, statusCode, err := c.CallJSON(c.plugin.getConfiguration().AzureDevopsAPIBaseURL, getGitRepositoriesURL, http.MethodGet, mattermostUserID, nil, &gitRepositories, nil) - if err != nil { - return nil, statusCode, errors.Wrap(err, "failed to get the git repositories for a project") - } - - return gitRepositories, statusCode, nil -} - -func (c *client) GetGitRepositoryBranches(organization, projectName, repository, mattermostUserID string) (*serializers.GitBranchesResponse, int, error) { - getGitRepositoriesURL := fmt.Sprintf(constants.GetGitRepositoryBranches, organization, projectName, repository) - - var gitBranchesResponse *serializers.GitBranchesResponse - _, statusCode, err := c.CallJSON(c.plugin.getConfiguration().AzureDevopsAPIBaseURL, getGitRepositoriesURL, http.MethodGet, mattermostUserID, nil, &gitBranchesResponse, nil) - if err != nil { - return nil, statusCode, errors.Wrap(err, "failed to get the git repository branches for a project") - } - - return gitBranchesResponse, statusCode, nil -} - func (c *client) GetSubscriptionFilterPossibleValues(request *serializers.GetSubscriptionFilterPossibleValuesRequestPayload, mattermostUserID string) (*serializers.SubscriptionFilterPossibleValuesResponseFromClient, int, error) { getSubscriptionFilterValuesURL := fmt.Sprintf(constants.GetSubscriptionFilterPossibleValues, request.Organization) @@ -310,8 +291,8 @@ func (c *client) GetSubscriptionFilterPossibleValues(request *serializers.GetSub if strings.Contains(request.EventType, constants.EventTypeRelease) { subscriptionFiltersRequest.Subscription.PublisherInputs = serializers.PublisherInputsGeneric{ - ProjectID: request.ProjectID, - ReleasePipeline: request.ReleasePipelineID, + ProjectID: request.ProjectID, + ReleaseDefinitionID: request.ReleasePipelineID, } } diff --git a/server/plugin/client_test.go b/server/plugin/client_test.go index cc82e9eb..1ddd59d7 100644 --- a/server/plugin/client_test.go +++ b/server/plugin/client_test.go @@ -317,81 +317,6 @@ func TestCall(t *testing.T) { } } -func TestGetGitRepositories(t *testing.T) { - defer monkey.UnpatchAll() - mockAPI := &plugintest.API{} - p := setupTestPlugin(mockAPI) - for _, testCase := range []struct { - description string - err error - statusCode int - }{ - { - description: "GetGitRepositories: valid", - err: nil, - statusCode: http.StatusOK, - }, - { - description: "GetGitRepositories: with error", - err: errors.New("mock-error"), - statusCode: http.StatusInternalServerError, - }, - } { - t.Run(testCase.description, func(t *testing.T) { - monkey.PatchInstanceMethod(reflect.TypeOf(&client{}), "Call", func(_ *client, basePath, method, path, contentType, mattermostUserID string, inBody io.Reader, out interface{}, formValues url.Values) (responseData []byte, statusCode int, err error) { - return nil, testCase.statusCode, testCase.err - }) - - _, statusCode, err := p.Client.GetGitRepositories("mockOrganization", "mockProjectName", "mockMattermostUSerID") - - if testCase.err != nil { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - - assert.Equal(t, testCase.statusCode, statusCode) - }) - } -} - -func TestGetGitRepositoryBranches(t *testing.T) { - defer monkey.UnpatchAll() - p := setupTestPlugin(&plugintest.API{}) - for _, testCase := range []struct { - description string - err error - statusCode int - }{ - { - description: "GetGitRepositoryBranches: valid", - err: nil, - statusCode: http.StatusOK, - }, - { - description: "GetGitRepositoryBranches: with error", - err: errors.New("mock-error"), - statusCode: http.StatusInternalServerError, - }, - } { - t.Run(testCase.description, func(t *testing.T) { - monkey.PatchInstanceMethod(reflect.TypeOf(&client{}), "Call", func(_ *client, basePath, method, path, contentType, mattermostUserID string, inBody io.Reader, out interface{}, formValues url.Values) (responseData []byte, statusCode int, err error) { - return nil, testCase.statusCode, testCase.err - }) - - _, statusCode, err := p.Client.GetGitRepositoryBranches("mockOrganization", "mockProjectName", "mockRepository", "mockMattermostUSerID") - - if testCase.err != nil { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - - assert.Equal(t, testCase.statusCode, statusCode) - }) - } -} - func setupTestPlugin(api *plugintest.API) *Plugin { p := Plugin{} p.API = api diff --git a/server/plugin/utils.go b/server/plugin/utils.go index 97a358b8..9924c29b 100644 --- a/server/plugin/utils.go +++ b/server/plugin/utils.go @@ -187,7 +187,7 @@ func (p *Plugin) IsProjectLinked(projectList []serializers.ProjectDetails, proje func (p *Plugin) IsSubscriptionPresent(subscriptionList []*serializers.SubscriptionDetails, subscription *serializers.SubscriptionDetails) (*serializers.SubscriptionDetails, bool) { for _, a := range subscriptionList { - if a.ProjectName == subscription.ProjectName && a.OrganizationName == subscription.OrganizationName && a.ChannelID == subscription.ChannelID && a.EventType == subscription.EventType && a.Repository == subscription.Repository && a.TargetBranch == subscription.TargetBranch && a.PullRequestCreatedBy == subscription.PullRequestCreatedBy && a.PullRequestReviewersContains == subscription.PullRequestReviewersContains && a.PushedBy == subscription.PushedBy && a.MergeResult == subscription.MergeResult && a.NotificationType == subscription.NotificationType && a.AreaPath == subscription.AreaPath { + if a.ProjectName == subscription.ProjectName && a.OrganizationName == subscription.OrganizationName && a.ChannelID == subscription.ChannelID && a.EventType == subscription.EventType && a.Repository == subscription.Repository && a.TargetBranch == subscription.TargetBranch && a.PullRequestCreatedBy == subscription.PullRequestCreatedBy && a.PullRequestReviewersContains == subscription.PullRequestReviewersContains && a.PushedBy == subscription.PushedBy && a.MergeResult == subscription.MergeResult && a.NotificationType == subscription.NotificationType && a.AreaPath == subscription.AreaPath && a.BuildPipeline == subscription.BuildPipeline && a.BuildStatus == subscription.BuildStatus && a.StageName == subscription.StageName && a.ReleasePipeline == subscription.ReleasePipeline && a.ReleaseStatus == subscription.ReleaseStatus && a.ApprovalType == subscription.ApprovalType && a.ApprovalStatus == subscription.ApprovalStatus { return a, true } } diff --git a/server/serializers/subscriptions.go b/server/serializers/subscriptions.go index f88b841e..fa8bb779 100644 --- a/server/serializers/subscriptions.go +++ b/server/serializers/subscriptions.go @@ -24,7 +24,13 @@ type PublisherInputsGeneric struct { PushedBy string `json:"pushedBy,omitempty"` MergeResult string `json:"mergeResult,omitempty"` NotificationType string `json:"notificationType,omitempty"` - ReleasePipeline string `json:"releaseDefinitionId,omitempty"` + DefinitionName string `json:"definitionName,omitempty"` + BuildStatus string `json:"buildStatus,omitempty"` + ReleaseDefinitionID string `json:"releaseDefinitionId,omitempty"` + ReleaseEnvironmentID string `json:"releaseEnvironmentId,omitempty"` + ReleaseApprovalType string `json:"releaseApprovalType,omitempty"` + ReleaseApprovalStatus string `json:"releaseApprovalStatus,omitempty"` + ReleaseEnvironmentStatus string `json:"releaseEnvironmentStatus,omitempty"` } type ConsumerInputs struct { @@ -69,6 +75,19 @@ type CreateSubscriptionRequestPayload struct { NotificationType string `json:"notificationType"` NotificationTypeName string `json:"notificationTypeName"` AreaPath string `json:"areaPath"` + BuildPipeline string `json:"buildPipeline"` + BuildStatus string `json:"buildStatus"` + BuildStatusName string `json:"buildStatusName"` + ReleasePipeline string `json:"releasePipeline"` + ReleasePipelineName string `json:"releasePipelineName"` + StageName string `json:"stageName"` + StageNameValue string `json:"stageNameValue"` + ApprovalType string `json:"approvalType"` + ApprovalTypeName string `json:"approvalTypeName"` + ApprovalStatus string `json:"approvalStatus"` + ApprovalStatusName string `json:"approvalStatusName"` + ReleaseStatus string `json:"releaseStatus"` + ReleaseStatusName string `json:"releaseStatusName"` } type GetSubscriptionFilterPossibleValuesRequestPayload struct { @@ -139,6 +158,19 @@ type SubscriptionDetails struct { NotificationType string `json:"notificationType"` NotificationTypeName string `json:"notificationTypeName"` AreaPath string `json:"areaPath"` + BuildPipeline string `json:"buildPipeline"` + BuildStatus string `json:"buildStatus"` + BuildStatusName string `json:"buildStatusName"` + ReleasePipeline string `json:"releasePipeline"` + ReleasePipelineName string `json:"releasePipelineName"` + StageName string `json:"stageName"` + StageNameValue string `json:"stageNameValue"` + ApprovalType string `json:"approvalType"` + ApprovalTypeName string `json:"approvalTypeName"` + ApprovalStatus string `json:"approvalStatus"` + ApprovalStatusName string `json:"approvalStatusName"` + ReleaseStatus string `json:"releaseStatus"` + ReleaseStatusName string `json:"releaseStatusName"` } type DetailedMessage struct { @@ -261,6 +293,13 @@ type DeleteSubscriptionRequestPayload struct { MergeResult string `json:"mergeResult"` NotificationType string `json:"notificationType"` AreaPath string `json:"areaPath"` + BuildPipeline string `json:"buildPipeline"` + BuildStatus string `json:"buildStatus"` + ReleasePipeline string `json:"releasePipeline"` + StageName string `json:"stageName"` + ApprovalType string `json:"approvalType"` + ApprovalStatus string `json:"approvalStatus"` + ReleaseStatus string `json:"releaseStatus"` } func GetSubscriptionFilterPossibleValuesRequestPayloadFromJSON(data io.Reader) (*GetSubscriptionFilterPossibleValuesRequestPayload, error) { diff --git a/server/store/subscriptions.go b/server/store/subscriptions.go index b121dcc5..defec1e9 100644 --- a/server/store/subscriptions.go +++ b/server/store/subscriptions.go @@ -58,7 +58,7 @@ func (subscriptionList *SubscriptionList) AddSubscription(userID string, subscri subscriptionList.ByMattermostUserID[userID] = make(SubscriptionListMap) } - subscriptionKey := GetSubscriptionKey(userID, subscription.ProjectName, subscription.ChannelID, subscription.EventType, subscription.Repository, subscription.TargetBranch, subscription.PullRequestCreatedBy, subscription.PullRequestReviewersContains, subscription.PushedBy, subscription.MergeResult, subscription.NotificationType, subscription.AreaPath) + subscriptionKey := GetSubscriptionKey(userID, subscription.ProjectName, subscription.ChannelID, subscription.EventType, subscription.Repository, subscription.TargetBranch, subscription.PullRequestCreatedBy, subscription.PullRequestReviewersContains, subscription.PushedBy, subscription.MergeResult, subscription.NotificationType, subscription.AreaPath, subscription.ReleasePipeline, subscription.BuildPipeline, subscription.BuildStatus, subscription.ApprovalType, subscription.ApprovalStatus, subscription.StageName, subscription.ReleaseStatus) subscriptionListValue := serializers.SubscriptionDetails{ MattermostUserID: userID, ProjectName: subscription.ProjectName, @@ -85,6 +85,19 @@ func (subscriptionList *SubscriptionList) AddSubscription(userID string, subscri NotificationType: subscription.NotificationType, NotificationTypeName: subscription.NotificationTypeName, AreaPath: subscription.AreaPath, + BuildStatus: subscription.BuildStatus, + BuildPipeline: subscription.BuildPipeline, + StageName: subscription.StageName, + ReleasePipeline: subscription.ReleasePipeline, + ReleaseStatus: subscription.ReleaseStatus, + ApprovalType: subscription.ApprovalType, + ApprovalStatus: subscription.ApprovalStatus, + BuildStatusName: subscription.BuildStatusName, + StageNameValue: subscription.StageNameValue, + ReleasePipelineName: subscription.ReleasePipelineName, + ReleaseStatusName: subscription.ReleaseStatusName, + ApprovalTypeName: subscription.ApprovalTypeName, + ApprovalStatusName: subscription.ApprovalStatusName, } subscriptionList.ByMattermostUserID[userID][subscriptionKey] = subscriptionListValue } @@ -133,7 +146,7 @@ func deleteSubscriptionAtomicModify(subscription *serializers.SubscriptionDetail if err != nil { return nil, err } - subscriptionKey := GetSubscriptionKey(subscription.MattermostUserID, subscription.ProjectName, subscription.ChannelID, subscription.EventType, subscription.Repository, subscription.TargetBranch, subscription.PullRequestCreatedBy, subscription.PullRequestReviewersContains, subscription.PushedBy, subscription.MergeResult, subscription.NotificationType, subscription.AreaPath) + subscriptionKey := GetSubscriptionKey(subscription.MattermostUserID, subscription.ProjectName, subscription.ChannelID, subscription.EventType, subscription.Repository, subscription.TargetBranch, subscription.PullRequestCreatedBy, subscription.PullRequestReviewersContains, subscription.PushedBy, subscription.MergeResult, subscription.NotificationType, subscription.AreaPath, subscription.ReleasePipeline, subscription.BuildPipeline, subscription.BuildStatus, subscription.ApprovalType, subscription.ApprovalStatus, subscription.StageName, subscription.ReleaseStatus) subscriptionList.DeleteSubscriptionByKey(subscription.MattermostUserID, subscriptionKey) modifiedBytes, marshalErr := json.Marshal(subscriptionList) if marshalErr != nil { diff --git a/server/store/subscriptions_test.go b/server/store/subscriptions_test.go index 4bec699a..141f0b59 100644 --- a/server/store/subscriptions_test.go +++ b/server/store/subscriptions_test.go @@ -249,7 +249,7 @@ func TestDeleteSubscriptionAtomicModify(t *testing.T) { }, } { t.Run(testCase.description, func(t *testing.T) { - monkey.Patch(GetSubscriptionKey, func(string, string, string, string, string, string, string, string, string, string, string, string) string { + monkey.Patch(GetSubscriptionKey, func(string, string, string, string, string, string, string, string, string, string, string, string, string, string, string, string, string, string, string) string { return "mockSubscriptionKey" }) monkey.Patch(SubscriptionListFromJSON, func([]byte) (*SubscriptionList, error) { diff --git a/server/store/utils.go b/server/store/utils.go index f82925cc..86a02fda 100644 --- a/server/store/utils.go +++ b/server/store/utils.go @@ -136,8 +136,8 @@ func GetSubscriptionListMapKey() string { return constants.SubscriptionPrefix } -func GetSubscriptionKey(mattermostUserID, projectID, channelID, eventType, repository, targetBranch, pullRequestCreatedBy, pullRequestReviewersContains, pushedBy, mergeResult, notificationType, areaPath string) string { - return GetKeyMD5Hash(fmt.Sprintf("%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s", mattermostUserID, projectID, channelID, eventType, repository, targetBranch, pullRequestCreatedBy, pullRequestReviewersContains, pushedBy, mergeResult, notificationType, areaPath)) +func GetSubscriptionKey(mattermostUserID, projectID, channelID, eventType, repository, targetBranch, pullrequestCreatedBy, pullRequestReviewersContains, pushedBy, mergeResult, notificationType, areaPath, releasePipeline, buildPipeline, buildStatus, approvalType, approvalStatus, stageName, releaseStatus string) string { + return GetKeyMD5Hash(fmt.Sprintf("%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s_%s", mattermostUserID, projectID, channelID, eventType, repository, targetBranch, pullrequestCreatedBy, pullRequestReviewersContains, pushedBy, mergeResult, notificationType, areaPath, releasePipeline, buildPipeline, buildStatus, approvalType, approvalStatus, stageName, releaseStatus)) } // GetKeyMD5Hash can be used to create a md5 hash from a string diff --git a/webapp/src/components/card/subscription/index.tsx b/webapp/src/components/card/subscription/index.tsx index db2fcdc2..23d816f1 100644 --- a/webapp/src/components/card/subscription/index.tsx +++ b/webapp/src/components/card/subscription/index.tsx @@ -17,7 +17,7 @@ type SubscriptionCardProps = { subscriptionDetails: SubscriptionDetails } -const SubscriptionCard = ({handleDeleteSubscrption, subscriptionDetails: {channelType, eventType, serviceType, channelName, createdBy, targetBranch, repositoryName, pullRequestCreatedByName, pullRequestReviewersContainsName, pushedByName, mergeResultName, notificationTypeName, areaPath}, subscriptionDetails}: SubscriptionCardProps) => { +const SubscriptionCard = ({handleDeleteSubscrption, subscriptionDetails: {channelType, eventType, serviceType, channelName, createdBy, targetBranch, repositoryName, pullRequestCreatedByName, pullRequestReviewersContainsName, pushedByName, mergeResultName, notificationTypeName, areaPath, releasePipelineName, buildPipeline, buildStatusName, approvalStatusName, approvalTypeName, releaseStatusName, stageNameValue}, subscriptionDetails}: SubscriptionCardProps) => { const showFilter = areaPath || repositoryName || targetBranch || pullRequestCreatedByName || pullRequestReviewersContainsName || pushedByName || mergeResultName || notificationTypeName; return ( @@ -90,6 +90,13 @@ const SubscriptionCard = ({handleDeleteSubscrption, subscriptionDetails: {channe {pushedByName && } {mergeResultName && } {notificationTypeName && } + {releasePipelineName && } + {buildPipeline && } + {buildStatusName && } + {stageNameValue && } + {approvalStatusName && } + {approvalTypeName && } + {releaseStatusName && } ) diff --git a/webapp/src/containers/Rhs/projectDetails/index.tsx b/webapp/src/containers/Rhs/projectDetails/index.tsx index d065d67f..e5dcc120 100644 --- a/webapp/src/containers/Rhs/projectDetails/index.tsx +++ b/webapp/src/containers/Rhs/projectDetails/index.tsx @@ -110,6 +110,19 @@ const ProjectDetails = memo((projectDetails: ProjectDetails) => { notificationType: subscriptionDetails.notificationType, notificationTypeName: subscriptionDetails.notificationTypeName, areaPath: subscriptionDetails.areaPath, + buildPipeline: subscriptionDetails.buildPipeline, + releasePipeline: subscriptionDetails.releasePipeline, + buildStatus: subscriptionDetails.buildStatus, + approvalStatus: subscriptionDetails.approvalStatus, + approvalType: subscriptionDetails.approvalType, + stageName: subscriptionDetails.stageName, + releaseStatus: subscriptionDetails.releaseStatus, + releasePipelineName: subscriptionDetails.releasePipelineName, + buildStatusName: subscriptionDetails.buildStatusName, + approvalStatusName: subscriptionDetails.approvalStatusName, + approvalTypeName: subscriptionDetails.approvalTypeName, + stageNameValue: subscriptionDetails.stageNameValue, + releaseStatusName: subscriptionDetails.releaseStatusName, }); setDeleteConfirmationModalError(null); setShowSubscriptionConfirmationModal(true); diff --git a/webapp/src/containers/modals/SubscribeModal/index.tsx b/webapp/src/containers/modals/SubscribeModal/index.tsx index 152a6b51..9badb7ae 100644 --- a/webapp/src/containers/modals/SubscribeModal/index.tsx +++ b/webapp/src/containers/modals/SubscribeModal/index.tsx @@ -317,36 +317,42 @@ const SubscribeModal = () => { setSpecificFieldValue({ ...formFields, buildStatus: newValue === filterLabelValuePairAll.value ? '' : newValue, + buildStatusName: status === filterLabelValuePairAll.value ? '' : status, }); const handleSetReleasePipelineFilter = (newValue: string, pipelineName?: string) => setSpecificFieldValue({ ...formFields, releasePipeline: newValue === filterLabelValuePairAll.value ? '' : newValue, + releasePipelineName: pipelineName === filterLabelValuePairAll.value ? '' : pipelineName, }); const handleSetStageNameFilter = (newValue: string, stageName?: string) => setSpecificFieldValue({ ...formFields, stageName: newValue === filterLabelValuePairAll.value ? '' : newValue, + stageNameValue: stageName === filterLabelValuePairAll.value ? '' : stageName, }); const handleSetApprovalTypeFilter = (newValue: string, type?: string) => setSpecificFieldValue({ ...formFields, approvalType: newValue === filterLabelValuePairAll.value ? '' : newValue, + approvalTypeName: type === filterLabelValuePairAll.value ? '' : type, }); const handleSetApprovalStatusFilter = (newValue: string, status?: string) => setSpecificFieldValue({ ...formFields, approvalStatus: newValue === filterLabelValuePairAll.value ? '' : newValue, + approvalStatusName: status === filterLabelValuePairAll.value ? '' : status, }); const handleSetReleaseStatusFilter = (newValue: string, status?: string) => setSpecificFieldValue({ ...formFields, releaseStatus: newValue === filterLabelValuePairAll.value ? '' : newValue, + releaseStatusName: status === filterLabelValuePairAll.value ? '' : status, }); const {isLoading: isCreateSubscriptionLoading, isError, error} = getApiState(pluginConstants.pluginApiServiceConfigs.createSubscription.apiServiceName, formFields as APIRequestPayload); diff --git a/webapp/src/pluginConstants/apiService.ts b/webapp/src/pluginConstants/apiService.ts index 97269f21..0fc671e4 100644 --- a/webapp/src/pluginConstants/apiService.ts +++ b/webapp/src/pluginConstants/apiService.ts @@ -45,16 +45,6 @@ export const pluginApiServiceConfigs: Record = method: 'DELETE', apiServiceName: 'deleteSubscription', }, - getRepositories: { - path: '/:organization/:project/repositories', - method: 'GET', - apiServiceName: 'getRepositories', - }, - getRepositoryBranches: { - path: '/:organization/:project/repositories/:repository/branches', - method: 'GET', - apiServiceName: 'getRepositoryBranches', - }, getSubscriptionFilters: { path: '/subscriptions/filters', method: 'POST', diff --git a/webapp/src/pluginConstants/form.ts b/webapp/src/pluginConstants/form.ts index 0577c60d..327d15c0 100644 --- a/webapp/src/pluginConstants/form.ts +++ b/webapp/src/pluginConstants/form.ts @@ -301,34 +301,64 @@ export const subscriptionModal: Record({ - query: (pathParams) => ({ - headers: {[Constants.common.HeaderCSRFToken]: Cookies.get(Constants.common.MMCSRF)}, - url: addPathParamsToApiUrl(Constants.pluginApiServiceConfigs.getRepositories.path, pathParams as unknown as Record), - method: Constants.pluginApiServiceConfigs.getRepositories.method, - }), - }), - [Constants.pluginApiServiceConfigs.getRepositoryBranches.apiServiceName]: builder.query({ - query: (pathParams) => ({ - headers: {[Constants.common.HeaderCSRFToken]: Cookies.get(Constants.common.MMCSRF)}, - url: addPathParamsToApiUrl(Constants.pluginApiServiceConfigs.getRepositoryBranches.path, pathParams as unknown as Record), - method: Constants.pluginApiServiceConfigs.getRepositoryBranches.method, - }), - }), [Constants.pluginApiServiceConfigs.deleteSubscription.apiServiceName]: builder.query({ query: (payload) => ({ headers: {[Constants.common.HeaderCSRFToken]: Cookies.get(Constants.common.MMCSRF)}, diff --git a/webapp/src/types/common/form.d.ts b/webapp/src/types/common/form.d.ts index c0bd2387..ac262add 100644 --- a/webapp/src/types/common/form.d.ts +++ b/webapp/src/types/common/form.d.ts @@ -4,7 +4,7 @@ type ErrorComponents = 'LinkProjectModal' | 'CreateTaskModal' | 'SubscribeModal' type LinkProjectModalFields = 'organization' | 'project' | 'timestamp' type CreateTaskModalFields = 'organization' | 'project' | 'type' | 'title' | 'description' | 'areaPath' | 'timestamp' -type SubscriptionModalFields = 'organization' | 'project' | 'eventType' | 'channelID' | 'timestamp' | 'serviceType' | 'repository' | 'targetBranch' | 'repositoryName' | 'pullRequestCreatedBy' | 'pullRequestReviewersContains' | 'pullRequestCreatedByName' | 'pullRequestReviewersContainsName' | 'pushedBy' | 'mergeResult' | 'notificationType' | 'pushedByName' | 'mergeResultName' | 'notificationTypeName' | 'areaPath' | 'buildPipeline' | 'buildStatus' | 'releasePipeline' | 'stageName' | 'approvalType' | 'approvalStatus' | 'releaseStatus' +type SubscriptionModalFields = 'organization' | 'project' | 'eventType' | 'channelID' | 'timestamp' | 'serviceType' | 'repository' | 'targetBranch' | 'repositoryName' | 'pullRequestCreatedBy' | 'pullRequestReviewersContains' | 'pullRequestCreatedByName' | 'pullRequestReviewersContainsName' | 'pushedBy' | 'mergeResult' | 'notificationType' | 'pushedByName' | 'mergeResultName' | 'notificationTypeName' | 'areaPath' | 'buildPipeline' | 'buildStatus' | 'releasePipeline' | 'stageName' | 'approvalType' | 'approvalStatus' | 'releaseStatus' | 'buildStatusName' | 'releasePipelineName' | 'stageNameValue' | 'approvalTypeName' | 'approvalStatusName' | 'releaseStatusName' type ModalFormFieldConfig = { label: string diff --git a/webapp/src/types/common/index.d.ts b/webapp/src/types/common/index.d.ts index f639be67..45cee13d 100644 --- a/webapp/src/types/common/index.d.ts +++ b/webapp/src/types/common/index.d.ts @@ -97,6 +97,19 @@ type SubscriptionDetails = { notificationType: string notificationTypeName: string areaPath: string + buildPipeline: string + buildStatus: string + releasePipeline: string + stageName: string + approvalType: string + approvalStatus: string + releaseStatus: string + buildStatusName: string + releasePipelineName: string + stageNameValue: string + approvalTypeName: string + approvalStatusName: string + releaseStatusName: string } type WebsocketEventParams = { @@ -114,13 +127,3 @@ type SubscriptionFilters = { serviceType: string, eventType: string, } - -type ReposSubscriptionFiltersResponse = { - id: string, - name: string -} - -type ReposSubscriptionTargetBranchFilterResponse = { - objectId: string, - name: string -} diff --git a/webapp/src/types/common/payload.d.ts b/webapp/src/types/common/payload.d.ts index 66af57f5..dd95cd02 100644 --- a/webapp/src/types/common/payload.d.ts +++ b/webapp/src/types/common/payload.d.ts @@ -32,6 +32,19 @@ type SubscriptionPayload = { notificationType: string notificationTypeName: string areaPath: string + buildPipeline: string + buildStatus: string + releasePipeline: string + stageName: string + approvalType: string + approvalStatus: string + releaseStatus: string + buildStatusName: string + releasePipelineName: string + stageNameValue: string + approvalTypeName: string + approvalStatusName: string + releaseStatusName: string } interface PaginationQueryParams { @@ -39,12 +52,6 @@ interface PaginationQueryParams { per_page: number; } -type ReposSubscriptionFiltersRequest = { - organization: string - project: string - repository?: string -} - type SubscriptionFiltersPossibleValues = { displayValue: string value: string diff --git a/webapp/src/types/common/service.d.ts b/webapp/src/types/common/service.d.ts index a4fbde9b..af2bf556 100644 --- a/webapp/src/types/common/service.d.ts +++ b/webapp/src/types/common/service.d.ts @@ -10,8 +10,6 @@ type ApiServiceName = 'getChannels' | 'getSubscriptionList' | 'deleteSubscription' | - 'getRepositories' | - 'getRepositoryBranches' | 'getSubscriptionFilters' type PluginApiService = { @@ -35,9 +33,6 @@ type APIRequestPayload = SubscriptionPayload | FetchChannelParams | FetchSubscriptionList | - ReposSubscriptionFiltersRequest | - ReposSubscriptionFiltersResponse | - ReposSubscriptionTargetBranchFilterResponse | GetSubscriptionFiltersRequest | GetSubscriptionFiltersResponse | void;