Repository: xanzy/go-gitlab
Pull request #1135 (2021-04-20 16:13:47)

Add DirectAssetURL, FilePath & LinkType to Release Links API
Add optional DirectAssetURL and LinkType for Release Links response data.

Add optional FilePath and LinkType to creation and updating of release links.

See https://docs.gitlab.com/13.10/ee/api/releases/links.html#create-a-link for documentation


Patch:
diff --git a/releaselinks.go b/releaselinks.go
index 4ea1af8..7d56ff5 100644
--- a/releaselinks.go
+++ b/releaselinks.go
@@ -33,10 +33,12 @@ type ReleaseLinksService struct {
 //
 // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html
 type ReleaseLink struct {
-	ID       int    `json:"id"`
-	Name     string `json:"name"`
-	URL      string `json:"url"`
-	External bool   `json:"external"`
+	ID             int           `json:"id"`
+	Name           string        `json:"name"`
+	URL            string        `json:"url"`
+	DirectAssetURL string        `json:"direct_asset_url"`
+	External       bool          `json:"external"`
+	LinkType       LinkTypeValue `json:"link_type"`
 }
 
 // ListReleaseLinksOptions represents ListReleaseLinks() options.
@@ -99,8 +101,10 @@ func (s *ReleaseLinksService) GetReleaseLink(pid interface{}, tagName string, li
 //
 // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#create-a-link
 type CreateReleaseLinkOptions struct {
-	Name *string `url:"name" json:"name"`
-	URL  *string `url:"url" json:"url"`
+	Name     *string        `url:"name,omitempty" json:"name,omitempty"`
+	URL      *string        `url:"url,omitempty" json:"url,omitempty"`
+	FilePath *string        `url:"filepath,omitempty" json:"filepath,omitempty"`
+	LinkType *LinkTypeValue `url:"link_type,omitempty" json:"link_type,omitempty"`
 }
 
 // CreateReleaseLink creates a link.
@@ -133,8 +137,10 @@ func (s *ReleaseLinksService) CreateReleaseLink(pid interface{}, tagName string,
 //
 // GitLab API docs: https://docs.gitlab.com/ee/api/releases/links.html#update-a-link
 type UpdateReleaseLinkOptions struct {
-	Name *string `url:"name,omitempty" json:"name,omitempty"`
-	URL  *string `url:"url,omitempty" json:"url,omitempty"`
+	Name     *string        `url:"name,omitempty" json:"name,omitempty"`
+	URL      *string        `url:"url,omitempty" json:"url,omitempty"`
+	FilePath *string        `url:"filepath,omitempty" json:"filepath,omitempty"`
+	LinkType *LinkTypeValue `url:"link_type,omitempty" json:"link_type,omitempty"`
 }
 
 // UpdateReleaseLink updates an asset link.
diff --git a/types.go b/types.go
index a893def..487429a 100644
--- a/types.go
+++ b/types.go
@@ -121,6 +121,42 @@ func DeploymentStatus(v DeploymentStatusValue) *DeploymentStatusValue {
 	return p
 }
 
+// EventTypeValue represents actions type for contribution events
+type EventTypeValue string
+
+// List of available action type
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#action-types
+const (
+	CreatedEventType   EventTypeValue = "created"
+	UpdatedEventType   EventTypeValue = "updated"
+	ClosedEventType    EventTypeValue = "closed"
+	ReopenedEventType  EventTypeValue = "reopened"
+	PushedEventType    EventTypeValue = "pushed"
+	CommentedEventType EventTypeValue = "commented"
+	MergedEventType    EventTypeValue = "merged"
+	JoinedEventType    EventTypeValue = "joined"
+	LeftEventType      EventTypeValue = "left"
+	DestroyedEventType EventTypeValue = "destroyed"
+	ExpiredEventType   EventTypeValue = "expired"
+)
+
+// EventTargetTypeValue represents actions type value for contribution events
+type EventTargetTypeValue string
+
+// List of available action type
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#target-types
+const (
+	IssueEventTargetType        EventTargetTypeValue = "issue"
+	MilestoneEventTargetType    EventTargetTypeValue = "milestone"
+	MergeRequestEventTargetType EventTargetTypeValue = "merge_request"
+	NoteEventTargetType         EventTargetTypeValue = "note"
+	ProjectEventTargetType      EventTargetTypeValue = "project"
+	SnippetEventTargetType      EventTargetTypeValue = "snippet"
+	UserEventTargetType         EventTargetTypeValue = "user"
+)
+
 // FileActionValue represents the available actions that can be performed on a file.
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
@@ -191,6 +227,50 @@ func (t ISOTime) String() string {
 	return time.Time(t).Format(iso8601)
 }
 
+// LinkTypeValue represents a release link type.
+type LinkTypeValue string
+
+// List of available release link types
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/releases/links.html#create-a-link
+const (
+	ImageLinkType   LinkTypeValue = "image"
+	OtherLinkType   LinkTypeValue = "other"
+	PackageLinkType LinkTypeValue = "package"
+	RunbookLinkType LinkTypeValue = "runbook"
+)
+
+// LinkType is a helper routine that allocates a new LinkType value
+// to store v and returns a pointer to it.
+func LinkType(v LinkTypeValue) *LinkTypeValue {
+	p := new(LinkTypeValue)
+	*p = v
+	return p
+}
+
+// MergeMethodValue represents a project merge type within GitLab.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
+type MergeMethodValue string
+
+// List of available merge type
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
+const (
+	NoFastForwardMerge MergeMethodValue = "merge"
+	FastForwardMerge   MergeMethodValue = "ff"
+	RebaseMerge        MergeMethodValue = "rebase_merge"
+)
+
+// MergeMethod is a helper routine that allocates a new MergeMethod
+// to sotre v and returns a pointer to it.
+func MergeMethod(v MergeMethodValue) *MergeMethodValue {
+	p := new(MergeMethodValue)
+	*p = v
+	return p
+}
+
 // NotificationLevelValue represents a notification level.
 type NotificationLevelValue int
 
@@ -261,28 +341,6 @@ func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue {
 	return p
 }
 
-// VisibilityValue represents a visibility level within GitLab.
-//
-// GitLab API docs: https://docs.gitlab.com/ce/api/
-type VisibilityValue string
-
-// List of available visibility levels.
-//
-// GitLab API docs: https://docs.gitlab.com/ce/api/
-const (
-	PrivateVisibility  VisibilityValue = "private"
-	InternalVisibility VisibilityValue = "internal"
-	PublicVisibility   VisibilityValue = "public"
-)
-
-// Visibility is a helper routine that allocates a new VisibilityValue
-// to store v and returns a pointer to it.
-func Visibility(v VisibilityValue) *VisibilityValue {
-	p := new(VisibilityValue)
-	*p = v
-	return p
-}
-
 // ProjectCreationLevelValue represents a project creation level within GitLab.
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/
@@ -347,6 +405,28 @@ func VariableType(v VariableTypeValue) *VariableTypeValue {
 	return p
 }
 
+// VisibilityValue represents a visibility level within GitLab.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/
+type VisibilityValue string
+
+// List of available visibility levels.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/
+const (
+	PrivateVisibility  VisibilityValue = "private"
+	InternalVisibility VisibilityValue = "internal"
+	PublicVisibility   VisibilityValue = "public"
+)
+
+// Visibility is a helper routine that allocates a new VisibilityValue
+// to store v and returns a pointer to it.
+func Visibility(v VisibilityValue) *VisibilityValue {
+	p := new(VisibilityValue)
+	*p = v
+	return p
+}
+
 // WikiFormatValue represents the available wiki formats.
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/wikis.html
@@ -368,64 +448,6 @@ func WikiFormat(v WikiFormatValue) *WikiFormatValue {
 	return p
 }
 
-// MergeMethodValue represents a project merge type within GitLab.
-//
-// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
-type MergeMethodValue string
-
-// List of available merge type
-//
-// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
-const (
-	NoFastForwardMerge MergeMethodValue = "merge"
-	FastForwardMerge   MergeMethodValue = "ff"
-	RebaseMerge        MergeMethodValue = "rebase_merge"
-)
-
-// MergeMethod is a helper routine that allocates a new MergeMethod
-// to sotre v and returns a pointer to it.
-func MergeMethod(v MergeMethodValue) *MergeMethodValue {
-	p := new(MergeMethodValue)
-	*p = v
-	return p
-}
-
-// EventTypeValue represents actions type for contribution events
-type EventTypeValue string
-
-// List of available action type
-//
-// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#action-types
-const (
-	CreatedEventType   EventTypeValue = "created"
-	UpdatedEventType   EventTypeValue = "updated"
-	ClosedEventType    EventTypeValue = "closed"
-	ReopenedEventType  EventTypeValue = "reopened"
-	PushedEventType    EventTypeValue = "pushed"
-	CommentedEventType EventTypeValue = "commented"
-	MergedEventType    EventTypeValue = "merged"
-	JoinedEventType    EventTypeValue = "joined"
-	LeftEventType      EventTypeValue = "left"
-	DestroyedEventType EventTypeValue = "destroyed"
-	ExpiredEventType   EventTypeValue = "expired"
-)
-
-// EventTargetTypeValue represents actions type value for contribution events
-type EventTargetTypeValue string
-
-// List of available action type
-//
-// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#target-types
-const (
-	IssueEventTargetType        EventTargetTypeValue = "issue"
-	MilestoneEventTargetType    EventTargetTypeValue = "milestone"
-	MergeRequestEventTargetType EventTargetTypeValue = "merge_request"
-	NoteEventTargetType         EventTargetTypeValue = "note"
-	ProjectEventTargetType      EventTargetTypeValue = "project"
-	SnippetEventTargetType      EventTargetTypeValue = "snippet"
-	UserEventTargetType         EventTargetTypeValue = "user"
-)
-
 // Bool is a helper routine that allocates a new bool value
 // to store v and returns a pointer to it.
 func Bool(v bool) *bool {


Test patch:
diff --git a/fixtures_test.go b/fixtures_test.go
index 4528d98..f1f6681 100644
--- a/fixtures_test.go
+++ b/fixtures_test.go
@@ -68,7 +68,9 @@ const (
 		"id":1,
 		"name":"awesome-v0.2.dmg",
 		"url":"http://192.168.10.15:3000",
-		"external":true
+		"direct_asset_url": "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/awesome-v0.2.dmg",
+		"external":true,
+		"link_type": "other"
 	}`
 
 	// exampleReleaseLinkList provides fixture for Release Links tests.
@@ -83,7 +85,9 @@ const (
 			"id": 1,
 			"name": "awesome-v0.2.dmg",
 			"url": "http://192.168.10.15:3000",
-			"external": true
+			"direct_asset_url": "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/awesome-v0.2.dmg",
+			"external": false,
+			"link_type": "other"
 		}
 	]`
 
diff --git a/releaselinks_test.go b/releaselinks_test.go
index c725a4f..0387eb6 100644
--- a/releaselinks_test.go
+++ b/releaselinks_test.go
@@ -20,6 +20,9 @@ import (
 	"fmt"
 	"net/http"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestReleaseLinksService_ListReleaseLinks(t *testing.T) {
@@ -35,40 +38,94 @@ func TestReleaseLinksService_ListReleaseLinks(t *testing.T) {
 	releaseLinks, _, err := client.ReleaseLinks.ListReleaseLinks(
 		1, exampleTagName, &ListReleaseLinksOptions{},
 	)
-	if err != nil {
-		t.Error(err)
-	}
-	if len(releaseLinks) != 2 {
-		t.Error("expected 2 links")
-	}
-	if releaseLinks[0].Name != "awesome-v0.2.msi" {
-		t.Errorf("release link name, expected '%s', got '%s'", "awesome-v0.2.msi",
-			releaseLinks[0].Name)
+
+	require.NoError(t, err)
+	expectedReleaseLinks := []*ReleaseLink{
+		{
+			ID:       2,
+			Name:     "awesome-v0.2.msi",
+			URL:      "http://192.168.10.15:3000/msi",
+			External: true,
+		},
+		{
+			ID:             1,
+			Name:           "awesome-v0.2.dmg",
+			URL:            "http://192.168.10.15:3000",
+			DirectAssetURL: "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/awesome-v0.2.dmg",
+			External:       false,
+			LinkType:       OtherLinkType,
+		},
 	}
+	assert.Equal(t, expectedReleaseLinks, releaseLinks)
 }
 
 func TestReleaseLinksService_CreateReleaseLink(t *testing.T) {
-	mux, server, client := setup(t)
-	defer teardown(server)
+	testCases := []struct {
+		description string
+		options     *CreateReleaseLinkOptions
+		response    string
+		want        *ReleaseLink
+	}{
+		{
+			description: "Mandatory Attributes",
+			options: &CreateReleaseLinkOptions{
+				Name: String("awesome-v0.2.dmg"),
+				URL:  String("http://192.168.10.15:3000")},
+			response: `{
+				"id":1,
+				"name":"awesome-v0.2.dmg",
+				"url":"http://192.168.10.15:3000",
+				"external":true
+			}`,
+			want: &ReleaseLink{
+				ID:       1,
+				Name:     "awesome-v0.2.dmg",
+				URL:      "http://192.168.10.15:3000",
+				External: true,
+			},
+		},
+		{
+			description: "Optional Attributes",
+			options: &CreateReleaseLinkOptions{
+				Name:     String("release-notes.md"),
+				URL:      String("http://192.168.10.15:3000"),
+				FilePath: String("docs/release-notes.md"),
+				LinkType: LinkType(OtherLinkType),
+			},
+			response: `{
+				"id":1,
+				"name":"release-notes.md",
+				"url":"http://192.168.10.15:3000",
+				"direct_asset_url": "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/docs/release-notes.md",
+				"external": false,
+				"link_type": "other"
+			}`,
+			want: &ReleaseLink{
+				ID:             1,
+				Name:           "release-notes.md",
+				URL:            "http://192.168.10.15:3000",
+				DirectAssetURL: "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/docs/release-notes.md",
+				External:       false,
+				LinkType:       OtherLinkType,
+			},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.description, func(t *testing.T) {
+			mux, server, client := setup(t)
+			defer teardown(server)
 
-	mux.HandleFunc("/api/v4/projects/1/releases/v0.1/assets/links",
-		func(w http.ResponseWriter, r *http.Request) {
-			testMethod(t, r, http.MethodPost)
-			fmt.Fprint(w, exampleReleaseLink)
-		})
+			mux.HandleFunc("/api/v4/projects/1/releases/v0.1/assets/links",
+				func(w http.ResponseWriter, r *http.Request) {
+					testMethod(t, r, http.MethodPost)
+					fmt.Fprint(w, tc.response)
+				})
+
+			releaseLink, _, err := client.ReleaseLinks.CreateReleaseLink(1, exampleTagName, tc.options)
 
-	releaseLink, _, err := client.ReleaseLinks.CreateReleaseLink(
-		1, exampleTagName,
-		&CreateReleaseLinkOptions{
-			Name: String(exampleReleaseName),
-			URL:  String("http://192.168.10.15:3000"),
+			require.NoError(t, err)
+			assert.Equal(t, tc.want, releaseLink)
 		})
-	if err != nil {
-		t.Error(err)
-	}
-	if releaseLink.Name != exampleReleaseName {
-		t.Errorf("release link name, expected '%s', got '%s'", exampleReleaseName,
-			releaseLink.Name)
 	}
 }
 
@@ -105,15 +162,21 @@ func TestReleaseLinksService_UpdateReleaseLink(t *testing.T) {
 	releaseLink, _, err := client.ReleaseLinks.UpdateReleaseLink(
 		1, exampleTagName, 1,
 		&UpdateReleaseLinkOptions{
-			Name: String(exampleReleaseName),
+			Name:     String(exampleReleaseName),
+			FilePath: String("http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/awesome-v0.2.dmg"),
+			LinkType: LinkType(OtherLinkType),
 		})
-	if err != nil {
-		t.Error(err)
-	}
-	if releaseLink.Name != exampleReleaseName {
-		t.Errorf("release link name, expected '%s', got '%s'", exampleReleaseName,
-			releaseLink.Name)
+
+	require.NoError(t, err)
+	expectedRelease := &ReleaseLink{
+		ID:             1,
+		Name:           "awesome-v0.2.dmg",
+		URL:            "http://192.168.10.15:3000",
+		DirectAssetURL: "http://192.168.10.15:3000/namespace/example/-/releases/v0.1/downloads/awesome-v0.2.dmg",
+		External:       true,
+		LinkType:       OtherLinkType,
 	}
+	assert.Equal(t, expectedRelease, releaseLink)
 }
 
 func TestReleaseLinksService_DeleteReleaseLink(t *testing.T) {