Skip to content

Commit cf7f460

Browse files
committed
first commit
0 parents  commit cf7f460

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+4437
-0
lines changed

.travis.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
language: go
2+
go:
3+
- 1.7
4+
- 1.8
5+
- tip
6+
sudo: false
7+

LICENSE.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2017 Kenta Tsuji
2+
3+
MIT License
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Qiita SDK for Go
2+
<span style="display: inline-block;">
3+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4+
</span>
5+
6+
Qiita API v2 client library written in Golang.
7+
8+
## Install
9+
```
10+
go get -u github.com/ktsujichan/qiita-sdk-go
11+
```
12+
13+
## Library
14+
```golang
15+
package main
16+
17+
import (
18+
"context"
19+
"github.com/ktsujichan/qiita-sdk-go/qiita"
20+
)
21+
22+
func main() {
23+
config := qiita.NewConfig()
24+
c, _ := qiita.NewClient("<qiita access token>", *config)
25+
ctx, cancel := context.WithCancel(context.Background())
26+
defer cancel()
27+
28+
c.ListItems(ctx, 1, 10, "Golang")
29+
c.ListUsers(ctx, 1, 10)
30+
c.GetUser(ctx, "r7kamura")
31+
}
32+
```

qiita/access_token.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package qiita
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"errors"
8+
"fmt"
9+
"net/http"
10+
)
11+
12+
// Access token for Qiita API v2
13+
type AccessToken struct {
14+
ClientId string `json:"client_id"`
15+
Scopes []string `json:"scopes"`
16+
Token string `json:"token"`
17+
}
18+
19+
type Auth struct {
20+
ClientId string `json:"client_id"`
21+
ClientSecret string `json:"client_secret"`
22+
Code string `json:"code"`
23+
}
24+
25+
/*
26+
Create a new access token.
27+
28+
POST /api/v2/access_tokens
29+
*/
30+
func (c *Client) CreateAccessToken(ctx context.Context, auth Auth) error {
31+
b, _ := json.Marshal(auth)
32+
res, err := c.post(ctx, "/api/v2/access_tokens", bytes.NewBuffer(b))
33+
if err != nil {
34+
return err
35+
}
36+
if res.StatusCode != http.StatusCreated {
37+
return errors.New(res.Status)
38+
}
39+
return nil
40+
}
41+
42+
/*
43+
DELETE /api/v2/access_tokens/:access_token
44+
45+
Deactivate an access token.
46+
*/
47+
func (c *Client) DeleteAccessToken(ctx context.Context, accessToken string) error {
48+
p := fmt.Sprintf("/api/v2/access_tokens/%s", accessToken)
49+
res, err := c.delete(ctx, p)
50+
if err != nil {
51+
return err
52+
}
53+
if res.StatusCode != http.StatusNoContent {
54+
return errors.New(res.Status)
55+
}
56+
return nil
57+
}

qiita/access_token_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package qiita
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
)
9+
10+
func TestCreateAccessToken(t *testing.T) {
11+
// 201
12+
func() {
13+
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
14+
w.WriteHeader(http.StatusCreated)
15+
http.ServeFile(w, r, "")
16+
}))
17+
c, _ := mockClient(server)
18+
ctx, _ := context.WithCancel(context.Background())
19+
err := c.CreateAccessToken(ctx, Auth{})
20+
if err != nil {
21+
t.Fatal(err)
22+
}
23+
}()
24+
25+
// 400
26+
func() {
27+
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
28+
w.WriteHeader(http.StatusBadRequest)
29+
http.ServeFile(w, r, "")
30+
}))
31+
c, _ := mockClient(server)
32+
ctx, _ := context.WithCancel(context.Background())
33+
err := c.CreateAccessToken(ctx, Auth{})
34+
if err == nil {
35+
t.Fail()
36+
}
37+
}()
38+
}
39+
40+
func TestDeleteAccessToken(t *testing.T) {
41+
// 204
42+
func() {
43+
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
44+
w.WriteHeader(http.StatusNoContent)
45+
http.ServeFile(w, r, "")
46+
}))
47+
c, _ := mockClient(server)
48+
ctx, _ := context.WithCancel(context.Background())
49+
err := c.DeleteAccessToken(ctx, "")
50+
if err != nil {
51+
t.Fatal(err)
52+
}
53+
}()
54+
55+
// 400
56+
func() {
57+
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
58+
w.WriteHeader(http.StatusBadRequest)
59+
http.ServeFile(w, r, "")
60+
}))
61+
c, _ := mockClient(server)
62+
ctx, _ := context.WithCancel(context.Background())
63+
err := c.DeleteAccessToken(ctx, "")
64+
if err == nil {
65+
t.Fail()
66+
}
67+
}()
68+
}

qiita/authenticated_user.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package qiita
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net/http"
7+
)
8+
9+
// An user currently authenticated by a given access token. This resources has more detailed information than normal User resource.
10+
type AuthenticatedUser struct {
11+
*User
12+
ImageMonthlyUploadLimit uint `json:"image_monthly_upload_limit"`
13+
ImageMonthlyUploadRemaining uint `json:"image_monthly_upload_remaining"`
14+
TeamOnly bool `json:"team_only"`
15+
}
16+
17+
/*
18+
Get a user associated to the current access token.
19+
20+
GET /api/v2/authenticated_user
21+
*/
22+
func (c *Client) GetAuthenticatedUser(ctx context.Context) (*AuthenticatedUser, error) {
23+
res, err := c.get(ctx, "/api/v2/authenticated_user", nil)
24+
if err != nil {
25+
return nil, err
26+
}
27+
if res.StatusCode != http.StatusOK {
28+
return nil, errors.New(res.Status)
29+
}
30+
var authenticatedUser AuthenticatedUser
31+
if err := decodeBody(res, &authenticatedUser); err != nil {
32+
return nil, err
33+
}
34+
return &authenticatedUser, nil
35+
}

qiita/authenticated_user_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package qiita
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
)
9+
10+
func TestGetAuthenticatedUser(t *testing.T) {
11+
// 200
12+
func() {
13+
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
14+
w.WriteHeader(http.StatusOK)
15+
http.ServeFile(w, r, "testdata/get_authenticated_user.json")
16+
}))
17+
c, _ := mockClient(server)
18+
ctx, _ := context.WithCancel(context.Background())
19+
_, err := c.GetAuthenticatedUser(ctx)
20+
if err != nil {
21+
t.Fatal(err)
22+
}
23+
}()
24+
25+
// 400
26+
func() {
27+
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
28+
w.WriteHeader(http.StatusBadRequest)
29+
http.ServeFile(w, r, "testdata/get_authenticated_user.json")
30+
}))
31+
c, _ := mockClient(server)
32+
ctx, _ := context.WithCancel(context.Background())
33+
_, err := c.GetAuthenticatedUser(ctx)
34+
if err == nil {
35+
t.Fail()
36+
}
37+
}()
38+
}

qiita/client.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package qiita
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"net/url"
10+
"path"
11+
"runtime"
12+
)
13+
14+
type Client struct {
15+
URL *url.URL
16+
HTTPClient *http.Client
17+
Token string
18+
}
19+
20+
var userAgent = fmt.Sprintf("QiitaGoClient/%s (%s)", version, runtime.Version())
21+
22+
func NewClient(token string, config Config) (*Client, error) {
23+
parsedURL, err := url.ParseRequestURI(config.Endpoint)
24+
if err != nil {
25+
return nil, err
26+
}
27+
return &Client{
28+
URL: parsedURL,
29+
HTTPClient: &http.Client{},
30+
Token: token,
31+
}, nil
32+
}
33+
34+
func (c *Client) newRequest(ctx context.Context, method, p string, body io.Reader) (*http.Request, error) {
35+
u := *c.URL
36+
u.Path = path.Join(c.URL.Path, p)
37+
req, err := http.NewRequest(method, u.String(), body)
38+
if err != nil {
39+
return nil, err
40+
}
41+
req = req.WithContext(ctx)
42+
req.Header.Set("Authorization", "Bearer "+c.Token)
43+
req.Header.Set("Content-Type", "application/json")
44+
req.Header.Set("User-Agent", userAgent)
45+
return req, nil
46+
}
47+
48+
func decodeBody(res *http.Response, out interface{}) error {
49+
defer res.Body.Close()
50+
decoder := json.NewDecoder(res.Body)
51+
return decoder.Decode(out)
52+
}
53+
54+
func (c *Client) url(endpoint string) string {
55+
u := *c.URL
56+
u.Path = path.Join(c.URL.Path, endpoint)
57+
return u.String()
58+
}
59+
60+
func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, error) {
61+
req.Header.Set("Authorization", "Bearer "+c.Token)
62+
req.Header.Set("User-Agent", userAgent)
63+
return c.HTTPClient.Do(req)
64+
}
65+
66+
func (c *Client) get(ctx context.Context, endpoint string, rawQuery *string) (*http.Response, error) {
67+
req, err := http.NewRequest("GET", c.url(endpoint), nil)
68+
if rawQuery != nil {
69+
req.URL.RawQuery = *rawQuery
70+
}
71+
if err != nil {
72+
return nil, err
73+
}
74+
return c.do(ctx, req)
75+
}
76+
77+
func (client *Client) post(ctx context.Context, endpoint string, body io.Reader) (*http.Response, error) {
78+
req, err := http.NewRequest("POST", client.url(endpoint), body)
79+
if err != nil {
80+
return nil, err
81+
}
82+
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
83+
return client.do(ctx, req)
84+
}
85+
86+
func (client *Client) patch(ctx context.Context, endpoint string, body io.Reader) (*http.Response, error) {
87+
req, err := http.NewRequest("PATCH", client.url(endpoint), body)
88+
if err != nil {
89+
return nil, err
90+
}
91+
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
92+
return client.do(ctx, req)
93+
}
94+
95+
func (client *Client) put(ctx context.Context, endpoint string, body io.Reader) (*http.Response, error) {
96+
req, err := http.NewRequest("PUT", client.url(endpoint), body)
97+
if err != nil {
98+
return nil, err
99+
}
100+
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
101+
return client.do(ctx, req)
102+
}
103+
104+
func (c *Client) delete(ctx context.Context, endpoint string) (*http.Response, error) {
105+
req, err := http.NewRequest("DELETE", c.url(endpoint), nil)
106+
if err != nil {
107+
return nil, err
108+
}
109+
return c.do(ctx, req)
110+
}

0 commit comments

Comments
 (0)