1 /**
2 * Copyright (c) 2013-2023, jcabi.com
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met: 1) Redistributions of source code must retain the above
8 * copyright notice, this list of conditions and the following
9 * disclaimer. 2) Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution. 3) Neither the name of the jcabi.com nor
13 * the names of its contributors may be used to endorse or promote
14 * products derived from this software without specific prior written
15 * permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 package com.jcabi.github;
31
32 import com.jcabi.aspects.Immutable;
33 import com.jcabi.aspects.Loggable;
34 import java.io.IOException;
35 import java.net.URL;
36 import java.text.ParseException;
37 import java.util.Date;
38 import javax.json.JsonObject;
39 import lombok.EqualsAndHashCode;
40 import lombok.ToString;
41
42 /**
43 * Github user.
44 * @author Yegor Bugayenko (yegor256@gmail.com)
45 * @version $Id: 48081d00dcc7539b7a9cb39a53ca95d8c9b6bbf6 $
46 * @checkstyle MultipleStringLiterals (500 lines)
47 * @see <a href="https://developer.github.com/v3/users/">User API</a>
48 * @since 0.1
49 */
50 @Immutable
51 @SuppressWarnings({"PMD.TooManyMethods", "PMD.ExcessivePublicCount",
52 "PMD.GodClass" })
53 public interface User extends JsonReadable, JsonPatchable {
54
55 /**
56 * Github we're in.
57 * @return Github
58 * @since 0.4
59 */
60 Github github();
61
62 /**
63 * Get his login.
64 * @return Login name
65 * @throws IOException If it fails
66 */
67 String login() throws IOException;
68
69 /**
70 * Get his organizations.
71 * @return UserOrganizations organizations
72 */
73 UserOrganizations organizations();
74
75 /**
76 * Get his keys.
77 * @return PublicKeys keys
78 */
79 PublicKeys keys();
80
81 /**
82 * Get user's emails.
83 * @return User's emails
84 * @since 0.8
85 */
86 UserEmails emails();
87
88 /**
89 * Notifications for this user.
90 * Wraps the call "List your notifications". See "List your notifications"
91 * at https://developer.github.com/v3/activity/notifications/
92 * @see <a href="https://developer.github.com/v3/activity/notifications/#list-your-notifications">List your notifications</a>
93 * @return Notifications for this user.
94 * @throws IOException Thrown, if an error during sending request and/or
95 * receiving response occurs.
96 */
97 Notifications notifications() throws IOException;
98
99 /**
100 * Marks notifications as read.
101 * @param lastread Describes the last point that notifications were
102 * checked.
103 * @see <a href="https://developer.github.com/v3/activity/notifications/#mark-as-read">Mark as read</a>
104 * @throws IOException Thrown, if an error during sending request and/or
105 * receiving response occurs.
106 */
107 void markAsRead(final Date lastread) throws IOException;
108
109 /**
110 * Smart user with extra features.
111 * @see <a href="https://developer.github.com/v3/users/#get-a-single-user">Get a Single User</a>
112 */
113 @Immutable
114 @ToString
115 @Loggable(Loggable.DEBUG)
116 @EqualsAndHashCode(of = { "user", "jsn" })
117 final class Smart implements User {
118 /**
119 * Encapsulated user.
120 */
121 private final transient User user;
122 /**
123 * SmartJson object for convenient JSON parsing.
124 */
125 private final transient SmartJson jsn;
126
127 /**
128 * Public ctor.
129 * @param usr User
130 */
131 public Smart(final User usr) {
132 this.user = usr;
133 this.jsn = new SmartJson(usr);
134 }
135
136 /**
137 * Does it exist in GitHub?
138 * @return TRUE if this user truly exists
139 * @throws IOException If it fails
140 * @since 0.34
141 */
142 public boolean exists() throws IOException {
143 return new Existence(this.user).check();
144 }
145
146 /**
147 * Get his ID.
148 * @return Unique user ID
149 * @throws IOException If it fails
150 * @checkstyle MethodName (3 lines)
151 */
152 @SuppressWarnings("PMD.ShortMethodName")
153 public int id() throws IOException {
154 return this.user.json().getJsonNumber("id").intValue();
155 }
156
157 /**
158 * Get his avatar URL.
159 * @return URL of the avatar
160 * @throws IOException If it fails
161 */
162 public URL avatarUrl() throws IOException {
163 return new URL(this.jsn.text("avatar_url"));
164 }
165
166 /**
167 * Get his URL.
168 * @return URL of the user
169 * @throws IOException If it fails
170 */
171 public URL url() throws IOException {
172 return new URL(this.jsn.text("url"));
173 }
174
175 /**
176 * Get his name.
177 * @return User name
178 * @throws IOException If it fails
179 */
180 public String name() throws IOException {
181 final JsonObject json = this.json();
182 if (!json.containsKey("name")) {
183 throw new IllegalStateException(
184 String.format(
185 // @checkstyle LineLength (1 line)
186 "User %s doesn't have a name specified in his/her Github account; use #hasName() first.",
187 this.login()
188 )
189 );
190 }
191 return json.getString("name");
192 }
193
194 /**
195 * Check if user has name.
196 * @return True if user has name
197 * @throws IOException If it fails
198 */
199 public boolean hasName() throws IOException {
200 return this.json().containsKey("name");
201 }
202
203 /**
204 * Get his company.
205 * @return Company name
206 * @throws IOException If it fails
207 */
208 public String company() throws IOException {
209 return this.jsn.text("company");
210 }
211
212 /**
213 * Get his location.
214 * @return Location name
215 * @throws IOException If it fails
216 */
217 public String location() throws IOException {
218 return this.jsn.text("location");
219 }
220
221 /**
222 * Get his email.
223 * @return Email
224 * @throws IOException If it fails
225 */
226 public String email() throws IOException {
227 return this.jsn.text("email");
228 }
229
230 @Override
231 public Github github() {
232 return this.user.github();
233 }
234
235 @Override
236 public String login() throws IOException {
237 return this.user.login();
238 }
239
240 @Override
241 public UserOrganizations organizations() {
242 return this.user.organizations();
243 }
244
245 @Override
246 public PublicKeys keys() {
247 return this.user.keys();
248 }
249
250 @Override
251 public UserEmails emails() {
252 return this.user.emails();
253 }
254
255 @Override
256 public Notifications notifications() throws IOException {
257 return this.user.notifications();
258 }
259
260 @Override
261 public void markAsRead(final Date lastread) throws IOException {
262 this.user.markAsRead(lastread);
263 }
264
265 @Override
266 public JsonObject json() throws IOException {
267 return this.user.json();
268 }
269
270 @Override
271 public void patch(
272 final JsonObject json
273 ) throws IOException {
274 this.user.patch(json);
275 }
276
277 /**
278 * Returns the value of html_url property of User's JSON.
279 * @return The 'html_url' property value.
280 * @throws IOException If any I/O error occurs.
281 */
282 public String htmlUrl() throws IOException {
283 return this.jsn.text("html_url");
284 }
285
286 /**
287 * Returns the value of followers_url property of User's JSON.
288 * @return The 'followers_url' property value.
289 * @throws IOException If any I/O error occurs.
290 */
291 public String followersUrl() throws IOException {
292 return this.jsn.text("followers_url");
293 }
294
295 /**
296 * Returns the value of following_url property of User's JSON.
297 * @return The 'following_url' property value.
298 * @throws IOException If any I/O error occurs.
299 */
300 public String followingUrl() throws IOException {
301 return this.jsn.text("following_url");
302 }
303
304 /**
305 * Returns the value of gists_url property of User's JSON.
306 * @return The 'gists_url' property value.
307 * @throws IOException If any I/O error occurs.
308 */
309 public String gistsUrl() throws IOException {
310 return this.jsn.text("gists_url");
311 }
312
313 /**
314 * Returns the value of starred_url property of User's JSON.
315 * @return The 'starred_url' property value.
316 * @throws IOException If any I/O error occurs.
317 */
318 public String starredUrl() throws IOException {
319 return this.jsn.text("starred_url");
320 }
321
322 /**
323 * Returns the value of subscriptions_url property of User's JSON.
324 * @return The 'subscriptions_url' property value.
325 * @throws IOException If any I/O error occurs.
326 */
327 public String subscriptionsUrl() throws IOException {
328 return this.jsn.text("subscriptions_url");
329 }
330
331 /**
332 * Returns the value of organizations_url property of User's JSON.
333 * @return The 'organizations_url' property value.
334 * @throws IOException If any I/O error occurs.
335 */
336 public String organizationsUrl() throws IOException {
337 return this.jsn.text("organizations_url");
338 }
339
340 /**
341 * Returns the value of repos_url property of User's JSON.
342 * @return The 'repos_url' property value.
343 * @throws IOException If any I/O error occurs.
344 */
345 public String reposUrl() throws IOException {
346 return this.jsn.text("repos_url");
347 }
348
349 /**
350 * Returns the value of events_url property of User's JSON.
351 * @return The 'events_url' property value.
352 * @throws IOException If any I/O error occurs.
353 */
354 public String eventsUrl() throws IOException {
355 return this.jsn.text("events_url");
356 }
357
358 /**
359 * Returns the value of received_events_url property of User's JSON.
360 * @return The 'received_events_url' property value.
361 * @throws IOException If any I/O error occurs.
362 */
363 public String receivedEventsUrl() throws IOException {
364 return this.jsn.text("received_events_url");
365 }
366
367 /**
368 * Returns the value of type property of User's JSON.
369 * @return The 'type' property value.
370 * @throws IOException If any I/O error occurs.
371 */
372 public String type() throws IOException {
373 return this.jsn.text("type");
374 }
375
376 /**
377 * Returns the value of site_admin property of User's JSON.
378 * @return The 'site_admin' property value.
379 * @throws IOException If any I/O error occurs.
380 */
381 public boolean siteAdmin() throws IOException {
382 return "true".equals(this.jsn.text("site_admin"));
383 }
384
385 /**
386 * Returns the value of blog property of User's JSON.
387 * @return The 'blog' property value.
388 * @throws IOException If any I/O error occurs.
389 */
390 public String blog() throws IOException {
391 return this.jsn.text("blog");
392 }
393
394 /**
395 * Returns the value of hireable property of User's JSON.
396 * @return The 'hireable' property value.
397 * @throws IOException If any I/O error occurs.
398 */
399 public boolean hireable() throws IOException {
400 return "true".equals(this.jsn.text("hireable"));
401 }
402
403 /**
404 * Returns the value of bio property of User's JSON.
405 * @return The 'bio' property value.
406 * @throws IOException If any I/O error occurs.
407 */
408 public String bio() throws IOException {
409 return this.jsn.text("bio");
410 }
411
412 /**
413 * Returns the value of public_repos property of User's JSON.
414 * @return The 'public_repos' property value.
415 * @throws IOException If any I/O error occurs.
416 */
417 public int publicRepos() throws IOException {
418 return Integer.parseInt(this.jsn.text("public_repos"));
419 }
420
421 /**
422 * Returns the value of public_gists property of User's JSON.
423 * @return The 'public_gists' property value.
424 * @throws IOException If any I/O error occurs.
425 */
426 public int publicGists() throws IOException {
427 return Integer.parseInt(this.jsn.text("public_gists"));
428 }
429
430 /**
431 * Returns the value of followers property of User's JSON.
432 * @return The 'followers' property value.
433 * @throws IOException If any I/O error occurs.
434 */
435 public int followersCount() throws IOException {
436 return Integer.parseInt(this.jsn.text("followers"));
437 }
438
439 /**
440 * Returns the value of following property of User's JSON.
441 * @return The 'following' property value.
442 * @throws IOException If any I/O error occurs.
443 */
444 public int followingCount() throws IOException {
445 return Integer.parseInt(this.jsn.text("following"));
446 }
447
448 /**
449 * Returns the value of created_at property of User's JSON.
450 * @return The 'created_at' property value.
451 * @throws IOException If any I/O error occurs.
452 */
453 public Github.Time created() throws IOException {
454 try {
455 return new Github.Time(this.jsn.text("created_at"));
456 } catch (final ParseException ex) {
457 throw new IllegalArgumentException(
458 "Cannot parse value of 'created_at' property",
459 ex
460 );
461 }
462 }
463
464 /**
465 * Returns the value of updated_at property of User's JSON.
466 * @return The 'updated_at' property value.
467 * @throws IOException If any I/O error occurs.
468 */
469 public Github.Time updated() throws IOException {
470 try {
471 return new Github.Time(this.jsn.text("updated_at"));
472 } catch (final ParseException ex) {
473 throw new IllegalArgumentException(
474 "Cannot parse value of 'updated_at' property",
475 ex
476 );
477 }
478 }
479 }
480 }