Passed
Push — master ( a2c608...ad6bf9 )
by Victor Hugo
59s
created

devto.*ErrorResponse.Error   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 0
dl 0
loc 2
ccs 0
cts 1
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
package devto
2
3
import (
4
	"encoding/json"
5
	"fmt"
6
	"net/url"
7
	"strings"
8
	"time"
9
)
10
11
// User contains information about a devto account
12
type User struct {
13
	Name            string  `json:"name,omitempty"`
14
	Username        string  `json:"username,omitempty"`
15
	TwitterUsername string  `json:"twitter_username,omitempty"`
16
	GithubUsername  string  `json:"github_username,omitempty"`
17
	WebsiteURL      *WebURL `json:"website_url,omitempty"`
18
	ProfileImage    *WebURL `json:"profile_image,omitempty"`
19
	ProfileImage90  *WebURL `json:"profile_image_90,omitempty"`
20
}
21
22
// Organization describes a company or group that
23
// publishes content to devto.
24
type Organization struct {
25
	Name           string  `json:"name,omitempty"`
26
	Username       string  `json:"username,omitempty"`
27
	Slug           string  `json:"slug,omitempty"`
28
	ProfileImage   *WebURL `json:"profile_image,omitempty"`
29
	ProfileImage90 *WebURL `json:"profile_image_90,omitempty"`
30
}
31
32
// FlareTag represents an article's flare tag, if the article
33
// has one.
34
type FlareTag struct {
35
	Name         string `json:"name"`
36
	BGColorHex   string `json:"bg_color_hex"`
37
	TextColorHex string `json:"text_color_hex"`
38
}
39
40
// Tags are a group of topics related to an article
41
type Tags []string
42
43
// This deserialization logic is so that if a listed article
44
// originates from the /articles endpoint instead of
45
// /articles/me/*, its Published field is returned as true,
46
// since /articles exclusively returns articles that have been
47
// published.
48
type listedArticleJSON struct {
49
	TypeOf                 string        `json:"type_of,omitempty"`
50
	ID                     uint32        `json:"id,omitempty"`
51
	Title                  string        `json:"title,omitempty"`
52
	Description            string        `json:"description,omitempty"`
53
	CoverImage             *WebURL       `json:"cover_image,omitempty"`
54
	PublishedAt            *time.Time    `json:"published_at,omitempty"`
55
	PublishedTimestamp     string        `json:"published_timestamp,omitempty"`
56
	TagList                Tags          `json:"tag_list,omitempty"`
57
	Slug                   string        `json:"slug,omitempty"`
58
	Path                   string        `json:"path,omitempty"`
59
	URL                    *WebURL       `json:"url,omitempty"`
60
	CanonicalURL           *WebURL       `json:"canonical_url,omitempty"`
61
	CommentsCount          uint          `json:"comments_count,omitempty"`
62
	PositiveReactionsCount uint          `json:"positive_reactions_count,omitempty"`
63
	User                   User          `json:"user,omitempty"`
64
	Organization           *Organization `json:"organization,omitempty"`
65
	FlareTag               *FlareTag     `json:"flare_tag,omitempty"`
66
	BodyMarkdown           string        `json:"body_markdown,omitempty"`
67
	Published              *bool         `json:"published,omitempty"`
68
}
69
70
func (j *listedArticleJSON) listedArticle() ListedArticle {
71 1
	a := ListedArticle{
72
		TypeOf:                 j.TypeOf,
73
		ID:                     j.ID,
74
		Title:                  j.Title,
75
		Description:            j.Description,
76
		CoverImage:             j.CoverImage,
77
		PublishedAt:            j.PublishedAt,
78
		PublishedTimestamp:     j.PublishedTimestamp,
79
		TagList:                j.TagList,
80
		Slug:                   j.Slug,
81
		Path:                   j.Path,
82
		URL:                    j.URL,
83
		CanonicalURL:           j.CanonicalURL,
84
		CommentsCount:          j.CommentsCount,
85
		PositiveReactionsCount: j.PositiveReactionsCount,
86
		User:                   j.User,
87
		Organization:           j.Organization,
88
		FlareTag:               j.FlareTag,
89
		BodyMarkdown:           j.BodyMarkdown,
90
	}
91
92 1
	if j.Published != nil {
93 1
		a.Published = *j.Published
94
	} else {
95
		// "published" currently is included in the API
96
		// response for dev.to's /articles/me/* endpoints,
97
		// but not in /articles, so we are setting this
98
		// to true since /articles only returns articles
99
		// that are published.
100 1
		a.Published = true
101
	}
102 1
	return a
103
}
104
105
// ListedArticle represents an article returned from one of
106
// the list articles endpoints (/articles, /articles/me/*).
107
type ListedArticle struct {
108
	TypeOf                 string        `json:"type_of,omitempty"`
109
	ID                     uint32        `json:"id,omitempty"`
110
	Title                  string        `json:"title,omitempty"`
111
	Description            string        `json:"description,omitempty"`
112
	CoverImage             *WebURL       `json:"cover_image,omitempty"`
113
	PublishedAt            *time.Time    `json:"published_at,omitempty"`
114
	PublishedTimestamp     string        `json:"published_timestamp,omitempty"`
115
	TagList                Tags          `json:"tag_list,omitempty"`
116
	Slug                   string        `json:"slug,omitempty"`
117
	Path                   string        `json:"path,omitempty"`
118
	URL                    *WebURL       `json:"url,omitempty"`
119
	CanonicalURL           *WebURL       `json:"canonical_url,omitempty"`
120
	CommentsCount          uint          `json:"comments_count,omitempty"`
121
	PositiveReactionsCount uint          `json:"positive_reactions_count,omitempty"`
122
	User                   User          `json:"user,omitempty"`
123
	Organization           *Organization `json:"organization,omitempty"`
124
	FlareTag               *FlareTag     `json:"flare_tag,omitempty"`
125
	// Only present in "/articles/me/*" endpoints
126
	BodyMarkdown string `json:"body_markdown,omitempty"`
127
	Published    bool   `json:"published,omitempty"`
128
}
129
130
// UnmarshalJSON implements the JSON Unmarshaler interface.
131
func (a *ListedArticle) UnmarshalJSON(b []byte) error {
132 1
	var j listedArticleJSON
133 1
	if err := json.Unmarshal(b, &j); err != nil {
134
		return err
135
	}
136
137 1
	*a = j.listedArticle()
138 1
	return nil
139
}
140
141
// Article contains all the information related to a single
142
// information resource from devto.
143
type Article struct {
144
	TypeOf                 string     `json:"type_of,omitempty"`
145
	ID                     uint32     `json:"id,omitempty"`
146
	Title                  string     `json:"title,omitempty"`
147
	Description            string     `json:"description,omitempty"`
148
	CoverImage             *WebURL    `json:"cover_image,omitempty"`
149
	SocialImage            *WebURL    `json:"social_image,omitempty"`
150
	ReadablePublishDate    string     `json:"readable_publish_date"`
151
	Published              bool       `json:"published,omitempty"`
152
	PublishedAt            *time.Time `json:"published_at,omitempty"`
153
	CreatedAt              *time.Time `json:"created_at,omitempty"`
154
	EditedAt               *time.Time `json:"edited_at,omitempty"`
155
	CrossPostedAt          *time.Time `json:"crossposted_at,omitempty"`
156
	LastCommentAt          *time.Time `json:"last_comment_at,omitempty"`
157
	TagList                string     `json:"tag_list,omitempty"`
158
	Tags                   Tags       `json:"tags,omitempty"`
159
	Slug                   string     `json:"slug,omitempty"`
160
	Path                   *WebURL    `json:"path,omitempty"`
161
	URL                    *WebURL    `json:"url,omitempty"`
162
	CanonicalURL           *WebURL    `json:"canonical_url,omitempty"`
163
	CommentsCount          uint       `json:"comments_count,omitempty"`
164
	PositiveReactionsCount uint       `json:"positive_reactions_count,omitempty"`
165
	User                   User       `json:"user,omitempty"`
166
	BodyHTML               string     `json:"body_html,omitempty"`
167
	BodyMarkdown           string     `json:"body_markdown,omitempty"`
168
}
169
170
// ArticleUpdate represents an update to an article; it is
171
// used as the payload in POST and PUT requests for writing
172
// articles.
173
type ArticleUpdate struct {
174
	Title          string   `json:"title"`
175
	BodyMarkdown   string   `json:"body_markdown"`
176
	Published      bool     `json:"published"`
177
	Series         *string  `json:"series"`
178
	MainImage      string   `json:"main_image,omitempty"`
179
	CanonicalURL   string   `json:"canonical_url,omitempty"`
180
	Description    string   `json:"description,omitempty"`
181
	Tags           []string `json:"tags,omitempty"`
182
	OrganizationID int32    `json:"organization_id,omitempty"`
183
}
184
185
// ArticleListOptions holds the query values to pass as
186
// query string parameter to the Articles List action.
187
type ArticleListOptions struct {
188
	Tags     string `url:"tag,omitempty"`
189
	Username string `url:"username,omitempty"`
190
	State    string `url:"state,omitempty"`
191
	Top      string `url:"top,omitempty"`
192
	Page     int    `url:"page,omitempty"`
193
}
194
195
// MyArticlesOptions defines pagination options used as query
196
// params in the dev.to "list my articles" endpoints.
197
type MyArticlesOptions struct {
198
	Page    int `url:"page,omitempty"`
199
	PerPage int `url:"per_page,omitempty"`
200
}
201
202
// WebURL is a class embed to override default unmarshal
203
// behavior.
204
type WebURL struct {
205
	*url.URL
206
}
207
208
// UnmarshalJSON overrides the default unmarshal behaviour
209
// for URL
210
func (s *WebURL) UnmarshalJSON(b []byte) error {
211 1
	c := string(b)
212 1
	c = strings.Trim(c, "\"")
213 1
	uri, err := url.Parse(c)
214 1
	if err != nil {
215 1
		return err
216
	}
217 1
	s.URL = uri
218 1
	return nil
219
}
220
221
// ErrorResponse is an error returned from a dev.to API
222
// endpoint.
223
type ErrorResponse struct {
224
	ErrorMessage string `json:"error"`
225
	Status       int    `json:"status"`
226
}
227
228
func (e *ErrorResponse) Error() string {
229
	return fmt.Sprintf(`%d error: "%s"`, e.Status, e.ErrorMessage)
230
}
231