-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tgb: CallbackDataFilter and CallbackDataCodec (#140)
* feat: add callback data encode/decode * examples: CallbackDataParser * add MustButton method * remove url.JoinPath * tests: CallbackDataFilter * lint: ignore some lines in tests
- Loading branch information
Showing
5 changed files
with
1,420 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"strconv" | ||
) | ||
|
||
// Primitive API client for https://jsonplaceholder.typicode.com | ||
type API struct { | ||
BaseURL string | ||
Client *http.Client | ||
} | ||
|
||
type User struct { | ||
ID int `json:"id"` | ||
Name string `json:"name"` | ||
Username string `json:"username"` | ||
Email string `json:"email"` | ||
Address struct { | ||
Street string `json:"street"` | ||
Suite string `json:"suite"` | ||
City string `json:"city"` | ||
Zipcode string `json:"zipcode"` | ||
Geo struct { | ||
Lat float64 `json:"lat,string"` | ||
Lng float64 `json:"lng,string"` | ||
} `json:"geo"` | ||
} `json:"address"` | ||
Phone string `json:"phone"` | ||
Website string `json:"website"` | ||
Company struct { | ||
Name string `json:"name"` | ||
CatchPhrase string `json:"catchPhrase"` | ||
Bs string `json:"bs"` | ||
} `json:"company"` | ||
} | ||
|
||
type Post struct { | ||
UserID int `json:"userId"` | ||
ID int `json:"id"` | ||
Title string `json:"title"` | ||
Body string `json:"body"` | ||
} | ||
|
||
type Comment struct { | ||
PostID int `json:"postId"` | ||
ID int `json:"id"` | ||
Name string `json:"name"` | ||
Email string `json:"email"` | ||
Body string `json:"body"` | ||
} | ||
|
||
func (a *API) request(ctx context.Context, path string, params url.Values, dst any) error { | ||
// better use url.JoinPath, but it's not available at go1.18 that specified as minimal version | ||
endpoint := a.BaseURL + path | ||
|
||
if len(params) > 0 { | ||
endpoinAsURL, err := url.Parse(endpoint) | ||
if err != nil { | ||
return fmt.Errorf("parse endpoint: %w", err) | ||
} | ||
|
||
endpoinAsURL.RawQuery = params.Encode() | ||
|
||
endpoint = endpoinAsURL.String() | ||
} | ||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, http.NoBody) | ||
if err != nil { | ||
return fmt.Errorf("build http request: %w", err) | ||
} | ||
|
||
res, err := a.Client.Do(req) | ||
if err != nil { | ||
return fmt.Errorf("execute http request: %w", err) | ||
} | ||
defer res.Body.Close() | ||
|
||
if res.StatusCode != http.StatusOK { | ||
return fmt.Errorf("http status: %d", res.StatusCode) | ||
} | ||
|
||
if err := json.NewDecoder(res.Body).Decode(dst); err != nil { | ||
return fmt.Errorf("unmarshal: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (a *API) Users(ctx context.Context) (users []User, err error) { | ||
err = a.request(ctx, "/users", nil, &users) | ||
return | ||
} | ||
|
||
func (a *API) User(ctx context.Context, id int) (user User, err error) { | ||
err = a.request(ctx, fmt.Sprintf("/users/%d", id), nil, &user) | ||
return | ||
} | ||
|
||
type PostsParams struct { | ||
UserID int `json:"userId"` | ||
} | ||
|
||
func (a *API) Posts(ctx context.Context, params *PostsParams) (posts []Post, err error) { | ||
vs := url.Values{} | ||
|
||
if params != nil { | ||
if params.UserID != 0 { | ||
vs.Set("userId", strconv.Itoa(params.UserID)) | ||
} | ||
} | ||
|
||
err = a.request(ctx, "/posts", vs, &posts) | ||
|
||
return | ||
} | ||
|
||
func (a *API) Post(ctx context.Context, id int) (post Post, err error) { | ||
err = a.request(ctx, fmt.Sprintf("/posts/%d", id), nil, &post) | ||
return | ||
} | ||
|
||
type CommentsParams struct { | ||
PostID int `json:"postId"` | ||
} | ||
|
||
func (a *API) Comments(ctx context.Context, params *CommentsParams) (comments []Comment, err error) { | ||
vs := url.Values{} | ||
|
||
if params != nil { | ||
if params.PostID != 0 { | ||
vs.Set("postId", strconv.Itoa(params.PostID)) | ||
} | ||
} | ||
|
||
err = a.request(ctx, "/comments", vs, &comments) | ||
|
||
return | ||
} | ||
|
||
func (a *API) Comment(ctx context.Context, id int) (comment Comment, err error) { | ||
err = a.request(ctx, fmt.Sprintf("/comments/%d", id), nil, &comment) | ||
return | ||
} |
Oops, something went wrong.