From 1fbbb91dbce8ae3c21b159c9f679601ee98e5a2b Mon Sep 17 00:00:00 2001 From: Carlos Treminio Date: Sun, 14 Nov 2021 19:40:14 -0600 Subject: [PATCH] :sparkles: Added the workflow scheme service. 1. Added the ability to CRUD the worflow schemes on the V2 2. Added the ability to assign workflow scheme to a project (v3,v2) 3. Added the ability to get the project/workflow scheme associations (v3,v2) --- .../get-worflow-scheme-associations.json | 38 + jira/mocks/get-workflow-scheme.json | 12 + jira/mocks/get-workflow-schemes.json | 30 + jira/v2/jira.go | 6 +- jira/v2/workflow.go | 1 + jira/v2/workflowScheme.go | 224 ++++ jira/v2/workflowScheme_test.go | 1039 +++++++++++++++++ jira/v3/workflowScheme.go | 73 ++ jira/v3/workflowScheme_test.go | 283 +++++ pkg/infra/models/jira/errors.go | 1 + pkg/infra/models/jira/workflowScheme.go | 9 + 11 files changed, 1715 insertions(+), 1 deletion(-) create mode 100644 jira/mocks/get-worflow-scheme-associations.json create mode 100644 jira/mocks/get-workflow-scheme.json create mode 100644 jira/mocks/get-workflow-schemes.json create mode 100644 jira/v2/workflowScheme.go create mode 100644 jira/v2/workflowScheme_test.go diff --git a/jira/mocks/get-worflow-scheme-associations.json b/jira/mocks/get-worflow-scheme-associations.json new file mode 100644 index 00000000..a5395181 --- /dev/null +++ b/jira/mocks/get-worflow-scheme-associations.json @@ -0,0 +1,38 @@ +{ + "values": [ + { + "projectIds": [ + "10001" + ], + "workflowScheme": { + "id": 10002, + "name": "Jira Service Management IT Support Workflow Scheme generated for Project DESK", + "description": "This Jira Service Management IT Support Workflow Scheme was generated for Project DESK", + "defaultWorkflow": "jira", + "issueTypeMappings": { + "10002": "DESK: Jira Service Management default workflow", + "10003": "DESK: Jira Service Management default workflow", + "10005": "DESK: Service Request Fulfilment workflow for Jira Service Management", + "10006": "DESK: Service Request Fulfilment workflow for Jira Service Management", + "10007": "DESK: Service Request Fulfilment with Approvals workflow for Jira Service Management" + }, + "self": "https://ctreminiom.atlassian.net/rest/api/2/workflowscheme/10002" + } + }, + { + "projectIds": [ + "10003" + ], + "workflowScheme": { + "id": 10004, + "name": "K2: Software Simplified Workflow Scheme", + "description": "Generated by JIRA Software version 1001.0.0-SNAPSHOT. This workflow scheme is managed internally by Jira Software. Do not manually modify this workflow scheme.", + "defaultWorkflow": "Software Simplified Workflow for Project K2", + "issueTypeMappings": { + + }, + "self": "https://ctreminiom.atlassian.net/rest/api/2/workflowscheme/10004" + } + } + ] +} \ No newline at end of file diff --git a/jira/mocks/get-workflow-scheme.json b/jira/mocks/get-workflow-scheme.json new file mode 100644 index 00000000..c4ec16d3 --- /dev/null +++ b/jira/mocks/get-workflow-scheme.json @@ -0,0 +1,12 @@ +{ + "id": 101010, + "name": "Example workflow scheme", + "description": "The description of the example workflow scheme.", + "defaultWorkflow": "jira", + "issueTypeMappings": { + "10000": "scrum workflow", + "10001": "builds workflow" + }, + "draft": false, + "self": "https://your-domain.atlassian.net/rest/api/2/workflowscheme/101010" +} \ No newline at end of file diff --git a/jira/mocks/get-workflow-schemes.json b/jira/mocks/get-workflow-schemes.json new file mode 100644 index 00000000..1c6b906c --- /dev/null +++ b/jira/mocks/get-workflow-schemes.json @@ -0,0 +1,30 @@ +{ + "maxResults": 50, + "startAt": 0, + "total": 2, + "isLast": true, + "values": [ + { + "id": 101010, + "name": "Example workflow scheme", + "description": "The description of the example workflow scheme.", + "defaultWorkflow": "jira", + "issueTypeMappings": { + "10000": "scrum workflow", + "10001": "builds workflow" + }, + "self": "https://your-domain.atlassian.net/rest/api/2/workflowscheme/101010" + }, + { + "id": 101011, + "name": "Another example workflow scheme", + "description": "The description of the another example workflow scheme.", + "defaultWorkflow": "jira", + "issueTypeMappings": { + "10000": "scrum workflow", + "10001": "builds workflow" + }, + "self": "https://your-domain.atlassian.net/rest/api/2/workflowscheme/101011" + } + ] +} \ No newline at end of file diff --git a/jira/v2/jira.go b/jira/v2/jira.go index d3c3fceb..935858e8 100644 --- a/jira/v2/jira.go +++ b/jira/v2/jira.go @@ -149,7 +149,11 @@ func New(httpClient *http.Client, site string) (client *Client, err error) { } client.MySelf = &MySelfService{client: client} - client.Workflow = &WorkflowService{client: client} + client.Workflow = &WorkflowService{ + client: client, + Scheme: &WorkflowSchemeService{client: client}, + } + return } diff --git a/jira/v2/workflow.go b/jira/v2/workflow.go index 63b1c8e8..6af12fc8 100644 --- a/jira/v2/workflow.go +++ b/jira/v2/workflow.go @@ -12,6 +12,7 @@ import ( type WorkflowService struct { client *Client + Scheme *WorkflowSchemeService } func (w *WorkflowService) Create(ctx context.Context, payload *models.WorkflowPayloadScheme) (result *models.WorkflowCreatedResponseScheme, diff --git a/jira/v2/workflowScheme.go b/jira/v2/workflowScheme.go new file mode 100644 index 00000000..5305bf6b --- /dev/null +++ b/jira/v2/workflowScheme.go @@ -0,0 +1,224 @@ +package v2 + +import ( + "context" + "fmt" + models "github.com/ctreminiom/go-atlassian/pkg/infra/models/jira" + "net/http" + "net/url" + "strconv" + "strings" +) + +type WorkflowSchemeService struct { + client *Client +} + +// Gets returns a paginated list of all workflow schemes, not including draft workflow schemes. +// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-workflow-schemes/#api-rest-api-2-workflowscheme-get +func (w *WorkflowSchemeService) Gets(ctx context.Context, startAt, maxResults int) (result *models.WorkflowSchemePageScheme, + response *ResponseScheme, err error) { + + params := url.Values{} + params.Add("startAt", strconv.Itoa(startAt)) + params.Add("maxResults", strconv.Itoa(maxResults)) + + endpoint := fmt.Sprintf("rest/api/2/workflowscheme?%v", params.Encode()) + + request, err := w.client.newRequest(ctx, http.MethodGet, endpoint, nil) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + + response, err = w.client.call(request, &result) + if err != nil { + return + } + + return +} + +// Create creates a workflow scheme. +// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-workflow-schemes/#api-rest-api-2-workflowscheme-post +func (w *WorkflowSchemeService) Create(ctx context.Context, payload *models.WorkflowSchemePayloadScheme) (result *models.WorkflowSchemeScheme, + response *ResponseScheme, err error) { + + payloadAsReader, err := transformStructToReader(payload) + if err != nil { + return nil, nil, err + } + + endpoint := "/rest/api/2/workflowscheme" + request, err := w.client.newRequest(ctx, http.MethodPost, endpoint, payloadAsReader) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + request.Header.Set("Content-Type", "application/json") + + response, err = w.client.call(request, &result) + if err != nil { + return + } + + return +} + +// Get returns a workflow scheme. +// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-workflow-schemes/#api-rest-api-2-workflowscheme-id-get +func (w *WorkflowSchemeService) Get(ctx context.Context, workflowSchemeID int, returnDraftIfExists bool) (result *models.WorkflowSchemeScheme, + response *ResponseScheme, err error) { + + params := url.Values{} + if returnDraftIfExists { + params.Add("returnDraftIfExists", "true") + } + + var endpoint strings.Builder + endpoint.WriteString(fmt.Sprintf("rest/api/2/workflowscheme/%v", workflowSchemeID)) + + if params.Encode() != "" { + endpoint.WriteString(fmt.Sprintf("?%v", params.Encode())) + } + + request, err := w.client.newRequest(ctx, http.MethodGet, endpoint.String(), nil) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + + response, err = w.client.call(request, &result) + if err != nil { + return + } + + return +} + +// Update updates a workflow scheme, including the name, default workflow, issue type to project mappings, and more. +// If the workflow scheme is active (that is, being used by at least one project), then a draft workflow scheme is +// created or updated instead, provided that updateDraftIfNeeded is set to true. +// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-workflow-schemes/#api-rest-api-2-workflowscheme-id-put +func (w *WorkflowSchemeService) Update(ctx context.Context, workflowSchemeID int, payload *models.WorkflowSchemePayloadScheme) ( + result *models.WorkflowSchemeScheme, response *ResponseScheme, err error) { + + payloadAsReader, err := transformStructToReader(payload) + if err != nil { + return nil, nil, err + } + + var endpoint strings.Builder + endpoint.WriteString(fmt.Sprintf("rest/api/2/workflowscheme/%v", workflowSchemeID)) + + request, err := w.client.newRequest(ctx, http.MethodPut, endpoint.String(), payloadAsReader) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + request.Header.Set("Content-Type", "application/json") + + response, err = w.client.call(request, &result) + if err != nil { + return + } + + return +} + +// Delete deletes a workflow scheme. +// Note that a workflow scheme cannot be deleted if it is active (that is, being used by at least one project). +// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-workflow-schemes/#api-rest-api-2-workflowscheme-id-delete +func (w *WorkflowSchemeService) Delete(ctx context.Context, workflowSchemeID int) (response *ResponseScheme, err error) { + + endpoint := fmt.Sprintf("rest/api/2/workflowscheme/%v", workflowSchemeID) + + request, err := w.client.newRequest(ctx, http.MethodDelete, endpoint, nil) + if err != nil { + return + } + + response, err = w.client.call(request, nil) + if err != nil { + return + } + + return +} + +// Associations returns a list of the workflow schemes associated with a list of projects. +// Each returned workflow scheme includes a list of the requested projects associated with it. +// Any team-managed or non-existent projects in the request are ignored and no errors are returned. +// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-workflow-scheme-project-associations/#api-rest-api-2-workflowscheme-project-get +func (w *WorkflowSchemeService) Associations(ctx context.Context, projectIDs []int) (result *models.WorkflowSchemeAssociationPageScheme, + response *ResponseScheme, err error) { + + if len(projectIDs) == 0 { + return nil, nil, models.ErrNoProjectsError + } + + params := url.Values{} + for _, projectID := range projectIDs { + params.Add("projectId", strconv.Itoa(projectID)) + } + + endpoint := fmt.Sprintf("rest/api/2/workflowscheme/project?%v", params.Encode()) + + request, err := w.client.newRequest(ctx, http.MethodGet, endpoint, nil) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + + response, err = w.client.call(request, &result) + if err != nil { + return + } + + return +} + +// Assign assigns a workflow scheme to a project. +// This operation is performed only when there are no issues in the project. +// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-workflow-scheme-project-associations/#api-rest-api-2-workflowscheme-project-put +func (w *WorkflowSchemeService) Assign(ctx context.Context, workflowSchemeID, projectID string) (response *ResponseScheme, err error) { + + if len(projectID) == 0 { + return nil, models.ErrNoProjectIDError + } + + if len(workflowSchemeID) == 0 { + return nil, models.ErrNoWorkflowSchemeIDError + } + + payload := struct { + WorkflowSchemeID string `json:"workflowSchemeId"` + ProjectID string `json:"projectId"` + }{ + WorkflowSchemeID: workflowSchemeID, + ProjectID: projectID, + } + + payloadAsReader, _ := transformStructToReader(&payload) + endpoint := "rest/api/2/workflowscheme/project" + + request, err := w.client.newRequest(ctx, http.MethodPut, endpoint, payloadAsReader) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + request.Header.Set("Content-Type", "application/json") + + response, err = w.client.call(request, nil) + if err != nil { + return + } + + return +} diff --git a/jira/v2/workflowScheme_test.go b/jira/v2/workflowScheme_test.go new file mode 100644 index 00000000..aac7ce16 --- /dev/null +++ b/jira/v2/workflowScheme_test.go @@ -0,0 +1,1039 @@ +package v2 + +import ( + "context" + "fmt" + models "github.com/ctreminiom/go-atlassian/pkg/infra/models/jira" + "github.com/stretchr/testify/assert" + "log" + "net/http" + "net/url" + "testing" +) + +func TestWorkflowSchemeService_Create(t *testing.T) { + + testCases := []struct { + name string + payload *models.WorkflowSchemePayloadScheme + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + }{ + { + name: "CreateWorkflowSchemeWhenTheParametersAreCorrect", + mockFile: "../mocks/get-workflow-scheme.json", + payload: &models.WorkflowSchemePayloadScheme{ + DefaultWorkflow: "jira", + Name: "Example workflow scheme", + Description: "The description of the example workflow scheme.", + IssueTypeMappings: map[string]string{ + "10000": "scrum workflow", + "10001": "builds workflow", + }, + }, + wantHTTPMethod: http.MethodPost, + endpoint: "/rest/api/2/workflowscheme", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: false, + }, + + { + name: "CreateWorkflowSchemeWhenThePayloadIsNotSet", + mockFile: "../mocks/get-workflow-scheme.json", + payload: nil, + wantHTTPMethod: http.MethodPost, + endpoint: "/rest/api/2/workflowscheme", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "CreateWorkflowSchemeWhenTheContextIsNotSet", + mockFile: "../mocks/get-workflow-scheme.json", + payload: &models.WorkflowSchemePayloadScheme{ + DefaultWorkflow: "jira", + Name: "Example workflow scheme", + Description: "The description of the example workflow scheme.", + IssueTypeMappings: map[string]string{ + "10000": "scrum workflow", + "10001": "builds workflow", + }, + }, + wantHTTPMethod: http.MethodPost, + endpoint: "/rest/api/2/workflowscheme", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "CreateWorkflowSchemeWhenTheRequestMethodIsIncorrect", + mockFile: "../mocks/get-workflow-scheme.json", + payload: &models.WorkflowSchemePayloadScheme{ + DefaultWorkflow: "jira", + Name: "Example workflow scheme", + Description: "The description of the example workflow scheme.", + IssueTypeMappings: map[string]string{ + "10000": "scrum workflow", + "10001": "builds workflow", + }, + }, + wantHTTPMethod: http.MethodHead, + endpoint: "/rest/api/2/workflowscheme", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "CreateWorkflowSchemeWhenTheStatusCodeIsIncorrect", + mockFile: "../mocks/get-workflow-scheme.json", + payload: &models.WorkflowSchemePayloadScheme{ + DefaultWorkflow: "jira", + Name: "Example workflow scheme", + Description: "The description of the example workflow scheme.", + IssueTypeMappings: map[string]string{ + "10000": "scrum workflow", + "10001": "builds workflow", + }, + }, + wantHTTPMethod: http.MethodPost, + endpoint: "/rest/api/2/workflowscheme", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + }, + } + + for _, testCase := range testCases { + + t.Run(testCase.name, func(t *testing.T) { + + //Init a new HTTP mock server + mockOptions := mockServerOptions{ + Endpoint: testCase.endpoint, + MockFilePath: testCase.mockFile, + MethodAccepted: testCase.wantHTTPMethod, + ResponseCodeWanted: testCase.wantHTTPCodeReturn, + } + + mockServer, err := startMockServer(&mockOptions) + if err != nil { + t.Fatal(err) + } + + defer mockServer.Close() + + //Init the library instance + mockClient, err := startMockClient(mockServer.URL) + if err != nil { + t.Fatal(err) + } + + i := &WorkflowSchemeService{client: mockClient} + + gotResult, gotResponse, err := i.Create(testCase.context, testCase.payload) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.Error(t, err) + } else { + + assert.NoError(t, err) + assert.NotEqual(t, gotResponse, nil) + assert.NotEqual(t, gotResult, nil) + + apiEndpoint, err := url.Parse(gotResponse.Endpoint) + if err != nil { + t.Fatal(err) + } + + var endpointToAssert string + + if apiEndpoint.Query().Encode() != "" { + endpointToAssert = fmt.Sprintf("%v?%v", apiEndpoint.Path, apiEndpoint.Query().Encode()) + } else { + endpointToAssert = apiEndpoint.Path + } + + t.Logf("HTTP Endpoint Wanted: %v, HTTP Endpoint Returned: %v", testCase.endpoint, endpointToAssert) + assert.Equal(t, testCase.endpoint, endpointToAssert) + } + }) + + } + +} + +func TestWorkflowSchemeService_Get(t *testing.T) { + + testCases := []struct { + name string + workflowSchemeID int + isExits bool + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + }{ + { + name: "GetWorkflowWorkflowSchemeWhenTheParametersAreCorrect", + workflowSchemeID: 1006, + isExits: true, + mockFile: "../mocks/get-workflow-scheme.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme/1006?returnDraftIfExists=true", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: false, + }, + + { + name: "GetWorkflowWorkflowSchemeWhenTheContextIsNotSet", + workflowSchemeID: 1006, + isExits: true, + mockFile: "../mocks/get-workflow-scheme.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme/1006?returnDraftIfExists=true", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetWorkflowWorkflowSchemeWhenTheRequestMethodIsIncorrect", + workflowSchemeID: 1006, + isExits: true, + mockFile: "../mocks/get-workflow-scheme.json", + wantHTTPMethod: http.MethodHead, + endpoint: "/rest/api/2/workflowscheme/1006?returnDraftIfExists=true", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetWorkflowWorkflowSchemeWhenTheStatusCodeIsIncorrect", + workflowSchemeID: 1006, + isExits: true, + mockFile: "../mocks/get-workflow-scheme.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme/1006?returnDraftIfExists=true", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + }, + + { + name: "GetWorkflowWorkflowSchemeWhenTheResponseBodyIsEmpty", + workflowSchemeID: 1006, + isExits: true, + mockFile: "../mocks/empty_json.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme/1006?returnDraftIfExists=true", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + } + + for _, testCase := range testCases { + + t.Run(testCase.name, func(t *testing.T) { + + //Init a new HTTP mock server + mockOptions := mockServerOptions{ + Endpoint: testCase.endpoint, + MockFilePath: testCase.mockFile, + MethodAccepted: testCase.wantHTTPMethod, + ResponseCodeWanted: testCase.wantHTTPCodeReturn, + } + + mockServer, err := startMockServer(&mockOptions) + if err != nil { + t.Fatal(err) + } + + defer mockServer.Close() + + //Init the library instance + mockClient, err := startMockClient(mockServer.URL) + if err != nil { + t.Fatal(err) + } + + i := &WorkflowSchemeService{client: mockClient} + + gotResult, gotResponse, err := i.Get(testCase.context, testCase.workflowSchemeID, testCase.isExits) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.Error(t, err) + } else { + + assert.NoError(t, err) + assert.NotEqual(t, gotResponse, nil) + assert.NotEqual(t, gotResult, nil) + + apiEndpoint, err := url.Parse(gotResponse.Endpoint) + if err != nil { + t.Fatal(err) + } + + var endpointToAssert string + + if apiEndpoint.Query().Encode() != "" { + endpointToAssert = fmt.Sprintf("%v?%v", apiEndpoint.Path, apiEndpoint.Query().Encode()) + } else { + endpointToAssert = apiEndpoint.Path + } + + t.Logf("HTTP Endpoint Wanted: %v, HTTP Endpoint Returned: %v", testCase.endpoint, endpointToAssert) + assert.Equal(t, testCase.endpoint, endpointToAssert) + } + }) + + } + +} + +func TestWorkflowSchemeService_Update(t *testing.T) { + + testCases := []struct { + name string + workflowSchemeID int + payload *models.WorkflowSchemePayloadScheme + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + }{ + { + name: "UpdateWorkflowSchemeWhenTheParametersAreCorrect", + workflowSchemeID: 1006, + mockFile: "../mocks/get-workflow-scheme.json", + payload: &models.WorkflowSchemePayloadScheme{ + DefaultWorkflow: "jira", + Name: "Example workflow scheme", + Description: "The description of the example workflow scheme.", + IssueTypeMappings: map[string]string{ + "10000": "scrum workflow", + "10001": "builds workflow", + }, + }, + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/workflowscheme/1006", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: false, + }, + + { + name: "UpdateWorkflowSchemeWhenTheContextIsNotSet", + workflowSchemeID: 1006, + mockFile: "../mocks/get-workflow-scheme.json", + payload: &models.WorkflowSchemePayloadScheme{ + DefaultWorkflow: "jira", + Name: "Example workflow scheme", + Description: "The description of the example workflow scheme.", + IssueTypeMappings: map[string]string{ + "10000": "scrum workflow", + "10001": "builds workflow", + }, + }, + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/workflowscheme/1006", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "UpdateWorkflowSchemeWhenTheResponseBodyIsEmpty", + workflowSchemeID: 1006, + mockFile: "../mocks/empty-json.json", + payload: &models.WorkflowSchemePayloadScheme{ + DefaultWorkflow: "jira", + Name: "Example workflow scheme", + Description: "The description of the example workflow scheme.", + IssueTypeMappings: map[string]string{ + "10000": "scrum workflow", + "10001": "builds workflow", + }, + }, + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/workflowscheme/1006", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "UpdateWorkflowSchemeWhenTheRequestMethodIsIncorrect", + workflowSchemeID: 1006, + mockFile: "../mocks/get-workflow-scheme.json", + payload: &models.WorkflowSchemePayloadScheme{ + DefaultWorkflow: "jira", + Name: "Example workflow scheme", + Description: "The description of the example workflow scheme.", + IssueTypeMappings: map[string]string{ + "10000": "scrum workflow", + "10001": "builds workflow", + }, + }, + wantHTTPMethod: http.MethodHead, + endpoint: "/rest/api/2/workflowscheme/1006", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "UpdateWorkflowSchemeWhenTheStatusCodeIsIncorrect", + workflowSchemeID: 1006, + mockFile: "../mocks/get-workflow-scheme.json", + payload: &models.WorkflowSchemePayloadScheme{ + DefaultWorkflow: "jira", + Name: "Example workflow scheme", + Description: "The description of the example workflow scheme.", + IssueTypeMappings: map[string]string{ + "10000": "scrum workflow", + "10001": "builds workflow", + }, + }, + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/workflowscheme/1006", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + }, + + { + name: "UpdateWorkflowSchemeWhenThePayloadIsNotSet", + workflowSchemeID: 1006, + mockFile: "../mocks/get-workflow-scheme.json", + payload: nil, + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/workflowscheme/1006", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + } + + for _, testCase := range testCases { + + t.Run(testCase.name, func(t *testing.T) { + + //Init a new HTTP mock server + mockOptions := mockServerOptions{ + Endpoint: testCase.endpoint, + MockFilePath: testCase.mockFile, + MethodAccepted: testCase.wantHTTPMethod, + ResponseCodeWanted: testCase.wantHTTPCodeReturn, + } + + mockServer, err := startMockServer(&mockOptions) + if err != nil { + t.Fatal(err) + } + + defer mockServer.Close() + + //Init the library instance + mockClient, err := startMockClient(mockServer.URL) + if err != nil { + t.Fatal(err) + } + + i := &WorkflowSchemeService{client: mockClient} + + gotResult, gotResponse, err := i.Update(testCase.context, testCase.workflowSchemeID, testCase.payload) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.Error(t, err) + } else { + + assert.NoError(t, err) + assert.NotEqual(t, gotResponse, nil) + assert.NotEqual(t, gotResult, nil) + + apiEndpoint, err := url.Parse(gotResponse.Endpoint) + if err != nil { + t.Fatal(err) + } + + var endpointToAssert string + + if apiEndpoint.Query().Encode() != "" { + endpointToAssert = fmt.Sprintf("%v?%v", apiEndpoint.Path, apiEndpoint.Query().Encode()) + } else { + endpointToAssert = apiEndpoint.Path + } + + t.Logf("HTTP Endpoint Wanted: %v, HTTP Endpoint Returned: %v", testCase.endpoint, endpointToAssert) + assert.Equal(t, testCase.endpoint, endpointToAssert) + } + }) + + } + +} + +func TestWorkflowSchemeService_Gets(t *testing.T) { + + testCases := []struct { + name string + startAt, maxResults int + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + }{ + { + name: "GetWorkflowSchemesWhenTheParametersAreCorrect", + startAt: 0, + maxResults: 50, + mockFile: "../mocks/get-workflow-schemes.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme?maxResults=50&startAt=0", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: false, + }, + + { + name: "GetWorkflowSchemesWhenTheRequestMethodIsIncorrect", + startAt: 0, + maxResults: 50, + mockFile: "../mocks/get-workflow-schemes.json", + wantHTTPMethod: http.MethodHead, + endpoint: "/rest/api/2/workflowscheme?maxResults=50&startAt=0", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetWorkflowSchemesWhenTheStatusCodeIsIncorrect", + startAt: 0, + maxResults: 50, + mockFile: "../mocks/get-workflow-schemes.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme?maxResults=50&startAt=0", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + }, + + { + name: "GetWorkflowSchemesWhenTheContextIsNotSet", + startAt: 0, + maxResults: 50, + mockFile: "../mocks/get-workflow-schemes.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme?maxResults=50&startAt=0", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetWorkflowSchemesWhenTheResponseBodyIsEmpty", + startAt: 0, + maxResults: 50, + mockFile: "../mocks/empty_json.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme?maxResults=50&startAt=0", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + } + + for _, testCase := range testCases { + + t.Run(testCase.name, func(t *testing.T) { + + //Init a new HTTP mock server + mockOptions := mockServerOptions{ + Endpoint: testCase.endpoint, + MockFilePath: testCase.mockFile, + MethodAccepted: testCase.wantHTTPMethod, + ResponseCodeWanted: testCase.wantHTTPCodeReturn, + } + + mockServer, err := startMockServer(&mockOptions) + if err != nil { + t.Fatal(err) + } + + defer mockServer.Close() + + //Init the library instance + mockClient, err := startMockClient(mockServer.URL) + if err != nil { + t.Fatal(err) + } + + i := &WorkflowSchemeService{client: mockClient} + + gotResult, gotResponse, err := i.Gets(testCase.context, testCase.startAt, testCase.maxResults) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.Error(t, err) + } else { + + assert.NoError(t, err) + assert.NotEqual(t, gotResponse, nil) + assert.NotEqual(t, gotResult, nil) + + apiEndpoint, err := url.Parse(gotResponse.Endpoint) + if err != nil { + t.Fatal(err) + } + + var endpointToAssert string + + if apiEndpoint.Query().Encode() != "" { + endpointToAssert = fmt.Sprintf("%v?%v", apiEndpoint.Path, apiEndpoint.Query().Encode()) + } else { + endpointToAssert = apiEndpoint.Path + } + + t.Logf("HTTP Endpoint Wanted: %v, HTTP Endpoint Returned: %v", testCase.endpoint, endpointToAssert) + assert.Equal(t, testCase.endpoint, endpointToAssert) + } + }) + + } + +} + +func TestWorkflowSchemeService_Delete(t *testing.T) { + + testCases := []struct { + name string + workflowSchemeID int + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + }{ + { + name: "DeleteWorkflowSchemeWhenTheParametersAreCorrect", + workflowSchemeID: 1006, + wantHTTPMethod: http.MethodDelete, + endpoint: "/rest/api/2/workflowscheme/1006", + context: context.Background(), + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: false, + }, + + { + name: "DeleteWorkflowSchemeWhenTheRequestMethodIsIncorrect", + workflowSchemeID: 1006, + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme/1006", + context: context.Background(), + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: true, + }, + + { + name: "DeleteWorkflowSchemeWhenTheStatusCodeIsIncorrect", + workflowSchemeID: 1006, + wantHTTPMethod: http.MethodDelete, + endpoint: "/rest/api/2/workflowscheme/1006", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + }, + + { + name: "DeleteWorkflowSchemeWhenTheContextIsNotSet", + workflowSchemeID: 1006, + wantHTTPMethod: http.MethodDelete, + endpoint: "/rest/api/2/workflowscheme/1006", + context: nil, + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: true, + }, + } + + for _, testCase := range testCases { + + t.Run(testCase.name, func(t *testing.T) { + + //Init a new HTTP mock server + mockOptions := mockServerOptions{ + Endpoint: testCase.endpoint, + MockFilePath: testCase.mockFile, + MethodAccepted: testCase.wantHTTPMethod, + ResponseCodeWanted: testCase.wantHTTPCodeReturn, + } + + mockServer, err := startMockServer(&mockOptions) + if err != nil { + t.Fatal(err) + } + + defer mockServer.Close() + + //Init the library instance + mockClient, err := startMockClient(mockServer.URL) + if err != nil { + t.Fatal(err) + } + + i := &WorkflowSchemeService{client: mockClient} + + gotResponse, err := i.Delete(testCase.context, testCase.workflowSchemeID) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.Error(t, err) + } else { + + assert.NoError(t, err) + assert.NotEqual(t, gotResponse, nil) + + apiEndpoint, err := url.Parse(gotResponse.Endpoint) + if err != nil { + t.Fatal(err) + } + + var endpointToAssert string + + if apiEndpoint.Query().Encode() != "" { + endpointToAssert = fmt.Sprintf("%v?%v", apiEndpoint.Path, apiEndpoint.Query().Encode()) + } else { + endpointToAssert = apiEndpoint.Path + } + + t.Logf("HTTP Endpoint Wanted: %v, HTTP Endpoint Returned: %v", testCase.endpoint, endpointToAssert) + assert.Equal(t, testCase.endpoint, endpointToAssert) + } + }) + + } + +} + +func TestWorkflowSchemeService_Associations(t *testing.T) { + + testCases := []struct { + name string + projectIDs []int + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + }{ + { + name: "GetWorkflowAssociationsWhenTheParametersAreCorrect", + projectIDs: []int{10001, 10002, 10003}, + mockFile: "../mocks/get-worflow-scheme-associations.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: false, + }, + + { + name: "GetWorkflowAssociationsWhenTheProjectIDsAreNotSet", + projectIDs: nil, + mockFile: "../mocks/get-worflow-scheme-associations.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetWorkflowAssociationsWhenTheContextIsNotProvided", + projectIDs: []int{10001, 10002, 10003}, + mockFile: "../mocks/get-worflow-scheme-associations.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetWorkflowAssociationsWhenTheRequestMethodIsIncorrect", + projectIDs: []int{10001, 10002, 10003}, + mockFile: "../mocks/get-worflow-scheme-associations.json", + wantHTTPMethod: http.MethodPost, + endpoint: "/rest/api/2/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetWorkflowAssociationsWhenTheStatusCodeIsIncorrect", + projectIDs: []int{10001, 10002, 10003}, + mockFile: "../mocks/get-worflow-scheme-associations.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + }, + + { + name: "GetWorkflowAssociationsWhenTheResponseBodyIsEmpty", + projectIDs: []int{10001, 10002, 10003}, + mockFile: "../mocks/empty-json.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + + //Init a new HTTP mock server + mockOptions := mockServerOptions{ + Endpoint: testCase.endpoint, + MockFilePath: testCase.mockFile, + MethodAccepted: testCase.wantHTTPMethod, + ResponseCodeWanted: testCase.wantHTTPCodeReturn, + } + + mockServer, err := startMockServer(&mockOptions) + if err != nil { + t.Fatal(err) + } + + defer mockServer.Close() + + //Init the library instance + mockClient, err := startMockClient(mockServer.URL) + if err != nil { + t.Fatal(err) + } + + i := &WorkflowSchemeService{client: mockClient} + + getResult, gotResponse, err := i.Associations(testCase.context, testCase.projectIDs) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.Error(t, err) + } else { + + assert.NoError(t, err) + assert.NotEqual(t, gotResponse, nil) + assert.NotEqual(t, getResult, nil) + + apiEndpoint, err := url.Parse(gotResponse.Endpoint) + if err != nil { + t.Fatal(err) + } + + var endpointToAssert string + + if apiEndpoint.Query().Encode() != "" { + endpointToAssert = fmt.Sprintf("%v?%v", apiEndpoint.Path, apiEndpoint.Query().Encode()) + } else { + endpointToAssert = apiEndpoint.Path + } + + t.Logf("HTTP Endpoint Wanted: %v, HTTP Endpoint Returned: %v", testCase.endpoint, endpointToAssert) + assert.Equal(t, testCase.endpoint, endpointToAssert) + + for _, value := range getResult.Values { + log.Println(value.ProjectIds, value.WorkflowScheme.Name) + } + } + + }) + } +} + +func TestWorkflowSchemeService_Assign(t *testing.T) { + + testCases := []struct { + name string + workflowSchemeID string + projectID string + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + }{ + { + name: "AssignWorkflowSchemeToProjectWhenTheParametersAreCorrect", + workflowSchemeID: "10001", + projectID: "10002", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/workflowscheme/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: false, + }, + + { + name: "AssignWorkflowSchemeToProjectWhenTheWorkflowSchemeIDIsNotSet", + workflowSchemeID: "", + projectID: "10002", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/workflowscheme/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: true, + }, + + { + name: "AssignWorkflowSchemeToProjectWhenTheProjectIDIsNotSet", + workflowSchemeID: "10001", + projectID: "", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/workflowscheme/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: true, + }, + + { + name: "AssignWorkflowSchemeToProjectWhenTheRequestMethodIsIncorrect", + workflowSchemeID: "10001", + projectID: "10002", + wantHTTPMethod: http.MethodHead, + endpoint: "/rest/api/2/workflowscheme/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: true, + }, + + { + name: "AssignWorkflowSchemeToProjectWhenTheStatusCodeIsIncorrect", + workflowSchemeID: "10001", + projectID: "10002", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/workflowscheme/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + }, + + { + name: "AssignWorkflowSchemeToProjectWhenTheContextIsNotProvided", + workflowSchemeID: "10001", + projectID: "10002", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/workflowscheme/project", + context: nil, + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + //Init a new HTTP mock server + mockOptions := mockServerOptions{ + Endpoint: testCase.endpoint, + MockFilePath: testCase.mockFile, + MethodAccepted: testCase.wantHTTPMethod, + ResponseCodeWanted: testCase.wantHTTPCodeReturn, + } + + mockServer, err := startMockServer(&mockOptions) + if err != nil { + t.Fatal(err) + } + + defer mockServer.Close() + + //Init the library instance + mockClient, err := startMockClient(mockServer.URL) + if err != nil { + t.Fatal(err) + } + + i := &WorkflowSchemeService{client: mockClient} + + gotResponse, err := i.Assign(testCase.context, testCase.workflowSchemeID, testCase.projectID) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.Error(t, err) + } else { + + assert.NoError(t, err) + assert.NotEqual(t, gotResponse, nil) + + apiEndpoint, err := url.Parse(gotResponse.Endpoint) + if err != nil { + t.Fatal(err) + } + + var endpointToAssert string + + if apiEndpoint.Query().Encode() != "" { + endpointToAssert = fmt.Sprintf("%v?%v", apiEndpoint.Path, apiEndpoint.Query().Encode()) + } else { + endpointToAssert = apiEndpoint.Path + } + + t.Logf("HTTP Endpoint Wanted: %v, HTTP Endpoint Returned: %v", testCase.endpoint, endpointToAssert) + assert.Equal(t, testCase.endpoint, endpointToAssert) + } + }) + } +} diff --git a/jira/v3/workflowScheme.go b/jira/v3/workflowScheme.go index d76b1fab..c949e2c6 100644 --- a/jira/v3/workflowScheme.go +++ b/jira/v3/workflowScheme.go @@ -147,3 +147,76 @@ func (w *WorkflowSchemeService) Delete(ctx context.Context, workflowSchemeID int return } + +// Associations returns a list of the workflow schemes associated with a list of projects. +// Each returned workflow scheme includes a list of the requested projects associated with it. +// Any team-managed or non-existent projects in the request are ignored and no errors are returned. +// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-workflow-scheme-project-associations/#api-rest-api-3-workflowscheme-project-get +func (w *WorkflowSchemeService) Associations(ctx context.Context, projectIDs []int) (result *models.WorkflowSchemeAssociationPageScheme, + response *ResponseScheme, err error) { + + if len(projectIDs) == 0 { + return nil, nil, models.ErrNoProjectsError + } + + params := url.Values{} + for _, projectID := range projectIDs { + params.Add("projectId", strconv.Itoa(projectID)) + } + + endpoint := fmt.Sprintf("rest/api/3/workflowscheme/project?%v", params.Encode()) + + request, err := w.client.newRequest(ctx, http.MethodGet, endpoint, nil) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + + response, err = w.client.call(request, &result) + if err != nil { + return + } + + return +} + +// Assign assigns a workflow scheme to a project. +// This operation is performed only when there are no issues in the project. +// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-workflow-scheme-project-associations/#api-rest-api-3-workflowscheme-project-put +func (w *WorkflowSchemeService) Assign(ctx context.Context, workflowSchemeID, projectID string) (response *ResponseScheme, err error) { + + if len(projectID) == 0 { + return nil, models.ErrNoProjectIDError + } + + if len(workflowSchemeID) == 0 { + return nil, models.ErrNoWorkflowSchemeIDError + } + + payload := struct { + WorkflowSchemeID string `json:"workflowSchemeId"` + ProjectID string `json:"projectId"` + }{ + WorkflowSchemeID: workflowSchemeID, + ProjectID: projectID, + } + + payloadAsReader, _ := transformStructToReader(&payload) + endpoint := "rest/api/3/workflowscheme/project" + + request, err := w.client.newRequest(ctx, http.MethodPut, endpoint, payloadAsReader) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + request.Header.Set("Content-Type", "application/json") + + response, err = w.client.call(request, nil) + if err != nil { + return + } + + return +} diff --git a/jira/v3/workflowScheme_test.go b/jira/v3/workflowScheme_test.go index e9530a51..433274e7 100644 --- a/jira/v3/workflowScheme_test.go +++ b/jira/v3/workflowScheme_test.go @@ -5,6 +5,7 @@ import ( "fmt" models "github.com/ctreminiom/go-atlassian/pkg/infra/models/jira" "github.com/stretchr/testify/assert" + "log" "net/http" "net/url" "testing" @@ -754,3 +755,285 @@ func TestWorkflowSchemeService_Delete(t *testing.T) { } } + +func TestWorkflowSchemeService_Associations(t *testing.T) { + + testCases := []struct { + name string + projectIDs []int + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + }{ + { + name: "GetWorkflowAssociationsWhenTheParametersAreCorrect", + projectIDs: []int{10001, 10002, 10003}, + mockFile: "../mocks/get-worflow-scheme-associations.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/3/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: false, + }, + + { + name: "GetWorkflowAssociationsWhenTheProjectIDsAreNotSet", + projectIDs: nil, + mockFile: "../mocks/get-worflow-scheme-associations.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/3/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetWorkflowAssociationsWhenTheContextIsNotProvided", + projectIDs: []int{10001, 10002, 10003}, + mockFile: "../mocks/get-worflow-scheme-associations.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/3/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetWorkflowAssociationsWhenTheRequestMethodIsIncorrect", + projectIDs: []int{10001, 10002, 10003}, + mockFile: "../mocks/get-worflow-scheme-associations.json", + wantHTTPMethod: http.MethodPost, + endpoint: "/rest/api/3/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetWorkflowAssociationsWhenTheStatusCodeIsIncorrect", + projectIDs: []int{10001, 10002, 10003}, + mockFile: "../mocks/get-worflow-scheme-associations.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/3/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + }, + + { + name: "GetWorkflowAssociationsWhenTheResponseBodyIsEmpty", + projectIDs: []int{10001, 10002, 10003}, + mockFile: "../mocks/empty-json.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/3/workflowscheme/project?projectId=10001&projectId=10002&projectId=10003", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + + //Init a new HTTP mock server + mockOptions := mockServerOptions{ + Endpoint: testCase.endpoint, + MockFilePath: testCase.mockFile, + MethodAccepted: testCase.wantHTTPMethod, + ResponseCodeWanted: testCase.wantHTTPCodeReturn, + } + + mockServer, err := startMockServer(&mockOptions) + if err != nil { + t.Fatal(err) + } + + defer mockServer.Close() + + //Init the library instance + mockClient, err := startMockClient(mockServer.URL) + if err != nil { + t.Fatal(err) + } + + i := &WorkflowSchemeService{client: mockClient} + + getResult, gotResponse, err := i.Associations(testCase.context, testCase.projectIDs) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.Error(t, err) + } else { + + assert.NoError(t, err) + assert.NotEqual(t, gotResponse, nil) + assert.NotEqual(t, getResult, nil) + + apiEndpoint, err := url.Parse(gotResponse.Endpoint) + if err != nil { + t.Fatal(err) + } + + var endpointToAssert string + + if apiEndpoint.Query().Encode() != "" { + endpointToAssert = fmt.Sprintf("%v?%v", apiEndpoint.Path, apiEndpoint.Query().Encode()) + } else { + endpointToAssert = apiEndpoint.Path + } + + t.Logf("HTTP Endpoint Wanted: %v, HTTP Endpoint Returned: %v", testCase.endpoint, endpointToAssert) + assert.Equal(t, testCase.endpoint, endpointToAssert) + + for _, value := range getResult.Values { + log.Println(value.ProjectIds, value.WorkflowScheme.Name) + } + } + + }) + } +} + +func TestWorkflowSchemeService_Assign(t *testing.T) { + + testCases := []struct { + name string + workflowSchemeID string + projectID string + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + }{ + { + name: "AssignWorkflowSchemeToProjectWhenTheParametersAreCorrect", + workflowSchemeID: "10001", + projectID: "10002", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/workflowscheme/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: false, + }, + + { + name: "AssignWorkflowSchemeToProjectWhenTheWorkflowSchemeIDIsNotSet", + workflowSchemeID: "", + projectID: "10002", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/workflowscheme/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: true, + }, + + { + name: "AssignWorkflowSchemeToProjectWhenTheProjectIDIsNotSet", + workflowSchemeID: "10001", + projectID: "", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/workflowscheme/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: true, + }, + + { + name: "AssignWorkflowSchemeToProjectWhenTheRequestMethodIsIncorrect", + workflowSchemeID: "10001", + projectID: "10002", + wantHTTPMethod: http.MethodHead, + endpoint: "/rest/api/3/workflowscheme/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: true, + }, + + { + name: "AssignWorkflowSchemeToProjectWhenTheStatusCodeIsIncorrect", + workflowSchemeID: "10001", + projectID: "10002", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/workflowscheme/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + }, + + { + name: "AssignWorkflowSchemeToProjectWhenTheContextIsNotProvided", + workflowSchemeID: "10001", + projectID: "10002", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/workflowscheme/project", + context: nil, + wantHTTPCodeReturn: http.StatusNoContent, + wantErr: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + //Init a new HTTP mock server + mockOptions := mockServerOptions{ + Endpoint: testCase.endpoint, + MockFilePath: testCase.mockFile, + MethodAccepted: testCase.wantHTTPMethod, + ResponseCodeWanted: testCase.wantHTTPCodeReturn, + } + + mockServer, err := startMockServer(&mockOptions) + if err != nil { + t.Fatal(err) + } + + defer mockServer.Close() + + //Init the library instance + mockClient, err := startMockClient(mockServer.URL) + if err != nil { + t.Fatal(err) + } + + i := &WorkflowSchemeService{client: mockClient} + + gotResponse, err := i.Assign(testCase.context, testCase.workflowSchemeID, testCase.projectID) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.Error(t, err) + } else { + + assert.NoError(t, err) + assert.NotEqual(t, gotResponse, nil) + + apiEndpoint, err := url.Parse(gotResponse.Endpoint) + if err != nil { + t.Fatal(err) + } + + var endpointToAssert string + + if apiEndpoint.Query().Encode() != "" { + endpointToAssert = fmt.Sprintf("%v?%v", apiEndpoint.Path, apiEndpoint.Query().Encode()) + } else { + endpointToAssert = apiEndpoint.Path + } + + t.Logf("HTTP Endpoint Wanted: %v, HTTP Endpoint Returned: %v", testCase.endpoint, endpointToAssert) + assert.Equal(t, testCase.endpoint, endpointToAssert) + } + }) + } +} diff --git a/pkg/infra/models/jira/errors.go b/pkg/infra/models/jira/errors.go index ca875305..29394ec9 100644 --- a/pkg/infra/models/jira/errors.go +++ b/pkg/infra/models/jira/errors.go @@ -44,4 +44,5 @@ var ( ErrNoAccountSliceError = errors.New("jira: no account id's set") ErrNoProjectKeySliceError = errors.New("jira: no project key's set") ErrNoWorkflowIDError = errors.New("jira: no workflow id set") + ErrNoWorkflowSchemeIDError = errors.New("jira: no workflow scheme id set") ) diff --git a/pkg/infra/models/jira/workflowScheme.go b/pkg/infra/models/jira/workflowScheme.go index 73d112d9..5b6d0c0b 100644 --- a/pkg/infra/models/jira/workflowScheme.go +++ b/pkg/infra/models/jira/workflowScheme.go @@ -28,3 +28,12 @@ type WorkflowSchemeScheme struct { Self string `json:"self,omitempty"` UpdateDraftIfNeeded bool `json:"updateDraftIfNeeded,omitempty"` } + +type WorkflowSchemeAssociationPageScheme struct { + Values []*WorkflowSchemeAssociationsScheme `json:"values,omitempty"` +} + +type WorkflowSchemeAssociationsScheme struct { + ProjectIds []string `json:"projectIds,omitempty"` + WorkflowScheme *WorkflowSchemeScheme `json:"workflowScheme,omitempty"` +}