Coverage Report - com.jcabi.github.Issue
 
Classes in this File Line Coverage Branch Coverage Complexity
Issue
N/A
N/A
1.455
Issue$Smart
60%
40/66
20%
7/34
1.455
Issue$Smart$1
50%
4/8
N/A
1.455
Issue$Smart$AjcClosure1
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure11
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure13
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure15
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure17
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure19
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure21
0%
0/1
N/A
1.455
Issue$Smart$AjcClosure23
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure25
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure27
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure29
0%
0/1
N/A
1.455
Issue$Smart$AjcClosure3
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure31
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure33
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure35
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure37
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure39
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure41
0%
0/1
N/A
1.455
Issue$Smart$AjcClosure43
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure45
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure47
0%
0/1
N/A
1.455
Issue$Smart$AjcClosure49
0%
0/1
N/A
1.455
Issue$Smart$AjcClosure5
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure51
0%
0/1
N/A
1.455
Issue$Smart$AjcClosure53
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure55
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure57
0%
0/1
N/A
1.455
Issue$Smart$AjcClosure59
0%
0/1
N/A
1.455
Issue$Smart$AjcClosure61
0%
0/1
N/A
1.455
Issue$Smart$AjcClosure7
100%
1/1
N/A
1.455
Issue$Smart$AjcClosure9
100%
1/1
N/A
1.455
 
 1  0
 /**
 2  
  * Copyright (c) 2013-2015, 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.ArrayList;
 38  
 import java.util.Collection;
 39  
 import java.util.Date;
 40  
 import javax.json.Json;
 41  
 import javax.json.JsonArray;
 42  
 import javax.json.JsonObject;
 43  
 import javax.validation.constraints.NotNull;
 44  
 import lombok.EqualsAndHashCode;
 45  
 import lombok.ToString;
 46  
 
 47  
 /**
 48  
  * Github issue.
 49  
  *
 50  
  * <p>Use a supplementary "smart" decorator to get other properties
 51  
  * from an issue, for example:
 52  
  *
 53  
  * <pre> Issue.Smart issue = new Issue.Smart(origin);
 54  
  * if (issue.isOpen()) {
 55  
  *   issue.close();
 56  
  * }</pre>
 57  
  *
 58  
  * @author Yegor Bugayenko (yegor@tpc2.com)
 59  
  * @version $Id$
 60  
  * @since 0.1
 61  
  * @see <a href="http://developer.github.com/v3/issues/">Issues API</a>
 62  
  * @checkstyle MultipleStringLiterals (500 lines)
 63  
  */
 64  
 @Immutable
 65  
 @SuppressWarnings({ "PMD.TooManyMethods", "PMD.GodClass" })
 66  
 public interface Issue extends Comparable<Issue>, JsonReadable, JsonPatchable {
 67  
 
 68  
     /**
 69  
      * Issue state.
 70  
      */
 71  
     String OPEN_STATE = "open";
 72  
 
 73  
     /**
 74  
      * Issue state.
 75  
      */
 76  
     String CLOSED_STATE = "closed";
 77  
 
 78  
     /**
 79  
      * Repository we're in.
 80  
      * @return Repo
 81  
      */
 82  
     @NotNull(message = "repository is never NULL")
 83  
     Repo repo();
 84  
 
 85  
     /**
 86  
      * Get its number.
 87  
      * @return Issue number
 88  
      */
 89  
     int number();
 90  
 
 91  
     /**
 92  
      * Get all comments of the issue.
 93  
      * @return Comments
 94  
      * @see <a href="http://developer.github.com/v3/issues/comments/">Issue Comments API</a>
 95  
      */
 96  
     @NotNull(message = "comments are never NULL")
 97  
     Comments comments();
 98  
 
 99  
     /**
 100  
      * Get all labels of the issue.
 101  
      * @return Labels
 102  
      * @see <a href="http://developer.github.com/v3/issues/labels/">Labels API</a>
 103  
      */
 104  
     @NotNull(message = "labels are never NULL")
 105  
     IssueLabels labels();
 106  
 
 107  
     /**
 108  
      * Get all events of the issue.
 109  
      * @return Events
 110  
      * @throws IOException If there is any I/O problem
 111  
      * @see <a href="http://developer.github.com/v3/issues/events/#list-events-for-an-issue">List Events for an Issue</a>
 112  
      */
 113  
     @NotNull(message = "iterable of events is never NULL")
 114  
     Iterable<Event> events() throws IOException;
 115  
 
 116  
     /**
 117  
      * Does this issue exist in Github?
 118  
      * @return TRUE if this issue exists
 119  
      * @throws IOException If there is any I/O problem
 120  
      */
 121  
     boolean exists() throws IOException;
 122  
 
 123  
     /**
 124  
      * Smart Issue with extra features.
 125  
      */
 126  0
     @Immutable
 127  0
     @ToString
 128  
     @Loggable(Loggable.DEBUG)
 129  0
     @EqualsAndHashCode(of = { "issue", "jsn" })
 130  
     final class Smart implements Issue {
 131  
         /**
 132  
          * Encapsulated issue.
 133  
          */
 134  
         private final transient Issue issue;
 135  
         /**
 136  
          * SmartJson object for convenient JSON parsing.
 137  
          */
 138  
         private final transient SmartJson jsn;
 139  
         /**
 140  
          * Public ctor.
 141  
          * @param iss Issue
 142  
          */
 143  32
         public Smart(@NotNull(message = "iss can't be NULL") final Issue iss) {
 144  32
             this.issue = iss;
 145  32
             this.jsn = new SmartJson(iss);
 146  32
         }
 147  
         /**
 148  
          * Get its author.
 149  
          * @return Author of issue (who submitted it)
 150  
          * @throws IOException If there is any I/O problem
 151  
          */
 152  
         @NotNull(message = "user is never NULL")
 153  
         public User author() throws IOException {
 154  6
             return this.issue.repo().github().users().get(
 155  
                 this.jsn.value(
 156  
                     "user", JsonObject.class
 157  
                 ).getString("login")
 158  
             );
 159  
         }
 160  
         /**
 161  
          * Is it open?
 162  
          * @return TRUE if it's open
 163  
          * @throws IOException If there is any I/O problem
 164  
          */
 165  
         public boolean isOpen() throws IOException {
 166  32
             return Issue.OPEN_STATE.equals(this.state());
 167  
         }
 168  
         /**
 169  
          * Open it (make sure it's open).
 170  
          * @throws IOException If there is any I/O problem
 171  
          */
 172  
         public void open() throws IOException {
 173  2
             this.state(Issue.OPEN_STATE);
 174  1
         }
 175  
         /**
 176  
          * Close it (make sure it's closed).
 177  
          * @throws IOException If there is any I/O problem
 178  
          */
 179  
         public void close() throws IOException {
 180  6
             this.state(Issue.CLOSED_STATE);
 181  3
         }
 182  
         /**
 183  
          * Get its state.
 184  
          * @return State of issue
 185  
          * @throws IOException If there is any I/O problem
 186  
          */
 187  
         @NotNull(message = "state is never NULL")
 188  
         public String state() throws IOException {
 189  32
             return this.jsn.text("state");
 190  
         }
 191  
         /**
 192  
          * Change its state.
 193  
          * @param state State of issue
 194  
          * @throws IOException If there is any I/O problem
 195  
          */
 196  
         public void state(
 197  
             @NotNull(message = "state can't be NULL") final String state
 198  
         ) throws IOException {
 199  8
             this.issue.patch(
 200  
                 Json.createObjectBuilder().add("state", state).build()
 201  
             );
 202  4
         }
 203  
         /**
 204  
          * Get its title.
 205  
          * @return Title of issue
 206  
          * @throws IOException If there is any I/O problem
 207  
          */
 208  
         @NotNull(message = "title is never NULL")
 209  
         public String title() throws IOException {
 210  10
             return this.jsn.text("title");
 211  
         }
 212  
         /**
 213  
          * Change its title.
 214  
          * @param text Title of issue
 215  
          * @throws IOException If there is any I/O problem
 216  
          */
 217  
         public void title(
 218  
             @NotNull(message = "text can't be NULL") final String text
 219  
         ) throws IOException {
 220  2
             this.issue.patch(
 221  
                 Json.createObjectBuilder().add("title", text).build()
 222  
             );
 223  1
         }
 224  
         /**
 225  
          * Get its body.
 226  
          * @return Body of issue
 227  
          * @throws IOException If there is any I/O problem
 228  
          */
 229  
         @NotNull(message = "body is never NULL")
 230  
         public String body() throws IOException {
 231  4
             return this.jsn.text("body");
 232  
         }
 233  
         /**
 234  
          * Change its body.
 235  
          * @param text Body of issue
 236  
          * @throws IOException If there is any I/O problem
 237  
          */
 238  
         public void body(
 239  
             @NotNull(message = "text can't be NULL") final String text
 240  
         ) throws IOException {
 241  2
             this.issue.patch(
 242  
                 Json.createObjectBuilder().add("body", text).build()
 243  
             );
 244  1
         }
 245  
         /**
 246  
          * Has body?
 247  
          * @return TRUE if body exists
 248  
          * @throws IOException If there is any I/O problem
 249  
          * @since 0.22
 250  
          */
 251  
         public boolean hasBody() throws IOException {
 252  0
             return this.jsn.hasNotNull("body");
 253  
         }
 254  
         /**
 255  
          * Has assignee?
 256  
          * @return TRUE if assignee exists
 257  
          * @throws IOException If there is any I/O problem
 258  
          */
 259  
         public boolean hasAssignee() throws IOException {
 260  2
             return this.jsn.hasNotNull("assignee");
 261  
         }
 262  
         /**
 263  
          * Get its assignee.
 264  
          * @return User Assignee of issue
 265  
          * @throws IOException If there is any I/O problem
 266  
          */
 267  
         @NotNull(message = "user is never NULL")
 268  
         public User assignee() throws IOException {
 269  2
             if (!this.hasAssignee()) {
 270  0
                 throw new IllegalArgumentException(
 271  
                     String.format(
 272  
                         "issue #%d doesn't have an assignee, use hasAssignee()",
 273  
                         this.number()
 274  
                     )
 275  
                 );
 276  
             }
 277  1
             return this.issue.repo().github().users().get(
 278  
                 this.jsn.value(
 279  
                     "assignee", JsonObject.class
 280  
                 ).getString("login")
 281  
             );
 282  
         }
 283  
         /**
 284  
          * Assign this issue to another user.
 285  
          * @param login Login of the user to assign to
 286  
          * @throws IOException If there is any I/O problem
 287  
          */
 288  
         public void assign(
 289  
             @NotNull(message = "login can't be NULL") final String login
 290  
         ) throws IOException {
 291  2
             this.issue.patch(
 292  
                 Json.createObjectBuilder().add("assignee", login).build()
 293  
             );
 294  1
         }
 295  
         /**
 296  
          * Get its URL.
 297  
          * @return URL of issue
 298  
          * @throws IOException If there is any I/O problem
 299  
          */
 300  
         @NotNull(message = "URL is never NULL")
 301  
         public URL url() throws IOException {
 302  0
             return new URL(this.jsn.text("url"));
 303  
         }
 304  
         /**
 305  
          * Get its HTML URL.
 306  
          * @return URL of issue
 307  
          * @throws IOException If there is any I/O problem
 308  
          */
 309  
         @NotNull(message = "URL is never NULL")
 310  
         public URL htmlUrl() throws IOException {
 311  2
             return new URL(this.jsn.text("html_url"));
 312  
         }
 313  
         /**
 314  
          * When this issue was created.
 315  
          * @return Date of creation
 316  
          * @throws IOException If there is any I/O problem
 317  
          */
 318  
         @NotNull(message = "date is never NULL")
 319  
         public Date createdAt() throws IOException {
 320  
             try {
 321  2
                 return new Github.Time(
 322  
                     this.jsn.text("created_at")
 323  
                 ).date();
 324  0
             } catch (final ParseException ex) {
 325  0
                 throw new IllegalStateException(ex);
 326  
             }
 327  
         }
 328  
         /**
 329  
          * When this issue was updated.
 330  
          * @return Date of update
 331  
          * @throws IOException If there is any I/O problem
 332  
          */
 333  
         @NotNull(message = "date is never NULL")
 334  
         public Date updatedAt() throws IOException {
 335  
             try {
 336  2
                 return new Github.Time(
 337  
                     this.jsn.text("updated_at")
 338  
                 ).date();
 339  0
             } catch (final ParseException ex) {
 340  0
                 throw new IllegalStateException(ex);
 341  
             }
 342  
         }
 343  
         /**
 344  
          * Is it a pull request?
 345  
          * @return TRUE if it is a pull request
 346  
          * @throws IOException If there is any I/O problem
 347  
          */
 348  
         public boolean isPull() throws IOException {
 349  8
             return this.json().containsKey("pull_request")
 350  
                 && !this.jsn.value("pull_request", JsonObject.class)
 351  
                     .isNull("html_url");
 352  
         }
 353  
 
 354  
         /**
 355  
          * Get pull request.
 356  
          * @return Pull request
 357  
          * @throws IOException If there is any I/O problem
 358  
          */
 359  
         @NotNull(message = "pull is never NULL")
 360  
         public Pull pull() throws IOException {
 361  2
             final String url = this.jsn.value(
 362  
                 "pull_request", JsonObject.class
 363  
             ).getString("html_url");
 364  1
             return this.issue.repo().pulls().get(
 365  
                 Integer.parseInt(url.substring(url.lastIndexOf('/') + 1))
 366  
             );
 367  
         }
 368  
         /**
 369  
          * Get the latest event of a given type.
 370  
          * Throws {@link IllegalStateException} if the issue has no events of
 371  
          * the given type.
 372  
          * @param type Type of event
 373  
          * @return Latest event of the given type
 374  
          * @throws IOException If there is any I/O problem
 375  
          */
 376  
         @NotNull(message = "event is never NULL")
 377  
         public Event latestEvent(
 378  
             @NotNull(message = "type can't be NULL") final String type
 379  
         ) throws IOException {
 380  0
             final Iterable<Event.Smart> events = new Smarts<Event.Smart>(
 381  
                 this.issue.events()
 382  
             );
 383  0
             Event found = null;
 384  0
             for (final Event.Smart event : events) {
 385  0
                 if (event.type().equals(type) && (found == null
 386  
                     || found.number() < event.number())) {
 387  0
                     found = event;
 388  
                 }
 389  0
             }
 390  0
             if (found == null) {
 391  0
                 throw new IllegalStateException(
 392  
                     String.format(
 393  
                         "event of type '%s' not found in issue #%d",
 394  
                         type, this.issue.number()
 395  
                     )
 396  
                 );
 397  
             }
 398  0
             return found;
 399  
         }
 400  
         /**
 401  
          * Get read-only labels.
 402  
          * @return Collection of labels
 403  
          * @throws IOException If there is any I/O problem
 404  
          * @since 0.6.2
 405  
          */
 406  
         @NotNull(message = "collection is never NULL")
 407  
         public IssueLabels roLabels() throws IOException {
 408  4
             final Collection<JsonObject> array =
 409  
                 this.jsn.value("labels", JsonArray.class)
 410  
                     .getValuesAs(JsonObject.class);
 411  2
             final Collection<Label> labels = new ArrayList<Label>(array.size());
 412  2
             for (final JsonObject obj : array) {
 413  2
                 labels.add(
 414  
                     new Label.Unmodified(
 415  
                         Issue.Smart.this.repo(),
 416  
                         obj.toString()
 417  
                     )
 418  
                 );
 419  2
             }
 420  
             // @checkstyle AnonInnerLength (1 line)
 421  2
             return new IssueLabels() {
 422  
                 @Override
 423  
                 @NotNull(message = "issue is never NULL")
 424  
                 public Issue issue() {
 425  0
                     return Issue.Smart.this;
 426  
                 }
 427  
                 @Override
 428  
                 public void add(
 429  
                     @NotNull(message = "iterable of label names can't be NULL")
 430  
                     final Iterable<String> labels) throws IOException {
 431  1
                     throw new UnsupportedOperationException(
 432  
                         "The issue is read-only."
 433  
                     );
 434  
                 }
 435  
                 @Override
 436  
                 public void replace(
 437  
                     @NotNull(message = "iterable of label names can't be NULL")
 438  
                     final Iterable<String> labels) throws IOException {
 439  0
                     throw new UnsupportedOperationException(
 440  
                         "The issue is read-only."
 441  
                     );
 442  
                 }
 443  
                 @Override
 444  
                 @NotNull(message = "Iterable of labels is never NULL")
 445  
                 public Iterable<Label> iterate() {
 446  1
                     return labels;
 447  
                 }
 448  
                 @Override
 449  
                 public void remove(
 450  
                     @NotNull(message = "label name can't be NULL")
 451  
                     final String name) throws IOException {
 452  0
                     throw new UnsupportedOperationException(
 453  
                         "This issue is read-only."
 454  
                     );
 455  
                 }
 456  
                 @Override
 457  
                 public void clear() throws IOException {
 458  0
                     throw new UnsupportedOperationException(
 459  
                         "This issue is read-only."
 460  
                     );
 461  
                 }
 462  
             };
 463  
         }
 464  
         @Override
 465  
         @NotNull(message = "repository is never NULL")
 466  
         public Repo repo() {
 467  4
             return this.issue.repo();
 468  
         }
 469  
         @Override
 470  
         public int number() {
 471  0
             return this.issue.number();
 472  
         }
 473  
         @Override
 474  
         @NotNull(message = "comments is never NULL")
 475  
         public Comments comments() {
 476  0
             return this.issue.comments();
 477  
         }
 478  
         @Override
 479  
         @NotNull(message = "labels is never NULL")
 480  
         public IssueLabels labels() {
 481  0
             return this.issue.labels();
 482  
         }
 483  
         @Override
 484  
         @NotNull(message = "Iterable of events is never NULL")
 485  
         public Iterable<Event> events() throws IOException {
 486  8
             return this.issue.events();
 487  
         }
 488  
         @Override
 489  
         @NotNull(message = "JSON is never NULL")
 490  
         public JsonObject json() throws IOException {
 491  8
             return this.issue.json();
 492  
         }
 493  
         @Override
 494  
         public void patch(
 495  
             @NotNull(message = "json can't be NULL") final JsonObject json
 496  
         ) throws IOException {
 497  0
             this.issue.patch(json);
 498  0
         }
 499  
         @Override
 500  
         public int compareTo(
 501  
             @NotNull(message = "obj can't be NULL") final Issue obj
 502  
         ) {
 503  0
             return this.issue.compareTo(obj);
 504  
         }
 505  
         @Override
 506  
         public boolean exists() throws IOException {
 507  0
             return this.issue.exists();
 508  
         }
 509  
     }
 510  
 
 511  
 }