Repository: ctreminiom/go-atlassian
Pull request #108 (2022-03-03 01:28:16)

:sparkles: Added on the Project.Property service.
1. Added the project property service on the Jira V2


Patch:
diff --git a/jira/mocks/get-project-properties.json b/jira/mocks/get-project-properties.json
new file mode 100644
index 0000000..236e5ba
--- /dev/null
+++ b/jira/mocks/get-project-properties.json
@@ -0,0 +1,8 @@
+{
+  "keys": [
+    {
+      "self": "https://your-domain.atlassian.net/rest/api/3/issue/EX-2/properties/issue.support",
+      "key": "issue.support"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/jira/mocks/get-project-property.json b/jira/mocks/get-project-property.json
new file mode 100644
index 0000000..bd8ec98
--- /dev/null
+++ b/jira/mocks/get-project-property.json
@@ -0,0 +1,7 @@
+{
+  "key": "issue.support",
+  "value": {
+    "system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+    "system.support.time": "1m"
+  }
+}
\ No newline at end of file
diff --git a/jira/v2/jira.go b/jira/v2/jira.go
index bf948bc..9137159 100644
--- a/jira/v2/jira.go
+++ b/jira/v2/jira.go
@@ -143,9 +143,10 @@ func New(httpClient *http.Client, site string) (client *Client, err error) {
 			Actor:  &ProjectRoleActorService{client: client},
 		},
 
-		Type:    &ProjectTypeService{client: client},
-		Version: &ProjectVersionService{client: client},
-		Feature: &ProjectFeatureService{client: client},
+		Type:     &ProjectTypeService{client: client},
+		Version:  &ProjectVersionService{client: client},
+		Feature:  &ProjectFeatureService{client: client},
+		Property: &ProjectPropertyService{client: client},
 	}
 
 	client.User = &UserService{
diff --git a/jira/v2/project.go b/jira/v2/project.go
index 9c20589..4e9005a 100644
--- a/jira/v2/project.go
+++ b/jira/v2/project.go
@@ -20,6 +20,7 @@ type ProjectService struct {
 	Type       *ProjectTypeService
 	Version    *ProjectVersionService
 	Feature    *ProjectFeatureService
+	Property   *ProjectPropertyService
 }
 
 // Create creates a project based on a project type template, as shown in the following table:
diff --git a/jira/v2/projectProperty.go b/jira/v2/projectProperty.go
new file mode 100644
index 0000000..a007ae8
--- /dev/null
+++ b/jira/v2/projectProperty.go
@@ -0,0 +1,132 @@
+package v2
+
+import (
+	"context"
+	"fmt"
+	"github.com/ctreminiom/go-atlassian/pkg/infra/models"
+	"net/http"
+)
+
+type ProjectPropertyService struct{ client *Client }
+
+// Gets returns all project property keys for the project.
+// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/properties#get-project-properties-keys
+func (p *ProjectPropertyService) Gets(ctx context.Context, projectKeyOrID string) (result *models.ProjectPropertyPageScheme,
+	response *ResponseScheme, err error) {
+
+	if len(projectKeyOrID) == 0 {
+		return nil, nil, models.ErrNoProjectIDError
+	}
+
+	endpoint := fmt.Sprintf("rest/api/2/project/%v/properties", 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
+}
+
+// Get returns the value of a project property.
+// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/properties#get-project-property
+func (p *ProjectPropertyService) Get(ctx context.Context, projectKeyOrID, propertyKey string) (result *models.EntityPropertyScheme,
+	response *ResponseScheme, err error) {
+
+	if len(projectKeyOrID) == 0 {
+		return nil, nil, models.ErrNoProjectIDError
+	}
+
+	if len(propertyKey) == 0 {
+		return nil, nil, models.ErrNoPropertyKeyError
+	}
+
+	endpoint := fmt.Sprintf("rest/api/2/project/%v/properties/%v", projectKeyOrID, propertyKey)
+
+	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 value of the project property. You can use project properties to store custom data against the project.
+// The value of the request body must be a valid, non-empty JSON blob. The maximum length is 32768 characters.
+// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/properties#set-project-property
+func (p *ProjectPropertyService) Set(ctx context.Context, projectKeyOrID, propertyKey string, payload interface{}) (response *ResponseScheme, err error) {
+
+	if len(projectKeyOrID) == 0 {
+		return nil, models.ErrNoProjectIDError
+	}
+
+	if len(propertyKey) == 0 {
+		return nil, models.ErrNoPropertyKeyError
+	}
+
+	payloadAsReader, err := transformStructToReader(payload)
+	if err != nil {
+		return nil, err
+	}
+
+	endpoint := fmt.Sprintf("rest/api/2/project/%v/properties/%v", projectKeyOrID, propertyKey)
+
+	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, nil)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// Delete deletes the property from a project.
+// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/properties#delete-project-property
+func (p *ProjectPropertyService) Delete(ctx context.Context, projectKeyOrID, propertyKey string) (response *ResponseScheme, err error) {
+
+	if len(projectKeyOrID) == 0 {
+		return nil, models.ErrNoProjectIDError
+	}
+
+	if len(propertyKey) == 0 {
+		return nil, models.ErrNoPropertyKeyError
+	}
+
+	endpoint := fmt.Sprintf("rest/api/2/project/%v/properties/%v", projectKeyOrID, propertyKey)
+
+	request, err := p.client.newRequest(ctx, http.MethodDelete, endpoint, nil)
+	if err != nil {
+		return
+	}
+
+	request.Header.Set("Accept", "application/json")
+	request.Header.Set("Content-Type", "application/json")
+
+	response, err = p.client.call(request, nil)
+	if err != nil {
+		return
+	}
+
+	return
+}
diff --git a/jira/v3/jira.go b/jira/v3/jira.go
index 21310f3..0a45dd5 100644
--- a/jira/v3/jira.go
+++ b/jira/v3/jira.go
@@ -145,9 +145,10 @@ func New(httpClient *http.Client, site string) (client *Client, err error) {
 			Actor:  &ProjectRoleActorService{client: client},
 		},
 
-		Type:    &ProjectTypeService{client: client},
-		Version: &ProjectVersionService{client: client},
-		Feature: &ProjectFeatureService{client: client},
+		Type:     &ProjectTypeService{client: client},
+		Version:  &ProjectVersionService{client: client},
+		Feature:  &ProjectFeatureService{client: client},
+		Property: &ProjectPropertyService{client: client},
 	}
 
 	client.User = &UserService{
diff --git a/jira/v3/project.go b/jira/v3/project.go
index 8bb08dc..527261f 100644
--- a/jira/v3/project.go
+++ b/jira/v3/project.go
@@ -20,6 +20,7 @@ type ProjectService struct {
 	Type       *ProjectTypeService
 	Version    *ProjectVersionService
 	Feature    *ProjectFeatureService
+	Property   *ProjectPropertyService
 }
 
 // Create creates a project based on a project type template, as shown in the following table:
diff --git a/jira/v3/projectProperty.go b/jira/v3/projectProperty.go
new file mode 100644
index 0000000..74f72bf
--- /dev/null
+++ b/jira/v3/projectProperty.go
@@ -0,0 +1,132 @@
+package v3
+
+import (
+	"context"
+	"fmt"
+	"github.com/ctreminiom/go-atlassian/pkg/infra/models"
+	"net/http"
+)
+
+type ProjectPropertyService struct{ client *Client }
+
+// Gets returns all project property keys for the project.
+// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/properties#get-project-properties-keys
+func (p *ProjectPropertyService) Gets(ctx context.Context, projectKeyOrID string) (result *models.ProjectPropertyPageScheme,
+	response *ResponseScheme, err error) {
+
+	if len(projectKeyOrID) == 0 {
+		return nil, nil, models.ErrNoProjectIDError
+	}
+
+	endpoint := fmt.Sprintf("rest/api/3/project/%v/properties", 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
+}
+
+// Get returns the value of a project property.
+// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/properties#get-project-property
+func (p *ProjectPropertyService) Get(ctx context.Context, projectKeyOrID, propertyKey string) (result *models.EntityPropertyScheme,
+	response *ResponseScheme, err error) {
+
+	if len(projectKeyOrID) == 0 {
+		return nil, nil, models.ErrNoProjectIDError
+	}
+
+	if len(propertyKey) == 0 {
+		return nil, nil, models.ErrNoPropertyKeyError
+	}
+
+	endpoint := fmt.Sprintf("rest/api/3/project/%v/properties/%v", projectKeyOrID, propertyKey)
+
+	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 value of the project property. You can use project properties to store custom data against the project.
+// The value of the request body must be a valid, non-empty JSON blob. The maximum length is 32768 characters.
+// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/properties#set-project-property
+func (p *ProjectPropertyService) Set(ctx context.Context, projectKeyOrID, propertyKey string, payload interface{}) (response *ResponseScheme, err error) {
+
+	if len(projectKeyOrID) == 0 {
+		return nil, models.ErrNoProjectIDError
+	}
+
+	if len(propertyKey) == 0 {
+		return nil, models.ErrNoPropertyKeyError
+	}
+
+	payloadAsReader, err := transformStructToReader(payload)
+	if err != nil {
+		return nil, err
+	}
+
+	endpoint := fmt.Sprintf("rest/api/3/project/%v/properties/%v", projectKeyOrID, propertyKey)
+
+	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, nil)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// Delete deletes the property from a project.
+// Docs: https://docs.go-atlassian.io/jira-software-cloud/projects/properties#delete-project-property
+func (p *ProjectPropertyService) Delete(ctx context.Context, projectKeyOrID, propertyKey string) (response *ResponseScheme, err error) {
+
+	if len(projectKeyOrID) == 0 {
+		return nil, models.ErrNoProjectIDError
+	}
+
+	if len(propertyKey) == 0 {
+		return nil, models.ErrNoPropertyKeyError
+	}
+
+	endpoint := fmt.Sprintf("rest/api/3/project/%v/properties/%v", projectKeyOrID, propertyKey)
+
+	request, err := p.client.newRequest(ctx, http.MethodDelete, endpoint, nil)
+	if err != nil {
+		return
+	}
+
+	request.Header.Set("Accept", "application/json")
+	request.Header.Set("Content-Type", "application/json")
+
+	response, err = p.client.call(request, nil)
+	if err != nil {
+		return
+	}
+
+	return
+}
diff --git a/pkg/infra/models/errors.go b/pkg/infra/models/errors.go
index e9991f5..777d5f1 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")
+	ErrNoPropertyKeyError                  = errors.New("jira: no property key 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")
diff --git a/pkg/infra/models/jira_project_property.go b/pkg/infra/models/jira_project_property.go
new file mode 100644
index 0000000..35fa22f
--- /dev/null
+++ b/pkg/infra/models/jira_project_property.go
@@ -0,0 +1,15 @@
+package models
+
+type ProjectPropertyPageScheme struct {
+	Keys []*ProjectPropertyScheme `json:"keys,omitempty"`
+}
+
+type ProjectPropertyScheme struct {
+	Self string `json:"self,omitempty"`
+	Key  string `json:"key,omitempty"`
+}
+
+type EntityPropertyScheme struct {
+	Key   string      `json:"key"`
+	Value interface{} `json:"value"`
+}


Test patch:
diff --git a/jira/v2/projectProperty_test.go b/jira/v2/projectProperty_test.go
new file mode 100644
index 0000000..cc68836
--- /dev/null
+++ b/jira/v2/projectProperty_test.go
@@ -0,0 +1,599 @@
+package v2
+
+import (
+	"context"
+	"fmt"
+	"github.com/stretchr/testify/assert"
+	"net/http"
+	"net/url"
+	"testing"
+)
+
+func Test_ProjectPropertyService_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-properties.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/project/DUMMY/properties",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+		},
+
+		{
+			name:               "when project key or id is not provided",
+			projectKeyOrID:     "",
+			mockFile:           "../mocks/get-project-properties.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/project/DUMMY/properties",
+			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-properties.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/project/DUMMY/properties",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "request creation failed: net/http: nil Context",
+		},
+
+		{
+			name:               "when the response body is empty",
+			projectKeyOrID:     "DUMMY",
+			mockFile:           "../mocks/empty-json.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/project/DUMMY/properties",
+			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 := &ProjectPropertyService{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_ProjectPropertyService_Get(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		projectKeyOrID     string
+		propertyKey        string
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+		expectedError      string
+	}{
+		{
+			name:               "when the parameters are correct",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			mockFile:           "../mocks/get-project-property.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+		},
+
+		{
+			name:               "when the property key is not provided",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "",
+			mockFile:           "../mocks/get-project-property.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "jira: no property key set",
+		},
+
+		{
+			name:               "when project key or id is not provided",
+			projectKeyOrID:     "",
+			mockFile:           "../mocks/get-project-property.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "jira: no project id set",
+		},
+
+		{
+			name:               "when the context is not provided",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			mockFile:           "../mocks/get-project-property.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "request creation failed: net/http: nil Context",
+		},
+
+		{
+			name:               "when the response body is empty",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			mockFile:           "../mocks/empty-json.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			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 := &ProjectPropertyService{client: mockClient}
+
+			gotResult, gotResponse, err := i.Get(testCase.context, testCase.projectKeyOrID, testCase.propertyKey)
+
+			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_ProjectPropertyService_Set(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		projectKeyOrID     string
+		propertyKey        string
+		payload            interface{}
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+		expectedError      string
+	}{
+		{
+			name:           "when the parameters are correct",
+			projectKeyOrID: "DUMMY",
+			propertyKey:    "property-key-sample",
+			payload: map[string]interface{}{
+				"system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+				"system.support.time":    "1m",
+			},
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+		},
+
+		{
+			name:           "when the response code is incorrect",
+			projectKeyOrID: "DUMMY",
+			propertyKey:    "property-key-sample",
+			payload: map[string]interface{}{
+				"system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+				"system.support.time":    "1m",
+			},
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			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 payload is not provided",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			payload:            nil,
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "failed to parse the interface pointer, please provide a valid one",
+		},
+
+		{
+			name:           "when the property key is not provided",
+			projectKeyOrID: "DUMMY",
+			propertyKey:    "",
+			payload: map[string]interface{}{
+				"system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+				"system.support.time":    "1m",
+			},
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "jira: no property key set",
+		},
+
+		{
+			name:           "when project key or id is not provided",
+			projectKeyOrID: "",
+			payload: map[string]interface{}{
+				"system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+				"system.support.time":    "1m",
+			},
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "jira: no project id set",
+		},
+
+		{
+			name:           "when the context is not provided",
+			projectKeyOrID: "DUMMY",
+			propertyKey:    "property-key-sample",
+			payload: map[string]interface{}{
+				"system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+				"system.support.time":    "1m",
+			},
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "request creation failed: net/http: nil Context",
+		},
+	}
+
+	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 := &ProjectPropertyService{client: mockClient}
+
+			gotResponse, err := i.Set(testCase.context, testCase.projectKeyOrID, testCase.propertyKey, testCase.payload)
+
+			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)
+
+				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_ProjectPropertyService_Delete(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		projectKeyOrID     string
+		propertyKey        string
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+		expectedError      string
+	}{
+		{
+			name:               "when the parameters are correct",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusNoContent,
+		},
+
+		{
+			name:               "when the response code is incorrect",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			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 property key is not provided",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusNoContent,
+			wantErr:            true,
+			expectedError:      "jira: no property key set",
+		},
+
+		{
+			name:               "when project key or id is not provided",
+			projectKeyOrID:     "",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusNoContent,
+			wantErr:            true,
+			expectedError:      "jira: no project id set",
+		},
+
+		{
+			name:               "when the context is not provided",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/2/project/DUMMY/properties/property-key-sample",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusNoContent,
+			wantErr:            true,
+			expectedError:      "request creation failed: net/http: nil Context",
+		},
+	}
+
+	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 := &ProjectPropertyService{client: mockClient}
+
+			gotResponse, err := i.Delete(testCase.context, testCase.projectKeyOrID, testCase.propertyKey)
+
+			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)
+
+				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/projectProperty_test.go b/jira/v3/projectProperty_test.go
new file mode 100644
index 0000000..804ce4e
--- /dev/null
+++ b/jira/v3/projectProperty_test.go
@@ -0,0 +1,599 @@
+package v3
+
+import (
+	"context"
+	"fmt"
+	"github.com/stretchr/testify/assert"
+	"net/http"
+	"net/url"
+	"testing"
+)
+
+func Test_ProjectPropertyService_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-properties.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/3/project/DUMMY/properties",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+		},
+
+		{
+			name:               "when project key or id is not provided",
+			projectKeyOrID:     "",
+			mockFile:           "../mocks/get-project-properties.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/3/project/DUMMY/properties",
+			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-properties.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/3/project/DUMMY/properties",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "request creation failed: net/http: nil Context",
+		},
+
+		{
+			name:               "when the response body is empty",
+			projectKeyOrID:     "DUMMY",
+			mockFile:           "../mocks/empty-json.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/3/project/DUMMY/properties",
+			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 := &ProjectPropertyService{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_ProjectPropertyService_Get(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		projectKeyOrID     string
+		propertyKey        string
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+		expectedError      string
+	}{
+		{
+			name:               "when the parameters are correct",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			mockFile:           "../mocks/get-project-property.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+		},
+
+		{
+			name:               "when the property key is not provided",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "",
+			mockFile:           "../mocks/get-project-property.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "jira: no property key set",
+		},
+
+		{
+			name:               "when project key or id is not provided",
+			projectKeyOrID:     "",
+			mockFile:           "../mocks/get-project-property.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "jira: no project id set",
+		},
+
+		{
+			name:               "when the context is not provided",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			mockFile:           "../mocks/get-project-property.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "request creation failed: net/http: nil Context",
+		},
+
+		{
+			name:               "when the response body is empty",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			mockFile:           "../mocks/empty-json.json",
+			wantHTTPMethod:     http.MethodGet,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			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 := &ProjectPropertyService{client: mockClient}
+
+			gotResult, gotResponse, err := i.Get(testCase.context, testCase.projectKeyOrID, testCase.propertyKey)
+
+			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_ProjectPropertyService_Set(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		projectKeyOrID     string
+		propertyKey        string
+		payload            interface{}
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+		expectedError      string
+	}{
+		{
+			name:           "when the parameters are correct",
+			projectKeyOrID: "DUMMY",
+			propertyKey:    "property-key-sample",
+			payload: map[string]interface{}{
+				"system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+				"system.support.time":    "1m",
+			},
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+		},
+
+		{
+			name:           "when the response code is incorrect",
+			projectKeyOrID: "DUMMY",
+			propertyKey:    "property-key-sample",
+			payload: map[string]interface{}{
+				"system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+				"system.support.time":    "1m",
+			},
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			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 payload is not provided",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			payload:            nil,
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "failed to parse the interface pointer, please provide a valid one",
+		},
+
+		{
+			name:           "when the property key is not provided",
+			projectKeyOrID: "DUMMY",
+			propertyKey:    "",
+			payload: map[string]interface{}{
+				"system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+				"system.support.time":    "1m",
+			},
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "jira: no property key set",
+		},
+
+		{
+			name:           "when project key or id is not provided",
+			projectKeyOrID: "",
+			payload: map[string]interface{}{
+				"system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+				"system.support.time":    "1m",
+			},
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "jira: no project id set",
+		},
+
+		{
+			name:           "when the context is not provided",
+			projectKeyOrID: "DUMMY",
+			propertyKey:    "property-key-sample",
+			payload: map[string]interface{}{
+				"system.conversation.id": "b1bf38be-5e94-4b40-a3b8-9278735ee1e6",
+				"system.support.time":    "1m",
+			},
+			wantHTTPMethod:     http.MethodPut,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusOK,
+			wantErr:            true,
+			expectedError:      "request creation failed: net/http: nil Context",
+		},
+	}
+
+	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 := &ProjectPropertyService{client: mockClient}
+
+			gotResponse, err := i.Set(testCase.context, testCase.projectKeyOrID, testCase.propertyKey, testCase.payload)
+
+			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)
+
+				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_ProjectPropertyService_Delete(t *testing.T) {
+
+	testCases := []struct {
+		name               string
+		projectKeyOrID     string
+		propertyKey        string
+		mockFile           string
+		wantHTTPMethod     string
+		endpoint           string
+		context            context.Context
+		wantHTTPCodeReturn int
+		wantErr            bool
+		expectedError      string
+	}{
+		{
+			name:               "when the parameters are correct",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusNoContent,
+		},
+
+		{
+			name:               "when the response code is incorrect",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			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 property key is not provided",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusNoContent,
+			wantErr:            true,
+			expectedError:      "jira: no property key set",
+		},
+
+		{
+			name:               "when project key or id is not provided",
+			projectKeyOrID:     "",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            context.Background(),
+			wantHTTPCodeReturn: http.StatusNoContent,
+			wantErr:            true,
+			expectedError:      "jira: no project id set",
+		},
+
+		{
+			name:               "when the context is not provided",
+			projectKeyOrID:     "DUMMY",
+			propertyKey:        "property-key-sample",
+			wantHTTPMethod:     http.MethodDelete,
+			endpoint:           "/rest/api/3/project/DUMMY/properties/property-key-sample",
+			context:            nil,
+			wantHTTPCodeReturn: http.StatusNoContent,
+			wantErr:            true,
+			expectedError:      "request creation failed: net/http: nil Context",
+		},
+	}
+
+	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 := &ProjectPropertyService{client: mockClient}
+
+			gotResponse, err := i.Delete(testCase.context, testCase.projectKeyOrID, testCase.propertyKey)
+
+			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)
+
+				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)
+			}
+		})
+
+	}
+
+}