From 5536973de177a3c7a8002bc9e054b5aa95e0ec8c Mon Sep 17 00:00:00 2001 From: Carlos Treminio Date: Sun, 30 Jan 2022 00:56:57 -0600 Subject: [PATCH 1/3] :memo: Added the avelino/awesome-go badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index abf0e413..dbfa1b76 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ +

Communicate with the [Atlassian API's](https://developer.atlassian.com/cloud/) quickly and easily From f6049e4a2907ff758827d8ec217e5121f65386e9 Mon Sep 17 00:00:00 2001 From: Carlos Treminio Date: Sun, 30 Jan 2022 13:03:39 -0600 Subject: [PATCH 2/3] :sparkles: Added the Jira Project Feature service --- jira/mocks/get-project-features.json | 24 ++ jira/v2/jira.go | 1 + jira/v2/project.go | 1 + jira/v2/projectFeature.go | 73 ++++++ jira/v2/projectFeature_test.go | 313 +++++++++++++++++++++++ jira/v3/jira.go | 1 + jira/v3/project.go | 1 + jira/v3/projectFeature.go | 73 ++++++ jira/v3/projectFeature_test.go | 313 +++++++++++++++++++++++ pkg/infra/models/errors.go | 1 + pkg/infra/models/jira_project_feature.go | 16 ++ 11 files changed, 817 insertions(+) create mode 100644 jira/mocks/get-project-features.json create mode 100644 jira/v2/projectFeature.go create mode 100644 jira/v2/projectFeature_test.go create mode 100644 jira/v3/projectFeature.go create mode 100644 jira/v3/projectFeature_test.go create mode 100644 pkg/infra/models/jira_project_feature.go diff --git a/jira/mocks/get-project-features.json b/jira/mocks/get-project-features.json new file mode 100644 index 00000000..fe3ee32b --- /dev/null +++ b/jira/mocks/get-project-features.json @@ -0,0 +1,24 @@ +{ + "features": [ + { + "projectId": 10001, + "state": "ENABLED", + "toggleLocked": true, + "feature": "jsw.classic.roadmap", + "prerequisites": [], + "localisedName": "Roadmap", + "localisedDescription": "Your roadmap is an optimized location to create and manage your epics.", + "imageUri": "https://jira.atlassian.com/s/sb53l8/b/3/ab8a7691e4738b4f147e293f0864adfd5b8d3c85/_/download/resources/com.atlassian.jira.rest:classic-project-features/simple-roadmap-feature.svg" + }, + { + "projectId": 10001, + "state": "ENABLED", + "toggleLocked": true, + "feature": "jsw.classic.backlog", + "prerequisites": [], + "localisedName": "Backlog", + "localisedDescription": "Plan and prioritize work in a dedicated space. To enable and configure the backlog for each board, go to board settings.", + "imageUri": "https://jira.atlassian.com/s/sb53l8/b/3/ab8a7691e4738b4f147e293f0864adfd5b8d3c85/_/download/resources/com.atlassian.jira.rest:classic-project-features/simple-backlog-feature.svg" + } + ] +} \ No newline at end of file diff --git a/jira/v2/jira.go b/jira/v2/jira.go index 41f1ae12..bf948bcc 100644 --- a/jira/v2/jira.go +++ b/jira/v2/jira.go @@ -145,6 +145,7 @@ func New(httpClient *http.Client, site string) (client *Client, err error) { Type: &ProjectTypeService{client: client}, Version: &ProjectVersionService{client: client}, + Feature: &ProjectFeatureService{client: client}, } client.User = &UserService{ diff --git a/jira/v2/project.go b/jira/v2/project.go index fe105f6d..9c20589d 100644 --- a/jira/v2/project.go +++ b/jira/v2/project.go @@ -19,6 +19,7 @@ type ProjectService struct { Role *ProjectRoleService Type *ProjectTypeService Version *ProjectVersionService + Feature *ProjectFeatureService } // Create creates a project based on a project type template, as shown in the following table: diff --git a/jira/v2/projectFeature.go b/jira/v2/projectFeature.go new file mode 100644 index 00000000..0a73b2b3 --- /dev/null +++ b/jira/v2/projectFeature.go @@ -0,0 +1,73 @@ +package v2 + +import ( + "context" + "fmt" + "github.com/ctreminiom/go-atlassian/pkg/infra/models" + "net/http" +) + +type ProjectFeatureService struct{ client *Client } + +// Gets returns the list of features for a project. +// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/features#get-project-features +func (p *ProjectFeatureService) Gets(ctx context.Context, projectKeyOrID string) (result *models.ProjectFeaturesScheme, response *ResponseScheme, err error) { + + if projectKeyOrID == "" { + return nil, nil, models.ErrNoProjectIDError + } + + var endpoint = fmt.Sprintf("rest/api/2/project/%v/features", projectKeyOrID) + + request, err := p.client.newRequest(ctx, http.MethodGet, endpoint, nil) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + + response, err = p.client.call(request, &result) + if err != nil { + return + } + + return +} + +// Set sets the state of a project feature. +// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/features#set-project-feature-state +func (p *ProjectFeatureService) Set(ctx context.Context, projectKeyOrID, featureKey, state string) (result *models.ProjectFeaturesScheme, response *ResponseScheme, err error) { + + if projectKeyOrID == "" { + return nil, nil, models.ErrNoProjectIDError + } + + if featureKey == "" { + return nil, nil, models.ErrNoProjectFeatureKeyError + } + + payload := struct { + State string `json:"state,omitempty"` + }{ + State: state, + } + + payloadAsReader, _ := transformStructToReader(&payload) + + var endpoint = fmt.Sprintf("rest/api/2/project/%v/features/%v", projectKeyOrID, featureKey) + + request, err := p.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 = p.client.call(request, &result) + if err != nil { + return + } + + return +} diff --git a/jira/v2/projectFeature_test.go b/jira/v2/projectFeature_test.go new file mode 100644 index 00000000..01aedb4c --- /dev/null +++ b/jira/v2/projectFeature_test.go @@ -0,0 +1,313 @@ +package v2 + +import ( + "context" + "fmt" + "github.com/stretchr/testify/assert" + "net/http" + "net/url" + "testing" +) + +func Test_ProjectFeatureService_Gets(t *testing.T) { + + testCases := []struct { + name string + projectKeyOrID string + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + expectedError string + }{ + { + name: "when the parameters are correct", + projectKeyOrID: "DUMMY", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/project/DUMMY/features", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + }, + { + name: "when the project key or id not provided", + projectKeyOrID: "", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/project/DUMMY/features", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "jira: no project id set", + }, + { + name: "when the context is not provided", + projectKeyOrID: "DUMMY", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/project/DUMMY/features", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "request creation failed: net/http: nil Context", + }, + + { + name: "when the response status is incorrect", + projectKeyOrID: "DUMMY", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/project/DUMMY/features", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + expectedError: "request failed. Please analyze the request body for more details. Status Code: 400", + }, + + { + name: "when the response body is empty", + projectKeyOrID: "DUMMY", + mockFile: "../mocks/empty-json.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/2/project/DUMMY/features", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "unexpected end of JSON input", + }, + } + + 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 := &ProjectFeatureService{client: mockClient} + + gotResult, gotResponse, err := i.Gets(testCase.context, testCase.projectKeyOrID) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.EqualError(t, err, testCase.expectedError) + + if gotResponse != nil { + t.Logf("HTTP Code Wanted: %v, HTTP Code Returned: %v", testCase.wantHTTPCodeReturn, gotResponse.Code) + } + + } 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 Test_ProjectFeatureService_Set(t *testing.T) { + + testCases := []struct { + name string + projectKeyOrID string + featureKey, state string + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + expectedError string + }{ + { + name: "when the parameters are correct", + projectKeyOrID: "DUMMY", + featureKey: "jsw.classic.roadmap", + state: "ENABLED", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/project/DUMMY/features/jsw.classic.roadmap", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + }, + + { + name: "when the feature key is not provided", + projectKeyOrID: "DUMMY", + featureKey: "", + state: "ENABLED", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/project/DUMMY/features/jsw.classic.roadmap", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "jira: no project feature key set", + }, + { + name: "when the project key or id not provided", + projectKeyOrID: "", + featureKey: "jsw.classic.roadmap", + state: "ENABLED", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/project/DUMMY/features/jsw.classic.roadmap", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "jira: no project id set", + }, + { + name: "when the context is not provided", + projectKeyOrID: "DUMMY", + featureKey: "jsw.classic.roadmap", + state: "ENABLED", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/project/DUMMY/features/jsw.classic.roadmap", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "request creation failed: net/http: nil Context", + }, + + { + name: "when the response status is incorrect", + projectKeyOrID: "DUMMY", + featureKey: "jsw.classic.roadmap", + state: "ENABLED", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/project/DUMMY/features/jsw.classic.roadmap", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + expectedError: "request failed. Please analyze the request body for more details. Status Code: 400", + }, + + { + name: "when the response body is empty", + projectKeyOrID: "DUMMY", + featureKey: "jsw.classic.roadmap", + state: "ENABLED", + mockFile: "../mocks/empty-json.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/2/project/DUMMY/features/jsw.classic.roadmap", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "unexpected end of JSON input", + }, + } + + 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 := &ProjectFeatureService{client: mockClient} + + gotResult, gotResponse, err := i.Set(testCase.context, testCase.projectKeyOrID, testCase.featureKey, testCase.state) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.EqualError(t, err, testCase.expectedError) + + if gotResponse != nil { + t.Logf("HTTP Code Wanted: %v, HTTP Code Returned: %v", testCase.wantHTTPCodeReturn, gotResponse.Code) + } + + } 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/jira.go b/jira/v3/jira.go index 8df437a4..21310f3c 100644 --- a/jira/v3/jira.go +++ b/jira/v3/jira.go @@ -147,6 +147,7 @@ func New(httpClient *http.Client, site string) (client *Client, err error) { Type: &ProjectTypeService{client: client}, Version: &ProjectVersionService{client: client}, + Feature: &ProjectFeatureService{client: client}, } client.User = &UserService{ diff --git a/jira/v3/project.go b/jira/v3/project.go index 59eb2fbb..8bb08dcc 100644 --- a/jira/v3/project.go +++ b/jira/v3/project.go @@ -19,6 +19,7 @@ type ProjectService struct { Role *ProjectRoleService Type *ProjectTypeService Version *ProjectVersionService + Feature *ProjectFeatureService } // Create creates a project based on a project type template, as shown in the following table: diff --git a/jira/v3/projectFeature.go b/jira/v3/projectFeature.go new file mode 100644 index 00000000..668d5eeb --- /dev/null +++ b/jira/v3/projectFeature.go @@ -0,0 +1,73 @@ +package v3 + +import ( + "context" + "fmt" + "github.com/ctreminiom/go-atlassian/pkg/infra/models" + "net/http" +) + +type ProjectFeatureService struct{ client *Client } + +// Gets returns the list of features for a project. +// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/features#get-project-features +func (p *ProjectFeatureService) Gets(ctx context.Context, projectKeyOrID string) (result *models.ProjectFeaturesScheme, response *ResponseScheme, err error) { + + if projectKeyOrID == "" { + return nil, nil, models.ErrNoProjectIDError + } + + var endpoint = fmt.Sprintf("rest/api/3/project/%v/features", projectKeyOrID) + + request, err := p.client.newRequest(ctx, http.MethodGet, endpoint, nil) + if err != nil { + return + } + + request.Header.Set("Accept", "application/json") + + response, err = p.client.call(request, &result) + if err != nil { + return + } + + return +} + +// Set sets the state of a project feature. +// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/features#set-project-feature-state +func (p *ProjectFeatureService) Set(ctx context.Context, projectKeyOrID, featureKey, state string) (result *models.ProjectFeaturesScheme, response *ResponseScheme, err error) { + + if projectKeyOrID == "" { + return nil, nil, models.ErrNoProjectIDError + } + + if featureKey == "" { + return nil, nil, models.ErrNoProjectFeatureKeyError + } + + payload := struct { + State string `json:"state,omitempty"` + }{ + State: state, + } + + payloadAsReader, _ := transformStructToReader(&payload) + + var endpoint = fmt.Sprintf("rest/api/3/project/%v/features/%v", projectKeyOrID, featureKey) + + request, err := p.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 = p.client.call(request, &result) + if err != nil { + return + } + + return +} diff --git a/jira/v3/projectFeature_test.go b/jira/v3/projectFeature_test.go new file mode 100644 index 00000000..16681568 --- /dev/null +++ b/jira/v3/projectFeature_test.go @@ -0,0 +1,313 @@ +package v3 + +import ( + "context" + "fmt" + "github.com/stretchr/testify/assert" + "net/http" + "net/url" + "testing" +) + +func Test_ProjectFeatureService_Gets(t *testing.T) { + + testCases := []struct { + name string + projectKeyOrID string + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + expectedError string + }{ + { + name: "when the parameters are correct", + projectKeyOrID: "DUMMY", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/3/project/DUMMY/features", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + }, + { + name: "when the project key or id not provided", + projectKeyOrID: "", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/3/project/DUMMY/features", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "jira: no project id set", + }, + { + name: "when the context is not provided", + projectKeyOrID: "DUMMY", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/3/project/DUMMY/features", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "request creation failed: net/http: nil Context", + }, + + { + name: "when the response status is incorrect", + projectKeyOrID: "DUMMY", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/3/project/DUMMY/features", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + expectedError: "request failed. Please analyze the request body for more details. Status Code: 400", + }, + + { + name: "when the response body is empty", + projectKeyOrID: "DUMMY", + mockFile: "../mocks/empty-json.json", + wantHTTPMethod: http.MethodGet, + endpoint: "/rest/api/3/project/DUMMY/features", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "unexpected end of JSON input", + }, + } + + 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 := &ProjectFeatureService{client: mockClient} + + gotResult, gotResponse, err := i.Gets(testCase.context, testCase.projectKeyOrID) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.EqualError(t, err, testCase.expectedError) + + if gotResponse != nil { + t.Logf("HTTP Code Wanted: %v, HTTP Code Returned: %v", testCase.wantHTTPCodeReturn, gotResponse.Code) + } + + } 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 Test_ProjectFeatureService_Set(t *testing.T) { + + testCases := []struct { + name string + projectKeyOrID string + featureKey, state string + mockFile string + wantHTTPMethod string + endpoint string + context context.Context + wantHTTPCodeReturn int + wantErr bool + expectedError string + }{ + { + name: "when the parameters are correct", + projectKeyOrID: "DUMMY", + featureKey: "jsw.classic.roadmap", + state: "ENABLED", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/project/DUMMY/features/jsw.classic.roadmap", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + }, + + { + name: "when the feature key is not provided", + projectKeyOrID: "DUMMY", + featureKey: "", + state: "ENABLED", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/project/DUMMY/features/jsw.classic.roadmap", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "jira: no project feature key set", + }, + { + name: "when the project key or id not provided", + projectKeyOrID: "", + featureKey: "jsw.classic.roadmap", + state: "ENABLED", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/project/DUMMY/features/jsw.classic.roadmap", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "jira: no project id set", + }, + { + name: "when the context is not provided", + projectKeyOrID: "DUMMY", + featureKey: "jsw.classic.roadmap", + state: "ENABLED", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/project/DUMMY/features/jsw.classic.roadmap", + context: nil, + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "request creation failed: net/http: nil Context", + }, + + { + name: "when the response status is incorrect", + projectKeyOrID: "DUMMY", + featureKey: "jsw.classic.roadmap", + state: "ENABLED", + mockFile: "../mocks/get-project-features.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/project/DUMMY/features/jsw.classic.roadmap", + context: context.Background(), + wantHTTPCodeReturn: http.StatusBadRequest, + wantErr: true, + expectedError: "request failed. Please analyze the request body for more details. Status Code: 400", + }, + + { + name: "when the response body is empty", + projectKeyOrID: "DUMMY", + featureKey: "jsw.classic.roadmap", + state: "ENABLED", + mockFile: "../mocks/empty-json.json", + wantHTTPMethod: http.MethodPut, + endpoint: "/rest/api/3/project/DUMMY/features/jsw.classic.roadmap", + context: context.Background(), + wantHTTPCodeReturn: http.StatusOK, + wantErr: true, + expectedError: "unexpected end of JSON input", + }, + } + + 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 := &ProjectFeatureService{client: mockClient} + + gotResult, gotResponse, err := i.Set(testCase.context, testCase.projectKeyOrID, testCase.featureKey, testCase.state) + + if testCase.wantErr { + + if err != nil { + t.Logf("error returned: %v", err.Error()) + } + + assert.EqualError(t, err, testCase.expectedError) + + if gotResponse != nil { + t.Logf("HTTP Code Wanted: %v, HTTP Code Returned: %v", testCase.wantHTTPCodeReturn, gotResponse.Code) + } + + } 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/pkg/infra/models/errors.go b/pkg/infra/models/errors.go index e7e82938..e9991f54 100644 --- a/pkg/infra/models/errors.go +++ b/pkg/infra/models/errors.go @@ -56,6 +56,7 @@ var ( ErrNoReaderError = errors.New("jira: no reader set") ErrNoCommentIDError = errors.New("jira: no comment id set") ErrNoProjectIDError = errors.New("jira: no project id set") + ErrNoProjectFeatureKeyError = errors.New("jira: no project feature key set") ErrNoFieldIDError = errors.New("jira: no field id set") ErrNoFieldContextIDError = errors.New("jira: no field context id set") ErrNoIssueTypesError = errors.New("jira: no issue types id's set") diff --git a/pkg/infra/models/jira_project_feature.go b/pkg/infra/models/jira_project_feature.go new file mode 100644 index 00000000..8bf0ca45 --- /dev/null +++ b/pkg/infra/models/jira_project_feature.go @@ -0,0 +1,16 @@ +package models + +type ProjectFeaturesScheme struct { + Features []*ProjectFeatureScheme `json:"features,omitempty"` +} + +type ProjectFeatureScheme struct { + ProjectID int `json:"projectId,omitempty"` + State string `json:"state,omitempty"` + ToggleLocked bool `json:"toggleLocked,omitempty"` + Feature string `json:"feature,omitempty"` + Prerequisites []string `json:"prerequisites,omitempty"` + LocalisedName string `json:"localisedName,omitempty"` + LocalisedDescription string `json:"localisedDescription,omitempty"` + ImageURI string `json:"imageUri,omitempty"` +} From 7a14d84209e149ce6acc72d2715762c32bfeb8f8 Mon Sep 17 00:00:00 2001 From: Carlos Treminio Date: Sun, 30 Jan 2022 22:29:42 -0600 Subject: [PATCH 3/3] :label: Parsed more structures 1. Added the Confluence History tag under the Confluence Content Scheme History. 2. Fixed the Content Version scheme (it was using the Jira version format). --- pkg/infra/models/confluence_content.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/infra/models/confluence_content.go b/pkg/infra/models/confluence_content.go index c78e1a30..1942ee44 100644 --- a/pkg/infra/models/confluence_content.go +++ b/pkg/infra/models/confluence_content.go @@ -46,6 +46,7 @@ type ContentScheme struct { Version *ContentVersionScheme `json:"version,omitempty"` Extensions *ContentExtensionScheme `json:"extensions,omitempty"` Ancestors []*ContentScheme `json:"ancestors,omitempty"` + History *ContentHistoryScheme `json:"history,omitempty"` } type ContentExtensionScheme struct { @@ -141,10 +142,10 @@ type ContentHistoryScheme struct { Latest bool `json:"latest,omitempty"` CreatedBy *ContentUserScheme `json:"createdBy,omitempty"` CreatedDate string `json:"createdDate,omitempty"` - LastUpdated *VersionScheme `json:"lastUpdated,omitempty"` - PreviousVersion *VersionScheme `json:"previousVersion,omitempty"` + LastUpdated *ContentVersionScheme `json:"lastUpdated,omitempty"` + PreviousVersion *ContentVersionScheme `json:"previousVersion,omitempty"` Contributors *ContentHistoryContributorsScheme `json:"contributors,omitempty"` - NextVersion *VersionScheme `json:"nextVersion,omitempty"` + NextVersion *ContentVersionScheme `json:"nextVersion,omitempty"` Expandable *ExpandableScheme `json:"_expandable,omitempty"` Links *LinkScheme `json:"_links,omitempty"` }