├── .gitignore ├── LICENSE ├── README.md ├── circle.yml ├── esa ├── comment.go ├── comment_test.go ├── esa.go ├── members.go ├── members_test.go ├── post.go ├── post_test.go ├── stats.go ├── stats_test.go ├── team.go ├── team_test.go └── test_helper.go ├── main.go └── tests └── stubs ├── comment_delete.json ├── comment_get_comment.json ├── comment_get_comments.json ├── comment_patch_request.json ├── comment_patch_response.json ├── comment_post_request.json ├── comment_post_response.json ├── members_get.json ├── post_delete.json ├── post_patch_request.json ├── post_patch_response.json ├── post_post.json ├── post_post_request.json ├── post_post_request_response.json ├── post_posts.json ├── post_sharing_create.json ├── post_sharing_delete.json ├── stats_team_stats.json ├── team_team.json └── team_teams.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 upamune 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | where 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-esa 2 | 3 | [](https://circleci.com/gh/upamune/go-esa/tree/master) 4 | [](https://coveralls.io/github/upamune/go-esa?branch=master) 5 | [](https://godoc.org/github.com/upamune/go-esa) 6 | 7 | esa API v1 client library, written in Golang 8 | 9 | ## Install 10 | 11 | ``` 12 | go get github.com/upamune/go-esa 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```go 18 | // Initialization 19 | client := esa.NewClient("access_token") 20 | 21 | // Team API 22 | client.Team.GetTeams() 23 | // => GET /v1/teams 24 | 25 | client.Team.GetTeam("bar") 26 | // => GET /v1/teams/bar 27 | 28 | // Stats API 29 | client.Stats.Get("bar") 30 | // => GET /v1/teams/bar/stats 31 | 32 | // Post API 33 | client.Post.GetPosts("foo") 34 | // => GET /v1/teams/foo/posts 35 | 36 | query := url.Values{} 37 | query.Add("in", "help") 38 | query.Add("page", "1") 39 | query.Add("sort", "created") 40 | query.Add("order", "asc") 41 | client.Post.GetPosts("foo", query) 42 | // => GET /v1/teams/foo/posts?q=in%3Ahelp&page=1&sort=created&order=asc 43 | 44 | client.Post.GetPost("foo", 1) 45 | // => GET /v1/teams/foobar/posts/1 46 | 47 | var post esa.Post 48 | client.Post.Create("foobar", post) 49 | // => POST /v1/teams/foobar/posts 50 | 51 | client.Post.Update("foobar", 1, post) 52 | // => PATCH /v1/teams/foobar/posts/1 53 | 54 | client.Post.Delete("foobar", 1) 55 | // => DELETE /v1/teams/foobar/posts/1 56 | 57 | client.Post.CreateSharing("foobar", 1) 58 | // => POST /v1/teams/foobar/posts/1/sharing 59 | 60 | client.Post.DeleteSharing("foobar", 1) 61 | // => DELETE /v1/teams/foobar/posts/1/sharing 62 | 63 | // Comment API 64 | client.Comment.GetComments("foobar", 1) 65 | // => GET /v1/teams/foobar/posts/1/comments 66 | 67 | client.Comment.GetComment("foobar", 123) 68 | // => GET /v1/teams/foobar/comments/123 69 | 70 | var comment esa.Comment 71 | client.Comment.Create("foobar", 1, comment) 72 | // => POST /v1/teams/foobar/posts/1/comments 73 | 74 | client.Comment.Update("foobar", 123, comment) 75 | // => PATCH /v1/teams/foobar/comments/123 76 | 77 | client.Comment.Delete("foobar", 123) 78 | // => DELETE /v1/teams/foobar/comments/123 79 | 80 | // Members API 81 | client.Members.Get("foo") 82 | // => GET /v1/teams/foo/members 83 | ``` 84 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | override: 3 | - go get -t -d -v ./... 4 | 5 | test: 6 | pre: 7 | - go get github.com/axw/gocov/gocov 8 | - go get github.com/mattn/goveralls 9 | - if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi 10 | - mkdir -p $HOME/.go_workspace/src/_/home/ubuntu/ 11 | - ln -s $HOME/$CIRCLE_PROJECT_REPONAME $HOME/.go_workspace/src/_/home/ubuntu/ 12 | override: 13 | - go test -v -cover -race -coverprofile=/home/ubuntu/coverage.out ./esa 14 | post: 15 | - ~/.go_workspace/bin/goveralls -coverprofile=/home/ubuntu/coverage.out -service=circle-ci -repotoken=$COVERALLS_TOKEN ./esa 16 | -------------------------------------------------------------------------------- /esa/comment.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "net/url" 7 | "strconv" 8 | ) 9 | 10 | const ( 11 | // CommentURL esa API のコメントのベ-スURL 12 | CommnetURL = "/v1/teams" 13 | ) 14 | 15 | // CommentService API docs: https://docs.esa.io/posts/102#7-0-0 16 | type CommentService struct { 17 | client *Client 18 | } 19 | 20 | // CommentResponse コメントのレスポンス 21 | type CommentResponse struct { 22 | BodyHTML string `json:"body_html"` 23 | BodyMd string `json:"body_md"` 24 | CreatedAt string `json:"created_at"` 25 | CreatedBy struct { 26 | Icon string `json:"icon"` 27 | Name string `json:"name"` 28 | ScreenName string `json:"screen_name"` 29 | } `json:"created_by"` 30 | ID int `json:"id"` 31 | UpdatedAt string `json:"updated_at"` 32 | URL string `json:"url"` 33 | } 34 | 35 | // CommentsResponse 複数コメントのレスポンス 36 | type CommentsResponse struct { 37 | Comments []CommentResponse `json:"comments"` 38 | NextPage interface{} `json:"next_page"` 39 | PrevPage interface{} `json:"prev_page"` 40 | TotalCount int `json:"total_count"` 41 | } 42 | 43 | // CommentReq コメントのリクエスト 44 | type CommentReq struct { 45 | Comment Comment `json:"comment"` 46 | } 47 | 48 | // Comment コメント 49 | type Comment struct { 50 | BodyMd string `json:"body_md"` 51 | User string `json:"user"` 52 | } 53 | 54 | // GetTeamPostComments チ-ム名と記事番号を指定してコメントを取得する. 55 | func (c *CommentService) GetComments(teamName string, postNumber int) (*CommentsResponse, error) { 56 | var commentsResponse CommentsResponse 57 | postNumberStr := strconv.Itoa(postNumber) 58 | commentURL := CommnetURL + "/" + teamName + "/posts" + "/" + postNumberStr + "/comments" 59 | 60 | res, err := c.client.get(commentURL, url.Values{}, &commentsResponse) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | defer res.Body.Close() 66 | 67 | return &commentsResponse, nil 68 | } 69 | 70 | // GetTeamComment チ-ム名とコメントIDを取得してコメントを取得する. 71 | func (c *CommentService) GetComment(teamName string, commentID int) (*CommentResponse, error) { 72 | var commentResponse CommentResponse 73 | commentIDStr := strconv.Itoa(commentID) 74 | commentURL := CommnetURL + "/" + teamName + "/comments" + "/" + commentIDStr 75 | 76 | res, err := c.client.get(commentURL, url.Values{}, &commentResponse) 77 | if err != nil { 78 | return nil, err 79 | } 80 | 81 | defer res.Body.Close() 82 | 83 | return &commentResponse, nil 84 | } 85 | 86 | // PostTeamPostComment チ-ム名と記事番号とコメントを指定してコメントを投稿する 87 | func (c *CommentService) Create(teamName string, postNumber int, comment Comment) (*CommentResponse, error) { 88 | postNumberStr := strconv.Itoa(postNumber) 89 | commentURL := CommnetURL + "/" + teamName + "/posts" + "/" + postNumberStr + "/comments" 90 | var commentResponse CommentResponse 91 | var commentReq CommentReq 92 | commentReq.Comment = comment 93 | 94 | var data []byte 95 | var err error 96 | if data, err = json.Marshal(commentReq); err != nil { 97 | return nil, err 98 | } 99 | 100 | res, err := c.client.post(commentURL, "application/json", bytes.NewReader(data), &commentResponse) 101 | if err != nil { 102 | return nil, err 103 | } 104 | 105 | defer res.Body.Close() 106 | 107 | return &commentResponse, nil 108 | } 109 | 110 | // PatchTeamComment チ-ム名とコメントIDとコメントを指定してコメントを更新する 111 | func (c *CommentService) Update(teamName string, commentID int, comment Comment) (*CommentResponse, error) { 112 | commentIDStr := strconv.Itoa(commentID) 113 | commentURL := CommnetURL + "/" + teamName + "/comments" + "/" + commentIDStr 114 | var commentResponse CommentResponse 115 | var commentReq CommentReq 116 | commentReq.Comment = comment 117 | 118 | var data []byte 119 | var err error 120 | if data, err = json.Marshal(commentReq); err != nil { 121 | return nil, err 122 | } 123 | 124 | res, err := c.client.patch(commentURL, "application/json", bytes.NewReader(data), &commentResponse) 125 | if err != nil { 126 | return nil, err 127 | } 128 | 129 | defer res.Body.Close() 130 | 131 | return &commentResponse, nil 132 | } 133 | 134 | // DeleteTeamComment チ-ム名とコメントIDを指定してコメントを削除する 135 | func (c *CommentService) Delete(teamName string, commentID int) error { 136 | commentIDStr := strconv.Itoa(commentID) 137 | commentURL := CommnetURL + "/" + teamName + "/comments" + "/" + commentIDStr 138 | 139 | res, err := c.client.delete(commentURL) 140 | if err != nil { 141 | return err 142 | } 143 | 144 | defer res.Body.Close() 145 | 146 | return nil 147 | } 148 | -------------------------------------------------------------------------------- /esa/comment_test.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | "reflect" 8 | "testing" 9 | ) 10 | 11 | func TestCommentGetTeamPostComments(t *testing.T) { 12 | type TestCase struct { 13 | in string 14 | out CommentsResponse 15 | } 16 | 17 | testCase := TestCase{ 18 | in: "../tests/stubs/comment_get_comments.json", 19 | } 20 | 21 | serve, client := Stub(testCase.in, &testCase.out) 22 | defer serve.Close() 23 | 24 | res, err := client.Comment.GetComments("docs", 2) 25 | if err != nil { 26 | t.Errorf("error Request %s\n", err) 27 | } 28 | 29 | if !reflect.DeepEqual(*res, testCase.out) { 30 | t.Errorf("error Response %s != %s", res, testCase.out) 31 | } 32 | } 33 | 34 | func TestCommentGetTeamComment(t *testing.T) { 35 | type TestCase struct { 36 | in string 37 | out CommentResponse 38 | } 39 | 40 | testCase := TestCase{ 41 | in: "../tests/stubs/comment_get_comment.json", 42 | } 43 | 44 | serve, client := Stub(testCase.in, &testCase.out) 45 | defer serve.Close() 46 | 47 | res, err := client.Comment.GetComment("docs", 13) 48 | if err != nil { 49 | t.Errorf("error Request %s\n", err) 50 | } 51 | 52 | if !reflect.DeepEqual(*res, testCase.out) { 53 | t.Errorf("error Response %s != %s", res, testCase.out) 54 | } 55 | } 56 | 57 | func TestCommentPostTeamPostComment(t *testing.T) { 58 | type TestCase struct { 59 | in string 60 | out CommentResponse 61 | } 62 | 63 | testCase := TestCase{ 64 | in: "../tests/stubs/comment_post_response.json", 65 | } 66 | 67 | var comment Comment 68 | fileName := "../tests/stubs/comment_post_request.json" 69 | data, err := ioutil.ReadFile(fileName) 70 | if err != nil { 71 | log.Fatalln(err) 72 | } 73 | if err := json.Unmarshal(data, &comment); err != nil { 74 | log.Fatalln(err) 75 | } 76 | 77 | serve, client := Stub(testCase.in, &testCase.out) 78 | defer serve.Close() 79 | 80 | res, err := client.Comment.Create("docs", 2, comment) 81 | 82 | if err != nil { 83 | t.Errorf("error Request %s\n", err) 84 | } 85 | 86 | if !reflect.DeepEqual(*res, testCase.out) { 87 | t.Errorf("error Response %s != %s", res, testCase.out) 88 | } 89 | } 90 | 91 | func TestCommentPatchTeamComment(t *testing.T) { 92 | type TestCase struct { 93 | in string 94 | out CommentResponse 95 | } 96 | 97 | testCase := TestCase{ 98 | in: "../tests/stubs/comment_patch_response.json", 99 | } 100 | 101 | var comment Comment 102 | fileName := "../tests/stubs/comment_patch_request.json" 103 | data, err := ioutil.ReadFile(fileName) 104 | if err != nil { 105 | log.Fatalln(err) 106 | } 107 | if err := json.Unmarshal(data, &comment); err != nil { 108 | log.Fatalln(err) 109 | } 110 | 111 | serve, client := Stub(testCase.in, &testCase.out) 112 | defer serve.Close() 113 | 114 | res, err := client.Comment.Update("docs", 22767, comment) 115 | 116 | if err != nil { 117 | t.Errorf("error Request %s\n", err) 118 | } 119 | 120 | if !reflect.DeepEqual(*res, testCase.out) { 121 | t.Errorf("error Response %s != %s", res, testCase.out) 122 | } 123 | } 124 | 125 | func TestCommentDeleteComment(t *testing.T) { 126 | type TestCase struct { 127 | in string 128 | out interface{} 129 | } 130 | 131 | testCase := TestCase{ 132 | in: "../tests/stubs/comment_delete.json", 133 | } 134 | 135 | serve, client := Stub(testCase.in, &testCase.out) 136 | defer serve.Close() 137 | 138 | err := client.Comment.Delete("docs", 22767) 139 | 140 | if err != nil { 141 | t.Errorf("error Request %s\n", err) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /esa/esa.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io" 7 | "io/ioutil" 8 | "net/http" 9 | "net/url" 10 | ) 11 | 12 | const ( 13 | // defaultBaseURL esa API の host 14 | defaultBaseURL = "https://api.esa.io" 15 | ) 16 | 17 | // Client esa API クライアント 18 | type Client struct { 19 | Client *http.Client 20 | apiKey string 21 | baseURL string 22 | Team *TeamService 23 | Stats *StatsService 24 | Post *PostService 25 | Comment *CommentService 26 | Members *MembersService 27 | } 28 | 29 | // NewClient esa APIのClientを生成する 30 | func NewClient(apikey string) *Client { 31 | c := &Client{} 32 | c.Client = http.DefaultClient 33 | c.apiKey = apikey 34 | c.baseURL = defaultBaseURL 35 | c.Team = &TeamService{client: c} 36 | c.Stats = &StatsService{client: c} 37 | c.Post = &PostService{client: c} 38 | c.Comment = &CommentService{client: c} 39 | c.Members = &MembersService{client: c} 40 | 41 | return c 42 | } 43 | 44 | func (c *Client) createURL(esaURL string) string { 45 | return c.baseURL + esaURL + "?access_token=" + c.apiKey 46 | } 47 | 48 | func (c *Client) post(esaURL string, bodyType string, body io.Reader, v interface{}) (resp *http.Response, err error) { 49 | res, err := c.Client.Post(c.createURL(esaURL), bodyType, body) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | defer res.Body.Close() 55 | 56 | if res.StatusCode != http.StatusCreated { 57 | return nil, errors.New(http.StatusText(res.StatusCode)) 58 | } 59 | 60 | if err := responseUnmarshal(res.Body, v); err != nil { 61 | return nil, err 62 | } 63 | 64 | return res, nil 65 | } 66 | 67 | func (c *Client) patch(esaURL string, bodyType string, body io.Reader, v interface{}) (resp *http.Response, err error) { 68 | path := c.createURL(esaURL) 69 | req, err := http.NewRequest("PATCH", path, body) 70 | if err != nil { 71 | return nil, err 72 | } 73 | req.Header.Add("Content-Type", bodyType) 74 | res, err := c.Client.Do(req) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | defer res.Body.Close() 80 | 81 | if res.StatusCode != http.StatusOK { 82 | return nil, errors.New(http.StatusText(res.StatusCode)) 83 | } 84 | 85 | if err := responseUnmarshal(res.Body, v); err != nil { 86 | return nil, err 87 | } 88 | 89 | return res, nil 90 | } 91 | 92 | func (c *Client) delete(esaURL string) (resp *http.Response, err error) { 93 | path := c.createURL(esaURL) 94 | req, err := http.NewRequest("DELETE", path, nil) 95 | if err != nil { 96 | return nil, err 97 | } 98 | res, err := c.Client.Do(req) 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | defer res.Body.Close() 104 | 105 | if res.StatusCode != http.StatusNoContent { 106 | return nil, errors.New(http.StatusText(res.StatusCode)) 107 | } 108 | 109 | return res, nil 110 | } 111 | 112 | func (c *Client) get(esaURL string, query url.Values, v interface{}) (resp *http.Response, err error) { 113 | path := c.createURL(esaURL) 114 | queries := query.Encode() 115 | if len(queries) != 0 { 116 | path += "&" + queries 117 | } 118 | 119 | res, err := c.Client.Get(path) 120 | if err != nil { 121 | return nil, err 122 | } 123 | 124 | defer res.Body.Close() 125 | 126 | if res.StatusCode != http.StatusOK { 127 | return nil, errors.New(http.StatusText(res.StatusCode)) 128 | } 129 | 130 | if err := responseUnmarshal(res.Body, v); err != nil { 131 | return nil, err 132 | } 133 | 134 | return res, err 135 | } 136 | 137 | func responseUnmarshal(body io.ReadCloser, v interface{}) error { 138 | data, err := ioutil.ReadAll(body) 139 | if err != nil { 140 | return err 141 | } 142 | 143 | if err := json.Unmarshal(data, v); err != nil { 144 | return err 145 | } 146 | return nil 147 | } 148 | -------------------------------------------------------------------------------- /esa/members.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import "net/url" 4 | 5 | const ( 6 | // MembersURL esa API のメンバーのベ-スURL 7 | MembersURL = "/v1/teams" 8 | ) 9 | 10 | // MembersService API docs: https://docs.esa.io/posts/102#6-0-0 11 | type MembersService struct { 12 | client *Client 13 | } 14 | 15 | // Member メンバー情報 16 | type Member struct { 17 | Email string `json:"email"` 18 | Icon string `json:"icon"` 19 | Name string `json:"name"` 20 | ScreenName string `json:"screen_name"` 21 | } 22 | 23 | // MembersResponse メンバー情報のレスポンス 24 | type MembersResponse struct { 25 | Members []Member `json:"members"` 26 | NextPage interface{} `json:"next_page"` 27 | PrevPage interface{} `json:"prev_page"` 28 | TotalCount int `json:"total_count"` 29 | } 30 | 31 | // GetTeamMembers チ-ム名を指定してメンバー情報を取得する 32 | func (s *MembersService) Get(teamName string) (*MembersResponse, error) { 33 | var membersRes MembersResponse 34 | 35 | membersURL := MembersURL+ "/" + teamName + "/members" 36 | res, err := s.client.get(membersURL, url.Values{}, &membersRes) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | defer res.Body.Close() 42 | 43 | return &membersRes, nil 44 | } 45 | -------------------------------------------------------------------------------- /esa/members_test.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestMembersGet(t *testing.T) { 9 | type TestCase struct { 10 | in string 11 | out MembersResponse 12 | } 13 | 14 | testCase := TestCase{ 15 | in: "../tests/stubs/members_get.json", 16 | } 17 | 18 | serve, client := Stub(testCase.in, &testCase.out) 19 | defer serve.Close() 20 | 21 | res, err := client.Members.Get("esa") 22 | if err != nil { 23 | t.Errorf("error Request %s\n", err) 24 | } 25 | 26 | if !reflect.DeepEqual(*res, testCase.out) { 27 | t.Errorf("error Response %s != %s", res, testCase.out) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /esa/post.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "net/url" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | const ( 12 | // PostURL esa API のコメントのベ-スURL 13 | PostURL = "/v1/teams" 14 | ) 15 | 16 | // PostService API docs: https://docs.esa.io/posts/102#6-0-0 17 | type PostService struct { 18 | client *Client 19 | } 20 | 21 | // PostReq 記事のリクエスト 22 | type PostReq struct { 23 | Post Post `json:"post"` 24 | } 25 | 26 | // Post 記事 27 | type Post struct { 28 | BodyMd string `json:"body_md"` 29 | Category string `json:"category"` 30 | Message string `json:"message"` 31 | Name string `json:"name"` 32 | Tags []string `json:"tags"` 33 | Wip bool `json:"wip"` 34 | } 35 | 36 | // PostResponse 記事のレスポンス 37 | type PostResponse struct { 38 | BodyHTML string `json:"body_html"` 39 | BodyMd string `json:"body_md"` 40 | Category string `json:"category"` 41 | CommentsCount int `json:"comments_count"` 42 | CreatedAt string `json:"created_at"` 43 | CreatedBy struct { 44 | Icon string `json:"icon"` 45 | Name string `json:"name"` 46 | ScreenName string `json:"screen_name"` 47 | } `json:"created_by"` 48 | DoneTasksCount int `json:"done_tasks_count"` 49 | FullName string `json:"full_name"` 50 | Kind string `json:"kind"` 51 | Message string `json:"message"` 52 | Name string `json:"name"` 53 | Number int `json:"number"` 54 | OverLapped bool `json:"overlapped"` 55 | RevisionNumber int `json:"revision_number"` 56 | Star bool `json:"star"` 57 | StargazersCount int `json:"stargazers_count"` 58 | Tags []string `json:"tags"` 59 | TasksCount int `json:"tasks_count"` 60 | UpdatedAt string `json:"updated_at"` 61 | UpdatedBy struct { 62 | Icon string `json:"icon"` 63 | Name string `json:"name"` 64 | ScreenName string `json:"screen_name"` 65 | } `json:"updated_by"` 66 | URL string `json:"url"` 67 | Watch bool `json:"watch"` 68 | WatchersCount int `json:"watchers_count"` 69 | Wip bool `json:"wip"` 70 | } 71 | 72 | // PostsResponse 複数記事のレスポンス 73 | type PostsResponse struct { 74 | NextPage interface{} `json:"next_page"` 75 | Posts []PostResponse `json:"posts"` 76 | PrevPage interface{} `json:"prev_page"` 77 | TotalCount int `json:"total_count"` 78 | } 79 | 80 | // SharedPost 公開された記事 81 | type SharedPost struct { 82 | HTML string `json:"html"` 83 | Slides string `json:"slides"` 84 | } 85 | 86 | func createSearchQuery(query url.Values) string { 87 | var queries []string 88 | for key, values := range query { 89 | for _, value := range values { 90 | query := value 91 | if key != "" { 92 | query = key + ":" + query 93 | } 94 | queries = append(queries, query) 95 | } 96 | } 97 | 98 | return strings.Join(queries, " ") 99 | } 100 | 101 | func createQuery(query url.Values) url.Values { 102 | queries := url.Values{} 103 | searchQuery := query 104 | 105 | queryKey := []string{"page", "per_page", "q", "include", "sort", "order"} 106 | for _, key := range queryKey { 107 | if value := query.Get(key); value != "" { 108 | queries.Add(key, value) 109 | searchQuery.Del(key) 110 | } 111 | } 112 | 113 | queries.Add("q", createSearchQuery(searchQuery)) 114 | return queries 115 | } 116 | 117 | // GetPosts チ-ム名とクエリを指定して記事を取得する 118 | func (p *PostService) GetPosts(teamName string, query url.Values) (*PostsResponse, error) { 119 | var postsRes PostsResponse 120 | queries := createQuery(query) 121 | 122 | postsURL := PostURL + "/" + teamName + "/posts" 123 | res, err := p.client.get(postsURL, queries, &postsRes) 124 | if err != nil { 125 | return nil, err 126 | } 127 | 128 | defer res.Body.Close() 129 | 130 | return &postsRes, nil 131 | 132 | } 133 | 134 | // GetPost チ-ム名と記事番号を指定して記事を取得する 135 | func (p *PostService) GetPost(teamName string, postNumber int) (*PostResponse, error) { 136 | var postRes PostResponse 137 | 138 | postNumberStr := strconv.Itoa(postNumber) 139 | 140 | postURL := PostURL + "/" + teamName + "/posts" + "/" + postNumberStr 141 | res, err := p.client.get(postURL, url.Values{}, &postRes) 142 | if err != nil { 143 | return nil, err 144 | } 145 | 146 | defer res.Body.Close() 147 | 148 | return &postRes, nil 149 | } 150 | 151 | // Create チ-ム名と記事を指定して記事を投稿する 152 | func (p *PostService) Create(teamName string, post Post) (*PostResponse, error) { 153 | postURL := PostURL + "/" + teamName + "/posts" 154 | var postRes PostResponse 155 | var postReq PostReq 156 | postReq.Post = post 157 | var data []byte 158 | var err error 159 | if data, err = json.Marshal(postReq); err != nil { 160 | return nil, err 161 | } 162 | 163 | res, err := p.client.post(postURL, "application/json", bytes.NewReader(data), &postRes) 164 | if err != nil { 165 | return nil, err 166 | } 167 | 168 | defer res.Body.Close() 169 | 170 | return &postRes, nil 171 | } 172 | 173 | // Update チ-ム名と記事番号と記事を指定して記事を更新する 174 | func (p *PostService) Update(teamName string, postNumber int, post Post) (*PostResponse, error) { 175 | var postRes PostResponse 176 | var postReq PostReq 177 | postReq.Post = post 178 | postNumberStr := strconv.Itoa(postNumber) 179 | postURL := PostURL + "/" + teamName + "/posts" + "/" + postNumberStr 180 | 181 | var data []byte 182 | var err error 183 | if data, err = json.Marshal(postReq); err != nil { 184 | return nil, err 185 | } 186 | 187 | res, err := p.client.patch(postURL, "application/json", bytes.NewReader(data), &postRes) 188 | if err != nil { 189 | return nil, err 190 | } 191 | 192 | defer res.Body.Close() 193 | 194 | return &postRes, nil 195 | } 196 | 197 | // Delete チ-ム名と記事番号を指定して記事を削除する 198 | func (p *PostService) Delete(teamName string, postNumber int) error { 199 | postNumberStr := strconv.Itoa(postNumber) 200 | postURL := PostURL + "/" + teamName + "/posts" + "/" + postNumberStr 201 | 202 | res, err := p.client.delete(postURL) 203 | if err != nil { 204 | return err 205 | } 206 | defer res.Body.Close() 207 | 208 | return nil 209 | } 210 | 211 | // CreateSharing チ-ム名と記事を指定して記事を公開する 212 | func (p *PostService) CreateSharing(teamName string, postNumber int) (*SharedPost, error) { 213 | postNumberStr := strconv.Itoa(postNumber) 214 | postURL := PostURL + "/" + teamName + "/posts" + "/" + postNumberStr + "/sharing" 215 | var sharedRes SharedPost 216 | var postReq PostReq 217 | var data []byte 218 | var err error 219 | if data, err = json.Marshal(postReq); err != nil { 220 | return nil, err 221 | } 222 | 223 | res, err := p.client.post(postURL, "application/json", bytes.NewReader(data), &sharedRes) 224 | if err != nil { 225 | return nil, err 226 | } 227 | defer res.Body.Close() 228 | 229 | return &sharedRes, nil 230 | } 231 | 232 | // DeleteSharing チ-ム名と記事番号を指定して公開リンクを削除する 233 | func (p *PostService) DeleteSharing(teamName string, postNumber int) error { 234 | postNumberStr := strconv.Itoa(postNumber) 235 | postURL := PostURL + "/" + teamName + "/posts" + "/" + postNumberStr + "/sharing" 236 | 237 | res, err := p.client.delete(postURL) 238 | if err != nil { 239 | return err 240 | } 241 | defer res.Body.Close() 242 | 243 | return nil 244 | } 245 | -------------------------------------------------------------------------------- /esa/post_test.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | "net/url" 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | func TestPostGetPosts(t *testing.T) { 13 | type TestCase struct { 14 | in string 15 | out PostsResponse 16 | } 17 | 18 | testCase := TestCase{ 19 | in: "../tests/stubs/post_posts.json", 20 | } 21 | 22 | serve, client := Stub(testCase.in, &testCase.out) 23 | defer serve.Close() 24 | 25 | res, err := client.Post.GetPosts("docs", url.Values{}) 26 | if err != nil { 27 | t.Errorf("error Request %s\n", err) 28 | } 29 | 30 | if !reflect.DeepEqual(*res, testCase.out) { 31 | t.Errorf("error Response %s != %s", res, testCase.out) 32 | } 33 | } 34 | 35 | func TestPostGetPost(t *testing.T) { 36 | type TestCase struct { 37 | in string 38 | out PostResponse 39 | } 40 | 41 | testCase := TestCase{ 42 | in: "../tests/stubs/post_post.json", 43 | } 44 | 45 | serve, client := Stub(testCase.in, &testCase.out) 46 | defer serve.Close() 47 | 48 | res, err := client.Post.GetPost("docs", 1) 49 | if err != nil { 50 | t.Errorf("error Request %s\n", err) 51 | } 52 | 53 | if !reflect.DeepEqual(*res, testCase.out) { 54 | t.Errorf("error Response %s != %s", res, testCase.out) 55 | } 56 | } 57 | 58 | func TestPostCreate(t *testing.T) { 59 | type TestCase struct { 60 | in string 61 | out PostResponse 62 | } 63 | 64 | testCase := TestCase{ 65 | in: "../tests/stubs/post_post_request_response.json", 66 | } 67 | 68 | var post Post 69 | fileName := "../tests/stubs/post_post_request.json" 70 | data, err := ioutil.ReadFile(fileName) 71 | if err != nil { 72 | log.Fatalln(err) 73 | } 74 | if err := json.Unmarshal(data, &post); err != nil { 75 | log.Fatalln(err) 76 | } 77 | 78 | serve, client := Stub(testCase.in, &testCase.out) 79 | defer serve.Close() 80 | 81 | res, err := client.Post.Create("docs", post) 82 | 83 | if err != nil { 84 | t.Errorf("error Request %s\n", err) 85 | } 86 | 87 | if !reflect.DeepEqual(*res, testCase.out) { 88 | t.Errorf("error Response %s != %s", res, testCase.out) 89 | } 90 | } 91 | 92 | func TestPostUpdate(t *testing.T) { 93 | type TestCase struct { 94 | in string 95 | out PostResponse 96 | } 97 | 98 | testCase := TestCase{ 99 | in: "../tests/stubs/post_patch_response.json", 100 | } 101 | 102 | var post Post 103 | fileName := "../tests/stubs/post_patch_request.json" 104 | data, err := ioutil.ReadFile(fileName) 105 | if err != nil { 106 | log.Fatalln(err) 107 | } 108 | if err := json.Unmarshal(data, &post); err != nil { 109 | log.Fatalln(err) 110 | } 111 | 112 | serve, client := Stub(testCase.in, &testCase.out) 113 | defer serve.Close() 114 | 115 | res, err := client.Post.Update("docs", 5, post) 116 | 117 | if err != nil { 118 | t.Errorf("error Request %s\n", err) 119 | } 120 | 121 | if !reflect.DeepEqual(*res, testCase.out) { 122 | t.Errorf("error Response %s != %s", res, testCase.out) 123 | } 124 | } 125 | 126 | func TestPostDelete(t *testing.T) { 127 | type TestCase struct { 128 | in string 129 | out interface{} 130 | } 131 | 132 | testCase := TestCase{ 133 | in: "../tests/stubs/post_delete.json", 134 | } 135 | 136 | serve, client := Stub(testCase.in, &testCase.out) 137 | defer serve.Close() 138 | 139 | err := client.Post.Delete("docs", 5) 140 | 141 | if err != nil { 142 | t.Errorf("error Request %s\n", err) 143 | } 144 | } 145 | 146 | func TestCretaeSharing(t *testing.T) { 147 | type TestCase struct { 148 | in string 149 | out SharedPost 150 | } 151 | 152 | testCase := TestCase{ 153 | in: "../tests/stubs/post_sharing_create.json", 154 | } 155 | 156 | serve, client := Stub(testCase.in, &testCase.out) 157 | defer serve.Close() 158 | 159 | res, err := client.Post.CreateSharing("docs", 5) 160 | 161 | if err != nil { 162 | t.Errorf("error Request %s\n", err) 163 | } 164 | 165 | if !reflect.DeepEqual(*res, testCase.out) { 166 | t.Errorf("error Response %s != %s", res, testCase.out) 167 | } 168 | } 169 | 170 | func TestDeleteSharing(t *testing.T) { 171 | type TestCase struct { 172 | in string 173 | out interface{} 174 | } 175 | 176 | testCase := TestCase{ 177 | in: "../tests/stubs/post_sharing_delete.json", 178 | } 179 | 180 | serve, client := Stub(testCase.in, &testCase.out) 181 | defer serve.Close() 182 | 183 | err := client.Post.DeleteSharing("docs", 5) 184 | 185 | if err != nil { 186 | t.Errorf("error Request %s\n", err) 187 | } 188 | } 189 | 190 | func Test_createSearchQuery(t *testing.T) { 191 | type TestCase struct { 192 | in url.Values 193 | out string 194 | } 195 | 196 | testCases := []TestCase{ 197 | { 198 | in: url.Values{ 199 | "": []string{"esa"}, 200 | }, 201 | out: "esa", 202 | }, 203 | { 204 | in: url.Values{ 205 | "": []string{"esa", "docs"}, 206 | }, 207 | out: "esa docs", 208 | }, 209 | { 210 | in: url.Values{ 211 | "body": []string{"esa"}, 212 | }, 213 | out: "body:esa", 214 | }, 215 | { 216 | in: url.Values{ 217 | "body": []string{"esa", "docs"}, 218 | }, 219 | out: "body:esa body:docs", 220 | }, 221 | { 222 | in: url.Values{ 223 | "": []string{"esa", "docs"}, 224 | "body": []string{"start"}, 225 | }, 226 | out: "esa docs body:start", 227 | }, 228 | } 229 | 230 | for _, ts := range testCases { 231 | searchQuery := createSearchQuery(ts.in) 232 | if ts.out != searchQuery { 233 | t.Errorf("error searchQuery [%s] != [%s]", searchQuery, ts.out) 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /esa/stats.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import "net/url" 4 | 5 | const ( 6 | // StatsURL esa API の統計情報のベ-スURL 7 | StatsURL = "/v1/teams" 8 | ) 9 | 10 | // StatsService API docs: https://docs.esa.io/posts/102#5-0-0 11 | type StatsService struct { 12 | client *Client 13 | } 14 | 15 | // StatsResponse 統計情報のレスポンス 16 | type StatsResponse struct { 17 | Comments int `json:"comments"` 18 | DailyActiveUsers int `json:"daily_active_users"` 19 | Members int `json:"members"` 20 | MonthlyActiveUsers int `json:"monthly_active_users"` 21 | Posts int `json:"posts"` 22 | Stars int `json:"stars"` 23 | WeeklyActiveUsers int `json:"weekly_active_users"` 24 | } 25 | 26 | // GetTeamStats チ-ム名を指定して統計情報を取得する 27 | func (s *StatsService) Get(teamName string) (*StatsResponse, error) { 28 | var statsRes StatsResponse 29 | 30 | statsURL := StatsURL + "/" + teamName + "/stats" 31 | res, err := s.client.get(statsURL, url.Values{}, &statsRes) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | defer res.Body.Close() 37 | 38 | return &statsRes, nil 39 | } 40 | -------------------------------------------------------------------------------- /esa/stats_test.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestStatsGet(t *testing.T) { 9 | type TestCase struct { 10 | in string 11 | out StatsResponse 12 | } 13 | 14 | testCase := TestCase{ 15 | in: "../tests/stubs/stats_team_stats.json", 16 | } 17 | 18 | serve, client := Stub(testCase.in, &testCase.out) 19 | defer serve.Close() 20 | 21 | res, err := client.Stats.Get("esa") 22 | if err != nil { 23 | t.Errorf("error Request %s\n", err) 24 | } 25 | 26 | if !reflect.DeepEqual(*res, testCase.out) { 27 | t.Errorf("error Response %s != %s", res, testCase.out) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /esa/team.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "net/url" 5 | ) 6 | 7 | const ( 8 | // TeamURL esa API のチ-ムのベ-スURL 9 | TeamURL = "/v1/teams" 10 | ) 11 | 12 | // TeamService API docs: https://docs.esa.io/posts/102#4-0-0 13 | type TeamService struct { 14 | client *Client 15 | } 16 | 17 | // TeamResponse チ-ムのレスポンス 18 | type TeamResponse struct { 19 | Description string `json:"description"` 20 | Icon string `json:"icon"` 21 | Name string `json:"name"` 22 | Privacy string `json:"privacy"` 23 | URL string `json:"url"` 24 | } 25 | 26 | // TeamsRespons 複数チ-ムのレスポンス 27 | type TeamsResponse struct { 28 | Teams []TeamResponse `json:"teams"` 29 | PrevPage interface{} `json:"prev_page"` 30 | NextPage interface{} `json:"next_page"` 31 | TotalCount int `json:"total_count"` 32 | } 33 | 34 | // GetTeams チ-ムを取得する 35 | func (t *TeamService) GetTeams() (*TeamsResponse, error) { 36 | var teamsRes TeamsResponse 37 | _, err := t.client.get(TeamURL, url.Values{}, &teamsRes) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | return &teamsRes, nil 43 | } 44 | 45 | // GetTeam チ-ム名を取得してチ-ムを取得する 46 | func (t *TeamService) GetTeam(teamName string) (*TeamResponse, error) { 47 | var teamRes TeamResponse 48 | teamURL := TeamURL + "/" + teamName 49 | _, err := t.client.get(teamURL, url.Values{}, &teamRes) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | return &teamRes, nil 55 | } 56 | -------------------------------------------------------------------------------- /esa/team_test.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestTeamGetTeams(t *testing.T) { 9 | type TestCase struct { 10 | in string 11 | out TeamsResponse 12 | } 13 | 14 | testCase := TestCase{ 15 | in: "../tests/stubs/team_teams.json", 16 | } 17 | 18 | serve, client := Stub(testCase.in, &testCase.out) 19 | defer serve.Close() 20 | 21 | res, err := client.Team.GetTeams() 22 | if err != nil { 23 | t.Errorf("error Request %s\n", err) 24 | } 25 | 26 | if !reflect.DeepEqual(*res, testCase.out) { 27 | t.Errorf("error Response %s != %s", res, testCase.out) 28 | } 29 | } 30 | 31 | func TestTeamGetTeam(t *testing.T) { 32 | type TestCase struct { 33 | in string 34 | out TeamResponse 35 | } 36 | 37 | testCase := TestCase{ 38 | in: "../tests/stubs/team_team.json", 39 | } 40 | 41 | serve, client := Stub(testCase.in, &testCase.out) 42 | defer serve.Close() 43 | 44 | res, err := client.Team.GetTeam("docs") 45 | if err != nil { 46 | t.Errorf("error Request %s\n", err) 47 | } 48 | 49 | if !reflect.DeepEqual(*res, testCase.out) { 50 | t.Errorf("error Response %s != %s", res, testCase.out) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /esa/test_helper.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "net/http/httptest" 9 | ) 10 | 11 | // Stub テスト用のスタブ 12 | func Stub(filename string, outRes interface{}) (*httptest.Server, *Client) { 13 | stub, err := ioutil.ReadFile(filename) 14 | if err != nil { 15 | log.Fatalln(err) 16 | } 17 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 18 | var statusCode int 19 | switch r.Method { 20 | case "GET": 21 | statusCode = http.StatusOK 22 | case "POST": 23 | statusCode = http.StatusCreated 24 | case "PATCH": 25 | statusCode = http.StatusOK 26 | case "DELETE": 27 | statusCode = http.StatusNoContent 28 | default: 29 | statusCode = http.StatusOK 30 | } 31 | w.WriteHeader(statusCode) 32 | w.Write([]byte(stub)) 33 | })) 34 | c := NewClient("") 35 | c.baseURL = ts.URL 36 | 37 | data, err := ioutil.ReadFile(filename) 38 | if err != nil { 39 | log.Fatalln(err) 40 | } 41 | if err := json.Unmarshal([]byte(data), outRes); err != nil { 42 | log.Fatalln(err) 43 | } 44 | 45 | return ts, c 46 | } 47 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/upamune/go-esa/esa" 6 | "log" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | apikey := os.Getenv("ESA_API_KEY") 12 | client := esa.NewClient(apikey) 13 | 14 | res, err := client.Team.GetTeam("moomin") 15 | if err != nil { 16 | log.Fatal(err) 17 | return 18 | } 19 | 20 | fmt.Println(res) 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tests/stubs/comment_delete.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /tests/stubs/comment_get_comment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 13, 3 | "body_md": "読みたい", 4 | "body_html": "
読みたい
", 5 | "created_at": "2014-05-13T16:17:42+09:00", 6 | "updated_at": "2014-05-18T23:02:29+09:00", 7 | "url": "https://docs.esa.io/posts/13#comment-13", 8 | "created_by": { 9 | "name": "TAEKO AKATSUKA", 10 | "screen_name": "taea", 11 | "icon": "https://img.esa.io/uploads/production/users/2/icon/thumb_m_2690997f07b7de3014a36d90827603d6.jpg" 12 | } 13 | } -------------------------------------------------------------------------------- /tests/stubs/comment_get_comments.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": [ 3 | { 4 | "id": 1, 5 | "body_md": "(大事)", 6 | "body_html": "(大事)
", 7 | "created_at": "2014-05-10T12:45:42+09:00", 8 | "updated_at": "2014-05-18T23:02:29+09:00", 9 | "url": "https://docs.esa.io/posts/2#comment-1", 10 | "created_by": { 11 | "name": "Atsuo Fukaya", 12 | "screen_name": "fukayatsu", 13 | "icon": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 14 | } 15 | } 16 | ], 17 | "prev_page": null, 18 | "next_page": null, 19 | "total_count": 1 20 | } -------------------------------------------------------------------------------- /tests/stubs/comment_patch_request.json: -------------------------------------------------------------------------------- 1 | {"comment":{"body_md":"LGTM!!!"}} -------------------------------------------------------------------------------- /tests/stubs/comment_patch_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 22767, 3 | "body_md": "LGTM! :sushi:", 4 | "body_html": "LGTM!!!
\n", 5 | "created_at": "2015-06-21T19:36:20+09:00", 6 | "updated_at": "2015-06-21T19:40:33+09:00", 7 | "url": "https://docs.esa.io/posts/2#comment-22767", 8 | "created_by": { 9 | "name": "Atsuo Fukaya", 10 | "screen_name": "fukayatsu", 11 | "icon": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 12 | } 13 | } -------------------------------------------------------------------------------- /tests/stubs/comment_post_request.json: -------------------------------------------------------------------------------- 1 | {"comment":{"body_md":"LGTM!"}} -------------------------------------------------------------------------------- /tests/stubs/comment_post_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 22767, 3 | "body_md": "LGTM!", 4 | "body_html": "LGTM!
\n", 5 | "created_at": "2015-06-21T19:36:20+09:00", 6 | "updated_at": "2015-06-21T19:36:20+09:00", 7 | "url": "https://docs.esa.io/posts/2#comment-22767", 8 | "created_by": { 9 | "name": "Atsuo Fukaya", 10 | "screen_name": "fukayatsu", 11 | "icon": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 12 | } 13 | } -------------------------------------------------------------------------------- /tests/stubs/members_get.json: -------------------------------------------------------------------------------- 1 | { 2 | "members": [ 3 | { 4 | "name": "Atsuo Fukaya", 5 | "screen_name": "fukayatsu", 6 | "icon": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png", 7 | "email": "fukayatsu@esa.io" 8 | }, 9 | { 10 | "name": "TAEKO AKATSUKA", 11 | "screen_name": "taea", 12 | "icon": "https://img.esa.io/uploads/production/users/2/icon/thumb_m_2690997f07b7de3014a36d90827603d6.jpg", 13 | "email": "taea@esa.io" 14 | } 15 | ], 16 | "prev_page": null, 17 | "next_page": null, 18 | "total_count": 2 19 | } -------------------------------------------------------------------------------- /tests/stubs/post_delete.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /tests/stubs/post_patch_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "post":{ 3 | "name":"hi!", 4 | "body_md":"# Getting Started\n", 5 | "tags":[ 6 | "api", 7 | "dev" 8 | ], 9 | "category":"dev/2015/05/10", 10 | "wip":false, 11 | "message":"Add Getting Started section", 12 | "original_revision": { 13 | "body_md": "# Getting ...", 14 | "number":1, 15 | "user": "fukayatsu" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /tests/stubs/post_patch_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "number": 5, 3 | "name": "hi!", 4 | "full_name": "日報/2015/05/10/hi! #api #dev", 5 | "wip": false, 6 | "body_md": "# Getting Started\n", 7 | "body_html": "