mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
Update dependencies
This commit is contained in:
parent
8f3ae94b4e
commit
3a0bcd21e7
77
Gopkg.lock
generated
77
Gopkg.lock
generated
@ -6,16 +6,16 @@
|
||||
name = "cloud.google.com/go"
|
||||
packages = ["compute/metadata"]
|
||||
pruneopts = "UT"
|
||||
revision = "64a2037ec6be8a4b0c1d1f706ed35b428b989239"
|
||||
version = "v0.26.0"
|
||||
revision = "dfffe386c33fb24c34ee501e5723df5b97b98514"
|
||||
version = "v0.30.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:1164f5eccc8905c03a2c688141c81f06522ed5fd2eb65cb43debeb730a018276"
|
||||
digest = "1:636ac9f696c988f0038afd43592f7a0fff29038588ab1064ba8ba3476bb41091"
|
||||
name = "github.com/adlio/trello"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "8a458717123e328d9103a3bf075e64bc1ec961f8"
|
||||
revision = "e4cc07c871d0ee17c5579131fc1c786efbb5fe2a"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9253f97cfbbe049b631877c80badecc69620711b3e335f6cf97a7809681da388"
|
||||
@ -59,11 +59,11 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:b5d1ba5dc01e8129b7df5cf95380a66b0e0f122850189b7ec255953b15522f16"
|
||||
digest = "1:579829cc9bf515e2f9e39f91df03b9333c311402bac5a4935dc8ef81e3a57160"
|
||||
name = "github.com/andygrunwald/go-gerrit"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "197fe0d2e796b3d985b9cd91be8afd4415692f39"
|
||||
revision = "30ce279197661497040ec81c4b64539562eb2d4b"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@ -117,12 +117,12 @@
|
||||
version = "v1.1.6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:6f9339c912bbdda81302633ad7e99a28dfa5a639c864061f1929510a9a64aa74"
|
||||
name = "github.com/dustin/go-humanize"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "9f541cc9db5d55bce703bd99987c9d5cb8eea45e"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@ -134,14 +134,14 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:00b9cce210566117aff926677c005aeaea6c85374e67bdcb72783af237c48f97"
|
||||
digest = "1:490cf9d7deec1b0dcac6cc8b17f307c48c4821bc2140d46e48175a83acbfe40d"
|
||||
name = "github.com/gdamore/tcell"
|
||||
packages = [
|
||||
".",
|
||||
"terminfo",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "de7e78efa4a71b3f36c7154989c529dbdf9ae623"
|
||||
revision = "493f3b46b3c20880afc8e04ceeb1c6d5aa3363d7"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:57fa4c058c21ce25d0b7272518dd746065117abf6cc706158b0d361202024520"
|
||||
@ -152,28 +152,28 @@
|
||||
version = "v4.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:15042ad3498153684d09f393bbaec6b216c8eec6d61f63dff711de7d64ed8861"
|
||||
digest = "1:97df918963298c287643883209a2c3f642e6593379f97ab400c2a2e219ab647d"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = ["proto"]
|
||||
pruneopts = "UT"
|
||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||
version = "v1.1.0"
|
||||
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:4be3c01ef56542da4f1268c52a8b88bae5309ec3dade7755e250bc933e13ac70"
|
||||
digest = "1:8c90219e8a1f5f7cad019b77697aaae2b236db5a1a46ae688e5ede406e109182"
|
||||
name = "github.com/google/go-github"
|
||||
packages = ["github"]
|
||||
pruneopts = "UT"
|
||||
revision = "d7732128a00e8e95e8fe896017da18ee20b2180d"
|
||||
revision = "68a79fc6a32bab9406083545e667a65ba67b0a3e"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a63cff6b5d8b95638bfe300385d93b2a6d9d687734b863da8e09dc834510a690"
|
||||
name = "github.com/google/go-querystring"
|
||||
packages = ["query"]
|
||||
pruneopts = "UT"
|
||||
revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a"
|
||||
revision = "44c6ddd0a2342c386950e880b658017258da92fc"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2cff208d4759f6ba1b1cd228587b0a1869f95f22542ec9cd17fff64430113c7"
|
||||
@ -201,11 +201,11 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3a16300d913e1050d4a7ff296106a11b72da05c5dd3475f2ae1feb0830866461"
|
||||
digest = "1:2277d06c9dcd34d70119b624115f3d74672647322349bb76a8adedbc623a3955"
|
||||
name = "github.com/olebedev/config"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "ed90d2035b8114c30b9cb65e7d52e10a7148f8c6"
|
||||
revision = "57f804269e64d41bfe46efb76232c47f49d40902"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
|
||||
@ -241,19 +241,19 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:9deb696e9739b45e91369ffb3876d4e0d2bc45e0d35062fb3c5aa44fa77eff6c"
|
||||
digest = "1:00d9f4017b55d590139b77fbd851aef56f179479fc40a4afdedaf187e828f99d"
|
||||
name = "github.com/rivo/tview"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "21f50f5bc400083b4eb23304887d9cd0fc00d075"
|
||||
revision = "a7c1880d62d37422830f0a15823b51301ca9f354"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:de0cf35afb9cad7b20ff8871ae3b64cecc7116bffb583bc7b3c1bb5c37473149"
|
||||
digest = "1:93ff598337cb35c40079a1ba766729c20eb1756e37f5740eac4c8e943f59664d"
|
||||
name = "github.com/sticreations/spotigopher"
|
||||
packages = ["spotigopher"]
|
||||
pruneopts = "UT"
|
||||
revision = "995ee350dc1c54259597de65dde7a9e56a4b73af"
|
||||
revision = "98632f6f94b087f2582be76980f76bc070d37176"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:18752d0b95816a1b777505a97f71c7467a8445b8ffb55631a7bf779f6ba4fa83"
|
||||
@ -265,11 +265,11 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:ca067f3d378064d50a239b1f0d4dcd2dab6356f7643993bdf02c91d6e621c028"
|
||||
digest = "1:afc7e1c726a88e6cd5689cca19fbc86128dffe86fd37e3e0841767f3a951182e"
|
||||
name = "github.com/xanzy/go-gitlab"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "f3bc634ab936f7e4ee5e21334ccfdfeb5601d477"
|
||||
revision = "1444249c1b2a8e4cdb5a76dc9c8d02d1133180be"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@ -281,11 +281,19 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:f6f2814b88ad3dd5ca45e1e9a7a0caba1ddb1bfaf1c4173ebd991d854e1669eb"
|
||||
digest = "1:1ff6915a45fb06d272e614e72219aa058aefc545f597a708eb0cc6cb778c344a"
|
||||
name = "github.com/zmb3/spotify"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "a4bd83f60e06ab58f3516c61680d0532aabf470e"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:02a2f8f0718f239866000d2d1bca016d14f0f6de37615c315f34da17dc630259"
|
||||
name = "github.com/zorkian/go-datadog-api"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "d7b8b10db6a7eb1c1c2424b10a795a1662e80c9a"
|
||||
revision = "dc324c09cf05eef3e3a82bde06ae0c4dd349a767"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@ -296,11 +304,11 @@
|
||||
"context/ctxhttp",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "aaf60122140d3fcf75376d319f0554393160eb50"
|
||||
revision = "04a2e542c03f1d053ab3e4d6e5abcd4b66e2be8e"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:bea0314c10bd362ab623af4880d853b5bad3b63d0ab9945c47e461b8d04203ed"
|
||||
digest = "1:faa25cb78cf9c8cec9345d4ed07322cdef6a8c968b3d0a6b6c3609067c7386eb"
|
||||
name = "golang.org/x/oauth2"
|
||||
packages = [
|
||||
".",
|
||||
@ -310,7 +318,7 @@
|
||||
"jwt",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "3d292e4d0cdc3a0113e6d207bb137145ef1de42f"
|
||||
revision = "9dcd33a902f40452422c2367fefcb95b54f9f8f8"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:37672ad5821719e2df8509c2edd4ba5ae192463237c73c3a2d24ef8b2bc9e36f"
|
||||
@ -328,7 +336,7 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c8d95fbc783cab4e8986b1bf3dbe5312db7521e4342ac755280cfa78ce0ee792"
|
||||
digest = "1:a1d58b7c9eeceeac201a6f6b10092cd2a5985791a45843c838151ee3f751d3c3"
|
||||
name = "google.golang.org/api"
|
||||
packages = [
|
||||
"calendar/v3",
|
||||
@ -338,10 +346,10 @@
|
||||
"sheets/v4",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "e21acd801f91da814261b938941d193bb036441a"
|
||||
revision = "a2651947f503a1793446d4058bb073a6fdf99e53"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c8907869850adaa8bd7631887948d0684f3787d0912f1c01ab72581a6c34432e"
|
||||
digest = "1:193950893ea275f89ed92e5da11ed8fa1436872f755a9ea5d4afa83dc9d9c3a8"
|
||||
name = "google.golang.org/appengine"
|
||||
packages = [
|
||||
".",
|
||||
@ -356,8 +364,8 @@
|
||||
"urlfetch",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
|
||||
version = "v1.1.0"
|
||||
revision = "ae0ab99deb4dc413a2b4bd6c8bdd0eb67f1e4d06"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
|
||||
@ -390,6 +398,7 @@
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/xanzy/go-gitlab",
|
||||
"github.com/yfronto/newrelic",
|
||||
"github.com/zmb3/spotify",
|
||||
"github.com/zorkian/go-datadog-api",
|
||||
"golang.org/x/oauth2",
|
||||
"golang.org/x/oauth2/google",
|
||||
|
15
vendor/github.com/adlio/trello/action.go
generated
vendored
15
vendor/github.com/adlio/trello/action.go
generated
vendored
@ -23,10 +23,10 @@ type Action struct {
|
||||
type ActionData struct {
|
||||
Text string `json:"text,omitempty"`
|
||||
List *List `json:"list,omitempty"`
|
||||
Card *Card `json:"card,omitempty"`
|
||||
CardSource *Card `json:"cardSource,omitempty"`
|
||||
Card *ActionDataCard `json:"card,omitempty"`
|
||||
CardSource *ActionDataCard `json:"cardSource,omitempty"`
|
||||
Board *Board `json:"board,omitempty"`
|
||||
Old *Card `json:"old,omitempty"`
|
||||
Old *ActionDataCard `json:"old,omitempty"`
|
||||
ListBefore *List `json:"listBefore,omitempty"`
|
||||
ListAfter *List `json:"listAfter,omitempty"`
|
||||
DateLastEdited time.Time `json:"dateLastEdited"`
|
||||
@ -35,6 +35,15 @@ type ActionData struct {
|
||||
Checklist *Checklist `json:"checklist"`
|
||||
}
|
||||
|
||||
type ActionDataCard struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
IDShort int `json:"idShort"`
|
||||
ShortLink string `json:"shortLink"`
|
||||
Pos float64 `json:"pos"`
|
||||
Closed bool `json:"closed"`
|
||||
}
|
||||
|
||||
func (b *Board) GetActions(args Arguments) (actions ActionCollection, err error) {
|
||||
path := fmt.Sprintf("boards/%s/actions", b.ID)
|
||||
err = b.client.Get(path, args, &actions)
|
||||
|
1
vendor/github.com/adlio/trello/board.go
generated
vendored
1
vendor/github.com/adlio/trello/board.go
generated
vendored
@ -54,6 +54,7 @@ type Board struct {
|
||||
} `json:"labelNames"`
|
||||
Lists []*List `json:"lists"`
|
||||
Actions []*Action `json:"actions"`
|
||||
Organization Organization `json:"organization"`
|
||||
}
|
||||
|
||||
type BackgroundImage struct {
|
||||
|
63
vendor/github.com/adlio/trello/card.go
generated
vendored
63
vendor/github.com/adlio/trello/card.go
generated
vendored
@ -76,6 +76,11 @@ type Card struct {
|
||||
// Labels
|
||||
IDLabels []string `json:"idLabels,omitempty"`
|
||||
Labels []*Label `json:"labels,omitempty"`
|
||||
|
||||
// Custom Fields
|
||||
CustomFieldItems []*CustomFieldItem `json:"customFieldItems",omitempty`
|
||||
|
||||
customFieldMap *map[string]interface{}
|
||||
}
|
||||
|
||||
func (c *Card) CreatedAt() time.Time {
|
||||
@ -83,6 +88,52 @@ func (c *Card) CreatedAt() time.Time {
|
||||
return t
|
||||
}
|
||||
|
||||
func (c *Card) CustomFields(boardCustomFields []*CustomField) (map[string]interface{}) {
|
||||
|
||||
cfm := c.customFieldMap
|
||||
|
||||
if cfm == nil {
|
||||
cfm = &(map[string]interface{} {})
|
||||
|
||||
// bcfOptionNames[CustomField ID] = Custom Field Name
|
||||
bcfOptionNames := map[string]string{}
|
||||
|
||||
// bcfOptionsMap[CustomField ID][ID of the option] = Value of the option
|
||||
bcfOptionsMap := map[string] map[string]interface{}{}
|
||||
|
||||
for _, bcf := range boardCustomFields {
|
||||
bcfOptionNames[bcf.ID] = bcf.Name
|
||||
for _, cf := range bcf.Options {
|
||||
// create 2nd level map when not available yet
|
||||
map2, ok := bcfOptionsMap[cf.IDCustomField]
|
||||
if !ok {
|
||||
map2 = map[string]interface{}{}
|
||||
bcfOptionsMap[bcf.ID] = map2
|
||||
}
|
||||
|
||||
bcfOptionsMap[bcf.ID][cf.ID] = cf.Value.Text
|
||||
}
|
||||
}
|
||||
|
||||
for _, cf := range c.CustomFieldItems {
|
||||
name := bcfOptionNames[cf.IDCustomField]
|
||||
|
||||
// create 2nd level map when not available yet
|
||||
map2, ok := bcfOptionsMap[cf.IDCustomField]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
value, ok := map2[cf.IDValue]
|
||||
|
||||
if ok {
|
||||
(*cfm)[name] = value
|
||||
}
|
||||
}
|
||||
c.customFieldMap = cfm
|
||||
}
|
||||
return *cfm
|
||||
}
|
||||
|
||||
func (c *Card) MoveToList(listID string, args Arguments) error {
|
||||
path := fmt.Sprintf("cards/%s", c.ID)
|
||||
args["idList"] = listID
|
||||
@ -105,6 +156,18 @@ func (c *Card) AddMemberID(memberID string) (member []*Member, err error) {
|
||||
return member, err
|
||||
}
|
||||
|
||||
func (c *Card) RemoveIDLabel(labelID string, label *Label) error {
|
||||
path := fmt.Sprintf("cards/%s/idLabels/%s", c.ID, labelID)
|
||||
return c.client.Delete(path, Defaults(), label)
|
||||
|
||||
}
|
||||
|
||||
func (c *Card) AddIDLabel(labelID string) error {
|
||||
path := fmt.Sprintf("cards/%s/idLabels", c.ID)
|
||||
err := c.client.Post(path, Arguments{"value": labelID}, &c.IDLabels)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Card) MoveToTopOfList() error {
|
||||
path := fmt.Sprintf("cards/%s", c.ID)
|
||||
return c.client.Put(path, Arguments{"pos": "top"}, c)
|
||||
|
2
vendor/github.com/adlio/trello/client.go
generated
vendored
2
vendor/github.com/adlio/trello/client.go
generated
vendored
@ -234,6 +234,6 @@ func (c *Client) Delete(path string, args Arguments, target interface{}) error {
|
||||
|
||||
func (c *Client) log(format string, args ...interface{}) {
|
||||
if c.Logger != nil {
|
||||
c.Logger.Debugf(format, args)
|
||||
c.Logger.Debugf(format, args...)
|
||||
}
|
||||
}
|
||||
|
49
vendor/github.com/adlio/trello/custom-fields.go
generated
vendored
Normal file
49
vendor/github.com/adlio/trello/custom-fields.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package trello
|
||||
|
||||
import "fmt"
|
||||
|
||||
type CustomFieldItem struct {
|
||||
ID string `json:"id"`
|
||||
IDValue string `json:"idValue"`
|
||||
IDCustomField string `json:"idCustomField"`
|
||||
IDModel string `json:"idModel"`
|
||||
IDModelType string `json:"modelType,omitempty"`
|
||||
}
|
||||
|
||||
type CustomField struct {
|
||||
ID string `json:"id"`
|
||||
IDModel string `json:"idModel"`
|
||||
IDModelType string `json:"modelType,omitempty"`
|
||||
FieldGroup string `json:"fieldGroup"`
|
||||
Name string `json:"name"`
|
||||
Pos int `json:"pos"`
|
||||
Display struct {
|
||||
CardFront bool `json:"cardfront"`
|
||||
} `json:"display"`
|
||||
Type string `json:"type"`
|
||||
Options []*CustomFieldOption `json:"options"`
|
||||
}
|
||||
|
||||
type CustomFieldOption struct {
|
||||
ID string `json:"id"`
|
||||
IDCustomField string `json:"idCustomField"`
|
||||
Value struct {
|
||||
Text string `json:"text"`
|
||||
} `json:"value"`
|
||||
Color string `json:"color,omitempty"`
|
||||
Pos int `json:"pos"`
|
||||
}
|
||||
|
||||
func (c *Client) GetCustomField(fieldID string, args Arguments) (customField *CustomField, err error) {
|
||||
path := fmt.Sprintf("customFields/%s", fieldID)
|
||||
err = c.Get(path, args, &customField)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func (b *Board) GetCustomFields(args Arguments) (customFields []*CustomField, err error) {
|
||||
path := fmt.Sprintf("boards/%s/customFields", b.ID)
|
||||
err = b.client.Get(path, args, &customFields)
|
||||
return
|
||||
}
|
||||
|
1
vendor/github.com/andygrunwald/go-gerrit/.travis.yml
generated
vendored
1
vendor/github.com/andygrunwald/go-gerrit/.travis.yml
generated
vendored
@ -3,6 +3,7 @@ language: go
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.10.x"
|
||||
- "1.9.x"
|
||||
- "1.8.x"
|
||||
|
8
vendor/github.com/andygrunwald/go-gerrit/authentication.go
generated
vendored
8
vendor/github.com/andygrunwald/go-gerrit/authentication.go
generated
vendored
@ -1,7 +1,7 @@
|
||||
package gerrit
|
||||
|
||||
import (
|
||||
"crypto/md5" // nolint: gas
|
||||
"crypto/md5" // nolint: gosec
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
@ -117,7 +117,7 @@ func (s *AuthenticationService) digestAuthHeader(response *http.Response) (strin
|
||||
uriHeader := authenticate["uri"]
|
||||
|
||||
// A1
|
||||
h := md5.New() // nolint: gas
|
||||
h := md5.New() // nolint: gosec
|
||||
A1 := fmt.Sprintf("%s:%s:%s", s.name, realmHeader, s.secret)
|
||||
if _, err := io.WriteString(h, A1); err != nil {
|
||||
return "", err
|
||||
@ -125,7 +125,7 @@ func (s *AuthenticationService) digestAuthHeader(response *http.Response) (strin
|
||||
HA1 := fmt.Sprintf("%x", h.Sum(nil))
|
||||
|
||||
// A2
|
||||
h = md5.New() // nolint: gas
|
||||
h = md5.New() // nolint: gosec
|
||||
A2 := fmt.Sprintf("%s:%s", response.Request.Method, uriHeader)
|
||||
if _, err := io.WriteString(h, A2); err != nil {
|
||||
return "", err
|
||||
@ -141,7 +141,7 @@ func (s *AuthenticationService) digestAuthHeader(response *http.Response) (strin
|
||||
bytes += n
|
||||
}
|
||||
cnonce := base64.StdEncoding.EncodeToString(k)
|
||||
digest := md5.New() // nolint: gas
|
||||
digest := md5.New() // nolint: gosec
|
||||
if _, err := digest.Write([]byte(strings.Join([]string{HA1, nonceHeader, "00000001", cnonce, qopHeader, HA2}, ":"))); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
26
vendor/github.com/andygrunwald/go-gerrit/changes.go
generated
vendored
26
vendor/github.com/andygrunwald/go-gerrit/changes.go
generated
vendored
@ -258,11 +258,11 @@ type RobotCommentInput struct {
|
||||
CommentInput
|
||||
|
||||
// The ID of the robot that generated this comment.
|
||||
RobotId string `json:"robot_id"`
|
||||
RobotID string `json:"robot_id"`
|
||||
// An ID of the run of the robot.
|
||||
RobotRunId string `json:"robot_run_id"`
|
||||
RobotRunID string `json:"robot_run_id"`
|
||||
// URL to more information.
|
||||
Url string `json:"url,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
// Robot specific properties as map that maps arbitrary keys to values.
|
||||
Properties *map[string]*string `json:"properties,omitempty"`
|
||||
// Suggested fixes for this robot comment as a list of FixSuggestionInfo
|
||||
@ -277,11 +277,11 @@ type RobotCommentInfo struct {
|
||||
CommentInfo
|
||||
|
||||
// The ID of the robot that generated this comment.
|
||||
RobotId string `json:"robot_id"`
|
||||
RobotID string `json:"robot_id"`
|
||||
// An ID of the run of the robot.
|
||||
RobotRunId string `json:"robot_run_id"`
|
||||
RobotRunID string `json:"robot_run_id"`
|
||||
// URL to more information.
|
||||
Url string `json:"url,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
// Robot specific properties as map that maps arbitrary keys to values.
|
||||
Properties map[string]string `json:"properties,omitempty"`
|
||||
// Suggested fixes for this robot comment as a list of FixSuggestionInfo
|
||||
@ -294,7 +294,7 @@ type RobotCommentInfo struct {
|
||||
type FixSuggestionInfo struct {
|
||||
// The UUID of the suggested fix. It will be generated automatically and
|
||||
// hence will be ignored if it’s set for input objects.
|
||||
FixId string `json:"fix_id"`
|
||||
FixID string `json:"fix_id"`
|
||||
// A description of the suggested fix.
|
||||
Description string `json:"description"`
|
||||
// A list of FixReplacementInfo entities indicating how the content of one or
|
||||
@ -780,11 +780,17 @@ func (s *ChangesService) change(tail string, changeID string, input interface{})
|
||||
|
||||
v := new(ChangeInfo)
|
||||
resp, err := s.client.Do(req, v)
|
||||
if resp.StatusCode == http.StatusConflict {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
err = errors.New(string(body[:]))
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
if resp.StatusCode == http.StatusConflict {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return v, resp, err
|
||||
}
|
||||
return v, resp, errors.New(string(body[:]))
|
||||
}
|
||||
return v, resp, nil
|
||||
}
|
||||
|
||||
// SubmitChange submits a change.
|
||||
|
1
vendor/github.com/gdamore/tcell/README.md
generated
vendored
1
vendor/github.com/gdamore/tcell/README.md
generated
vendored
@ -25,6 +25,7 @@ ways. It also adds substantial functionality beyond termbox.
|
||||
* [tui-go](https://github.com/marcusolsson/tui-go) - UI library for terminal apps
|
||||
* [gomandelbrot](https://github.com/rgm3/gomandelbrot) - Mandelbrot!
|
||||
* [WTF](https://github.com/senorprogrammer/wtf)- Personal information dashboard for your terminal
|
||||
* [browsh](https://github.com/browsh-org/browsh) - A fully-modern text-based browser, rendering to TTY and browsers ([video](https://www.youtube.com/watch?v=HZq86XfBoRo))
|
||||
|
||||
## Pure Go Terminfo Database
|
||||
|
||||
|
7
vendor/github.com/gdamore/tcell/cell.go
generated
vendored
7
vendor/github.com/gdamore/tcell/cell.go
generated
vendored
@ -52,6 +52,10 @@ func (cb *CellBuffer) SetContent(x int, y int,
|
||||
i := 0
|
||||
for i < len(c.currComb) {
|
||||
r := c.currComb[i]
|
||||
if r == '\u200d' {
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
if runewidth.RuneWidth(r) != 0 {
|
||||
// not a combining character, yank it
|
||||
c.currComb = append(c.currComb[:i-1], c.currComb[i+1:]...)
|
||||
@ -175,12 +179,13 @@ func (cb *CellBuffer) Resize(w, h int) {
|
||||
|
||||
// Fill fills the entire cell buffer array with the specified character
|
||||
// and style. Normally choose ' ' to clear the screen. This API doesn't
|
||||
// support combining characters.
|
||||
// support combining characters, or characters with a width larger than one.
|
||||
func (cb *CellBuffer) Fill(r rune, style Style) {
|
||||
for i := range cb.cells {
|
||||
c := &cb.cells[i]
|
||||
c.currMain = r
|
||||
c.currComb = nil
|
||||
c.currStyle = style
|
||||
c.width = 1
|
||||
}
|
||||
}
|
||||
|
3
vendor/github.com/golang/protobuf/LICENSE
generated
vendored
3
vendor/github.com/golang/protobuf/LICENSE
generated
vendored
@ -1,7 +1,4 @@
|
||||
Go support for Protocol Buffers - Google's data interchange format
|
||||
|
||||
Copyright 2010 The Go Authors. All rights reserved.
|
||||
https://github.com/golang/protobuf
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
|
18
vendor/github.com/golang/protobuf/proto/encode.go
generated
vendored
18
vendor/github.com/golang/protobuf/proto/encode.go
generated
vendored
@ -37,27 +37,9 @@ package proto
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// RequiredNotSetError is the error returned if Marshal is called with
|
||||
// a protocol buffer struct whose required fields have not
|
||||
// all been initialized. It is also the error returned if Unmarshal is
|
||||
// called with an encoded protocol buffer that does not include all the
|
||||
// required fields.
|
||||
//
|
||||
// When printed, RequiredNotSetError reports the first unset required field in a
|
||||
// message. If the field cannot be precisely determined, it is reported as
|
||||
// "{Unknown}".
|
||||
type RequiredNotSetError struct {
|
||||
field string
|
||||
}
|
||||
|
||||
func (e *RequiredNotSetError) Error() string {
|
||||
return fmt.Sprintf("proto: required field %q not set", e.field)
|
||||
}
|
||||
|
||||
var (
|
||||
// errRepeatedHasNil is the error returned if Marshal is called with
|
||||
// a struct with a repeated field containing a nil element.
|
||||
|
62
vendor/github.com/golang/protobuf/proto/lib.go
generated
vendored
62
vendor/github.com/golang/protobuf/proto/lib.go
generated
vendored
@ -265,7 +265,6 @@ package proto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
@ -274,7 +273,66 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var errInvalidUTF8 = errors.New("proto: invalid UTF-8 string")
|
||||
// RequiredNotSetError is an error type returned by either Marshal or Unmarshal.
|
||||
// Marshal reports this when a required field is not initialized.
|
||||
// Unmarshal reports this when a required field is missing from the wire data.
|
||||
type RequiredNotSetError struct{ field string }
|
||||
|
||||
func (e *RequiredNotSetError) Error() string {
|
||||
if e.field == "" {
|
||||
return fmt.Sprintf("proto: required field not set")
|
||||
}
|
||||
return fmt.Sprintf("proto: required field %q not set", e.field)
|
||||
}
|
||||
func (e *RequiredNotSetError) RequiredNotSet() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type invalidUTF8Error struct{ field string }
|
||||
|
||||
func (e *invalidUTF8Error) Error() string {
|
||||
if e.field == "" {
|
||||
return "proto: invalid UTF-8 detected"
|
||||
}
|
||||
return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field)
|
||||
}
|
||||
func (e *invalidUTF8Error) InvalidUTF8() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8.
|
||||
// This error should not be exposed to the external API as such errors should
|
||||
// be recreated with the field information.
|
||||
var errInvalidUTF8 = &invalidUTF8Error{}
|
||||
|
||||
// isNonFatal reports whether the error is either a RequiredNotSet error
|
||||
// or a InvalidUTF8 error.
|
||||
func isNonFatal(err error) bool {
|
||||
if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() {
|
||||
return true
|
||||
}
|
||||
if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type nonFatal struct{ E error }
|
||||
|
||||
// Merge merges err into nf and reports whether it was successful.
|
||||
// Otherwise it returns false for any fatal non-nil errors.
|
||||
func (nf *nonFatal) Merge(err error) (ok bool) {
|
||||
if err == nil {
|
||||
return true // not an error
|
||||
}
|
||||
if !isNonFatal(err) {
|
||||
return false // fatal error
|
||||
}
|
||||
if nf.E == nil {
|
||||
nf.E = err // store first instance of non-fatal error
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Message is implemented by generated protocol buffer messages.
|
||||
type Message interface {
|
||||
|
14
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
14
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
@ -139,7 +139,7 @@ type Properties struct {
|
||||
Repeated bool
|
||||
Packed bool // relevant for repeated primitives only
|
||||
Enum string // set for enum types only
|
||||
proto3 bool // whether this is known to be a proto3 field; set for []byte only
|
||||
proto3 bool // whether this is known to be a proto3 field
|
||||
oneof bool // whether this is a oneof field
|
||||
|
||||
Default string // default value
|
||||
@ -149,8 +149,8 @@ type Properties struct {
|
||||
sprop *StructProperties // set for struct types only
|
||||
|
||||
mtype reflect.Type // set for map types only
|
||||
mkeyprop *Properties // set for map types only
|
||||
mvalprop *Properties // set for map types only
|
||||
MapKeyProp *Properties // set for map types only
|
||||
MapValProp *Properties // set for map types only
|
||||
}
|
||||
|
||||
// String formats the properties in the protobuf struct field tag style.
|
||||
@ -275,16 +275,16 @@ func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, loc
|
||||
|
||||
case reflect.Map:
|
||||
p.mtype = t1
|
||||
p.mkeyprop = &Properties{}
|
||||
p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
||||
p.mvalprop = &Properties{}
|
||||
p.MapKeyProp = &Properties{}
|
||||
p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
||||
p.MapValProp = &Properties{}
|
||||
vtype := p.mtype.Elem()
|
||||
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
||||
// The value type is not a message (*T) or bytes ([]byte),
|
||||
// so we need encoders for the pointer to this type.
|
||||
vtype = reflect.PtrTo(vtype)
|
||||
}
|
||||
p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
||||
p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
||||
}
|
||||
|
||||
if p.stype != nil {
|
||||
|
190
vendor/github.com/golang/protobuf/proto/table_marshal.go
generated
vendored
190
vendor/github.com/golang/protobuf/proto/table_marshal.go
generated
vendored
@ -231,7 +231,7 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte
|
||||
return b, err
|
||||
}
|
||||
|
||||
var err, errreq error
|
||||
var err, errLater error
|
||||
// The old marshaler encodes extensions at beginning.
|
||||
if u.extensions.IsValid() {
|
||||
e := ptr.offset(u.extensions).toExtensions()
|
||||
@ -252,11 +252,13 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte
|
||||
}
|
||||
}
|
||||
for _, f := range u.fields {
|
||||
if f.required && errreq == nil {
|
||||
if f.required {
|
||||
if ptr.offset(f.field).getPointer().isNil() {
|
||||
// Required field is not set.
|
||||
// We record the error but keep going, to give a complete marshaling.
|
||||
errreq = &RequiredNotSetError{f.name}
|
||||
if errLater == nil {
|
||||
errLater = &RequiredNotSetError{f.name}
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -269,14 +271,21 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte
|
||||
if err1, ok := err.(*RequiredNotSetError); ok {
|
||||
// Required field in submessage is not set.
|
||||
// We record the error but keep going, to give a complete marshaling.
|
||||
if errreq == nil {
|
||||
errreq = &RequiredNotSetError{f.name + "." + err1.field}
|
||||
if errLater == nil {
|
||||
errLater = &RequiredNotSetError{f.name + "." + err1.field}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err == errRepeatedHasNil {
|
||||
err = errors.New("proto: repeated field " + f.name + " has nil element")
|
||||
}
|
||||
if err == errInvalidUTF8 {
|
||||
if errLater == nil {
|
||||
fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name
|
||||
errLater = &invalidUTF8Error{fullName}
|
||||
}
|
||||
continue
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
@ -284,7 +293,7 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte
|
||||
s := *ptr.offset(u.unrecognized).toBytes()
|
||||
b = append(b, s...)
|
||||
}
|
||||
return b, errreq
|
||||
return b, errLater
|
||||
}
|
||||
|
||||
// computeMarshalInfo initializes the marshal info.
|
||||
@ -530,6 +539,7 @@ func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, ma
|
||||
|
||||
packed := false
|
||||
proto3 := false
|
||||
validateUTF8 := true
|
||||
for i := 2; i < len(tags); i++ {
|
||||
if tags[i] == "packed" {
|
||||
packed = true
|
||||
@ -538,6 +548,7 @@ func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, ma
|
||||
proto3 = true
|
||||
}
|
||||
}
|
||||
validateUTF8 = validateUTF8 && proto3
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
@ -735,6 +746,18 @@ func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, ma
|
||||
}
|
||||
return sizeFloat64Value, appendFloat64Value
|
||||
case reflect.String:
|
||||
if validateUTF8 {
|
||||
if pointer {
|
||||
return sizeStringPtr, appendUTF8StringPtr
|
||||
}
|
||||
if slice {
|
||||
return sizeStringSlice, appendUTF8StringSlice
|
||||
}
|
||||
if nozero {
|
||||
return sizeStringValueNoZero, appendUTF8StringValueNoZero
|
||||
}
|
||||
return sizeStringValue, appendUTF8StringValue
|
||||
}
|
||||
if pointer {
|
||||
return sizeStringPtr, appendStringPtr
|
||||
}
|
||||
@ -1984,9 +2007,6 @@ func appendBoolPackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byt
|
||||
}
|
||||
func appendStringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||
v := *ptr.toString()
|
||||
if !utf8.ValidString(v) {
|
||||
return nil, errInvalidUTF8
|
||||
}
|
||||
b = appendVarint(b, wiretag)
|
||||
b = appendVarint(b, uint64(len(v)))
|
||||
b = append(b, v...)
|
||||
@ -1997,9 +2017,6 @@ func appendStringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]b
|
||||
if v == "" {
|
||||
return b, nil
|
||||
}
|
||||
if !utf8.ValidString(v) {
|
||||
return nil, errInvalidUTF8
|
||||
}
|
||||
b = appendVarint(b, wiretag)
|
||||
b = appendVarint(b, uint64(len(v)))
|
||||
b = append(b, v...)
|
||||
@ -2011,9 +2028,6 @@ func appendStringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, err
|
||||
return b, nil
|
||||
}
|
||||
v := *p
|
||||
if !utf8.ValidString(v) {
|
||||
return nil, errInvalidUTF8
|
||||
}
|
||||
b = appendVarint(b, wiretag)
|
||||
b = appendVarint(b, uint64(len(v)))
|
||||
b = append(b, v...)
|
||||
@ -2022,12 +2036,74 @@ func appendStringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, err
|
||||
func appendStringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||
s := *ptr.toStringSlice()
|
||||
for _, v := range s {
|
||||
b = appendVarint(b, wiretag)
|
||||
b = appendVarint(b, uint64(len(v)))
|
||||
b = append(b, v...)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
func appendUTF8StringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||
var invalidUTF8 bool
|
||||
v := *ptr.toString()
|
||||
if !utf8.ValidString(v) {
|
||||
return nil, errInvalidUTF8
|
||||
invalidUTF8 = true
|
||||
}
|
||||
b = appendVarint(b, wiretag)
|
||||
b = appendVarint(b, uint64(len(v)))
|
||||
b = append(b, v...)
|
||||
if invalidUTF8 {
|
||||
return b, errInvalidUTF8
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
func appendUTF8StringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||
var invalidUTF8 bool
|
||||
v := *ptr.toString()
|
||||
if v == "" {
|
||||
return b, nil
|
||||
}
|
||||
if !utf8.ValidString(v) {
|
||||
invalidUTF8 = true
|
||||
}
|
||||
b = appendVarint(b, wiretag)
|
||||
b = appendVarint(b, uint64(len(v)))
|
||||
b = append(b, v...)
|
||||
if invalidUTF8 {
|
||||
return b, errInvalidUTF8
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
func appendUTF8StringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||
var invalidUTF8 bool
|
||||
p := *ptr.toStringPtr()
|
||||
if p == nil {
|
||||
return b, nil
|
||||
}
|
||||
v := *p
|
||||
if !utf8.ValidString(v) {
|
||||
invalidUTF8 = true
|
||||
}
|
||||
b = appendVarint(b, wiretag)
|
||||
b = appendVarint(b, uint64(len(v)))
|
||||
b = append(b, v...)
|
||||
if invalidUTF8 {
|
||||
return b, errInvalidUTF8
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
func appendUTF8StringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||
var invalidUTF8 bool
|
||||
s := *ptr.toStringSlice()
|
||||
for _, v := range s {
|
||||
if !utf8.ValidString(v) {
|
||||
invalidUTF8 = true
|
||||
}
|
||||
b = appendVarint(b, wiretag)
|
||||
b = appendVarint(b, uint64(len(v)))
|
||||
b = append(b, v...)
|
||||
}
|
||||
if invalidUTF8 {
|
||||
return b, errInvalidUTF8
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
@ -2107,7 +2183,8 @@ func makeGroupSliceMarshaler(u *marshalInfo) (sizer, marshaler) {
|
||||
},
|
||||
func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) {
|
||||
s := ptr.getPointerSlice()
|
||||
var err, errreq error
|
||||
var err error
|
||||
var nerr nonFatal
|
||||
for _, v := range s {
|
||||
if v.isNil() {
|
||||
return b, errRepeatedHasNil
|
||||
@ -2115,22 +2192,14 @@ func makeGroupSliceMarshaler(u *marshalInfo) (sizer, marshaler) {
|
||||
b = appendVarint(b, wiretag) // start group
|
||||
b, err = u.marshal(b, v, deterministic)
|
||||
b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group
|
||||
if err != nil {
|
||||
if _, ok := err.(*RequiredNotSetError); ok {
|
||||
// Required field in submessage is not set.
|
||||
// We record the error but keep going, to give a complete marshaling.
|
||||
if errreq == nil {
|
||||
errreq = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !nerr.Merge(err) {
|
||||
if err == ErrNil {
|
||||
err = errRepeatedHasNil
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, errreq
|
||||
return b, nerr.E
|
||||
}
|
||||
}
|
||||
|
||||
@ -2174,7 +2243,8 @@ func makeMessageSliceMarshaler(u *marshalInfo) (sizer, marshaler) {
|
||||
},
|
||||
func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) {
|
||||
s := ptr.getPointerSlice()
|
||||
var err, errreq error
|
||||
var err error
|
||||
var nerr nonFatal
|
||||
for _, v := range s {
|
||||
if v.isNil() {
|
||||
return b, errRepeatedHasNil
|
||||
@ -2184,22 +2254,14 @@ func makeMessageSliceMarshaler(u *marshalInfo) (sizer, marshaler) {
|
||||
b = appendVarint(b, uint64(siz))
|
||||
b, err = u.marshal(b, v, deterministic)
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*RequiredNotSetError); ok {
|
||||
// Required field in submessage is not set.
|
||||
// We record the error but keep going, to give a complete marshaling.
|
||||
if errreq == nil {
|
||||
errreq = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !nerr.Merge(err) {
|
||||
if err == ErrNil {
|
||||
err = errRepeatedHasNil
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, errreq
|
||||
return b, nerr.E
|
||||
}
|
||||
}
|
||||
|
||||
@ -2223,6 +2285,25 @@ func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) {
|
||||
// value.
|
||||
// Key cannot be pointer-typed.
|
||||
valIsPtr := valType.Kind() == reflect.Ptr
|
||||
|
||||
// If value is a message with nested maps, calling
|
||||
// valSizer in marshal may be quadratic. We should use
|
||||
// cached version in marshal (but not in size).
|
||||
// If value is not message type, we don't have size cache,
|
||||
// but it cannot be nested either. Just use valSizer.
|
||||
valCachedSizer := valSizer
|
||||
if valIsPtr && valType.Elem().Kind() == reflect.Struct {
|
||||
u := getMarshalInfo(valType.Elem())
|
||||
valCachedSizer = func(ptr pointer, tagsize int) int {
|
||||
// Same as message sizer, but use cache.
|
||||
p := ptr.getPointer()
|
||||
if p.isNil() {
|
||||
return 0
|
||||
}
|
||||
siz := u.cachedsize(p)
|
||||
return siz + SizeVarint(uint64(siz)) + tagsize
|
||||
}
|
||||
}
|
||||
return func(ptr pointer, tagsize int) int {
|
||||
m := ptr.asPointerTo(t).Elem() // the map
|
||||
n := 0
|
||||
@ -2243,24 +2324,26 @@ func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) {
|
||||
if len(keys) > 1 && deterministic {
|
||||
sort.Sort(mapKeys(keys))
|
||||
}
|
||||
|
||||
var nerr nonFatal
|
||||
for _, k := range keys {
|
||||
ki := k.Interface()
|
||||
vi := m.MapIndex(k).Interface()
|
||||
kaddr := toAddrPointer(&ki, false) // pointer to key
|
||||
vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value
|
||||
b = appendVarint(b, tag)
|
||||
siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
|
||||
siz := keySizer(kaddr, 1) + valCachedSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
|
||||
b = appendVarint(b, uint64(siz))
|
||||
b, err = keyMarshaler(b, kaddr, keyWireTag, deterministic)
|
||||
if err != nil {
|
||||
if !nerr.Merge(err) {
|
||||
return b, err
|
||||
}
|
||||
b, err = valMarshaler(b, vaddr, valWireTag, deterministic)
|
||||
if err != nil && err != ErrNil { // allow nil value in map
|
||||
if err != ErrNil && !nerr.Merge(err) { // allow nil value in map
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
return b, nerr.E
|
||||
}
|
||||
}
|
||||
|
||||
@ -2333,6 +2416,7 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de
|
||||
defer mu.Unlock()
|
||||
|
||||
var err error
|
||||
var nerr nonFatal
|
||||
|
||||
// Fast-path for common cases: zero or one extensions.
|
||||
// Don't bother sorting the keys.
|
||||
@ -2352,11 +2436,11 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
||||
if err != nil {
|
||||
if !nerr.Merge(err) {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
return b, nerr.E
|
||||
}
|
||||
|
||||
// Sort the keys to provide a deterministic encoding.
|
||||
@ -2383,11 +2467,11 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
||||
if err != nil {
|
||||
if !nerr.Merge(err) {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
return b, nerr.E
|
||||
}
|
||||
|
||||
// message set format is:
|
||||
@ -2444,6 +2528,7 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de
|
||||
defer mu.Unlock()
|
||||
|
||||
var err error
|
||||
var nerr nonFatal
|
||||
|
||||
// Fast-path for common cases: zero or one extensions.
|
||||
// Don't bother sorting the keys.
|
||||
@ -2470,12 +2555,12 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
|
||||
if err != nil {
|
||||
if !nerr.Merge(err) {
|
||||
return b, err
|
||||
}
|
||||
b = append(b, 1<<3|WireEndGroup)
|
||||
}
|
||||
return b, nil
|
||||
return b, nerr.E
|
||||
}
|
||||
|
||||
// Sort the keys to provide a deterministic encoding.
|
||||
@ -2509,11 +2594,11 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
|
||||
b = append(b, 1<<3|WireEndGroup)
|
||||
if err != nil {
|
||||
if !nerr.Merge(err) {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
return b, nerr.E
|
||||
}
|
||||
|
||||
// sizeV1Extensions computes the size of encoded data for a V1-API extension field.
|
||||
@ -2556,6 +2641,7 @@ func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, determ
|
||||
sort.Ints(keys)
|
||||
|
||||
var err error
|
||||
var nerr nonFatal
|
||||
for _, k := range keys {
|
||||
e := m[int32(k)]
|
||||
if e.value == nil || e.desc == nil {
|
||||
@ -2572,11 +2658,11 @@ func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, determ
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
||||
if err != nil {
|
||||
if !nerr.Merge(err) {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
return b, nerr.E
|
||||
}
|
||||
|
||||
// newMarshaler is the interface representing objects that can marshal themselves.
|
||||
|
144
vendor/github.com/golang/protobuf/proto/table_unmarshal.go
generated
vendored
144
vendor/github.com/golang/protobuf/proto/table_unmarshal.go
generated
vendored
@ -97,6 +97,8 @@ type unmarshalFieldInfo struct {
|
||||
|
||||
// if a required field, contains a single set bit at this field's index in the required field list.
|
||||
reqMask uint64
|
||||
|
||||
name string // name of the field, for error reporting
|
||||
}
|
||||
|
||||
var (
|
||||
@ -137,7 +139,7 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error {
|
||||
return UnmarshalMessageSet(b, m.offset(u.extensions).toExtensions())
|
||||
}
|
||||
var reqMask uint64 // bitmask of required fields we've seen.
|
||||
var rnse *RequiredNotSetError // an instance of a RequiredNotSetError returned by a submessage.
|
||||
var errLater error
|
||||
for len(b) > 0 {
|
||||
// Read tag and wire type.
|
||||
// Special case 1 and 2 byte varints.
|
||||
@ -176,11 +178,20 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error {
|
||||
if r, ok := err.(*RequiredNotSetError); ok {
|
||||
// Remember this error, but keep parsing. We need to produce
|
||||
// a full parse even if a required field is missing.
|
||||
rnse = r
|
||||
if errLater == nil {
|
||||
errLater = r
|
||||
}
|
||||
reqMask |= f.reqMask
|
||||
continue
|
||||
}
|
||||
if err != errInternalBadWireType {
|
||||
if err == errInvalidUTF8 {
|
||||
if errLater == nil {
|
||||
fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name
|
||||
errLater = &invalidUTF8Error{fullName}
|
||||
}
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
// Fragments with bad wire type are treated as unknown fields.
|
||||
@ -239,20 +250,16 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error {
|
||||
emap[int32(tag)] = e
|
||||
}
|
||||
}
|
||||
if rnse != nil {
|
||||
// A required field of a submessage/group is missing. Return that error.
|
||||
return rnse
|
||||
}
|
||||
if reqMask != u.reqMask {
|
||||
if reqMask != u.reqMask && errLater == nil {
|
||||
// A required field of this message is missing.
|
||||
for _, n := range u.reqFields {
|
||||
if reqMask&1 == 0 {
|
||||
return &RequiredNotSetError{n}
|
||||
errLater = &RequiredNotSetError{n}
|
||||
}
|
||||
reqMask >>= 1
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return errLater
|
||||
}
|
||||
|
||||
// computeUnmarshalInfo fills in u with information for use
|
||||
@ -351,7 +358,7 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
|
||||
}
|
||||
|
||||
// Store the info in the correct slot in the message.
|
||||
u.setTag(tag, toField(&f), unmarshal, reqMask)
|
||||
u.setTag(tag, toField(&f), unmarshal, reqMask, name)
|
||||
}
|
||||
|
||||
// Find any types associated with oneof fields.
|
||||
@ -366,10 +373,17 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
|
||||
|
||||
f := typ.Field(0) // oneof implementers have one field
|
||||
baseUnmarshal := fieldUnmarshaler(&f)
|
||||
tagstr := strings.Split(f.Tag.Get("protobuf"), ",")[1]
|
||||
tag, err := strconv.Atoi(tagstr)
|
||||
tags := strings.Split(f.Tag.Get("protobuf"), ",")
|
||||
fieldNum, err := strconv.Atoi(tags[1])
|
||||
if err != nil {
|
||||
panic("protobuf tag field not an integer: " + tagstr)
|
||||
panic("protobuf tag field not an integer: " + tags[1])
|
||||
}
|
||||
var name string
|
||||
for _, tag := range tags {
|
||||
if strings.HasPrefix(tag, "name=") {
|
||||
name = strings.TrimPrefix(tag, "name=")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Find the oneof field that this struct implements.
|
||||
@ -380,7 +394,7 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
|
||||
// That lets us know where this struct should be stored
|
||||
// when we encounter it during unmarshaling.
|
||||
unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal)
|
||||
u.setTag(tag, of.field, unmarshal, 0)
|
||||
u.setTag(fieldNum, of.field, unmarshal, 0, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -401,7 +415,7 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
|
||||
// [0 0] is [tag=0/wiretype=varint varint-encoded-0].
|
||||
u.setTag(0, zeroField, func(b []byte, f pointer, w int) ([]byte, error) {
|
||||
return nil, fmt.Errorf("proto: %s: illegal tag 0 (wire type %d)", t, w)
|
||||
}, 0)
|
||||
}, 0, "")
|
||||
|
||||
// Set mask for required field check.
|
||||
u.reqMask = uint64(1)<<uint(len(u.reqFields)) - 1
|
||||
@ -413,8 +427,9 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
|
||||
// tag = tag # for field
|
||||
// field/unmarshal = unmarshal info for that field.
|
||||
// reqMask = if required, bitmask for field position in required field list. 0 otherwise.
|
||||
func (u *unmarshalInfo) setTag(tag int, field field, unmarshal unmarshaler, reqMask uint64) {
|
||||
i := unmarshalFieldInfo{field: field, unmarshal: unmarshal, reqMask: reqMask}
|
||||
// name = short name of the field.
|
||||
func (u *unmarshalInfo) setTag(tag int, field field, unmarshal unmarshaler, reqMask uint64, name string) {
|
||||
i := unmarshalFieldInfo{field: field, unmarshal: unmarshal, reqMask: reqMask, name: name}
|
||||
n := u.typ.NumField()
|
||||
if tag >= 0 && (tag < 16 || tag < 2*n) { // TODO: what are the right numbers here?
|
||||
for len(u.dense) <= tag {
|
||||
@ -442,11 +457,17 @@ func typeUnmarshaler(t reflect.Type, tags string) unmarshaler {
|
||||
tagArray := strings.Split(tags, ",")
|
||||
encoding := tagArray[0]
|
||||
name := "unknown"
|
||||
proto3 := false
|
||||
validateUTF8 := true
|
||||
for _, tag := range tagArray[3:] {
|
||||
if strings.HasPrefix(tag, "name=") {
|
||||
name = tag[5:]
|
||||
}
|
||||
if tag == "proto3" {
|
||||
proto3 = true
|
||||
}
|
||||
}
|
||||
validateUTF8 = validateUTF8 && proto3
|
||||
|
||||
// Figure out packaging (pointer, slice, or both)
|
||||
slice := false
|
||||
@ -594,6 +615,15 @@ func typeUnmarshaler(t reflect.Type, tags string) unmarshaler {
|
||||
}
|
||||
return unmarshalBytesValue
|
||||
case reflect.String:
|
||||
if validateUTF8 {
|
||||
if pointer {
|
||||
return unmarshalUTF8StringPtr
|
||||
}
|
||||
if slice {
|
||||
return unmarshalUTF8StringSlice
|
||||
}
|
||||
return unmarshalUTF8StringValue
|
||||
}
|
||||
if pointer {
|
||||
return unmarshalStringPtr
|
||||
}
|
||||
@ -1448,9 +1478,6 @@ func unmarshalStringValue(b []byte, f pointer, w int) ([]byte, error) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
v := string(b[:x])
|
||||
if !utf8.ValidString(v) {
|
||||
return nil, errInvalidUTF8
|
||||
}
|
||||
*f.toString() = v
|
||||
return b[x:], nil
|
||||
}
|
||||
@ -1468,9 +1495,6 @@ func unmarshalStringPtr(b []byte, f pointer, w int) ([]byte, error) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
v := string(b[:x])
|
||||
if !utf8.ValidString(v) {
|
||||
return nil, errInvalidUTF8
|
||||
}
|
||||
*f.toStringPtr() = &v
|
||||
return b[x:], nil
|
||||
}
|
||||
@ -1488,14 +1512,72 @@ func unmarshalStringSlice(b []byte, f pointer, w int) ([]byte, error) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
v := string(b[:x])
|
||||
if !utf8.ValidString(v) {
|
||||
return nil, errInvalidUTF8
|
||||
}
|
||||
s := f.toStringSlice()
|
||||
*s = append(*s, v)
|
||||
return b[x:], nil
|
||||
}
|
||||
|
||||
func unmarshalUTF8StringValue(b []byte, f pointer, w int) ([]byte, error) {
|
||||
if w != WireBytes {
|
||||
return b, errInternalBadWireType
|
||||
}
|
||||
x, n := decodeVarint(b)
|
||||
if n == 0 {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
b = b[n:]
|
||||
if x > uint64(len(b)) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
v := string(b[:x])
|
||||
*f.toString() = v
|
||||
if !utf8.ValidString(v) {
|
||||
return b[x:], errInvalidUTF8
|
||||
}
|
||||
return b[x:], nil
|
||||
}
|
||||
|
||||
func unmarshalUTF8StringPtr(b []byte, f pointer, w int) ([]byte, error) {
|
||||
if w != WireBytes {
|
||||
return b, errInternalBadWireType
|
||||
}
|
||||
x, n := decodeVarint(b)
|
||||
if n == 0 {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
b = b[n:]
|
||||
if x > uint64(len(b)) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
v := string(b[:x])
|
||||
*f.toStringPtr() = &v
|
||||
if !utf8.ValidString(v) {
|
||||
return b[x:], errInvalidUTF8
|
||||
}
|
||||
return b[x:], nil
|
||||
}
|
||||
|
||||
func unmarshalUTF8StringSlice(b []byte, f pointer, w int) ([]byte, error) {
|
||||
if w != WireBytes {
|
||||
return b, errInternalBadWireType
|
||||
}
|
||||
x, n := decodeVarint(b)
|
||||
if n == 0 {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
b = b[n:]
|
||||
if x > uint64(len(b)) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
v := string(b[:x])
|
||||
s := f.toStringSlice()
|
||||
*s = append(*s, v)
|
||||
if !utf8.ValidString(v) {
|
||||
return b[x:], errInvalidUTF8
|
||||
}
|
||||
return b[x:], nil
|
||||
}
|
||||
|
||||
var emptyBuf [0]byte
|
||||
|
||||
func unmarshalBytesValue(b []byte, f pointer, w int) ([]byte, error) {
|
||||
@ -1674,6 +1756,7 @@ func makeUnmarshalMap(f *reflect.StructField) unmarshaler {
|
||||
// Maps will be somewhat slow. Oh well.
|
||||
|
||||
// Read key and value from data.
|
||||
var nerr nonFatal
|
||||
k := reflect.New(kt)
|
||||
v := reflect.New(vt)
|
||||
for len(b) > 0 {
|
||||
@ -1694,7 +1777,7 @@ func makeUnmarshalMap(f *reflect.StructField) unmarshaler {
|
||||
err = errInternalBadWireType // skip unknown tag
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if nerr.Merge(err) {
|
||||
continue
|
||||
}
|
||||
if err != errInternalBadWireType {
|
||||
@ -1717,7 +1800,7 @@ func makeUnmarshalMap(f *reflect.StructField) unmarshaler {
|
||||
// Insert into map.
|
||||
m.SetMapIndex(k.Elem(), v.Elem())
|
||||
|
||||
return r, nil
|
||||
return r, nerr.E
|
||||
}
|
||||
}
|
||||
|
||||
@ -1743,15 +1826,16 @@ func makeUnmarshalOneof(typ, ityp reflect.Type, unmarshal unmarshaler) unmarshal
|
||||
// Unmarshal data into holder.
|
||||
// We unmarshal into the first field of the holder object.
|
||||
var err error
|
||||
var nerr nonFatal
|
||||
b, err = unmarshal(b, valToPointer(v).offset(field0), w)
|
||||
if err != nil {
|
||||
if !nerr.Merge(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Write pointer to holder into target field.
|
||||
f.asPointerTo(ityp).Elem().Set(v)
|
||||
|
||||
return b, nil
|
||||
return b, nerr.E
|
||||
}
|
||||
}
|
||||
|
||||
|
4
vendor/github.com/golang/protobuf/proto/text.go
generated
vendored
4
vendor/github.com/golang/protobuf/proto/text.go
generated
vendored
@ -353,7 +353,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
|
||||
if err := tm.writeAny(w, key, props.MapKeyProp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
@ -370,7 +370,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tm.writeAny(w, val, props.mvalprop); err != nil {
|
||||
if err := tm.writeAny(w, val, props.MapValProp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
|
6
vendor/github.com/golang/protobuf/proto/text_parser.go
generated
vendored
6
vendor/github.com/golang/protobuf/proto/text_parser.go
generated
vendored
@ -630,17 +630,17 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
||||
if err := p.consumeToken(":"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.readAny(key, props.mkeyprop); err != nil {
|
||||
if err := p.readAny(key, props.MapKeyProp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeOptionalSeparator(); err != nil {
|
||||
return err
|
||||
}
|
||||
case "value":
|
||||
if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
|
||||
if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.readAny(val, props.mvalprop); err != nil {
|
||||
if err := p.readAny(val, props.MapValProp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.consumeOptionalSeparator(); err != nil {
|
||||
|
15
vendor/github.com/google/go-github/AUTHORS
generated
vendored
15
vendor/github.com/google/go-github/AUTHORS
generated
vendored
@ -13,6 +13,7 @@ Abhinav Gupta <mail@abhinavg.net>
|
||||
Ahmed Hagy <a.akram93@gmail.com>
|
||||
Ainsley Chong <ainsley.chong@gmail.com>
|
||||
Akeda Bagus <akeda@x-team.com>
|
||||
Akhil Mohan <akhilerm@gmail.com>
|
||||
Alec Thomas <alec@swapoff.org>
|
||||
Aleks Clark <aleks.clark@gmail.com>
|
||||
Alex Bramley <a.bramley@gmail.com>
|
||||
@ -38,6 +39,7 @@ Björn Häuser <b.haeuser@rebuy.de>
|
||||
Brad Harris <bmharris@gmail.com>
|
||||
Brad Moylan <moylan.brad@gmail.com>
|
||||
Bradley Falzon <brad@teambrad.net>
|
||||
Brandon Cook <phylake@gmail.com>
|
||||
Brian Egizi <brian@mojotech.com>
|
||||
Bryan Boreham <bryan@weave.works>
|
||||
Cami Diez <diezcami@gmail.com>
|
||||
@ -70,6 +72,7 @@ Drew Fradette <drew.fradette@gmail.com>
|
||||
Eli Uriegas <seemethere101@gmail.com>
|
||||
Elliott Beach <elliott2.71828@gmail.com>
|
||||
Emerson Wood <emersonwood94@gmail.com>
|
||||
eperm <staffordworrell@gmail.com>
|
||||
erwinvaneyk <erwinvaneyk@gmail.com>
|
||||
Fabrice <fabrice.vaillant@student.ecp.fr>
|
||||
Filippo Valsorda <hi@filippo.io>
|
||||
@ -78,6 +81,7 @@ Francesc Gil <xescugil@gmail.com>
|
||||
Francis <hello@francismakes.com>
|
||||
Fredrik Jönsson <fredrik.jonsson@izettle.com>
|
||||
Garrett Squire <garrettsquire@gmail.com>
|
||||
George Kontridze <george.kontridze@gmail.com>
|
||||
Georgy Buranov <gburanov@gmail.com>
|
||||
Gnahz <p@oath.pl>
|
||||
Google Inc.
|
||||
@ -95,15 +99,19 @@ Isao Jonas <isao.jonas@gmail.com>
|
||||
isqua <isqua@isqua.ru>
|
||||
Jameel Haffejee <RC1140@republiccommandos.co.za>
|
||||
Jan Kosecki <jan.kosecki91@gmail.com>
|
||||
Javier Campanini <jcampanini@palantir.com>
|
||||
Jeremy Morris <jeremylevanmorris@gmail.com>
|
||||
Jesse Newland <jesse@jnewland.com>
|
||||
Jihoon Chung <j.c@navercorp.com>
|
||||
Jimmi Dyson <jimmidyson@gmail.com>
|
||||
Joan Saum <joan.saum@epitech.eu>
|
||||
Joe Tsai <joetsai@digital-static.net>
|
||||
John Barton <jrbarton@gmail.com>
|
||||
John Engelman <john.r.engelman@gmail.com>
|
||||
JP Phillips <jonphill9@gmail.com>
|
||||
jpbelanger-mtl <jp.belanger@gmail.com>
|
||||
Juan Basso <jrbasso@gmail.com>
|
||||
Julien Garcia Gonzalez <garciagonzalez.julien@gmail.com>
|
||||
Julien Rostand <jrostand@users.noreply.github.com>
|
||||
Justin Abrahms <justin@abrah.ms>
|
||||
Jusung Lee <e.jusunglee@gmail.com>
|
||||
@ -123,9 +131,11 @@ Luke Roberts <email@luke-roberts.co.uk>
|
||||
Luke Young <luke@hydrantlabs.org>
|
||||
Maksim Zhylinski <uzzable@gmail.com>
|
||||
Martin-Louis Bright <mlbright@gmail.com>
|
||||
Marwan Sulaiman <marwan.sameer@gmail.com>
|
||||
Mat Geist <matgeist@gmail.com>
|
||||
Matt <alpmatthew@gmail.com>
|
||||
Matt Brender <mjbrender@gmail.com>
|
||||
Matt Gaunt <matt@gauntface.co.uk>
|
||||
Matt Landis <landis.matt@gmail.com>
|
||||
Maxime Bury <maxime.bury@gmail.com>
|
||||
Michael Spiegel <michael.m.spiegel@gmail.com>
|
||||
@ -145,17 +155,20 @@ Parham Alvani <parham.alvani@gmail.com>
|
||||
Parker Moore <parkrmoore@gmail.com>
|
||||
parkhyukjun89 <park.hyukjun89@gmail.com>
|
||||
Pavel Shtanko <pavel.shtanko@gmail.com>
|
||||
Pete Wagner <thepwagner@github.com>
|
||||
Petr Shevtsov <petr.shevtsov@gmail.com>
|
||||
Pierre Carrier <pierre@meteor.com>
|
||||
Piotr Zurek <p.zurek@gmail.com>
|
||||
Quinn Slack <qslack@qslack.com>
|
||||
Rackspace US, Inc.
|
||||
Radek Simko <radek.simko@gmail.com>
|
||||
Radliński Ignacy <radlinsk@student.agh.edu.pl>
|
||||
Rajendra arora <rajendraarora16@yahoo.com>
|
||||
RaviTeja Pothana <ravi-teja@live.com>
|
||||
rc1140 <jameel@republiccommandos.co.za>
|
||||
Red Hat, Inc.
|
||||
Rob Figueiredo <robfig@yext.com>
|
||||
Rohit Upadhyay <urohit011@gmail.com>
|
||||
Ronak Jain <ronakjain@outlook.in>
|
||||
Ruben Vereecken <rubenvereecken@gmail.com>
|
||||
Ryan Lower <rpjlower@gmail.com>
|
||||
@ -176,6 +189,7 @@ Shawn Smith <shawnpsmith@gmail.com>
|
||||
sona-tar <sona.zip@gmail.com>
|
||||
SoundCloud, Ltd.
|
||||
Stian Eikeland <stian@eikeland.se>
|
||||
Tasya Aditya Rukmana <tadityar@gmail.com>
|
||||
Thomas Bruyelle <thomas.bruyelle@gmail.com>
|
||||
Timothée Peignier <timothee.peignier@tryphon.org>
|
||||
Trey Tacon <ttacon@gmail.com>
|
||||
@ -186,6 +200,7 @@ Victor Vrantchan <vrancean+github@gmail.com>
|
||||
Vlad Ungureanu <vladu@palantir.com>
|
||||
Will Maier <wcmaier@gmail.com>
|
||||
William Bailey <mail@williambailey.org.uk>
|
||||
xibz <impactbchang@gmail.com>
|
||||
Yann Malet <yann.malet@gmail.com>
|
||||
Yannick Utard <yannickutard@gmail.com>
|
||||
Yicheng Qin <qycqycqycqycqyc@gmail.com>
|
||||
|
2
vendor/github.com/google/go-github/github/activity_events.go
generated
vendored
2
vendor/github.com/google/go-github/github/activity_events.go
generated
vendored
@ -96,6 +96,8 @@ func (e *Event) ParsePayload() (payload interface{}, err error) {
|
||||
payload = &ReleaseEvent{}
|
||||
case "RepositoryEvent":
|
||||
payload = &RepositoryEvent{}
|
||||
case "RepositoryVulnerabilityAlertEvent":
|
||||
payload = &RepositoryVulnerabilityAlertEvent{}
|
||||
case "StatusEvent":
|
||||
payload = &StatusEvent{}
|
||||
case "TeamEvent":
|
||||
|
6
vendor/github.com/google/go-github/github/activity_star.go
generated
vendored
6
vendor/github.com/google/go-github/github/activity_star.go
generated
vendored
@ -8,6 +8,7 @@ package github
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StarredRepository is returned by ListStarred.
|
||||
@ -84,8 +85,9 @@ func (s *ActivityService) ListStarred(ctx context.Context, user string, opt *Act
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// TODO: remove custom Accept header when this API fully launches
|
||||
req.Header.Set("Accept", mediaTypeStarringPreview)
|
||||
// TODO: remove custom Accept header when APIs fully launch
|
||||
acceptHeaders := []string{mediaTypeStarringPreview, mediaTypeTopicsPreview}
|
||||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
|
||||
|
||||
var repos []*StarredRepository
|
||||
resp, err := s.client.Do(ctx, req, &repos)
|
||||
|
2
vendor/github.com/google/go-github/github/apps.go
generated
vendored
2
vendor/github.com/google/go-github/github/apps.go
generated
vendored
@ -164,7 +164,7 @@ func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOption
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/apps/#create-a-new-installation-token
|
||||
func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64) (*InstallationToken, *Response, error) {
|
||||
u := fmt.Sprintf("installations/%v/access_tokens", id)
|
||||
u := fmt.Sprintf("app/installations/%v/access_tokens", id)
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, nil)
|
||||
if err != nil {
|
||||
|
23
vendor/github.com/google/go-github/github/checks.go
generated
vendored
23
vendor/github.com/google/go-github/github/checks.go
generated
vendored
@ -19,6 +19,7 @@ type ChecksService service
|
||||
// CheckRun represents a GitHub check run on a repository associated with a GitHub app.
|
||||
type CheckRun struct {
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
NodeID *string `json:"node_id,omitempty"`
|
||||
HeadSHA *string `json:"head_sha,omitempty"`
|
||||
ExternalID *string `json:"external_id,omitempty"`
|
||||
URL *string `json:"url,omitempty"`
|
||||
@ -47,11 +48,11 @@ type CheckRunOutput struct {
|
||||
|
||||
// CheckRunAnnotation represents an annotation object for a CheckRun output.
|
||||
type CheckRunAnnotation struct {
|
||||
FileName *string `json:"filename,omitempty"`
|
||||
Path *string `json:"path,omitempty"`
|
||||
BlobHRef *string `json:"blob_href,omitempty"`
|
||||
StartLine *int `json:"start_line,omitempty"`
|
||||
EndLine *int `json:"end_line,omitempty"`
|
||||
WarningLevel *string `json:"warning_level,omitempty"`
|
||||
AnnotationLevel *string `json:"annotation_level,omitempty"`
|
||||
Message *string `json:"message,omitempty"`
|
||||
Title *string `json:"title,omitempty"`
|
||||
RawDetails *string `json:"raw_details,omitempty"`
|
||||
@ -67,6 +68,7 @@ type CheckRunImage struct {
|
||||
// CheckSuite represents a suite of check runs.
|
||||
type CheckSuite struct {
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
NodeID *string `json:"node_id,omitempty"`
|
||||
HeadBranch *string `json:"head_branch,omitempty"`
|
||||
HeadSHA *string `json:"head_sha,omitempty"`
|
||||
URL *string `json:"url,omitempty"`
|
||||
@ -401,20 +403,11 @@ func (s *ChecksService) CreateCheckSuite(ctx context.Context, owner, repo string
|
||||
return checkSuite, resp, nil
|
||||
}
|
||||
|
||||
// RequestCheckSuiteOptions sets up the parameters for a request check suite endpoint.
|
||||
type RequestCheckSuiteOptions struct {
|
||||
HeadSHA string `json:"head_sha"` // The sha of the head commit. (Required.)
|
||||
}
|
||||
|
||||
// RequestCheckSuite triggers GitHub to create a new check suite, without pushing new code to a repository.
|
||||
// ReRequestCheckSuite triggers GitHub to rerequest an existing check suite, without pushing new code to a repository.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/checks/suites/#request-check-suites
|
||||
func (s *ChecksService) RequestCheckSuite(ctx context.Context, owner, repo string, opt RequestCheckSuiteOptions) (*Response, error) {
|
||||
u := fmt.Sprintf("repos/%v/%v/check-suite-requests", owner, repo)
|
||||
u, err := addOptions(u, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// GitHub API docs: https://developer.github.com/v3/checks/suites/#rerequest-check-suite
|
||||
func (s *ChecksService) ReRequestCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*Response, error) {
|
||||
u := fmt.Sprintf("repos/%v/%v/check-suites/%v/rerequest", owner, repo, checkSuiteID)
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, nil)
|
||||
if err != nil {
|
||||
|
26
vendor/github.com/google/go-github/github/event_types.go
generated
vendored
26
vendor/github.com/google/go-github/github/event_types.go
generated
vendored
@ -519,6 +519,7 @@ type PullRequestEvent struct {
|
||||
// the pull request was closed with unmerged commits. If the action is "closed"
|
||||
// and the merged key is true, the pull request was merged.
|
||||
Action *string `json:"action,omitempty"`
|
||||
Assignee *User `json:"assignee,omitempty"`
|
||||
Number *int `json:"number,omitempty"`
|
||||
PullRequest *PullRequest `json:"pull_request,omitempty"`
|
||||
|
||||
@ -532,6 +533,10 @@ type PullRequestEvent struct {
|
||||
Sender *User `json:"sender,omitempty"`
|
||||
Installation *Installation `json:"installation,omitempty"`
|
||||
Label *Label `json:"label,omitempty"` // Populated in "labeled" event deliveries.
|
||||
|
||||
// The following field is only present when the webhook is triggered on
|
||||
// a repository belonging to an organization.
|
||||
Organization *Organization `json:"organization,omitempty"`
|
||||
}
|
||||
|
||||
// PullRequestReviewEvent is triggered when a review is submitted on a pull
|
||||
@ -705,6 +710,27 @@ type RepositoryEvent struct {
|
||||
Installation *Installation `json:"installation,omitempty"`
|
||||
}
|
||||
|
||||
// RepositoryVulnerabilityAlertEvent is triggered when a security alert is created, dismissed, or resolved.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryvulnerabilityalertevent
|
||||
type RepositoryVulnerabilityAlertEvent struct {
|
||||
// Action is the action that was performed. This can be: "create", "dismiss", "resolve".
|
||||
Action *string `json:"action,omitempty"`
|
||||
|
||||
//The security alert of the vulnerable dependency.
|
||||
Alert *struct {
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
AffectedRange *string `json:"affected_range,omitempty"`
|
||||
AffectedPackageName *string `json:"affected_package_name,omitempty"`
|
||||
ExternalReference *string `json:"external_reference,omitempty"`
|
||||
ExternalIdentifier *string `json:"external_identifier,omitempty"`
|
||||
FixedIn *string `json:"fixed_in,omitempty"`
|
||||
Dismisser *User `json:"dismisser,omitempty"`
|
||||
DismissReason *string `json:"dismiss_reason,omitempty"`
|
||||
DismissedAt *Timestamp `json:"dismissed_at,omitempty"`
|
||||
} `json:"alert,omitempty"`
|
||||
}
|
||||
|
||||
// StatusEvent is triggered when the status of a Git commit changes.
|
||||
// The Webhook event name is "status".
|
||||
//
|
||||
|
2
vendor/github.com/google/go-github/github/git_commits.go
generated
vendored
2
vendor/github.com/google/go-github/github/git_commits.go
generated
vendored
@ -58,7 +58,7 @@ func (c CommitAuthor) String() string {
|
||||
return Stringify(c)
|
||||
}
|
||||
|
||||
// GetCommit fetchs the Commit object for a given SHA.
|
||||
// GetCommit fetches the Commit object for a given SHA.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/git/commits/#get-a-commit
|
||||
func (s *GitService) GetCommit(ctx context.Context, owner string, repo string, sha string) (*Commit, *Response, error) {
|
||||
|
2
vendor/github.com/google/go-github/github/git_tags.go
generated
vendored
2
vendor/github.com/google/go-github/github/git_tags.go
generated
vendored
@ -33,7 +33,7 @@ type createTagRequest struct {
|
||||
Tagger *CommitAuthor `json:"tagger,omitempty"`
|
||||
}
|
||||
|
||||
// GetTag fetchs a tag from a repo given a SHA.
|
||||
// GetTag fetches a tag from a repo given a SHA.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/git/tags/#get-a-tag
|
||||
func (s *GitService) GetTag(ctx context.Context, owner string, repo string, sha string) (*Tag, *Response, error) {
|
||||
|
216
vendor/github.com/google/go-github/github/github-accessors.go
generated
vendored
216
vendor/github.com/google/go-github/github/github-accessors.go
generated
vendored
@ -532,6 +532,14 @@ func (c *CheckRun) GetName() string {
|
||||
return *c.Name
|
||||
}
|
||||
|
||||
// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise.
|
||||
func (c *CheckRun) GetNodeID() string {
|
||||
if c == nil || c.NodeID == nil {
|
||||
return ""
|
||||
}
|
||||
return *c.NodeID
|
||||
}
|
||||
|
||||
// GetOutput returns the Output field.
|
||||
func (c *CheckRun) GetOutput() *CheckRunOutput {
|
||||
if c == nil {
|
||||
@ -564,6 +572,14 @@ func (c *CheckRun) GetURL() string {
|
||||
return *c.URL
|
||||
}
|
||||
|
||||
// GetAnnotationLevel returns the AnnotationLevel field if it's non-nil, zero value otherwise.
|
||||
func (c *CheckRunAnnotation) GetAnnotationLevel() string {
|
||||
if c == nil || c.AnnotationLevel == nil {
|
||||
return ""
|
||||
}
|
||||
return *c.AnnotationLevel
|
||||
}
|
||||
|
||||
// GetBlobHRef returns the BlobHRef field if it's non-nil, zero value otherwise.
|
||||
func (c *CheckRunAnnotation) GetBlobHRef() string {
|
||||
if c == nil || c.BlobHRef == nil {
|
||||
@ -580,14 +596,6 @@ func (c *CheckRunAnnotation) GetEndLine() int {
|
||||
return *c.EndLine
|
||||
}
|
||||
|
||||
// GetFileName returns the FileName field if it's non-nil, zero value otherwise.
|
||||
func (c *CheckRunAnnotation) GetFileName() string {
|
||||
if c == nil || c.FileName == nil {
|
||||
return ""
|
||||
}
|
||||
return *c.FileName
|
||||
}
|
||||
|
||||
// GetMessage returns the Message field if it's non-nil, zero value otherwise.
|
||||
func (c *CheckRunAnnotation) GetMessage() string {
|
||||
if c == nil || c.Message == nil {
|
||||
@ -596,6 +604,14 @@ func (c *CheckRunAnnotation) GetMessage() string {
|
||||
return *c.Message
|
||||
}
|
||||
|
||||
// GetPath returns the Path field if it's non-nil, zero value otherwise.
|
||||
func (c *CheckRunAnnotation) GetPath() string {
|
||||
if c == nil || c.Path == nil {
|
||||
return ""
|
||||
}
|
||||
return *c.Path
|
||||
}
|
||||
|
||||
// GetRawDetails returns the RawDetails field if it's non-nil, zero value otherwise.
|
||||
func (c *CheckRunAnnotation) GetRawDetails() string {
|
||||
if c == nil || c.RawDetails == nil {
|
||||
@ -620,14 +636,6 @@ func (c *CheckRunAnnotation) GetTitle() string {
|
||||
return *c.Title
|
||||
}
|
||||
|
||||
// GetWarningLevel returns the WarningLevel field if it's non-nil, zero value otherwise.
|
||||
func (c *CheckRunAnnotation) GetWarningLevel() string {
|
||||
if c == nil || c.WarningLevel == nil {
|
||||
return ""
|
||||
}
|
||||
return *c.WarningLevel
|
||||
}
|
||||
|
||||
// GetAction returns the Action field if it's non-nil, zero value otherwise.
|
||||
func (c *CheckRunEvent) GetAction() string {
|
||||
if c == nil || c.Action == nil {
|
||||
@ -796,6 +804,14 @@ func (c *CheckSuite) GetID() int64 {
|
||||
return *c.ID
|
||||
}
|
||||
|
||||
// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise.
|
||||
func (c *CheckSuite) GetNodeID() string {
|
||||
if c == nil || c.NodeID == nil {
|
||||
return ""
|
||||
}
|
||||
return *c.NodeID
|
||||
}
|
||||
|
||||
// GetRepository returns the Repository field.
|
||||
func (c *CheckSuite) GetRepository() *Repository {
|
||||
if c == nil {
|
||||
@ -2380,6 +2396,14 @@ func (d *DiscussionComment) GetNumber() int {
|
||||
return *d.Number
|
||||
}
|
||||
|
||||
// GetReactions returns the Reactions field.
|
||||
func (d *DiscussionComment) GetReactions() *Reactions {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
return d.Reactions
|
||||
}
|
||||
|
||||
// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
|
||||
func (d *DiscussionComment) GetUpdatedAt() Timestamp {
|
||||
if d == nil || d.UpdatedAt == nil {
|
||||
@ -3116,14 +3140,6 @@ func (h *Hook) GetID() int64 {
|
||||
return *h.ID
|
||||
}
|
||||
|
||||
// GetName returns the Name field if it's non-nil, zero value otherwise.
|
||||
func (h *Hook) GetName() string {
|
||||
if h == nil || h.Name == nil {
|
||||
return ""
|
||||
}
|
||||
return *h.Name
|
||||
}
|
||||
|
||||
// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
|
||||
func (h *Hook) GetUpdatedAt() time.Time {
|
||||
if h == nil || h.UpdatedAt == nil {
|
||||
@ -3860,6 +3876,14 @@ func (i *IssueComment) GetIssueURL() string {
|
||||
return *i.IssueURL
|
||||
}
|
||||
|
||||
// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise.
|
||||
func (i *IssueComment) GetNodeID() string {
|
||||
if i == nil || i.NodeID == nil {
|
||||
return ""
|
||||
}
|
||||
return *i.NodeID
|
||||
}
|
||||
|
||||
// GetReactions returns the Reactions field.
|
||||
func (i *IssueComment) GetReactions() *Reactions {
|
||||
if i == nil {
|
||||
@ -5524,6 +5548,22 @@ func (o *Organization) GetCreatedAt() time.Time {
|
||||
return *o.CreatedAt
|
||||
}
|
||||
|
||||
// GetDefaultRepoPermission returns the DefaultRepoPermission field if it's non-nil, zero value otherwise.
|
||||
func (o *Organization) GetDefaultRepoPermission() string {
|
||||
if o == nil || o.DefaultRepoPermission == nil {
|
||||
return ""
|
||||
}
|
||||
return *o.DefaultRepoPermission
|
||||
}
|
||||
|
||||
// GetDefaultRepoSettings returns the DefaultRepoSettings field if it's non-nil, zero value otherwise.
|
||||
func (o *Organization) GetDefaultRepoSettings() string {
|
||||
if o == nil || o.DefaultRepoSettings == nil {
|
||||
return ""
|
||||
}
|
||||
return *o.DefaultRepoSettings
|
||||
}
|
||||
|
||||
// GetDescription returns the Description field if it's non-nil, zero value otherwise.
|
||||
func (o *Organization) GetDescription() string {
|
||||
if o == nil || o.Description == nil {
|
||||
@ -5620,6 +5660,14 @@ func (o *Organization) GetLogin() string {
|
||||
return *o.Login
|
||||
}
|
||||
|
||||
// GetMembersCanCreateRepos returns the MembersCanCreateRepos field if it's non-nil, zero value otherwise.
|
||||
func (o *Organization) GetMembersCanCreateRepos() bool {
|
||||
if o == nil || o.MembersCanCreateRepos == nil {
|
||||
return false
|
||||
}
|
||||
return *o.MembersCanCreateRepos
|
||||
}
|
||||
|
||||
// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise.
|
||||
func (o *Organization) GetMembersURL() string {
|
||||
if o == nil || o.MembersURL == nil {
|
||||
@ -5708,6 +5756,14 @@ func (o *Organization) GetTotalPrivateRepos() int {
|
||||
return *o.TotalPrivateRepos
|
||||
}
|
||||
|
||||
// GetTwoFactorRequirementEnabled returns the TwoFactorRequirementEnabled field if it's non-nil, zero value otherwise.
|
||||
func (o *Organization) GetTwoFactorRequirementEnabled() bool {
|
||||
if o == nil || o.TwoFactorRequirementEnabled == nil {
|
||||
return false
|
||||
}
|
||||
return *o.TwoFactorRequirementEnabled
|
||||
}
|
||||
|
||||
// GetType returns the Type field if it's non-nil, zero value otherwise.
|
||||
func (o *Organization) GetType() string {
|
||||
if o == nil || o.Type == nil {
|
||||
@ -6156,6 +6212,78 @@ func (p *PreReceiveHook) GetName() string {
|
||||
return *p.Name
|
||||
}
|
||||
|
||||
// GetHRef returns the HRef field if it's non-nil, zero value otherwise.
|
||||
func (p *PRLink) GetHRef() string {
|
||||
if p == nil || p.HRef == nil {
|
||||
return ""
|
||||
}
|
||||
return *p.HRef
|
||||
}
|
||||
|
||||
// GetComments returns the Comments field.
|
||||
func (p *PRLinks) GetComments() *PRLink {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.Comments
|
||||
}
|
||||
|
||||
// GetCommits returns the Commits field.
|
||||
func (p *PRLinks) GetCommits() *PRLink {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.Commits
|
||||
}
|
||||
|
||||
// GetHTML returns the HTML field.
|
||||
func (p *PRLinks) GetHTML() *PRLink {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.HTML
|
||||
}
|
||||
|
||||
// GetIssue returns the Issue field.
|
||||
func (p *PRLinks) GetIssue() *PRLink {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.Issue
|
||||
}
|
||||
|
||||
// GetReviewComment returns the ReviewComment field.
|
||||
func (p *PRLinks) GetReviewComment() *PRLink {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.ReviewComment
|
||||
}
|
||||
|
||||
// GetReviewComments returns the ReviewComments field.
|
||||
func (p *PRLinks) GetReviewComments() *PRLink {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.ReviewComments
|
||||
}
|
||||
|
||||
// GetSelf returns the Self field.
|
||||
func (p *PRLinks) GetSelf() *PRLink {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.Self
|
||||
}
|
||||
|
||||
// GetStatuses returns the Statuses field.
|
||||
func (p *PRLinks) GetStatuses() *PRLink {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.Statuses
|
||||
}
|
||||
|
||||
// GetBody returns the Body field if it's non-nil, zero value otherwise.
|
||||
func (p *Project) GetBody() string {
|
||||
if p == nil || p.Body == nil {
|
||||
@ -6884,6 +7012,14 @@ func (p *PullRequest) GetIssueURL() string {
|
||||
return *p.IssueURL
|
||||
}
|
||||
|
||||
// GetLinks returns the Links field.
|
||||
func (p *PullRequest) GetLinks() *PRLinks {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.Links
|
||||
}
|
||||
|
||||
// GetMaintainerCanModify returns the MaintainerCanModify field if it's non-nil, zero value otherwise.
|
||||
func (p *PullRequest) GetMaintainerCanModify() bool {
|
||||
if p == nil || p.MaintainerCanModify == nil {
|
||||
@ -7228,6 +7364,14 @@ func (p *PullRequestEvent) GetAction() string {
|
||||
return *p.Action
|
||||
}
|
||||
|
||||
// GetAssignee returns the Assignee field.
|
||||
func (p *PullRequestEvent) GetAssignee() *User {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.Assignee
|
||||
}
|
||||
|
||||
// GetChanges returns the Changes field.
|
||||
func (p *PullRequestEvent) GetChanges() *EditChange {
|
||||
if p == nil {
|
||||
@ -7260,6 +7404,14 @@ func (p *PullRequestEvent) GetNumber() int {
|
||||
return *p.Number
|
||||
}
|
||||
|
||||
// GetOrganization returns the Organization field.
|
||||
func (p *PullRequestEvent) GetOrganization() *Organization {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.Organization
|
||||
}
|
||||
|
||||
// GetPullRequest returns the PullRequest field.
|
||||
func (p *PullRequestEvent) GetPullRequest() *PullRequest {
|
||||
if p == nil {
|
||||
@ -9804,6 +9956,14 @@ func (r *RepositoryTag) GetZipballURL() string {
|
||||
return *r.ZipballURL
|
||||
}
|
||||
|
||||
// GetAction returns the Action field if it's non-nil, zero value otherwise.
|
||||
func (r *RepositoryVulnerabilityAlertEvent) GetAction() string {
|
||||
if r == nil || r.Action == nil {
|
||||
return ""
|
||||
}
|
||||
return *r.Action
|
||||
}
|
||||
|
||||
// GetForkRepos returns the ForkRepos field if it's non-nil, zero value otherwise.
|
||||
func (r *RepoStats) GetForkRepos() int {
|
||||
if r == nil || r.ForkRepos == nil {
|
||||
@ -10564,6 +10724,14 @@ func (t *TeamDiscussion) GetPrivate() bool {
|
||||
return *t.Private
|
||||
}
|
||||
|
||||
// GetReactions returns the Reactions field.
|
||||
func (t *TeamDiscussion) GetReactions() *Reactions {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
return t.Reactions
|
||||
}
|
||||
|
||||
// GetTeamURL returns the TeamURL field if it's non-nil, zero value otherwise.
|
||||
func (t *TeamDiscussion) GetTeamURL() string {
|
||||
if t == nil || t.TeamURL == nil {
|
||||
|
31
vendor/github.com/google/go-github/github/github.go
generated
vendored
31
vendor/github.com/google/go-github/github/github.go
generated
vendored
@ -375,7 +375,9 @@ type Response struct {
|
||||
FirstPage int
|
||||
LastPage int
|
||||
|
||||
Rate
|
||||
// Explicitly specify the Rate type so Rate's String() receiver doesn't
|
||||
// propagate to Response.
|
||||
Rate Rate
|
||||
}
|
||||
|
||||
// newResponse creates a new Response for the provided http.Response.
|
||||
@ -500,13 +502,23 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
|
||||
|
||||
err = CheckResponse(resp)
|
||||
if err != nil {
|
||||
// Even though there was an error, we still return the response
|
||||
// in case the caller wants to inspect it further.
|
||||
// However, if the error is AcceptedError, decode it below before
|
||||
// returning from this function and closing the response body.
|
||||
if _, ok := err.(*AcceptedError); !ok {
|
||||
return response, err
|
||||
// Special case for AcceptedErrors. If an AcceptedError
|
||||
// has been encountered, the response's payload will be
|
||||
// added to the AcceptedError and returned.
|
||||
//
|
||||
// Issue #1022
|
||||
aerr, ok := err.(*AcceptedError)
|
||||
if ok {
|
||||
b, readErr := ioutil.ReadAll(resp.Body)
|
||||
if readErr != nil {
|
||||
return response, readErr
|
||||
}
|
||||
|
||||
aerr.Raw = b
|
||||
return response, aerr
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
if v != nil {
|
||||
@ -608,7 +620,10 @@ func (r *RateLimitError) Error() string {
|
||||
// Technically, 202 Accepted is not a real error, it's just used to
|
||||
// indicate that results are not ready yet, but should be available soon.
|
||||
// The request can be repeated after some time.
|
||||
type AcceptedError struct{}
|
||||
type AcceptedError struct {
|
||||
// Raw contains the response body.
|
||||
Raw []byte
|
||||
}
|
||||
|
||||
func (*AcceptedError) Error() string {
|
||||
return "job scheduled on GitHub side; try again later"
|
||||
|
2
vendor/github.com/google/go-github/github/issues.go
generated
vendored
2
vendor/github.com/google/go-github/github/issues.go
generated
vendored
@ -228,7 +228,7 @@ func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo strin
|
||||
}
|
||||
|
||||
// TODO: remove custom Accept headers when APIs fully launch.
|
||||
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
|
||||
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeIntegrationPreview}
|
||||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
|
||||
|
||||
var issues []*Issue
|
||||
|
1
vendor/github.com/google/go-github/github/issues_comments.go
generated
vendored
1
vendor/github.com/google/go-github/github/issues_comments.go
generated
vendored
@ -14,6 +14,7 @@ import (
|
||||
// IssueComment represents a comment left on an issue.
|
||||
type IssueComment struct {
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
NodeID *string `json:"node_id,omitempty"`
|
||||
Body *string `json:"body,omitempty"`
|
||||
User *User `json:"user,omitempty"`
|
||||
Reactions *Reactions `json:"reactions,omitempty"`
|
||||
|
7
vendor/github.com/google/go-github/github/messages.go
generated
vendored
7
vendor/github.com/google/go-github/github/messages.go
generated
vendored
@ -72,6 +72,7 @@ var (
|
||||
"pull_request": "PullRequestEvent",
|
||||
"push": "PushEvent",
|
||||
"repository": "RepositoryEvent",
|
||||
"repository_vulnerability_alert": "RepositoryVulnerabilityAlertEvent",
|
||||
"release": "ReleaseEvent",
|
||||
"status": "StatusEvent",
|
||||
"team": "TeamEvent",
|
||||
@ -175,19 +176,19 @@ func ValidatePayload(r *http.Request, secretKey []byte) (payload []byte, err err
|
||||
}
|
||||
|
||||
sig := r.Header.Get(signatureHeader)
|
||||
if err := validateSignature(sig, body, secretKey); err != nil {
|
||||
if err := ValidateSignature(sig, body, secretKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// validateSignature validates the signature for the given payload.
|
||||
// ValidateSignature validates the signature for the given payload.
|
||||
// signature is the GitHub hash signature delivered in the X-Hub-Signature header.
|
||||
// payload is the JSON payload sent by GitHub Webhooks.
|
||||
// secretKey is the GitHub Webhook secret message.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github
|
||||
func validateSignature(signature string, payload, secretKey []byte) error {
|
||||
func ValidateSignature(signature string, payload, secretKey []byte) error {
|
||||
messageMAC, hashFunc, err := messageMAC(signature)
|
||||
if err != nil {
|
||||
return err
|
||||
|
11
vendor/github.com/google/go-github/github/orgs.go
generated
vendored
11
vendor/github.com/google/go-github/github/orgs.go
generated
vendored
@ -44,6 +44,17 @@ type Organization struct {
|
||||
BillingEmail *string `json:"billing_email,omitempty"`
|
||||
Type *string `json:"type,omitempty"`
|
||||
Plan *Plan `json:"plan,omitempty"`
|
||||
TwoFactorRequirementEnabled *bool `json:"two_factor_requirement_enabled,omitempty"`
|
||||
|
||||
// DefaultRepoPermission can be one of: "read", "write", "admin", or "none". (Default: "read").
|
||||
// It is only used in OrganizationsService.Edit.
|
||||
DefaultRepoPermission *string `json:"default_repository_permission,omitempty"`
|
||||
// DefaultRepoSettings can be one of: "read", "write", "admin", or "none". (Default: "read").
|
||||
// It is only used in OrganizationsService.Get.
|
||||
DefaultRepoSettings *string `json:"default_repository_settings,omitempty"`
|
||||
|
||||
// MembersCanCreateRepos default value is true and is only used in Organizations.Edit.
|
||||
MembersCanCreateRepos *bool `json:"members_can_create_repositories,omitempty"`
|
||||
|
||||
// API URLs
|
||||
URL *string `json:"url,omitempty"`
|
||||
|
14
vendor/github.com/google/go-github/github/orgs_hooks.go
generated
vendored
14
vendor/github.com/google/go-github/github/orgs_hooks.go
generated
vendored
@ -49,12 +49,22 @@ func (s *OrganizationsService) GetHook(ctx context.Context, org string, id int64
|
||||
}
|
||||
|
||||
// CreateHook creates a Hook for the specified org.
|
||||
// Name and Config are required fields.
|
||||
// Config is a required field.
|
||||
//
|
||||
// Note that only a subset of the hook fields are used and hook must
|
||||
// not be nil.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#create-a-hook
|
||||
func (s *OrganizationsService) CreateHook(ctx context.Context, org string, hook *Hook) (*Hook, *Response, error) {
|
||||
u := fmt.Sprintf("orgs/%v/hooks", org)
|
||||
req, err := s.client.NewRequest("POST", u, hook)
|
||||
|
||||
hookReq := &createHookRequest{
|
||||
Events: hook.Events,
|
||||
Active: hook.Active,
|
||||
Config: hook.Config,
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, hookReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
22
vendor/github.com/google/go-github/github/pulls.go
generated
vendored
22
vendor/github.com/google/go-github/github/pulls.go
generated
vendored
@ -60,6 +60,11 @@ type PullRequest struct {
|
||||
NodeID *string `json:"node_id,omitempty"`
|
||||
RequestedReviewers []*User `json:"requested_reviewers,omitempty"`
|
||||
|
||||
// RequestedTeams is populated as part of the PullRequestEvent.
|
||||
// See, https://developer.github.com/v3/activity/events/types/#pullrequestevent for an example.
|
||||
RequestedTeams []*Team `json:"requested_teams,omitempty"`
|
||||
|
||||
Links *PRLinks `json:"_links,omitempty"`
|
||||
Head *PullRequestBranch `json:"head,omitempty"`
|
||||
Base *PullRequestBranch `json:"base,omitempty"`
|
||||
|
||||
@ -72,6 +77,23 @@ func (p PullRequest) String() string {
|
||||
return Stringify(p)
|
||||
}
|
||||
|
||||
// PRLink represents a single link object from Github pull request _links.
|
||||
type PRLink struct {
|
||||
HRef *string `json:"href,omitempty"`
|
||||
}
|
||||
|
||||
// PRLinks represents the "_links" object in a Github pull request.
|
||||
type PRLinks struct {
|
||||
Self *PRLink `json:"self,omitempty"`
|
||||
HTML *PRLink `json:"html,omitempty"`
|
||||
Issue *PRLink `json:"issue,omitempty"`
|
||||
Comments *PRLink `json:"comments,omitempty"`
|
||||
ReviewComments *PRLink `json:"review_comments,omitempty"`
|
||||
ReviewComment *PRLink `json:"review_comment,omitempty"`
|
||||
Commits *PRLink `json:"commits,omitempty"`
|
||||
Statuses *PRLink `json:"statuses,omitempty"`
|
||||
}
|
||||
|
||||
// PullRequestBranch represents a base or head branch in a GitHub pull request.
|
||||
type PullRequestBranch struct {
|
||||
Label *string `json:"label,omitempty"`
|
||||
|
101
vendor/github.com/google/go-github/github/reactions.go
generated
vendored
101
vendor/github.com/google/go-github/github/reactions.go
generated
vendored
@ -74,6 +74,7 @@ func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo
|
||||
// CreateCommentReaction creates a reaction for a commit comment.
|
||||
// Note that if you have already created a reaction of type content, the
|
||||
// previously created reaction will be returned with Status: 200 OK.
|
||||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray".
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-commit-comment
|
||||
func (s ReactionsService) CreateCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) {
|
||||
@ -127,6 +128,7 @@ func (s *ReactionsService) ListIssueReactions(ctx context.Context, owner, repo s
|
||||
// CreateIssueReaction creates a reaction for an issue.
|
||||
// Note that if you have already created a reaction of type content, the
|
||||
// previously created reaction will be returned with Status: 200 OK.
|
||||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray".
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue
|
||||
func (s ReactionsService) CreateIssueReaction(ctx context.Context, owner, repo string, number int, content string) (*Reaction, *Response, error) {
|
||||
@ -180,6 +182,7 @@ func (s *ReactionsService) ListIssueCommentReactions(ctx context.Context, owner,
|
||||
// CreateIssueCommentReaction creates a reaction for an issue comment.
|
||||
// Note that if you have already created a reaction of type content, the
|
||||
// previously created reaction will be returned with Status: 200 OK.
|
||||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray".
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment
|
||||
func (s ReactionsService) CreateIssueCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) {
|
||||
@ -233,6 +236,7 @@ func (s *ReactionsService) ListPullRequestCommentReactions(ctx context.Context,
|
||||
// CreatePullRequestCommentReaction creates a reaction for a pull request review comment.
|
||||
// Note that if you have already created a reaction of type content, the
|
||||
// previously created reaction will be returned with Status: 200 OK.
|
||||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray".
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment
|
||||
func (s ReactionsService) CreatePullRequestCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) {
|
||||
@ -256,6 +260,103 @@ func (s ReactionsService) CreatePullRequestCommentReaction(ctx context.Context,
|
||||
return m, resp, nil
|
||||
}
|
||||
|
||||
// ListTeamDiscussionReactions lists the reactions for a team discussion.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-team-discussion
|
||||
func (s *ReactionsService) ListTeamDiscussionReactions(ctx context.Context, teamID int64, discussionNumber int, opt *ListOptions) ([]*Reaction, *Response, error) {
|
||||
u := fmt.Sprintf("teams/%v/discussions/%v/reactions", teamID, discussionNumber)
|
||||
u, err := addOptions(u, opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", mediaTypeReactionsPreview)
|
||||
|
||||
var m []*Reaction
|
||||
resp, err := s.client.Do(ctx, req, &m)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return m, resp, nil
|
||||
}
|
||||
|
||||
// CreateTeamDiscussionReaction creates a reaction for a team discussion.
|
||||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray".
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-team-discussion
|
||||
func (s *ReactionsService) CreateTeamDiscussionReaction(ctx context.Context, teamID int64, discussionNumber int, content string) (*Reaction, *Response, error) {
|
||||
u := fmt.Sprintf("teams/%v/discussions/%v/reactions", teamID, discussionNumber)
|
||||
|
||||
body := &Reaction{Content: String(content)}
|
||||
req, err := s.client.NewRequest("POST", u, body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", mediaTypeReactionsPreview)
|
||||
|
||||
m := &Reaction{}
|
||||
resp, err := s.client.Do(ctx, req, m)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return m, resp, nil
|
||||
}
|
||||
|
||||
// ListTeamDiscussionCommentReactions lists the reactions for a team discussion comment.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-team-discussion-comment
|
||||
func (s *ReactionsService) ListTeamDiscussionCommentReactions(ctx context.Context, teamID int64, discussionNumber, commentNumber int, opt *ListOptions) ([]*Reaction, *Response, error) {
|
||||
u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v/reactions", teamID, discussionNumber, commentNumber)
|
||||
u, err := addOptions(u, opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", mediaTypeReactionsPreview)
|
||||
|
||||
var m []*Reaction
|
||||
resp, err := s.client.Do(ctx, req, &m)
|
||||
|
||||
return m, resp, nil
|
||||
}
|
||||
|
||||
// CreateTeamDiscussionCommentReaction creates a reaction for a team discussion comment.
|
||||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray".
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-team-discussion-comment
|
||||
func (s *ReactionsService) CreateTeamDiscussionCommentReaction(ctx context.Context, teamID int64, discussionNumber, commentNumber int, content string) (*Reaction, *Response, error) {
|
||||
u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v/reactions", teamID, discussionNumber, commentNumber)
|
||||
|
||||
body := &Reaction{Content: String(content)}
|
||||
req, err := s.client.NewRequest("POST", u, body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", mediaTypeReactionsPreview)
|
||||
|
||||
m := &Reaction{}
|
||||
resp, err := s.client.Do(ctx, req, m)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return m, resp, nil
|
||||
}
|
||||
|
||||
// DeleteReaction deletes a reaction.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/reaction/reactions/#delete-a-reaction-archive
|
||||
|
51
vendor/github.com/google/go-github/github/repos.go
generated
vendored
51
vendor/github.com/google/go-github/github/repos.go
generated
vendored
@ -56,6 +56,7 @@ type Repository struct {
|
||||
AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"`
|
||||
AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"`
|
||||
Topics []string `json:"topics,omitempty"`
|
||||
Archived *bool `json:"archived,omitempty"`
|
||||
|
||||
// Only provided when using RepositoriesService.Get while in preview
|
||||
License *License `json:"license,omitempty"`
|
||||
@ -69,7 +70,6 @@ type Repository struct {
|
||||
HasDownloads *bool `json:"has_downloads,omitempty"`
|
||||
LicenseTemplate *string `json:"license_template,omitempty"`
|
||||
GitignoreTemplate *string `json:"gitignore_template,omitempty"`
|
||||
Archived *bool `json:"archived,omitempty"`
|
||||
|
||||
// Creating an organization repository. Required for non-owners.
|
||||
TeamID *int64 `json:"team_id,omitempty"`
|
||||
@ -259,10 +259,40 @@ func (s *RepositoriesService) ListAll(ctx context.Context, opt *RepositoryListAl
|
||||
return repos, resp, nil
|
||||
}
|
||||
|
||||
// createRepoRequest is a subset of Repository and is used internally
|
||||
// by Create to pass only the known fields for the endpoint.
|
||||
//
|
||||
// See https://github.com/google/go-github/issues/1014 for more
|
||||
// information.
|
||||
type createRepoRequest struct {
|
||||
// Name is required when creating a repo.
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Homepage *string `json:"homepage,omitempty"`
|
||||
|
||||
Private *bool `json:"private,omitempty"`
|
||||
HasIssues *bool `json:"has_issues,omitempty"`
|
||||
HasProjects *bool `json:"has_projects,omitempty"`
|
||||
HasWiki *bool `json:"has_wiki,omitempty"`
|
||||
|
||||
// Creating an organization repository. Required for non-owners.
|
||||
TeamID *int64 `json:"team_id,omitempty"`
|
||||
|
||||
AutoInit *bool `json:"auto_init,omitempty"`
|
||||
GitignoreTemplate *string `json:"gitignore_template,omitempty"`
|
||||
LicenseTemplate *string `json:"license_template,omitempty"`
|
||||
AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"`
|
||||
AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"`
|
||||
AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"`
|
||||
}
|
||||
|
||||
// Create a new repository. If an organization is specified, the new
|
||||
// repository will be created under that org. If the empty string is
|
||||
// specified, it will be created for the authenticated user.
|
||||
//
|
||||
// Note that only a subset of the repo fields are used and repo must
|
||||
// not be nil.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/repos/#create
|
||||
func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repository) (*Repository, *Response, error) {
|
||||
var u string
|
||||
@ -272,7 +302,24 @@ func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repo
|
||||
u = "user/repos"
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, repo)
|
||||
repoReq := &createRepoRequest{
|
||||
Name: repo.Name,
|
||||
Description: repo.Description,
|
||||
Homepage: repo.Homepage,
|
||||
Private: repo.Private,
|
||||
HasIssues: repo.HasIssues,
|
||||
HasProjects: repo.HasProjects,
|
||||
HasWiki: repo.HasWiki,
|
||||
TeamID: repo.TeamID,
|
||||
AutoInit: repo.AutoInit,
|
||||
GitignoreTemplate: repo.GitignoreTemplate,
|
||||
LicenseTemplate: repo.LicenseTemplate,
|
||||
AllowSquashMerge: repo.AllowSquashMerge,
|
||||
AllowMergeCommit: repo.AllowMergeCommit,
|
||||
AllowRebaseMerge: repo.AllowRebaseMerge,
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, repoReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
11
vendor/github.com/google/go-github/github/repos_forks.go
generated
vendored
11
vendor/github.com/google/go-github/github/repos_forks.go
generated
vendored
@ -8,6 +8,8 @@ package github
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// RepositoryListForksOptions specifies the optional parameters to the
|
||||
@ -78,10 +80,15 @@ func (s *RepositoriesService) CreateFork(ctx context.Context, owner, repo string
|
||||
|
||||
fork := new(Repository)
|
||||
resp, err := s.client.Do(ctx, req, fork)
|
||||
if _, ok := err.(*AcceptedError); ok {
|
||||
if err != nil {
|
||||
// Persist AcceptedError's metadata to the Repository object.
|
||||
if aerr, ok := err.(*AcceptedError); ok {
|
||||
if err := json.Unmarshal(aerr.Raw, fork); err != nil {
|
||||
return fork, resp, err
|
||||
}
|
||||
|
||||
return fork, resp, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
|
34
vendor/github.com/google/go-github/github/repos_hooks.go
generated
vendored
34
vendor/github.com/google/go-github/github/repos_hooks.go
generated
vendored
@ -71,25 +71,49 @@ func (w WebHookAuthor) String() string {
|
||||
type Hook struct {
|
||||
CreatedAt *time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
URL *string `json:"url,omitempty"`
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
|
||||
// Only the following fields are used when creating a hook.
|
||||
// Config is required.
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
Events []string `json:"events,omitempty"`
|
||||
Active *bool `json:"active,omitempty"`
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func (h Hook) String() string {
|
||||
return Stringify(h)
|
||||
}
|
||||
|
||||
// createHookRequest is a subset of Hook and is used internally
|
||||
// by CreateHook to pass only the known fields for the endpoint.
|
||||
//
|
||||
// See https://github.com/google/go-github/issues/1015 for more
|
||||
// information.
|
||||
type createHookRequest struct {
|
||||
// Config is required.
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
Events []string `json:"events,omitempty"`
|
||||
Active *bool `json:"active,omitempty"`
|
||||
}
|
||||
|
||||
// CreateHook creates a Hook for the specified repository.
|
||||
// Name and Config are required fields.
|
||||
// Config is a required field.
|
||||
//
|
||||
// Note that only a subset of the hook fields are used and hook must
|
||||
// not be nil.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#create-a-hook
|
||||
func (s *RepositoriesService) CreateHook(ctx context.Context, owner, repo string, hook *Hook) (*Hook, *Response, error) {
|
||||
u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo)
|
||||
req, err := s.client.NewRequest("POST", u, hook)
|
||||
|
||||
hookReq := &createHookRequest{
|
||||
Events: hook.Events,
|
||||
Active: hook.Active,
|
||||
Config: hook.Config,
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, hookReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
47
vendor/github.com/google/go-github/github/repos_releases.go
generated
vendored
47
vendor/github.com/google/go-github/github/repos_releases.go
generated
vendored
@ -19,13 +19,15 @@ import (
|
||||
|
||||
// RepositoryRelease represents a GitHub release in a repository.
|
||||
type RepositoryRelease struct {
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
TagName *string `json:"tag_name,omitempty"`
|
||||
TargetCommitish *string `json:"target_commitish,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Body *string `json:"body,omitempty"`
|
||||
Draft *bool `json:"draft,omitempty"`
|
||||
Prerelease *bool `json:"prerelease,omitempty"`
|
||||
|
||||
// The following fields are not used in CreateRelease or EditRelease:
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
||||
PublishedAt *Timestamp `json:"published_at,omitempty"`
|
||||
URL *string `json:"url,omitempty"`
|
||||
@ -125,13 +127,40 @@ func (s *RepositoriesService) getSingleRelease(ctx context.Context, url string)
|
||||
return release, resp, nil
|
||||
}
|
||||
|
||||
// repositoryReleaseRequest is a subset of RepositoryRelease and
|
||||
// is used internally by CreateRelease and EditRelease to pass
|
||||
// only the known fields for these endpoints.
|
||||
//
|
||||
// See https://github.com/google/go-github/issues/992 for more
|
||||
// information.
|
||||
type repositoryReleaseRequest struct {
|
||||
TagName *string `json:"tag_name,omitempty"`
|
||||
TargetCommitish *string `json:"target_commitish,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Body *string `json:"body,omitempty"`
|
||||
Draft *bool `json:"draft,omitempty"`
|
||||
Prerelease *bool `json:"prerelease,omitempty"`
|
||||
}
|
||||
|
||||
// CreateRelease adds a new release for a repository.
|
||||
//
|
||||
// Note that only a subset of the release fields are used.
|
||||
// See RepositoryRelease for more information.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/repos/releases/#create-a-release
|
||||
func (s *RepositoriesService) CreateRelease(ctx context.Context, owner, repo string, release *RepositoryRelease) (*RepositoryRelease, *Response, error) {
|
||||
u := fmt.Sprintf("repos/%s/%s/releases", owner, repo)
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, release)
|
||||
releaseReq := &repositoryReleaseRequest{
|
||||
TagName: release.TagName,
|
||||
TargetCommitish: release.TargetCommitish,
|
||||
Name: release.Name,
|
||||
Body: release.Body,
|
||||
Draft: release.Draft,
|
||||
Prerelease: release.Prerelease,
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, releaseReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -146,11 +175,23 @@ func (s *RepositoriesService) CreateRelease(ctx context.Context, owner, repo str
|
||||
|
||||
// EditRelease edits a repository release.
|
||||
//
|
||||
// Note that only a subset of the release fields are used.
|
||||
// See RepositoryRelease for more information.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/repos/releases/#edit-a-release
|
||||
func (s *RepositoriesService) EditRelease(ctx context.Context, owner, repo string, id int64, release *RepositoryRelease) (*RepositoryRelease, *Response, error) {
|
||||
u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id)
|
||||
|
||||
req, err := s.client.NewRequest("PATCH", u, release)
|
||||
releaseReq := &repositoryReleaseRequest{
|
||||
TagName: release.TagName,
|
||||
TargetCommitish: release.TargetCommitish,
|
||||
Name: release.Name,
|
||||
Body: release.Body,
|
||||
Draft: release.Draft,
|
||||
Prerelease: release.Prerelease,
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest("PATCH", u, releaseReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
1
vendor/github.com/google/go-github/github/teams_discussion_comments.go
generated
vendored
1
vendor/github.com/google/go-github/github/teams_discussion_comments.go
generated
vendored
@ -24,6 +24,7 @@ type DiscussionComment struct {
|
||||
Number *int `json:"number,omitempty"`
|
||||
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
||||
URL *string `json:"url,omitempty"`
|
||||
Reactions *Reactions `json:"reactions,omitempty"`
|
||||
}
|
||||
|
||||
func (c DiscussionComment) String() string {
|
||||
|
1
vendor/github.com/google/go-github/github/teams_discussions.go
generated
vendored
1
vendor/github.com/google/go-github/github/teams_discussions.go
generated
vendored
@ -29,6 +29,7 @@ type TeamDiscussion struct {
|
||||
Title *string `json:"title,omitempty"`
|
||||
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
||||
URL *string `json:"url,omitempty"`
|
||||
Reactions *Reactions `json:"reactions,omitempty"`
|
||||
}
|
||||
|
||||
func (d TeamDiscussion) String() string {
|
||||
|
1
vendor/github.com/google/go-github/github/users.go
generated
vendored
1
vendor/github.com/google/go-github/github/users.go
generated
vendored
@ -76,6 +76,7 @@ func (u User) String() string {
|
||||
// user.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/users/#get-a-single-user
|
||||
// and: https://developer.github.com/v3/users/#get-the-authenticated-user
|
||||
func (s *UsersService) Get(ctx context.Context, user string) (*User, *Response, error) {
|
||||
var u string
|
||||
if user != "" {
|
||||
|
2
vendor/github.com/olebedev/config/config.go
generated
vendored
2
vendor/github.com/olebedev/config/config.go
generated
vendored
@ -498,7 +498,7 @@ func Set(cfg interface{}, path string, value interface{}) error {
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"Invalid type at %q: expected []interface{} or map[string]interface{}; got %T",
|
||||
strings.Join(parts[:pos+1], "."), cfg)
|
||||
strings.Join(parts[:pos+1], "."), c)
|
||||
}
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/rivo/tview/README.md
generated
vendored
2
vendor/github.com/rivo/tview/README.md
generated
vendored
@ -65,6 +65,8 @@ Add your issue here on GitHub. Feel free to get in touch if you have any questio
|
||||
|
||||
(There are no corresponding tags in the project. I only keep such a history in this README.)
|
||||
|
||||
- v0.18 (2018-10-18)
|
||||
- `InputField` elements can now be navigated freely.
|
||||
- v0.17 (2018-06-20)
|
||||
- Added `TreeView`.
|
||||
- v0.15 (2018-05-02)
|
||||
|
39
vendor/github.com/rivo/tview/application.go
generated
vendored
39
vendor/github.com/rivo/tview/application.go
generated
vendored
@ -19,6 +19,9 @@ type Application struct {
|
||||
// The application's screen.
|
||||
screen tcell.Screen
|
||||
|
||||
// Indicates whether the application's screen is currently active.
|
||||
running bool
|
||||
|
||||
// The primitive which currently has the keyboard focus.
|
||||
focus Primitive
|
||||
|
||||
@ -70,22 +73,53 @@ func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.Event
|
||||
return a.inputCapture
|
||||
}
|
||||
|
||||
// SetScreen allows you to provide your own tcell.Screen object. For most
|
||||
// applications, this is not needed and you should be familiar with
|
||||
// tcell.Screen when using this function. Run() will call Init() and Fini() on
|
||||
// the provided screen object.
|
||||
//
|
||||
// This function is typically called before calling Run(). Calling it while an
|
||||
// application is running will switch the application to the new screen. Fini()
|
||||
// will be called on the old screen and Init() on the new screen (errors
|
||||
// returned by Init() will lead to a panic).
|
||||
//
|
||||
// Note that calling Suspend() will invoke Fini() on your screen object and it
|
||||
// will not be restored when suspended mode ends. Instead, a new default screen
|
||||
// object will be created.
|
||||
func (a *Application) SetScreen(screen tcell.Screen) *Application {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
if a.running {
|
||||
a.screen.Fini()
|
||||
}
|
||||
a.screen = screen
|
||||
if a.running {
|
||||
if err := a.screen.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Run starts the application and thus the event loop. This function returns
|
||||
// when Stop() was called.
|
||||
func (a *Application) Run() error {
|
||||
var err error
|
||||
a.Lock()
|
||||
|
||||
// Make a screen.
|
||||
// Make a screen if there is none yet.
|
||||
if a.screen == nil {
|
||||
a.screen, err = tcell.NewScreen()
|
||||
if err != nil {
|
||||
a.Unlock()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = a.screen.Init(); err != nil {
|
||||
a.Unlock()
|
||||
return err
|
||||
}
|
||||
a.running = true
|
||||
|
||||
// We catch panics to clean up because they mess up the terminal.
|
||||
defer func() {
|
||||
@ -93,6 +127,7 @@ func (a *Application) Run() error {
|
||||
if a.screen != nil {
|
||||
a.screen.Fini()
|
||||
}
|
||||
a.running = false
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
@ -170,6 +205,7 @@ func (a *Application) Stop() {
|
||||
}
|
||||
a.screen.Fini()
|
||||
a.screen = nil
|
||||
a.running = false
|
||||
}
|
||||
|
||||
// Suspend temporarily suspends the application by exiting terminal UI mode and
|
||||
@ -217,6 +253,7 @@ func (a *Application) Suspend(f func()) bool {
|
||||
a.Unlock()
|
||||
panic(err)
|
||||
}
|
||||
a.running = true
|
||||
a.Unlock()
|
||||
a.Draw()
|
||||
|
||||
|
12
vendor/github.com/rivo/tview/box.go
generated
vendored
12
vendor/github.com/rivo/tview/box.go
generated
vendored
@ -51,10 +51,6 @@ type Box struct {
|
||||
// Whether or not this box has focus.
|
||||
hasFocus bool
|
||||
|
||||
// If set to true, the inner rect of this box will be within the screen at the
|
||||
// last time the box was drawn.
|
||||
clampToScreen bool
|
||||
|
||||
// An optional capture function which receives a key event and returns the
|
||||
// event to be forwarded to the primitive's default input handler (nil if
|
||||
// nothing should be forwarded).
|
||||
@ -74,7 +70,6 @@ func NewBox() *Box {
|
||||
borderColor: Styles.BorderColor,
|
||||
titleColor: Styles.TitleColor,
|
||||
titleAlign: AlignCenter,
|
||||
clampToScreen: true,
|
||||
}
|
||||
b.focus = b
|
||||
return b
|
||||
@ -117,6 +112,7 @@ func (b *Box) SetRect(x, y, width, height int) {
|
||||
b.y = y
|
||||
b.width = width
|
||||
b.height = height
|
||||
b.innerX = -1 // Mark inner rect as uninitialized.
|
||||
}
|
||||
|
||||
// SetDrawFunc sets a callback function which is invoked after the box primitive
|
||||
@ -277,8 +273,8 @@ func (b *Box) Draw(screen tcell.Screen) {
|
||||
|
||||
// Draw title.
|
||||
if b.title != "" && b.width >= 4 {
|
||||
_, printed := Print(screen, b.title, b.x+1, b.y, b.width-2, b.titleAlign, b.titleColor)
|
||||
if StringWidth(b.title)-printed > 0 && printed > 0 {
|
||||
printed, _ := Print(screen, b.title, b.x+1, b.y, b.width-2, b.titleAlign, b.titleColor)
|
||||
if len(b.title)-printed > 0 && printed > 0 {
|
||||
_, _, style, _ := screen.GetContent(b.x+b.width-2, b.y)
|
||||
fg, _, _ := style.Decompose()
|
||||
Print(screen, string(SemigraphicsHorizontalEllipsis), b.x+b.width-2, b.y, 1, AlignLeft, fg)
|
||||
@ -296,7 +292,6 @@ func (b *Box) Draw(screen tcell.Screen) {
|
||||
}
|
||||
|
||||
// Clamp inner rect to screen.
|
||||
if b.clampToScreen {
|
||||
width, height := screen.Size()
|
||||
if b.innerX < 0 {
|
||||
b.innerWidth += b.innerX
|
||||
@ -312,7 +307,6 @@ func (b *Box) Draw(screen tcell.Screen) {
|
||||
b.innerHeight += b.innerY
|
||||
b.innerY = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Focus is called when this primitive receives focus.
|
||||
|
6
vendor/github.com/rivo/tview/checkbox.go
generated
vendored
6
vendor/github.com/rivo/tview/checkbox.go
generated
vendored
@ -124,9 +124,9 @@ func (c *Checkbox) SetChangedFunc(handler func(checked bool)) *Checkbox {
|
||||
return c
|
||||
}
|
||||
|
||||
// SetDoneFunc sets a handler which is called when the user is done entering
|
||||
// text. The callback function is provided with the key that was pressed, which
|
||||
// is one of the following:
|
||||
// SetDoneFunc sets a handler which is called when the user is done using the
|
||||
// checkbox. The callback function is provided with the key that was pressed,
|
||||
// which is one of the following:
|
||||
//
|
||||
// - KeyEscape: Abort text input.
|
||||
// - KeyTab: Move to the next field.
|
||||
|
12
vendor/github.com/rivo/tview/form.go
generated
vendored
12
vendor/github.com/rivo/tview/form.go
generated
vendored
@ -218,6 +218,13 @@ func (f *Form) AddButton(label string, selected func()) *Form {
|
||||
return f
|
||||
}
|
||||
|
||||
// GetButton returns the button at the specified 0-based index. Note that
|
||||
// buttons have been specially prepared for this form and modifying some of
|
||||
// their attributes may have unintended side effects.
|
||||
func (f *Form) GetButton(index int) *Button {
|
||||
return f.buttons[index]
|
||||
}
|
||||
|
||||
// RemoveButton removes the button at the specified position, starting with 0
|
||||
// for the button that was added first.
|
||||
func (f *Form) RemoveButton(index int) *Form {
|
||||
@ -225,6 +232,11 @@ func (f *Form) RemoveButton(index int) *Form {
|
||||
return f
|
||||
}
|
||||
|
||||
// GetButtonCount returns the number of buttons in this form.
|
||||
func (f *Form) GetButtonCount() int {
|
||||
return len(f.buttons)
|
||||
}
|
||||
|
||||
// GetButtonIndex returns the index of the button with the given label, starting
|
||||
// with 0 for the button that was added first. If no such label was found, -1
|
||||
// is returned.
|
||||
|
192
vendor/github.com/rivo/tview/inputfield.go
generated
vendored
192
vendor/github.com/rivo/tview/inputfield.go
generated
vendored
@ -11,10 +11,23 @@ import (
|
||||
)
|
||||
|
||||
// InputField is a one-line box (three lines if there is a title) where the
|
||||
// user can enter text.
|
||||
// user can enter text. Use SetAcceptanceFunc() to accept or reject input,
|
||||
// SetChangedFunc() to listen for changes, and SetMaskCharacter() to hide input
|
||||
// from onlookers (e.g. for password input).
|
||||
//
|
||||
// Use SetMaskCharacter() to hide input from onlookers (e.g. for password
|
||||
// input).
|
||||
// The following keys can be used for navigation and editing:
|
||||
//
|
||||
// - Left arrow: Move left by one character.
|
||||
// - Right arrow: Move right by one character.
|
||||
// - Home, Ctrl-A, Alt-a: Move to the beginning of the line.
|
||||
// - End, Ctrl-E, Alt-e: Move to the end of the line.
|
||||
// - Alt-left, Alt-b: Move left by one word.
|
||||
// - Alt-right, Alt-f: Move right by one word.
|
||||
// - Backspace: Delete the character before the cursor.
|
||||
// - Delete: Delete the character after the cursor.
|
||||
// - Ctrl-K: Delete from the cursor to the end of the line.
|
||||
// - Ctrl-W: Delete the last word before the cursor.
|
||||
// - Ctrl-U: Delete the entire line.
|
||||
//
|
||||
// See https://github.com/rivo/tview/wiki/InputField for an example.
|
||||
type InputField struct {
|
||||
@ -53,6 +66,12 @@ type InputField struct {
|
||||
// disables masking.
|
||||
maskCharacter rune
|
||||
|
||||
// The cursor position as a byte index into the text string.
|
||||
cursorPos int
|
||||
|
||||
// The number of bytes of the text string skipped ahead while drawing.
|
||||
offset int
|
||||
|
||||
// An optional function which may reject the last character that was entered.
|
||||
accept func(text string, ch rune) bool
|
||||
|
||||
@ -174,7 +193,7 @@ func (i *InputField) SetMaskCharacter(mask rune) *InputField {
|
||||
// SetAcceptanceFunc sets a handler which may reject the last character that was
|
||||
// entered (by returning false).
|
||||
//
|
||||
// This package defines a number of variables Prefixed with InputField which may
|
||||
// This package defines a number of variables prefixed with InputField which may
|
||||
// be used for common input (e.g. numbers, maximum text length).
|
||||
func (i *InputField) SetAcceptanceFunc(handler func(textToCheck string, lastChar rune) bool) *InputField {
|
||||
i.accept = handler
|
||||
@ -244,56 +263,69 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
||||
screen.SetContent(x+index, y, ' ', nil, fieldStyle)
|
||||
}
|
||||
|
||||
// Draw placeholder text.
|
||||
// Text.
|
||||
var cursorScreenPos int
|
||||
text := i.text
|
||||
if text == "" && i.placeholder != "" {
|
||||
Print(screen, i.placeholder, x, y, fieldWidth, AlignLeft, i.placeholderTextColor)
|
||||
// Draw placeholder text.
|
||||
Print(screen, Escape(i.placeholder), x, y, fieldWidth, AlignLeft, i.placeholderTextColor)
|
||||
i.offset = 0
|
||||
} else {
|
||||
// Draw entered text.
|
||||
if i.maskCharacter > 0 {
|
||||
text = strings.Repeat(string(i.maskCharacter), utf8.RuneCountInString(i.text))
|
||||
} else {
|
||||
text = Escape(text)
|
||||
}
|
||||
fieldWidth-- // We need one cell for the cursor.
|
||||
if fieldWidth < runewidth.StringWidth(text) {
|
||||
Print(screen, text, x, y, fieldWidth, AlignRight, i.fieldTextColor)
|
||||
stringWidth := runewidth.StringWidth(text)
|
||||
if fieldWidth >= stringWidth {
|
||||
// We have enough space for the full text.
|
||||
Print(screen, Escape(text), x, y, fieldWidth, AlignLeft, i.fieldTextColor)
|
||||
i.offset = 0
|
||||
iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
if textPos >= i.cursorPos {
|
||||
return true
|
||||
}
|
||||
cursorScreenPos += screenWidth
|
||||
return false
|
||||
})
|
||||
} else {
|
||||
Print(screen, text, x, y, fieldWidth, AlignLeft, i.fieldTextColor)
|
||||
// The text doesn't fit. Where is the cursor?
|
||||
if i.cursorPos < 0 {
|
||||
i.cursorPos = 0
|
||||
} else if i.cursorPos > len(text) {
|
||||
i.cursorPos = len(text)
|
||||
}
|
||||
// Shift the text so the cursor is inside the field.
|
||||
var shiftLeft int
|
||||
if i.offset > i.cursorPos {
|
||||
i.offset = i.cursorPos
|
||||
} else if subWidth := runewidth.StringWidth(text[i.offset:i.cursorPos]); subWidth > fieldWidth-1 {
|
||||
shiftLeft = subWidth - fieldWidth + 1
|
||||
}
|
||||
currentOffset := i.offset
|
||||
iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
if textPos >= currentOffset {
|
||||
if shiftLeft > 0 {
|
||||
i.offset = textPos + textWidth
|
||||
shiftLeft -= screenWidth
|
||||
} else {
|
||||
if textPos+textWidth > i.cursorPos {
|
||||
return true
|
||||
}
|
||||
cursorScreenPos += screenWidth
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
Print(screen, Escape(text[i.offset:]), x, y, fieldWidth, AlignLeft, i.fieldTextColor)
|
||||
}
|
||||
}
|
||||
|
||||
// Set cursor.
|
||||
if i.focus.HasFocus() {
|
||||
i.setCursor(screen)
|
||||
screen.ShowCursor(x+cursorScreenPos, y)
|
||||
}
|
||||
}
|
||||
|
||||
// setCursor sets the cursor position.
|
||||
func (i *InputField) setCursor(screen tcell.Screen) {
|
||||
x := i.x
|
||||
y := i.y
|
||||
rightLimit := x + i.width
|
||||
if i.border {
|
||||
x++
|
||||
y++
|
||||
rightLimit -= 2
|
||||
}
|
||||
fieldWidth := runewidth.StringWidth(i.text)
|
||||
if i.fieldWidth > 0 && fieldWidth > i.fieldWidth-1 {
|
||||
fieldWidth = i.fieldWidth - 1
|
||||
}
|
||||
if i.labelWidth > 0 {
|
||||
x += i.labelWidth + fieldWidth
|
||||
} else {
|
||||
x += StringWidth(i.label) + fieldWidth
|
||||
}
|
||||
if x >= rightLimit {
|
||||
x = rightLimit - 1
|
||||
}
|
||||
screen.ShowCursor(x, y)
|
||||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
return i.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
@ -305,27 +337,95 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
|
||||
}
|
||||
}()
|
||||
|
||||
// Movement functions.
|
||||
home := func() { i.cursorPos = 0 }
|
||||
end := func() { i.cursorPos = len(i.text) }
|
||||
moveLeft := func() {
|
||||
iterateStringReverse(i.text[:i.cursorPos], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
i.cursorPos -= textWidth
|
||||
return true
|
||||
})
|
||||
}
|
||||
moveRight := func() {
|
||||
iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
i.cursorPos += textWidth
|
||||
return true
|
||||
})
|
||||
}
|
||||
moveWordLeft := func() {
|
||||
i.cursorPos = len(regexp.MustCompile(`\S+\s*$`).ReplaceAllString(i.text[:i.cursorPos], ""))
|
||||
}
|
||||
moveWordRight := func() {
|
||||
i.cursorPos = len(i.text) - len(regexp.MustCompile(`^\s*\S+\s*`).ReplaceAllString(i.text[i.cursorPos:], ""))
|
||||
}
|
||||
|
||||
// Process key event.
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyRune: // Regular character.
|
||||
newText := i.text + string(event.Rune())
|
||||
modifiers := event.Modifiers()
|
||||
if modifiers == tcell.ModNone {
|
||||
ch := string(event.Rune())
|
||||
newText := i.text[:i.cursorPos] + ch + i.text[i.cursorPos:]
|
||||
if i.accept != nil {
|
||||
if !i.accept(newText, event.Rune()) {
|
||||
break
|
||||
}
|
||||
}
|
||||
i.text = newText
|
||||
i.cursorPos += len(ch)
|
||||
} else if modifiers&tcell.ModAlt > 0 {
|
||||
// We accept some Alt- key combinations.
|
||||
switch event.Rune() {
|
||||
case 'a': // Home.
|
||||
home()
|
||||
case 'e': // End.
|
||||
end()
|
||||
case 'b': // Move word left.
|
||||
moveWordLeft()
|
||||
case 'f': // Move word right.
|
||||
moveWordRight()
|
||||
}
|
||||
}
|
||||
case tcell.KeyCtrlU: // Delete all.
|
||||
i.text = ""
|
||||
i.cursorPos = 0
|
||||
case tcell.KeyCtrlK: // Delete until the end of the line.
|
||||
i.text = i.text[:i.cursorPos]
|
||||
case tcell.KeyCtrlW: // Delete last word.
|
||||
lastWord := regexp.MustCompile(`\s*\S+\s*$`)
|
||||
i.text = lastWord.ReplaceAllString(i.text, "")
|
||||
case tcell.KeyBackspace, tcell.KeyBackspace2: // Delete last character.
|
||||
if len(i.text) == 0 {
|
||||
break
|
||||
lastWord := regexp.MustCompile(`\S+\s*$`)
|
||||
newText := lastWord.ReplaceAllString(i.text[:i.cursorPos], "") + i.text[i.cursorPos:]
|
||||
i.cursorPos -= len(i.text) - len(newText)
|
||||
i.text = newText
|
||||
case tcell.KeyBackspace, tcell.KeyBackspace2: // Delete character before the cursor.
|
||||
iterateStringReverse(i.text[:i.cursorPos], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
i.text = i.text[:textPos] + i.text[textPos+textWidth:]
|
||||
i.cursorPos -= textWidth
|
||||
return true
|
||||
})
|
||||
if i.offset >= i.cursorPos {
|
||||
i.offset = 0
|
||||
}
|
||||
runes := []rune(i.text)
|
||||
i.text = string(runes[:len(runes)-1])
|
||||
case tcell.KeyDelete: // Delete character after the cursor.
|
||||
iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
i.text = i.text[:i.cursorPos] + i.text[i.cursorPos+textWidth:]
|
||||
return true
|
||||
})
|
||||
case tcell.KeyLeft:
|
||||
if event.Modifiers()&tcell.ModAlt > 0 {
|
||||
moveWordLeft()
|
||||
} else {
|
||||
moveLeft()
|
||||
}
|
||||
case tcell.KeyRight:
|
||||
if event.Modifiers()&tcell.ModAlt > 0 {
|
||||
moveWordRight()
|
||||
} else {
|
||||
moveRight()
|
||||
}
|
||||
case tcell.KeyHome, tcell.KeyCtrlA:
|
||||
home()
|
||||
case tcell.KeyEnd, tcell.KeyCtrlE:
|
||||
end()
|
||||
case tcell.KeyEnter, tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape: // We're done.
|
||||
if i.done != nil {
|
||||
i.done(key)
|
||||
|
15
vendor/github.com/rivo/tview/list.go
generated
vendored
15
vendor/github.com/rivo/tview/list.go
generated
vendored
@ -85,6 +85,19 @@ func (l *List) GetCurrentItem() int {
|
||||
return l.currentItem
|
||||
}
|
||||
|
||||
// RemoveItem removes the item with the given index (starting at 0) from the
|
||||
// list. Does nothing if the index is out of range.
|
||||
func (l *List) RemoveItem(index int) *List {
|
||||
if index < 0 || index >= len(l.items) {
|
||||
return l
|
||||
}
|
||||
l.items = append(l.items[:index], l.items[index+1:]...)
|
||||
if l.currentItem >= len(l.items) {
|
||||
l.currentItem = len(l.items) - 1
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// SetMainTextColor sets the color of the items' main text.
|
||||
func (l *List) SetMainTextColor(color tcell.Color) *List {
|
||||
l.mainTextColor = color
|
||||
@ -127,7 +140,7 @@ func (l *List) ShowSecondaryText(show bool) *List {
|
||||
//
|
||||
// This function is also called when the first item is added or when
|
||||
// SetCurrentItem() is called.
|
||||
func (l *List) SetChangedFunc(handler func(int, string, string, rune)) *List {
|
||||
func (l *List) SetChangedFunc(handler func(index int, mainText string, secondaryText string, shortcut rune)) *List {
|
||||
l.changed = handler
|
||||
return l
|
||||
}
|
||||
|
10
vendor/github.com/rivo/tview/modal.go
generated
vendored
10
vendor/github.com/rivo/tview/modal.go
generated
vendored
@ -86,6 +86,16 @@ func (m *Modal) AddButtons(labels []string) *Modal {
|
||||
m.done(i, l)
|
||||
}
|
||||
})
|
||||
button := m.form.GetButton(m.form.GetButtonCount() - 1)
|
||||
button.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch event.Key() {
|
||||
case tcell.KeyDown, tcell.KeyRight:
|
||||
return tcell.NewEventKey(tcell.KeyTab, 0, tcell.ModNone)
|
||||
case tcell.KeyUp, tcell.KeyLeft:
|
||||
return tcell.NewEventKey(tcell.KeyBacktab, 0, tcell.ModNone)
|
||||
}
|
||||
return event
|
||||
})
|
||||
}(index, label)
|
||||
}
|
||||
return m
|
||||
|
59
vendor/github.com/rivo/tview/table.go
generated
vendored
59
vendor/github.com/rivo/tview/table.go
generated
vendored
@ -231,6 +231,10 @@ type Table struct {
|
||||
// The number of visible rows the last time the table was drawn.
|
||||
visibleRows int
|
||||
|
||||
// The style of the selected rows. If this value is 0, selected rows are
|
||||
// simply inverted.
|
||||
selectedStyle tcell.Style
|
||||
|
||||
// An optional function which gets called when the user presses Enter on a
|
||||
// selected cell. If entire rows selected, the column value is undefined.
|
||||
// Likewise for entire columns.
|
||||
@ -276,6 +280,18 @@ func (t *Table) SetBordersColor(color tcell.Color) *Table {
|
||||
return t
|
||||
}
|
||||
|
||||
// SetSelectedStyle sets a specific style for selected cells. If no such style
|
||||
// is set, per default, selected cells are inverted (i.e. their foreground and
|
||||
// background colors are swapped).
|
||||
//
|
||||
// To reset a previous setting to its default, make the following call:
|
||||
//
|
||||
// table.SetSelectedStyle(tcell.ColorDefault, tcell.ColorDefault, 0)
|
||||
func (t *Table) SetSelectedStyle(foregroundColor, backgroundColor tcell.Color, attributes tcell.AttrMask) *Table {
|
||||
t.selectedStyle = tcell.StyleDefault.Foreground(foregroundColor).Background(backgroundColor) | tcell.Style(attributes)
|
||||
return t
|
||||
}
|
||||
|
||||
// SetSeparator sets the character used to fill the space between two
|
||||
// neighboring cells. This is a space character ' ' per default but you may
|
||||
// want to set it to Borders.Vertical (or any other rune) if the column
|
||||
@ -743,12 +759,17 @@ ColumnLoop:
|
||||
}
|
||||
|
||||
// Helper function which colors the background of a box.
|
||||
colorBackground := func(fromX, fromY, w, h int, backgroundColor, textColor tcell.Color, selected bool) {
|
||||
// backgroundColor == tcell.ColorDefault => Don't color the background.
|
||||
// textColor == tcell.ColorDefault => Don't change the text color.
|
||||
// attr == 0 => Don't change attributes.
|
||||
// invert == true => Ignore attr, set text to backgroundColor or t.backgroundColor;
|
||||
// set background to textColor.
|
||||
colorBackground := func(fromX, fromY, w, h int, backgroundColor, textColor tcell.Color, attr tcell.AttrMask, invert bool) {
|
||||
for by := 0; by < h && fromY+by < y+height; by++ {
|
||||
for bx := 0; bx < w && fromX+bx < x+width; bx++ {
|
||||
m, c, style, _ := screen.GetContent(fromX+bx, fromY+by)
|
||||
if selected {
|
||||
fg, _, _ := style.Decompose()
|
||||
fg, bg, a := style.Decompose()
|
||||
if invert {
|
||||
if fg == textColor || fg == t.bordersColor {
|
||||
fg = backgroundColor
|
||||
}
|
||||
@ -757,10 +778,16 @@ ColumnLoop:
|
||||
}
|
||||
style = style.Background(textColor).Foreground(fg)
|
||||
} else {
|
||||
if backgroundColor == tcell.ColorDefault {
|
||||
continue
|
||||
if backgroundColor != tcell.ColorDefault {
|
||||
bg = backgroundColor
|
||||
}
|
||||
style = style.Background(backgroundColor)
|
||||
if textColor != tcell.ColorDefault {
|
||||
fg = textColor
|
||||
}
|
||||
if attr != 0 {
|
||||
a = attr
|
||||
}
|
||||
style = style.Background(bg).Foreground(fg) | tcell.Style(a)
|
||||
}
|
||||
screen.SetContent(fromX+bx, fromY+by, m, c, style)
|
||||
}
|
||||
@ -769,11 +796,12 @@ ColumnLoop:
|
||||
|
||||
// Color the cell backgrounds. To avoid undesirable artefacts, we combine
|
||||
// the drawing of a cell by background color, selected cells last.
|
||||
cellsByBackgroundColor := make(map[tcell.Color][]*struct {
|
||||
type cellInfo struct {
|
||||
x, y, w, h int
|
||||
text tcell.Color
|
||||
selected bool
|
||||
})
|
||||
}
|
||||
cellsByBackgroundColor := make(map[tcell.Color][]*cellInfo)
|
||||
var backgroundColors []tcell.Color
|
||||
for rowY, row := range rows {
|
||||
columnX := 0
|
||||
@ -793,11 +821,7 @@ ColumnLoop:
|
||||
columnSelected := t.columnsSelectable && !t.rowsSelectable && column == t.selectedColumn
|
||||
cellSelected := !cell.NotSelectable && (columnSelected || rowSelected || t.rowsSelectable && t.columnsSelectable && column == t.selectedColumn && row == t.selectedRow)
|
||||
entries, ok := cellsByBackgroundColor[cell.BackgroundColor]
|
||||
cellsByBackgroundColor[cell.BackgroundColor] = append(entries, &struct {
|
||||
x, y, w, h int
|
||||
text tcell.Color
|
||||
selected bool
|
||||
}{
|
||||
cellsByBackgroundColor[cell.BackgroundColor] = append(entries, &cellInfo{
|
||||
x: bx,
|
||||
y: by,
|
||||
w: bw,
|
||||
@ -821,13 +845,18 @@ ColumnLoop:
|
||||
_, _, lj := c.Hcl()
|
||||
return li < lj
|
||||
})
|
||||
selFg, selBg, selAttr := t.selectedStyle.Decompose()
|
||||
for _, bgColor := range backgroundColors {
|
||||
entries := cellsByBackgroundColor[bgColor]
|
||||
for _, cell := range entries {
|
||||
if cell.selected {
|
||||
defer colorBackground(cell.x, cell.y, cell.w, cell.h, bgColor, cell.text, true)
|
||||
if t.selectedStyle != 0 {
|
||||
defer colorBackground(cell.x, cell.y, cell.w, cell.h, selBg, selFg, selAttr, false)
|
||||
} else {
|
||||
colorBackground(cell.x, cell.y, cell.w, cell.h, bgColor, cell.text, false)
|
||||
defer colorBackground(cell.x, cell.y, cell.w, cell.h, bgColor, cell.text, 0, true)
|
||||
}
|
||||
} else {
|
||||
colorBackground(cell.x, cell.y, cell.w, cell.h, bgColor, tcell.ColorDefault, 0, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
126
vendor/github.com/rivo/tview/textview.go
generated
vendored
126
vendor/github.com/rivo/tview/textview.go
generated
vendored
@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
@ -549,12 +548,6 @@ func (t *TextView) reindexBuffer(width int) {
|
||||
strippedStr = regionPattern.ReplaceAllString(strippedStr, "")
|
||||
}
|
||||
|
||||
// Find all escape tags in this line. Escape them.
|
||||
if t.dynamicColors || t.regions {
|
||||
escapeIndices = escapePattern.FindAllStringIndex(str, -1)
|
||||
strippedStr = escapePattern.ReplaceAllString(strippedStr, "[$1$2]")
|
||||
}
|
||||
|
||||
// We don't need the original string anymore for now.
|
||||
str = strippedStr
|
||||
|
||||
@ -810,8 +803,9 @@ func (t *TextView) Draw(screen tcell.Screen) {
|
||||
colorTags [][]string
|
||||
escapeIndices [][]int
|
||||
)
|
||||
strippedText := text
|
||||
if t.dynamicColors {
|
||||
colorTagIndices, colorTags, escapeIndices, _, _ = decomposeString(text)
|
||||
colorTagIndices, colorTags, escapeIndices, strippedText, _ = decomposeString(text)
|
||||
}
|
||||
|
||||
// Get regions.
|
||||
@ -822,8 +816,10 @@ func (t *TextView) Draw(screen tcell.Screen) {
|
||||
if t.regions {
|
||||
regionIndices = regionPattern.FindAllStringIndex(text, -1)
|
||||
regions = regionPattern.FindAllStringSubmatch(text, -1)
|
||||
strippedText = regionPattern.ReplaceAllString(strippedText, "")
|
||||
if !t.dynamicColors {
|
||||
escapeIndices = escapePattern.FindAllStringIndex(text, -1)
|
||||
strippedText = string(escapePattern.ReplaceAllString(strippedText, "[$1$2]"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -842,11 +838,26 @@ func (t *TextView) Draw(screen tcell.Screen) {
|
||||
}
|
||||
|
||||
// Print the line.
|
||||
var currentTag, currentRegion, currentEscapeTag, skipped, runeSeqWidth int
|
||||
runeSequence := make([]rune, 0, 10)
|
||||
flush := func() {
|
||||
if len(runeSequence) == 0 {
|
||||
return
|
||||
var colorPos, regionPos, escapePos, tagOffset, skipped int
|
||||
iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
// Get the color.
|
||||
if colorPos < len(colorTags) && textPos+tagOffset >= colorTagIndices[colorPos][0] && textPos+tagOffset < colorTagIndices[colorPos][1] {
|
||||
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colorTags[colorPos])
|
||||
tagOffset += colorTagIndices[colorPos][1] - colorTagIndices[colorPos][0]
|
||||
colorPos++
|
||||
}
|
||||
|
||||
// Get the region.
|
||||
if regionPos < len(regionIndices) && textPos+tagOffset >= regionIndices[regionPos][0] && textPos+tagOffset < regionIndices[regionPos][1] {
|
||||
regionID = regions[regionPos][1]
|
||||
tagOffset += regionIndices[regionPos][1] - regionIndices[regionPos][0]
|
||||
regionPos++
|
||||
}
|
||||
|
||||
// Skip the second-to-last character of an escape tag.
|
||||
if escapePos < len(escapeIndices) && textPos+tagOffset == escapeIndices[escapePos][1]-2 {
|
||||
tagOffset++
|
||||
escapePos++
|
||||
}
|
||||
|
||||
// Mix the existing style with the new style.
|
||||
@ -876,87 +887,30 @@ func (t *TextView) Draw(screen tcell.Screen) {
|
||||
style = style.Background(fg).Foreground(bg)
|
||||
}
|
||||
|
||||
// Draw the character.
|
||||
var comb []rune
|
||||
if len(runeSequence) > 1 && !unicode.IsControl(runeSequence[1]) {
|
||||
// Allocate space for the combining characters only when necessary.
|
||||
comb = make([]rune, len(runeSequence)-1)
|
||||
copy(comb, runeSequence[1:])
|
||||
}
|
||||
for offset := 0; offset < runeSeqWidth; offset++ {
|
||||
screen.SetContent(x+posX+offset, y+line-t.lineOffset, runeSequence[0], comb, style)
|
||||
}
|
||||
|
||||
// Advance.
|
||||
posX += runeSeqWidth
|
||||
runeSequence = runeSequence[:0]
|
||||
runeSeqWidth = 0
|
||||
}
|
||||
for pos, ch := range text {
|
||||
// Get the color.
|
||||
if currentTag < len(colorTags) && pos >= colorTagIndices[currentTag][0] && pos < colorTagIndices[currentTag][1] {
|
||||
flush()
|
||||
if pos == colorTagIndices[currentTag][1]-1 {
|
||||
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colorTags[currentTag])
|
||||
currentTag++
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the region.
|
||||
if currentRegion < len(regionIndices) && pos >= regionIndices[currentRegion][0] && pos < regionIndices[currentRegion][1] {
|
||||
flush()
|
||||
if pos == regionIndices[currentRegion][1]-1 {
|
||||
regionID = regions[currentRegion][1]
|
||||
currentRegion++
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip the second-to-last character of an escape tag.
|
||||
if currentEscapeTag < len(escapeIndices) && pos >= escapeIndices[currentEscapeTag][0] && pos < escapeIndices[currentEscapeTag][1] {
|
||||
flush()
|
||||
if pos == escapeIndices[currentEscapeTag][1]-1 {
|
||||
currentEscapeTag++
|
||||
} else if pos == escapeIndices[currentEscapeTag][1]-2 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the width of this rune.
|
||||
chWidth := runewidth.RuneWidth(ch)
|
||||
if chWidth == 0 {
|
||||
// If this is not a modifier, we treat it as a space character.
|
||||
if len(runeSequence) == 0 {
|
||||
ch = ' '
|
||||
chWidth = 1
|
||||
} else {
|
||||
runeSequence = append(runeSequence, ch)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Skip to the right.
|
||||
if !t.wrap && skipped < skip {
|
||||
skipped += chWidth
|
||||
continue
|
||||
skipped += screenWidth
|
||||
return false
|
||||
}
|
||||
|
||||
// Stop at the right border.
|
||||
if posX+runeSeqWidth+chWidth > width {
|
||||
break
|
||||
if posX+screenWidth >= width {
|
||||
return true
|
||||
}
|
||||
|
||||
// Flush the rune sequence.
|
||||
flush()
|
||||
// Draw the character.
|
||||
for offset := screenWidth - 1; offset >= 0; offset-- {
|
||||
if offset == 0 {
|
||||
screen.SetContent(x+posX+offset, y+line-t.lineOffset, main, comb, style)
|
||||
} else {
|
||||
screen.SetContent(x+posX+offset, y+line-t.lineOffset, ' ', nil, style)
|
||||
}
|
||||
}
|
||||
|
||||
// Queue this rune.
|
||||
runeSequence = append(runeSequence, ch)
|
||||
runeSeqWidth += chWidth
|
||||
}
|
||||
if posX+runeSeqWidth <= width {
|
||||
flush()
|
||||
}
|
||||
// Advance.
|
||||
posX += screenWidth
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// If this view is not scrollable, we'll purge the buffer of lines that have
|
||||
|
528
vendor/github.com/rivo/tview/util.go
generated
vendored
528
vendor/github.com/rivo/tview/util.go
generated
vendored
@ -1,11 +1,9 @@
|
||||
package tview
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
@ -25,7 +23,7 @@ var (
|
||||
regionPattern = regexp.MustCompile(`\["([a-zA-Z0-9_,;: \-\.]*)"\]`)
|
||||
escapePattern = regexp.MustCompile(`\[([a-zA-Z0-9_,;: \-\."#]+)\[(\[*)\]`)
|
||||
nonEscapePattern = regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\]`)
|
||||
boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)")
|
||||
boundaryPattern = regexp.MustCompile(`(([[:punct:]]|\n)[ \t\f\r]*|(\s+))`)
|
||||
spacePattern = regexp.MustCompile(`\s+`)
|
||||
)
|
||||
|
||||
@ -203,8 +201,8 @@ func decomposeString(text string) (colorIndices [][]int, colors [][]string, esca
|
||||
// You can change the colors and text styles mid-text by inserting a color tag.
|
||||
// See the package description for details.
|
||||
//
|
||||
// Returns the number of actual runes printed (not including color tags) and the
|
||||
// actual width used for the printed runes.
|
||||
// Returns the number of actual bytes of the text printed (including color tags)
|
||||
// and the actual width used for the printed runes.
|
||||
func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tcell.Color) (int, int) {
|
||||
return printWithStyle(screen, text, x, y, maxWidth, align, tcell.StyleDefault.Foreground(color))
|
||||
}
|
||||
@ -217,115 +215,136 @@ func printWithStyle(screen tcell.Screen, text string, x, y, maxWidth, align int,
|
||||
}
|
||||
|
||||
// Decompose the text.
|
||||
colorIndices, colors, escapeIndices, strippedText, _ := decomposeString(text)
|
||||
colorIndices, colors, escapeIndices, strippedText, strippedWidth := decomposeString(text)
|
||||
|
||||
// We deal with runes, not with bytes.
|
||||
runes := []rune(strippedText)
|
||||
|
||||
// This helper function takes positions for a substring of "runes" and returns
|
||||
// a new string corresponding to this substring, making sure printing that
|
||||
// substring will observe color tags.
|
||||
substring := func(from, to int) string {
|
||||
// We want to reduce all alignments to AlignLeft.
|
||||
if align == AlignRight {
|
||||
if strippedWidth <= maxWidth {
|
||||
// There's enough space for the entire text.
|
||||
return printWithStyle(screen, text, x+maxWidth-strippedWidth, y, maxWidth, AlignLeft, style)
|
||||
}
|
||||
// Trim characters off the beginning.
|
||||
var (
|
||||
colorPos, escapePos, runePos, startPos int
|
||||
bytes, width, colorPos, escapePos, tagOffset int
|
||||
foregroundColor, backgroundColor, attributes string
|
||||
)
|
||||
if from >= len(runes) {
|
||||
return ""
|
||||
}
|
||||
for pos := range text {
|
||||
// Handle color tags.
|
||||
if colorPos < len(colorIndices) && pos >= colorIndices[colorPos][0] && pos < colorIndices[colorPos][1] {
|
||||
if pos == colorIndices[colorPos][1]-1 {
|
||||
if runePos <= from {
|
||||
_, originalBackground, _ := style.Decompose()
|
||||
iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
// Update color/escape tag offset and style.
|
||||
if colorPos < len(colorIndices) && textPos+tagOffset >= colorIndices[colorPos][0] && textPos+tagOffset < colorIndices[colorPos][1] {
|
||||
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos])
|
||||
}
|
||||
style = overlayStyle(originalBackground, style, foregroundColor, backgroundColor, attributes)
|
||||
tagOffset += colorIndices[colorPos][1] - colorIndices[colorPos][0]
|
||||
colorPos++
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle escape tags.
|
||||
if escapePos < len(escapeIndices) && pos >= escapeIndices[escapePos][0] && pos < escapeIndices[escapePos][1] {
|
||||
if pos == escapeIndices[escapePos][1]-1 {
|
||||
if escapePos < len(escapeIndices) && textPos+tagOffset >= escapeIndices[escapePos][0] && textPos+tagOffset < escapeIndices[escapePos][1] {
|
||||
tagOffset++
|
||||
escapePos++
|
||||
} else if pos == escapeIndices[escapePos][1]-2 {
|
||||
continue
|
||||
}
|
||||
if strippedWidth-screenPos < maxWidth {
|
||||
// We chopped off enough.
|
||||
if escapePos > 0 && textPos+tagOffset-1 >= escapeIndices[escapePos-1][0] && textPos+tagOffset-1 < escapeIndices[escapePos-1][1] {
|
||||
// Unescape open escape sequences.
|
||||
escapeCharPos := escapeIndices[escapePos-1][1] - 2
|
||||
text = text[:escapeCharPos] + text[escapeCharPos+1:]
|
||||
}
|
||||
|
||||
// Check boundaries.
|
||||
if runePos == from {
|
||||
startPos = pos
|
||||
} else if runePos >= to {
|
||||
return fmt.Sprintf(`[%s:%s:%s]%s`, foregroundColor, backgroundColor, attributes, text[startPos:pos])
|
||||
// Print and return.
|
||||
bytes, width = printWithStyle(screen, text[textPos+tagOffset:], x, y, maxWidth, AlignLeft, style)
|
||||
return true
|
||||
}
|
||||
|
||||
runePos++
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`[%s:%s:%s]%s`, foregroundColor, backgroundColor, attributes, text[startPos:])
|
||||
}
|
||||
|
||||
// We want to reduce everything to AlignLeft.
|
||||
if align == AlignRight {
|
||||
width := 0
|
||||
start := len(runes)
|
||||
for index := start - 1; index >= 0; index-- {
|
||||
w := runewidth.RuneWidth(runes[index])
|
||||
if width+w > maxWidth {
|
||||
break
|
||||
}
|
||||
width += w
|
||||
start = index
|
||||
}
|
||||
for start < len(runes) && runewidth.RuneWidth(runes[start]) == 0 {
|
||||
start++
|
||||
}
|
||||
return printWithStyle(screen, substring(start, len(runes)), x+maxWidth-width, y, width, AlignLeft, style)
|
||||
return false
|
||||
})
|
||||
return bytes, width
|
||||
} else if align == AlignCenter {
|
||||
width := runewidth.StringWidth(strippedText)
|
||||
if width == maxWidth {
|
||||
if strippedWidth == maxWidth {
|
||||
// Use the exact space.
|
||||
return printWithStyle(screen, text, x, y, maxWidth, AlignLeft, style)
|
||||
} else if width < maxWidth {
|
||||
} else if strippedWidth < maxWidth {
|
||||
// We have more space than we need.
|
||||
half := (maxWidth - width) / 2
|
||||
half := (maxWidth - strippedWidth) / 2
|
||||
return printWithStyle(screen, text, x+half, y, maxWidth-half, AlignLeft, style)
|
||||
} else {
|
||||
// Chop off runes until we have a perfect fit.
|
||||
var choppedLeft, choppedRight, leftIndex, rightIndex int
|
||||
rightIndex = len(runes) - 1
|
||||
for rightIndex > leftIndex && width-choppedLeft-choppedRight > maxWidth {
|
||||
rightIndex = len(strippedText)
|
||||
for rightIndex-1 > leftIndex && strippedWidth-choppedLeft-choppedRight > maxWidth {
|
||||
if choppedLeft < choppedRight {
|
||||
leftWidth := runewidth.RuneWidth(runes[leftIndex])
|
||||
choppedLeft += leftWidth
|
||||
leftIndex++
|
||||
for leftIndex < len(runes) && leftIndex < rightIndex && runewidth.RuneWidth(runes[leftIndex]) == 0 {
|
||||
leftIndex++
|
||||
}
|
||||
// Iterate on the left by one character.
|
||||
iterateString(strippedText[leftIndex:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
choppedLeft += screenWidth
|
||||
leftIndex += textWidth
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
rightWidth := runewidth.RuneWidth(runes[rightIndex])
|
||||
choppedRight += rightWidth
|
||||
rightIndex--
|
||||
// Iterate on the right by one character.
|
||||
iterateStringReverse(strippedText[leftIndex:rightIndex], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
choppedRight += screenWidth
|
||||
rightIndex -= textWidth
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
return printWithStyle(screen, substring(leftIndex, rightIndex), x, y, maxWidth, AlignLeft, style)
|
||||
|
||||
// Add tag offsets and determine start style.
|
||||
var (
|
||||
colorPos, escapePos, tagOffset int
|
||||
foregroundColor, backgroundColor, attributes string
|
||||
)
|
||||
_, originalBackground, _ := style.Decompose()
|
||||
for index := range strippedText {
|
||||
// We only need the offset of the left index.
|
||||
if index > leftIndex {
|
||||
// We're done.
|
||||
if escapePos > 0 && leftIndex+tagOffset-1 >= escapeIndices[escapePos-1][0] && leftIndex+tagOffset-1 < escapeIndices[escapePos-1][1] {
|
||||
// Unescape open escape sequences.
|
||||
escapeCharPos := escapeIndices[escapePos-1][1] - 2
|
||||
text = text[:escapeCharPos] + text[escapeCharPos+1:]
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Update color/escape tag offset.
|
||||
if colorPos < len(colorIndices) && index+tagOffset >= colorIndices[colorPos][0] && index+tagOffset < colorIndices[colorPos][1] {
|
||||
if index <= leftIndex {
|
||||
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos])
|
||||
style = overlayStyle(originalBackground, style, foregroundColor, backgroundColor, attributes)
|
||||
}
|
||||
tagOffset += colorIndices[colorPos][1] - colorIndices[colorPos][0]
|
||||
colorPos++
|
||||
}
|
||||
if escapePos < len(escapeIndices) && index+tagOffset >= escapeIndices[escapePos][0] && index+tagOffset < escapeIndices[escapePos][1] {
|
||||
tagOffset++
|
||||
escapePos++
|
||||
}
|
||||
}
|
||||
return printWithStyle(screen, text[leftIndex+tagOffset:], x, y, maxWidth, AlignLeft, style)
|
||||
}
|
||||
}
|
||||
|
||||
// Draw text.
|
||||
drawn := 0
|
||||
drawnWidth := 0
|
||||
var (
|
||||
colorPos, escapePos int
|
||||
drawn, drawnWidth, colorPos, escapePos, tagOffset int
|
||||
foregroundColor, backgroundColor, attributes string
|
||||
)
|
||||
runeSequence := make([]rune, 0, 10)
|
||||
runeSeqWidth := 0
|
||||
flush := func() {
|
||||
if len(runeSequence) == 0 {
|
||||
return // Nothing to flush.
|
||||
iterateString(strippedText, func(main rune, comb []rune, textPos, length, screenPos, screenWidth int) bool {
|
||||
// Only continue if there is still space.
|
||||
if drawnWidth+screenWidth > maxWidth {
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle color tags.
|
||||
if colorPos < len(colorIndices) && textPos+tagOffset >= colorIndices[colorPos][0] && textPos+tagOffset < colorIndices[colorPos][1] {
|
||||
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos])
|
||||
tagOffset += colorIndices[colorPos][1] - colorIndices[colorPos][0]
|
||||
colorPos++
|
||||
}
|
||||
|
||||
// Handle scape tags.
|
||||
if escapePos < len(escapeIndices) && textPos+tagOffset >= escapeIndices[escapePos][0] && textPos+tagOffset < escapeIndices[escapePos][1] {
|
||||
if textPos+tagOffset == escapeIndices[escapePos][1]-2 {
|
||||
tagOffset++
|
||||
escapePos++
|
||||
}
|
||||
}
|
||||
|
||||
// Print the rune sequence.
|
||||
@ -333,69 +352,23 @@ func printWithStyle(screen tcell.Screen, text string, x, y, maxWidth, align int,
|
||||
_, _, finalStyle, _ := screen.GetContent(finalX, y)
|
||||
_, background, _ := finalStyle.Decompose()
|
||||
finalStyle = overlayStyle(background, style, foregroundColor, backgroundColor, attributes)
|
||||
var comb []rune
|
||||
if len(runeSequence) > 1 && !unicode.IsControl(runeSequence[1]) {
|
||||
// Allocate space for the combining characters only when necessary.
|
||||
comb = make([]rune, len(runeSequence)-1)
|
||||
copy(comb, runeSequence[1:])
|
||||
}
|
||||
for offset := 0; offset < runeSeqWidth; offset++ {
|
||||
// To avoid undesired effects, we place the same character in all cells.
|
||||
screen.SetContent(finalX+offset, y, runeSequence[0], comb, finalStyle)
|
||||
}
|
||||
|
||||
// Advance and reset.
|
||||
drawn += len(runeSequence)
|
||||
drawnWidth += runeSeqWidth
|
||||
runeSequence = runeSequence[:0]
|
||||
runeSeqWidth = 0
|
||||
}
|
||||
for pos, ch := range text {
|
||||
// Handle color tags.
|
||||
if colorPos < len(colorIndices) && pos >= colorIndices[colorPos][0] && pos < colorIndices[colorPos][1] {
|
||||
flush()
|
||||
if pos == colorIndices[colorPos][1]-1 {
|
||||
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos])
|
||||
colorPos++
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle escape tags.
|
||||
if escapePos < len(escapeIndices) && pos >= escapeIndices[escapePos][0] && pos < escapeIndices[escapePos][1] {
|
||||
flush()
|
||||
if pos == escapeIndices[escapePos][1]-1 {
|
||||
escapePos++
|
||||
} else if pos == escapeIndices[escapePos][1]-2 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have enough space for this rune.
|
||||
chWidth := runewidth.RuneWidth(ch)
|
||||
if drawnWidth+chWidth > maxWidth {
|
||||
break // No. We're done then.
|
||||
}
|
||||
|
||||
// Put this rune in the queue.
|
||||
if chWidth == 0 {
|
||||
// If this is not a modifier, we treat it as a space character.
|
||||
if len(runeSequence) == 0 {
|
||||
ch = ' '
|
||||
chWidth = 1
|
||||
}
|
||||
for offset := screenWidth - 1; offset >= 0; offset-- {
|
||||
// To avoid undesired effects, we populate all cells.
|
||||
if offset == 0 {
|
||||
screen.SetContent(finalX+offset, y, main, comb, finalStyle)
|
||||
} else {
|
||||
// We have a character. Flush all previous runes.
|
||||
flush()
|
||||
screen.SetContent(finalX+offset, y, ' ', nil, finalStyle)
|
||||
}
|
||||
runeSequence = append(runeSequence, ch)
|
||||
runeSeqWidth += chWidth
|
||||
}
|
||||
if drawnWidth+runeSeqWidth <= maxWidth {
|
||||
flush()
|
||||
}
|
||||
|
||||
return drawn, drawnWidth
|
||||
// Advance.
|
||||
drawn += length
|
||||
drawnWidth += screenWidth
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return drawn + tagOffset + len(escapeIndices), drawnWidth
|
||||
}
|
||||
|
||||
// PrintSimple prints white text to the screen at the given position.
|
||||
@ -421,102 +394,83 @@ func WordWrap(text string, width int) (lines []string) {
|
||||
colorTagIndices, _, escapeIndices, strippedText, _ := decomposeString(text)
|
||||
|
||||
// Find candidate breakpoints.
|
||||
breakPoints := boundaryPattern.FindAllStringIndex(strippedText, -1)
|
||||
breakpoints := boundaryPattern.FindAllStringSubmatchIndex(strippedText, -1)
|
||||
// Results in one entry for each candidate. Each entry is an array a of
|
||||
// indices into strippedText where a[6] < 0 for newline/punctuation matches
|
||||
// and a[4] < 0 for whitespace matches.
|
||||
|
||||
// This helper function adds a new line to the result slice. The provided
|
||||
// positions are in stripped index space.
|
||||
addLine := func(from, to int) {
|
||||
// Shift indices back to original index space.
|
||||
var colorTagIndex, escapeIndex int
|
||||
for colorTagIndex < len(colorTagIndices) && to >= colorTagIndices[colorTagIndex][0] ||
|
||||
escapeIndex < len(escapeIndices) && to >= escapeIndices[escapeIndex][0] {
|
||||
past := 0
|
||||
if colorTagIndex < len(colorTagIndices) {
|
||||
tagWidth := colorTagIndices[colorTagIndex][1] - colorTagIndices[colorTagIndex][0]
|
||||
if colorTagIndices[colorTagIndex][0] < from {
|
||||
from += tagWidth
|
||||
to += tagWidth
|
||||
colorTagIndex++
|
||||
} else if colorTagIndices[colorTagIndex][0] < to {
|
||||
to += tagWidth
|
||||
colorTagIndex++
|
||||
} else {
|
||||
past++
|
||||
}
|
||||
} else {
|
||||
past++
|
||||
}
|
||||
if escapeIndex < len(escapeIndices) {
|
||||
tagWidth := escapeIndices[escapeIndex][1] - escapeIndices[escapeIndex][0]
|
||||
if escapeIndices[escapeIndex][0] < from {
|
||||
from += tagWidth
|
||||
to += tagWidth
|
||||
escapeIndex++
|
||||
} else if escapeIndices[escapeIndex][0] < to {
|
||||
to += tagWidth
|
||||
escapeIndex++
|
||||
} else {
|
||||
past++
|
||||
}
|
||||
} else {
|
||||
past++
|
||||
}
|
||||
if past == 2 {
|
||||
break // All other indices are beyond the requested string.
|
||||
// Process stripped text one character at a time.
|
||||
var (
|
||||
colorPos, escapePos, breakpointPos, tagOffset int
|
||||
lastBreakpoint, lastContinuation, currentLineStart int
|
||||
lineWidth, continuationWidth int
|
||||
newlineBreakpoint bool
|
||||
)
|
||||
unescape := func(substr string, startIndex int) string {
|
||||
// A helper function to unescape escaped tags.
|
||||
for index := escapePos; index >= 0; index-- {
|
||||
if index < len(escapeIndices) && startIndex > escapeIndices[index][0] && startIndex < escapeIndices[index][1]-1 {
|
||||
pos := escapeIndices[index][1] - 2 - startIndex
|
||||
return substr[:pos] + substr[pos+1:]
|
||||
}
|
||||
}
|
||||
lines = append(lines, text[from:to])
|
||||
return substr
|
||||
}
|
||||
iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
|
||||
// Handle colour tags.
|
||||
if colorPos < len(colorTagIndices) && textPos+tagOffset >= colorTagIndices[colorPos][0] && textPos+tagOffset < colorTagIndices[colorPos][1] {
|
||||
tagOffset += colorTagIndices[colorPos][1] - colorTagIndices[colorPos][0]
|
||||
colorPos++
|
||||
}
|
||||
|
||||
// Determine final breakpoints.
|
||||
var start, lastEnd, newStart, breakPoint int
|
||||
for {
|
||||
// What's our candidate string?
|
||||
var candidate string
|
||||
if breakPoint < len(breakPoints) {
|
||||
candidate = text[start:breakPoints[breakPoint][1]]
|
||||
} else {
|
||||
candidate = text[start:]
|
||||
// Handle escape tags.
|
||||
if escapePos < len(escapeIndices) && textPos+tagOffset == escapeIndices[escapePos][1]-2 {
|
||||
tagOffset++
|
||||
escapePos++
|
||||
}
|
||||
candidate = strings.TrimRightFunc(candidate, unicode.IsSpace)
|
||||
|
||||
if runewidth.StringWidth(candidate) >= width {
|
||||
// We're past the available width.
|
||||
if lastEnd > start {
|
||||
// Use the previous candidate.
|
||||
addLine(start, lastEnd)
|
||||
start = newStart
|
||||
} else {
|
||||
// We have no previous candidate. Make a hard break.
|
||||
var lineWidth int
|
||||
for index, ch := range text {
|
||||
if index < start {
|
||||
continue
|
||||
// Check if a break is warranted.
|
||||
afterContinuation := lastContinuation > 0 && textPos+tagOffset >= lastContinuation
|
||||
noBreakpoint := lastContinuation == 0
|
||||
beyondWidth := lineWidth > 0 && lineWidth > width
|
||||
if beyondWidth && noBreakpoint {
|
||||
// We need a hard break without a breakpoint.
|
||||
lines = append(lines, unescape(text[currentLineStart:textPos+tagOffset], currentLineStart))
|
||||
currentLineStart = textPos + tagOffset
|
||||
lineWidth = continuationWidth
|
||||
} else if afterContinuation && (beyondWidth || newlineBreakpoint) {
|
||||
// Break at last breakpoint or at newline.
|
||||
lines = append(lines, unescape(text[currentLineStart:lastBreakpoint], currentLineStart))
|
||||
currentLineStart = lastContinuation
|
||||
lineWidth = continuationWidth
|
||||
lastBreakpoint, lastContinuation, newlineBreakpoint = 0, 0, false
|
||||
}
|
||||
chWidth := runewidth.RuneWidth(ch)
|
||||
if lineWidth > 0 && lineWidth+chWidth >= width {
|
||||
addLine(start, index)
|
||||
start = index
|
||||
break
|
||||
|
||||
// Is this a breakpoint?
|
||||
if breakpointPos < len(breakpoints) && textPos == breakpoints[breakpointPos][0] {
|
||||
// Yes, it is. Set up breakpoint infos depending on its type.
|
||||
lastBreakpoint = breakpoints[breakpointPos][0] + tagOffset
|
||||
lastContinuation = breakpoints[breakpointPos][1] + tagOffset
|
||||
newlineBreakpoint = main == '\n'
|
||||
if breakpoints[breakpointPos][6] < 0 && !newlineBreakpoint {
|
||||
lastBreakpoint++ // Don't skip punctuation.
|
||||
}
|
||||
lineWidth += chWidth
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We haven't hit the right border yet.
|
||||
if breakPoint >= len(breakPoints) {
|
||||
// It's the last line. We're done.
|
||||
if len(candidate) > 0 {
|
||||
addLine(start, len(strippedText))
|
||||
}
|
||||
break
|
||||
} else {
|
||||
// We have a new candidate.
|
||||
lastEnd = start + len(candidate)
|
||||
newStart = breakPoints[breakPoint][1]
|
||||
breakPoint++
|
||||
breakpointPos++
|
||||
}
|
||||
|
||||
// Once we hit the continuation point, we start buffering widths.
|
||||
if textPos+tagOffset < lastContinuation {
|
||||
continuationWidth = 0
|
||||
}
|
||||
|
||||
lineWidth += screenWidth
|
||||
continuationWidth += screenWidth
|
||||
return false
|
||||
})
|
||||
|
||||
// Flush the rest.
|
||||
if currentLineStart < len(text) {
|
||||
lines = append(lines, unescape(text[currentLineStart:], currentLineStart))
|
||||
}
|
||||
|
||||
return
|
||||
@ -531,3 +485,121 @@ func WordWrap(text string, width int) (lines []string) {
|
||||
func Escape(text string) string {
|
||||
return nonEscapePattern.ReplaceAllString(text, "$1[]")
|
||||
}
|
||||
|
||||
// iterateString iterates through the given string one printed character at a
|
||||
// time. For each such character, the callback function is called with the
|
||||
// Unicode code points of the character (the first rune and any combining runes
|
||||
// which may be nil if there aren't any), the starting position (in bytes)
|
||||
// within the original string, its length in bytes, the screen position of the
|
||||
// character, and the screen width of it. The iteration stops if the callback
|
||||
// returns true. This function returns true if the iteration was stopped before
|
||||
// the last character.
|
||||
func iterateString(text string, callback func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool) bool {
|
||||
var (
|
||||
runes []rune
|
||||
lastZeroWidthJoiner bool
|
||||
startIndex int
|
||||
startPos int
|
||||
pos int
|
||||
)
|
||||
|
||||
// Helper function which invokes the callback.
|
||||
flush := func(index int) bool {
|
||||
var comb []rune
|
||||
if len(runes) > 1 {
|
||||
comb = runes[1:]
|
||||
}
|
||||
return callback(runes[0], comb, startIndex, index-startIndex, startPos, pos-startPos)
|
||||
}
|
||||
|
||||
for index, r := range text {
|
||||
if unicode.In(r, unicode.Lm, unicode.M) || r == '\u200d' {
|
||||
lastZeroWidthJoiner = r == '\u200d'
|
||||
} else {
|
||||
// We have a rune that's not a modifier. It could be the beginning of a
|
||||
// new character.
|
||||
if !lastZeroWidthJoiner {
|
||||
if len(runes) > 0 {
|
||||
// It is. Invoke callback.
|
||||
if flush(index) {
|
||||
return true // We're done.
|
||||
}
|
||||
// Reset rune store.
|
||||
runes = runes[:0]
|
||||
startIndex = index
|
||||
startPos = pos
|
||||
}
|
||||
pos += runewidth.RuneWidth(r)
|
||||
} else {
|
||||
lastZeroWidthJoiner = false
|
||||
}
|
||||
}
|
||||
runes = append(runes, r)
|
||||
}
|
||||
|
||||
// Flush any remaining runes.
|
||||
if len(runes) > 0 {
|
||||
flush(len(text))
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// iterateStringReverse iterates through the given string in reverse, starting
|
||||
// from the end of the string, one printed character at a time. For each such
|
||||
// character, the callback function is called with the Unicode code points of
|
||||
// the character (the first rune and any combining runes which may be nil if
|
||||
// there aren't any), the starting position (in bytes) within the original
|
||||
// string, its length in bytes, the screen position of the character, and the
|
||||
// screen width of it. The iteration stops if the callback returns true. This
|
||||
// function returns true if the iteration was stopped before the last character.
|
||||
func iterateStringReverse(text string, callback func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool) bool {
|
||||
type runePos struct {
|
||||
r rune
|
||||
pos int // The byte position of the rune in the original string.
|
||||
width int // The screen width of the rune.
|
||||
mod bool // Modifier or zero-width-joiner.
|
||||
}
|
||||
|
||||
// We use the following:
|
||||
// len(text) >= number of runes in text.
|
||||
|
||||
// Put all runes into a runePos slice in reverse.
|
||||
runesReverse := make([]runePos, len(text))
|
||||
index := len(text) - 1
|
||||
for pos, ch := range text {
|
||||
runesReverse[index].r = ch
|
||||
runesReverse[index].pos = pos
|
||||
runesReverse[index].width = runewidth.RuneWidth(ch)
|
||||
runesReverse[index].mod = unicode.In(ch, unicode.Lm, unicode.M) || ch == '\u200d'
|
||||
index--
|
||||
}
|
||||
runesReverse = runesReverse[index+1:]
|
||||
|
||||
// Parse reverse runes.
|
||||
var screenWidth int
|
||||
buffer := make([]rune, len(text)) // We fill this up from the back so it's forward again.
|
||||
bufferPos := len(text)
|
||||
stringWidth := runewidth.StringWidth(text)
|
||||
for index, r := range runesReverse {
|
||||
// Put this rune into the buffer.
|
||||
bufferPos--
|
||||
buffer[bufferPos] = r.r
|
||||
|
||||
// Do we need to flush the buffer?
|
||||
if r.pos == 0 || !r.mod && runesReverse[index+1].r != '\u200d' {
|
||||
// Yes, invoke callback.
|
||||
var comb []rune
|
||||
if len(text)-bufferPos > 1 {
|
||||
comb = buffer[bufferPos+1:]
|
||||
}
|
||||
if callback(r.r, comb, r.pos, len(text)-r.pos, stringWidth-screenWidth, r.width) {
|
||||
return true
|
||||
}
|
||||
screenWidth += r.width
|
||||
bufferPos = len(text)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
674
vendor/github.com/sticreations/spotigopher/LICENCE.md
generated
vendored
Normal file
674
vendor/github.com/sticreations/spotigopher/LICENCE.md
generated
vendored
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
89
vendor/github.com/sticreations/spotigopher/spotigopher/spotigopher.go
generated
vendored
89
vendor/github.com/sticreations/spotigopher/spotigopher/spotigopher.go
generated
vendored
@ -3,6 +3,10 @@ package spotigopher
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
@ -51,29 +55,44 @@ func getSpotifyBus() dbus.BusObject {
|
||||
PlayPause sends a PlayPause Command on the DBus
|
||||
*/
|
||||
func (s *SpotifyClient) PlayPause() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
sendDarwinAction("playpause")
|
||||
} else {
|
||||
sendAction("org.mpris.MediaPlayer2.Player.PlayPause")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Next sends a Next Command on the DBus
|
||||
*/
|
||||
func (s *SpotifyClient) Next() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
sendDarwinAction("next")
|
||||
} else {
|
||||
sendAction("org.mpris.MediaPlayer2.Player.Next")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Previous sends a Previous Command on the DBus
|
||||
*/
|
||||
func (s *SpotifyClient) Previous() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
sendDarwinAction("previous")
|
||||
} else {
|
||||
sendAction("org.mpris.MediaPlayer2.Player.Previous")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Stop sends a Stop Command on the DBus
|
||||
*/
|
||||
func (s *SpotifyClient) Stop() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
sendDarwinAction("stop")
|
||||
} else {
|
||||
sendAction("org.mpris.MediaPlayer2.Player.Stop")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -82,6 +101,7 @@ GetInfo returns all Spotify related Information, when Spotify is running
|
||||
func (s *SpotifyClient) GetInfo() (Info, error) {
|
||||
info := Info{}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
spotifyBus := getSpotifyBus()
|
||||
props, err := spotifyBus.GetProperty("org.mpris.MediaPlayer2.Player.Metadata")
|
||||
if err != nil {
|
||||
@ -102,9 +122,76 @@ func (s *SpotifyClient) GetInfo() (Info, error) {
|
||||
|
||||
}
|
||||
info.Status = status.Value().(string)
|
||||
} else if runtime.GOOS == "darwin" {
|
||||
info.TrackID = getDarwinInfo("trackid")
|
||||
info.Artist = strings.Split(getDarwinInfo("artist"), ",")
|
||||
info.Album = getDarwinInfo("album")
|
||||
info.Title = getDarwinInfo("track")
|
||||
tmpTrackNumber, err := strconv.ParseInt(getDarwinInfo("tracknumber"), 0, 32)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not get track number: %s", err)
|
||||
}
|
||||
info.TrackNumber = int32(tmpTrackNumber)
|
||||
info.URL = getDarwinInfo("url")
|
||||
info.ArtworkURL = getDarwinInfo("artworkurl")
|
||||
info.Status = getDarwinInfo("status")
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func getDarwinInfo(infoType string) string {
|
||||
args := []string{`-etell application "Spotify" to name of current track as string`}
|
||||
switch infoType {
|
||||
case "trackid":
|
||||
args = []string{`-etell application "Spotify" to id of current track as string`}
|
||||
case "artist":
|
||||
args = []string{`-etell application "Spotify" to artist of current track as string`}
|
||||
case "title":
|
||||
args = []string{`-etell application "Spotify" to name of current track as string`}
|
||||
case "album":
|
||||
args = []string{`-etell application "Spotify" to album of current track as string`}
|
||||
case "tracknumber":
|
||||
args = []string{`-etell application "Spotify" to track number of current track as string`}
|
||||
case "url":
|
||||
args = []string{`-etell application "Spotify" to spotify url of current track as string`}
|
||||
case "artworkurl":
|
||||
args = []string{`-etell application "Spotify" to artwork url of current track as string`}
|
||||
case "status":
|
||||
args = []string{`-etell application "Spotify" to player state as string`}
|
||||
default:
|
||||
args = []string{`-etell application "Spotify" to name of current track as string`}
|
||||
}
|
||||
info, err := exec.Command("osascript", args...).Output()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not get info: %s", infoType)
|
||||
}
|
||||
return strings.Trim(string(info), "\n")
|
||||
}
|
||||
|
||||
func sendDarwinAction(action string) {
|
||||
args := []string{`-etell application "Spotify" to pause`}
|
||||
switch action {
|
||||
case "play":
|
||||
args = []string{`-etell application "Spotify" to play`}
|
||||
case "pause":
|
||||
args = []string{`-etell application "Spotify" to pause`}
|
||||
case "stop":
|
||||
args = []string{`-etell application "Spotify" to pause`}
|
||||
case "playpause":
|
||||
args = []string{`-etell application "Spotify" to playpause`}
|
||||
case "next":
|
||||
args = []string{`-etell application "Spotify" to next track`}
|
||||
case "previous":
|
||||
args = []string{`-etell application "Spotify" to previous track`}
|
||||
default:
|
||||
args = []string{`-etell application "Spotify" to pause`}
|
||||
}
|
||||
err := exec.Command("osascript", args...).Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not completed action: %s", action)
|
||||
}
|
||||
}
|
||||
|
||||
func sendAction(method string) error {
|
||||
sdbus := getSpotifyBus()
|
||||
call := sdbus.Call(method, 0)
|
||||
|
4
vendor/github.com/xanzy/go-gitlab/.travis.yml
generated
vendored
4
vendor/github.com/xanzy/go-gitlab/.travis.yml
generated
vendored
@ -1,9 +1,9 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- master
|
||||
|
||||
stages:
|
||||
@ -14,7 +14,7 @@ jobs:
|
||||
include:
|
||||
- stage: lint
|
||||
script:
|
||||
- go get github.com/golang/lint/golint
|
||||
- go get golang.org/x/lint/golint
|
||||
- golint -set_exit_status
|
||||
- go vet -v
|
||||
- stage: test
|
||||
|
18
vendor/github.com/xanzy/go-gitlab/README.md
generated
vendored
18
vendor/github.com/xanzy/go-gitlab/README.md
generated
vendored
@ -26,7 +26,7 @@ to add new and/or missing endpoints. Currently the following services are suppor
|
||||
- [x] Project-level Variables
|
||||
- [x] Group-level Variables
|
||||
- [x] Commits
|
||||
- [ ] Custom Attributes
|
||||
- [x] Custom Attributes
|
||||
- [x] Deployments
|
||||
- [x] Deploy Keys
|
||||
- [x] Environments
|
||||
@ -36,9 +36,9 @@ to add new and/or missing endpoints. Currently the following services are suppor
|
||||
- [x] Feature flags
|
||||
- [ ] Geo Nodes
|
||||
- [x] Gitignores templates
|
||||
- [ ] GitLab CI Config templates
|
||||
- [x] GitLab CI Config templates
|
||||
- [x] Groups
|
||||
- [ ] Group Access Requests
|
||||
- [x] Group Access Requests
|
||||
- [x] Group Members
|
||||
- [x] Issues
|
||||
- [x] Issue Boards
|
||||
@ -50,19 +50,19 @@ to add new and/or missing endpoints. Currently the following services are suppor
|
||||
- [x] Merge Requests
|
||||
- [x] Merge Request Approvals
|
||||
- [x] Project Milestones
|
||||
- [ ] Group Milestones
|
||||
- [x] Group Milestones
|
||||
- [x] Namespaces
|
||||
- [x] Notes (comments)
|
||||
- [ ] Discussions (threaded comments)
|
||||
- [x] Notification settings
|
||||
- [ ] Open source license templates
|
||||
- [x] Open source license templates
|
||||
- [x] Pages Domains
|
||||
- [x] Pipelines
|
||||
- [x] Pipeline Triggers
|
||||
- [x] Pipeline Schedules
|
||||
- [x] Projects (including setting Webhooks)
|
||||
- [ ] Project Access Requests
|
||||
- [ ] Project badges
|
||||
- [x] Project Access Requests
|
||||
- [x] Project badges
|
||||
- [ ] Project import/export
|
||||
- [x] Project Members
|
||||
- [x] Project Snippets
|
||||
@ -70,11 +70,10 @@ to add new and/or missing endpoints. Currently the following services are suppor
|
||||
- [x] Repositories
|
||||
- [x] Repository Files
|
||||
- [x] Runners
|
||||
- [ ] Search
|
||||
- [x] Search
|
||||
- [x] Services
|
||||
- [x] Settings
|
||||
- [x] Sidekiq metrics
|
||||
- [x] Session
|
||||
- [x] System Hooks
|
||||
- [x] Tags
|
||||
- [x] Todos
|
||||
@ -150,7 +149,6 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
For complete usage of go-gitlab, see the full [package docs](https://godoc.org/github.com/xanzy/go-gitlab).
|
||||
|
237
vendor/github.com/xanzy/go-gitlab/access_requests.go
generated
vendored
Normal file
237
vendor/github.com/xanzy/go-gitlab/access_requests.go
generated
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AccessRequest represents a access request for a group or project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html
|
||||
type AccessRequest struct {
|
||||
ID int `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
State string `json:"state"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
RequestedAt *time.Time `json:"requested_at"`
|
||||
AccessLevel AccessLevelValue `json:"access_level"`
|
||||
}
|
||||
|
||||
// AccessRequestsService handles communication with the project/group
|
||||
// access requests related methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/access_requests.html
|
||||
type AccessRequestsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ListAccessRequestsOptions represents the available
|
||||
// ListProjectAccessRequests() or ListGroupAccessRequests() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html#list-access-requests-for-a-group-or-project
|
||||
type ListAccessRequestsOptions ListOptions
|
||||
|
||||
// ListProjectAccessRequests gets a list of access requests
|
||||
// viewable by the authenticated user.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html#list-access-requests-for-a-group-or-project
|
||||
func (s *AccessRequestsService) ListProjectAccessRequests(pid interface{}, opt *ListAccessRequestsOptions, options ...OptionFunc) ([]*AccessRequest, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/access_requests", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var ars []*AccessRequest
|
||||
resp, err := s.client.Do(req, &ars)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return ars, resp, err
|
||||
}
|
||||
|
||||
// ListGroupAccessRequests gets a list of access requests
|
||||
// viewable by the authenticated user.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html#list-access-requests-for-a-group-or-project
|
||||
func (s *AccessRequestsService) ListGroupAccessRequests(gid interface{}, opt *ListAccessRequestsOptions, options ...OptionFunc) ([]*AccessRequest, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/access_requests", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var ars []*AccessRequest
|
||||
resp, err := s.client.Do(req, &ars)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return ars, resp, err
|
||||
}
|
||||
|
||||
// RequestProjectAccess requests access for the authenticated user
|
||||
// to a group or project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html#request-access-to-a-group-or-project
|
||||
func (s *AccessRequestsService) RequestProjectAccess(pid interface{}, options ...OptionFunc) (*AccessRequest, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/access_requests", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ar := new(AccessRequest)
|
||||
resp, err := s.client.Do(req, ar)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return ar, resp, err
|
||||
}
|
||||
|
||||
// RequestGroupAccess requests access for the authenticated user
|
||||
// to a group or project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html#request-access-to-a-group-or-project
|
||||
func (s *AccessRequestsService) RequestGroupAccess(gid interface{}, options ...OptionFunc) (*AccessRequest, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/access_requests", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ar := new(AccessRequest)
|
||||
resp, err := s.client.Do(req, ar)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return ar, resp, err
|
||||
}
|
||||
|
||||
// ApproveAccessRequestOptions represents the available
|
||||
// ApproveProjectAccessRequest() and ApproveGroupAccessRequest() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html#approve-an-access-request
|
||||
type ApproveAccessRequestOptions struct {
|
||||
AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
|
||||
}
|
||||
|
||||
// ApproveProjectAccessRequest approves an access request for the given user.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html#approve-an-access-request
|
||||
func (s *AccessRequestsService) ApproveProjectAccessRequest(pid interface{}, user int, opt *ApproveAccessRequestOptions, options ...OptionFunc) (*AccessRequest, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/access_requests/%d/approve", url.QueryEscape(project), user)
|
||||
|
||||
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ar := new(AccessRequest)
|
||||
resp, err := s.client.Do(req, ar)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return ar, resp, err
|
||||
}
|
||||
|
||||
// ApproveGroupAccessRequest approves an access request for the given user.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html#approve-an-access-request
|
||||
func (s *AccessRequestsService) ApproveGroupAccessRequest(gid interface{}, user int, opt *ApproveAccessRequestOptions, options ...OptionFunc) (*AccessRequest, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/access_requests/%d/approve", url.QueryEscape(group), user)
|
||||
|
||||
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ar := new(AccessRequest)
|
||||
resp, err := s.client.Do(req, ar)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return ar, resp, err
|
||||
}
|
||||
|
||||
// DenyProjectAccessRequest denies an access request for the given user.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html#deny-an-access-request
|
||||
func (s *AccessRequestsService) DenyProjectAccessRequest(pid interface{}, user int, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/access_requests/%d", url.QueryEscape(project), user)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// DenyGroupAccessRequest denies an access request for the given user.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/access_requests.html#deny-an-access-request
|
||||
func (s *AccessRequestsService) DenyGroupAccessRequest(gid interface{}, user int, options ...OptionFunc) (*Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/access_requests/%d", url.QueryEscape(group), user)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
70
vendor/github.com/xanzy/go-gitlab/ci_yml_templates.go
generated
vendored
Normal file
70
vendor/github.com/xanzy/go-gitlab/ci_yml_templates.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// CIYMLTemplatesService handles communication with the gitlab
|
||||
// CI YML templates related methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html
|
||||
type CIYMLTemplatesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// CIYMLTemplate represents a GitLab CI YML template.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html
|
||||
type CIYMLTemplate struct {
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// ListCIYMLTemplatesOptions represents the available ListAllTemplates() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/gitignores.html#list-gitignore-templates
|
||||
type ListCIYMLTemplatesOptions ListOptions
|
||||
|
||||
// ListAllTemplates get all GitLab CI YML templates.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html#list-gitlab-ci-yml-templates
|
||||
func (s *CIYMLTemplatesService) ListAllTemplates(opt *ListCIYMLTemplatesOptions, options ...OptionFunc) ([]*CIYMLTemplate, *Response, error) {
|
||||
req, err := s.client.NewRequest("GET", "templates/gitlab_ci_ymls", opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var cts []*CIYMLTemplate
|
||||
resp, err := s.client.Do(req, &cts)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return cts, resp, err
|
||||
}
|
||||
|
||||
// GetTemplate get a single GitLab CI YML template.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html#single-gitlab-ci-yml-template
|
||||
func (s *CIYMLTemplatesService) GetTemplate(key string, options ...OptionFunc) (*CIYMLTemplate, *Response, error) {
|
||||
u := fmt.Sprintf("templates/gitlab_ci_ymls/%s", url.QueryEscape(key))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ct := new(CIYMLTemplate)
|
||||
resp, err := s.client.Do(req, ct)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return ct, resp, err
|
||||
}
|
171
vendor/github.com/xanzy/go-gitlab/custom_attributes.go
generated
vendored
Normal file
171
vendor/github.com/xanzy/go-gitlab/custom_attributes.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CustomAttributesService handles communication with the group, project and
|
||||
// user custom attributes related methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/custom_attributes.html
|
||||
type CustomAttributesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// CustomAttribute struct is used to unmarshal response to api calls.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/custom_attributes.html
|
||||
type CustomAttribute struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// ListCustomUserAttributes lists the custom attributes of the specified user.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes
|
||||
func (s *CustomAttributesService) ListCustomUserAttributes(user int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) {
|
||||
return s.listCustomAttributes("users", user, options...)
|
||||
}
|
||||
|
||||
// ListCustomGroupAttributes lists the custom attributes of the specified group.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes
|
||||
func (s *CustomAttributesService) ListCustomGroupAttributes(group int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) {
|
||||
return s.listCustomAttributes("groups", group, options...)
|
||||
}
|
||||
|
||||
// ListCustomProjectAttributes lists the custom attributes of the specified project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes
|
||||
func (s *CustomAttributesService) ListCustomProjectAttributes(project int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) {
|
||||
return s.listCustomAttributes("projects", project, options...)
|
||||
}
|
||||
|
||||
func (s *CustomAttributesService) listCustomAttributes(resource string, id int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) {
|
||||
u := fmt.Sprintf("%s/%d/custom_attributes", resource, id)
|
||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var cas []*CustomAttribute
|
||||
resp, err := s.client.Do(req, &cas)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return cas, resp, err
|
||||
}
|
||||
|
||||
// GetCustomUserAttribute returns the user attribute with a speciifc key.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute
|
||||
func (s *CustomAttributesService) GetCustomUserAttribute(user int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
||||
return s.getCustomAttribute("users", user, key, options...)
|
||||
}
|
||||
|
||||
// GetCustomGroupAttribute returns the group attribute with a speciifc key.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute
|
||||
func (s *CustomAttributesService) GetCustomGroupAttribute(group int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
||||
return s.getCustomAttribute("groups", group, key, options...)
|
||||
}
|
||||
|
||||
// GetCustomProjectAttribute returns the project attribute with a speciifc key.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute
|
||||
func (s *CustomAttributesService) GetCustomProjectAttribute(project int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
||||
return s.getCustomAttribute("projects", project, key, options...)
|
||||
}
|
||||
|
||||
func (s *CustomAttributesService) getCustomAttribute(resource string, id int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
||||
u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, key)
|
||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var ca *CustomAttribute
|
||||
resp, err := s.client.Do(req, &ca)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ca, resp, err
|
||||
}
|
||||
|
||||
// SetCustomUserAttribute sets the custom attributes of the specified user.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute
|
||||
func (s *CustomAttributesService) SetCustomUserAttribute(user int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
||||
return s.setCustomAttribute("users", user, c, options...)
|
||||
}
|
||||
|
||||
// SetCustomGroupAttribute sets the custom attributes of the specified group.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute
|
||||
func (s *CustomAttributesService) SetCustomGroupAttribute(group int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
||||
return s.setCustomAttribute("groups", group, c, options...)
|
||||
}
|
||||
|
||||
// SetCustomProjectAttribute sets the custom attributes of the specified project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute
|
||||
func (s *CustomAttributesService) SetCustomProjectAttribute(project int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
||||
return s.setCustomAttribute("projects", project, c, options...)
|
||||
}
|
||||
|
||||
func (s *CustomAttributesService) setCustomAttribute(resource string, id int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) {
|
||||
u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, c.Key)
|
||||
req, err := s.client.NewRequest("PUT", u, c, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ca := new(CustomAttribute)
|
||||
resp, err := s.client.Do(req, ca)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ca, resp, err
|
||||
}
|
||||
|
||||
// DeleteCustomUserAttribute removes the custom attribute of the specified user.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute
|
||||
func (s *CustomAttributesService) DeleteCustomUserAttribute(user int, key string, options ...OptionFunc) (*Response, error) {
|
||||
return s.deleteCustomAttribute("users", user, key, options...)
|
||||
}
|
||||
|
||||
// DeleteCustomGroupAttribute removes the custom attribute of the specified group.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute
|
||||
func (s *CustomAttributesService) DeleteCustomGroupAttribute(group int, key string, options ...OptionFunc) (*Response, error) {
|
||||
return s.deleteCustomAttribute("groups", group, key, options...)
|
||||
}
|
||||
|
||||
// DeleteCustomProjectAttribute removes the custom attribute of the specified project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute
|
||||
func (s *CustomAttributesService) DeleteCustomProjectAttribute(project int, key string, options ...OptionFunc) (*Response, error) {
|
||||
return s.deleteCustomAttribute("projects", project, key, options...)
|
||||
}
|
||||
|
||||
func (s *CustomAttributesService) deleteCustomAttribute(resource string, id int, key string, options ...OptionFunc) (*Response, error) {
|
||||
u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, key)
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.client.Do(req, nil)
|
||||
}
|
1112
vendor/github.com/xanzy/go-gitlab/discussions.go
generated
vendored
Normal file
1112
vendor/github.com/xanzy/go-gitlab/discussions.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
19
vendor/github.com/xanzy/go-gitlab/event_types.go
generated
vendored
19
vendor/github.com/xanzy/go-gitlab/event_types.go
generated
vendored
@ -477,6 +477,7 @@ type MergeEvent struct {
|
||||
WorkInProgress bool `json:"work_in_progress"`
|
||||
URL string `json:"url"`
|
||||
Action string `json:"action"`
|
||||
OldRev string `json:"oldrev"`
|
||||
Assignee struct {
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
@ -489,6 +490,24 @@ type MergeEvent struct {
|
||||
Username string `json:"username"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
} `json:"assignee"`
|
||||
Changes struct {
|
||||
AssigneeID struct {
|
||||
Previous int `json:"previous"`
|
||||
Current int `json:"current"`
|
||||
} `json:"assignee_id"`
|
||||
Description struct {
|
||||
Previous string `json:"previous"`
|
||||
Current string `json:"current"`
|
||||
} `json:"description"`
|
||||
Labels struct {
|
||||
Previous []Label `json:"previous"`
|
||||
Current []Label `json:"current"`
|
||||
} `json:"labels"`
|
||||
UpdatedByID struct {
|
||||
Previous int `json:"previous"`
|
||||
Current int `json:"current"`
|
||||
} `json:"updated_by_id"`
|
||||
} `json:"changes"`
|
||||
}
|
||||
|
||||
// WikiPageEvent represents a wiki page event.
|
||||
|
51
vendor/github.com/xanzy/go-gitlab/gitlab.go
generated
vendored
51
vendor/github.com/xanzy/go-gitlab/gitlab.go
generated
vendored
@ -68,6 +68,10 @@ const (
|
||||
GuestPermissions AccessLevelValue = 10
|
||||
ReporterPermissions AccessLevelValue = 20
|
||||
DeveloperPermissions AccessLevelValue = 30
|
||||
MaintainerPermissions AccessLevelValue = 40
|
||||
OwnerPermissions AccessLevelValue = 50
|
||||
|
||||
// These are deprecated and should be removed in a future version
|
||||
MasterPermissions AccessLevelValue = 40
|
||||
OwnerPermission AccessLevelValue = 50
|
||||
)
|
||||
@ -273,13 +277,17 @@ type Client struct {
|
||||
UserAgent string
|
||||
|
||||
// Services used for talking to different parts of the GitLab API.
|
||||
AccessRequests *AccessRequestsService
|
||||
AwardEmoji *AwardEmojiService
|
||||
Branches *BranchesService
|
||||
BuildVariables *BuildVariablesService
|
||||
BroadcastMessage *BroadcastMessagesService
|
||||
CIYMLTemplate *CIYMLTemplatesService
|
||||
Commits *CommitsService
|
||||
CustomAttribute *CustomAttributesService
|
||||
DeployKeys *DeployKeysService
|
||||
Deployments *DeploymentsService
|
||||
Discussions *DiscussionsService
|
||||
Environments *EnvironmentsService
|
||||
Events *EventsService
|
||||
Features *FeaturesService
|
||||
@ -295,6 +303,7 @@ type Client struct {
|
||||
Keys *KeysService
|
||||
Boards *IssueBoardsService
|
||||
Labels *LabelsService
|
||||
LicenseTemplates *LicenseTemplatesService
|
||||
MergeRequests *MergeRequestsService
|
||||
MergeRequestApprovals *MergeRequestApprovalsService
|
||||
Milestones *MilestonesService
|
||||
@ -307,6 +316,7 @@ type Client struct {
|
||||
PipelineTriggers *PipelineTriggersService
|
||||
Projects *ProjectsService
|
||||
ProjectMembers *ProjectMembersService
|
||||
ProjectBadges *ProjectBadgesService
|
||||
ProjectSnippets *ProjectSnippetsService
|
||||
ProjectVariables *ProjectVariablesService
|
||||
ProtectedBranches *ProtectedBranchesService
|
||||
@ -315,7 +325,6 @@ type Client struct {
|
||||
Runners *RunnersService
|
||||
Search *SearchService
|
||||
Services *ServicesService
|
||||
Session *SessionService
|
||||
Settings *SettingsService
|
||||
Sidekiq *SidekiqService
|
||||
Snippets *SnippetsService
|
||||
@ -407,13 +416,17 @@ func newClient(httpClient *http.Client) *Client {
|
||||
timeStats := &timeStatsService{client: c}
|
||||
|
||||
// Create all the public services.
|
||||
c.AccessRequests = &AccessRequestsService{client: c}
|
||||
c.AwardEmoji = &AwardEmojiService{client: c}
|
||||
c.Branches = &BranchesService{client: c}
|
||||
c.BuildVariables = &BuildVariablesService{client: c}
|
||||
c.BroadcastMessage = &BroadcastMessagesService{client: c}
|
||||
c.CIYMLTemplate = &CIYMLTemplatesService{client: c}
|
||||
c.Commits = &CommitsService{client: c}
|
||||
c.CustomAttribute = &CustomAttributesService{client: c}
|
||||
c.DeployKeys = &DeployKeysService{client: c}
|
||||
c.Deployments = &DeploymentsService{client: c}
|
||||
c.Discussions = &DiscussionsService{client: c}
|
||||
c.Environments = &EnvironmentsService{client: c}
|
||||
c.Events = &EventsService{client: c}
|
||||
c.Features = &FeaturesService{client: c}
|
||||
@ -429,6 +442,7 @@ func newClient(httpClient *http.Client) *Client {
|
||||
c.Keys = &KeysService{client: c}
|
||||
c.Boards = &IssueBoardsService{client: c}
|
||||
c.Labels = &LabelsService{client: c}
|
||||
c.LicenseTemplates = &LicenseTemplatesService{client: c}
|
||||
c.MergeRequests = &MergeRequestsService{client: c, timeStats: timeStats}
|
||||
c.MergeRequestApprovals = &MergeRequestApprovalsService{client: c}
|
||||
c.Milestones = &MilestonesService{client: c}
|
||||
@ -441,6 +455,7 @@ func newClient(httpClient *http.Client) *Client {
|
||||
c.PipelineTriggers = &PipelineTriggersService{client: c}
|
||||
c.Projects = &ProjectsService{client: c}
|
||||
c.ProjectMembers = &ProjectMembersService{client: c}
|
||||
c.ProjectBadges = &ProjectBadgesService{client: c}
|
||||
c.ProjectSnippets = &ProjectSnippetsService{client: c}
|
||||
c.ProjectVariables = &ProjectVariablesService{client: c}
|
||||
c.ProtectedBranches = &ProtectedBranchesService{client: c}
|
||||
@ -449,7 +464,6 @@ func newClient(httpClient *http.Client) *Client {
|
||||
c.Runners = &RunnersService{client: c}
|
||||
c.Services = &ServicesService{client: c}
|
||||
c.Search = &SearchService{client: c}
|
||||
c.Session = &SessionService{client: c}
|
||||
c.Settings = &SettingsService{client: c}
|
||||
c.Sidekiq = &SidekiqService{client: c}
|
||||
c.Snippets = &SnippetsService{client: c}
|
||||
@ -500,8 +514,14 @@ func (c *Client) SetBaseURL(urlStr string) error {
|
||||
// request body.
|
||||
func (c *Client) NewRequest(method, path string, opt interface{}, options []OptionFunc) (*http.Request, error) {
|
||||
u := *c.baseURL
|
||||
// Set the encoded opaque data
|
||||
u.Opaque = c.baseURL.Path + path
|
||||
unescaped, err := url.PathUnescape(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set the encoded path data
|
||||
u.RawPath = c.baseURL.Path + path
|
||||
u.Path = c.baseURL.Path + unescaped
|
||||
|
||||
if opt != nil {
|
||||
q, err := query.Values(opt)
|
||||
@ -681,7 +701,7 @@ type ErrorResponse struct {
|
||||
}
|
||||
|
||||
func (e *ErrorResponse) Error() string {
|
||||
path, _ := url.QueryUnescape(e.Response.Request.URL.Opaque)
|
||||
path, _ := url.QueryUnescape(e.Response.Request.URL.Path)
|
||||
u := fmt.Sprintf("%s://%s%s", e.Response.Request.URL.Scheme, e.Response.Request.URL.Host, path)
|
||||
return fmt.Sprintf("%s %s: %d %s", e.Response.Request.Method, u, e.Response.StatusCode, e.Message)
|
||||
}
|
||||
@ -842,3 +862,24 @@ func MergeMethod(v MergeMethodValue) *MergeMethodValue {
|
||||
*p = v
|
||||
return p
|
||||
}
|
||||
|
||||
// BoolValue is a boolean value with advanced json unmarshaling features.
|
||||
type BoolValue bool
|
||||
|
||||
// UnmarshalJSON allows 1 and 0 to be considered as boolean values
|
||||
// Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/50122
|
||||
func (t *BoolValue) UnmarshalJSON(b []byte) error {
|
||||
switch string(b) {
|
||||
case `"1"`:
|
||||
*t = true
|
||||
return nil
|
||||
case `"0"`:
|
||||
*t = false
|
||||
return nil
|
||||
default:
|
||||
var v bool
|
||||
err := json.Unmarshal(b, &v)
|
||||
*t = BoolValue(v)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
32
vendor/github.com/xanzy/go-gitlab/group_members.go
generated
vendored
32
vendor/github.com/xanzy/go-gitlab/group_members.go
generated
vendored
@ -44,8 +44,8 @@ type GroupMember struct {
|
||||
ExpiresAt *ISOTime `json:"expires_at"`
|
||||
}
|
||||
|
||||
// ListGroupMembersOptions represents the available ListGroupMembers()
|
||||
// options.
|
||||
// ListGroupMembersOptions represents the available ListGroupMembers() and
|
||||
// ListAllGroupMembers() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
|
||||
@ -55,7 +55,7 @@ type ListGroupMembersOptions struct {
|
||||
}
|
||||
|
||||
// ListGroupMembers get a list of group members viewable by the authenticated
|
||||
// user.
|
||||
// user. Returns a list including inherited members through ancestor groups.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
|
||||
@ -80,6 +80,32 @@ func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersO
|
||||
return gm, resp, err
|
||||
}
|
||||
|
||||
// ListAllGroupMembers get a list of group members viewable by the authenticated
|
||||
// user. Returns a list including inherited members through ancestor groups.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project-including-inherited-members
|
||||
func (s *GroupsService) ListAllGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...OptionFunc) ([]*GroupMember, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/members/all", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var gm []*GroupMember
|
||||
resp, err := s.client.Do(req, &gm)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return gm, resp, err
|
||||
}
|
||||
|
||||
// AddGroupMemberOptions represents the available AddGroupMember() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
|
57
vendor/github.com/xanzy/go-gitlab/issues.go
generated
vendored
57
vendor/github.com/xanzy/go-gitlab/issues.go
generated
vendored
@ -305,7 +305,7 @@ func (s *IssuesService) CreateIssue(pid interface{}, opt *CreateIssueOptions, op
|
||||
|
||||
// UpdateIssueOptions represents the available UpdateIssue() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#edit-issues
|
||||
// GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#edit-issue
|
||||
type UpdateIssueOptions struct {
|
||||
Title *string `url:"title,omitempty" json:"title,omitempty"`
|
||||
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
||||
@ -316,6 +316,7 @@ type UpdateIssueOptions struct {
|
||||
StateEvent *string `url:"state_event,omitempty" json:"state_event,omitempty"`
|
||||
UpdatedAt *time.Time `url:"updated_at,omitempty" json:"updated_at,omitempty"`
|
||||
DueDate *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
|
||||
Weight *int `url:"weight,omitempty" json:"weight,omitempty"`
|
||||
DiscussionLocked *bool `url:"discussion_locked,omitempty" json:"discussion_locked,omitempty"`
|
||||
}
|
||||
|
||||
@ -362,6 +363,60 @@ func (s *IssuesService) DeleteIssue(pid interface{}, issue int, options ...Optio
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// SubscribeToIssue subscribes the authenticated user to the given issue to
|
||||
// receive notifications. If the user is already subscribed to the issue, the
|
||||
// status code 304 is returned.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/merge_requests.html#subscribe-to-a-merge-request
|
||||
func (s *IssuesService) SubscribeToIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/issues/%d/subscribe", url.QueryEscape(project), issue)
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
i := new(Issue)
|
||||
resp, err := s.client.Do(req, i)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return i, resp, err
|
||||
}
|
||||
|
||||
// UnsubscribeFromIssue unsubscribes the authenticated user from the given
|
||||
// issue to not receive notifications from that merge request. If the user
|
||||
// is not subscribed to the issue, status code 304 is returned.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/merge_requests.html#unsubscribe-from-a-merge-request
|
||||
func (s *IssuesService) UnsubscribeFromIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/issues/%d/unsubscribe", url.QueryEscape(project), issue)
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
i := new(Issue)
|
||||
resp, err := s.client.Do(req, i)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return i, resp, err
|
||||
}
|
||||
|
||||
// ListMergeRequestsClosingIssueOptions represents the available
|
||||
// ListMergeRequestsClosingIssue() options.
|
||||
//
|
||||
|
35
vendor/github.com/xanzy/go-gitlab/jobs.go
generated
vendored
35
vendor/github.com/xanzy/go-gitlab/jobs.go
generated
vendored
@ -59,6 +59,7 @@ type Job struct {
|
||||
Status string `json:"status"`
|
||||
Tag bool `json:"tag"`
|
||||
User *User `json:"user"`
|
||||
WebURL string `json:"web_url"`
|
||||
}
|
||||
|
||||
// ListJobsOptions are options for two list apis
|
||||
@ -197,6 +198,40 @@ func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, job
|
||||
return artifactsBuf, resp, err
|
||||
}
|
||||
|
||||
// DownloadSingleArtifactsFile download a file from the artifacts from the
|
||||
// given reference name and job provided the job finished successfully.
|
||||
// Only a single file is going to be extracted from the archive and streamed
|
||||
// to a client.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/jobs.html#download-a-single-artifact-file
|
||||
func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, artifactPath string, options ...OptionFunc) (io.Reader, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
u := fmt.Sprintf(
|
||||
"projects/%s/jobs/%d/artifacts/%s",
|
||||
url.QueryEscape(project),
|
||||
jobID,
|
||||
artifactPath,
|
||||
)
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
artifactBuf := new(bytes.Buffer)
|
||||
resp, err := s.client.Do(req, artifactBuf)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return artifactBuf, resp, err
|
||||
}
|
||||
|
||||
// GetTraceFile gets a trace of a specific job of a project
|
||||
//
|
||||
// GitLab API docs:
|
||||
|
21
vendor/github.com/xanzy/go-gitlab/labels.go
generated
vendored
21
vendor/github.com/xanzy/go-gitlab/labels.go
generated
vendored
@ -17,6 +17,7 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
@ -44,6 +45,26 @@ type Label struct {
|
||||
Priority int `json:"priority"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (l *Label) UnmarshalJSON(data []byte) error {
|
||||
type alias Label
|
||||
if err := json.Unmarshal(data, (*alias)(l)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if l.Name == "" {
|
||||
var raw map[string]interface{}
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
if title, ok := raw["title"].(string); ok {
|
||||
l.Name = title
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l Label) String() string {
|
||||
return Stringify(l)
|
||||
}
|
||||
|
92
vendor/github.com/xanzy/go-gitlab/license_templates.go
generated
vendored
Normal file
92
vendor/github.com/xanzy/go-gitlab/license_templates.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// LicenseTemplate represents a license template.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/licenses.html
|
||||
type LicenseTemplate struct {
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
Nickname string `json:"nickname"`
|
||||
Featured bool `json:"featured"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
SourceURL string `json:"source_url"`
|
||||
Description string `json:"description"`
|
||||
Conditions []string `json:"conditions"`
|
||||
Permissions []string `json:"permissions"`
|
||||
Limitations []string `json:"limitations"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// LicenseTemplatesService handles communication with the license templates
|
||||
// related methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/templates/licenses.html
|
||||
type LicenseTemplatesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ListLicenseTemplatesOptions represents the available
|
||||
// ListLicenseTemplates() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/licenses.html#list-license-templates
|
||||
type ListLicenseTemplatesOptions struct {
|
||||
ListOptions
|
||||
Popular *bool `url:"popular,omitempty" json:"popular,omitempty"`
|
||||
}
|
||||
|
||||
// ListLicenseTemplates get all license templates.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/licenses.html#list-license-templates
|
||||
func (s *LicenseTemplatesService) ListLicenseTemplates(opt *ListLicenseTemplatesOptions, options ...OptionFunc) ([]*LicenseTemplate, *Response, error) {
|
||||
req, err := s.client.NewRequest("GET", "templates/licenses", opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var lts []*LicenseTemplate
|
||||
resp, err := s.client.Do(req, <s)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return lts, resp, err
|
||||
}
|
||||
|
||||
// GetLicenseTemplateOptions represents the available
|
||||
// GetLicenseTemplate() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/licenses.html#single-license-template
|
||||
type GetLicenseTemplateOptions struct {
|
||||
Project *string `url:"project,omitempty" json:"project,omitempty"`
|
||||
Fullname *string `url:"fullname,omitempty" json:"fullname,omitempty"`
|
||||
}
|
||||
|
||||
// GetLicenseTemplate get a single license template. You can pass parameters
|
||||
// to replace the license placeholder.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/templates/licenses.html#single-license-template
|
||||
func (s *LicenseTemplatesService) GetLicenseTemplate(template string, opt *GetLicenseTemplateOptions, options ...OptionFunc) (*LicenseTemplate, *Response, error) {
|
||||
u := fmt.Sprintf("templates/licenses/%s", template)
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
lt := new(LicenseTemplate)
|
||||
resp, err := s.client.Do(req, lt)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return lt, resp, err
|
||||
}
|
1
vendor/github.com/xanzy/go-gitlab/merge_request_approvals.go
generated
vendored
1
vendor/github.com/xanzy/go-gitlab/merge_request_approvals.go
generated
vendored
@ -28,6 +28,7 @@ type MergeRequestApprovals struct {
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
MergeStatus string `json:"merge_status"`
|
||||
ApprovalsBeforeMerge int `json:"approvals_before_merge"`
|
||||
ApprovalsRequired int `json:"approvals_required"`
|
||||
ApprovalsLeft int `json:"approvals_left"`
|
||||
ApprovedBy []struct {
|
||||
|
83
vendor/github.com/xanzy/go-gitlab/merge_requests.go
generated
vendored
83
vendor/github.com/xanzy/go-gitlab/merge_requests.go
generated
vendored
@ -153,10 +153,15 @@ type ListMergeRequestsOptions struct {
|
||||
Labels Labels `url:"labels,omitempty" json:"labels,omitempty"`
|
||||
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
||||
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
||||
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||
SourceBranch *string `url:"source_branch,omitempty" json:"source_branch,omitempty"`
|
||||
TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"`
|
||||
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||
}
|
||||
|
||||
// ListMergeRequests gets all merge requests. The state parameter can be used
|
||||
@ -181,6 +186,57 @@ func (s *MergeRequestsService) ListMergeRequests(opt *ListMergeRequestsOptions,
|
||||
return m, resp, err
|
||||
}
|
||||
|
||||
// ListGroupMergeRequestsOptions represents the available ListGroupMergeRequests()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/merge_requests.html#list-group-merge-requests
|
||||
type ListGroupMergeRequestsOptions struct {
|
||||
ListOptions
|
||||
State *string `url:"state,omitempty" json:"state,omitempty"`
|
||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
|
||||
View *string `url:"view,omitempty" json:"view,omitempty"`
|
||||
Labels Labels `url:"labels,omitempty" json:"labels,omitempty"`
|
||||
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
||||
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
||||
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||
SourceBranch *string `url:"source_branch,omitempty" json:"source_branch,omitempty"`
|
||||
TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"`
|
||||
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||
}
|
||||
|
||||
// ListGroupMergeRequests gets all merge requests for this group.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/merge_requests.html#list-group-merge-requests
|
||||
func (s *MergeRequestsService) ListGroupMergeRequests(gid interface{}, opt *ListGroupMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
||||
group, err := parseID(gid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("groups/%s/merge_requests", url.QueryEscape(group))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var m []*MergeRequest
|
||||
resp, err := s.client.Do(req, &m)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return m, resp, err
|
||||
}
|
||||
|
||||
// ListProjectMergeRequestsOptions represents the available ListMergeRequests()
|
||||
// options.
|
||||
//
|
||||
@ -197,19 +253,21 @@ type ListProjectMergeRequestsOptions struct {
|
||||
Labels Labels `url:"labels,omitempty" json:"labels,omitempty"`
|
||||
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
||||
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
||||
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||
SourceBranch *string `url:"source_branch,omitempty" json:"source_branch,omitempty"`
|
||||
TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"`
|
||||
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||
}
|
||||
|
||||
// ListProjectMergeRequests gets all merge requests for this project. The state
|
||||
// parameter can be used to get only merge requests with a given state (opened,
|
||||
// closed, or merged) or all of them (all). The pagination parameters page and
|
||||
// per_page can be used to restrict the list of merge requests.
|
||||
// ListProjectMergeRequests gets all merge requests for this project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
|
||||
// https://docs.gitlab.com/ce/api/merge_requests.html#list-project-merge-requests
|
||||
func (s *MergeRequestsService) ListProjectMergeRequests(pid interface{}, opt *ListProjectMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
@ -454,6 +512,10 @@ type UpdateMergeRequestOptions struct {
|
||||
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
||||
MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
|
||||
StateEvent *string `url:"state_event,omitempty" json:"state_event,omitempty"`
|
||||
RemoveSourceBranch *bool `url:"remove_source_branch,omitempty" json:"remove_source_branch,omitempty"`
|
||||
Squash *bool `url:"squash,omitempty" json:"squash,omitempty"`
|
||||
DiscussionLocked *bool `url:"discussion_locked,omitempty" json:"discussion_locked,omitempty"`
|
||||
AllowCollaboration *bool `url:"allow_collaboration,omitempty" json:"allow_collaboration,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateMergeRequest updates an existing project milestone.
|
||||
@ -626,8 +688,8 @@ func (s *MergeRequestsService) GetSingleMergeRequestDiffVersion(pid interface{},
|
||||
return v, resp, err
|
||||
}
|
||||
|
||||
// SubscribeToMergeRequest subscribes the authenticated user to the given merge request
|
||||
// to receive notifications. If the user is already subscribed to the
|
||||
// SubscribeToMergeRequest subscribes the authenticated user to the given merge
|
||||
// request to receive notifications. If the user is already subscribed to the
|
||||
// merge request, the status code 304 is returned.
|
||||
//
|
||||
// GitLab API docs:
|
||||
@ -653,9 +715,10 @@ func (s *MergeRequestsService) SubscribeToMergeRequest(pid interface{}, mergeReq
|
||||
return m, resp, err
|
||||
}
|
||||
|
||||
// UnsubscribeFromMergeRequest unsubscribes the authenticated user from the given merge request
|
||||
// to not receive notifications from that merge request. If the user is
|
||||
// not subscribed to the merge request, status code 304 is returned.
|
||||
// UnsubscribeFromMergeRequest unsubscribes the authenticated user from the
|
||||
// given merge request to not receive notifications from that merge request.
|
||||
// If the user is not subscribed to the merge request, status code 304 is
|
||||
// returned.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/merge_requests.html#unsubscribe-from-a-merge-request
|
||||
|
35
vendor/github.com/xanzy/go-gitlab/notes.go
generated
vendored
35
vendor/github.com/xanzy/go-gitlab/notes.go
generated
vendored
@ -45,7 +45,6 @@ type Note struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
State string `json:"state"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
WebURL string `json:"web_url"`
|
||||
} `json:"author"`
|
||||
@ -55,9 +54,37 @@ type Note struct {
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
NoteableID int `json:"noteable_id"`
|
||||
NoteableType string `json:"noteable_type"`
|
||||
Position *NotePosition `json:"position"`
|
||||
Resolvable bool `json:"resolvable"`
|
||||
Resolved bool `json:"resolved"`
|
||||
ResolvedBy struct {
|
||||
ID int `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
State string `json:"state"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
WebURL string `json:"web_url"`
|
||||
} `json:"resolved_by"`
|
||||
NoteableIID int `json:"noteable_iid"`
|
||||
}
|
||||
|
||||
// NotePosition represents the position attributes of a note.
|
||||
type NotePosition struct {
|
||||
BaseSHA string `json:"base_sha"`
|
||||
StartSHA string `json:"start_sha"`
|
||||
HeadSHA string `json:"head_sha"`
|
||||
PositionType string `json:"position_type"`
|
||||
NewPath string `json:"new_path,omitempty"`
|
||||
NewLine int `json:"new_line,omitempty"`
|
||||
OldPath string `json:"old_path,omitempty"`
|
||||
OldLine int `json:"old_line,omitempty"`
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
X int `json:"x,omitempty"`
|
||||
Y int `json:"y,omitempty"`
|
||||
}
|
||||
|
||||
func (n Note) String() string {
|
||||
return Stringify(n)
|
||||
}
|
||||
@ -66,7 +93,11 @@ func (n Note) String() string {
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/notes.html#list-project-issue-notes
|
||||
type ListIssueNotesOptions ListOptions
|
||||
type ListIssueNotesOptions struct {
|
||||
ListOptions
|
||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||
}
|
||||
|
||||
// ListIssueNotes gets a list of all notes for a single issue.
|
||||
//
|
||||
|
19
vendor/github.com/xanzy/go-gitlab/pages_domains.go
generated
vendored
19
vendor/github.com/xanzy/go-gitlab/pages_domains.go
generated
vendored
@ -61,6 +61,25 @@ func (s *PagesDomainsService) ListPagesDomains(pid interface{}, opt *ListPagesDo
|
||||
return pd, resp, err
|
||||
}
|
||||
|
||||
// ListAllPagesDomains gets a list of all pages domains.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/pages_domains.html#list-all-pages-domains
|
||||
func (s *PagesDomainsService) ListAllPagesDomains(options ...OptionFunc) ([]*PagesDomain, *Response, error) {
|
||||
req, err := s.client.NewRequest("GET", "pages/domains", nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var pd []*PagesDomain
|
||||
resp, err := s.client.Do(req, &pd)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return pd, resp, err
|
||||
}
|
||||
|
||||
// GetPagesDomain get a specific pages domain for a project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
|
9
vendor/github.com/xanzy/go-gitlab/pipeline_schedules.go
generated
vendored
9
vendor/github.com/xanzy/go-gitlab/pipeline_schedules.go
generated
vendored
@ -30,15 +30,6 @@ type PipelineSchedulesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// PipelineVariable represents a pipeline schedule variable.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#pipeline-schedule-variable
|
||||
type PipelineVariable struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// PipelineSchedule represents a pipeline schedule.
|
||||
//
|
||||
// GitLab API docs:
|
||||
|
12
vendor/github.com/xanzy/go-gitlab/pipelines.go
generated
vendored
12
vendor/github.com/xanzy/go-gitlab/pipelines.go
generated
vendored
@ -30,6 +30,14 @@ type PipelinesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// PipelineVariable represents a pipeline variable.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html
|
||||
type PipelineVariable struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Pipeline represents a GitLab pipeline.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html
|
||||
@ -84,6 +92,7 @@ type ListProjectPipelinesOptions struct {
|
||||
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
||||
Status *BuildStateValue `url:"status,omitempty" json:"status,omitempty"`
|
||||
Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
|
||||
SHA *string `url:"sha,omitempty" json:"sha,omitempty"`
|
||||
YamlErrors *bool `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"`
|
||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||
Username *string `url:"username,omitempty" json:"username,omitempty"`
|
||||
@ -142,7 +151,8 @@ func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ..
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline
|
||||
type CreatePipelineOptions struct {
|
||||
Ref *string `url:"ref,omitempty" json:"ref"`
|
||||
Ref *string `url:"ref" json:"ref"`
|
||||
Variables []*PipelineVariable `url:"variables,omitempty" json:"variables,omitempty"`
|
||||
}
|
||||
|
||||
// CreatePipeline creates a new project pipeline.
|
||||
|
208
vendor/github.com/xanzy/go-gitlab/project_badges.go
generated
vendored
Normal file
208
vendor/github.com/xanzy/go-gitlab/project_badges.go
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// ProjectBadge represents a project badge.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project
|
||||
type ProjectBadge struct {
|
||||
ID int `json:"id"`
|
||||
LinkURL string `json:"link_url"`
|
||||
ImageURL string `json:"image_url"`
|
||||
RenderedLinkURL string `json:"rendered_link_url"`
|
||||
RenderedImageURL string `json:"rendered_image_url"`
|
||||
// Kind represents a project badge kind. Can be empty, when used PreviewProjectBadge().
|
||||
Kind string `json:"kind"`
|
||||
}
|
||||
|
||||
// ProjectBadgesService handles communication with the project badges
|
||||
// related methods of the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ee/api/project_badges.html
|
||||
type ProjectBadgesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ListProjectBadgesOptions represents the available ListProjectBadges()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project
|
||||
type ListProjectBadgesOptions ListOptions
|
||||
|
||||
// ListProjectBadges gets a list of a project's badges and its group badges.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project
|
||||
func (s *ProjectBadgesService) ListProjectBadges(pid interface{}, opt *ListProjectBadgesOptions, options ...OptionFunc) ([]*ProjectBadge, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/badges", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var pb []*ProjectBadge
|
||||
resp, err := s.client.Do(req, &pb)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return pb, resp, err
|
||||
}
|
||||
|
||||
// GetProjectBadge gets a project badge.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#get-a-badge-of-a-project
|
||||
func (s *ProjectBadgesService) GetProjectBadge(pid interface{}, badge int, options ...OptionFunc) (*ProjectBadge, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/badges/%d", url.QueryEscape(project), badge)
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pb := new(ProjectBadge)
|
||||
resp, err := s.client.Do(req, pb)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return pb, resp, err
|
||||
}
|
||||
|
||||
// AddProjectBadgeOptions represents the available AddProjectBadge() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#add-a-badge-to-a-project
|
||||
type AddProjectBadgeOptions struct {
|
||||
LinkURL *string `url:"link_url,omitempty" json:"link_url,omitempty"`
|
||||
ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"`
|
||||
}
|
||||
|
||||
// AddProjectBadge adds a badge to a project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#add-a-badge-to-a-project
|
||||
func (s *ProjectBadgesService) AddProjectBadge(pid interface{}, opt *AddProjectBadgeOptions, options ...OptionFunc) (*ProjectBadge, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/badges", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pb := new(ProjectBadge)
|
||||
resp, err := s.client.Do(req, pb)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return pb, resp, err
|
||||
}
|
||||
|
||||
// EditProjectBadgeOptions represents the available EditProjectBadge() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#edit-a-badge-of-a-project
|
||||
type EditProjectBadgeOptions struct {
|
||||
LinkURL *string `url:"link_url,omitempty" json:"link_url,omitempty"`
|
||||
ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"`
|
||||
}
|
||||
|
||||
// EditProjectBadge updates a badge of a project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#edit-a-badge-of-a-project
|
||||
func (s *ProjectBadgesService) EditProjectBadge(pid interface{}, badge int, opt *EditProjectBadgeOptions, options ...OptionFunc) (*ProjectBadge, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/badges/%d", url.QueryEscape(project), badge)
|
||||
|
||||
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pb := new(ProjectBadge)
|
||||
resp, err := s.client.Do(req, pb)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return pb, resp, err
|
||||
}
|
||||
|
||||
// DeleteProjectBadge removes a badge from a project. Only project's
|
||||
// badges will be removed by using this endpoint.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#remove-a-badge-from-a-project
|
||||
func (s *ProjectBadgesService) DeleteProjectBadge(pid interface{}, badge int, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/badges/%d", url.QueryEscape(project), badge)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// ProjectBadgePreviewOptions represents the available PreviewProjectBadge() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#preview-a-badge-from-a-project
|
||||
type ProjectBadgePreviewOptions struct {
|
||||
LinkURL *string `url:"link_url,omitempty" json:"link_url,omitempty"`
|
||||
ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"`
|
||||
}
|
||||
|
||||
// PreviewProjectBadge returns how the link_url and image_url final URLs would be after
|
||||
// resolving the placeholder interpolation.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ee/api/project_badges.html#preview-a-badge-from-a-project
|
||||
func (s *ProjectBadgesService) PreviewProjectBadge(pid interface{}, opt *ProjectBadgePreviewOptions, options ...OptionFunc) (*ProjectBadge, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/badges/render", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pb := new(ProjectBadge)
|
||||
resp, err := s.client.Do(req, &pb)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return pb, resp, err
|
||||
}
|
35
vendor/github.com/xanzy/go-gitlab/project_members.go
generated
vendored
35
vendor/github.com/xanzy/go-gitlab/project_members.go
generated
vendored
@ -29,8 +29,8 @@ type ProjectMembersService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ListProjectMembersOptions represents the available ListProjectMembers()
|
||||
// options.
|
||||
// ListProjectMembersOptions represents the available ListProjectMembers() and
|
||||
// ListAllProjectMembers() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
|
||||
@ -39,7 +39,9 @@ type ListProjectMembersOptions struct {
|
||||
Query *string `url:"query,omitempty" json:"query,omitempty"`
|
||||
}
|
||||
|
||||
// ListProjectMembers gets a list of a project's team members.
|
||||
// ListProjectMembers gets a list of a project's team members viewable by the
|
||||
// authenticated user. Returns only direct members and not inherited members
|
||||
// through ancestors groups.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
|
||||
@ -64,6 +66,33 @@ func (s *ProjectMembersService) ListProjectMembers(pid interface{}, opt *ListPro
|
||||
return pm, resp, err
|
||||
}
|
||||
|
||||
// ListAllProjectMembers gets a list of a project's team members viewable by the
|
||||
// authenticated user. Returns a list including inherited members through
|
||||
// ancestor groups.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project-including-inherited-members
|
||||
func (s *ProjectMembersService) ListAllProjectMembers(pid interface{}, opt *ListProjectMembersOptions, options ...OptionFunc) ([]*ProjectMember, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/members/all", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var pm []*ProjectMember
|
||||
resp, err := s.client.Do(req, &pm)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return pm, resp, err
|
||||
}
|
||||
|
||||
// GetProjectMember gets a project team member.
|
||||
//
|
||||
// GitLab API docs:
|
||||
|
33
vendor/github.com/xanzy/go-gitlab/projects.go
generated
vendored
33
vendor/github.com/xanzy/go-gitlab/projects.go
generated
vendored
@ -194,6 +194,7 @@ type ListProjectsOptions struct {
|
||||
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
||||
WithIssuesEnabled *bool `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"`
|
||||
WithMergeRequestsEnabled *bool `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"`
|
||||
MinAccessLevel *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"`
|
||||
}
|
||||
|
||||
// ListProjects gets a list of projects accessible by the authenticated user.
|
||||
@ -282,6 +283,35 @@ func (s *ProjectsService) ListProjectsUsers(pid interface{}, opt *ListProjectUse
|
||||
return p, resp, err
|
||||
}
|
||||
|
||||
// ProjectLanguages is a map of strings because the response is arbitrary
|
||||
//
|
||||
// Gitlab API docs: https://docs.gitlab.com/ce/api/projects.html#languages
|
||||
type ProjectLanguages map[string]float32
|
||||
|
||||
// GetProjectLanguages gets a list of languages used by the project
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#languages
|
||||
func (s *ProjectsService) GetProjectLanguages(pid interface{}, options ...OptionFunc) (*ProjectLanguages, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/languages", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
p := new(ProjectLanguages)
|
||||
resp, err := s.client.Do(req, p)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return p, resp, err
|
||||
}
|
||||
|
||||
// GetProject gets a specific project, identified by project ID or
|
||||
// NAMESPACE/PROJECT_NAME, which is owned by the authenticated user.
|
||||
//
|
||||
@ -371,7 +401,7 @@ func (s *ProjectsService) GetProjectEvents(pid interface{}, opt *GetProjectEvent
|
||||
|
||||
// CreateProjectOptions represents the available CreateProjects() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#create-project
|
||||
// GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project
|
||||
type CreateProjectOptions struct {
|
||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||
Path *string `url:"path,omitempty" json:"path,omitempty"`
|
||||
@ -397,6 +427,7 @@ type CreateProjectOptions struct {
|
||||
TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"`
|
||||
PrintingMergeRequestLinkEnabled *bool `url:"printing_merge_request_link_enabled,omitempty" json:"printing_merge_request_link_enabled,omitempty"`
|
||||
CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"`
|
||||
ApprovalsBeforeMerge *int `url:"approvals_before_merge" json:"approvals_before_merge"`
|
||||
}
|
||||
|
||||
// CreateProject creates a new project owned by the authenticated user.
|
||||
|
10
vendor/github.com/xanzy/go-gitlab/repository_files.go
generated
vendored
10
vendor/github.com/xanzy/go-gitlab/repository_files.go
generated
vendored
@ -66,7 +66,7 @@ func (s *RepositoryFilesService) GetFile(pid interface{}, fileName string, opt *
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.QueryEscape(fileName))
|
||||
u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.PathEscape(fileName))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
@ -99,7 +99,7 @@ func (s *RepositoryFilesService) GetRawFile(pid interface{}, fileName string, op
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/files/%s/raw", url.QueryEscape(project), url.QueryEscape(fileName))
|
||||
u := fmt.Sprintf("projects/%s/repository/files/%s/raw", url.QueryEscape(project), url.PathEscape(fileName))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||
if err != nil {
|
||||
@ -149,7 +149,7 @@ func (s *RepositoryFilesService) CreateFile(pid interface{}, fileName string, op
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.QueryEscape(fileName))
|
||||
u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.PathEscape(fileName))
|
||||
|
||||
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||
if err != nil {
|
||||
@ -188,7 +188,7 @@ func (s *RepositoryFilesService) UpdateFile(pid interface{}, fileName string, op
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.QueryEscape(fileName))
|
||||
u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.PathEscape(fileName))
|
||||
|
||||
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||
if err != nil {
|
||||
@ -224,7 +224,7 @@ func (s *RepositoryFilesService) DeleteFile(pid interface{}, fileName string, op
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.QueryEscape(fileName))
|
||||
u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.PathEscape(fileName))
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, opt, options)
|
||||
if err != nil {
|
||||
|
94
vendor/github.com/xanzy/go-gitlab/runners.go
generated
vendored
94
vendor/github.com/xanzy/go-gitlab/runners.go
generated
vendored
@ -18,6 +18,7 @@ package gitlab
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
@ -38,9 +39,11 @@ type Runner struct {
|
||||
Description string `json:"description"`
|
||||
Active bool `json:"active"`
|
||||
IsShared bool `json:"is_shared"`
|
||||
IPAddress *net.IP `json:"ip_address"`
|
||||
Name string `json:"name"`
|
||||
Online bool `json:"online"`
|
||||
Status string `json:"status"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// RunnerDetails represents the GitLab CI runner details.
|
||||
@ -52,11 +55,11 @@ type RunnerDetails struct {
|
||||
Description string `json:"description"`
|
||||
ID int `json:"id"`
|
||||
IsShared bool `json:"is_shared"`
|
||||
ContactedAt *time.Time `json:"contacted_at,omitempty"`
|
||||
ContactedAt *time.Time `json:"contacted_at"`
|
||||
Name string `json:"name"`
|
||||
Online bool `json:"online"`
|
||||
Status string `json:"status"`
|
||||
Platform string `json:"platform,omitempty"`
|
||||
Platform string `json:"platform"`
|
||||
Projects []struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@ -64,11 +67,12 @@ type RunnerDetails struct {
|
||||
Path string `json:"path"`
|
||||
PathWithNamespace string `json:"path_with_namespace"`
|
||||
} `json:"projects"`
|
||||
Token string `json:"Token"`
|
||||
Revision string `json:"revision,omitempty"`
|
||||
Token string `json:"token"`
|
||||
Revision string `json:"revision"`
|
||||
TagList []string `json:"tag_list"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Version string `json:"version"`
|
||||
AccessLevel string `json:"access_level"`
|
||||
MaximumTimeout int `json:"maximum_timeout"`
|
||||
}
|
||||
|
||||
// ListRunnersOptions represents the available ListRunners() options.
|
||||
@ -155,6 +159,7 @@ type UpdateRunnerDetailsOptions struct {
|
||||
RunUntagged *bool `url:"run_untagged,omitempty" json:"run_untagged,omitempty"`
|
||||
Locked *bool `url:"locked,omitempty" json:"locked,omitempty"`
|
||||
AccessLevel *string `url:"access_level,omitempty" json:"access_level,omitempty"`
|
||||
MaximumTimeout *int `url:"maximum_timeout,omitempty" json:"maximum_timeout,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateRunnerDetails updates details for a given runner.
|
||||
@ -324,3 +329,82 @@ func (s *RunnersService) DisableProjectRunner(pid interface{}, rid interface{},
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// RegisterNewRunnerOptions represents the available RegisterNewRunner()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/runners.html#register-a-new-runner
|
||||
type RegisterNewRunnerOptions struct {
|
||||
Token *string `url:"token" json:"token"`
|
||||
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
||||
Info *string `url:"info,omitempty" json:"info,omitempty"`
|
||||
Active *bool `url:"active,omitempty" json:"active,omitempty"`
|
||||
Locked *bool `url:"locked,omitempty" json:"locked,omitempty"`
|
||||
RunUntagged *bool `url:"run_untagged,omitempty" json:"run_untagged,omitempty"`
|
||||
TagList []string `url:"tag_list[],omitempty" json:"tag_list,omitempty"`
|
||||
MaximumTimeout *int `url:"maximum_timeout,omitempty" json:"maximum_timeout,omitempty"`
|
||||
}
|
||||
|
||||
// RegisterNewRunner registers a new Runner for the instance.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/runners.html#register-a-new-runner
|
||||
func (s *RunnersService) RegisterNewRunner(opt *RegisterNewRunnerOptions, options ...OptionFunc) (*Runner, *Response, error) {
|
||||
req, err := s.client.NewRequest("POST", "runners", opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var r *Runner
|
||||
resp, err := s.client.Do(req, &r)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return r, resp, err
|
||||
}
|
||||
|
||||
// DeleteRegisteredRunnerOptions represents the available
|
||||
// DeleteRegisteredRunner() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/runners.html#delete-a-registered-runner
|
||||
type DeleteRegisteredRunnerOptions struct {
|
||||
Token *string `url:"token" json:"token"`
|
||||
}
|
||||
|
||||
// DeleteRegisteredRunner registers a new Runner for the instance.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/runners.html#delete-a-registered-runner
|
||||
func (s *RunnersService) DeleteRegisteredRunner(opt *DeleteRegisteredRunnerOptions, options ...OptionFunc) (*Response, error) {
|
||||
req, err := s.client.NewRequest("DELETE", "runners", opt, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// VerifyRegisteredRunnerOptions represents the available
|
||||
// VerifyRegisteredRunner() options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/runners.html#verify-authentication-for-a-registered-runner
|
||||
type VerifyRegisteredRunnerOptions struct {
|
||||
Token *string `url:"token" json:"token"`
|
||||
}
|
||||
|
||||
// VerifyRegisteredRunner registers a new Runner for the instance.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/runners.html#verify-authentication-for-a-registered-runner
|
||||
func (s *RunnersService) VerifyRegisteredRunner(opt *VerifyRegisteredRunnerOptions, options ...OptionFunc) (*Response, error) {
|
||||
req, err := s.client.NewRequest("POST", "runners/verify", opt, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
140
vendor/github.com/xanzy/go-gitlab/services.go
generated
vendored
140
vendor/github.com/xanzy/go-gitlab/services.go
generated
vendored
@ -45,8 +45,10 @@ type Service struct {
|
||||
MergeRequestsEvents bool `json:"merge_requests_events"`
|
||||
TagPushEvents bool `json:"tag_push_events"`
|
||||
NoteEvents bool `json:"note_events"`
|
||||
ConfidentialNoteEvents bool `json:"confidential_note_events"`
|
||||
PipelineEvents bool `json:"pipeline_events"`
|
||||
JobEvents bool `json:"job_events"`
|
||||
WikiPageEvents bool `json:"wiki_page_events"`
|
||||
}
|
||||
|
||||
// SetGitLabCIServiceOptions represents the available SetGitLabCIService()
|
||||
@ -252,7 +254,28 @@ type SlackService struct {
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/services.html#slack
|
||||
type SlackServiceProperties struct {
|
||||
NotifyOnlyBrokenPipelines bool `json:"notify_only_broken_pipelines"`
|
||||
// Note: NotifyOnlyBrokenPipelines and NotifyOnlyDefaultBranch are not
|
||||
// just "bool" because in some cases gitlab returns
|
||||
// "notify_only_broken_pipelines": true, and in other cases
|
||||
// "notify_only_broken_pipelines": "1". The same is for
|
||||
// "notify_only_default_branch" field.
|
||||
// We need to handle this, until the bug will be fixed.
|
||||
// Ref: https://gitlab.com/gitlab-org/gitlab-ce/issues/50122
|
||||
|
||||
NotifyOnlyBrokenPipelines BoolValue `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"`
|
||||
NotifyOnlyDefaultBranch BoolValue `url:"notify_only_default_branch,omitempty" json:"notify_only_default_branch,omitempty"`
|
||||
WebHook string `url:"webhook,omitempty" json:"webhook,omitempty"`
|
||||
Username string `url:"username,omitempty" json:"username,omitempty"`
|
||||
Channel string `url:"channel,omitempty" json:"channel,omitempty"`
|
||||
PushChannel string `url:"push_channel,omitempty" json:"push_channel,omitempty"`
|
||||
IssueChannel string `url:"issue_channel,omitempty" json:"issue_channel,omitempty"`
|
||||
ConfidentialIssueChannel string `url:"confidential_issue_channel,omitempty" json:"confidential_issue_channel,omitempty"`
|
||||
MergeRequestChannel string `url:"merge_request_channel,omitempty" json:"merge_request_channel,omitempty"`
|
||||
NoteChannel string `url:"note_channel,omitempty" json:"note_channel,omitempty"`
|
||||
ConfidentialNoteChannel string `url:"confidential_note_channel,omitempty" json:"confidential_note_channel,omitempty"`
|
||||
TagPushChannel string `url:"tag_push_channel,omitempty" json:"tag_push_channel,omitempty"`
|
||||
PipelineChannel string `url:"pipeline_channel,omitempty" json:"pipeline_channel,omitempty"`
|
||||
WikiPageChannel string `url:"wiki_page_channel,omitempty" json:"wiki_page_channel,omitempty"`
|
||||
}
|
||||
|
||||
// GetSlackService gets Slack service settings for a project.
|
||||
@ -286,9 +309,32 @@ func (s *ServicesService) GetSlackService(pid interface{}, options ...OptionFunc
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/services.html#edit-slack-service
|
||||
type SetSlackServiceOptions struct {
|
||||
WebHook *string `url:"webhook,omitempty" json:"webhook,omitempty" `
|
||||
Username *string `url:"username,omitempty" json:"username,omitempty" `
|
||||
WebHook *string `url:"webhook,omitempty" json:"webhook,omitempty"`
|
||||
Username *string `url:"username,omitempty" json:"username,omitempty"`
|
||||
Channel *string `url:"channel,omitempty" json:"channel,omitempty"`
|
||||
NotifyOnlyBrokenPipelines *bool `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"`
|
||||
NotifyOnlyDefaultBranch *bool `url:"notify_only_default_branch,omitempty" json:"notify_only_default_branch,omitempty"`
|
||||
PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"`
|
||||
PushChannel *string `url:"push_channel,omitempty" json:"push_channel,omitempty"`
|
||||
IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"`
|
||||
IssueChannel *string `url:"issue_channel,omitempty" json:"issue_channel,omitempty"`
|
||||
ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
|
||||
ConfidentialIssueChannel *string `url:"confidential_issue_channel,omitempty" json:"confidential_issue_channel,omitempty"`
|
||||
MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
|
||||
MergeRequestChannel *string `url:"merge_request_channel,omitempty" json:"merge_request_channel,omitempty"`
|
||||
TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
|
||||
TagPushChannel *string `url:"tag_push_channel,omitempty" json:"tag_push_channel,omitempty"`
|
||||
NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"`
|
||||
NoteChannel *string `url:"note_channel,omitempty" json:"note_channel,omitempty"`
|
||||
ConfidentialNoteEvents *bool `url:"confidential_note_events" json:"confidential_note_events"`
|
||||
// TODO: Currently, GitLab ignores this option (not implemented yet?), so
|
||||
// there is no way to set it. Uncomment when this is fixed.
|
||||
// See: https://gitlab.com/gitlab-org/gitlab-ce/issues/49730
|
||||
//ConfidentialNoteChannel *string `json:"confidential_note_channel,omitempty"`
|
||||
PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
|
||||
PipelineChannel *string `url:"pipeline_channel,omitempty" json:"pipeline_channel,omitempty"`
|
||||
WikiPageChannel *string `url:"wiki_page_channel,omitempty" json:"wiki_page_channel,omitempty"`
|
||||
WikiPageEvents *bool `url:"wiki_page_events" json:"wiki_page_events"`
|
||||
}
|
||||
|
||||
// SetSlackService sets Slack service for a project
|
||||
@ -513,3 +559,91 @@ func (s *ServicesService) DeleteJenkinsCIService(pid interface{}, options ...Opt
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// MicrosoftTeamsService represents Microsoft Teams service settings.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/services.html#microsoft-teams
|
||||
type MicrosoftTeamsService struct {
|
||||
Service
|
||||
Properties *MicrosoftTeamsServiceProperties `json:"properties"`
|
||||
}
|
||||
|
||||
// MicrosoftTeamsServiceProperties represents Microsoft Teams specific properties.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/services.html#microsoft-teams
|
||||
type MicrosoftTeamsServiceProperties struct {
|
||||
WebHook string `json:"webhook"`
|
||||
}
|
||||
|
||||
// GetMicrosoftTeamsService gets MicrosoftTeams service settings for a project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/services.html#get-microsoft-teams-service-settings
|
||||
func (s *ServicesService) GetMicrosoftTeamsService(pid interface{}, options ...OptionFunc) (*MicrosoftTeamsService, *Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/services/microsoft-teams", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
svc := new(MicrosoftTeamsService)
|
||||
resp, err := s.client.Do(req, svc)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return svc, resp, err
|
||||
}
|
||||
|
||||
// SetMicrosoftTeamsServiceOptions represents the available SetMicrosoftTeamsService()
|
||||
// options.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/services.html#create-edit-microsoft-teams-service
|
||||
type SetMicrosoftTeamsServiceOptions struct {
|
||||
WebHook *string `url:"webhook,omitempty" json:"webhook,omitempty"`
|
||||
}
|
||||
|
||||
// SetMicrosoftTeamsService sets Microsoft Teams service for a project
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/services.html#create-edit-microsoft-teams-service
|
||||
func (s *ServicesService) SetMicrosoftTeamsService(pid interface{}, opt *SetMicrosoftTeamsServiceOptions, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/services/microsoft-teams", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// DeleteMicrosoftTeamsService deletes Microsoft Teams service for project.
|
||||
//
|
||||
// GitLab API docs:
|
||||
// https://docs.gitlab.com/ce/api/services.html#delete-microsoft-teams-service
|
||||
func (s *ServicesService) DeleteMicrosoftTeamsService(pid interface{}, options ...OptionFunc) (*Response, error) {
|
||||
project, err := parseID(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := fmt.Sprintf("projects/%s/services/microsoft-teams", url.QueryEscape(project))
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
78
vendor/github.com/xanzy/go-gitlab/session.go
generated
vendored
78
vendor/github.com/xanzy/go-gitlab/session.go
generated
vendored
@ -1,78 +0,0 @@
|
||||
//
|
||||
// Copyright 2017, Sander van Harmelen
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package gitlab
|
||||
|
||||
import "time"
|
||||
|
||||
// SessionService handles communication with the session related methods of
|
||||
// the GitLab API.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/session.html
|
||||
type SessionService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Session represents a GitLab session.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/session.html#session
|
||||
type Session struct {
|
||||
ID int `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
PrivateToken string `json:"private_token"`
|
||||
Blocked bool `json:"blocked"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
Bio interface{} `json:"bio"`
|
||||
Skype string `json:"skype"`
|
||||
Linkedin string `json:"linkedin"`
|
||||
Twitter string `json:"twitter"`
|
||||
WebsiteURL string `json:"website_url"`
|
||||
DarkScheme bool `json:"dark_scheme"`
|
||||
ThemeID int `json:"theme_id"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
CanCreateGroup bool `json:"can_create_group"`
|
||||
CanCreateTeam bool `json:"can_create_team"`
|
||||
CanCreateProject bool `json:"can_create_project"`
|
||||
}
|
||||
|
||||
// GetSessionOptions represents the available Session() options.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/session.html#session
|
||||
type GetSessionOptions struct {
|
||||
Login *string `url:"login,omitempty" json:"login,omitempty"`
|
||||
Email *string `url:"email,omitempty" json:"email,omitempty"`
|
||||
Password *string `url:"password,omitempty" json:"password,omitempty"`
|
||||
}
|
||||
|
||||
// GetSession logs in to get private token.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/session.html#session
|
||||
func (s *SessionService) GetSession(opt *GetSessionOptions, options ...OptionFunc) (*Session, *Response, error) {
|
||||
req, err := s.client.NewRequest("POST", "session", opt, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
session := new(Session)
|
||||
resp, err := s.client.Do(req, session)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return session, resp, err
|
||||
}
|
6
vendor/github.com/xanzy/go-gitlab/users.go
generated
vendored
6
vendor/github.com/xanzy/go-gitlab/users.go
generated
vendored
@ -32,7 +32,7 @@ type UsersService struct {
|
||||
|
||||
// User represents a GitLab user.
|
||||
//
|
||||
// GitLab API docs: https://docs.gitlab.com/ce/api/users.html
|
||||
// GitLab API docs: https://docs.gitlab.com/ee/api/users.html
|
||||
type User struct {
|
||||
ID int `json:"id"`
|
||||
Username string `json:"username"`
|
||||
@ -42,6 +42,7 @@ type User struct {
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
Bio string `json:"bio"`
|
||||
Location string `json:"location"`
|
||||
PublicEmail string `json:"public_email"`
|
||||
Skype string `json:"skype"`
|
||||
Linkedin string `json:"linkedin"`
|
||||
Twitter string `json:"twitter"`
|
||||
@ -59,9 +60,12 @@ type User struct {
|
||||
ProjectsLimit int `json:"projects_limit"`
|
||||
CurrentSignInAt *time.Time `json:"current_sign_in_at"`
|
||||
LastSignInAt *time.Time `json:"last_sign_in_at"`
|
||||
ConfirmedAt *time.Time `json:"confirmed_at"`
|
||||
TwoFactorEnabled bool `json:"two_factor_enabled"`
|
||||
Identities []*UserIdentity `json:"identities"`
|
||||
External bool `json:"external"`
|
||||
PrivateProfile bool `json:"private_profile"`
|
||||
SharedRunnersMinutesLimit int `json:"shared_runners_minutes_limit"`
|
||||
}
|
||||
|
||||
// UserIdentity represents a user identity.
|
||||
|
5
vendor/github.com/zmb3/spotify/.travis.yml
generated
vendored
Normal file
5
vendor/github.com/zmb3/spotify/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8
|
||||
- 1.9
|
201
vendor/github.com/zmb3/spotify/LICENSE
generated
vendored
Normal file
201
vendor/github.com/zmb3/spotify/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
106
vendor/github.com/zmb3/spotify/README.md
generated
vendored
Normal file
106
vendor/github.com/zmb3/spotify/README.md
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
|
||||
Spotify
|
||||
=======
|
||||
|
||||
[](http://godoc.org/github.com/zmb3/spotify)
|
||||
[](https://ci.appveyor.com/project/zmb3/spotify)
|
||||
[](https://travis-ci.org/zmb3/spotify)
|
||||
|
||||
This is a Go wrapper for working with Spotify's
|
||||
[Web API](https://developer.spotify.com/web-api/).
|
||||
|
||||
It aims to support every task listed in the Web API Endpoint Reference,
|
||||
located [here](https://developer.spotify.com/web-api/endpoint-reference/).
|
||||
|
||||
By using this library you agree to Spotify's
|
||||
[Developer Terms of Use](https://developer.spotify.com/developer-terms-of-use/).
|
||||
|
||||
## Installation
|
||||
|
||||
To install the library, simply
|
||||
|
||||
`go get github.com/zmb3/spotify`
|
||||
|
||||
## Authentication
|
||||
|
||||
Spotify uses OAuth2 for authentication and authorization.
|
||||
As of May 29, 2017 _all_ Web API endpoints require an access token.
|
||||
|
||||
You can authenticate using a client credentials flow, but this does not provide
|
||||
any authorization to access a user's private data. For most use cases, you'll
|
||||
want to use the authorization code flow. This package includes an `Authenticator`
|
||||
type to handle the details for you.
|
||||
|
||||
Start by registering your application at the following page:
|
||||
|
||||
https://developer.spotify.com/my-applications/.
|
||||
|
||||
You'll get a __client ID__ and __secret key__ for your application. An easy way to
|
||||
provide this data to your application is to set the SPOTIFY_ID and SPOTIFY_SECRET
|
||||
environment variables. If you choose not to use environment variables, you can
|
||||
provide this data manually.
|
||||
|
||||
|
||||
````Go
|
||||
// the redirect URL must be an exact match of a URL you've registered for your application
|
||||
// scopes determine which permissions the user is prompted to authorize
|
||||
auth := spotify.NewAuthenticator(redirectURL, spotify.ScopeUserReadPrivate)
|
||||
|
||||
// if you didn't store your ID and secret key in the specified environment variables,
|
||||
// you can set them manually here
|
||||
auth.SetAuthInfo(clientID, secretKey)
|
||||
|
||||
// get the user to this URL - how you do that is up to you
|
||||
// you should specify a unique state string to identify the session
|
||||
url := auth.AuthURL(state)
|
||||
|
||||
// the user will eventually be redirected back to your redirect URL
|
||||
// typically you'll have a handler set up like the following:
|
||||
func redirectHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// use the same state string here that you used to generate the URL
|
||||
token, err := auth.Token(state, r)
|
||||
if err != nil {
|
||||
http.Error(w, "Couldn't get token", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
// create a client using the specified token
|
||||
client := auth.NewClient(token)
|
||||
|
||||
// the client can now be used to make authenticated requests
|
||||
}
|
||||
````
|
||||
|
||||
You may find the following resources useful:
|
||||
|
||||
1. Spotify's Web API Authorization Guide:
|
||||
https://developer.spotify.com/web-api/authorization-guide/
|
||||
|
||||
2. Go's OAuth2 package:
|
||||
https://godoc.org/golang.org/x/oauth2/google
|
||||
|
||||
|
||||
## Helpful Hints
|
||||
|
||||
|
||||
### Optional Parameters
|
||||
|
||||
Many of the functions in this package come in two forms - a simple version that
|
||||
omits optional parameters and uses reasonable defaults, and a more sophisticated
|
||||
version that accepts additional parameters. The latter is suffixed with `Opt`
|
||||
to indicate that it accepts some optional parameters.
|
||||
|
||||
### Automatic Retries
|
||||
|
||||
The API will throttle your requests if you are sending them too rapidly.
|
||||
The client can be configured to wait and re-attempt the request.
|
||||
To enable this, set the `AutoRetry` field on the `Client` struct to `true`.
|
||||
|
||||
For more information, see Spotify [rate-limits](https://developer.spotify.com/web-api/user-guide/#rate-limiting).
|
||||
|
||||
## API Examples
|
||||
|
||||
Examples of the API can be found in the [examples](examples) directory.
|
||||
|
||||
You may find tools such as [Spotify's Web API Console](https://developer.spotify.com/web-api/console/)
|
||||
or [Rapid API](https://rapidapi.com/package/SpotifyPublicAPI/functions?utm_source=SpotifyGitHub&utm_medium=button&utm_content=Vendor_GitHub)
|
||||
valuable for experimenting with the API.
|
220
vendor/github.com/zmb3/spotify/album.go
generated
vendored
Normal file
220
vendor/github.com/zmb3/spotify/album.go
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
package spotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SimpleAlbum contains basic data about an album.
|
||||
type SimpleAlbum struct {
|
||||
// The name of the album.
|
||||
Name string `json:"name"`
|
||||
// A slice of SimpleArtists
|
||||
Artists []SimpleArtist `json:"artists"`
|
||||
// The field is present when getting an artist’s
|
||||
// albums. Possible values are “album”, “single”,
|
||||
// “compilation”, “appears_on”. Compare to album_type
|
||||
// this field represents relationship between the artist
|
||||
// and the album.
|
||||
AlbumGroup string `json:"album_group"`
|
||||
// The type of the album: one of "album",
|
||||
// "single", or "compilation".
|
||||
AlbumType string `json:"album_type"`
|
||||
// The SpotifyID for the album.
|
||||
ID ID `json:"id"`
|
||||
// The SpotifyURI for the album.
|
||||
URI URI `json:"uri"`
|
||||
// The markets in which the album is available,
|
||||
// identified using ISO 3166-1 alpha-2 country
|
||||
// codes. Note that al album is considered
|
||||
// available in a market when at least 1 of its
|
||||
// tracks is available in that market.
|
||||
AvailableMarkets []string `json:"available_markets"`
|
||||
// A link to the Web API enpoint providing full
|
||||
// details of the album.
|
||||
Endpoint string `json:"href"`
|
||||
// The cover art for the album in various sizes,
|
||||
// widest first.
|
||||
Images []Image `json:"images"`
|
||||
// Known external URLs for this album.
|
||||
ExternalURLs map[string]string `json:"external_urls"`
|
||||
// The date the album was first released. For example, "1981-12-15".
|
||||
// Depending on the ReleaseDatePrecision, it might be shown as
|
||||
// "1981" or "1981-12". You can use ReleaseDateTime to convert this
|
||||
// to a time.Time value.
|
||||
ReleaseDate string `json:"release_date"`
|
||||
// The precision with which ReleaseDate value is known: "year", "month", or "day"
|
||||
ReleaseDatePrecision string `json:"release_date_precision"`
|
||||
}
|
||||
|
||||
// Copyright contains the copyright statement associated with an album.
|
||||
type Copyright struct {
|
||||
// The copyright text for the album.
|
||||
Text string `json:"text"`
|
||||
// The type of copyright.
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// FullAlbum provides extra album data in addition to the data provided by SimpleAlbum.
|
||||
type FullAlbum struct {
|
||||
SimpleAlbum
|
||||
Artists []SimpleArtist `json:"artists"`
|
||||
Copyrights []Copyright `json:"copyrights"`
|
||||
Genres []string `json:"genres"`
|
||||
// The popularity of the album, represented as an integer between 0 and 100,
|
||||
// with 100 being the most popular. Popularity of an album is calculated
|
||||
// from the popularify of the album's individual tracks.
|
||||
Popularity int `json:"popularity"`
|
||||
// The date the album was first released. For example, "1981-12-15".
|
||||
// Depending on the ReleaseDatePrecision, it might be shown as
|
||||
// "1981" or "1981-12". You can use ReleaseDateTime to convert this
|
||||
// to a time.Time value.
|
||||
ReleaseDate string `json:"release_date"`
|
||||
// The precision with which ReleaseDate value is known: "year", "month", or "day"
|
||||
ReleaseDatePrecision string `json:"release_date_precision"`
|
||||
Tracks SimpleTrackPage `json:"tracks"`
|
||||
ExternalIDs map[string]string `json:"external_ids"`
|
||||
}
|
||||
|
||||
// SavedAlbum provides info about an album saved to an user's account.
|
||||
type SavedAlbum struct {
|
||||
// The date and time the track was saved, represented as an ISO
|
||||
// 8601 UTC timestamp with a zero offset (YYYY-MM-DDTHH:MM:SSZ).
|
||||
// You can use the TimestampLayout constant to convert this to
|
||||
// a time.Time value.
|
||||
AddedAt string `json:"added_at"`
|
||||
FullAlbum `json:"album"`
|
||||
}
|
||||
|
||||
// ReleaseDateTime converts the album's ReleaseDate to a time.TimeValue.
|
||||
// All of the fields in the result may not be valid. For example, if
|
||||
// f.ReleaseDatePrecision is "month", then only the month and year
|
||||
// (but not the day) of the result are valid.
|
||||
func (f *FullAlbum) ReleaseDateTime() time.Time {
|
||||
if f.ReleaseDatePrecision == "day" {
|
||||
result, _ := time.Parse(DateLayout, f.ReleaseDate)
|
||||
return result
|
||||
}
|
||||
if f.ReleaseDatePrecision == "month" {
|
||||
ym := strings.Split(f.ReleaseDate, "-")
|
||||
year, _ := strconv.Atoi(ym[0])
|
||||
month, _ := strconv.Atoi(ym[1])
|
||||
return time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
year, _ := strconv.Atoi(f.ReleaseDate)
|
||||
return time.Date(year, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
|
||||
// GetAlbum gets Spotify catalog information for a single album, given its Spotify ID.
|
||||
func (c *Client) GetAlbum(id ID) (*FullAlbum, error) {
|
||||
spotifyURL := fmt.Sprintf("%salbums/%s", c.baseURL, id)
|
||||
|
||||
var a FullAlbum
|
||||
|
||||
err := c.get(spotifyURL, &a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func toStringSlice(ids []ID) []string {
|
||||
result := make([]string, len(ids))
|
||||
for i, str := range ids {
|
||||
result[i] = str.String()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetAlbums gets Spotify Catalog information for multiple albums, given their
|
||||
// Spotify IDs. It supports up to 20 IDs in a single call. Albums are returned
|
||||
// in the order requested. If an album is not found, that position in the
|
||||
// result slice will be nil.
|
||||
func (c *Client) GetAlbums(ids ...ID) ([]*FullAlbum, error) {
|
||||
if len(ids) > 20 {
|
||||
return nil, errors.New("spotify: exceeded maximum number of albums")
|
||||
}
|
||||
spotifyURL := fmt.Sprintf("%salbums?ids=%s", c.baseURL, strings.Join(toStringSlice(ids), ","))
|
||||
|
||||
var a struct {
|
||||
Albums []*FullAlbum `json:"albums"`
|
||||
}
|
||||
|
||||
err := c.get(spotifyURL, &a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.Albums, nil
|
||||
}
|
||||
|
||||
// AlbumType represents the type of an album. It can be used to filter
|
||||
// results when searching for albums.
|
||||
type AlbumType int
|
||||
|
||||
// AlbumType values that can be used to filter which types of albums are
|
||||
// searched for. These are flags that can be bitwise OR'd together
|
||||
// to search for multiple types of albums simultaneously.
|
||||
const (
|
||||
AlbumTypeAlbum AlbumType = 1 << iota
|
||||
AlbumTypeSingle = 1 << iota
|
||||
AlbummTypeAppearsOn = 1 << iota
|
||||
AlbumTypeCompilation = 1 << iota
|
||||
)
|
||||
|
||||
func (at AlbumType) encode() string {
|
||||
types := []string{}
|
||||
if at&AlbumTypeAlbum != 0 {
|
||||
types = append(types, "album")
|
||||
}
|
||||
if at&AlbumTypeSingle != 0 {
|
||||
types = append(types, "single")
|
||||
}
|
||||
if at&AlbummTypeAppearsOn != 0 {
|
||||
types = append(types, "appears_on")
|
||||
}
|
||||
if at&AlbumTypeCompilation != 0 {
|
||||
types = append(types, "compilation")
|
||||
}
|
||||
return strings.Join(types, ",")
|
||||
}
|
||||
|
||||
// GetAlbumTracks gets the tracks for a particular album.
|
||||
// If you only care about the tracks, this call is more efficient
|
||||
// than GetAlbum.
|
||||
func (c *Client) GetAlbumTracks(id ID) (*SimpleTrackPage, error) {
|
||||
return c.GetAlbumTracksOpt(id, -1, -1)
|
||||
}
|
||||
|
||||
// GetAlbumTracksOpt behaves like GetAlbumTracks, with the exception that it
|
||||
// allows you to specify extra parameters that limit the number of results returned.
|
||||
// The maximum number of results to return is specified by limit.
|
||||
// The offset argument can be used to specify the index of the first track to return.
|
||||
// It can be used along with limit to reqeust the next set of results.
|
||||
func (c *Client) GetAlbumTracksOpt(id ID, limit, offset int) (*SimpleTrackPage, error) {
|
||||
spotifyURL := fmt.Sprintf("%salbums/%s/tracks", c.baseURL, id)
|
||||
v := url.Values{}
|
||||
if limit != -1 {
|
||||
v.Set("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if offset != -1 {
|
||||
v.Set("offset", strconv.Itoa(offset))
|
||||
}
|
||||
optional := v.Encode()
|
||||
if optional != "" {
|
||||
spotifyURL = spotifyURL + "?" + optional
|
||||
}
|
||||
|
||||
var result SimpleTrackPage
|
||||
err := c.get(spotifyURL, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
152
vendor/github.com/zmb3/spotify/artist.go
generated
vendored
Normal file
152
vendor/github.com/zmb3/spotify/artist.go
generated
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
package spotify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SimpleArtist contains basic info about an artist.
|
||||
type SimpleArtist struct {
|
||||
Name string `json:"name"`
|
||||
ID ID `json:"id"`
|
||||
// The Spotify URI for the artist.
|
||||
URI URI `json:"uri"`
|
||||
// A link to the Web API enpoint providing full details of the artist.
|
||||
Endpoint string `json:"href"`
|
||||
ExternalURLs map[string]string `json:"external_urls"`
|
||||
}
|
||||
|
||||
// FullArtist provides extra artist data in addition to what is provided by SimpleArtist.
|
||||
type FullArtist struct {
|
||||
SimpleArtist
|
||||
// The popularity of the artist, expressed as an integer between 0 and 100.
|
||||
// The artist's popularity is calculated from the popularity of the artist's tracks.
|
||||
Popularity int `json:"popularity"`
|
||||
// A list of genres the artist is associated with. For example, "Prog Rock"
|
||||
// or "Post-Grunge". If not yet classified, the slice is empty.
|
||||
Genres []string `json:"genres"`
|
||||
Followers Followers
|
||||
// Images of the artist in various sizes, widest first.
|
||||
Images []Image `json:"images"`
|
||||
}
|
||||
|
||||
// GetArtist gets Spotify catalog information for a single artist, given its Spotify ID.
|
||||
func (c *Client) GetArtist(id ID) (*FullArtist, error) {
|
||||
spotifyURL := fmt.Sprintf("%sartists/%s", c.baseURL, id)
|
||||
|
||||
var a FullArtist
|
||||
err := c.get(spotifyURL, &a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
// GetArtists gets spotify catalog information for several artists based on their
|
||||
// Spotify IDs. It supports up to 50 artists in a single call. Artists are
|
||||
// returned in the order requested. If an artist is not found, that position
|
||||
// in the result will be nil. Duplicate IDs will result in duplicate artists
|
||||
// in the result.
|
||||
func (c *Client) GetArtists(ids ...ID) ([]*FullArtist, error) {
|
||||
spotifyURL := fmt.Sprintf("%sartists?ids=%s", c.baseURL, strings.Join(toStringSlice(ids), ","))
|
||||
|
||||
var a struct {
|
||||
Artists []*FullArtist
|
||||
}
|
||||
|
||||
err := c.get(spotifyURL, &a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.Artists, nil
|
||||
}
|
||||
|
||||
// GetArtistsTopTracks gets Spotify catalog information about an artist's top
|
||||
// tracks in a particular country. It returns a maximum of 10 tracks. The
|
||||
// country is specified as an ISO 3166-1 alpha-2 country code.
|
||||
func (c *Client) GetArtistsTopTracks(artistID ID, country string) ([]FullTrack, error) {
|
||||
spotifyURL := fmt.Sprintf("%sartists/%s/top-tracks?country=%s", c.baseURL, artistID, country)
|
||||
|
||||
var t struct {
|
||||
Tracks []FullTrack `json:"tracks"`
|
||||
}
|
||||
|
||||
err := c.get(spotifyURL, &t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Tracks, nil
|
||||
}
|
||||
|
||||
// GetRelatedArtists gets Spotify catalog information about artists similar to a
|
||||
// given artist. Similarity is based on analysis of the Spotify community's
|
||||
// listening history. This function returns up to 20 artists that are considered
|
||||
// related to the specified artist.
|
||||
func (c *Client) GetRelatedArtists(id ID) ([]FullArtist, error) {
|
||||
spotifyURL := fmt.Sprintf("%sartists/%s/related-artists", c.baseURL, id)
|
||||
|
||||
var a struct {
|
||||
Artists []FullArtist `json:"artists"`
|
||||
}
|
||||
|
||||
err := c.get(spotifyURL, &a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.Artists, nil
|
||||
}
|
||||
|
||||
// GetArtistAlbums gets Spotify catalog information about an artist's albums.
|
||||
// It is equivalent to GetArtistAlbumsOpt(artistID, nil).
|
||||
func (c *Client) GetArtistAlbums(artistID ID) (*SimpleAlbumPage, error) {
|
||||
return c.GetArtistAlbumsOpt(artistID, nil, nil)
|
||||
}
|
||||
|
||||
// GetArtistAlbumsOpt is just like GetArtistAlbums, but it accepts optional
|
||||
// parameters used to filter and sort the result.
|
||||
//
|
||||
// The AlbumType argument can be used to find a particular type of album. Search
|
||||
// for multiple types by OR-ing the types together.
|
||||
func (c *Client) GetArtistAlbumsOpt(artistID ID, options *Options, t *AlbumType) (*SimpleAlbumPage, error) {
|
||||
spotifyURL := fmt.Sprintf("%sartists/%s/albums", c.baseURL, artistID)
|
||||
// add optional query string if options were specified
|
||||
values := url.Values{}
|
||||
if t != nil {
|
||||
values.Set("album_type", t.encode())
|
||||
}
|
||||
if options != nil {
|
||||
if options.Country != nil {
|
||||
values.Set("market", *options.Country)
|
||||
} else {
|
||||
// if the market is not specified, Spotify will likely return a lot
|
||||
// of duplicates (one for each market in which the album is available)
|
||||
// - prevent this behavior by falling back to the US by default
|
||||
// TODO: would this ever be the desired behavior?
|
||||
values.Set("market", CountryUSA)
|
||||
}
|
||||
if options.Limit != nil {
|
||||
values.Set("limit", strconv.Itoa(*options.Limit))
|
||||
}
|
||||
if options.Offset != nil {
|
||||
values.Set("offset", strconv.Itoa(*options.Offset))
|
||||
}
|
||||
}
|
||||
if query := values.Encode(); query != "" {
|
||||
spotifyURL += "?" + query
|
||||
}
|
||||
|
||||
var p SimpleAlbumPage
|
||||
|
||||
err := c.get(spotifyURL, &p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &p, nil
|
||||
}
|
110
vendor/github.com/zmb3/spotify/audio_analysis.go
generated
vendored
Normal file
110
vendor/github.com/zmb3/spotify/audio_analysis.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
package spotify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AudioAnalysis contains a detailed audio analysis for a single track
|
||||
// identified by its unique Spotify ID. See:
|
||||
// https://developer.spotify.com/web-api/get-audio-analysis/
|
||||
type AudioAnalysis struct {
|
||||
Bars []Marker `json:"bars"`
|
||||
Beats []Marker `json:"beats"`
|
||||
Meta AnalysisMeta `json:"meta"`
|
||||
Sections []Section `json:"sections"`
|
||||
Segments []Segment `json:"segments"`
|
||||
Tatums []Marker `json:"tatums"`
|
||||
Track AnalysisTrack `json:"track"`
|
||||
}
|
||||
|
||||
// Marker represents beats, bars, tatums and are used in segment and section
|
||||
// descriptions.
|
||||
type Marker struct {
|
||||
Start float64 `json:"start"`
|
||||
Duration float64 `json:"duration"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
}
|
||||
|
||||
// AnalysisMeta describes details about Spotify's audio analysis of the track
|
||||
type AnalysisMeta struct {
|
||||
AnalyzerVersion string `json:"analyzer_version"`
|
||||
Platform string `json:"platform"`
|
||||
DetailedStatus string `json:"detailed_status"`
|
||||
StatusCode int `json:"status"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
AnalysisTime float64 `json:"analysis_time"`
|
||||
InputProcess string `json:"input_process"`
|
||||
}
|
||||
|
||||
// Section represents a large variation in rhythm or timbre, e.g. chorus, verse,
|
||||
// bridge, guitar solo, etc. Each section contains its own descriptions of
|
||||
// tempo, key, mode, time_signature, and loudness.
|
||||
type Section struct {
|
||||
Marker
|
||||
Loudness float64 `json:"loudness"`
|
||||
Tempo float64 `json:"tempo"`
|
||||
TempoConfidence float64 `json:"tempo_confidence"`
|
||||
Key Key `json:"key"`
|
||||
KeyConfidence float64 `json:"key_confidence"`
|
||||
Mode Mode `json:"mode"`
|
||||
ModeConfidence float64 `json:"mode_confidence"`
|
||||
TimeSignature int `json:"time_signature"`
|
||||
TimeSignatureConfidence float64 `json:"time_signature_confidence"`
|
||||
}
|
||||
|
||||
// Segment is characterized by it's perceptual onset and duration in seconds,
|
||||
// loudness (dB), pitch and timbral content.
|
||||
type Segment struct {
|
||||
Marker
|
||||
LoudnessStart float64 `json:"loudness_start"`
|
||||
LoudnessMaxTime float64 `json:"loudness_max_time"`
|
||||
LoudnessMax float64 `json:"loudness_max"`
|
||||
LoudnessEnd float64 `json:"loudness_end"`
|
||||
Pitches []float64 `json:"pitches"`
|
||||
Timbre []float64 `json:"timbre"`
|
||||
}
|
||||
|
||||
// AnalysisTrack contains audio analysis data about the track as a whole
|
||||
type AnalysisTrack struct {
|
||||
NumSamples int64 `json:"num_samples"`
|
||||
Duration float64 `json:"duration"`
|
||||
SampleMD5 string `json:"sample_md5"`
|
||||
OffsetSeconds int `json:"offset_seconds"`
|
||||
WindowSeconds int `json:"window_seconds"`
|
||||
AnalysisSampleRate int64 `json:"analysis_sample_rate"`
|
||||
AnalysisChannels int `json:"analysis_channels"`
|
||||
EndOfFadeIn float64 `json:"end_of_fade_in"`
|
||||
StartOfFadeOut float64 `json:"start_of_fade_out"`
|
||||
Loudness float64 `json:"loudness"`
|
||||
Tempo float64 `json:"tempo"`
|
||||
TempoConfidence float64 `json:"tempo_confidence"`
|
||||
TimeSignature int `json:"time_signature"`
|
||||
TimeSignatureConfidence float64 `json:"time_signature_confidence"`
|
||||
Key Key `json:"key"`
|
||||
KeyConfidence float64 `json:"key_confidence"`
|
||||
Mode Mode `json:"mode"`
|
||||
ModeConfidence float64 `json:"mode_confidence"`
|
||||
CodeString string `json:"codestring"`
|
||||
CodeVersion float64 `json:"code_version"`
|
||||
EchoprintString string `json:"echoprintstring"`
|
||||
EchoprintVersion float64 `json:"echoprint_version"`
|
||||
SynchString string `json:"synchstring"`
|
||||
SynchVersion float64 `json:"synch_version"`
|
||||
RhythmString string `json:"rhythmstring"`
|
||||
RhythmVersion float64 `json:"rhythm_version"`
|
||||
}
|
||||
|
||||
// GetAudioAnalysis queries the Spotify web API for an audio analysis of a
|
||||
// single track.
|
||||
func (c *Client) GetAudioAnalysis(id ID) (*AudioAnalysis, error) {
|
||||
url := fmt.Sprintf("%saudio-analysis/%s", c.baseURL, id)
|
||||
|
||||
temp := AudioAnalysis{}
|
||||
|
||||
err := c.get(url, &temp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &temp, nil
|
||||
}
|
123
vendor/github.com/zmb3/spotify/audio_features.go
generated
vendored
Normal file
123
vendor/github.com/zmb3/spotify/audio_features.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
package spotify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AudioFeatures contains various high-level acoustic attributes
|
||||
// for a particular track.
|
||||
type AudioFeatures struct {
|
||||
//Acousticness is a confidence measure from 0.0 to 1.0 of whether
|
||||
// the track is acoustic. A value of 1.0 represents high confidence
|
||||
// that the track is acoustic.
|
||||
Acousticness float32 `json:"acousticness"`
|
||||
// An HTTP URL to access the full audio analysis of the track.
|
||||
// The URL is cryptographically signed and configured to expire
|
||||
// after roughly 10 minutes. Do not store these URLs for later use.
|
||||
AnalysisURL string `json:"analysis_url"`
|
||||
// Danceability describes how suitable a track is for dancing based
|
||||
// on a combination of musical elements including tempo, rhythm stability,
|
||||
// beat strength, and overall regularity. A value of 0.0 is least danceable
|
||||
// and 1.0 is most danceable.
|
||||
Danceability float32 `json:"danceability"`
|
||||
// The length of the track in milliseconds.
|
||||
Duration int `json:"duration_ms"`
|
||||
// Energy is a measure from 0.0 to 1.0 and represents a perceptual mesaure
|
||||
// of intensity and activity. Typically, energetic tracks feel fast, loud,
|
||||
// and noisy.
|
||||
Energy float32 `json:"energy"`
|
||||
// The Spotify ID for the track.
|
||||
ID ID `json:"id"`
|
||||
// Predicts whether a track contains no vocals. "Ooh" and "aah" sounds are
|
||||
// treated as instrumental in this context. Rap or spoken words are clearly
|
||||
// "vocal". The closer the Instrumentalness value is to 1.0, the greater
|
||||
// likelihood the track contains no vocal content. Values above 0.5 are
|
||||
// intended to represent instrumental tracks, but confidence is higher as the
|
||||
// value approaches 1.0.
|
||||
Instrumentalness float32 `json:"instrumentalness"`
|
||||
// The key the track is in. Integers map to pitches using standard Pitch Class notation
|
||||
// (https://en.wikipedia.org/wiki/Pitch_class).
|
||||
Key int `json:"key"`
|
||||
// Detects the presence of an audience in the recording. Higher liveness
|
||||
// values represent an increased probability that the track was performed live.
|
||||
// A value above 0.8 provides strong likelihook that the track is live.
|
||||
Liveness float32 `json:"liveness"`
|
||||
// The overall loudness of a track in decibels (dB). Loudness values are
|
||||
// averaged across the entire track and are useful for comparing the relative
|
||||
// loudness of tracks. Typical values range between -60 and 0 dB.
|
||||
Loudness float32 `json:"loudness"`
|
||||
// Mode indicates the modality (major or minor) of a track.
|
||||
Mode int `json:"mode"`
|
||||
// Detects the presence of spoken words in a track. The more exclusively
|
||||
// speech-like the recording, the closer to 1.0 the speechiness will be.
|
||||
// Values above 0.66 describe tracks that are probably made entirely of
|
||||
// spoken words. Values between 0.33 and 0.66 describe tracks that may
|
||||
// contain both music and speech, including such cases as rap music.
|
||||
// Values below 0.33 most likely represent music and other non-speech-like tracks.
|
||||
Speechiness float32 `json:"speechiness"`
|
||||
// The overall estimated tempo of the track in beats per minute (BPM).
|
||||
Tempo float32 `json:"tempo"`
|
||||
// An estimated overall time signature of a track. The time signature (meter)
|
||||
// is a notational convention to specify how many beats are in each bar (or measure).
|
||||
TimeSignature int `json:"time_signature"`
|
||||
// A link to the Web API endpoint providing full details of the track.
|
||||
TrackURL string `json:"track_href"`
|
||||
// The Spotify URI for the track.
|
||||
URI URI `json:"uri"`
|
||||
// A measure from 0.0 to 1.0 describing the musical positiveness conveyed
|
||||
// by a track. Tracks with high valence sound more positive (e.g. happy,
|
||||
// cheerful, euphoric), while tracks with low valence sound more negative
|
||||
// (e.g. sad, depressed, angry).
|
||||
Valence float32 `json:"valence"`
|
||||
}
|
||||
|
||||
// Key represents a pitch using Pitch Class notation.
|
||||
type Key int
|
||||
|
||||
const (
|
||||
C Key = iota
|
||||
CSharp
|
||||
D
|
||||
DSharp
|
||||
E
|
||||
F
|
||||
FSharp
|
||||
G
|
||||
GSharp
|
||||
A
|
||||
ASharp
|
||||
B
|
||||
DFlat = CSharp
|
||||
EFlat = DSharp
|
||||
GFlat = FSharp
|
||||
AFlat = GSharp
|
||||
BFlat = ASharp
|
||||
)
|
||||
|
||||
// Mode indicates the modality (major or minor) of a track.
|
||||
type Mode int
|
||||
|
||||
const (
|
||||
Minor Mode = iota
|
||||
Major
|
||||
)
|
||||
|
||||
// GetAudioFeatures queries the Spotify Web API for various
|
||||
// high-level acoustic attributes of audio tracks.
|
||||
// Objects are returned in the order requested. If an object
|
||||
// is not found, a nil value is returned in the appropriate position.
|
||||
func (c *Client) GetAudioFeatures(ids ...ID) ([]*AudioFeatures, error) {
|
||||
url := fmt.Sprintf("%saudio-features?ids=%s", c.baseURL, strings.Join(toStringSlice(ids), ","))
|
||||
|
||||
temp := struct {
|
||||
F []*AudioFeatures `json:"audio_features"`
|
||||
}{}
|
||||
|
||||
err := c.get(url, &temp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return temp.F, nil
|
||||
}
|
180
vendor/github.com/zmb3/spotify/auth.go
generated
vendored
Normal file
180
vendor/github.com/zmb3/spotify/auth.go
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
package spotify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
const (
|
||||
// AuthURL is the URL to Spotify Accounts Service's OAuth2 endpoint.
|
||||
AuthURL = "https://accounts.spotify.com/authorize"
|
||||
// TokenURL is the URL to the Spotify Accounts Service's OAuth2
|
||||
// token endpoint.
|
||||
TokenURL = "https://accounts.spotify.com/api/token"
|
||||
)
|
||||
|
||||
// Scopes let you specify exactly which types of data your application wants to access.
|
||||
// The set of scopes you pass in your authentication request determines what access the
|
||||
// permissions the user is asked to grant.
|
||||
const (
|
||||
// ScopeImageUpload seeks permission to upload images to Spotify on your behalf.
|
||||
ScopeImageUpload = "ugc-image-upload"
|
||||
// ScopePlaylistReadPrivate seeks permission to read
|
||||
// a user's private playlists.
|
||||
ScopePlaylistReadPrivate = "playlist-read-private"
|
||||
// ScopePlaylistModifyPublic seeks write access
|
||||
// to a user's public playlists.
|
||||
ScopePlaylistModifyPublic = "playlist-modify-public"
|
||||
// ScopePlaylistModifyPrivate seeks write access to
|
||||
// a user's private playlists.
|
||||
ScopePlaylistModifyPrivate = "playlist-modify-private"
|
||||
// ScopePlaylistReadCollaborative seeks permission to
|
||||
// access a user's collaborative playlists.
|
||||
ScopePlaylistReadCollaborative = "playlist-read-collaborative"
|
||||
// ScopeUserFollowModify seeks write/delete access to
|
||||
// the list of artists and other users that a user follows.
|
||||
ScopeUserFollowModify = "user-follow-modify"
|
||||
// ScopeUserFollowRead seeks read access to the list of
|
||||
// artists and other users that a user follows.
|
||||
ScopeUserFollowRead = "user-follow-read"
|
||||
// ScopeUserLibraryModify seeks write/delete access to a
|
||||
// user's "Your Music" library.
|
||||
ScopeUserLibraryModify = "user-library-modify"
|
||||
// ScopeUserLibraryRead seeks read access to a user's "Your Music" library.
|
||||
ScopeUserLibraryRead = "user-library-read"
|
||||
// ScopeUserReadPrivate seeks read access to a user's
|
||||
// subsription details (type of user account).
|
||||
ScopeUserReadPrivate = "user-read-private"
|
||||
// ScopeUserReadEmail seeks read access to a user's email address.
|
||||
ScopeUserReadEmail = "user-read-email"
|
||||
// ScopeUserReadBirthdate seeks read access to a user's birthdate.
|
||||
ScopeUserReadBirthdate = "user-read-birthdate"
|
||||
// ScopeUserReadCurrentlyPlaying seeks read access to a user's currently playing track
|
||||
ScopeUserReadCurrentlyPlaying = "user-read-currently-playing"
|
||||
// ScopeUserReadPlaybackState seeks read access to the user's current playback state
|
||||
ScopeUserReadPlaybackState = "user-read-playback-state"
|
||||
// ScopeUserModifyPlaybackState seeks write access to the user's current playback state
|
||||
ScopeUserModifyPlaybackState = "user-modify-playback-state"
|
||||
// ScopeUserReadRecentlyPlayed allows access to a user's recently-played songs
|
||||
ScopeUserReadRecentlyPlayed = "user-read-recently-played"
|
||||
// ScopeUserTopRead seeks read access to a user's top tracks and artists
|
||||
ScopeUserTopRead = "user-top-read"
|
||||
)
|
||||
|
||||
// Authenticator provides convenience functions for implementing the OAuth2 flow.
|
||||
// You should always use `NewAuthenticator` to make them.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// a := spotify.NewAuthenticator(redirectURL, spotify.ScopeUserLibaryRead, spotify.ScopeUserFollowRead)
|
||||
// // direct user to Spotify to log in
|
||||
// http.Redirect(w, r, a.AuthURL("state-string"), http.StatusFound)
|
||||
//
|
||||
// // then, in redirect handler:
|
||||
// token, err := a.Token(state, r)
|
||||
// client := a.NewClient(token)
|
||||
//
|
||||
type Authenticator struct {
|
||||
config *oauth2.Config
|
||||
context context.Context
|
||||
}
|
||||
|
||||
// NewAuthenticator creates an authenticator which is used to implement the
|
||||
// OAuth2 authorization flow. The redirectURL must exactly match one of the
|
||||
// URLs specified in your Spotify developer account.
|
||||
//
|
||||
// By default, NewAuthenticator pulls your client ID and secret key from the
|
||||
// SPOTIFY_ID and SPOTIFY_SECRET environment variables. If you'd like to provide
|
||||
// them from some other source, you can call `SetAuthInfo(id, key)` on the
|
||||
// returned authenticator.
|
||||
func NewAuthenticator(redirectURL string, scopes ...string) Authenticator {
|
||||
cfg := &oauth2.Config{
|
||||
ClientID: os.Getenv("SPOTIFY_ID"),
|
||||
ClientSecret: os.Getenv("SPOTIFY_SECRET"),
|
||||
RedirectURL: redirectURL,
|
||||
Scopes: scopes,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: AuthURL,
|
||||
TokenURL: TokenURL,
|
||||
},
|
||||
}
|
||||
|
||||
// disable HTTP/2 for DefaultClient, see: https://github.com/zmb3/spotify/issues/20
|
||||
tr := &http.Transport{
|
||||
TLSNextProto: map[string]func(authority string, c *tls.Conn) http.RoundTripper{},
|
||||
}
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{Transport: tr})
|
||||
return Authenticator{
|
||||
config: cfg,
|
||||
context: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// SetAuthInfo overwrites the client ID and secret key used by the authenticator.
|
||||
// You can use this if you don't want to store this information in environment variables.
|
||||
func (a *Authenticator) SetAuthInfo(clientID, secretKey string) {
|
||||
a.config.ClientID = clientID
|
||||
a.config.ClientSecret = secretKey
|
||||
}
|
||||
|
||||
// AuthURL returns a URL to the the Spotify Accounts Service's OAuth2 endpoint.
|
||||
//
|
||||
// State is a token to protect the user from CSRF attacks. You should pass the
|
||||
// same state to `Token`, where it will be validated. For more info, refer to
|
||||
// http://tools.ietf.org/html/rfc6749#section-10.12.
|
||||
func (a Authenticator) AuthURL(state string) string {
|
||||
return a.config.AuthCodeURL(state)
|
||||
}
|
||||
|
||||
// Token pulls an authorization code from an HTTP request and attempts to exchange
|
||||
// it for an access token. The standard use case is to call Token from the handler
|
||||
// that handles requests to your application's redirect URL.
|
||||
func (a Authenticator) Token(state string, r *http.Request) (*oauth2.Token, error) {
|
||||
values := r.URL.Query()
|
||||
if e := values.Get("error"); e != "" {
|
||||
return nil, errors.New("spotify: auth failed - " + e)
|
||||
}
|
||||
code := values.Get("code")
|
||||
if code == "" {
|
||||
return nil, errors.New("spotify: didn't get access code")
|
||||
}
|
||||
actualState := values.Get("state")
|
||||
if actualState != state {
|
||||
return nil, errors.New("spotify: redirect state parameter doesn't match")
|
||||
}
|
||||
return a.config.Exchange(a.context, code)
|
||||
}
|
||||
|
||||
// Exchange is like Token, except it allows you to manually specify the access
|
||||
// code instead of pulling it out of an HTTP request.
|
||||
func (a Authenticator) Exchange(code string) (*oauth2.Token, error) {
|
||||
return a.config.Exchange(a.context, code)
|
||||
}
|
||||
|
||||
// NewClient creates a Client that will use the specified access token for its API requests.
|
||||
func (a Authenticator) NewClient(token *oauth2.Token) Client {
|
||||
client := a.config.Client(a.context, token)
|
||||
return Client{
|
||||
http: client,
|
||||
baseURL: baseAddress,
|
||||
}
|
||||
}
|
||||
|
||||
// Token gets the client's current token.
|
||||
func (c *Client) Token() (*oauth2.Token, error) {
|
||||
transport, ok := c.http.Transport.(*oauth2.Transport)
|
||||
if !ok {
|
||||
return nil, errors.New("spotify: oauth2 transport type not correct")
|
||||
}
|
||||
t, err := transport.Source.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
140
vendor/github.com/zmb3/spotify/category.go
generated
vendored
Normal file
140
vendor/github.com/zmb3/spotify/category.go
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
package spotify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Category is used by Spotify to tag items in. For example, on the Spotify
|
||||
// player's "Browse" tab.
|
||||
type Category struct {
|
||||
// A link to the Web API endpoint returning full details of the category
|
||||
Endpoint string `json:"href"`
|
||||
// The category icon, in various sizes
|
||||
Icons []Image `json:"icons"`
|
||||
// The Spotify category ID. This isn't a base-62 Spotify ID, its just
|
||||
// a short string that describes and identifies the category (ie "party").
|
||||
ID string `json:"id"`
|
||||
// The name of the category
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// GetCategoryOpt is like GetCategory, but it accepts optional arguments.
|
||||
// The country parameter is an ISO 3166-1 alpha-2 country code. It can be
|
||||
// used to ensure that the category exists for a particular country. The
|
||||
// locale argument is an ISO 639 language code and an ISO 3166-1 alpha-2
|
||||
// country code, separated by an underscore. It can be used to get the
|
||||
// category strings in a particular language (for example: "es_MX" means
|
||||
// get categories in Mexico, returned in Spanish).
|
||||
//
|
||||
// This call requries authorization.
|
||||
func (c *Client) GetCategoryOpt(id, country, locale string) (Category, error) {
|
||||
cat := Category{}
|
||||
spotifyURL := fmt.Sprintf("%sbrowse/categories/%s", c.baseURL, id)
|
||||
values := url.Values{}
|
||||
if country != "" {
|
||||
values.Set("country", country)
|
||||
}
|
||||
if locale != "" {
|
||||
values.Set("locale", locale)
|
||||
}
|
||||
if query := values.Encode(); query != "" {
|
||||
spotifyURL += "?" + query
|
||||
}
|
||||
|
||||
err := c.get(spotifyURL, &cat)
|
||||
if err != nil {
|
||||
return cat, err
|
||||
}
|
||||
|
||||
return cat, err
|
||||
}
|
||||
|
||||
// GetCategory gets a single category used to tag items in Spotify
|
||||
// (on, for example, the Spotify player's Browse tab).
|
||||
func (c *Client) GetCategory(id string) (Category, error) {
|
||||
return c.GetCategoryOpt(id, "", "")
|
||||
}
|
||||
|
||||
// GetCategoryPlaylists gets a list of Spotify playlists tagged with a paricular category.
|
||||
func (c *Client) GetCategoryPlaylists(catID string) (*SimplePlaylistPage, error) {
|
||||
return c.GetCategoryPlaylistsOpt(catID, nil)
|
||||
}
|
||||
|
||||
// GetCategoryPlaylistsOpt is like GetCategoryPlaylists, but it accepts optional
|
||||
// arguments.
|
||||
func (c *Client) GetCategoryPlaylistsOpt(catID string, opt *Options) (*SimplePlaylistPage, error) {
|
||||
spotifyURL := fmt.Sprintf("%sbrowse/categories/%s/playlists", c.baseURL, catID)
|
||||
if opt != nil {
|
||||
values := url.Values{}
|
||||
if opt.Country != nil {
|
||||
values.Set("country", *opt.Country)
|
||||
}
|
||||
if opt.Limit != nil {
|
||||
values.Set("limit", strconv.Itoa(*opt.Limit))
|
||||
}
|
||||
if opt.Offset != nil {
|
||||
values.Set("offset", strconv.Itoa(*opt.Offset))
|
||||
}
|
||||
if query := values.Encode(); query != "" {
|
||||
spotifyURL += "?" + query
|
||||
}
|
||||
}
|
||||
|
||||
wrapper := struct {
|
||||
Playlists SimplePlaylistPage `json:"playlists"`
|
||||
}{}
|
||||
|
||||
err := c.get(spotifyURL, &wrapper)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &wrapper.Playlists, nil
|
||||
}
|
||||
|
||||
// GetCategories gets a list of categories used to tag items in Spotify
|
||||
// (on, for example, the Spotify player's "Browse" tab).
|
||||
func (c *Client) GetCategories() (*CategoryPage, error) {
|
||||
return c.GetCategoriesOpt(nil, "")
|
||||
}
|
||||
|
||||
// GetCategoriesOpt is like GetCategories, but it accepts optional parameters.
|
||||
//
|
||||
// The locale option can be used to get the results in a particular language.
|
||||
// It consists of an ISO 639 language code and an ISO 3166-1 alpha-2 country
|
||||
// code, separated by an underscore. Specify the empty string to have results
|
||||
// returned in the Spotify default language (American English).
|
||||
func (c *Client) GetCategoriesOpt(opt *Options, locale string) (*CategoryPage, error) {
|
||||
spotifyURL := c.baseURL + "browse/categories"
|
||||
values := url.Values{}
|
||||
if locale != "" {
|
||||
values.Set("locale", locale)
|
||||
}
|
||||
if opt != nil {
|
||||
if opt.Country != nil {
|
||||
values.Set("country", *opt.Country)
|
||||
}
|
||||
if opt.Limit != nil {
|
||||
values.Set("limit", strconv.Itoa(*opt.Limit))
|
||||
}
|
||||
if opt.Offset != nil {
|
||||
values.Set("offset", strconv.Itoa(*opt.Offset))
|
||||
}
|
||||
}
|
||||
if query := values.Encode(); query != "" {
|
||||
spotifyURL += "?" + query
|
||||
}
|
||||
|
||||
wrapper := struct {
|
||||
Categories CategoryPage `json:"categories"`
|
||||
}{}
|
||||
|
||||
err := c.get(spotifyURL, &wrapper)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &wrapper.Categories, nil
|
||||
}
|
32
vendor/github.com/zmb3/spotify/countries.go
generated
vendored
Normal file
32
vendor/github.com/zmb3/spotify/countries.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package spotify
|
||||
|
||||
// ISO 3166-1 alpha 2 country codes.
|
||||
//
|
||||
// see: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
|
||||
const (
|
||||
CountryArgentina = "AR"
|
||||
CountryAustralia = "AU"
|
||||
CountryAustria = "AT"
|
||||
CountryBelarus = "BY"
|
||||
CountryBelgium = "BE"
|
||||
CountryBrazil = "BR"
|
||||
CountryCanada = "CA"
|
||||
CountryChile = "CL"
|
||||
CountryChina = "CN"
|
||||
CountryGermany = "DE"
|
||||
CountryHongKong = "HK"
|
||||
CountryIreland = "IE"
|
||||
CountryIndia = "IN"
|
||||
CountryItaly = "IT"
|
||||
CountryJapan = "JP"
|
||||
CountrySpain = "ES"
|
||||
CountryFinland = "FI"
|
||||
CountryFrance = "FR"
|
||||
CountryMexico = "MX"
|
||||
CountryNewZealand = "NZ"
|
||||
CountryRussia = "RU"
|
||||
CountrySwitzerland = "CH"
|
||||
CountryUnitedArabEmirates = "AE"
|
||||
CountryUnitedKingdom = "GB"
|
||||
CountryUSA = "US"
|
||||
)
|
37
vendor/github.com/zmb3/spotify/cursor.go
generated
vendored
Normal file
37
vendor/github.com/zmb3/spotify/cursor.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
package spotify
|
||||
|
||||
// This file contains the types that implement Spotify's cursor-based
|
||||
// paging object. Like the standard paging object, this object is a
|
||||
// container for a set of items. Unlike the standard paging object, a
|
||||
// cursor-based paging object does not provide random access to the results.
|
||||
|
||||
// Cursor contains a key that can be used to find the next set
|
||||
// of items.
|
||||
type Cursor struct {
|
||||
After string `json:"after"`
|
||||
}
|
||||
|
||||
// cursorPage contains all of the fields in a Spotify cursor-based
|
||||
// paging object, except for the actual items. This type is meant
|
||||
// to be embedded in other types that add the Items field.
|
||||
type cursorPage struct {
|
||||
// A link to the Web API endpoint returning the full
|
||||
// result of this request.
|
||||
Endpoint string `json:"href"`
|
||||
// The maximum number of items returned, as set in the query
|
||||
// (or default value if unset).
|
||||
Limit int `json:"limit"`
|
||||
// The URL to the next set of items.
|
||||
Next string `json:"next"`
|
||||
// The total number of items available to return.
|
||||
Total int `json:"total"`
|
||||
// The cursor used to find the next set of items.
|
||||
Cursor Cursor `json:"cursors"`
|
||||
}
|
||||
|
||||
// FullArtistCursorPage is a cursor-based paging object containing
|
||||
// a set of FullArtist objects.
|
||||
type FullArtistCursorPage struct {
|
||||
cursorPage
|
||||
Artists []FullArtist `json:"items"`
|
||||
}
|
5
vendor/github.com/zmb3/spotify/full_tests.bat
generated
vendored
Normal file
5
vendor/github.com/zmb3/spotify/full_tests.bat
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
REM - The tests that actually hit the Spotify Web API don't run by default.
|
||||
REM - Use this script to run them in addition to the standard unit tests.
|
||||
|
||||
cmd /C "set FULLTEST=y && go test %*"
|
61
vendor/github.com/zmb3/spotify/library.go
generated
vendored
Normal file
61
vendor/github.com/zmb3/spotify/library.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package spotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UserHasTracks checks if one or more tracks are saved to the current user's
|
||||
// "Your Music" library.
|
||||
func (c *Client) UserHasTracks(ids ...ID) ([]bool, error) {
|
||||
if l := len(ids); l == 0 || l > 50 {
|
||||
return nil, errors.New("spotify: UserHasTracks supports 1 to 50 IDs per call")
|
||||
}
|
||||
spotifyURL := fmt.Sprintf("%sme/tracks/contains?ids=%s", c.baseURL, strings.Join(toStringSlice(ids), ","))
|
||||
|
||||
var result []bool
|
||||
|
||||
err := c.get(spotifyURL, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
// AddTracksToLibrary saves one or more tracks to the current user's
|
||||
// "Your Music" library. This call requires the ScopeUserLibraryModify scope.
|
||||
// A track can only be saved once; duplicate IDs are ignored.
|
||||
func (c *Client) AddTracksToLibrary(ids ...ID) error {
|
||||
return c.modifyLibraryTracks(true, ids...)
|
||||
}
|
||||
|
||||
// RemoveTracksFromLibrary removes one or more tracks from the current user's
|
||||
// "Your Music" library. This call requires the ScopeUserModifyLibrary scope.
|
||||
// Trying to remove a track when you do not have the user's authorization
|
||||
// results in a `spotify.Error` with the status code set to http.StatusUnauthorized.
|
||||
func (c *Client) RemoveTracksFromLibrary(ids ...ID) error {
|
||||
return c.modifyLibraryTracks(false, ids...)
|
||||
}
|
||||
|
||||
func (c *Client) modifyLibraryTracks(add bool, ids ...ID) error {
|
||||
if l := len(ids); l == 0 || l > 50 {
|
||||
return errors.New("spotify: this call supports 1 to 50 IDs per call")
|
||||
}
|
||||
spotifyURL := fmt.Sprintf("%sme/tracks?ids=%s", c.baseURL, strings.Join(toStringSlice(ids), ","))
|
||||
method := "DELETE"
|
||||
if add {
|
||||
method = "PUT"
|
||||
}
|
||||
req, err := http.NewRequest(method, spotifyURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.execute(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
87
vendor/github.com/zmb3/spotify/page.go
generated
vendored
Normal file
87
vendor/github.com/zmb3/spotify/page.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
package spotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ErrNoMorePages is the error returned when you attempt to get the next
|
||||
// (or previous) set of data but you've reached the end of the data set.
|
||||
var ErrNoMorePages = errors.New("spotify: no more pages")
|
||||
|
||||
// This file contains the types that implement Spotify's paging object.
|
||||
// See: https://developer.spotify.com/web-api/object-model/#paging-object
|
||||
|
||||
// basePage contains all of the fields in a Spotify paging object, except
|
||||
// for the actual items. This type is meant to be embedded in other types
|
||||
// that add the Items field.
|
||||
type basePage struct {
|
||||
// A link to the Web API Endpoint returning the full
|
||||
// result of this request.
|
||||
Endpoint string `json:"href"`
|
||||
// The maximum number of items in the response, as set
|
||||
// in the query (or default value if unset).
|
||||
Limit int `json:"limit"`
|
||||
// The offset of the items returned, as set in the query
|
||||
// (or default value if unset).
|
||||
Offset int `json:"offset"`
|
||||
// The total number of items available to return.
|
||||
Total int `json:"total"`
|
||||
// The URL to the next page of items (if available).
|
||||
Next string `json:"next"`
|
||||
// The URL to the previous page of items (if available).
|
||||
Previous string `json:"previous"`
|
||||
}
|
||||
|
||||
// FullArtistPage contains FullArtists returned by the Web API.
|
||||
type FullArtistPage struct {
|
||||
basePage
|
||||
Artists []FullArtist `json:"items"`
|
||||
}
|
||||
|
||||
// SimpleAlbumPage contains SimpleAlbums returned by the Web API.
|
||||
type SimpleAlbumPage struct {
|
||||
basePage
|
||||
Albums []SimpleAlbum `json:"items"`
|
||||
}
|
||||
|
||||
// SavedAlbumPage contains SavedAlbums returned by the Web API.
|
||||
type SavedAlbumPage struct {
|
||||
basePage
|
||||
Albums []SavedAlbum `json:"items"`
|
||||
}
|
||||
|
||||
// SimplePlaylistPage contains SimplePlaylists returned by the Web API.
|
||||
type SimplePlaylistPage struct {
|
||||
basePage
|
||||
Playlists []SimplePlaylist `json:"items"`
|
||||
}
|
||||
|
||||
// SimpleTrackPage contains SimpleTracks returned by the Web API.
|
||||
type SimpleTrackPage struct {
|
||||
basePage
|
||||
Tracks []SimpleTrack `json:"items"`
|
||||
}
|
||||
|
||||
// FullTrackPage contains FullTracks returned by the Web API.
|
||||
type FullTrackPage struct {
|
||||
basePage
|
||||
Tracks []FullTrack `json:"items"`
|
||||
}
|
||||
|
||||
// SavedTrackPage contains SavedTracks return by the Web API.
|
||||
type SavedTrackPage struct {
|
||||
basePage
|
||||
Tracks []SavedTrack `json:"items"`
|
||||
}
|
||||
|
||||
// PlaylistTrackPage contains information about tracks in a playlist.
|
||||
type PlaylistTrackPage struct {
|
||||
basePage
|
||||
Tracks []PlaylistTrack `json:"items"`
|
||||
}
|
||||
|
||||
// CategoryPage contains Category objects returned by the Web API.
|
||||
type CategoryPage struct {
|
||||
basePage
|
||||
Categories []Category `json:"items"`
|
||||
}
|
528
vendor/github.com/zmb3/spotify/player.go
generated
vendored
Executable file
528
vendor/github.com/zmb3/spotify/player.go
generated
vendored
Executable file
@ -0,0 +1,528 @@
|
||||
package spotify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PlayerDevice contains information about a device that a user can play music on
|
||||
type PlayerDevice struct {
|
||||
// ID of the device. This may be empty.
|
||||
ID ID `json:"id"`
|
||||
// Active If this device is the currently active device.
|
||||
Active bool `json:"is_active"`
|
||||
// Restricted Whether controlling this device is restricted. At present if
|
||||
// this is "true" then no Web API commands will be accepted by this device.
|
||||
Restricted bool `json:"is_restricted"`
|
||||
// Name The name of the device.
|
||||
Name string `json:"name"`
|
||||
// Type of device, such as "Computer", "Smartphone" or "Speaker".
|
||||
Type string `json:"type"`
|
||||
// Volume The current volume in percent.
|
||||
Volume int `json:"volume_percent"`
|
||||
}
|
||||
|
||||
// PlayerState contains information about the current playback.
|
||||
type PlayerState struct {
|
||||
CurrentlyPlaying
|
||||
// Device The device that is currently active
|
||||
Device PlayerDevice `json:"device"`
|
||||
// ShuffleState Shuffle is on or off
|
||||
ShuffleState bool `json:"shuffle_state"`
|
||||
// RepeatState off, track, context
|
||||
RepeatState string `json:"repeat_state"`
|
||||
}
|
||||
|
||||
// PlaybackContext is the playback context
|
||||
type PlaybackContext struct {
|
||||
// ExternalURLs of the context, or null if not available.
|
||||
ExternalURLs map[string]string `json:"external_urls"`
|
||||
// Endpoint of the context, or null if not available.
|
||||
Endpoint string `json:"href"`
|
||||
// Type of the item's context. Can be one of album, artist or playlist.
|
||||
Type string `json:"type"`
|
||||
// URI is the Spotify URI for the context.
|
||||
URI URI `json:"uri"`
|
||||
}
|
||||
|
||||
// CurrentlyPlaying contains the information about currently playing items
|
||||
type CurrentlyPlaying struct {
|
||||
// Timestamp when data was fetched
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
// PlaybackContext current context
|
||||
PlaybackContext PlaybackContext `json:"context"`
|
||||
// Progress into the currently playing track.
|
||||
Progress int `json:"progress_ms"`
|
||||
// Playing If something is currently playing.
|
||||
Playing bool `json:"is_playing"`
|
||||
// The currently playing track. Can be null.
|
||||
Item *FullTrack `json:"Item"`
|
||||
}
|
||||
|
||||
type RecentlyPlayedItem struct {
|
||||
// Track is the track information
|
||||
Track SimpleTrack `json:"track"`
|
||||
|
||||
// PlayedAt is the time that this song was played
|
||||
PlayedAt time.Time `json:"played_at"`
|
||||
|
||||
// PlaybackContext is the current playback context
|
||||
PlaybackContext PlaybackContext `json:"context"`
|
||||
}
|
||||
|
||||
type RecentlyPlayedResult struct {
|
||||
Items []RecentlyPlayedItem `json:"items"`
|
||||
}
|
||||
|
||||
// PlaybackOffset can be specified either by track URI OR Position. If both are present the
|
||||
// request will return 400 BAD REQUEST. If incorrect values are provided for position or uri,
|
||||
// the request may be accepted but with an unpredictable resulting action on playback.
|
||||
type PlaybackOffset struct {
|
||||
// Position is zero based and can’t be negative.
|
||||
Position int `json:"position,omitempty"`
|
||||
// URI is a string representing the uri of the item to start at.
|
||||
URI URI `json:"uri,omitempty"`
|
||||
}
|
||||
|
||||
type PlayOptions struct {
|
||||
// DeviceID The id of the device this command is targeting. If not
|
||||
// supplied, the user's currently active device is the target.
|
||||
DeviceID *ID `json:"-"`
|
||||
// PlaybackContext Spotify URI of the context to play.
|
||||
// Valid contexts are albums, artists & playlists.
|
||||
PlaybackContext *URI `json:"context_uri,omitempty"`
|
||||
// URIs Array of the Spotify track URIs to play
|
||||
URIs []URI `json:"uris,omitempty"`
|
||||
// PlaybackOffset Indicates from where in the context playback should start.
|
||||
// Only available when context corresponds to an album or playlist
|
||||
// object, or when the URIs parameter is used.
|
||||
PlaybackOffset *PlaybackOffset `json:"offset,omitempty"`
|
||||
// PositionMs Indicates from what position to start playback.
|
||||
// Must be a positive number. Passing in a position that is greater
|
||||
// than the length of the track will cause the player to start playing the next song.
|
||||
// Defaults to 0, starting a track from the beginning.
|
||||
PositionMs int `json:"position_ms,omitempty"`
|
||||
}
|
||||
|
||||
// RecentlyPlayedOptions describes options for the recently-played request. All
|
||||
// fields are optional. Only one of `AfterEpochMs` and `BeforeEpochMs` may be
|
||||
// given. Note that it seems as if Spotify only remembers the fifty most-recent
|
||||
// tracks as of right now.
|
||||
type RecentlyPlayedOptions struct {
|
||||
// Limit is the maximum number of items to return. Must be no greater than
|
||||
// fifty.
|
||||
Limit int
|
||||
|
||||
// AfterEpochMs is a Unix epoch in milliseconds that describes a time after
|
||||
// which to return songs.
|
||||
AfterEpochMs int64
|
||||
|
||||
// BeforeEpochMs is a Unix epoch in milliseconds that describes a time
|
||||
// before which to return songs.
|
||||
BeforeEpochMs int64
|
||||
}
|
||||
|
||||
// PlayerDevices information about available devices for the current user.
|
||||
//
|
||||
// Requires the ScopeUserReadPlaybackState scope in order to read information
|
||||
func (c *Client) PlayerDevices() ([]PlayerDevice, error) {
|
||||
var result struct {
|
||||
PlayerDevices []PlayerDevice `json:"devices"`
|
||||
}
|
||||
|
||||
err := c.get(c.baseURL+"me/player/devices", &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result.PlayerDevices, nil
|
||||
}
|
||||
|
||||
// PlayerState gets information about the playing state for the current user
|
||||
//
|
||||
// Requires the ScopeUserReadPlaybackState scope in order to read information
|
||||
func (c *Client) PlayerState() (*PlayerState, error) {
|
||||
return c.PlayerStateOpt(nil)
|
||||
}
|
||||
|
||||
// PlayerStateOpt is like PlayerState, but it accepts additional
|
||||
// options for sorting and filtering the results.
|
||||
func (c *Client) PlayerStateOpt(opt *Options) (*PlayerState, error) {
|
||||
spotifyURL := c.baseURL + "me/player"
|
||||
if opt != nil {
|
||||
v := url.Values{}
|
||||
if opt.Country != nil {
|
||||
v.Set("market", *opt.Country)
|
||||
}
|
||||
if params := v.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
}
|
||||
|
||||
var result PlayerState
|
||||
|
||||
err := c.get(spotifyURL, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// PlayerCurrentlyPlaying gets information about the currently playing status
|
||||
// for the current user.
|
||||
//
|
||||
// Requires the ScopeUserReadCurrentlyPlaying scope or the ScopeUserReadPlaybackState
|
||||
// scope in order to read information
|
||||
func (c *Client) PlayerCurrentlyPlaying() (*CurrentlyPlaying, error) {
|
||||
return c.PlayerCurrentlyPlayingOpt(nil)
|
||||
}
|
||||
|
||||
// PlayerCurrentlyPlayingOpt is like PlayerCurrentlyPlaying, but it accepts
|
||||
// additional options for sorting and filtering the results.
|
||||
func (c *Client) PlayerCurrentlyPlayingOpt(opt *Options) (*CurrentlyPlaying, error) {
|
||||
spotifyURL := c.baseURL + "me/player/currently-playing"
|
||||
if opt != nil {
|
||||
v := url.Values{}
|
||||
if opt.Country != nil {
|
||||
v.Set("market", *opt.Country)
|
||||
}
|
||||
if params := v.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", spotifyURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result CurrentlyPlaying
|
||||
err = c.execute(req, &result, http.StatusNoContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// PlayerRecentlyPlayed gets a list of recently-played tracks for the current
|
||||
// user. This call requires ScopeUserReadRecentlyPlayed.
|
||||
func (c *Client) PlayerRecentlyPlayed() ([]RecentlyPlayedItem, error) {
|
||||
return c.PlayerRecentlyPlayedOpt(nil)
|
||||
}
|
||||
|
||||
// PlayerRecentlyPlayedOpt is like PlayerRecentlyPlayed, but it accepts
|
||||
// additional options for sorting and filtering the results.
|
||||
func (c *Client) PlayerRecentlyPlayedOpt(opt *RecentlyPlayedOptions) ([]RecentlyPlayedItem, error) {
|
||||
spotifyURL := c.baseURL + "me/player/recently-played"
|
||||
if opt != nil {
|
||||
v := url.Values{}
|
||||
if opt.Limit != 0 {
|
||||
v.Set("limit", strconv.FormatInt(int64(opt.Limit), 10))
|
||||
}
|
||||
if opt.BeforeEpochMs != 0 {
|
||||
v.Set("before", strconv.FormatInt(int64(opt.BeforeEpochMs), 10))
|
||||
}
|
||||
if opt.AfterEpochMs != 0 {
|
||||
v.Set("after", strconv.FormatInt(int64(opt.AfterEpochMs), 10))
|
||||
}
|
||||
if params := v.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
}
|
||||
|
||||
result := RecentlyPlayedResult{}
|
||||
err := c.get(spotifyURL, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result.Items, nil
|
||||
}
|
||||
|
||||
// TransferPlayback transfers playback to a new device and determine if
|
||||
// it should start playing.
|
||||
//
|
||||
// Note that a value of false for the play parameter when also transferring
|
||||
// to another device_id will not pause playback. To ensure that playback is
|
||||
// paused on the new device you should send a pause command to the currently
|
||||
// active device before transferring to the new device_id.
|
||||
//
|
||||
// Requires the ScopeUserModifyPlaybackState in order to modify the player state
|
||||
func (c *Client) TransferPlayback(deviceID ID, play bool) error {
|
||||
reqData := struct {
|
||||
DeviceID []ID `json:"device_ids"`
|
||||
Play bool `json:"play"`
|
||||
}{
|
||||
DeviceID: []ID{deviceID},
|
||||
Play: play,
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := json.NewEncoder(buf).Encode(reqData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPut, c.baseURL+"me/player", buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.execute(req, nil, http.StatusNoContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Play Start a new context or resume current playback on the user's active
|
||||
// device. This call requires ScopeUserModifyPlaybackState in order to modify the player state.
|
||||
func (c *Client) Play() error {
|
||||
return c.PlayOpt(nil)
|
||||
}
|
||||
|
||||
// PlayOpt is like Play but with more options
|
||||
func (c *Client) PlayOpt(opt *PlayOptions) error {
|
||||
spotifyURL := c.baseURL + "me/player/play"
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if opt != nil {
|
||||
v := url.Values{}
|
||||
if opt.DeviceID != nil {
|
||||
v.Set("device_id", opt.DeviceID.String())
|
||||
}
|
||||
if params := v.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
|
||||
err := json.NewEncoder(buf).Encode(opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPut, spotifyURL, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.execute(req, nil, http.StatusNoContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pause Playback on the user's currently active device.
|
||||
//
|
||||
// Requires the ScopeUserModifyPlaybackState in order to modify the player state
|
||||
func (c *Client) Pause() error {
|
||||
return c.PauseOpt(nil)
|
||||
}
|
||||
|
||||
// PauseOpt is like Pause but with more options
|
||||
//
|
||||
// Only expects PlayOptions.DeviceID, all other options will be ignored
|
||||
func (c *Client) PauseOpt(opt *PlayOptions) error {
|
||||
spotifyURL := c.baseURL + "me/player/pause"
|
||||
|
||||
if opt != nil {
|
||||
v := url.Values{}
|
||||
if opt.DeviceID != nil {
|
||||
v.Set("device_id", opt.DeviceID.String())
|
||||
}
|
||||
if params := v.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPut, spotifyURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.execute(req, nil, http.StatusNoContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next skips to the next track in the user's queue in the user's
|
||||
// currently active device. This call requires ScopeUserModifyPlaybackState
|
||||
// in order to modify the player state
|
||||
func (c *Client) Next() error {
|
||||
return c.NextOpt(nil)
|
||||
}
|
||||
|
||||
// NextOpt is like Next but with more options
|
||||
//
|
||||
// Only expects PlayOptions.DeviceID, all other options will be ignored
|
||||
func (c *Client) NextOpt(opt *PlayOptions) error {
|
||||
spotifyURL := c.baseURL + "me/player/next"
|
||||
|
||||
if opt != nil {
|
||||
v := url.Values{}
|
||||
if opt.DeviceID != nil {
|
||||
v.Set("device_id", opt.DeviceID.String())
|
||||
}
|
||||
if params := v.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPost, spotifyURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.execute(req, nil, http.StatusNoContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Previous skips to the Previous track in the user's queue in the user's
|
||||
// currently active device. This call requires ScopeUserModifyPlaybackState
|
||||
// in order to modify the player state
|
||||
func (c *Client) Previous() error {
|
||||
return c.PreviousOpt(nil)
|
||||
}
|
||||
|
||||
// PreviousOpt is like Previous but with more options
|
||||
//
|
||||
// Only expects PlayOptions.DeviceID, all other options will be ignored
|
||||
func (c *Client) PreviousOpt(opt *PlayOptions) error {
|
||||
spotifyURL := c.baseURL + "me/player/previous"
|
||||
|
||||
if opt != nil {
|
||||
v := url.Values{}
|
||||
if opt.DeviceID != nil {
|
||||
v.Set("device_id", opt.DeviceID.String())
|
||||
}
|
||||
if params := v.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodPost, spotifyURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.execute(req, nil, http.StatusNoContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Seek to the given position in the user’s currently playing track.
|
||||
//
|
||||
// The position in milliseconds to seek to. Must be a positive number.
|
||||
// Passing in a position that is greater than the length of the track
|
||||
// will cause the player to start playing the next song.
|
||||
//
|
||||
// Requires the ScopeUserModifyPlaybackState in order to modify the player state
|
||||
func (c *Client) Seek(position int) error {
|
||||
return c.SeekOpt(position, nil)
|
||||
}
|
||||
|
||||
// SeekOpt is like Seek but with more options
|
||||
//
|
||||
// Only expects PlayOptions.DeviceID, all other options will be ignored
|
||||
func (c *Client) SeekOpt(position int, opt *PlayOptions) error {
|
||||
return c.playerFuncWithOpt(
|
||||
"me/player/seek",
|
||||
url.Values{
|
||||
"position_ms": []string{strconv.FormatInt(int64(position), 10)},
|
||||
},
|
||||
opt,
|
||||
)
|
||||
}
|
||||
|
||||
// Repeat Set the repeat mode for the user's playback.
|
||||
//
|
||||
// Options are repeat-track, repeat-context, and off.
|
||||
//
|
||||
// Requires the ScopeUserModifyPlaybackState in order to modify the player state.
|
||||
func (c *Client) Repeat(state string) error {
|
||||
return c.RepeatOpt(state, nil)
|
||||
}
|
||||
|
||||
// RepeatOpt is like Repeat but with more options
|
||||
//
|
||||
// Only expects PlayOptions.DeviceID, all other options will be ignored.
|
||||
func (c *Client) RepeatOpt(state string, opt *PlayOptions) error {
|
||||
return c.playerFuncWithOpt(
|
||||
"me/player/repeat",
|
||||
url.Values{
|
||||
"state": []string{state},
|
||||
},
|
||||
opt,
|
||||
)
|
||||
}
|
||||
|
||||
// Volume set the volume for the user's current playback device.
|
||||
//
|
||||
// Percent is must be a value from 0 to 100 inclusive.
|
||||
//
|
||||
// Requires the ScopeUserModifyPlaybackState in order to modify the player state
|
||||
func (c *Client) Volume(percent int) error {
|
||||
return c.VolumeOpt(percent, nil)
|
||||
}
|
||||
|
||||
// VolumeOpt is like Volume but with more options
|
||||
//
|
||||
// Only expects PlayOptions.DeviceID, all other options will be ignored
|
||||
func (c *Client) VolumeOpt(percent int, opt *PlayOptions) error {
|
||||
return c.playerFuncWithOpt(
|
||||
"me/player/volume",
|
||||
url.Values{
|
||||
"volume_percent": []string{strconv.FormatInt(int64(percent), 10)},
|
||||
},
|
||||
opt,
|
||||
)
|
||||
}
|
||||
|
||||
// Shuffle switches shuffle on or off for user's playback.
|
||||
//
|
||||
// Requires the ScopeUserModifyPlaybackState in order to modify the player state
|
||||
func (c *Client) Shuffle(shuffle bool) error {
|
||||
return c.ShuffleOpt(shuffle, nil)
|
||||
}
|
||||
|
||||
// ShuffleOpt is like Shuffle but with more options
|
||||
//
|
||||
// Only expects PlayOptions.DeviceID, all other options will be ignored
|
||||
func (c *Client) ShuffleOpt(shuffle bool, opt *PlayOptions) error {
|
||||
return c.playerFuncWithOpt(
|
||||
"me/player/shuffle",
|
||||
url.Values{
|
||||
"state": []string{strconv.FormatBool(shuffle)},
|
||||
},
|
||||
opt,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Client) playerFuncWithOpt(urlSuffix string, values url.Values, opt *PlayOptions) error {
|
||||
spotifyURL := c.baseURL + urlSuffix
|
||||
|
||||
if opt != nil {
|
||||
if opt.DeviceID != nil {
|
||||
values.Set("device_id", opt.DeviceID.String())
|
||||
}
|
||||
}
|
||||
|
||||
if params := values.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPut, spotifyURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.execute(req, nil, http.StatusNoContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
662
vendor/github.com/zmb3/spotify/playlist.go
generated
vendored
Normal file
662
vendor/github.com/zmb3/spotify/playlist.go
generated
vendored
Normal file
@ -0,0 +1,662 @@
|
||||
package spotify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PlaylistTracks contains details about the tracks in a playlist.
|
||||
type PlaylistTracks struct {
|
||||
// A link to the Web API endpoint where full details of
|
||||
// the playlist's tracks can be retrieved.
|
||||
Endpoint string `json:"href"`
|
||||
// The total number of tracks in the playlist.
|
||||
Total uint `json:"total"`
|
||||
}
|
||||
|
||||
// SimplePlaylist contains basic info about a Spotify playlist.
|
||||
type SimplePlaylist struct {
|
||||
// Indicates whether the playlist owner allows others to modify the playlist.
|
||||
// Note: only non-collaborative playlists are currently returned by Spotify's Web API.
|
||||
Collaborative bool `json:"collaborative"`
|
||||
ExternalURLs map[string]string `json:"external_urls"`
|
||||
// A link to the Web API endpoint providing full details of the playlist.
|
||||
Endpoint string `json:"href"`
|
||||
ID ID `json:"id"`
|
||||
// The playlist image. Note: this field is only returned for modified,
|
||||
// verified playlists. Otherwise the slice is empty. If returned, the source
|
||||
// URL for the image is temporary and will expire in less than a day.
|
||||
Images []Image `json:"images"`
|
||||
Name string `json:"name"`
|
||||
Owner User `json:"owner"`
|
||||
IsPublic bool `json:"public"`
|
||||
// The version identifier for the current playlist. Can be supplied in other
|
||||
// requests to target a specific playlist version.
|
||||
SnapshotID string `json:"snapshot_id"`
|
||||
// A collection to the Web API endpoint where full details of the playlist's
|
||||
// tracks can be retrieved, along with the total number of tracks in the playlist.
|
||||
Tracks PlaylistTracks `json:"tracks"`
|
||||
URI URI `json:"uri"`
|
||||
}
|
||||
|
||||
// FullPlaylist provides extra playlist data in addition to the data provided by SimplePlaylist.
|
||||
type FullPlaylist struct {
|
||||
SimplePlaylist
|
||||
// The playlist description. Only returned for modified, verified playlists.
|
||||
Description string `json:"description"`
|
||||
// Information about the followers of this playlist.
|
||||
Followers Followers `json:"followers"`
|
||||
Tracks PlaylistTrackPage `json:"tracks"`
|
||||
}
|
||||
|
||||
// PlaylistOptions contains optional parameters that can be used when querying
|
||||
// for featured playlists. Only the non-nil fields are used in the request.
|
||||
type PlaylistOptions struct {
|
||||
Options
|
||||
// The desired language, consisting of a lowercase IO 639
|
||||
// language code and an uppercase ISO 3166-1 alpha-2
|
||||
// country code, joined by an underscore. Provide this
|
||||
// parameter if you want the results returned in a particular
|
||||
// language. If not specified, the result will be returned
|
||||
// in the Spotify default language (American English).
|
||||
Locale *string
|
||||
// A timestamp in ISO 8601 format (yyyy-MM-ddTHH:mm:ss).
|
||||
// use this paramter to specify the user's local time to
|
||||
// get results tailored for that specific date and time
|
||||
// in the day. If not provided, the response defaults to
|
||||
// the current UTC time.
|
||||
Timestamp *string
|
||||
}
|
||||
|
||||
// FeaturedPlaylistsOpt gets a list of playlists featured by Spotify.
|
||||
// It accepts a number of optional parameters via the opt argument.
|
||||
func (c *Client) FeaturedPlaylistsOpt(opt *PlaylistOptions) (message string, playlists *SimplePlaylistPage, e error) {
|
||||
spotifyURL := c.baseURL + "browse/featured-playlists"
|
||||
if opt != nil {
|
||||
v := url.Values{}
|
||||
if opt.Locale != nil {
|
||||
v.Set("locale", *opt.Locale)
|
||||
}
|
||||
if opt.Country != nil {
|
||||
v.Set("country", *opt.Country)
|
||||
}
|
||||
if opt.Timestamp != nil {
|
||||
v.Set("timestamp", *opt.Timestamp)
|
||||
}
|
||||
if opt.Limit != nil {
|
||||
v.Set("limit", strconv.Itoa(*opt.Limit))
|
||||
}
|
||||
if opt.Offset != nil {
|
||||
v.Set("offset", strconv.Itoa(*opt.Offset))
|
||||
}
|
||||
if params := v.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Playlists SimplePlaylistPage `json:"playlists"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
err := c.get(spotifyURL, &result)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return result.Message, &result.Playlists, nil
|
||||
}
|
||||
|
||||
// FeaturedPlaylists gets a list of playlists featured by Spotify.
|
||||
// It is equivalent to c.FeaturedPlaylistsOpt(nil).
|
||||
func (c *Client) FeaturedPlaylists() (message string, playlists *SimplePlaylistPage, e error) {
|
||||
return c.FeaturedPlaylistsOpt(nil)
|
||||
}
|
||||
|
||||
// FollowPlaylist adds the current user as a follower of the specified
|
||||
// playlist. Any playlist can be followed, regardless of its private/public
|
||||
// status, as long as you know the owner and playlist ID.
|
||||
//
|
||||
// If the public argument is true, then the playlist will be included in the
|
||||
// user's public playlists. To be able to follow playlists privately, the user
|
||||
// must have granted the ScopePlaylistModifyPrivate scope. The
|
||||
// ScopePlaylistModifyPublic scope is required to follow playlists publicly.
|
||||
func (c *Client) FollowPlaylist(owner ID, playlist ID, public bool) error {
|
||||
spotifyURL := buildFollowURI(c.baseURL, owner, playlist)
|
||||
body := strings.NewReader(strconv.FormatBool(public))
|
||||
req, err := http.NewRequest("PUT", spotifyURL, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
err = c.execute(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnfollowPlaylist removes the current user as a follower of a playlist.
|
||||
// Unfollowing a publicly followed playlist requires ScopePlaylistModifyPublic.
|
||||
// Unfolowing a privately followed playlist requies ScopePlaylistModifyPrivate.
|
||||
func (c *Client) UnfollowPlaylist(owner, playlist ID) error {
|
||||
spotifyURL := buildFollowURI(c.baseURL, owner, playlist)
|
||||
req, err := http.NewRequest("DELETE", spotifyURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.execute(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildFollowURI(url string, owner, playlist ID) string {
|
||||
return fmt.Sprintf("%susers/%s/playlists/%s/followers",
|
||||
url, string(owner), string(playlist))
|
||||
}
|
||||
|
||||
// GetPlaylistsForUser gets a list of the playlists owned or followed by a
|
||||
// particular Spotify user.
|
||||
//
|
||||
// Private playlists and collaborative playlists are only retrievable for the
|
||||
// current user. In order to read private playlists, the user must have granted
|
||||
// the ScopePlaylistReadPrivate scope. Note that this scope alone will not
|
||||
// return collaborative playlists, even though they are always private. In
|
||||
// order to read collaborative playlists, the user must have granted the
|
||||
// ScopePlaylistReadCollaborative scope.
|
||||
func (c *Client) GetPlaylistsForUser(userID string) (*SimplePlaylistPage, error) {
|
||||
return c.GetPlaylistsForUserOpt(userID, nil)
|
||||
}
|
||||
|
||||
// GetPlaylistsForUserOpt is like PlaylistsForUser, but it accepts optional paramters
|
||||
// for filtering the results.
|
||||
func (c *Client) GetPlaylistsForUserOpt(userID string, opt *Options) (*SimplePlaylistPage, error) {
|
||||
spotifyURL := c.baseURL + "users/" + userID + "/playlists"
|
||||
if opt != nil {
|
||||
v := url.Values{}
|
||||
if opt.Limit != nil {
|
||||
v.Set("limit", strconv.Itoa(*opt.Limit))
|
||||
}
|
||||
if opt.Offset != nil {
|
||||
v.Set("offset", strconv.Itoa(*opt.Offset))
|
||||
}
|
||||
if params := v.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
}
|
||||
|
||||
var result SimplePlaylistPage
|
||||
|
||||
err := c.get(spotifyURL, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, err
|
||||
}
|
||||
|
||||
// GetPlaylist gets a playlist
|
||||
func (c *Client) GetPlaylist(playlistID ID) (*FullPlaylist, error) {
|
||||
return c.GetPlaylistOpt(playlistID, "")
|
||||
}
|
||||
|
||||
// GetPlaylistOpt is like GetPlaylist, but it accepts an optional fields parameter
|
||||
// that can be used to filter the query.
|
||||
//
|
||||
// fields is a comma-separated list of the fields to return.
|
||||
// See the JSON tags on the FullPlaylist struct for valid field options.
|
||||
// For example, to get just the playlist's description and URI:
|
||||
// fields = "description,uri"
|
||||
//
|
||||
// A dot separator can be used to specify non-reoccurring fields, while
|
||||
// parentheses can be used to specify reoccurring fields within objects.
|
||||
// For example, to get just the added date and the user ID of the adder:
|
||||
// fields = "tracks.items(added_at,added_by.id)"
|
||||
//
|
||||
// Use multiple parentheses to drill down into nested objects, for example:
|
||||
// fields = "tracks.items(track(name,href,album(name,href)))"
|
||||
//
|
||||
// Fields can be excluded by prefixing them with an exclamation mark, for example;
|
||||
// fields = "tracks.items(track(name,href,album(!name,href)))"
|
||||
func (c *Client) GetPlaylistOpt(playlistID ID, fields string) (*FullPlaylist, error) {
|
||||
spotifyURL := fmt.Sprintf("%splaylists/%s", c.baseURL, playlistID)
|
||||
if fields != "" {
|
||||
spotifyURL += "?fields=" + url.QueryEscape(fields)
|
||||
}
|
||||
|
||||
var playlist FullPlaylist
|
||||
|
||||
err := c.get(spotifyURL, &playlist)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &playlist, err
|
||||
}
|
||||
|
||||
// GetPlaylistTracks gets full details of the tracks in a playlist, given the
|
||||
// playlist's Spotify ID.
|
||||
func (c *Client) GetPlaylistTracks(playlistID ID) (*PlaylistTrackPage, error) {
|
||||
return c.GetPlaylistTracksOpt(playlistID, nil, "")
|
||||
}
|
||||
|
||||
// GetPlaylistTracksOpt is like GetPlaylistTracks, but it accepts optional parameters
|
||||
// for sorting and filtering the results.
|
||||
//
|
||||
// The field parameter is a comma-separated list of the fields to return. See the
|
||||
// JSON struct tags for the PlaylistTrackPage type for valid field names.
|
||||
// For example, to get just the total number of tracks and the request limit:
|
||||
// fields = "total,limit"
|
||||
//
|
||||
// A dot separator can be used to specify non-reoccurring fields, while parentheses
|
||||
// can be used to specify reoccurring fields within objects. For example, to get
|
||||
// just the added date and user ID of the adder:
|
||||
// fields = "items(added_at,added_by.id
|
||||
//
|
||||
// Use multiple parentheses to drill down into nested objects. For example:
|
||||
// fields = "items(track(name,href,album(name,href)))"
|
||||
//
|
||||
// Fields can be excluded by prefixing them with an exclamation mark. For example:
|
||||
// fields = "items.track.album(!external_urls,images)"
|
||||
func (c *Client) GetPlaylistTracksOpt(playlistID ID,
|
||||
opt *Options, fields string) (*PlaylistTrackPage, error) {
|
||||
|
||||
spotifyURL := fmt.Sprintf("%splaylists/%s/tracks", c.baseURL, playlistID)
|
||||
v := url.Values{}
|
||||
if fields != "" {
|
||||
v.Set("fields", fields)
|
||||
}
|
||||
if opt != nil {
|
||||
if opt.Limit != nil {
|
||||
v.Set("limit", strconv.Itoa(*opt.Limit))
|
||||
}
|
||||
if opt.Offset != nil {
|
||||
v.Set("offset", strconv.Itoa(*opt.Offset))
|
||||
}
|
||||
}
|
||||
if params := v.Encode(); params != "" {
|
||||
spotifyURL += "?" + params
|
||||
}
|
||||
|
||||
var result PlaylistTrackPage
|
||||
|
||||
err := c.get(spotifyURL, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, err
|
||||
}
|
||||
|
||||
// CreatePlaylistForUser creates a playlist for a Spotify user.
|
||||
// The playlist will be empty until you add tracks to it.
|
||||
// The playlistName does not need to be unique - a user can have
|
||||
// several playlists with the same name.
|
||||
//
|
||||
// Creating a public playlist for a user requires ScopePlaylistModifyPublic;
|
||||
// creating a private playlist requires ScopePlaylistModifyPrivate.
|
||||
//
|
||||
// On success, the newly created playlist is returned.
|
||||
func (c *Client) CreatePlaylistForUser(userID, playlistName, description string, public bool) (*FullPlaylist, error) {
|
||||
spotifyURL := fmt.Sprintf("%susers/%s/playlists", c.baseURL, userID)
|
||||
body := struct {
|
||||
Name string `json:"name"`
|
||||
Public bool `json:"public"`
|
||||
Description string `json:"description"`
|
||||
}{
|
||||
playlistName,
|
||||
public,
|
||||
description,
|
||||
}
|
||||
bodyJSON, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest("POST", spotifyURL, bytes.NewReader(bodyJSON))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
var p FullPlaylist
|
||||
err = c.execute(req, &p, http.StatusCreated)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &p, err
|
||||
}
|
||||
|
||||
// ChangePlaylistName changes the name of a playlist. This call requires that the
|
||||
// user has authorized the ScopePlaylistModifyPublic or ScopePlaylistModifyPrivate
|
||||
// scopes (depending on whether the playlist is public or private).
|
||||
// The current user must own the playlist in order to modify it.
|
||||
func (c *Client) ChangePlaylistName(playlistID ID, newName string) error {
|
||||
return c.modifyPlaylist(playlistID, newName, "", nil)
|
||||
}
|
||||
|
||||
// ChangePlaylistAccess modifies the public/private status of a playlist. This call
|
||||
// requires that the user has authorized the ScopePlaylistModifyPublic or
|
||||
// ScopePlaylistModifyPrivate scopes (depending on whether the playlist is
|
||||
// currently public or private). The current user must own the playlist in order to modify it.
|
||||
func (c *Client) ChangePlaylistAccess(playlistID ID, public bool) error {
|
||||
return c.modifyPlaylist(playlistID, "", "", &public)
|
||||
}
|
||||
|
||||
// ChangePlaylistDescription modifies the description of a playlist. This call
|
||||
// requires that the user has authorized the ScopePlaylistModifyPublic or
|
||||
// ScopePlaylistModifyPrivate scopes (depending on whether the playlist is
|
||||
// currently public or private). The current user must own the playlist in order to modify it.
|
||||
func (c *Client) ChangePlaylistDescription(playlistID ID, newDescription string) error {
|
||||
return c.modifyPlaylist(playlistID, "", newDescription, nil)
|
||||
}
|
||||
|
||||
// ChangePlaylistNameAndAccess combines ChangePlaylistName and ChangePlaylistAccess into
|
||||
// a single Web API call. It requires that the user has authorized the ScopePlaylistModifyPublic
|
||||
// or ScopePlaylistModifyPrivate scopes (depending on whether the playlist is currently
|
||||
// public or private). The current user must own the playlist in order to modify it.
|
||||
func (c *Client) ChangePlaylistNameAndAccess(playlistID ID, newName string, public bool) error {
|
||||
return c.modifyPlaylist(playlistID, newName, "", &public)
|
||||
}
|
||||
|
||||
// ChangePlaylistNameAccessAndDescription combines ChangePlaylistName, ChangePlaylistAccess, and
|
||||
// ChangePlaylistDescription into a single Web API call. It requires that the user has authorized
|
||||
// the ScopePlaylistModifyPublic or ScopePlaylistModifyPrivate scopes (depending on whether the
|
||||
// playlist is currently public or private). The current user must own the playlist in order to modify it.
|
||||
func (c *Client) ChangePlaylistNameAccessAndDescription(playlistID ID, newName, newDescription string, public bool) error {
|
||||
return c.modifyPlaylist(playlistID, newName, newDescription, &public)
|
||||
}
|
||||
|
||||
func (c *Client) modifyPlaylist(playlistID ID, newName, newDescription string, public *bool) error {
|
||||
body := struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Public *bool `json:"public,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}{
|
||||
newName,
|
||||
public,
|
||||
newDescription,
|
||||
}
|
||||
bodyJSON, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spotifyURL := fmt.Sprintf("%splaylists/%s", c.baseURL, string(playlistID))
|
||||
req, err := http.NewRequest("PUT", spotifyURL, bytes.NewReader(bodyJSON))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
err = c.execute(req, nil, http.StatusCreated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTracksToPlaylist adds one or more tracks to a user's playlist.
|
||||
// This call requires ScopePlaylistModifyPublic or ScopePlaylistModifyPrivate.
|
||||
// A maximum of 100 tracks can be added per call. It returns a snapshot ID that
|
||||
// can be used to identify this version (the new version) of the playlist in
|
||||
// future requests.
|
||||
func (c *Client) AddTracksToPlaylist(playlistID ID, trackIDs ...ID) (snapshotID string, err error) {
|
||||
|
||||
uris := make([]string, len(trackIDs))
|
||||
for i, id := range trackIDs {
|
||||
uris[i] = fmt.Sprintf("spotify:track:%s", id)
|
||||
}
|
||||
m := make(map[string]interface{})
|
||||
m["uris"] = uris
|
||||
|
||||
spotifyURL := fmt.Sprintf("%splaylists/%s/tracks",
|
||||
c.baseURL, string(playlistID))
|
||||
body, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req, err := http.NewRequest("POST", spotifyURL, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
result := struct {
|
||||
SnapshotID string `json:"snapshot_id"`
|
||||
}{}
|
||||
|
||||
err = c.execute(req, &result, http.StatusCreated)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return result.SnapshotID, nil
|
||||
}
|
||||
|
||||
// RemoveTracksFromPlaylist removes one or more tracks from a user's playlist.
|
||||
// This call requrles that the user has authorized the ScopePlaylistModifyPublic
|
||||
// or ScopePlaylistModifyPrivate scopes.
|
||||
//
|
||||
// If the track(s) occur multiple times in the specified playlist, then all occurrences
|
||||
// of the track will be removed. If successful, the snapshot ID returned can be used to
|
||||
// identify the playlist version in future requests.
|
||||
func (c *Client) RemoveTracksFromPlaylist(playlistID ID, trackIDs ...ID) (newSnapshotID string, err error) {
|
||||
|
||||
tracks := make([]struct {
|
||||
URI string `json:"uri"`
|
||||
}, len(trackIDs))
|
||||
|
||||
for i, u := range trackIDs {
|
||||
tracks[i].URI = fmt.Sprintf("spotify:track:%s", u)
|
||||
}
|
||||
return c.removeTracksFromPlaylist(playlistID, tracks, "")
|
||||
}
|
||||
|
||||
// TrackToRemove specifies a track to be removed from a playlist.
|
||||
// Positions is a slice of 0-based track indices.
|
||||
// TrackToRemove is used with RemoveTracksFromPlaylistOpt.
|
||||
type TrackToRemove struct {
|
||||
URI string `json:"uri"`
|
||||
Positions []int `json:"positions"`
|
||||
}
|
||||
|
||||
// NewTrackToRemove creates a new TrackToRemove object with the specified
|
||||
// track ID and playlist locations.
|
||||
func NewTrackToRemove(trackID string, positions []int) TrackToRemove {
|
||||
return TrackToRemove{
|
||||
URI: fmt.Sprintf("spotify:track:%s", trackID),
|
||||
Positions: positions,
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveTracksFromPlaylistOpt is like RemoveTracksFromPlaylist, but it supports
|
||||
// optional parameters that offer more fine-grained control. Instead of deleting
|
||||
// all occurrences of a track, this function takes an index with each track URI
|
||||
// that indicates the position of the track in the playlist.
|
||||
//
|
||||
// In addition, the snapshotID parameter allows you to specify the snapshot ID
|
||||
// against which you want to make the changes. Spotify will validate that the
|
||||
// specified tracks exist in the specified positions and make the changes, even
|
||||
// if more recent changes have been made to the playlist. If a track in the
|
||||
// specified position is not found, the entire request will fail and no edits
|
||||
// will take place. (Note: the snapshot is optional, pass the empty string if
|
||||
// you don't care about it.)
|
||||
func (c *Client) RemoveTracksFromPlaylistOpt(playlistID ID,
|
||||
tracks []TrackToRemove, snapshotID string) (newSnapshotID string, err error) {
|
||||
|
||||
return c.removeTracksFromPlaylist(playlistID, tracks, snapshotID)
|
||||
}
|
||||
|
||||
func (c *Client) removeTracksFromPlaylist(playlistID ID,
|
||||
tracks interface{}, snapshotID string) (newSnapshotID string, err error) {
|
||||
|
||||
m := make(map[string]interface{})
|
||||
m["tracks"] = tracks
|
||||
if snapshotID != "" {
|
||||
m["snapshot_id"] = snapshotID
|
||||
}
|
||||
|
||||
spotifyURL := fmt.Sprintf("%splaylists/%s/tracks",
|
||||
c.baseURL, string(playlistID))
|
||||
body, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req, err := http.NewRequest("DELETE", spotifyURL, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
result := struct {
|
||||
SnapshotID string `json:"snapshot_id"`
|
||||
}{}
|
||||
|
||||
err = c.execute(req, &result)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return result.SnapshotID, err
|
||||
}
|
||||
|
||||
// ReplacePlaylistTracks replaces all of the tracks in a playlist, overwriting its
|
||||
// exising tracks This can be useful for replacing or reordering tracks, or for
|
||||
// clearing a playlist.
|
||||
//
|
||||
// Modifying a public playlist requires that the user has authorized the
|
||||
// ScopePlaylistModifyPublic scope. Modifying a private playlist requires the
|
||||
// ScopePlaylistModifyPrivate scope.
|
||||
//
|
||||
// A maximum of 100 tracks is permited in this call. Additional tracks must be
|
||||
// added via AddTracksToPlaylist.
|
||||
func (c *Client) ReplacePlaylistTracks(playlistID ID, trackIDs ...ID) error {
|
||||
trackURIs := make([]string, len(trackIDs))
|
||||
for i, u := range trackIDs {
|
||||
trackURIs[i] = fmt.Sprintf("spotify:track:%s", u)
|
||||
}
|
||||
spotifyURL := fmt.Sprintf("%splaylists/%s/tracks?uris=%s",
|
||||
c.baseURL, playlistID, strings.Join(trackURIs, ","))
|
||||
req, err := http.NewRequest("PUT", spotifyURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.execute(req, nil, http.StatusCreated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UserFollowsPlaylist checks if one or more (up to 5) Spotify users are following
|
||||
// a Spotify playlist, given the playlist's owner and ID.
|
||||
//
|
||||
// Checking if a user follows a playlist publicly doesn't require any scopes.
|
||||
// Checking if the user is privately following a playlist is only possible for the
|
||||
// current user when that user has granted access to the ScopePlaylistReadPrivate scope.
|
||||
func (c *Client) UserFollowsPlaylist(playlistID ID, userIDs ...string) ([]bool, error) {
|
||||
spotifyURL := fmt.Sprintf("%splaylists/%s/followers/contains?ids=%s",
|
||||
c.baseURL, playlistID, strings.Join(userIDs, ","))
|
||||
|
||||
follows := make([]bool, len(userIDs))
|
||||
|
||||
err := c.get(spotifyURL, &follows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return follows, err
|
||||
}
|
||||
|
||||
// PlaylistReorderOptions is used with ReorderPlaylistTracks to reorder
|
||||
// a track or group of tracks in a playlist.
|
||||
//
|
||||
// For example, in a playlist with 10 tracks, you can:
|
||||
//
|
||||
// - move the first track to the end of the playlist by setting
|
||||
// RangeStart to 0 and InsertBefore to 10
|
||||
// - move the last track to the beginning of the playlist by setting
|
||||
// RangeStart to 9 and InsertBefore to 0
|
||||
// - Move the last 2 tracks to the beginning of the playlist by setting
|
||||
// RangeStart to 8 and RangeLength to 2.
|
||||
type PlaylistReorderOptions struct {
|
||||
// The position of the first track to be reordered.
|
||||
// This field is required.
|
||||
RangeStart int `json:"range_start"`
|
||||
// The amount of tracks to be reordered. This field is optional. If
|
||||
// you don't set it, the value 1 will be used.
|
||||
RangeLength int `json:"range_length,omitempty"`
|
||||
// The position where the tracks should be inserted. To reorder the
|
||||
// tracks to the end of the playlist, simply set this to the position
|
||||
// after the last track. This field is required.
|
||||
InsertBefore int `json:"insert_before"`
|
||||
// The playlist's snapshot ID against which you wish to make the changes.
|
||||
// This field is optional.
|
||||
SnapshotID string `json:"snapshot_id,omitempty"`
|
||||
}
|
||||
|
||||
// ReorderPlaylistTracks reorders a track or group of tracks in a playlist. It
|
||||
// returns a snapshot ID that can be used to identify the [newly modified] playlist
|
||||
// version in future requests.
|
||||
//
|
||||
// See the docs for PlaylistReorderOptions for information on how the reordering
|
||||
// works.
|
||||
//
|
||||
// Reordering tracks in the current user's public playlist requires ScopePlaylistModifyPublic.
|
||||
// Reordering tracks in the user's private playlists (including collaborative playlists) requires
|
||||
// ScopePlaylistModifyPrivate.
|
||||
func (c *Client) ReorderPlaylistTracks(playlistID ID, opt PlaylistReorderOptions) (snapshotID string, err error) {
|
||||
spotifyURL := fmt.Sprintf("%splaylists/%s/tracks", c.baseURL, playlistID)
|
||||
j, err := json.Marshal(opt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req, err := http.NewRequest("PUT", spotifyURL, bytes.NewReader(j))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
result := struct {
|
||||
SnapshotID string `json:"snapshot_id"`
|
||||
}{}
|
||||
err = c.execute(req, &result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return result.SnapshotID, err
|
||||
}
|
||||
|
||||
// SetPlaylistImage replaces the image used to represent a playlist.
|
||||
// This action can only be performed by the owner of the playlist,
|
||||
// and requires ScopeImageUpload as well as ScopeModifyPlaylist{Public|Private}..
|
||||
func (c *Client) SetPlaylistImage(playlistID ID, img io.Reader) error {
|
||||
spotifyURL := fmt.Sprintf("%splaylists/%s/images", c.baseURL, playlistID)
|
||||
// data flow:
|
||||
// img (reader) -> copy into base64 encoder (writer) -> pipe (write end)
|
||||
// pipe (read end) -> request body
|
||||
r, w := io.Pipe()
|
||||
go func() {
|
||||
enc := base64.NewEncoder(base64.StdEncoding, w)
|
||||
_, err := io.Copy(enc, img)
|
||||
enc.Close()
|
||||
w.CloseWithError(err)
|
||||
}()
|
||||
|
||||
req, err := http.NewRequest("PUT", spotifyURL, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "image/jpeg")
|
||||
return c.execute(req, nil, http.StatusAccepted)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user