Add create project's tag and delete project's tag and tags (#146)

* Add create project's tag and delete project's tag and tags

Change-Id: I7ddebf587a7d387dd3174e01d621812267b2296f

* Add unittest

Change-Id: Iccd1257f39d74a94cfc779cea875ff4c51fcefbc

* go fmt

Change-Id: I948ec32df1d99e518aa774dedb19500f3c2bc3b2

---------

Co-authored-by: dai-wl <dai-wl@reachauto.com>
diff --git a/projects_tag.go b/projects_tag.go
index 7070d3f..6026eea 100644
--- a/projects_tag.go
+++ b/projects_tag.go
@@ -15,6 +15,18 @@
 	Created  *Timestamp    `json:"created,omitempty"`
 }
 
+// TagInput entity for create a tag.
+type TagInput struct {
+	Ref      string `json:"ref"`
+	Revision string `json:"revision,omitempty"`
+	Message  string `json:"message,omitempty"`
+}
+
+// DeleteTagsInput entity for delete tags.
+type DeleteTagsInput struct {
+	Tags []string `json:"tags"`
+}
+
 // ListTags list the tags of a project.
 //
 // Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#list-tags
@@ -58,3 +70,55 @@
 
 	return v, resp, err
 }
+
+// CreateTag create a tag of a project
+//
+// Gerrit API docs:https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#create-tag
+func (s *ProjectsService) CreateTag(projectName, tagName string, input *TagInput) (*TagInfo, *Response, error) {
+	u := fmt.Sprintf("projects/%s/tags/%s", url.QueryEscape(projectName), url.QueryEscape(tagName))
+
+	req, err := s.client.NewRequest("PUT", u, input)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	v := new(TagInfo)
+	resp, err := s.client.Do(req, v)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return v, resp, err
+}
+
+// DeleteTag delete a tag of a project
+//
+// Gerrit API docs:https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#delete-tag
+func (s *ProjectsService) DeleteTag(projectName, tagName string) (*Response, error) {
+	u := fmt.Sprintf("projects/%s/tags/%s", url.QueryEscape(projectName), url.QueryEscape(tagName))
+
+	req, err := s.client.NewRequest("DELETE", u, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := s.client.Do(req, nil)
+
+	return resp, err
+}
+
+// DeleteTags delete tags of a project
+//
+// Gerrit API docs:https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#delete-tags
+func (s *ProjectsService) DeleteTags(projectName string, input *DeleteTagsInput) (*Response, error) {
+	u := fmt.Sprintf("projects/%s/tags:delete", url.QueryEscape(projectName))
+
+	req, err := s.client.NewRequest("POST", u, input)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := s.client.Do(req, nil)
+
+	return resp, err
+}
diff --git a/projects_tag_test.go b/projects_tag_test.go
new file mode 100644
index 0000000..8f41b1a
--- /dev/null
+++ b/projects_tag_test.go
@@ -0,0 +1,94 @@
+package gerrit_test
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+
+	"github.com/andygrunwald/go-gerrit"
+)
+
+// +func (s *ProjectsService) CreateTag(projectName, tagName string, input *TagInput) (*TagInfo, *Response, error)
+func TestProjectsService_CreateTag(t *testing.T) {
+	setup()
+	defer teardown()
+
+	input := &gerrit.TagInput{
+		Ref:      "v1.0.0",
+		Revision: "master",
+		Message:  "v1.0.0 release",
+	}
+
+	testMux.HandleFunc("/projects/go/tags/v1.0.0", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PUT")
+
+		v := new(gerrit.TagInput)
+		if err := json.NewDecoder(r.Body).Decode(v); err != nil {
+			t.Error(err)
+		}
+
+		if !reflect.DeepEqual(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		fmt.Fprint(w, `)]}'`+"\n"+`{"ref":"v1.0.0","revision":"master","message":"v1.0.0 release"}`)
+	})
+
+	tag, _, err := testClient.Projects.CreateTag("go", "v1.0.0", input)
+	if err != nil {
+		t.Errorf("Projects.CreateTag returned error: %v", err)
+	}
+
+	want := &gerrit.TagInfo{
+		Ref:      "v1.0.0",
+		Revision: "master",
+		Message:  "v1.0.0 release",
+	}
+
+	if !reflect.DeepEqual(tag, want) {
+		t.Errorf("Projects.CreateTag returned %+v, want %+v", tag, want)
+	}
+}
+
+func TestProjectsService_DeleteTag(t *testing.T) {
+	setup()
+	defer teardown()
+
+	testMux.HandleFunc("/projects/go/tags/v1.0.0", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	_, err := testClient.Projects.DeleteTag("go", "v1.0.0")
+	if err != nil {
+		t.Errorf("Projects.DeleteTag returned error: %v", err)
+	}
+}
+
+func TestProjectsService_DeleteTags(t *testing.T) {
+	setup()
+	defer teardown()
+	input := &gerrit.DeleteTagsInput{
+		Tags: []string{"v1.0.0", "v1.1.0"},
+	}
+	testMux.HandleFunc("/projects/go/tags:delete", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		v := new(gerrit.DeleteTagsInput)
+		if err := json.NewDecoder(r.Body).Decode(v); err != nil {
+			t.Error(err)
+		}
+
+		if !reflect.DeepEqual(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	_, err := testClient.Projects.DeleteTags("go", input)
+	if err != nil {
+		t.Errorf("Projects.DeleteTags returned error: %v", err)
+	}
+}