devto.*ArticlesResource.List   B
last analyzed

Complexity

Conditions 6

Size

Total Lines 24
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 7.0986

Importance

Changes 0
Metric Value
cc 6
eloc 17
nop 2
dl 0
loc 24
ccs 11
cts 16
cp 0.6875
crap 7.0986
rs 8.6166
c 0
b 0
f 0
1
package devto
2
3
import (
4
	"context"
5
	"encoding/json"
6
	"fmt"
7
	"net/http"
8
	"strings"
9
10
	"github.com/google/go-querystring/query"
11
)
12
13
// ArticlesResource implements the APIResource interface
14
// for devto articles.
15
type ArticlesResource struct {
16
	API *Client
17
}
18
19
// List will return the articles uploaded to devto, the result
20
// can be narrowed down, filtered or enhanced using query
21
// parameters as specified on the documentation.
22
// See: https://docs.dev.to/api/#tag/articles/paths/~1articles/get
23
func (ar *ArticlesResource) List(ctx context.Context, opt ArticleListOptions) ([]ListedArticle, error) {
24 1
	q, err := query.Values(opt)
25 1
	if err != nil {
26
		return nil, err
27
	}
28 1
	req, err := ar.API.NewRequest(http.MethodGet, fmt.Sprintf("api/articles?%s", q.Encode()), nil)
29 1
	if err != nil {
30
		return nil, err
31
	}
32
33 1
	res, err := ar.API.HTTPClient.Do(req)
34 1
	if err != nil {
35
		return nil, err
36
	}
37 1
	defer res.Body.Close()
38
39 1
	if nonSuccessfulResponse(res) {
40
		return nil, unmarshalErrorResponse(res)
41
	}
42 1
	var articles []ListedArticle
43 1
	if err := json.NewDecoder(res.Body).Decode(&articles); err != nil {
44
		return nil, err
45
	}
46 1
	return articles, nil
47
}
48
49
// ListForTag is a convenience method for retrieving articles
50
// for a particular tag, calling the base List method.
51
func (ar *ArticlesResource) ListForTag(ctx context.Context, tag string, page int) ([]ListedArticle, error) {
52 1
	return ar.List(ctx, ArticleListOptions{Tags: tag, Page: page})
53
}
54
55
// ListForUser is a convenience method for retrieving articles
56
// written by a particular user, calling the base List method.
57
func (ar *ArticlesResource) ListForUser(ctx context.Context, username string, page int) ([]ListedArticle, error) {
58 1
	return ar.List(ctx, ArticleListOptions{Username: username, Page: page})
59
}
60
61
// ListMyPublishedArticles lists all published articles
62
// written by the user authenticated with this client,
63
// erroring if the caller is not authenticated. Articles in
64
// the response will be listed in reverse chronological order
65
// by their publication times.
66
//
67
// If opts is nil, then no query parameters will be sent; the
68
// page number will be 1 and the page size will be 30
69
// articles.
70
func (ar *ArticlesResource) ListMyPublishedArticles(ctx context.Context, opts *MyArticlesOptions) ([]ListedArticle, error) {
71 1
	return ar.listMyArticles(ctx, "api/articles/me/published", opts)
72
}
73
74
// ListMyUnpublishedArticles lists all unpublished articles
75
// written by the user authenticated with this client,
76
// erroring if the caller is not authenticated. Articles in
77
// the response will be listed in reverse chronological order
78
// by their creation times.
79
//
80
// If opts is nil, then no query parameters will be sent; the
81
// page number will be 1 and the page size will be 30
82
// articles.
83
func (ar *ArticlesResource) ListMyUnpublishedArticles(ctx context.Context, opts *MyArticlesOptions) ([]ListedArticle, error) {
84 1
	return ar.listMyArticles(ctx, "api/articles/me/unpublished", opts)
85
}
86
87
// ListAllMyArticles lists all articles written by the user
88
// authenticated with this client, erroring if the caller is
89
// not authenticated. Articles in the response will be listed
90
// in reverse chronological order by their creation times,
91
// with unpublished articles listed before published articles.
92
//
93
// If opts is nil, then no query parameters will be sent; the
94
// page number will be 1 and the page size will be 30
95
// articles.
96
func (ar *ArticlesResource) ListAllMyArticles(ctx context.Context, opts *MyArticlesOptions) ([]ListedArticle, error) {
97 1
	return ar.listMyArticles(ctx, "api/articles/me/all", opts)
98
}
99
100
// listMyArticles serves for handling roundtrips to the
101
// /api/articles/me/* endpoints, requesting articles from the
102
// endpoint passed in, and returning a list of articles.
103
func (ar *ArticlesResource) listMyArticles(
104
	ctx context.Context,
105
	endpoint string,
106
	opts *MyArticlesOptions,
107
) ([]ListedArticle, error) {
108 1
	if ar.API.Config.InsecureOnly {
109 1
		return nil, ErrProtectedEndpoint
110
	}
111
112 1
	req, err := ar.API.NewRequest(http.MethodGet, endpoint, nil)
113 1
	if err != nil {
114
		return nil, err
115
	}
116 1
	req.Header.Add(APIKeyHeader, ar.API.Config.APIKey)
117
118 1
	if opts != nil {
119 1
		q, err := query.Values(opts)
120 1
		if err != nil {
121
			return nil, err
122
		}
123 1
		req.URL.RawQuery = q.Encode()
124
	}
125
126 1
	res, err := ar.API.HTTPClient.Do(req)
127 1
	if err != nil {
128
		return nil, err
129
	}
130 1
	defer res.Body.Close()
131
132 1
	if nonSuccessfulResponse(res) {
133
		return nil, unmarshalErrorResponse(res)
134
	}
135 1
	var articles []ListedArticle
136 1
	if err := json.NewDecoder(res.Body).Decode(&articles); err != nil {
137
		return nil, err
138
	}
139 1
	return articles, nil
140
}
141
142
// Find will retrieve an Article matching the ID passed.
143
func (ar *ArticlesResource) Find(ctx context.Context, id uint32) (Article, error) {
144 1
	req, err := ar.API.NewRequest(http.MethodGet, fmt.Sprintf("api/articles/%d", id), nil)
145 1
	if err != nil {
146
		return Article{}, err
147
	}
148
149 1
	res, err := ar.API.HTTPClient.Do(req)
150 1
	if err != nil {
151
		return Article{}, err
152
	}
153 1
	defer res.Body.Close()
154
155 1
	if nonSuccessfulResponse(res) {
156 1
		return Article{}, unmarshalErrorResponse(res)
157
	}
158 1
	var art Article
159 1
	if err := json.NewDecoder(res.Body).Decode(&art); err != nil {
160
		return Article{}, err
161
	}
162 1
	return art, nil
163
}
164
165
// New will create a new article on dev.to
166
func (ar *ArticlesResource) New(ctx context.Context, u ArticleUpdate) (Article, error) {
167 1
	if ar.API.Config.InsecureOnly {
168 1
		return Article{}, ErrProtectedEndpoint
169
	}
170 1
	cont, err := json.Marshal(&u)
171 1
	if err != nil {
172
		return Article{}, err
173
	}
174 1
	req, err := ar.API.NewRequest(http.MethodPost, "api/articles", strings.NewReader(string(cont)))
175 1
	if err != nil {
176
		return Article{}, err
177
	}
178 1
	req.Header.Add(APIKeyHeader, ar.API.Config.APIKey)
179 1
	res, err := ar.API.HTTPClient.Do(req)
180 1
	if err != nil {
181
		return Article{}, err
182
	}
183 1
	defer res.Body.Close()
184
185 1
	if nonSuccessfulResponse(res) {
186 1
		return Article{}, unmarshalErrorResponse(res)
187
	}
188
189 1
	var a Article
190 1
	if err := json.NewDecoder(res.Body).Decode(&a); err != nil {
191
		return Article{}, err
192
	}
193 1
	return a, nil
194
}
195
196
// Update will mutate the resource by id, and all the changes
197
// performed to the Article will be applied, thus validation
198
// on the API side.
199
func (ar *ArticlesResource) Update(ctx context.Context, u ArticleUpdate, id uint32) (Article, error) {
200 1
	if ar.API.Config.InsecureOnly {
201 1
		return Article{}, ErrProtectedEndpoint
202
	}
203 1
	cont, err := json.Marshal(&u)
204 1
	if err != nil {
205
		return Article{}, err
206
	}
207 1
	req, err := ar.API.NewRequest(http.MethodPut, fmt.Sprintf("api/articles/%d", id), strings.NewReader(string(cont)))
208 1
	if err != nil {
209
		return Article{}, err
210
	}
211 1
	req.Header.Add(APIKeyHeader, ar.API.Config.APIKey)
212 1
	res, err := ar.API.HTTPClient.Do(req)
213 1
	if err != nil {
214
		return Article{}, err
215
	}
216 1
	defer res.Body.Close()
217
218 1
	if nonSuccessfulResponse(res) {
219 1
		return Article{}, unmarshalErrorResponse(res)
220
	}
221
222 1
	var a Article
223 1
	if err := json.NewDecoder(res.Body).Decode(&a); err != nil {
224
		return Article{}, err
225
	}
226 1
	return a, nil
227
}
228