diff --git a/github/codesofconduct.go b/github/codesofconduct.go new file mode 100644 index 00000000000..4318ba56d2c --- /dev/null +++ b/github/codesofconduct.go @@ -0,0 +1,81 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CodesOfConductService provides access to code-of-conduct-related functions in the GitHub API. +type CodesOfConductService service + +// CodeOfConduct represents a code of conduct. +type CodeOfConduct struct { + Name *string `json:"name,omitempty"` + Key *string `json:"key,omitempty"` + URL *string `json:"url,omitempty"` + Body *string `json:"body,omitempty"` +} + +func (c *CodeOfConduct) String() string { + return Stringify(c) +} + +// List returns all codes of conduct. +// +// GitHub API docs: https://docs.github.com/rest/codes-of-conduct/codes-of-conduct#get-all-codes-of-conduct +func (s *CodesOfConductService) List(ctx context.Context) ([]*CodeOfConduct, *Response, error) { + req, err := s.client.NewRequest("GET", "codes_of_conduct", nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeCodesOfConductPreview) + + var cs []*CodeOfConduct + resp, err := s.client.Do(ctx, req, &cs) + if err != nil { + return nil, resp, err + } + + return cs, resp, nil +} + +// ListCodesOfConduct +// Deprecated: Use CodesOfConductService.List instead +func (c *Client) ListCodesOfConduct(ctx context.Context) ([]*CodeOfConduct, *Response, error) { + return c.CodesOfConduct.List(ctx) +} + +// Get returns an individual code of conduct. +// +// GitHub API docs: https://docs.github.com/rest/codes-of-conduct/codes-of-conduct#get-a-code-of-conduct +func (s *CodesOfConductService) Get(ctx context.Context, key string) (*CodeOfConduct, *Response, error) { + u := fmt.Sprintf("codes_of_conduct/%s", key) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeCodesOfConductPreview) + + coc := new(CodeOfConduct) + resp, err := s.client.Do(ctx, req, coc) + if err != nil { + return nil, resp, err + } + + return coc, resp, nil +} + +// GetCodeOfConduct +// Deprecated: Use CodesOfConductService.Get instead +func (c *Client) GetCodeOfConduct(ctx context.Context, key string) (*CodeOfConduct, *Response, error) { + return c.CodesOfConduct.Get(ctx, key) +} diff --git a/github/codesofconduct_test.go b/github/codesofconduct_test.go new file mode 100644 index 00000000000..71ef31f7afd --- /dev/null +++ b/github/codesofconduct_test.go @@ -0,0 +1,117 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCodesOfConductService_List(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/codes_of_conduct", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) + fmt.Fprint(w, `[{ + "key": "key", + "name": "name", + "url": "url"} + ]`) + }) + + ctx := context.Background() + cs, _, err := client.ListCodesOfConduct(ctx) + assertNilError(t, err) + + want := []*CodeOfConduct{ + { + Key: String("key"), + Name: String("name"), + URL: String("url"), + }} + if !cmp.Equal(want, cs) { + t.Errorf("returned %+v, want %+v", cs, want) + } + + const methodName = "List" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodesOfConduct.List(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodesOfConductService_Get(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/codes_of_conduct/k", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) + fmt.Fprint(w, `{ + "key": "key", + "name": "name", + "url": "url", + "body": "body"}`, + ) + }) + + ctx := context.Background() + coc, _, err := client.GetCodeOfConduct(ctx, "k") + assertNilError(t, err) + + want := &CodeOfConduct{ + Key: String("key"), + Name: String("name"), + URL: String("url"), + Body: String("body"), + } + if !cmp.Equal(want, coc) { + t.Errorf("returned %+v, want %+v", coc, want) + } + + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodesOfConduct.Get(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodesOfConduct.Get(ctx, "k") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeOfConduct_Marshal(t *testing.T) { + testJSONMarshal(t, &CodeOfConduct{}, "{}") + + a := &CodeOfConduct{ + Name: String("name"), + Key: String("key"), + URL: String("url"), + Body: String("body"), + } + + want := `{ + "name": "name", + "key": "key", + "url": "url", + "body": "body" + }` + + testJSONMarshal(t, a, want) +} diff --git a/github/emojis.go b/github/emojis.go new file mode 100644 index 00000000000..15b57130b3b --- /dev/null +++ b/github/emojis.go @@ -0,0 +1,37 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" +) + +// EmojisService provides access to emoji-related functions in the GitHub API. +type EmojisService service + +// List returns the emojis available to use on GitHub. +// +// GitHub API docs: https://docs.github.com/rest/emojis/emojis#get-emojis +func (s *EmojisService) List(ctx context.Context) (map[string]string, *Response, error) { + req, err := s.client.NewRequest("GET", "emojis", nil) + if err != nil { + return nil, nil, err + } + + var emoji map[string]string + resp, err := s.client.Do(ctx, req, &emoji) + if err != nil { + return nil, resp, err + } + + return emoji, resp, nil +} + +// ListEmojis +// Deprecated: Use EmojisService.List instead +func (c *Client) ListEmojis(ctx context.Context) (map[string]string, *Response, error) { + return c.Emojis.List(ctx) +} diff --git a/github/emojis_test.go b/github/emojis_test.go new file mode 100644 index 00000000000..79c890e36d1 --- /dev/null +++ b/github/emojis_test.go @@ -0,0 +1,45 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEmojisService_List(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/emojis", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"+1": "+1.png"}`) + }) + + ctx := context.Background() + emoji, _, err := client.ListEmojis(ctx) + if err != nil { + t.Errorf("List returned error: %v", err) + } + + want := map[string]string{"+1": "+1.png"} + if !cmp.Equal(want, emoji) { + t.Errorf("List returned %+v, want %+v", emoji, want) + } + + const methodName = "List" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Emojis.List(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/examples_test.go b/github/examples_test.go index a2147bf417f..4aca86218fa 100644 --- a/github/examples_test.go +++ b/github/examples_test.go @@ -15,14 +15,14 @@ import ( "github.com/google/go-github/v55/github" ) -func ExampleClient_Markdown() { +func ExampleMarkdownService_Render() { client := github.NewClient(nil) input := "# heading #\n\nLink to issue #1" opt := &github.MarkdownOptions{Mode: "gfm", Context: "google/go-github"} ctx := context.Background() - output, _, err := client.Markdown(ctx, input, opt) + output, _, err := client.Markdown.Render(ctx, input, opt) if err != nil { fmt.Println(err) } diff --git a/github/github.go b/github/github.go index dace1184719..519f3f31a37 100644 --- a/github/github.go +++ b/github/github.go @@ -183,9 +183,11 @@ type Client struct { Billing *BillingService Checks *ChecksService CodeScanning *CodeScanningService + CodesOfConduct *CodesOfConductService Codespaces *CodespacesService Dependabot *DependabotService DependencyGraph *DependencyGraphService + Emojis *EmojisService Enterprise *EnterpriseService Gists *GistsService Git *GitService @@ -194,7 +196,9 @@ type Client struct { IssueImport *IssueImportService Issues *IssuesService Licenses *LicensesService + Markdown *MarkdownService Marketplace *MarketplaceService + Meta *MetaService Migrations *MigrationService Organizations *OrganizationsService Projects *ProjectsService @@ -401,8 +405,10 @@ func (c *Client) initialize() { c.Checks = (*ChecksService)(&c.common) c.CodeScanning = (*CodeScanningService)(&c.common) c.Codespaces = (*CodespacesService)(&c.common) + c.CodesOfConduct = (*CodesOfConductService)(&c.common) c.Dependabot = (*DependabotService)(&c.common) c.DependencyGraph = (*DependencyGraphService)(&c.common) + c.Emojis = (*EmojisService)(&c.common) c.Enterprise = (*EnterpriseService)(&c.common) c.Gists = (*GistsService)(&c.common) c.Git = (*GitService)(&c.common) @@ -411,7 +417,9 @@ func (c *Client) initialize() { c.IssueImport = (*IssueImportService)(&c.common) c.Issues = (*IssuesService)(&c.common) c.Licenses = (*LicensesService)(&c.common) + c.Markdown = (*MarkdownService)(&c.common) c.Marketplace = &MarketplaceService{client: c} + c.Meta = (*MetaService)(&c.common) c.Migrations = (*MigrationService)(&c.common) c.Organizations = (*OrganizationsService)(&c.common) c.Projects = (*ProjectsService)(&c.common) diff --git a/github/markdown.go b/github/markdown.go new file mode 100644 index 00000000000..48b445b3d85 --- /dev/null +++ b/github/markdown.go @@ -0,0 +1,67 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "context" +) + +// MarkdownService provides access to markdown-related functions in the GitHub API. +type MarkdownService service + +// MarkdownOptions specifies optional parameters to the Render method. +type MarkdownOptions struct { + // Mode identifies the rendering mode. Possible values are: + // markdown - render a document as plain Render, just like + // README files are rendered. + // + // gfm - to render a document as user-content, e.g. like user + // comments or issues are rendered. In GFM mode, hard line breaks are + // always taken into account, and issue and user mentions are linked + // accordingly. + // + // Default is "markdown". + Mode string + + // Context identifies the repository context. Only taken into account + // when rendering as "gfm". + Context string +} + +type markdownRenderRequest struct { + Text *string `json:"text,omitempty"` + Mode *string `json:"mode,omitempty"` + Context *string `json:"context,omitempty"` +} + +// Render renders an arbitrary Render document. +// +// GitHub API docs: https://docs.github.com/rest/markdown/markdown#render-a-markdown-document +func (s *MarkdownService) Render(ctx context.Context, text string, opts *MarkdownOptions) (string, *Response, error) { + request := &markdownRenderRequest{Text: String(text)} + if opts != nil { + if opts.Mode != "" { + request.Mode = String(opts.Mode) + } + if opts.Context != "" { + request.Context = String(opts.Context) + } + } + + req, err := s.client.NewRequest("POST", "markdown", request) + if err != nil { + return "", nil, err + } + + buf := new(bytes.Buffer) + resp, err := s.client.Do(ctx, req, buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} diff --git a/github/markdown_test.go b/github/markdown_test.go new file mode 100644 index 00000000000..2b6e7eee48e --- /dev/null +++ b/github/markdown_test.go @@ -0,0 +1,80 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestMarkdownService_Markdown(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &markdownRenderRequest{ + Text: String("# text #"), + Mode: String("gfm"), + Context: String("google/go-github"), + } + mux.HandleFunc("/markdown", func(w http.ResponseWriter, r *http.Request) { + v := new(markdownRenderRequest) + assertNilError(t, json.NewDecoder(r.Body).Decode(v)) + + testMethod(t, r, "POST") + if !cmp.Equal(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + fmt.Fprint(w, `

text

`) + }) + + ctx := context.Background() + md, _, err := client.Markdown.Render(ctx, "# text #", &MarkdownOptions{ + Mode: "gfm", + Context: "google/go-github", + }) + if err != nil { + t.Errorf("Render returned error: %v", err) + } + + if want := "

text

"; want != md { + t.Errorf("Render returned %+v, want %+v", md, want) + } + + const methodName = "Render" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Markdown.Render(ctx, "# text #", &MarkdownOptions{ + Mode: "gfm", + Context: "google/go-github", + }) + if got != "" { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestMarkdownRenderRequest_Marshal(t *testing.T) { + testJSONMarshal(t, &markdownRenderRequest{}, "{}") + + a := &markdownRenderRequest{ + Text: String("txt"), + Mode: String("mode"), + Context: String("ctx"), + } + + want := `{ + "text": "txt", + "mode": "mode", + "context": "ctx" + }` + + testJSONMarshal(t, a, want) +} diff --git a/github/meta.go b/github/meta.go new file mode 100644 index 00000000000..a4d9bac77b4 --- /dev/null +++ b/github/meta.go @@ -0,0 +1,146 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "context" + "fmt" + "net/url" +) + +// MetaService provides access to functions in the GitHub API that GitHub categorizes as "meta". +type MetaService service + +// APIMeta represents metadata about the GitHub API. +type APIMeta struct { + // An Array of IP addresses in CIDR format specifying the addresses + // that incoming service hooks will originate from on GitHub.com. + Hooks []string `json:"hooks,omitempty"` + + // An Array of IP addresses in CIDR format specifying the Git servers + // for GitHub.com. + Git []string `json:"git,omitempty"` + + // Whether authentication with username and password is supported. + // (GitHub Enterprise instances using CAS or OAuth for authentication + // will return false. Features like Basic Authentication with a + // username and password, sudo mode, and two-factor authentication are + // not supported on these servers.) + VerifiablePasswordAuthentication *bool `json:"verifiable_password_authentication,omitempty"` + + // An array of IP addresses in CIDR format specifying the addresses + // which serve GitHub Pages websites. + Pages []string `json:"pages,omitempty"` + + // An Array of IP addresses specifying the addresses that source imports + // will originate from on GitHub.com. + Importer []string `json:"importer,omitempty"` + + // An array of IP addresses in CIDR format specifying the IP addresses + // GitHub Actions will originate from. + Actions []string `json:"actions,omitempty"` + + // An array of IP addresses in CIDR format specifying the IP addresses + // Dependabot will originate from. + Dependabot []string `json:"dependabot,omitempty"` + + // A map of algorithms to SSH key fingerprints. + SSHKeyFingerprints map[string]string `json:"ssh_key_fingerprints,omitempty"` + + // An array of SSH keys. + SSHKeys []string `json:"ssh_keys,omitempty"` + + // An array of IP addresses in CIDR format specifying the addresses + // which serve GitHub websites. + Web []string `json:"web,omitempty"` + + // An array of IP addresses in CIDR format specifying the addresses + // which serve GitHub APIs. + API []string `json:"api,omitempty"` +} + +// Get returns information about GitHub.com, the service. Or, if you access +// this endpoint on your organization’s GitHub Enterprise installation, this +// endpoint provides information about that installation. +// +// GitHub API docs: https://docs.github.com/rest/meta/meta#get-github-meta-information +func (s *MetaService) Get(ctx context.Context) (*APIMeta, *Response, error) { + req, err := s.client.NewRequest("GET", "meta", nil) + if err != nil { + return nil, nil, err + } + + meta := new(APIMeta) + resp, err := s.client.Do(ctx, req, meta) + if err != nil { + return nil, resp, err + } + + return meta, resp, nil +} + +// APIMeta +// Deprecated: Use MetaService.Get instead. +func (c *Client) APIMeta(ctx context.Context) (*APIMeta, *Response, error) { + return c.Meta.Get(ctx) +} + +// Octocat returns an ASCII art octocat with the specified message in a speech +// bubble. If message is empty, a random zen phrase is used. +// +// GitHub API docs: https://docs.github.com/rest/meta/meta#get-octocat +func (s *MetaService) Octocat(ctx context.Context, message string) (string, *Response, error) { + u := "octocat" + if message != "" { + u = fmt.Sprintf("%s?s=%s", u, url.QueryEscape(message)) + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return "", nil, err + } + + buf := new(bytes.Buffer) + resp, err := s.client.Do(ctx, req, buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + +// Octocat +// Deprecated: Use MetaService.Octocat instead. +func (c *Client) Octocat(ctx context.Context, message string) (string, *Response, error) { + return c.Meta.Octocat(ctx, message) +} + +// Zen returns a random line from The Zen of GitHub. +// +// See also: http://warpspire.com/posts/taste/ +// +// GitHub API docs: https://docs.github.com/rest/meta/meta#get-the-zen-of-github +func (s *MetaService) Zen(ctx context.Context) (string, *Response, error) { + req, err := s.client.NewRequest("GET", "zen", nil) + if err != nil { + return "", nil, err + } + + buf := new(bytes.Buffer) + resp, err := s.client.Do(ctx, req, buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + +// Zen +// Deprecated: Use MetaService.Zen instead. +func (c *Client) Zen(ctx context.Context) (string, *Response, error) { + return c.Meta.Zen(ctx) +} diff --git a/github/meta_test.go b/github/meta_test.go new file mode 100644 index 00000000000..c7b01e298ed --- /dev/null +++ b/github/meta_test.go @@ -0,0 +1,155 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestAPIMeta_Marshal(t *testing.T) { + testJSONMarshal(t, &APIMeta{}, "{}") + + a := &APIMeta{ + Hooks: []string{"h"}, + Git: []string{"g"}, + VerifiablePasswordAuthentication: Bool(true), + Pages: []string{"p"}, + Importer: []string{"i"}, + Actions: []string{"a"}, + Dependabot: []string{"d"}, + SSHKeyFingerprints: map[string]string{"a": "f"}, + SSHKeys: []string{"k"}, + API: []string{"a"}, + Web: []string{"w"}, + } + want := `{ + "hooks":["h"], + "git":["g"], + "verifiable_password_authentication":true, + "pages":["p"], + "importer":["i"], + "actions":["a"], + "dependabot":["d"], + "ssh_key_fingerprints":{"a":"f"}, + "ssh_keys":["k"], + "api":["a"], + "web":["w"] + }` + + testJSONMarshal(t, a, want) +} + +func TestMetaService_Get(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/meta", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"web":["w"],"api":["a"],"hooks":["h"], "git":["g"], "pages":["p"], "importer":["i"], "actions":["a"], "dependabot":["d"], "verifiable_password_authentication": true}`) + }) + + ctx := context.Background() + meta, _, err := client.Meta.Get(ctx) + if err != nil { + t.Errorf("Get returned error: %v", err) + } + + want := &APIMeta{ + Hooks: []string{"h"}, + Git: []string{"g"}, + Pages: []string{"p"}, + Importer: []string{"i"}, + Actions: []string{"a"}, + Dependabot: []string{"d"}, + API: []string{"a"}, + Web: []string{"w"}, + + VerifiablePasswordAuthentication: Bool(true), + } + if !cmp.Equal(want, meta) { + t.Errorf("Get returned %+v, want %+v", meta, want) + } + + const methodName = "Get" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Meta.Get(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestMetaService_Octocat(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := "input" + output := "sample text" + + mux.HandleFunc("/octocat", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"s": input}) + w.Header().Set("Content-Type", "application/octocat-stream") + fmt.Fprint(w, output) + }) + + ctx := context.Background() + got, _, err := client.Meta.Octocat(ctx, input) + if err != nil { + t.Errorf("Octocat returned error: %v", err) + } + + if want := output; got != want { + t.Errorf("Octocat returned %+v, want %+v", got, want) + } + + const methodName = "Octocat" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Meta.Octocat(ctx, input) + if got != "" { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestMetaService_Zen(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + output := "sample text" + + mux.HandleFunc("/zen", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.Header().Set("Content-Type", "text/plain;charset=utf-8") + fmt.Fprint(w, output) + }) + + ctx := context.Background() + got, _, err := client.Meta.Zen(ctx) + if err != nil { + t.Errorf("Zen returned error: %v", err) + } + + if want := output; got != want { + t.Errorf("Zen returned %+v, want %+v", got, want) + } + + const methodName = "Zen" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Meta.Zen(ctx) + if got != "" { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/misc.go b/github/misc.go deleted file mode 100644 index a01b716fa23..00000000000 --- a/github/misc.go +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2014 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package github - -import ( - "bytes" - "context" - "fmt" - "net/url" -) - -// MarkdownOptions specifies optional parameters to the Markdown method. -type MarkdownOptions struct { - // Mode identifies the rendering mode. Possible values are: - // markdown - render a document as plain Markdown, just like - // README files are rendered. - // - // gfm - to render a document as user-content, e.g. like user - // comments or issues are rendered. In GFM mode, hard line breaks are - // always taken into account, and issue and user mentions are linked - // accordingly. - // - // Default is "markdown". - Mode string - - // Context identifies the repository context. Only taken into account - // when rendering as "gfm". - Context string -} - -type markdownRequest struct { - Text *string `json:"text,omitempty"` - Mode *string `json:"mode,omitempty"` - Context *string `json:"context,omitempty"` -} - -// Markdown renders an arbitrary Markdown document. -// -// GitHub API docs: https://docs.github.com/en/rest/markdown/ -func (c *Client) Markdown(ctx context.Context, text string, opts *MarkdownOptions) (string, *Response, error) { - request := &markdownRequest{Text: String(text)} - if opts != nil { - if opts.Mode != "" { - request.Mode = String(opts.Mode) - } - if opts.Context != "" { - request.Context = String(opts.Context) - } - } - - req, err := c.NewRequest("POST", "markdown", request) - if err != nil { - return "", nil, err - } - - buf := new(bytes.Buffer) - resp, err := c.Do(ctx, req, buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// ListEmojis returns the emojis available to use on GitHub. -// -// GitHub API docs: https://docs.github.com/en/rest/emojis/ -func (c *Client) ListEmojis(ctx context.Context) (map[string]string, *Response, error) { - req, err := c.NewRequest("GET", "emojis", nil) - if err != nil { - return nil, nil, err - } - - var emoji map[string]string - resp, err := c.Do(ctx, req, &emoji) - if err != nil { - return nil, resp, err - } - - return emoji, resp, nil -} - -// CodeOfConduct represents a code of conduct. -type CodeOfConduct struct { - Name *string `json:"name,omitempty"` - Key *string `json:"key,omitempty"` - URL *string `json:"url,omitempty"` - Body *string `json:"body,omitempty"` -} - -func (c *CodeOfConduct) String() string { - return Stringify(c) -} - -// ListCodesOfConduct returns all codes of conduct. -// -// GitHub API docs: https://docs.github.com/en/rest/codes_of_conduct/#list-all-codes-of-conduct -func (c *Client) ListCodesOfConduct(ctx context.Context) ([]*CodeOfConduct, *Response, error) { - req, err := c.NewRequest("GET", "codes_of_conduct", nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeCodesOfConductPreview) - - var cs []*CodeOfConduct - resp, err := c.Do(ctx, req, &cs) - if err != nil { - return nil, resp, err - } - - return cs, resp, nil -} - -// GetCodeOfConduct returns an individual code of conduct. -// -// https://docs.github.com/en/rest/codes_of_conduct/#get-an-individual-code-of-conduct -func (c *Client) GetCodeOfConduct(ctx context.Context, key string) (*CodeOfConduct, *Response, error) { - u := fmt.Sprintf("codes_of_conduct/%s", key) - req, err := c.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeCodesOfConductPreview) - - coc := new(CodeOfConduct) - resp, err := c.Do(ctx, req, coc) - if err != nil { - return nil, resp, err - } - - return coc, resp, nil -} - -// APIMeta represents metadata about the GitHub API. -type APIMeta struct { - // An Array of IP addresses in CIDR format specifying the addresses - // that incoming service hooks will originate from on GitHub.com. - Hooks []string `json:"hooks,omitempty"` - - // An Array of IP addresses in CIDR format specifying the Git servers - // for GitHub.com. - Git []string `json:"git,omitempty"` - - // Whether authentication with username and password is supported. - // (GitHub Enterprise instances using CAS or OAuth for authentication - // will return false. Features like Basic Authentication with a - // username and password, sudo mode, and two-factor authentication are - // not supported on these servers.) - VerifiablePasswordAuthentication *bool `json:"verifiable_password_authentication,omitempty"` - - // An array of IP addresses in CIDR format specifying the addresses - // which serve GitHub Pages websites. - Pages []string `json:"pages,omitempty"` - - // An Array of IP addresses specifying the addresses that source imports - // will originate from on GitHub.com. - Importer []string `json:"importer,omitempty"` - - // An array of IP addresses in CIDR format specifying the IP addresses - // GitHub Actions will originate from. - Actions []string `json:"actions,omitempty"` - - // An array of IP addresses in CIDR format specifying the IP addresses - // Dependabot will originate from. - Dependabot []string `json:"dependabot,omitempty"` - - // A map of algorithms to SSH key fingerprints. - SSHKeyFingerprints map[string]string `json:"ssh_key_fingerprints,omitempty"` - - // An array of SSH keys. - SSHKeys []string `json:"ssh_keys,omitempty"` - - // An array of IP addresses in CIDR format specifying the addresses - // which serve GitHub websites. - Web []string `json:"web,omitempty"` - - // An array of IP addresses in CIDR format specifying the addresses - // which serve GitHub APIs. - API []string `json:"api,omitempty"` -} - -// APIMeta returns information about GitHub.com, the service. Or, if you access -// this endpoint on your organization’s GitHub Enterprise installation, this -// endpoint provides information about that installation. -// -// GitHub API docs: https://docs.github.com/en/rest/meta#get-github-meta-information -func (c *Client) APIMeta(ctx context.Context) (*APIMeta, *Response, error) { - req, err := c.NewRequest("GET", "meta", nil) - if err != nil { - return nil, nil, err - } - - meta := new(APIMeta) - resp, err := c.Do(ctx, req, meta) - if err != nil { - return nil, resp, err - } - - return meta, resp, nil -} - -// Octocat returns an ASCII art octocat with the specified message in a speech -// bubble. If message is empty, a random zen phrase is used. -func (c *Client) Octocat(ctx context.Context, message string) (string, *Response, error) { - u := "octocat" - if message != "" { - u = fmt.Sprintf("%s?s=%s", u, url.QueryEscape(message)) - } - - req, err := c.NewRequest("GET", u, nil) - if err != nil { - return "", nil, err - } - - buf := new(bytes.Buffer) - resp, err := c.Do(ctx, req, buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// Zen returns a random line from The Zen of GitHub. -// -// see also: http://warpspire.com/posts/taste/ -func (c *Client) Zen(ctx context.Context) (string, *Response, error) { - req, err := c.NewRequest("GET", "zen", nil) - if err != nil { - return "", nil, err - } - - buf := new(bytes.Buffer) - resp, err := c.Do(ctx, req, buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} diff --git a/github/misc_test.go b/github/misc_test.go deleted file mode 100644 index 45c99893812..00000000000 --- a/github/misc_test.go +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright 2014 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package github - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "testing" - - "github.com/google/go-cmp/cmp" -) - -func TestMarkdown(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &markdownRequest{ - Text: String("# text #"), - Mode: String("gfm"), - Context: String("google/go-github"), - } - mux.HandleFunc("/markdown", func(w http.ResponseWriter, r *http.Request) { - v := new(markdownRequest) - assertNilError(t, json.NewDecoder(r.Body).Decode(v)) - - testMethod(t, r, "POST") - if !cmp.Equal(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - fmt.Fprint(w, `

text

`) - }) - - ctx := context.Background() - md, _, err := client.Markdown(ctx, "# text #", &MarkdownOptions{ - Mode: "gfm", - Context: "google/go-github", - }) - if err != nil { - t.Errorf("Markdown returned error: %v", err) - } - - if want := "

text

"; want != md { - t.Errorf("Markdown returned %+v, want %+v", md, want) - } - - const methodName = "Markdown" - testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.Markdown(ctx, "# text #", &MarkdownOptions{ - Mode: "gfm", - Context: "google/go-github", - }) - if got != "" { - t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) - } - return resp, err - }) -} - -func TestListEmojis(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/emojis", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `{"+1": "+1.png"}`) - }) - - ctx := context.Background() - emoji, _, err := client.ListEmojis(ctx) - if err != nil { - t.Errorf("ListEmojis returned error: %v", err) - } - - want := map[string]string{"+1": "+1.png"} - if !cmp.Equal(want, emoji) { - t.Errorf("ListEmojis returned %+v, want %+v", emoji, want) - } - - const methodName = "ListEmojis" - testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.ListEmojis(ctx) - if got != nil { - t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) - } - return resp, err - }) -} - -func TestListCodesOfConduct(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/codes_of_conduct", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) - fmt.Fprint(w, `[{ - "key": "key", - "name": "name", - "url": "url"} - ]`) - }) - - ctx := context.Background() - cs, _, err := client.ListCodesOfConduct(ctx) - if err != nil { - t.Errorf("ListCodesOfConduct returned error: %v", err) - } - - want := []*CodeOfConduct{ - { - Key: String("key"), - Name: String("name"), - URL: String("url"), - }} - if !cmp.Equal(want, cs) { - t.Errorf("ListCodesOfConduct returned %+v, want %+v", cs, want) - } - - const methodName = "ListCodesOfConduct" - testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.ListCodesOfConduct(ctx) - if got != nil { - t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) - } - return resp, err - }) -} - -func TestGetCodeOfConduct(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/codes_of_conduct/k", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) - fmt.Fprint(w, `{ - "key": "key", - "name": "name", - "url": "url", - "body": "body"}`, - ) - }) - - ctx := context.Background() - coc, _, err := client.GetCodeOfConduct(ctx, "k") - if err != nil { - t.Errorf("ListCodesOfConduct returned error: %v", err) - } - - want := &CodeOfConduct{ - Key: String("key"), - Name: String("name"), - URL: String("url"), - Body: String("body"), - } - if !cmp.Equal(want, coc) { - t.Errorf("GetCodeOfConductByKey returned %+v, want %+v", coc, want) - } - - const methodName = "GetCodeOfConduct" - testBadOptions(t, methodName, func() (err error) { - _, _, err = client.GetCodeOfConduct(ctx, "\n") - return err - }) - - testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.GetCodeOfConduct(ctx, "k") - if got != nil { - t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) - } - return resp, err - }) -} - -func TestAPIMeta_Marshal(t *testing.T) { - testJSONMarshal(t, &APIMeta{}, "{}") - - a := &APIMeta{ - Hooks: []string{"h"}, - Git: []string{"g"}, - VerifiablePasswordAuthentication: Bool(true), - Pages: []string{"p"}, - Importer: []string{"i"}, - Actions: []string{"a"}, - Dependabot: []string{"d"}, - SSHKeyFingerprints: map[string]string{"a": "f"}, - SSHKeys: []string{"k"}, - API: []string{"a"}, - Web: []string{"w"}, - } - want := `{ - "hooks":["h"], - "git":["g"], - "verifiable_password_authentication":true, - "pages":["p"], - "importer":["i"], - "actions":["a"], - "dependabot":["d"], - "ssh_key_fingerprints":{"a":"f"}, - "ssh_keys":["k"], - "api":["a"], - "web":["w"] - }` - - testJSONMarshal(t, a, want) -} - -func TestAPIMeta(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/meta", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `{"web":["w"],"api":["a"],"hooks":["h"], "git":["g"], "pages":["p"], "importer":["i"], "actions":["a"], "dependabot":["d"], "verifiable_password_authentication": true}`) - }) - - ctx := context.Background() - meta, _, err := client.APIMeta(ctx) - if err != nil { - t.Errorf("APIMeta returned error: %v", err) - } - - want := &APIMeta{ - Hooks: []string{"h"}, - Git: []string{"g"}, - Pages: []string{"p"}, - Importer: []string{"i"}, - Actions: []string{"a"}, - Dependabot: []string{"d"}, - API: []string{"a"}, - Web: []string{"w"}, - - VerifiablePasswordAuthentication: Bool(true), - } - if !cmp.Equal(want, meta) { - t.Errorf("APIMeta returned %+v, want %+v", meta, want) - } - - const methodName = "APIMeta" - testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.APIMeta(ctx) - if got != nil { - t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) - } - return resp, err - }) -} - -func TestOctocat(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := "input" - output := "sample text" - - mux.HandleFunc("/octocat", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testFormValues(t, r, values{"s": input}) - w.Header().Set("Content-Type", "application/octocat-stream") - fmt.Fprint(w, output) - }) - - ctx := context.Background() - got, _, err := client.Octocat(ctx, input) - if err != nil { - t.Errorf("Octocat returned error: %v", err) - } - - if want := output; got != want { - t.Errorf("Octocat returned %+v, want %+v", got, want) - } - - const methodName = "Octocat" - testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.Octocat(ctx, input) - if got != "" { - t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) - } - return resp, err - }) -} - -func TestZen(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - output := "sample text" - - mux.HandleFunc("/zen", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - w.Header().Set("Content-Type", "text/plain;charset=utf-8") - fmt.Fprint(w, output) - }) - - ctx := context.Background() - got, _, err := client.Zen(ctx) - if err != nil { - t.Errorf("Zen returned error: %v", err) - } - - if want := output; got != want { - t.Errorf("Zen returned %+v, want %+v", got, want) - } - - const methodName = "Zen" - testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.Zen(ctx) - if got != "" { - t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) - } - return resp, err - }) -} - -func TestMarkdownRequest_Marshal(t *testing.T) { - testJSONMarshal(t, &markdownRequest{}, "{}") - - a := &markdownRequest{ - Text: String("txt"), - Mode: String("mode"), - Context: String("ctx"), - } - - want := `{ - "text": "txt", - "mode": "mode", - "context": "ctx" - }` - - testJSONMarshal(t, a, want) -} - -func TestCodeOfConduct_Marshal(t *testing.T) { - testJSONMarshal(t, &CodeOfConduct{}, "{}") - - a := &CodeOfConduct{ - Name: String("name"), - Key: String("key"), - URL: String("url"), - Body: String("body"), - } - - want := `{ - "name": "name", - "key": "key", - "url": "url", - "body": "body" - }` - - testJSONMarshal(t, a, want) -} diff --git a/test/integration/misc_test.go b/test/integration/misc_test.go index e0cee29bf07..6ffb163fdcd 100644 --- a/test/integration/misc_test.go +++ b/test/integration/misc_test.go @@ -15,32 +15,32 @@ import ( ) func TestEmojis(t *testing.T) { - emoji, _, err := client.ListEmojis(context.Background()) + emoji, _, err := client.Emojis.List(context.Background()) if err != nil { - t.Fatalf("ListEmojis returned error: %v", err) + t.Fatalf("List returned error: %v", err) } if len(emoji) == 0 { - t.Errorf("ListEmojis returned no emojis") + t.Errorf("List returned no emojis") } if _, ok := emoji["+1"]; !ok { - t.Errorf("ListEmojis missing '+1' emoji") + t.Errorf("List missing '+1' emoji") } } func TestAPIMeta(t *testing.T) { - meta, _, err := client.APIMeta(context.Background()) + meta, _, err := client.Meta.Get(context.Background()) if err != nil { - t.Fatalf("APIMeta returned error: %v", err) + t.Fatalf("Get returned error: %v", err) } if len(meta.Hooks) == 0 { - t.Errorf("APIMeta returned no hook addresses") + t.Errorf("Get returned no hook addresses") } if len(meta.Git) == 0 { - t.Errorf("APIMeta returned no git addresses") + t.Errorf("Get returned no git addresses") } if !*meta.VerifiablePasswordAuthentication {