Repository: ctreminiom/go-atlassian
Pull request #126 (2022-06-27 18:04:50)

:sparkles: Mapped the Jira.Permission.Project method.
![image](https://user-images.githubusercontent.com/16035390/176006748-7d362a56-2919-4ab5-af47-99962afca74c.png)



Patch:
diff --git a/jira/mocks/get-permission-projects.json b/jira/mocks/get-permission-projects.json
new file mode 100644
index 0000000..e21a175
--- /dev/null
+++ b/jira/mocks/get-permission-projects.json
@@ -0,0 +1,8 @@
+{
+  "projects": [
+    {
+      "id": 22,
+      "key": "<string>"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jira/v2/issueTypeScreenScheme.go b/jira/v2/issueTypeScreenScheme.go
index ef339b6..3687e8d 100644
--- a/jira/v2/issueTypeScreenScheme.go
+++ b/jira/v2/issueTypeScreenScheme.go
@@ -7,6 +7,7 @@ import (
 	"net/http"
 	"net/url"
 	"strconv"
+	"strings"
 )
 
 type IssueTypeScreenSchemeService struct{ client *Client }
@@ -14,15 +15,30 @@ type IssueTypeScreenSchemeService struct{ client *Client }
 // Gets returns a paginated list of issue type screen schemes.
 // Docs: https://docs.go-atlassian.io/jira-software-cloud/issues/types/screen-scheme#get-issue-type-screen-schemes
 // Atlassian Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-type-screen-schemes/#api-rest-api-2-issuetypescreenscheme-get
-func (i *IssueTypeScreenSchemeService) Gets(ctx context.Context, ids []int, startAt, maxResults int) (
+func (i *IssueTypeScreenSchemeService) Gets(ctx context.Context, options *models.IssueTypeScreenSchemeParamsScheme, startAt, maxResults int) (
 	result *models.IssueTypeScreenSchemePageScheme, response *ResponseScheme, err error) {
 
 	params := url.Values{}
 	params.Add("startAt", strconv.Itoa(startAt))
 	params.Add("maxResults", strconv.Itoa(maxResults))
 
-	for _, id := range ids {
-		params.Add("id", strconv.Itoa(id))
+	if options != nil {
+
+		for _, id := range options.IDs {
+			params.Add("id", strconv.Itoa(id))
+		}
+
+		if options.QueryString != "" {
+			params.Add("queryString", options.QueryString)
+		}
+
+		if options.OrderBy != "orderBy" {
+			params.Add("", options.OrderBy)
+		}
+
+		if len(options.Expand) != 0 {
+			params.Add("expand", strings.Join(options.Expand, ","))
+		}
 	}
 
 	var endpoint = fmt.Sprintf("rest/api/2/issuetypescreenscheme?%v", params.Encode())
diff --git a/jira/v2/permission.go b/jira/v2/permission.go
index 240894f..7af5d02 100644
--- a/jira/v2/permission.go
+++ b/jira/v2/permission.go
@@ -84,3 +84,35 @@ func (p *PermissionService) Check(ctx context.Context, payload *models.Permissio
 
 	return
 }
+
+// Projects returns all the projects where the user is granted a list of project permissions.
+func (p *PermissionService) Projects(ctx context.Context, permissions []string) (result *models.PermittedProjectsScheme, response *ResponseScheme, err error) {
+
+	payload := struct {
+		Permissions []string `json:"permissions,omitempty"`
+	}{
+		Permissions: permissions,
+	}
+
+	payloadAsReader, err := transformStructToReader(&payload)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var endpoint = "/rest/api/2/permissions/project"
+
+	request, err := p.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 = p.client.call(request, &result)
+	if err != nil {
+		return
+	}
+
+	return
+}
diff --git a/jira/v3/issueTypeScreenScheme.go b/jira/v3/issueTypeScreenScheme.go
index 4759c0c..36ac5a2 100644
--- a/jira/v3/issueTypeScreenScheme.go
+++ b/jira/v3/issueTypeScreenScheme.go
@@ -7,6 +7,7 @@ import (
 	"net/http"
 	"net/url"
 	"strconv"
+	"strings"
 )
 
 type IssueTypeScreenSchemeService struct{ client *Client }
@@ -14,15 +15,30 @@ type IssueTypeScreenSchemeService struct{ client *Client }
 // Gets returns a paginated list of issue type screen schemes.
 // Docs: https://docs.go-atlassian.io/jira-software-cloud/issues/types/screen-scheme#get-issue-type-screen-schemes
 // Atlassian Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-type-screen-schemes/#api-rest-api-3-issuetypescreenscheme-get
-func (i *IssueTypeScreenSchemeService) Gets(ctx context.Context, ids []int, startAt, maxResults int) (
+func (i *IssueTypeScreenSchemeService) Gets(ctx context.Context, options *models.IssueTypeScreenSchemeParamsScheme, startAt, maxResults int) (
 	result *models.IssueTypeScreenSchemePageScheme, response *ResponseScheme, err error) {
 
 	params := url.Values{}
 	params.Add("startAt", strconv.Itoa(startAt))
 	params.Add("maxResults", strconv.Itoa(maxResults))
 
-	for _, id := range ids {
-		params.Add("id", strconv.Itoa(id))
+	if options != nil {
+
+		for _, id := range options.IDs {
+			params.Add("id", strconv.Itoa(id))
+		}
+
+		if options.QueryString != "" {
+			params.Add("queryString", options.QueryString)
+		}
+
+		if options.OrderBy != "orderBy" {
+			params.Add("", options.OrderBy)
+		}
+
+		if len(options.Expand) != 0 {
+			params.Add("expand", strings.Join(options.Expand, ","))
+		}
 	}
 
 	var endpoint = fmt.Sprintf("rest/api/3/issuetypescreenscheme?%v", params.Encode())
diff --git a/jira/v3/permission.go b/jira/v3/permission.go
index a295617..4d915b9 100644
--- a/jira/v3/permission.go
+++ b/jira/v3/permission.go
@@ -85,3 +85,35 @@ func (p *PermissionService) Check(ctx context.Context, payload *models.Permissio
 
 	return
 }
+
+// Projects returns all the projects where the user is granted a list of project permissions.
+func (p *PermissionService) Projects(ctx context.Context, permissions []string) (result *models.PermittedProjectsScheme, response *ResponseScheme, err error) {
+
+	payload := struct {
+		Permissions []string `json:"permissions,omitempty"`
+	}{
+		Permissions: permissions,
+	}
+
+	payloadAsReader, err := transformStructToReader(&payload)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var endpoint = "/rest/api/3/permissions/project"
+
+	request, err := p.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 = p.client.call(request, &result)
+	if err != nil {
+		return
+	}
+
+	return
+}
diff --git a/pkg/infra/models/jira_issue_type_screen_scheme.go b/pkg/infra/models/jira_issue_type_screen_scheme.go
index 11d80ea..aee4976 100644
--- a/pkg/infra/models/jira_issue_type_screen_scheme.go
+++ b/pkg/infra/models/jira_issue_type_screen_scheme.go
@@ -1,5 +1,12 @@
 package models
 
+type IssueTypeScreenSchemeParamsScheme struct {
+	IDs         []int
+	QueryString string
+	OrderBy     string
+	Expand      []string
+}
+
 type IssueTypeScreenSchemePageScheme struct {
 	Self       string                         `json:"self,omitempty"`
 	NextPage   string                         `json:"nextPage,omitempty"`
@@ -21,9 +28,10 @@ type IssueTypeScreenSchemeMappingPayloadScheme struct {
 }
 
 type IssueTypeScreenSchemeScheme struct {
-	ID          string `json:"id,omitempty"`
-	Name        string `json:"name,omitempty"`
-	Description string `json:"description,omitempty"`
+	ID          string               `json:"id,omitempty"`
+	Name        string               `json:"name,omitempty"`
+	Description string               `json:"description,omitempty"`
+	Projects    *ProjectSearchScheme `json:"projects,omitempty"`
 }
 
 type IssueTypeScreenScreenCreatedScheme struct {
diff --git a/pkg/infra/models/jira_permission.go b/pkg/infra/models/jira_permission.go
index 57820be..9b375b0 100644
--- a/pkg/infra/models/jira_permission.go
+++ b/pkg/infra/models/jira_permission.go
@@ -50,3 +50,7 @@ type PermissionGrantPayloadScheme struct {
 	Holder     *PermissionGrantHolderScheme `json:"holder,omitempty"`
 	Permission string                       `json:"permission,omitempty"`
 }
+
+type PermittedProjectsScheme struct {
+	Projects []*ProjectIdentifierScheme `json:"projects,omitempty"`
+}
diff --git a/pkg/infra/models/jira_project.go b/pkg/infra/models/jira_project.go
index f06dfcb..be73ac5 100644
--- a/pkg/infra/models/jira_project.go
+++ b/pkg/infra/models/jira_project.go
@@ -156,3 +156,8 @@ type ProjectHierarchyIssueTypeScheme struct {
 	Name     string `json:"name,omitempty"`
 	AvatarID int    `json:"avatarId,omitempty"`
 }
+
+type ProjectIdentifierScheme struct {
+	ID  int    `json:"id,omitempty"`
+	Key string `json:"key,omitempty"`
+}


Test patch:
diff --git a/jira/v2/issueTypeScreenScheme_test.go b/jira/v2/issueTypeScreenScheme_test.go
index 6b7485a..286bfe9 100644
--- a/jira/v2/issueTypeScreenScheme_test.go
+++ b/jira/v2/issueTypeScreenScheme_test.go
@@ -773,7 +773,7 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 
 	testCases := []struct {
 		name               string
-		ids                []int
+		options            *models.IssueTypeScreenSchemeParamsScheme
 		startAt            int
 		maxResults         int
 		mockFile           string
@@ -784,47 +784,49 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 		wantErr            bool
 	}{
 		{
-			name:               "GetIssueTypeSchemesWhenTheParametersAreCorrect",
-			ids:                []int{1000, 1001, 1002},
-			startAt:            0,
-			maxResults:         50,
-			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
-			wantHTTPMethod:     http.MethodGet,
-			endpoint:           "/rest/api/2/issuetypescreenscheme?id=1000&id=1001&id=1002&maxResults=50&startAt=0",
-			context:            context.Background(),
-			wantHTTPCodeReturn: http.StatusOK,
-			wantErr:            false,
-		},
-
-		{
-			name:               "GetIssueTypeSchemesWhenTheIdsAreNotSet",
-			ids:                nil,
+			name: "GetIssueTypeSchemesWhenTheParametersAreCorrect",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
 			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
 			wantHTTPMethod:     http.MethodGet,
-			endpoint:           "/rest/api/2/issuetypescreenscheme?maxResults=50&startAt=0",
+			endpoint:           "/rest/api/2/issuetypescreenscheme?=id&expand=projects&id=1000&id=1001&id=1002&maxResults=50&queryString=DUMMY&startAt=0",
 			context:            context.Background(),
 			wantHTTPCodeReturn: http.StatusOK,
 			wantErr:            false,
 		},
 
 		{
-			name:               "GetIssueTypeSchemesWhenTheEndpointIsIncorrect",
-			ids:                []int{1000, 1001, 1002},
+			name: "GetIssueTypeSchemesWhenTheEndpointIsIncorrect",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
 			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
 			wantHTTPMethod:     http.MethodGet,
-			endpoint:           "/rest/api/2/issuetypescreenschemes?id=1000&id=1001&id=1002&maxResults=50&startAt=0",
+			endpoint:           "/rest/api/2/issuetypescreesnschemes?id=1000&id=1001&id=1002&maxResults=50&startAt=0",
 			context:            context.Background(),
 			wantHTTPCodeReturn: http.StatusOK,
 			wantErr:            true,
 		},
 
 		{
-			name:               "GetIssueTypeSchemesWhenTheRequestMethodIsIncorrect",
-			ids:                []int{1000, 1001, 1002},
+			name: "GetIssueTypeSchemesWhenTheRequestMethodIsIncorrect",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
 			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
@@ -836,8 +838,13 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 		},
 
 		{
-			name:               "GetIssueTypeSchemesWhenTheStatusCodeIsIncorrect",
-			ids:                []int{1000, 1001, 1002},
+			name: "GetIssueTypeSchemesWhenTheStatusCodeIsIncorrect",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
 			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
@@ -849,8 +856,13 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 		},
 
 		{
-			name:               "GetIssueTypeSchemesWhenTheContextIsNil",
-			ids:                []int{1000, 1001, 1002},
+			name: "GetIssueTypeSchemesWhenTheContextIsNil",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
 			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
@@ -862,8 +874,13 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 		},
 
 		{
-			name:               "GetIssueTypeSchemesWhenTheResponseBodyHasADifferentFormat",
-			ids:                []int{1000, 1001, 1002},
+			name: "GetIssueTypeSchemesWhenTheResponseBodyHasADifferentFormat",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
 			mockFile:           "../v3/mocks/empty_json.json",
@@ -902,7 +919,7 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 
 			i := &IssueTypeScreenSchemeService{client: mockClient}
 
-			gotResult, gotResponse, err := i.Gets(testCase.context, testCase.ids, testCase.startAt, testCase.maxResults)
+			gotResult, gotResponse, err := i.Gets(testCase.context, testCase.options, testCase.startAt, testCase.maxResults)
 
 			if testCase.wantErr {
 
diff --git a/jira/v2/permission_test.go b/jira/v2/permission_test.go
index 3459f6b..1421cd5 100644
--- a/jira/v2/permission_test.go
+++ b/jira/v2/permission_test.go
@@ -342,3 +342,130 @@ func TestPermissionService_Check(t *testing.T) {
 	}
 
 }
+
+func TestPermissionService_Projects(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		permissions        []string
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+	}{
+		{
+			name:               "when the parameters are correct",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/2/permissions/project",
+			mockFile:           "../mocks/get-permission-projects.json",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            false,
+		},
+
+		{
+			name:               "When the request method is incorrect",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/2/permissions/project",
+			mockFile:           "../mocks/get-permission-projects.json",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name:               "when the response status is incorrect",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/2/permissions/project",
+			mockFile:           "../mocks/get-permission-projects.json",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusBadRequest,
+			wantErr:            true,
+		},
+
+		{
+			name:               "when the context provided is nil",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/2/permissions/project",
+			mockFile:           "../mocks/get-permission-projects.json",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name:               "when the response body is empty",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/2/permissions/project",
+			mockFile:           "../v3/mocks/empty_json.json",
+			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 := &PermissionService{client: mockClient}
+
+			gotResult, gotResponse, err := i.Projects(testCase.context, testCase.permissions)
+
+			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)
+			}
+		})
+
+	}
+
+}
diff --git a/jira/v3/issueTypeScreenScheme_test.go b/jira/v3/issueTypeScreenScheme_test.go
index 9a52129..8cad0b6 100644
--- a/jira/v3/issueTypeScreenScheme_test.go
+++ b/jira/v3/issueTypeScreenScheme_test.go
@@ -773,7 +773,7 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 
 	testCases := []struct {
 		name               string
-		ids                []int
+		options            *models.IssueTypeScreenSchemeParamsScheme
 		startAt            int
 		maxResults         int
 		mockFile           string
@@ -784,50 +784,52 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 		wantErr            bool
 	}{
 		{
-			name:               "GetIssueTypeSchemesWhenTheParametersAreCorrect",
-			ids:                []int{1000, 1001, 1002},
-			startAt:            0,
-			maxResults:         50,
-			mockFile:           "./mocks/get-issue-type-screen-schemes.json",
-			wantHTTPMethod:     http.MethodGet,
-			endpoint:           "/rest/api/3/issuetypescreenscheme?id=1000&id=1001&id=1002&maxResults=50&startAt=0",
-			context:            context.Background(),
-			wantHTTPCodeReturn: http.StatusOK,
-			wantErr:            false,
-		},
-
-		{
-			name:               "GetIssueTypeSchemesWhenTheIdsAreNotSet",
-			ids:                nil,
+			name: "GetIssueTypeSchemesWhenTheParametersAreCorrect",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
-			mockFile:           "./mocks/get-issue-type-screen-schemes.json",
+			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
 			wantHTTPMethod:     http.MethodGet,
-			endpoint:           "/rest/api/3/issuetypescreenscheme?maxResults=50&startAt=0",
+			endpoint:           "/rest/api/3/issuetypescreenscheme?=id&expand=projects&id=1000&id=1001&id=1002&maxResults=50&queryString=DUMMY&startAt=0",
 			context:            context.Background(),
 			wantHTTPCodeReturn: http.StatusOK,
 			wantErr:            false,
 		},
 
 		{
-			name:               "GetIssueTypeSchemesWhenTheEndpointIsIncorrect",
-			ids:                []int{1000, 1001, 1002},
+			name: "GetIssueTypeSchemesWhenTheEndpointIsIncorrect",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
-			mockFile:           "./mocks/get-issue-type-screen-schemes.json",
+			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
 			wantHTTPMethod:     http.MethodGet,
-			endpoint:           "/rest/api/3/issuetypescreenschemes?id=1000&id=1001&id=1002&maxResults=50&startAt=0",
+			endpoint:           "/rest/api/3/issuetypescreesnschemes?id=1000&id=1001&id=1002&maxResults=50&startAt=0",
 			context:            context.Background(),
 			wantHTTPCodeReturn: http.StatusOK,
 			wantErr:            true,
 		},
 
 		{
-			name:               "GetIssueTypeSchemesWhenTheRequestMethodIsIncorrect",
-			ids:                []int{1000, 1001, 1002},
+			name: "GetIssueTypeSchemesWhenTheRequestMethodIsIncorrect",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
-			mockFile:           "./mocks/get-issue-type-screen-schemes.json",
+			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
 			wantHTTPMethod:     http.MethodPost,
 			endpoint:           "/rest/api/3/issuetypescreenscheme?id=1000&id=1001&id=1002&maxResults=50&startAt=0",
 			context:            context.Background(),
@@ -836,11 +838,16 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 		},
 
 		{
-			name:               "GetIssueTypeSchemesWhenTheStatusCodeIsIncorrect",
-			ids:                []int{1000, 1001, 1002},
+			name: "GetIssueTypeSchemesWhenTheStatusCodeIsIncorrect",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
-			mockFile:           "./mocks/get-issue-type-screen-schemes.json",
+			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
 			wantHTTPMethod:     http.MethodGet,
 			endpoint:           "/rest/api/3/issuetypescreenscheme?id=1000&id=1001&id=1002&maxResults=50&startAt=0",
 			context:            context.Background(),
@@ -849,11 +856,16 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 		},
 
 		{
-			name:               "GetIssueTypeSchemesWhenTheContextIsNil",
-			ids:                []int{1000, 1001, 1002},
+			name: "GetIssueTypeSchemesWhenTheContextIsNil",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
-			mockFile:           "./mocks/get-issue-type-screen-schemes.json",
+			mockFile:           "../v3/mocks/get-issue-type-screen-schemes.json",
 			wantHTTPMethod:     http.MethodGet,
 			endpoint:           "/rest/api/3/issuetypescreenscheme?id=1000&id=1001&id=1002&maxResults=50&startAt=0",
 			context:            nil,
@@ -862,11 +874,16 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 		},
 
 		{
-			name:               "GetIssueTypeSchemesWhenTheResponseBodyHasADifferentFormat",
-			ids:                []int{1000, 1001, 1002},
+			name: "GetIssueTypeSchemesWhenTheResponseBodyHasADifferentFormat",
+			options: &models.IssueTypeScreenSchemeParamsScheme{
+				IDs:         []int{1000, 1001, 1002},
+				QueryString: "DUMMY",
+				OrderBy:     "id",
+				Expand:      []string{"projects"},
+			},
 			startAt:            0,
 			maxResults:         50,
-			mockFile:           "./mocks/empty_json.json",
+			mockFile:           "../v3/mocks/empty_json.json",
 			wantHTTPMethod:     http.MethodGet,
 			endpoint:           "/rest/api/3/issuetypescreenscheme?id=1000&id=1001&id=1002&maxResults=50&startAt=0",
 			context:            context.Background(),
@@ -902,7 +919,7 @@ func TestIssueTypeScreenSchemeService_Gets(t *testing.T) {
 
 			i := &IssueTypeScreenSchemeService{client: mockClient}
 
-			gotResult, gotResponse, err := i.Gets(testCase.context, testCase.ids, testCase.startAt, testCase.maxResults)
+			gotResult, gotResponse, err := i.Gets(testCase.context, testCase.options, testCase.startAt, testCase.maxResults)
 
 			if testCase.wantErr {
 
diff --git a/jira/v3/permission_test.go b/jira/v3/permission_test.go
index 49a6451..76188af 100644
--- a/jira/v3/permission_test.go
+++ b/jira/v3/permission_test.go
@@ -342,3 +342,130 @@ func TestPermissionService_Check(t *testing.T) {
 	}
 
 }
+
+func TestPermissionService_Projects(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		permissions        []string
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+	}{
+		{
+			name:               "when the parameters are correct",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/3/permissions/project",
+			mockFile:           "../mocks/get-permission-projects.json",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            false,
+		},
+
+		{
+			name:               "When the request method is incorrect",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/3/permissions/project",
+			mockFile:           "../mocks/get-permission-projects.json",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name:               "when the response status is incorrect",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/3/permissions/project",
+			mockFile:           "../mocks/get-permission-projects.json",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusBadRequest,
+			wantErr:            true,
+		},
+
+		{
+			name:               "when the context provided is nil",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/3/permissions/project",
+			mockFile:           "../mocks/get-permission-projects.json",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name:               "when the response body is empty",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/3/permissions/project",
+			mockFile:           "../v3/mocks/empty_json.json",
+			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 := &PermissionService{client: mockClient}
+
+			gotResult, gotResponse, err := i.Projects(testCase.context, testCase.permissions)
+
+			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)
+			}
+		})
+
+	}
+
+}