Repository: ctreminiom/go-atlassian
Pull request #78 (2021-11-11 05:13:45)

:sparkles: Added the jira workflow service


Patch:
diff --git a/jira/mocks/workflow-created.json b/jira/mocks/workflow-created.json
new file mode 100644
index 0000000..bdbb793
--- /dev/null
+++ b/jira/mocks/workflow-created.json
@@ -0,0 +1,4 @@
+{
+  "name": "Workflow 1",
+  "entityId": "c621f12b-4aff-4b3d-a717-7d9361f46264"
+}
\ No newline at end of file
diff --git a/jira/v2/jira.go b/jira/v2/jira.go
index b22c0a7..d3c3fce 100644
--- a/jira/v2/jira.go
+++ b/jira/v2/jira.go
@@ -31,6 +31,7 @@ type Client struct {
 	Task       *TaskService
 	User       *UserService
 	MySelf     *MySelfService
+	Workflow   *WorkflowService
 }
 
 const (
@@ -148,6 +149,7 @@ func New(httpClient *http.Client, site string) (client *Client, err error) {
 	}
 
 	client.MySelf = &MySelfService{client: client}
+	client.Workflow = &WorkflowService{client: client}
 	return
 }
 
diff --git a/jira/v2/workflow.go b/jira/v2/workflow.go
new file mode 100644
index 0000000..63b1c8e
--- /dev/null
+++ b/jira/v2/workflow.go
@@ -0,0 +1,104 @@
+package v2
+
+import (
+	"context"
+	"fmt"
+	models "github.com/ctreminiom/go-atlassian/pkg/infra/models/jira"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+)
+
+type WorkflowService struct {
+	client *Client
+}
+
+func (w *WorkflowService) Create(ctx context.Context, payload *models.WorkflowPayloadScheme) (result *models.WorkflowCreatedResponseScheme,
+	response *ResponseScheme, err error) {
+
+	payloadAsReader, err := transformStructToReader(payload)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	endpoint := "/rest/api/2/workflow"
+	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
+}
+
+// Gets returns a paginated list of published classic workflows.
+// When workflow names are specified, details of those workflows are returned.
+// Otherwise, all published classic workflows are returned.
+func (w *WorkflowService) Gets(ctx context.Context, workflowNames, expand []string, startAt, maxResults int) (result *models.WorkflowPageScheme,
+	response *ResponseScheme, err error) {
+
+	params := url.Values{}
+	params.Add("startAt", strconv.Itoa(startAt))
+	params.Add("maxResults", strconv.Itoa(maxResults))
+
+	for _, workflowName := range workflowNames {
+		params.Add("workflowName", workflowName)
+	}
+
+	if len(expand) != 0 {
+		params.Add("expand", strings.Join(expand, ","))
+	}
+
+	var endpoint = fmt.Sprintf("/rest/api/2/workflow/search?%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
+}
+
+// Delete deletes a workflow.
+//
+// The workflow cannot be deleted if it is:
+//
+// an active workflow.
+// a system workflow.
+// associated with any workflow scheme.
+// associated with any draft workflow scheme.
+func (w *WorkflowService) Delete(ctx context.Context, workflowID string) (response *ResponseScheme, err error) {
+
+	if len(workflowID) == 0 {
+		return nil, models.ErrNoWorkflowIDError
+	}
+
+	var endpoint = fmt.Sprintf("/rest/api/2/workflow/%v", workflowID)
+
+	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
+}
diff --git a/jira/v3/workflow.go b/jira/v3/workflow.go
index dd45231..3009fff 100644
--- a/jira/v3/workflow.go
+++ b/jira/v3/workflow.go
@@ -15,6 +15,31 @@ type WorkflowService struct {
 	Scheme *WorkflowSchemeService
 }
 
+func (w *WorkflowService) Create(ctx context.Context, payload *models.WorkflowPayloadScheme) (result *models.WorkflowCreatedResponseScheme,
+	response *ResponseScheme, err error) {
+
+	payloadAsReader, err := transformStructToReader(payload)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	endpoint := "/rest/api/3/workflow"
+	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
+}
+
 // Gets returns a paginated list of published classic workflows.
 // When workflow names are specified, details of those workflows are returned.
 // Otherwise, all published classic workflows are returned.
diff --git a/pkg/infra/models/jira/workflow.go b/pkg/infra/models/jira/workflow.go
index 1d7db26..a057467 100644
--- a/pkg/infra/models/jira/workflow.go
+++ b/pkg/infra/models/jira/workflow.go
@@ -57,3 +57,27 @@ type WorkflowStatusScheme struct {
 type WorkflowStatusPropertiesScheme struct {
 	IssueEditable bool `json:"issueEditable"`
 }
+
+type WorkflowCreatedResponseScheme struct {
+	Name     string `json:"name,omitempty"`
+	EntityID string `json:"entityId,omitempty"`
+}
+
+type WorkflowPayloadScheme struct {
+	Name        string                             `json:"name,omitempty"`
+	Description string                             `json:"description,omitempty"`
+	Statuses    []*WorkflowTransitionScreenScheme  `json:"statuses,omitempty"`
+	Transitions []*WorkflowTransitionPayloadScheme `json:"transitions,omitempty"`
+}
+
+type WorkflowTransitionPayloadScheme struct {
+	Name   string                                 `json:"name,omitempty"`
+	From   []string                               `json:"from,omitempty"`
+	To     string                                 `json:"to,omitempty"`
+	Type   string                                 `json:"type,omitempty"`
+	Screen *WorkflowTransitionScreenPayloadScheme `json:"screen,omitempty"`
+}
+
+type WorkflowTransitionScreenPayloadScheme struct {
+	ID string `json:"id"`
+}


Test patch:
diff --git a/jira/v2/workflow_test.go b/jira/v2/workflow_test.go
new file mode 100644
index 0000000..ab9f5a5
--- /dev/null
+++ b/jira/v2/workflow_test.go
@@ -0,0 +1,640 @@
+package v2
+
+import (
+	"context"
+	"fmt"
+	models "github.com/ctreminiom/go-atlassian/pkg/infra/models/jira"
+	"github.com/stretchr/testify/assert"
+	"net/http"
+	"net/url"
+	"testing"
+)
+
+func TestWorkflowService_Delete(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		workflowID         string
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+	}{
+		{
+			name:               "DeleteWorkflowWhenTheParametersAreCorrect",
+			workflowID:         "as49949ja-asjdasjd94-jasdjasjd",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/2/workflow/as49949ja-asjdasjd94-jasdjasjd",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusNoContent,
+			wantErr:            false,
+		},
+
+		{
+			name:               "DeleteWorkflowWhenTheWorkflowIDIsNotSet",
+			workflowID:         "",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/2/workflow/as49949ja-asjdasjd94-jasdjasjd",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusNoContent,
+			wantErr:            true,
+		},
+
+		{
+			name:               "DeleteWorkflowWhenTheContextIsNotSet",
+			workflowID:         "as49949ja-asjdasjd94-jasdjasjd",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/2/workflow/as49949ja-asjdasjd94-jasdjasjd",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusNoContent,
+			wantErr:            true,
+		},
+
+		{
+			name:               "DeleteWorkflowWhenTheRequestMethodIsIncorrect",
+			workflowID:         "as49949ja-asjdasjd94-jasdjasjd",
+			wantHTTPMethod:     http.MethodHead,
+			endpoint:           "/rest/api/2/workflow/as49949ja-asjdasjd94-jasdjasjd",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusNoContent,
+			wantErr:            true,
+		},
+
+		{
+			name:               "DeleteWorkflowWhenTheStatusCodeIsIncorrect",
+			workflowID:         "as49949ja-asjdasjd94-jasdjasjd",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/2/workflow/as49949ja-asjdasjd94-jasdjasjd",
+			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 := &WorkflowService{client: mockClient}
+
+			gotResponse, err := i.Delete(testCase.context, testCase.workflowID)
+
+			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 TestWorkflowService_Gets(t *testing.T) {
+
+	testCases := []struct {
+		name                  string
+		startAt, maxResults   int
+		mockFile              string
+		workflowNames, expand []string
+		wantHTTPMethod        string
+		endpoint              string
+		context               context.Context
+		wantHTTPCodeReturn    int
+		wantErr               bool
+	}{
+		{
+			name:               "GetsWorkflowsWheTheParametersAreCorrect",
+			startAt:            0,
+			maxResults:         50,
+			mockFile:           "../v3/mocks/get-workflows.json",
+			workflowNames:      []string{"workflow name 1", "workflow name 2"},
+			expand:             []string{"transitions", "transitions.rules", "default"},
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/workflow/search?expand=transitions%2Ctransitions.rules%2Cdefault&maxResults=50&startAt=0&workflowName=workflow+name+1&workflowName=workflow+name+2",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            false,
+		},
+
+		{
+			name:               "GetsWorkflowsWheTheContextIsNotSet",
+			startAt:            0,
+			maxResults:         50,
+			mockFile:           "../v3/mocks/get-workflows.json",
+			workflowNames:      []string{"workflow name 1", "workflow name 2"},
+			expand:             []string{"transitions", "transitions.rules", "default"},
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/workflow/search?expand=transitions%2Ctransitions.rules%2Cdefault&maxResults=50&startAt=0&workflowName=workflow+name+1&workflowName=workflow+name+2",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name:               "GetsWorkflowsWheTheRequestMethodIsIncorrect",
+			startAt:            0,
+			maxResults:         50,
+			mockFile:           "../v3/mocks/get-workflows.json",
+			workflowNames:      []string{"workflow name 1", "workflow name 2"},
+			expand:             []string{"transitions", "transitions.rules", "default"},
+			wantHTTPMethod:     http.MethodHead,
+			endpoint:           "/rest/api/2/workflow/search?expand=transitions%2Ctransitions.rules%2Cdefault&maxResults=50&startAt=0&workflowName=workflow+name+1&workflowName=workflow+name+2",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name:               "GetsWorkflowsWheTheStatusCodeIsIncorrect",
+			startAt:            0,
+			maxResults:         50,
+			mockFile:           "../v3/mocks/get-workflows.json",
+			workflowNames:      []string{"workflow name 1", "workflow name 2"},
+			expand:             []string{"transitions", "transitions.rules", "default"},
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/workflow/search?expand=transitions%2Ctransitions.rules%2Cdefault&maxResults=50&startAt=0&workflowName=workflow+name+1&workflowName=workflow+name+2",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusBadRequest,
+			wantErr:            true,
+		},
+
+		{
+			name:               "GetsWorkflowsWheTheExpandsAreNotSet",
+			startAt:            0,
+			maxResults:         50,
+			mockFile:           "../v3/mocks/get-workflows.json",
+			workflowNames:      []string{"workflow name 1", "workflow name 2"},
+			expand:             nil,
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/workflow/search?maxResults=50&startAt=0&workflowName=workflow+name+1&workflowName=workflow+name+2",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            false,
+		},
+
+		{
+			name:               "GetsWorkflowsWheTheWorkflowNamesAreNotSet",
+			startAt:            0,
+			maxResults:         50,
+			mockFile:           "../v3/mocks/get-workflows.json",
+			workflowNames:      nil,
+			expand:             []string{"transitions", "transitions.rules", "default"},
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/workflow/search?expand=transitions%2Ctransitions.rules%2Cdefault&maxResults=50&startAt=0",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            false,
+		},
+
+		{
+			name:               "GetsWorkflowsWheTheResponseBodyIsEmpty",
+			startAt:            0,
+			maxResults:         50,
+			mockFile:           "../v3/mocks/empty_json.json",
+			workflowNames:      []string{"workflow name 1", "workflow name 2"},
+			expand:             []string{"transitions", "transitions.rules", "default"},
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/workflow/search?expand=transitions%2Ctransitions.rules%2Cdefault&maxResults=50&startAt=0&workflowName=workflow+name+1&workflowName=workflow+name+2",
+			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 := &WorkflowService{client: mockClient}
+
+			gotResult, gotResponse, err := i.Gets(testCase.context, testCase.workflowNames, testCase.expand,
+				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 TestWorkflowService_Create(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		payload            *models.WorkflowPayloadScheme
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+	}{
+		{
+			name: "CreateWorkflowsWhenTheParametersAreCorrect",
+			payload: &models.WorkflowPayloadScheme{
+				Name:        "Workflow 1",
+				Description: "This is a workflow used for Stories and Tasks",
+				Statuses: []*models.WorkflowTransitionScreenScheme{
+					{
+						ID: "10002",
+					},
+					{
+						ID: "3",
+					},
+				},
+				Transitions: []*models.WorkflowTransitionPayloadScheme{
+					{
+						Name:   "Create",
+						To:     "10012",
+						Type:   "initial",
+						Screen: nil,
+					},
+					{
+						Name: "Start Progress",
+						To:   "3",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Done",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Stop Progress",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"3"},
+					},
+				},
+			},
+			mockFile:           "../mocks/workflow-created.json",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/2/workflow",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            false,
+		},
+
+		{
+			name:               "CreateWorkflowsWhenThePayloadIsNotProvided",
+			payload:            nil,
+			mockFile:           "../mocks/workflow-created.json",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/2/workflow",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name: "CreateWorkflowsWhenTheContextIsNotProvided",
+			payload: &models.WorkflowPayloadScheme{
+				Name:        "Workflow 1",
+				Description: "This is a workflow used for Stories and Tasks",
+				Statuses: []*models.WorkflowTransitionScreenScheme{
+					{
+						ID: "10002",
+					},
+					{
+						ID: "3",
+					},
+				},
+				Transitions: []*models.WorkflowTransitionPayloadScheme{
+					{
+						Name:   "Create",
+						To:     "10012",
+						Type:   "initial",
+						Screen: nil,
+					},
+					{
+						Name: "Start Progress",
+						To:   "3",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Done",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Stop Progress",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"3"},
+					},
+				},
+			},
+			mockFile:           "../mocks/workflow-created.json",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/2/workflow",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name: "CreateWorkflowsWhenTheRequestMethodIsIncorrect",
+			payload: &models.WorkflowPayloadScheme{
+				Name:        "Workflow 1",
+				Description: "This is a workflow used for Stories and Tasks",
+				Statuses: []*models.WorkflowTransitionScreenScheme{
+					{
+						ID: "10002",
+					},
+					{
+						ID: "3",
+					},
+				},
+				Transitions: []*models.WorkflowTransitionPayloadScheme{
+					{
+						Name:   "Create",
+						To:     "10012",
+						Type:   "initial",
+						Screen: nil,
+					},
+					{
+						Name: "Start Progress",
+						To:   "3",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Done",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Stop Progress",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"3"},
+					},
+				},
+			},
+			mockFile:           "../mocks/workflow-created.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/workflow",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name: "CreateWorkflowsWhenTheStatusCodeIsIncorrect",
+			payload: &models.WorkflowPayloadScheme{
+				Name:        "Workflow 1",
+				Description: "This is a workflow used for Stories and Tasks",
+				Statuses: []*models.WorkflowTransitionScreenScheme{
+					{
+						ID: "10002",
+					},
+					{
+						ID: "3",
+					},
+				},
+				Transitions: []*models.WorkflowTransitionPayloadScheme{
+					{
+						Name:   "Create",
+						To:     "10012",
+						Type:   "initial",
+						Screen: nil,
+					},
+					{
+						Name: "Start Progress",
+						To:   "3",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Done",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Stop Progress",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"3"},
+					},
+				},
+			},
+			mockFile:           "../mocks/workflow-created.json",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/2/workflow",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusBadRequest,
+			wantErr:            true,
+		},
+
+		{
+			name: "CreateWorkflowsWhenTheResponseBodyIsEmpty",
+			payload: &models.WorkflowPayloadScheme{
+				Name:        "Workflow 1",
+				Description: "This is a workflow used for Stories and Tasks",
+				Statuses: []*models.WorkflowTransitionScreenScheme{
+					{
+						ID: "10002",
+					},
+					{
+						ID: "3",
+					},
+				},
+				Transitions: []*models.WorkflowTransitionPayloadScheme{
+					{
+						Name:   "Create",
+						To:     "10012",
+						Type:   "initial",
+						Screen: nil,
+					},
+					{
+						Name: "Start Progress",
+						To:   "3",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Done",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Stop Progress",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"3"},
+					},
+				},
+			},
+			mockFile:           "../mocks/empty-json.json",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/2/workflow",
+			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 := &WorkflowService{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)
+			}
+		})
+	}
+}
diff --git a/jira/v3/workflow_test.go b/jira/v3/workflow_test.go
index 5f1a7f1..bc45691 100644
--- a/jira/v3/workflow_test.go
+++ b/jira/v3/workflow_test.go
@@ -3,6 +3,7 @@ package v3
 import (
 	"context"
 	"fmt"
+	models "github.com/ctreminiom/go-atlassian/pkg/infra/models/jira"
 	"github.com/stretchr/testify/assert"
 	"net/http"
 	"net/url"
@@ -312,3 +313,328 @@ func TestWorkflowService_Gets(t *testing.T) {
 	}
 
 }
+
+func TestWorkflowService_Create(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		payload            *models.WorkflowPayloadScheme
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+	}{
+		{
+			name: "CreateWorkflowsWhenTheParametersAreCorrect",
+			payload: &models.WorkflowPayloadScheme{
+				Name:        "Workflow 1",
+				Description: "This is a workflow used for Stories and Tasks",
+				Statuses: []*models.WorkflowTransitionScreenScheme{
+					{
+						ID: "10002",
+					},
+					{
+						ID: "3",
+					},
+				},
+				Transitions: []*models.WorkflowTransitionPayloadScheme{
+					{
+						Name:   "Create",
+						To:     "10012",
+						Type:   "initial",
+						Screen: nil,
+					},
+					{
+						Name: "Start Progress",
+						To:   "3",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Done",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Stop Progress",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"3"},
+					},
+				},
+			},
+			mockFile:           "../mocks/workflow-created.json",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/3/workflow",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            false,
+		},
+
+		{
+			name:               "CreateWorkflowsWhenThePayloadIsNotProvided",
+			payload:            nil,
+			mockFile:           "../mocks/workflow-created.json",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/3/workflow",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name: "CreateWorkflowsWhenTheContextIsNotProvided",
+			payload: &models.WorkflowPayloadScheme{
+				Name:        "Workflow 1",
+				Description: "This is a workflow used for Stories and Tasks",
+				Statuses: []*models.WorkflowTransitionScreenScheme{
+					{
+						ID: "10002",
+					},
+					{
+						ID: "3",
+					},
+				},
+				Transitions: []*models.WorkflowTransitionPayloadScheme{
+					{
+						Name:   "Create",
+						To:     "10012",
+						Type:   "initial",
+						Screen: nil,
+					},
+					{
+						Name: "Start Progress",
+						To:   "3",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Done",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Stop Progress",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"3"},
+					},
+				},
+			},
+			mockFile:           "../mocks/workflow-created.json",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/3/workflow",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name: "CreateWorkflowsWhenTheRequestMethodIsIncorrect",
+			payload: &models.WorkflowPayloadScheme{
+				Name:        "Workflow 1",
+				Description: "This is a workflow used for Stories and Tasks",
+				Statuses: []*models.WorkflowTransitionScreenScheme{
+					{
+						ID: "10002",
+					},
+					{
+						ID: "3",
+					},
+				},
+				Transitions: []*models.WorkflowTransitionPayloadScheme{
+					{
+						Name:   "Create",
+						To:     "10012",
+						Type:   "initial",
+						Screen: nil,
+					},
+					{
+						Name: "Start Progress",
+						To:   "3",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Done",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Stop Progress",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"3"},
+					},
+				},
+			},
+			mockFile:           "../mocks/workflow-created.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/3/workflow",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+		},
+
+		{
+			name: "CreateWorkflowsWhenTheStatusCodeIsIncorrect",
+			payload: &models.WorkflowPayloadScheme{
+				Name:        "Workflow 1",
+				Description: "This is a workflow used for Stories and Tasks",
+				Statuses: []*models.WorkflowTransitionScreenScheme{
+					{
+						ID: "10002",
+					},
+					{
+						ID: "3",
+					},
+				},
+				Transitions: []*models.WorkflowTransitionPayloadScheme{
+					{
+						Name:   "Create",
+						To:     "10012",
+						Type:   "initial",
+						Screen: nil,
+					},
+					{
+						Name: "Start Progress",
+						To:   "3",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Done",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Stop Progress",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"3"},
+					},
+				},
+			},
+			mockFile:           "../mocks/workflow-created.json",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/3/workflow",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusBadRequest,
+			wantErr:            true,
+		},
+
+		{
+			name: "CreateWorkflowsWhenTheResponseBodyIsEmpty",
+			payload: &models.WorkflowPayloadScheme{
+				Name:        "Workflow 1",
+				Description: "This is a workflow used for Stories and Tasks",
+				Statuses: []*models.WorkflowTransitionScreenScheme{
+					{
+						ID: "10002",
+					},
+					{
+						ID: "3",
+					},
+				},
+				Transitions: []*models.WorkflowTransitionPayloadScheme{
+					{
+						Name:   "Create",
+						To:     "10012",
+						Type:   "initial",
+						Screen: nil,
+					},
+					{
+						Name: "Start Progress",
+						To:   "3",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Done",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"10012"},
+					},
+					{
+						Name: "Stop Progress",
+						To:   "10002",
+						Type: "directed",
+						From: []string{"3"},
+					},
+				},
+			},
+			mockFile:           "../mocks/empty-json.json",
+			wantHTTPMethod:     http.MethodPost,
+			endpoint:           "/rest/api/3/workflow",
+			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 := &WorkflowService{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)
+			}
+		})
+	}
+}