1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00

Implementation of WTF devto module

This commit is contained in:
vavelar
2019-09-20 17:31:11 +02:00
parent d76db44b72
commit 425a08522e
50 changed files with 611 additions and 11710 deletions

21
vendor/github.com/VictorAvelar/devto-api-go/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Victor Hugo Avelar Ossorio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,105 @@
package devto
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/google/go-querystring/query"
)
//ArticlesResource implements the APIResource interface
//for devto articles.
type ArticlesResource struct {
API *Client
}
//List will return the articles uploaded to devto, the result
//can be narrowed down, filtered or enhanced using query
// parameters as specified on the documentation.
//See: https://docs.dev.to/api/#tag/articles/paths/~1articles/get
func (ar *ArticlesResource) List(ctx context.Context, opt ArticleListOptions) ([]Article, error) {
var l []Article
q, err := query.Values(opt)
if err != nil {
return nil, err
}
req, _ := ar.API.NewRequest(http.MethodGet, fmt.Sprintf("api/articles?%s", q.Encode()), nil)
res, _ := ar.API.HTTPClient.Do(req)
cont, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
json.Unmarshal(cont, &l)
return l, nil
}
//Find will retrieve an Article matching the ID passed.
func (ar *ArticlesResource) Find(ctx context.Context, id uint32) (Article, error) {
var art Article
req, _ := ar.API.NewRequest(http.MethodGet, fmt.Sprintf("api/articles/%d", id), nil)
res, err := ar.API.HTTPClient.Do(req)
if err != nil {
return art, err
}
cont, err := ioutil.ReadAll(res.Body)
if err != nil {
return art, err
}
json.Unmarshal(cont, &art)
return art, nil
}
//New will create a new article on dev.to
func (ar *ArticlesResource) New(ctx context.Context, a Article) (Article, error) {
if ar.API.Config.InsecureOnly {
return a, ErrProtectedEndpoint
}
cont, err := json.Marshal(a)
if err != nil {
return a, err
}
req, err := ar.API.NewRequest(http.MethodPost, "api/articles", strings.NewReader(string(cont)))
if err != nil {
return a, err
}
req.Header.Add(APIKeyHeader, ar.API.Config.APIKey)
res, err := ar.API.HTTPClient.Do(req)
if err != nil {
return a, err
}
content, err := ioutil.ReadAll(res.Body)
if err != nil {
return a, err
}
json.Unmarshal(content, &a)
return a, nil
}
func (ar *ArticlesResource) Update(ctx context.Context, a Article) (Article, error) {
if ar.API.Config.InsecureOnly {
return a, ErrProtectedEndpoint
}
cont, err := json.Marshal(a)
if err != nil {
return a, err
}
req, err := ar.API.NewRequest(http.MethodPut, fmt.Sprintf("api/articles/%d", a.ID), strings.NewReader(string(cont)))
if err != nil {
return a, err
}
req.Header.Add(APIKeyHeader, ar.API.Config.APIKey)
res, err := ar.API.HTTPClient.Do(req)
if err != nil {
return a, err
}
content, err := ioutil.ReadAll(res.Body)
if err != nil {
return a, err
}
json.Unmarshal(content, &a)
return a, nil
}

View File

@@ -0,0 +1,33 @@
package devto
import "errors"
//Confugration errors
var (
ErrMissingRequiredParameter = errors.New("a required parameter is missing")
)
//Config contains the elements required to initialize a
// devto client.
type Config struct {
APIKey string
InsecureOnly bool
}
//NewConfig build a devto configuration instance with the
//required parameters.
//
//It takes a boolean (p) as first parameter to indicate if
//you need access to endpoints which require authentication,
//and a API key as second paramenter, if p is set to true and
//you don't provide an API key, it will return an error.
func NewConfig(p bool, k string) (c *Config, err error) {
if p == true && k == "" {
return nil, ErrMissingRequiredParameter
}
return &Config{
InsecureOnly: !p,
APIKey: k,
}, nil
}

View File

@@ -0,0 +1,81 @@
package devto
import (
"context"
"errors"
"io"
"net/http"
"net/url"
)
//Configuration constants
const (
BaseURL string = "https://dev.to"
APIVersion string = "0.5.1"
APIKeyHeader string = "api-key"
)
//devto client errors
var (
ErrMissingConfig = errors.New("missing configuration")
ErrProtectedEndpoint = errors.New("to use this resource you need to provide an authentication method")
)
type httpClient interface {
Do(req *http.Request) (res *http.Response, err error)
}
//Client is the main data structure for performing actions
//against dev.to API
type Client struct {
Context context.Context
BaseURL *url.URL
HTTPClient httpClient
Config *Config
Articles *ArticlesResource
}
//NewClient takes a context, a configuration pointer and optionally a
//base http client (bc) to build an Client instance.
func NewClient(ctx context.Context, conf *Config, bc httpClient, bu string) (dev *Client, err error) {
if bc == nil {
bc = http.DefaultClient
}
if ctx == nil {
ctx = context.Background()
}
if conf == nil {
return nil, ErrMissingConfig
}
if bu == "" {
bu = BaseURL
}
u, _ := url.Parse(bu)
c := &Client{
Context: ctx,
BaseURL: u,
HTTPClient: bc,
Config: conf,
}
c.Articles = &ArticlesResource{API: c}
return c, nil
}
//NewRequest build the request relative to the client BaseURL
func (c *Client) NewRequest(method string, uri string, body io.Reader) (*http.Request, error) {
u, err := url.Parse(uri)
if err != nil {
return nil, err
}
fu := c.BaseURL.ResolveReference(u).String()
req, err := http.NewRequest(method, fu, body)
if err != nil {
return nil, err
}
return req, nil
}

View File

@@ -0,0 +1,87 @@
package devto
import (
"net/url"
"strings"
"time"
)
//User contains information about a devto account
type User struct {
Name string `json:"name,omitempty"`
Username string `json:"username,omitempty"`
TwitterUsername string `json:"twitter_username,omitempty"`
GithubUsername string `json:"github_username,omitempty"`
WebsiteURL *WebURL `json:"website_url,omitempty"`
ProfileImage *WebURL `json:"profile_image,omitempty"`
ProfileImage90 *WebURL `json:"profile_image_90,omitempty"`
}
//Organization describes a company or group that
//publishes content to devto.
type Organization struct {
Name string `json:"name,omitempty"`
Username string `json:"username,omitempty"`
Slug string `json:"slug,omitempty"`
ProfileImage *WebURL `json:"profile_image,omitempty"`
ProfileImage90 *WebURL `json:"profile_image_90,omitempty"`
}
//Tags are a group of topics related to an article
type Tags []string
//Article contains all the information related to a single
//information resource from devto.
type Article struct {
TypeOf string `json:"type_of,omitempty"`
ID uint32 `json:"id,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
CoverImage *WebURL `json:"cover_image,omitempty"`
SocialImage *WebURL `json:"social_image,omitempty"`
PublishedAt *time.Time `json:"published_at,omitempty"`
EditedAt *time.Time `json:"edited_at,omitempty"`
CrossPostedAt *time.Time `json:"crossposted_at,omitempty"`
LastCommentAt *time.Time `json:"last_comment_at,omitempty"`
TagList Tags `json:"tag_list,omitempty"`
Tags string `json:"tags,omitempty"`
Slug string `json:"slug,omitempty"`
Path *WebURL `json:"path,omitempty"`
URL *WebURL `json:"url,omitempty"`
CanonicalURL *WebURL `json:"canonical_url,omitempty"`
CommentsCount uint `json:"comments_count,omitempty"`
PositiveReactionsCount uint `json:"positive_reactions_count,omitempty"`
PublishedTimestamp *time.Time `json:"published_timestamp,omitempty"`
User User `json:"user,omitempty"`
Organization Organization `json:"organization,omitempty"`
BodyHTML string `json:"body_html,omitempty"`
BodyMarkdown string `json:"body_markdown,omitempty"`
Published bool `json:"published,omitempty"`
}
//ArticleListOptions holds the query values to pass as
//query string parameter to the Articles List action.
type ArticleListOptions struct {
Tags string `url:"tag,omitempty"`
Username string `url:"username,omitempty"`
State string `url:"state,omitempty"`
Top string `url:"top,omitempty"`
Page int `url:"page,omitempty"`
}
type WebURL struct {
*url.URL
}
//UnmarshalJSON overrides the default unmarshal behaviour
//for URL
func (s *WebURL) UnmarshalJSON(b []byte) error {
c := string(b)
c = strings.Trim(c, "\"")
uri, err := url.Parse(c)
if err != nil {
return err
}
s.URL = uri
return nil
}