diff --git a/jira/mocks/get-projects-v2.json b/jira/mocks/get-projects-v2.json new file mode 100644 index 00000000..ad829852 --- /dev/null +++ b/jira/mocks/get-projects-v2.json @@ -0,0 +1,50 @@ +[ + { + "self": "https://your-domain.atlassian.net/rest/api/2/project/EX", + "id": "10000", + "key": "EX", + "name": "Example", + "avatarUrls": { + "48x48": "https://your-domain.atlassian.net/secure/projectavatar?size=large&pid=10000", + "24x24": "https://your-domain.atlassian.net/secure/projectavatar?size=small&pid=10000", + "16x16": "https://your-domain.atlassian.net/secure/projectavatar?size=xsmall&pid=10000", + "32x32": "https://your-domain.atlassian.net/secure/projectavatar?size=medium&pid=10000" + }, + "projectCategory": { + "self": "https://your-domain.atlassian.net/rest/api/2/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + }, + "simplified": false, + "style": "classic", + "insight": { + "totalIssueCount": 100, + "lastIssueUpdateTime": "2021-11-03T02:38:47.060+0000" + } + }, + { + "self": "https://your-domain.atlassian.net/rest/api/2/project/ABC", + "id": "10001", + "key": "ABC", + "name": "Alphabetical", + "avatarUrls": { + "48x48": "https://your-domain.atlassian.net/secure/projectavatar?size=large&pid=10001", + "24x24": "https://your-domain.atlassian.net/secure/projectavatar?size=small&pid=10001", + "16x16": "https://your-domain.atlassian.net/secure/projectavatar?size=xsmall&pid=10001", + "32x32": "https://your-domain.atlassian.net/secure/projectavatar?size=medium&pid=10001" + }, + "projectCategory": { + "self": "https://your-domain.atlassian.net/rest/api/2/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + }, + "simplified": false, + "style": "classic", + "insight": { + "totalIssueCount": 100, + "lastIssueUpdateTime": "2021-11-03T02:38:47.060+0000" + } + } +] \ No newline at end of file diff --git a/jira/v2/project.go b/jira/v2/project.go index cbada08d..3274c399 100644 --- a/jira/v2/project.go +++ b/jira/v2/project.go @@ -104,6 +104,39 @@ func (p *ProjectService) Search(ctx context.Context, options *models.ProjectSear return } +// Gets returns all projects visible to the user. +// Deprecated, use Get projects paginated that supports search and pagination. +// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-projects/#api-rest-api-2-project-get +// Note: available on the Data Center versions +func (p *ProjectService) Gets(ctx context.Context, expand []string) (result []*models.ProjectScheme, response *ResponseScheme, err error) { + + params := url.Values{} + if len(expand) != 0 { + params.Add("expand", strings.Join(expand, ",")) + } + + var endpoint strings.Builder + endpoint.WriteString("rest/api/2/project") + + if params.Encode() != "" { + endpoint.WriteString(fmt.Sprintf("?%v", params.Encode())) + } + + request, err := p.client.newRequest(ctx, http.MethodGet, endpoint.String(), nil) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + + response, err = p.client.call(request, &result) + if err != nil { + return + } + + return +} + // Get returns the project details for a project. // Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-projects/#api-rest-api-2-project-projectidorkey-get // Atlassian Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-projects/#api-rest-api-2-project-projectidorkey-get diff --git a/jira/v2/project_test.go b/jira/v2/project_test.go index cf6334d5..0af9ff8c 100644 --- a/jira/v2/project_test.go +++ b/jira/v2/project_test.go @@ -645,6 +645,149 @@ func TestProjectService_DeleteAsynchronously(t *testing.T) { } +func TestProjectService_Gets(t *testing.T) { + + testCases := []struct { + name string + expands []string + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + }{ + { + name: "GetProjectsWhenTheParametersAreCorrect", + expands: []string{"issueTypes", "lead", "description"}, + mockFile: "../mocks/get-projects-v2.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/project?expand=issueTypes%2Clead%2Cdescription", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: false, + }, + + { + name: "GetProjectsWhenTheProjectExpandIsNil", + expands: nil, + mockFile: "../mocks/get-projects-v2.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/project", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: false, + }, + + { + name: "GetProjectsWhenTheRequestMethodIsIncorrect", + expands: []string{"issueTypes", "lead", "description"}, + mockFile: "../mocks/get-projects-v2.json", + wantHTTPMethod: http.MethodDelete, + endpoint: "/rest/api/2/project?expand=issueTypes%2Clead%2Cdescription", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetProjectsWhenTheStatusCodeIsIncorrect", + expands: []string{"issueTypes", "lead", "description"}, + mockFile: "../mocks/get-projects-v2.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/project?expand=issueTypes%2Clead%2Cdescription", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + }, + + { + name: "GetProjectsWhenTheContextIsNil", + expands: []string{"issueTypes", "lead", "description"}, + mockFile: "../mocks/get-projects-v2.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/project?expand=issueTypes%2Clead%2Cdescription", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + }, + + { + name: "GetProjectsWhenTheResponseBodyHasADifferentFormat", + expands: []string{"issueTypes", "lead", "description"}, + mockFile: "../v3/mocks/empty_json.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/project?expand=issueTypes%2Clead%2Cdescription", + 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 := &ProjectService{client: mockClient} + + gotResult, gotResponse, err := i.Gets(testCase.context, testCase.expands) + + 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 TestProjectService_Get(t *testing.T) { testCases := []struct {