├── README.md ├── esa.go ├── esa_test.go ├── request └── request.go └── response └── response.go /README.md: -------------------------------------------------------------------------------- 1 | # esa-go 2 | 3 | esa API v1 client written in go. 4 | 5 | ## Installation 6 | 7 | ``` 8 | go get github.com/hiroakis/esa-go 9 | ``` 10 | 11 | ## Basic Usage 12 | 13 | ``` 14 | import ( 15 | "encoding/json" 16 | "fmt" 17 | esa "github.com/hiroakis/esa-go" 18 | "github.com/hiroakis/esa-go/request" 19 | ) 20 | 21 | func main() { 22 | // Initializing client 23 | c := esa.NewEsaClient("API_KEY", "TEAM_NAME") 24 | 25 | // get all posts 26 | posts, err := c.GetPosts() 27 | if err != nil { 28 | fmt.Println(err) 29 | } 30 | fmt.Println(posts) 31 | 32 | // print specified field 33 | fmt.Println(posts.Posts[0].Name) 34 | 35 | // print with json string 36 | postsJson, _ := json.Marshal(posts) 37 | fmt.Println(string(postsJson)) 38 | 39 | // Pagenation 40 | c.SetPage(1) 41 | posts, err = c.GetPosts() 42 | if err != nil { 43 | fmt.Println(err) 44 | } 45 | fmt.Println(posts) 46 | 47 | // Query 48 | c.SetQuery("category:memo") 49 | posts, err = c.GetPosts() 50 | if err != nil { 51 | fmt.Println(err) 52 | } 53 | fmt.Println(posts) 54 | } 55 | ``` 56 | 57 | ## Examples 58 | 59 | ``` 60 | // teams 61 | teams, err := c.GetTeams() 62 | if err != nil { 63 | fmt.Println(err) 64 | } 65 | fmt.Println(teams) 66 | 67 | // team 68 | c.SetTeam("TEAM_NAME") 69 | team, err := c.GetTeam() 70 | if err != nil { 71 | fmt.Println(err) 72 | } 73 | fmt.Println(team) 74 | 75 | // stats 76 | stats, err := c.GetStats() 77 | if err != nil { 78 | fmt.Println(err) 79 | } 80 | fmt.Println(stats) 81 | 82 | // members 83 | members, err := c.GetMembers() 84 | if err != nil { 85 | fmt.Println(err) 86 | } 87 | fmt.Println(members) 88 | 89 | // post 90 | post, err := c.GetPost(1) 91 | if err != nil { 92 | fmt.Println(err) 93 | } 94 | fmt.Println(post) 95 | 96 | // posts 97 | posts, err := c.GetPosts() 98 | if err != nil { 99 | fmt.Println(err) 100 | } 101 | fmt.Println(posts) 102 | 103 | // posts 104 | c.SetQuery("category:memo") 105 | posts, err := c.GetPosts() 106 | if err != nil { 107 | fmt.Println(err) 108 | } 109 | fmt.Println(posts) 110 | 111 | // create new post 112 | reqPost := 113 | request.Post{ 114 | Name: "hi!", 115 | BodyMd: "hello", 116 | Category: "Users/hiroakis/memo", 117 | } 118 | 119 | createdPost, err := c.CreatePost(reqPost) 120 | if err != nil { 121 | fmt.Println(err) 122 | } 123 | fmt.Println(createdPost) 124 | 125 | // create new post use template 126 | reqPost := 127 | request.Post{ 128 | TemplatePostId: 123, 129 | } 130 | 131 | createdPost, err := c.CreatePost(reqPost) 132 | if err != nil { 133 | fmt.Println(err) 134 | } 135 | fmt.Println(createdPost) 136 | 137 | // update post 138 | reqPost := 139 | request.Post{ 140 | Name: "hi!", 141 | BodyMd: "おは", 142 | Category: "Users/hiroakis/memo", 143 | } 144 | 145 | updatedPost, err := c.UpdatePost(549, reqPost) 146 | if err != nil { 147 | fmt.Println(err) 148 | } 149 | fmt.Println(updatedPost) 150 | 151 | // delete post 152 | deletedPost, err := c.DeletePost(549) 153 | if err != nil { 154 | fmt.Println(err) 155 | } 156 | fmt.Println(deletedPost) 157 | 158 | // comments 159 | comments, err := c.GetComments(543) 160 | if err != nil { 161 | fmt.Println(err) 162 | } 163 | fmt.Println(comments) 164 | 165 | // comment 166 | comment, err := c.GetComment(80737) 167 | if err != nil { 168 | fmt.Println(err) 169 | } 170 | fmt.Println(comment) 171 | 172 | // create comment 173 | reqComment := request.Comment{ 174 | BodyMd: "comment!", 175 | } 176 | createdComment, err := c.CreateComment(543, reqComment) 177 | if err != nil { 178 | fmt.Println(err) 179 | } 180 | fmt.Println(createdComment) 181 | 182 | // update comment 183 | reqComment = request.Comment{ 184 | BodyMd: "comment!!!!", 185 | } 186 | updatedComment, err := c.UpdateComment(80737, reqComment) 187 | if err != nil { 188 | fmt.Println(err) 189 | } 190 | fmt.Println(updatedComment) 191 | 192 | deletedComment, err := c.DeleteComment(80737) 193 | if err != nil { 194 | fmt.Println(err) 195 | } 196 | fmt.Println(deletedComment) 197 | ``` 198 | 199 | ## Tests 200 | 201 | ``` 202 | go test -v 203 | ``` 204 | 205 | ## License 206 | 207 | MIT 208 | -------------------------------------------------------------------------------- /esa.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/hiroakis/esa-go/request" 8 | "github.com/hiroakis/esa-go/response" 9 | "io" 10 | "io/ioutil" 11 | "net/http" 12 | "net/url" 13 | "time" 14 | ) 15 | 16 | const ( 17 | EsaAPIv1 = "https://api.esa.io/v1" 18 | // EsaAPIv1 = "http://localhost:5000" 19 | ) 20 | 21 | type EsaClient struct { 22 | Team string 23 | AccessToken string 24 | Api string 25 | Page int 26 | Query string 27 | Client *http.Client 28 | } 29 | 30 | func NewEsaClient(accessToken, team string) *EsaClient { 31 | 32 | esaClient := &EsaClient{ 33 | AccessToken: accessToken, 34 | Team: team, 35 | Api: EsaAPIv1, 36 | Page: -1, 37 | Query: "", 38 | Client: &http.Client{Timeout: time.Duration(10 * time.Second)}, 39 | } 40 | 41 | return esaClient 42 | } 43 | 44 | func (c *EsaClient) SetTeam(team string) { 45 | c.Team = team 46 | } 47 | 48 | func (c *EsaClient) SetPage(page int) { 49 | c.Page = page 50 | } 51 | 52 | func (c *EsaClient) SetQuery(query string) { 53 | c.Query = query 54 | } 55 | 56 | func (c *EsaClient) SetClient(client *http.Client) { 57 | c.Client = client 58 | } 59 | 60 | func (c *EsaClient) SetApi(api string) { 61 | c.Api = api 62 | } 63 | 64 | func (c *EsaClient) GetTeams() (response.Teams, error) { 65 | teams := &response.Teams{} 66 | endpoint := fmt.Sprintf("%s/teams", c.Api) 67 | 68 | resp := c.sendGetRequest(endpoint) 69 | body, err := c.chackResponse(resp) 70 | if err != nil { 71 | return *teams, err 72 | } 73 | defer c.closeHttpResponse(resp) 74 | 75 | json.Unmarshal(body, &teams) 76 | return *teams, err 77 | } 78 | 79 | func (c *EsaClient) GetTeam() (response.Team, error) { 80 | team := &response.Team{} 81 | endpoint := fmt.Sprintf("%s/teams/%s", c.Api, c.Team) 82 | 83 | resp := c.sendGetRequest(endpoint) 84 | body, err := c.chackResponse(resp) 85 | if err != nil { 86 | return *team, err 87 | } 88 | defer c.closeHttpResponse(resp) 89 | 90 | json.Unmarshal(body, &team) 91 | return *team, nil 92 | } 93 | 94 | func (c *EsaClient) GetStats() (response.Stats, error) { 95 | stats := &response.Stats{} 96 | endpoint := fmt.Sprintf("%s/teams/%s/stats", c.Api, c.Team) 97 | 98 | resp := c.sendGetRequest(endpoint) 99 | body, err := c.chackResponse(resp) 100 | if err != nil { 101 | return *stats, err 102 | } 103 | defer c.closeHttpResponse(resp) 104 | 105 | json.Unmarshal(body, &stats) 106 | return *stats, err 107 | } 108 | 109 | func (c *EsaClient) GetMembers() (response.Members, error) { 110 | members := &response.Members{} 111 | endpoint := fmt.Sprintf("%s/teams/%s/members", c.Api, c.Team) 112 | 113 | resp := c.sendGetRequest(endpoint) 114 | body, err := c.chackResponse(resp) 115 | if err != nil { 116 | return *members, err 117 | } 118 | defer c.closeHttpResponse(resp) 119 | 120 | json.Unmarshal(body, &members) 121 | return *members, err 122 | } 123 | 124 | func (c *EsaClient) GetPost(postNumber int) (response.Post, error) { 125 | post := &response.Post{} 126 | endpoint := fmt.Sprintf("%s/teams/%s/posts/%d", c.Api, c.Team, postNumber) 127 | 128 | resp := c.sendGetRequest(endpoint) 129 | body, err := c.chackResponse(resp) 130 | if err != nil { 131 | return *post, err 132 | } 133 | defer c.closeHttpResponse(resp) 134 | 135 | json.Unmarshal(body, &post) 136 | return *post, err 137 | } 138 | 139 | func (c *EsaClient) GetPosts() (response.Posts, error) { 140 | posts := &response.Posts{} 141 | endpoint := fmt.Sprintf("%s/teams/%s/posts", c.Api, c.Team) 142 | 143 | resp := c.sendGetRequest(endpoint) 144 | body, err := c.chackResponse(resp) 145 | if err != nil { 146 | return *posts, err 147 | } 148 | defer c.closeHttpResponse(resp) 149 | 150 | json.Unmarshal(body, &posts) 151 | return *posts, err 152 | } 153 | 154 | func (c *EsaClient) CreatePost(reqPost request.Post) (response.Post, error) { 155 | post := &response.Post{} 156 | endpoint := fmt.Sprintf("%s/teams/%s/posts", c.Api, c.Team) 157 | 158 | postData, err := json.Marshal(request.PostData{reqPost}) 159 | if err != nil { 160 | return *post, err 161 | } 162 | 163 | resp := c.sendPostRequest(endpoint, bytes.NewBuffer(postData)) 164 | body, err := c.chackResponse(resp) 165 | if err != nil { 166 | return *post, err 167 | } 168 | defer c.closeHttpResponse(resp) 169 | 170 | json.Unmarshal(body, &post) 171 | return *post, err 172 | } 173 | 174 | func (c *EsaClient) UpdatePost(postNumber int, reqPost request.Post) (response.Post, error) { 175 | post := &response.Post{} 176 | endpoint := fmt.Sprintf("%s/teams/%s/posts/%d", c.Api, c.Team, postNumber) 177 | 178 | postData, err := json.Marshal(request.PostData{reqPost}) 179 | if err != nil { 180 | return *post, err 181 | } 182 | 183 | resp := c.sendPatchRequest(endpoint, bytes.NewBuffer(postData)) 184 | body, err := c.chackResponse(resp) 185 | if err != nil { 186 | return *post, err 187 | } 188 | defer c.closeHttpResponse(resp) 189 | 190 | json.Unmarshal(body, &post) 191 | return *post, err 192 | } 193 | 194 | func (c *EsaClient) DeletePost(postNumber int) (bool, error) { 195 | endpoint := fmt.Sprintf("%s/teams/%s/posts/%d", c.Api, c.Team, postNumber) 196 | 197 | resp := c.sendDeleteRequest(endpoint) 198 | _, err := c.chackResponse(resp) 199 | if err != nil { 200 | return false, err 201 | } 202 | defer c.closeHttpResponse(resp) 203 | return true, err 204 | } 205 | 206 | func (c *EsaClient) GetComments(postNumber int) (response.Comments, error) { 207 | comments := &response.Comments{} 208 | endpoint := fmt.Sprintf("%s/teams/%s/posts/%d/comments", c.Api, c.Team, postNumber) 209 | 210 | resp := c.sendGetRequest(endpoint) 211 | body, err := c.chackResponse(resp) 212 | if err != nil { 213 | return *comments, err 214 | } 215 | defer c.closeHttpResponse(resp) 216 | 217 | json.Unmarshal(body, &comments) 218 | return *comments, err 219 | } 220 | 221 | func (c *EsaClient) GetComment(commentNumber int) (response.Comment, error) { 222 | comment := &response.Comment{} 223 | endpoint := fmt.Sprintf("%s/teams/%s/comments/%d", c.Api, c.Team, commentNumber) 224 | 225 | resp := c.sendGetRequest(endpoint) 226 | body, err := c.chackResponse(resp) 227 | if err != nil { 228 | return *comment, err 229 | } 230 | defer c.closeHttpResponse(resp) 231 | 232 | json.Unmarshal(body, &comment) 233 | return *comment, err 234 | } 235 | 236 | func (c *EsaClient) CreateComment(postNumber int, reqComment request.Comment) (response.Comment, error) { 237 | comment := &response.Comment{} 238 | endpoint := fmt.Sprintf("%s/teams/%s/posts/%d/comments", c.Api, c.Team, postNumber) 239 | 240 | commentData, err := json.Marshal(request.CommentData{reqComment}) 241 | if err != nil { 242 | return *comment, err 243 | } 244 | 245 | resp := c.sendPostRequest(endpoint, bytes.NewBuffer(commentData)) 246 | body, err := c.chackResponse(resp) 247 | if err != nil { 248 | return *comment, err 249 | } 250 | defer c.closeHttpResponse(resp) 251 | 252 | json.Unmarshal(body, &comment) 253 | return *comment, err 254 | } 255 | 256 | func (c *EsaClient) UpdateComment(commentId int, reqComment request.Comment) (response.Comment, error) { 257 | comment := &response.Comment{} 258 | endpoint := fmt.Sprintf("%s/teams/%s/comments/%d", c.Api, c.Team, commentId) 259 | 260 | commentData, err := json.Marshal(request.CommentData{reqComment}) 261 | if err != nil { 262 | return *comment, err 263 | } 264 | 265 | resp := c.sendPatchRequest(endpoint, bytes.NewBuffer(commentData)) 266 | body, err := c.chackResponse(resp) 267 | if err != nil { 268 | return *comment, err 269 | } 270 | defer c.closeHttpResponse(resp) 271 | 272 | json.Unmarshal(body, &comment) 273 | return *comment, err 274 | } 275 | 276 | func (c *EsaClient) DeleteComment(commentId int) (bool, error) { 277 | endpoint := fmt.Sprintf("%s/teams/%s/comments/%d", c.Api, c.Team, commentId) 278 | 279 | resp := c.sendDeleteRequest(endpoint) 280 | _, err := c.chackResponse(resp) 281 | if err != nil { 282 | return false, err 283 | } 284 | defer c.closeHttpResponse(resp) 285 | return true, err 286 | } 287 | 288 | func (c *EsaClient) sendHttpRequest(method, endpoint string, data io.Reader) *http.Response { 289 | 290 | req, err := http.NewRequest(method, endpoint, data) 291 | if err != nil { 292 | fmt.Println(err) 293 | } 294 | req = c.buildRequest(req) 295 | 296 | resp, err := c.Client.Do(req) 297 | if err != nil { 298 | fmt.Println(err) 299 | } 300 | 301 | return resp 302 | } 303 | 304 | func (c *EsaClient) sendGetRequest(endpoint string) *http.Response { 305 | resp := c.sendHttpRequest("GET", endpoint, nil) 306 | return resp 307 | } 308 | 309 | func (c *EsaClient) sendPostRequest(endpoint string, data io.Reader) *http.Response { 310 | resp := c.sendHttpRequest("POST", endpoint, data) 311 | return resp 312 | } 313 | 314 | func (c *EsaClient) sendPatchRequest(endpoint string, data io.Reader) *http.Response { 315 | resp := c.sendHttpRequest("PATCH", endpoint, data) 316 | return resp 317 | } 318 | 319 | func (c *EsaClient) sendDeleteRequest(endpoint string) *http.Response { 320 | resp := c.sendHttpRequest("DELETE", endpoint, nil) 321 | return resp 322 | } 323 | 324 | func (c *EsaClient) buildRequest(req *http.Request) *http.Request { 325 | req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.AccessToken)) 326 | req.Header.Add("Content-Type", "application/json") 327 | values := url.Values{} 328 | if c.Page != -1 { 329 | values.Add("page", fmt.Sprintf("%d", c.Page)) 330 | } 331 | if c.Query != "" { 332 | values.Add("q", c.Query) 333 | } 334 | req.URL.RawQuery = values.Encode() 335 | return req 336 | } 337 | 338 | func (c *EsaClient) chackResponse(resp *http.Response) ([]byte, error) { 339 | var body []byte 340 | var err error 341 | 342 | if resp.StatusCode < 200 || resp.StatusCode > 300 { 343 | err = fmt.Errorf("%d %s", resp.StatusCode, http.StatusText(resp.StatusCode)) 344 | } 345 | body, _ = ioutil.ReadAll(resp.Body) 346 | return body, err 347 | } 348 | 349 | func (c *EsaClient) closeHttpResponse(resp *http.Response) { 350 | resp.Body.Close() 351 | } 352 | -------------------------------------------------------------------------------- /esa_test.go: -------------------------------------------------------------------------------- 1 | package esa 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | "time" 10 | 11 | "github.com/hiroakis/esa-go/request" 12 | ) 13 | 14 | // All of the dummy data are from https://docs.esa.io/posts/102 15 | 16 | var teamsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 17 | teams := ` 18 | { 19 | "teams": [ 20 | { 21 | "name": "docs", 22 | "privacy": "open", 23 | "description": "esa.io official documents", 24 | "icon": "https://img.esa.io/uploads/production/teams/105/icon/thumb_m_0537ab827c4b0c18b60af6cdd94f239c.png", 25 | "url": "https://docs.esa.io/" 26 | } 27 | ], 28 | "prev_page": null, 29 | "next_page": 1, 30 | "total_count": 1 31 | } 32 | ` 33 | 34 | w.Header().Set("Content-Type", "application/json") 35 | if r.Method != "GET" { 36 | w.WriteHeader(http.StatusBadRequest) 37 | return 38 | } 39 | w.WriteHeader(http.StatusOK) 40 | w.Write([]byte(teams)) 41 | }) 42 | 43 | var teamHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 44 | team := ` 45 | { 46 | "name": "docs", 47 | "privacy": "open", 48 | "description": "esa.io official documents", 49 | "icon": "https://img.esa.io/uploads/production/teams/105/icon/thumb_m_0537ab827c4b0c18b60af6cdd94f239c.png", 50 | "url": "https://docs.esa.io/" 51 | } 52 | ` 53 | 54 | w.Header().Set("Content-Type", "application/json") 55 | if r.Method != "GET" { 56 | w.WriteHeader(http.StatusBadRequest) 57 | return 58 | } 59 | w.WriteHeader(http.StatusOK) 60 | w.Write([]byte(team)) 61 | }) 62 | 63 | var statsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 64 | stats := ` 65 | { 66 | "members": 20, 67 | "posts": 1959, 68 | "comments": 2695, 69 | "stars": 3115, 70 | "daily_active_users": 8, 71 | "weekly_active_users": 14, 72 | "monthly_active_users": 15 73 | } 74 | ` 75 | 76 | w.Header().Set("Content-Type", "application/json") 77 | if r.Method != "GET" { 78 | w.WriteHeader(http.StatusBadRequest) 79 | return 80 | } 81 | w.WriteHeader(http.StatusOK) 82 | w.Write([]byte(stats)) 83 | }) 84 | 85 | var membersHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 86 | members := ` 87 | { 88 | "members": [ 89 | { 90 | "name": "Hiroaki Sano", 91 | "screen_name": "hiroakis", 92 | "icon": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png", 93 | "email": "hiroakis@example.com" 94 | }, 95 | { 96 | "name": "Sano Hiroaki", 97 | "screen_name": "sano", 98 | "icon": "https://img.esa.io/uploads/production/users/2/icon/thumb_m_2690997f07b7de3014a36d90827603d6.jpg", 99 | "email": "sano@example.com" 100 | } 101 | ], 102 | "prev_page": null, 103 | "next_page": 1, 104 | "total_count": 2 105 | } 106 | ` 107 | 108 | w.Header().Set("Content-Type", "application/json") 109 | if r.Method != "GET" { 110 | w.WriteHeader(http.StatusBadRequest) 111 | return 112 | } 113 | w.WriteHeader(http.StatusOK) 114 | w.Write([]byte(members)) 115 | }) 116 | 117 | var postsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 118 | posts := ` 119 | { 120 | "posts": [ 121 | { 122 | "number": 1, 123 | "name": "hi!", 124 | "full_name": "日報/2015/05/09/hi! #api #dev", 125 | "wip": true, 126 | "body_md": "# Getting Started", 127 | "body_html": "

\n > Getting StartedGetting Started

\n", 128 | "created_at": "2015-05-09T11:54:50+09:00", 129 | "message": "Add Getting Started section", 130 | "url": "https://docs.esa.io/posts/1", 131 | "updated_at": "2015-05-09T11:54:51+09:00", 132 | "tags": [ 133 | "api", 134 | "dev" 135 | ], 136 | "category": "日報/2015/05/09", 137 | "revision_number": 1, 138 | "created_by": { 139 | "name": "Hiroaki Sano", 140 | "screen_name": "hiroakis", 141 | "icon": "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 142 | }, 143 | "updated_by": { 144 | "name": "Hiroaki Sano", 145 | "screen_name": "hiroakis", 146 | "icon": "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 147 | } 148 | } 149 | ], 150 | "prev_page": null, 151 | "next_page": 1, 152 | "total_count": 1 153 | } 154 | ` 155 | 156 | w.Header().Set("Content-Type", "application/json") 157 | if r.Method != "GET" { 158 | w.WriteHeader(http.StatusBadRequest) 159 | return 160 | } 161 | w.WriteHeader(http.StatusOK) 162 | w.Write([]byte(posts)) 163 | }) 164 | 165 | var postHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 166 | post := ` 167 | { 168 | "number": 1, 169 | "name": "hi!", 170 | "full_name": "日報/2015/05/09/hi! #api #dev", 171 | "wip": true, 172 | "body_md": "# Getting Started", 173 | "body_html": "

\n > Getting StartedGetting Started

\n", 174 | "created_at": "2015-05-09T11:54:50+09:00", 175 | "message": "Add Getting Started section", 176 | "url": "https://docs.esa.io/posts/1", 177 | "updated_at": "2015-05-09T11:54:51+09:00", 178 | "tags": [ 179 | "api", 180 | "dev" 181 | ], 182 | "category": "日報/2015/05/09", 183 | "revision_number": 1, 184 | "created_by": { 185 | "name": "Hiroaki Sano", 186 | "screen_name": "hiroakis", 187 | "icon": "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 188 | }, 189 | "updated_by": { 190 | "name": "Hiroaki Sano", 191 | "screen_name": "hiroakis", 192 | "icon": "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 193 | }, 194 | "kind": "flow", 195 | "comments_count": 1, 196 | "tasks_count": 1, 197 | "done_tasks_count": 1, 198 | "stargazers_count": 1, 199 | "watchers_count": 1, 200 | "star": true, 201 | "watch": true 202 | } 203 | ` 204 | 205 | w.Header().Set("Content-Type", "application/json") 206 | if r.Method != "GET" { 207 | w.WriteHeader(http.StatusBadRequest) 208 | return 209 | } 210 | w.WriteHeader(http.StatusOK) 211 | w.Write([]byte(post)) 212 | }) 213 | 214 | var createPostHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 215 | post := ` 216 | { 217 | "number": 1, 218 | "name": "hi!", 219 | "full_name": "日報/2015/05/09/hi! #api #dev", 220 | "wip": true, 221 | "body_md": "# Getting Started", 222 | "body_html": "

\n > Getting StartedGetting Started

\n", 223 | "created_at": "2015-05-09T11:54:50+09:00", 224 | "message": "Add Getting Started section", 225 | "url": "https://docs.esa.io/posts/1", 226 | "updated_at": "2015-05-09T11:54:51+09:00", 227 | "tags": [ 228 | "api", 229 | "dev" 230 | ], 231 | "category": "日報/2015/05/09", 232 | "revision_number": 1, 233 | "created_by": { 234 | "name": "Hiroaki Sano", 235 | "screen_name": "hiroakis", 236 | "icon": "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 237 | }, 238 | "updated_by": { 239 | "name": "Hiroaki Sano", 240 | "screen_name": "hiroakis", 241 | "icon": "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 242 | }, 243 | "kind": "flow", 244 | "comments_count": 1, 245 | "tasks_count": 1, 246 | "done_tasks_count": 1, 247 | "stargazers_count": 1, 248 | "watchers_count": 1, 249 | "star": true, 250 | "watch": true 251 | } 252 | ` 253 | var postData request.PostData 254 | bufbody := &bytes.Buffer{} 255 | bufbody.ReadFrom(r.Body) 256 | json.Unmarshal(bufbody.Bytes(), &postData) 257 | 258 | w.Header().Set("Content-Type", "application/json") 259 | if r.Method != "POST" { 260 | w.WriteHeader(http.StatusBadRequest) 261 | return 262 | } 263 | if postData.Post.Name != "hi!" { 264 | w.WriteHeader(http.StatusBadRequest) 265 | return 266 | } 267 | if postData.Post.BodyMd != "# Getting Started\n" { 268 | w.WriteHeader(http.StatusBadRequest) 269 | return 270 | } 271 | if postData.Post.Tags[0] != "api" { 272 | w.WriteHeader(http.StatusBadRequest) 273 | return 274 | } 275 | if postData.Post.Tags[1] != "dev" { 276 | w.WriteHeader(http.StatusBadRequest) 277 | return 278 | } 279 | if postData.Post.Category != "dev/2015/05/10" { 280 | w.WriteHeader(http.StatusBadRequest) 281 | return 282 | } 283 | if postData.Post.Wip != false { 284 | w.WriteHeader(http.StatusBadRequest) 285 | return 286 | } 287 | if postData.Post.Message != "Add Getting Started section" { 288 | w.WriteHeader(http.StatusBadRequest) 289 | return 290 | } 291 | 292 | w.WriteHeader(http.StatusCreated) 293 | w.Write([]byte(post)) 294 | }) 295 | 296 | var updatePostHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 297 | post := ` 298 | { 299 | "number": 1, 300 | "name": "hi!", 301 | "full_name": "日報/2015/05/09/hi! #api #dev", 302 | "wip": true, 303 | "body_md": "# Getting Started", 304 | "body_html": "

\n > Getting StartedGetting Started

\n", 305 | "created_at": "2015-05-09T11:54:50+09:00", 306 | "message": "Add Getting Started section", 307 | "url": "https://docs.esa.io/posts/1", 308 | "updated_at": "2015-05-09T11:54:51+09:00", 309 | "tags": [ 310 | "api", 311 | "dev" 312 | ], 313 | "category": "日報/2015/05/09", 314 | "revision_number": 1, 315 | "created_by": { 316 | "name": "Hiroaki Sano", 317 | "screen_name": "hiroakis", 318 | "icon": "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 319 | }, 320 | "updated_by": { 321 | "name": "Hiroaki Sano", 322 | "screen_name": "hiroakis", 323 | "icon": "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 324 | }, 325 | "overlapped": false, 326 | "kind": "flow", 327 | "comments_count": 1, 328 | "tasks_count": 1, 329 | "done_tasks_count": 1, 330 | "stargazers_count": 1, 331 | "watchers_count": 1, 332 | "star": true, 333 | "watch": true 334 | } 335 | ` 336 | var postData request.PostData 337 | bufbody := &bytes.Buffer{} 338 | bufbody.ReadFrom(r.Body) 339 | json.Unmarshal(bufbody.Bytes(), &postData) 340 | 341 | w.Header().Set("Content-Type", "application/json") 342 | if r.Method != "PATCH" { 343 | w.WriteHeader(http.StatusBadRequest) 344 | return 345 | } 346 | if postData.Post.Name != "hi!" { 347 | w.WriteHeader(http.StatusBadRequest) 348 | return 349 | } 350 | if postData.Post.BodyMd != "# Getting Started\n" { 351 | w.WriteHeader(http.StatusBadRequest) 352 | return 353 | } 354 | if postData.Post.Tags[0] != "api" { 355 | w.WriteHeader(http.StatusBadRequest) 356 | return 357 | } 358 | if postData.Post.Tags[1] != "dev" { 359 | w.WriteHeader(http.StatusBadRequest) 360 | return 361 | } 362 | if postData.Post.Category != "dev/2015/05/10" { 363 | w.WriteHeader(http.StatusBadRequest) 364 | return 365 | } 366 | if postData.Post.Wip != false { 367 | w.WriteHeader(http.StatusBadRequest) 368 | return 369 | } 370 | if postData.Post.Message != "Add Getting Started section" { 371 | w.WriteHeader(http.StatusBadRequest) 372 | return 373 | } 374 | if postData.Post.OriginalRevision.BodyMd != "# Getting ..." { 375 | w.WriteHeader(http.StatusBadRequest) 376 | return 377 | } 378 | if postData.Post.OriginalRevision.Number != 1 { 379 | w.WriteHeader(http.StatusBadRequest) 380 | return 381 | } 382 | if postData.Post.OriginalRevision.User != "hiroakis" { 383 | w.WriteHeader(http.StatusBadRequest) 384 | return 385 | } 386 | 387 | w.WriteHeader(http.StatusOK) 388 | w.Write([]byte(post)) 389 | }) 390 | 391 | var deletePostHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 392 | 393 | w.Header().Set("Content-Type", "application/json") 394 | if r.Method != "DELETE" { 395 | w.WriteHeader(http.StatusBadRequest) 396 | return 397 | } 398 | w.WriteHeader(http.StatusNoContent) 399 | // w.Write([]byte(post)) 400 | }) 401 | 402 | var commentsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 403 | comments := ` 404 | { 405 | "comments": [ 406 | { 407 | "id": 1, 408 | "body_md": "(大事)", 409 | "body_html": "

(大事)

", 410 | "created_at": "2014-05-10T12:45:42+09:00", 411 | "updated_at": "2014-05-18T23:02:29+09:00", 412 | "url": "https://docs.esa.io/posts/2#comment-1", 413 | "created_by": { 414 | "name": "Hiroaki Sano", 415 | "screen_name": "hiroakis", 416 | "icon": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 417 | } 418 | } 419 | ], 420 | "prev_page": null, 421 | "next_page": 1, 422 | "total_count": 1 423 | } 424 | ` 425 | 426 | w.Header().Set("Content-Type", "application/json") 427 | if r.Method != "GET" { 428 | w.WriteHeader(http.StatusBadRequest) 429 | return 430 | } 431 | w.WriteHeader(http.StatusOK) 432 | w.Write([]byte(comments)) 433 | }) 434 | 435 | var commentHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 436 | comment := ` 437 | { 438 | "id": 13, 439 | "body_md": "読みたい", 440 | "body_html": "

読みたい

", 441 | "created_at": "2014-05-13T16:17:42+09:00", 442 | "updated_at": "2014-05-18T23:02:29+09:00", 443 | "url": "https://docs.esa.io/posts/13#comment-13", 444 | "created_by": { 445 | "name": "Sano Hiroaki", 446 | "screen_name": "sano", 447 | "icon": "https://img.esa.io/uploads/production/users/2/icon/thumb_m_2690997f07b7de3014a36d90827603d6.jpg" 448 | } 449 | } 450 | ` 451 | 452 | w.Header().Set("Content-Type", "application/json") 453 | if r.Method != "GET" { 454 | w.WriteHeader(http.StatusBadRequest) 455 | return 456 | } 457 | w.WriteHeader(http.StatusOK) 458 | w.Write([]byte(comment)) 459 | }) 460 | 461 | var createCommentHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 462 | comment := ` 463 | { 464 | "id": 22767, 465 | "body_md": "LGTM!", 466 | "body_html": "

LGTM!

\n", 467 | "created_at": "2015-06-21T19:36:20+09:00", 468 | "updated_at": "2015-06-21T19:36:20+09:00", 469 | "url": "https://docs.esa.io/posts/2#comment-22767", 470 | "created_by": { 471 | "name": "Hiroaki Sano", 472 | "screen_name": "hiroakis", 473 | "icon": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 474 | } 475 | } 476 | ` 477 | var commentData request.CommentData 478 | bufbody := &bytes.Buffer{} 479 | bufbody.ReadFrom(r.Body) 480 | json.Unmarshal(bufbody.Bytes(), &commentData) 481 | 482 | w.Header().Set("Content-Type", "application/json") 483 | if r.Method != "POST" { 484 | w.WriteHeader(http.StatusBadRequest) 485 | return 486 | } 487 | if commentData.Comment.BodyMd != "LGTM!" { 488 | w.WriteHeader(http.StatusBadRequest) 489 | return 490 | } 491 | w.WriteHeader(http.StatusCreated) 492 | w.Write([]byte(comment)) 493 | }) 494 | 495 | var updateCommentHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 496 | comment := ` 497 | { 498 | "id": 22767, 499 | "body_md": "LGTM!!!", 500 | "body_html": "

LGTM!!!

\n", 501 | "created_at": "2015-06-21T19:36:20+09:00", 502 | "updated_at": "2015-06-21T19:40:33+09:00", 503 | "url": "https://docs.esa.io/posts/2#comment-22767", 504 | "created_by": { 505 | "name": "Hiroaki Sano", 506 | "screen_name": "hiroakis", 507 | "icon": "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" 508 | } 509 | } 510 | ` 511 | var commentData request.CommentData 512 | bufbody := &bytes.Buffer{} 513 | bufbody.ReadFrom(r.Body) 514 | json.Unmarshal(bufbody.Bytes(), &commentData) 515 | 516 | w.Header().Set("Content-Type", "application/json") 517 | if r.Method != "PATCH" { 518 | w.WriteHeader(http.StatusBadRequest) 519 | return 520 | } 521 | if commentData.Comment.BodyMd != "LGTM!!!" { 522 | w.WriteHeader(http.StatusBadRequest) 523 | return 524 | } 525 | w.WriteHeader(http.StatusOK) 526 | w.Write([]byte(comment)) 527 | }) 528 | 529 | var deleteCommentHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 530 | 531 | w.Header().Set("Content-Type", "application/json") 532 | if r.Method != "DELETE" { 533 | w.WriteHeader(http.StatusBadRequest) 534 | return 535 | } 536 | w.WriteHeader(http.StatusNoContent) 537 | // w.Write([]byte(post)) 538 | }) 539 | 540 | func fakeClient(testURL string) *EsaClient { 541 | esaClient := NewEsaClient("accessToken", "team") 542 | esaClient.SetApi(testURL) 543 | return esaClient 544 | } 545 | 546 | func TestGetTeams(t *testing.T) { 547 | 548 | testServer := httptest.NewServer(teamsHandler) 549 | defer testServer.Close() 550 | teams, err := fakeClient(testServer.URL).GetTeams() 551 | 552 | if err != nil { 553 | t.Error("Error occurred") 554 | } 555 | if teams.Teams[0].Name != "docs" { 556 | t.Error("Name does not match") 557 | } 558 | if teams.Teams[0].Privacy != "open" { 559 | t.Error("Privacy does not match") 560 | } 561 | if teams.Teams[0].Description != "esa.io official documents" { 562 | t.Error("Description does not match") 563 | } 564 | if teams.Teams[0].Icon != "https://img.esa.io/uploads/production/teams/105/icon/thumb_m_0537ab827c4b0c18b60af6cdd94f239c.png" { 565 | t.Error("Icon does not match") 566 | } 567 | if teams.Teams[0].Url != "https://docs.esa.io/" { 568 | t.Error("Url does not match") 569 | } 570 | if teams.PrevPage.String() != "" { 571 | t.Error("PrevPage does not match") 572 | } 573 | if teams.NextPage.String() != "1" { 574 | t.Error("NextPage does not match") 575 | } 576 | if teams.TotalCount != 1 { 577 | t.Error("TotalCount does not match") 578 | } 579 | } 580 | 581 | func TestGetTeam(t *testing.T) { 582 | 583 | testServer := httptest.NewServer(teamHandler) 584 | defer testServer.Close() 585 | team, err := fakeClient(testServer.URL).GetTeam() 586 | 587 | if err != nil { 588 | t.Error("Error occurred") 589 | } 590 | if team.Name != "docs" { 591 | t.Error("Name does not match") 592 | } 593 | if team.Privacy != "open" { 594 | t.Error("Privacy does not match") 595 | } 596 | if team.Description != "esa.io official documents" { 597 | t.Error("Description does not match") 598 | } 599 | if team.Icon != "https://img.esa.io/uploads/production/teams/105/icon/thumb_m_0537ab827c4b0c18b60af6cdd94f239c.png" { 600 | t.Error("Icon does not match") 601 | } 602 | if team.Url != "https://docs.esa.io/" { 603 | t.Error("Url does not match") 604 | } 605 | } 606 | 607 | func TestGetStats(t *testing.T) { 608 | 609 | testServer := httptest.NewServer(statsHandler) 610 | defer testServer.Close() 611 | stats, err := fakeClient(testServer.URL).GetStats() 612 | 613 | if err != nil { 614 | t.Error("Error occurred") 615 | } 616 | if stats.Members != 20 { 617 | t.Error("Name does not match") 618 | } 619 | if stats.Posts != 1959 { 620 | t.Error("Posts does not match") 621 | } 622 | if stats.Comments != 2695 { 623 | t.Error("Comments does not match") 624 | } 625 | if stats.Stars != 3115 { 626 | t.Error("Stars does not match") 627 | } 628 | if stats.DailyActiveUsers != 8 { 629 | t.Error("DailyActiveUsers does not match") 630 | } 631 | if stats.WeeklyActiveUsers != 14 { 632 | t.Error("WeeklyActiveUsers does not match") 633 | } 634 | if stats.MonthlyActiveUsers != 15 { 635 | t.Error("MonthlyActiveUsers does not match") 636 | } 637 | } 638 | 639 | func TestGetMembers(t *testing.T) { 640 | 641 | testServer := httptest.NewServer(membersHandler) 642 | defer testServer.Close() 643 | members, err := fakeClient(testServer.URL).GetMembers() 644 | 645 | if err != nil { 646 | t.Error("Error occurred") 647 | } 648 | if members.Members[0].Name != "Hiroaki Sano" { 649 | t.Error("Name does not match") 650 | } 651 | if members.Members[1].Name != "Sano Hiroaki" { 652 | t.Error("Name does not match") 653 | } 654 | if members.Members[0].ScreenName != "hiroakis" { 655 | t.Error("ScreenName does not match") 656 | } 657 | if members.Members[1].ScreenName != "sano" { 658 | t.Error("ScreenName does not match") 659 | } 660 | if members.Members[0].Icon != "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" { 661 | t.Error("Icon does not match") 662 | } 663 | if members.Members[1].Icon != "https://img.esa.io/uploads/production/users/2/icon/thumb_m_2690997f07b7de3014a36d90827603d6.jpg" { 664 | t.Error("Icon does not match") 665 | } 666 | if members.Members[0].Email != "hiroakis@example.com" { 667 | t.Error("Email does not match") 668 | } 669 | if members.Members[1].Email != "sano@example.com" { 670 | t.Error("Email does not match") 671 | } 672 | if members.PrevPage.String() != "" { 673 | t.Error("PrevPage does not match") 674 | } 675 | if members.NextPage.String() != "1" { 676 | t.Error("NextPage does not match") 677 | } 678 | if members.TotalCount != 2 { 679 | t.Error("TotalCount does not match") 680 | } 681 | } 682 | 683 | func TestGetPosts(t *testing.T) { 684 | 685 | testServer := httptest.NewServer(postsHandler) 686 | defer testServer.Close() 687 | posts, err := fakeClient(testServer.URL).GetPosts() 688 | 689 | if err != nil { 690 | t.Error("Error occurred") 691 | } 692 | if posts.Posts[0].Number != 1 { 693 | t.Error("Number does not match") 694 | } 695 | if posts.Posts[0].Name != "hi!" { 696 | t.Error("Name does not match") 697 | } 698 | if posts.Posts[0].FullName != "日報/2015/05/09/hi! #api #dev" { 699 | t.Error("FullName does not match") 700 | } 701 | if posts.Posts[0].Wip != true { 702 | t.Error("Wip does not match") 703 | } 704 | if posts.Posts[0].BodyMd != "# Getting Started" { 705 | t.Error("BodyMd does not match") 706 | } 707 | if posts.Posts[0].BodyHtml != "

\n > Getting StartedGetting Started

\n" { 708 | t.Error("BodyHtml does not match") 709 | } 710 | 711 | createdAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-05-09T11:54:50+09:00") 712 | if posts.Posts[0].CreatedAt != createdAt { 713 | t.Error("CreatedAt does not match") 714 | } 715 | if posts.Posts[0].Message != "Add Getting Started section" { 716 | t.Error("Message does not match") 717 | } 718 | if posts.Posts[0].Url != "https://docs.esa.io/posts/1" { 719 | t.Error("Url does not match") 720 | } 721 | // "2015-05-09T11:54:51+09:00" 722 | updatedAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-05-09T11:54:51+09:00") 723 | if posts.Posts[0].UpdatedAt != updatedAt { 724 | t.Error("UpdatedAt does not match") 725 | } 726 | if posts.Posts[0].Tags[0] != "api" { 727 | t.Error("Tag[0] does not match") 728 | } 729 | if posts.Posts[0].Tags[1] != "dev" { 730 | t.Error("Tag[1] does not match") 731 | } 732 | if posts.Posts[0].Category != "日報/2015/05/09" { 733 | t.Error("Category does not match") 734 | } 735 | if posts.Posts[0].RevisionNumber != 1 { 736 | t.Error("RevisionNumber does not match") 737 | } 738 | if posts.Posts[0].CreatedBy.Name != "Hiroaki Sano" { 739 | t.Error("CreatedBy.Name does not match") 740 | } 741 | if posts.Posts[0].CreatedBy.ScreenName != "hiroakis" { 742 | t.Error("CreatedBy.ScreenName does not match") 743 | } 744 | if posts.Posts[0].CreatedBy.Icon != "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" { 745 | t.Error("CreatedBy.Icon does not match") 746 | } 747 | if posts.Posts[0].UpdatedBy.Name != "Hiroaki Sano" { 748 | t.Error("UpdatedBy.Name does not match") 749 | } 750 | if posts.Posts[0].UpdatedBy.ScreenName != "hiroakis" { 751 | t.Error("UpdatedBy.ScreenName does not match") 752 | } 753 | if posts.Posts[0].UpdatedBy.Icon != "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" { 754 | t.Error("UpdatedBy.Icon does not match") 755 | } 756 | if posts.PrevPage.String() != "" { 757 | t.Error("PrevPage does not match") 758 | } 759 | if posts.NextPage.String() != "1" { 760 | t.Error("NextPage does not match") 761 | } 762 | if posts.TotalCount != 1 { 763 | t.Error("TotalCount does not match") 764 | } 765 | } 766 | 767 | func TestGetPost(t *testing.T) { 768 | 769 | testServer := httptest.NewServer(postHandler) 770 | defer testServer.Close() 771 | post, err := fakeClient(testServer.URL).GetPost(1) 772 | 773 | if err != nil { 774 | t.Error("Error occurred") 775 | } 776 | if post.Number != 1 { 777 | t.Error("Number does not match") 778 | } 779 | if post.Name != "hi!" { 780 | t.Error("Name does not match") 781 | } 782 | if post.FullName != "日報/2015/05/09/hi! #api #dev" { 783 | t.Error("FullName does not match") 784 | } 785 | if post.Wip != true { 786 | t.Error("Wip does not match") 787 | } 788 | if post.BodyMd != "# Getting Started" { 789 | t.Error("BodyMd does not match") 790 | } 791 | if post.BodyHtml != "

\n > Getting StartedGetting Started

\n" { 792 | t.Error("BodyHtml does not match") 793 | } 794 | 795 | createdAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-05-09T11:54:50+09:00") 796 | if post.CreatedAt != createdAt { 797 | t.Error("CreatedAt does not match") 798 | } 799 | if post.Message != "Add Getting Started section" { 800 | t.Error("Message does not match") 801 | } 802 | if post.Url != "https://docs.esa.io/posts/1" { 803 | t.Error("Url does not match") 804 | } 805 | // "2015-05-09T11:54:51+09:00" 806 | updatedAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-05-09T11:54:51+09:00") 807 | if post.UpdatedAt != updatedAt { 808 | t.Error("UpdatedAt does not match") 809 | } 810 | if post.Tags[0] != "api" { 811 | t.Error("Tag[0] does not match") 812 | } 813 | if post.Tags[1] != "dev" { 814 | t.Error("Tag[1] does not match") 815 | } 816 | if post.Category != "日報/2015/05/09" { 817 | t.Error("Category does not match") 818 | } 819 | if post.RevisionNumber != 1 { 820 | t.Error("RevisionNumber does not match") 821 | } 822 | if post.CreatedBy.Name != "Hiroaki Sano" { 823 | t.Error("CreatedBy.Name does not match") 824 | } 825 | if post.CreatedBy.ScreenName != "hiroakis" { 826 | t.Error("CreatedBy.ScreenName does not match") 827 | } 828 | if post.CreatedBy.Icon != "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" { 829 | t.Error("CreatedBy.Icon does not match") 830 | } 831 | if post.UpdatedBy.Name != "Hiroaki Sano" { 832 | t.Error("UpdatedBy.Name does not match") 833 | } 834 | if post.UpdatedBy.ScreenName != "hiroakis" { 835 | t.Error("UpdatedBy.ScreenName does not match") 836 | } 837 | if post.UpdatedBy.Icon != "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" { 838 | t.Error("UpdatedBy.Icon does not match") 839 | } 840 | if post.Kind != "flow" { 841 | t.Error("Kind does not match") 842 | } 843 | if post.CommentsCount != 1 { 844 | t.Error("CommentsCount does not match") 845 | } 846 | if post.TasksCount != 1 { 847 | t.Error("TasksCount does not match") 848 | } 849 | if post.DoneTasksCount != 1 { 850 | t.Error("DoneTasksCount does not match") 851 | } 852 | if post.StargazersCount != 1 { 853 | t.Error("StargazersCount does not match") 854 | } 855 | if post.WatchersCount != 1 { 856 | t.Error("WatchersCount does not match") 857 | } 858 | if post.Star != true { 859 | t.Error("Star does not match") 860 | } 861 | if post.Watch != true { 862 | t.Error("Watch does not match") 863 | } 864 | } 865 | 866 | func TestCreatePost(t *testing.T) { 867 | testServer := httptest.NewServer(createPostHandler) 868 | defer testServer.Close() 869 | 870 | reqPost := 871 | request.Post{ 872 | Name: "hi!", 873 | BodyMd: "# Getting Started\n", 874 | Tags: []string{ 875 | "api", 876 | "dev", 877 | }, 878 | Category: "dev/2015/05/10", 879 | Wip: false, 880 | Message: "Add Getting Started section", 881 | } 882 | post, err := fakeClient(testServer.URL).CreatePost(reqPost) 883 | 884 | if err != nil { 885 | t.Error(err) 886 | } 887 | if post.Number != 1 { 888 | t.Error("Number does not match") 889 | } 890 | if post.Name != "hi!" { 891 | t.Error("Name does not match") 892 | } 893 | if post.FullName != "日報/2015/05/09/hi! #api #dev" { 894 | t.Error("FullName does not match") 895 | } 896 | if post.Wip != true { 897 | t.Error("Wip does not match") 898 | } 899 | if post.BodyMd != "# Getting Started" { 900 | t.Error("BodyMd does not match") 901 | } 902 | if post.BodyHtml != "

\n > Getting StartedGetting Started

\n" { 903 | t.Error("BodyHtml does not match") 904 | } 905 | 906 | createdAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-05-09T11:54:50+09:00") 907 | if post.CreatedAt != createdAt { 908 | t.Error("CreatedAt does not match") 909 | } 910 | if post.Message != "Add Getting Started section" { 911 | t.Error("Message does not match") 912 | } 913 | if post.Url != "https://docs.esa.io/posts/1" { 914 | t.Error("Url does not match") 915 | } 916 | // "2015-05-09T11:54:51+09:00" 917 | updatedAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-05-09T11:54:51+09:00") 918 | if post.UpdatedAt != updatedAt { 919 | t.Error("UpdatedAt does not match") 920 | } 921 | if post.Tags[0] != "api" { 922 | t.Error("Tag[0] does not match") 923 | } 924 | if post.Tags[1] != "dev" { 925 | t.Error("Tag[1] does not match") 926 | } 927 | if post.Category != "日報/2015/05/09" { 928 | t.Error("Category does not match") 929 | } 930 | if post.RevisionNumber != 1 { 931 | t.Error("RevisionNumber does not match") 932 | } 933 | if post.CreatedBy.Name != "Hiroaki Sano" { 934 | t.Error("CreatedBy.Name does not match") 935 | } 936 | if post.CreatedBy.ScreenName != "hiroakis" { 937 | t.Error("CreatedBy.ScreenName does not match") 938 | } 939 | if post.CreatedBy.Icon != "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" { 940 | t.Error("CreatedBy.Icon does not match") 941 | } 942 | if post.UpdatedBy.Name != "Hiroaki Sano" { 943 | t.Error("UpdatedBy.Name does not match") 944 | } 945 | if post.UpdatedBy.ScreenName != "hiroakis" { 946 | t.Error("UpdatedBy.ScreenName does not match") 947 | } 948 | } 949 | 950 | func TestUpdatePost(t *testing.T) { 951 | testServer := httptest.NewServer(updatePostHandler) 952 | defer testServer.Close() 953 | 954 | reqPost := 955 | request.Post{ 956 | Name: "hi!", 957 | BodyMd: "# Getting Started\n", 958 | Tags: []string{ 959 | "api", 960 | "dev", 961 | }, 962 | Category: "dev/2015/05/10", 963 | Wip: false, 964 | Message: "Add Getting Started section", 965 | OriginalRevision: request.OriginalRevision{ 966 | BodyMd: "# Getting ...", 967 | Number: 1, 968 | User: "hiroakis", 969 | }, 970 | } 971 | post, err := fakeClient(testServer.URL).UpdatePost(1, reqPost) 972 | 973 | if err != nil { 974 | t.Error(err) 975 | } 976 | if post.Number != 1 { 977 | t.Error("Number does not match") 978 | } 979 | if post.Name != "hi!" { 980 | t.Error("Name does not match") 981 | } 982 | if post.FullName != "日報/2015/05/09/hi! #api #dev" { 983 | t.Error("FullName does not match") 984 | } 985 | if post.Wip != true { 986 | t.Error("Wip does not match") 987 | } 988 | if post.BodyMd != "# Getting Started" { 989 | t.Error("BodyMd does not match") 990 | } 991 | if post.BodyHtml != "

\n > Getting StartedGetting Started

\n" { 992 | t.Error("BodyHtml does not match") 993 | } 994 | 995 | createdAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-05-09T11:54:50+09:00") 996 | if post.CreatedAt != createdAt { 997 | t.Error("CreatedAt does not match") 998 | } 999 | if post.Message != "Add Getting Started section" { 1000 | t.Error("Message does not match") 1001 | } 1002 | if post.Url != "https://docs.esa.io/posts/1" { 1003 | t.Error("Url does not match") 1004 | } 1005 | // "2015-05-09T11:54:51+09:00" 1006 | updatedAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-05-09T11:54:51+09:00") 1007 | if post.UpdatedAt != updatedAt { 1008 | t.Error("UpdatedAt does not match") 1009 | } 1010 | if post.Tags[0] != "api" { 1011 | t.Error("Tag[0] does not match") 1012 | } 1013 | if post.Tags[1] != "dev" { 1014 | t.Error("Tag[1] does not match") 1015 | } 1016 | if post.Category != "日報/2015/05/09" { 1017 | t.Error("Category does not match") 1018 | } 1019 | if post.RevisionNumber != 1 { 1020 | t.Error("RevisionNumber does not match") 1021 | } 1022 | if post.CreatedBy.Name != "Hiroaki Sano" { 1023 | t.Error("CreatedBy.Name does not match") 1024 | } 1025 | if post.CreatedBy.ScreenName != "hiroakis" { 1026 | t.Error("CreatedBy.ScreenName does not match") 1027 | } 1028 | if post.CreatedBy.Icon != "http://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" { 1029 | t.Error("CreatedBy.Icon does not match") 1030 | } 1031 | if post.UpdatedBy.Name != "Hiroaki Sano" { 1032 | t.Error("UpdatedBy.Name does not match") 1033 | } 1034 | if post.UpdatedBy.ScreenName != "hiroakis" { 1035 | t.Error("UpdatedBy.ScreenName does not match") 1036 | } 1037 | } 1038 | 1039 | func TestDeletePost(t *testing.T) { 1040 | testServer := httptest.NewServer(deletePostHandler) 1041 | defer testServer.Close() 1042 | deleted, err := fakeClient(testServer.URL).DeletePost(1) 1043 | 1044 | if err != nil { 1045 | t.Error(err) 1046 | } 1047 | if deleted != true { 1048 | t.Error("error") 1049 | } 1050 | } 1051 | 1052 | func TestGetComments(t *testing.T) { 1053 | 1054 | testServer := httptest.NewServer(commentsHandler) 1055 | defer testServer.Close() 1056 | comments, err := fakeClient(testServer.URL).GetComments(1) 1057 | 1058 | if err != nil { 1059 | t.Error("Error occurred") 1060 | } 1061 | if comments.Comments[0].Id != 1 { 1062 | t.Error("Id does not match") 1063 | } 1064 | if comments.Comments[0].BodyMd != "(大事)" { 1065 | t.Error("BodyMd does not match") 1066 | } 1067 | if comments.Comments[0].BodyHtml != "

(大事)

" { 1068 | t.Error("BodyHtml does not match") 1069 | } 1070 | 1071 | createdAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2014-05-10T12:45:42+09:00") 1072 | if comments.Comments[0].CreatedAt != createdAt { 1073 | t.Error("CreatedAt does not match") 1074 | } 1075 | if comments.Comments[0].Url != "https://docs.esa.io/posts/2#comment-1" { 1076 | t.Error("Url does not match") 1077 | } 1078 | updatedAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2014-05-18T23:02:29+09:00") 1079 | if comments.Comments[0].UpdatedAt != updatedAt { 1080 | t.Error("UpdatedAt does not match") 1081 | } 1082 | if comments.Comments[0].CreatedBy.Name != "Hiroaki Sano" { 1083 | t.Error("CreatedBy.Name does not match") 1084 | } 1085 | if comments.Comments[0].CreatedBy.ScreenName != "hiroakis" { 1086 | t.Error("CreatedBy.ScreenName does not match") 1087 | } 1088 | if comments.Comments[0].CreatedBy.Icon != "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" { 1089 | t.Error("CreatedBy.Icon does not match") 1090 | } 1091 | if comments.PrevPage.String() != "" { 1092 | t.Error("PrevPage does not match") 1093 | } 1094 | if comments.NextPage.String() != "1" { 1095 | t.Error("NextPage does not match") 1096 | } 1097 | if comments.TotalCount != 1 { 1098 | t.Error("TotalCount does not match") 1099 | } 1100 | } 1101 | 1102 | func TestGetComment(t *testing.T) { 1103 | 1104 | testServer := httptest.NewServer(commentHandler) 1105 | defer testServer.Close() 1106 | comment, err := fakeClient(testServer.URL).GetComment(1) 1107 | 1108 | if err != nil { 1109 | t.Error("Error occurred") 1110 | } 1111 | if comment.Id != 13 { 1112 | t.Error("Id does not match") 1113 | } 1114 | if comment.BodyMd != "読みたい" { 1115 | t.Error("BodyMd does not match") 1116 | } 1117 | if comment.BodyHtml != "

読みたい

" { 1118 | t.Error("BodyHtml does not match") 1119 | } 1120 | 1121 | createdAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2014-05-13T16:17:42+09:00") 1122 | if comment.CreatedAt != createdAt { 1123 | t.Error("CreatedAt does not match") 1124 | } 1125 | updatedAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2014-05-18T23:02:29+09:00") 1126 | if comment.UpdatedAt != updatedAt { 1127 | t.Error("UpdatedAt does not match") 1128 | } 1129 | if comment.Url != "https://docs.esa.io/posts/13#comment-13" { 1130 | t.Error("Url does not match") 1131 | } 1132 | if comment.CreatedBy.Name != "Sano Hiroaki" { 1133 | t.Error("CreatedBy.Name does not match") 1134 | } 1135 | if comment.CreatedBy.ScreenName != "sano" { 1136 | t.Error("CreatedBy.ScreenName does not match") 1137 | } 1138 | if comment.CreatedBy.Icon != "https://img.esa.io/uploads/production/users/2/icon/thumb_m_2690997f07b7de3014a36d90827603d6.jpg" { 1139 | t.Error("CreatedBy.Icon does not match") 1140 | } 1141 | } 1142 | 1143 | func TestCreateComment(t *testing.T) { 1144 | 1145 | testServer := httptest.NewServer(createCommentHandler) 1146 | defer testServer.Close() 1147 | 1148 | reqComment := request.Comment{ 1149 | BodyMd: "LGTM!", 1150 | } 1151 | comment, err := fakeClient(testServer.URL).CreateComment(2, reqComment) 1152 | 1153 | if err != nil { 1154 | t.Error(err) 1155 | } 1156 | if comment.Id != 22767 { 1157 | t.Error("Id does not match") 1158 | } 1159 | if comment.BodyMd != "LGTM!" { 1160 | t.Error("BodyMd does not match") 1161 | } 1162 | if comment.BodyHtml != "

LGTM!

\n" { 1163 | t.Error("BodyHtml does not match") 1164 | } 1165 | 1166 | createdAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-06-21T19:36:20+09:00") 1167 | if comment.CreatedAt != createdAt { 1168 | t.Error("CreatedAt does not match") 1169 | } 1170 | updatedAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-06-21T19:36:20+09:00") 1171 | if comment.UpdatedAt != updatedAt { 1172 | t.Error("UpdatedAt does not match") 1173 | } 1174 | if comment.Url != "https://docs.esa.io/posts/2#comment-22767" { 1175 | t.Error("Url does not match") 1176 | } 1177 | if comment.CreatedBy.Name != "Hiroaki Sano" { 1178 | t.Error("CreatedBy.Name does not match") 1179 | } 1180 | if comment.CreatedBy.ScreenName != "hiroakis" { 1181 | t.Error("CreatedBy.ScreenName does not match") 1182 | } 1183 | if comment.CreatedBy.Icon != "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" { 1184 | t.Error("CreatedBy.Icon does not match") 1185 | } 1186 | } 1187 | 1188 | func TestUpdateComment(t *testing.T) { 1189 | 1190 | testServer := httptest.NewServer(updateCommentHandler) 1191 | defer testServer.Close() 1192 | 1193 | reqComment := request.Comment{ 1194 | BodyMd: "LGTM!!!", 1195 | } 1196 | comment, err := fakeClient(testServer.URL).UpdateComment(2, reqComment) 1197 | 1198 | if err != nil { 1199 | t.Error(err) 1200 | } 1201 | if comment.Id != 22767 { 1202 | t.Error("Id does not match") 1203 | } 1204 | if comment.BodyMd != "LGTM!!!" { 1205 | t.Error("BodyMd does not match") 1206 | } 1207 | if comment.BodyHtml != "

LGTM!!!

\n" { 1208 | t.Error("BodyHtml does not match") 1209 | } 1210 | 1211 | createdAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-06-21T19:36:20+09:00") 1212 | if comment.CreatedAt != createdAt { 1213 | t.Error("CreatedAt does not match") 1214 | } 1215 | updatedAt, _ := time.Parse("2006-01-02T15:04:05-07:00", "2015-06-21T19:40:33+09:00") 1216 | if comment.UpdatedAt != updatedAt { 1217 | t.Error("UpdatedAt does not match") 1218 | } 1219 | if comment.Url != "https://docs.esa.io/posts/2#comment-22767" { 1220 | t.Error("Url does not match") 1221 | } 1222 | if comment.CreatedBy.Name != "Hiroaki Sano" { 1223 | t.Error("CreatedBy.Name does not match") 1224 | } 1225 | if comment.CreatedBy.ScreenName != "hiroakis" { 1226 | t.Error("CreatedBy.ScreenName does not match") 1227 | } 1228 | if comment.CreatedBy.Icon != "https://img.esa.io/uploads/production/users/1/icon/thumb_m_402685a258cf2a33c1d6c13a89adec92.png" { 1229 | t.Error("CreatedBy.Icon does not match") 1230 | } 1231 | } 1232 | 1233 | func TestDeleteComment(t *testing.T) { 1234 | testServer := httptest.NewServer(deleteCommentHandler) 1235 | defer testServer.Close() 1236 | deleted, err := fakeClient(testServer.URL).DeleteComment(1) 1237 | 1238 | if err != nil { 1239 | t.Error(err) 1240 | } 1241 | if deleted != true { 1242 | t.Error("error") 1243 | } 1244 | } 1245 | -------------------------------------------------------------------------------- /request/request.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | type PostData struct { 4 | Post Post `json:"post"` 5 | } 6 | 7 | type Post struct { 8 | Name string `json:"name,omitempty"` 9 | BodyMd string `json:"body_md,omitempty"` 10 | Tags []string `json:"tags"` 11 | Category string `json:"category,omitempty"` 12 | Wip bool `json:"wip"` 13 | Message string `json:"message"` 14 | OriginalRevision OriginalRevision `json:"original_revision,omitempty"` 15 | TemplatePostId int `json:"template_post_id"` 16 | } 17 | 18 | type OriginalRevision struct { 19 | BodyMd string `json:"body_md"` 20 | Number int `json:"number"` 21 | User string `json:"user"` 22 | } 23 | 24 | type CommentData struct { 25 | Comment Comment `json:"comment"` 26 | } 27 | 28 | type Comment struct { 29 | BodyMd string `json:"body_md"` 30 | } 31 | -------------------------------------------------------------------------------- /response/response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | type Team struct { 9 | Name string `json:"name"` 10 | Privacy string `json:"privacy"` 11 | Description string `json:"description"` 12 | Icon string `json:"icon"` 13 | Url string `json:"url"` 14 | } 15 | 16 | type Teams struct { 17 | Teams []Team `json:"teams"` 18 | PrevPage json.Number `json:"prev_page"` 19 | NextPage json.Number `json:"next_page"` 20 | TotalCount int `json:"total_count"` 21 | } 22 | 23 | type Stats struct { 24 | Members int `json:"members"` 25 | Posts int `json:"posts"` 26 | Comments int `json:"comments"` 27 | Stars int `json:"stars"` 28 | DailyActiveUsers int `json:"daily_active_users"` 29 | WeeklyActiveUsers int `json:"weekly_active_users"` 30 | MonthlyActiveUsers int `json:"monthly_active_users"` 31 | } 32 | 33 | type Member struct { 34 | Name string `json:"name"` 35 | ScreenName string `json:"screen_name"` 36 | Icon string `json:"icon"` 37 | Email string `json:"email"` 38 | } 39 | 40 | type Members struct { 41 | Members []Member `json:"members"` 42 | PrevPage json.Number `json:"prev_page"` 43 | NextPage json.Number `json:"next_page"` 44 | TotalCount int `json:"total_count"` 45 | } 46 | 47 | type ByUser struct { 48 | Name string `json:"name"` 49 | ScreenName string `json:"screen_name"` 50 | Icon string `json:"icon"` 51 | } 52 | 53 | type Posts struct { 54 | Posts []Post `json:"posts"` 55 | PrevPage json.Number `json:"prev_page"` 56 | NextPage json.Number `json:"next_page"` 57 | TotalCount int `json:"total_count"` 58 | } 59 | 60 | type Post struct { 61 | Number int `json:"number"` 62 | Name string `json:"name"` 63 | FullName string `json:"full_name"` 64 | Wip bool `json:"wip"` 65 | BodyMd string `json:"body_md"` 66 | BodyHtml string `json:"body_html"` 67 | CreatedAt time.Time `json:"created_at"` 68 | Message string `json:"message"` 69 | Url string `json:"url"` 70 | UpdatedAt time.Time `json:"updated_at"` 71 | Tags []string `json:"tags"` 72 | Category string `json:"category"` 73 | RevisionNumber int `json:"revision_number"` 74 | CreatedBy ByUser `json:"created_by"` 75 | UpdatedBy ByUser `json:"updated_by"` 76 | Overlapped bool `json:"overlapped"` 77 | Kind string `json:"kind"` 78 | CommentsCount int `json:"comments_count"` 79 | TasksCount int `json:"tasks_count"` 80 | DoneTasksCount int `json:"done_tasks_count"` 81 | StargazersCount int `json:"stargazers_count"` 82 | WatchersCount int `json:"watchers_count"` 83 | Star bool `json:"star"` 84 | Watch bool `json:"watch"` 85 | } 86 | 87 | type Comment struct { 88 | Id int `json:"id"` 89 | BodyMd string `json:"body_md"` 90 | BodyHtml string `json:"body_html"` 91 | CreatedAt time.Time `json:"created_at"` 92 | UpdatedAt time.Time `json:"updated_at"` 93 | Url string `json:"url"` 94 | CreatedBy ByUser `json:"created_by"` 95 | } 96 | 97 | type Comments struct { 98 | Comments []Comment `json:"comments"` 99 | PrevPage json.Number `json:"prev_page"` 100 | NextPage json.Number `json:"next_page"` 101 | TotalCount int `json:"total_count"` 102 | } 103 | --------------------------------------------------------------------------------