001package com.box.sdk;
002
003import com.box.sdk.internal.utils.CollectionUtils;
004import com.box.sdk.internal.utils.CollectionUtils.Mapper;
005import com.eclipsesource.json.Json;
006import com.eclipsesource.json.JsonArray;
007import com.eclipsesource.json.JsonObject;
008import com.eclipsesource.json.JsonValue;
009import java.net.MalformedURLException;
010import java.net.URL;
011import java.text.ParseException;
012import java.util.Arrays;
013import java.util.Collection;
014import java.util.Collections;
015import java.util.Date;
016import java.util.HashSet;
017import java.util.Set;
018
019/**
020 * Box WebHook resource.
021 *
022 * @since 2.2.1
023 */
024@BoxResourceType("webhook")
025public class BoxWebHook extends BoxResource {
026
027  /** {@link URLTemplate} for {@link BoxWebHook}s resource. */
028  public static final URLTemplate WEBHOOKS_URL_TEMPLATE = new URLTemplate("webhooks");
029  /** {@link URLTemplate} for single {@link BoxWebHook} resource. */
030  public static final URLTemplate WEBHOOK_URL_TEMPLATE = new URLTemplate("webhooks/%s");
031
032  /** JSON Key for {@link BoxWebHook} {@link #getID()}. */
033  private static final String JSON_KEY_ID = "id";
034
035  /** JSON Key for {@link BoxWebHook.Info#getTarget()}. */
036  private static final String JSON_KEY_TARGET = "target";
037
038  /** JSON Key for {@link BoxWebHook.Target#getType()}. */
039  private static final String JSON_KEY_TARGET_TYPE = "type";
040
041  /** JSON Key for {@link BoxWebHook.Target#getId()}. */
042  private static final String JSON_KEY_TARGET_ID = "id";
043
044  /** JSON Key for {@link BoxWebHook.Info#getAddress()}. */
045  private static final String JSON_KEY_ADDRESS = "address";
046
047  /** JSON Key for {@link BoxWebHook.Info#getTriggers()}. */
048  private static final String JSON_KEY_TRIGGERS = "triggers";
049
050  /** JSON Key for {@link BoxWebHook.Info#getCreatedBy()}. */
051  private static final String JSON_KEY_CREATED_BY = "created_by";
052
053  /** JSON Key for {@link BoxWebHook.Info#getCreatedAt()}. */
054  private static final String JSON_KEY_CREATED_AT = "created_at";
055
056  /** Maps a {@link Trigger} to its {@link Trigger#getValue()}. */
057  private static final Mapper<String, BoxWebHook.Trigger> TRIGGER_TO_VALUE = Trigger::getValue;
058
059  private static final Mapper<Trigger, JsonValue> JSON_VALUE_TO_TRIGGER =
060      value -> Trigger.fromValue(value.asString());
061
062  /**
063   * Constructor.
064   *
065   * @param api {@link #getAPI()}
066   * @param id {@link #getID()}
067   */
068  public BoxWebHook(BoxAPIConnection api, String id) {
069    super(api, id);
070  }
071
072  /**
073   * Adds a {@link BoxWebHook} to a provided {@link BoxResource}.
074   *
075   * @param target {@link BoxResource} web resource
076   * @param address {@link URL} where the notification should send to
077   * @param triggers events this {@link BoxWebHook} is interested in
078   * @return created {@link BoxWebHook}
079   * @see #create(BoxResource, URL, Set)
080   */
081  public static BoxWebHook.Info create(
082      BoxResource target, URL address, BoxWebHook.Trigger... triggers) {
083    return create(target, address, new HashSet<>(Arrays.asList(triggers)));
084  }
085
086  /**
087   * Adds a {@link BoxWebHook} to a provided {@link BoxResource}.
088   *
089   * @param target {@link BoxResource} web resource
090   * @param address {@link URL} where the notification should send to
091   * @param triggers events this {@link BoxWebHook} is interested in
092   * @return created {@link BoxWebHook}
093   * @see #create(BoxResource, URL, Trigger...)
094   */
095  public static BoxWebHook.Info create(
096      BoxResource target, URL address, Set<BoxWebHook.Trigger> triggers) {
097    BoxAPIConnection api = target.getAPI();
098
099    String type = BoxResource.getResourceType(target.getClass());
100    validateTriggers(type, triggers);
101
102    JsonObject targetJSON =
103        new JsonObject().add(JSON_KEY_TARGET_TYPE, type).add(JSON_KEY_TARGET_ID, target.getID());
104
105    JsonObject requestJSON =
106        new JsonObject()
107            .add(JSON_KEY_TARGET, targetJSON)
108            .add(JSON_KEY_ADDRESS, address.toExternalForm())
109            .add(JSON_KEY_TRIGGERS, toJsonArray(CollectionUtils.map(triggers, TRIGGER_TO_VALUE)));
110
111    URL url = WEBHOOKS_URL_TEMPLATE.build(api.getBaseURL());
112    BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
113    request.setBody(requestJSON.toString());
114
115    try (BoxJSONResponse response = request.send()) {
116      JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
117
118      BoxWebHook webHook = new BoxWebHook(api, responseJSON.get(JSON_KEY_ID).asString());
119      return webHook.new Info(responseJSON);
120    }
121  }
122
123  /**
124   * Helper function to create JsonArray from collection.
125   *
126   * @param values collection of values to convert to JsonArray
127   * @return JsonArray with collection values
128   */
129  private static JsonArray toJsonArray(Collection<String> values) {
130    JsonArray array = new JsonArray();
131    for (String value : values) {
132      array.add(value);
133    }
134    return array;
135  }
136
137  /**
138   * Returns iterator over all {@link BoxWebHook}-s.
139   *
140   * @param api the API connection to be used by the resource
141   * @return existing {@link BoxWebHook.Info}-s
142   */
143  public static Iterable<BoxWebHook.Info> all(final BoxAPIConnection api) {
144    return new BoxResourceIterable<BoxWebHook.Info>(
145        api, WEBHOOKS_URL_TEMPLATE.build(api.getBaseURL()), 64) {
146
147      @Override
148      protected BoxWebHook.Info factory(JsonObject jsonObject) {
149        BoxWebHook webHook = new BoxWebHook(api, jsonObject.get("id").asString());
150        return webHook.new Info(jsonObject);
151      }
152    };
153  }
154
155  /**
156   * Returns iterator over all {@link BoxWebHook}-s.
157   *
158   * @param api the API connection to be used by the resource
159   * @param fields the fields to retrieve.
160   * @return existing {@link BoxWebHook.Info}-s
161   */
162  public static Iterable<BoxWebHook.Info> all(final BoxAPIConnection api, String... fields) {
163    QueryStringBuilder builder = new QueryStringBuilder();
164    if (fields.length > 0) {
165      builder.appendParam("fields", fields);
166    }
167    return new BoxResourceIterable<BoxWebHook.Info>(
168        api, WEBHOOKS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString()), 64) {
169
170      @Override
171      protected BoxWebHook.Info factory(JsonObject jsonObject) {
172        BoxWebHook webHook = new BoxWebHook(api, jsonObject.get("id").asString());
173        return webHook.new Info(jsonObject);
174      }
175    };
176  }
177
178  /**
179   * Validates that provided {@link BoxWebHook.Trigger}-s can be applied on the provided {@link
180   * BoxResourceType}.
181   *
182   * @param targetType on which target the triggers should be applied to
183   * @param triggers for check
184   * @see #validateTrigger(String, Trigger)
185   */
186  public static void validateTriggers(String targetType, Collection<BoxWebHook.Trigger> triggers) {
187    for (BoxWebHook.Trigger trigger : triggers) {
188      validateTrigger(targetType, trigger);
189    }
190  }
191
192  /**
193   * Validates that provided {@link BoxWebHook.Trigger} can be applied on the provided {@link
194   * BoxResourceType}.
195   *
196   * @param targetType on which targets the trigger should be applied to
197   * @param trigger for check
198   * @see #validateTriggers(String, Collection)
199   */
200  private static void validateTrigger(String targetType, BoxWebHook.Trigger trigger) {
201    for (String type : trigger.getTypes()) {
202      if (targetType.equals(type)) {
203        return;
204      }
205    }
206    throw new IllegalArgumentException(
207        String.format(
208            "Provided trigger '%s' is not supported on provided target '%s'.",
209            trigger.name(), targetType));
210  }
211
212  /**
213   * @param fields the fields to retrieve.
214   * @return Gets information about this {@link BoxWebHook}.
215   */
216  public BoxWebHook.Info getInfo(String... fields) {
217    URL url = WEBHOOK_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
218    if (fields.length > 0) {
219      QueryStringBuilder builder = new QueryStringBuilder().appendParam("fields", fields);
220      url =
221          WEBHOOK_URL_TEMPLATE.buildWithQuery(
222              this.getAPI().getBaseURL(), builder.toString(), this.getID());
223    }
224    BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET");
225    try (BoxJSONResponse response = request.send()) {
226      return new Info(Json.parse(response.getJSON()).asObject());
227    }
228  }
229
230  /**
231   * Updates {@link BoxWebHook} information.
232   *
233   * @param info new state
234   */
235  public void updateInfo(BoxWebHook.Info info) {
236    URL url = WEBHOOK_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
237    BoxJSONRequest request = new BoxJSONRequest(getAPI(), url, "PUT");
238    request.setBody(info.getPendingChanges());
239
240    try (BoxJSONResponse response = request.send()) {
241      JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
242      info.update(jsonObject);
243    }
244  }
245
246  /** Deletes this webhook. */
247  public void delete() {
248    URL url = WEBHOOK_URL_TEMPLATE.build(getAPI().getBaseURL(), this.getID());
249    BoxAPIRequest request = new BoxAPIRequest(getAPI(), url, "DELETE");
250    request.send().close();
251  }
252
253  /** A Box related triggers. */
254  public enum Trigger {
255
256    // BoxFolder related triggers.
257
258    /** Triggered when a {@link BoxFolder} gets created. */
259    FOLDER_CREATED("FOLDER.CREATED", BoxResource.getResourceType(BoxFolder.class)),
260
261    /** Triggered when a {@link BoxFolder} gets copied. */
262    FOLDER_COPIED("FOLDER.COPIED", BoxResource.getResourceType(BoxFolder.class)),
263
264    /** Triggered when a {@link BoxFolder} gets moved. */
265    FOLDER_MOVED("FOLDER.MOVED", BoxResource.getResourceType(BoxFolder.class)),
266
267    /** Triggered when a {@link BoxFolder} is downloaded. */
268    FOLDER_DOWNLOADED("FOLDER.DOWNLOADED", BoxResource.getResourceType(BoxFolder.class)),
269
270    /** Triggered when a {@link BoxFolder} is trashed. */
271    FOLDER_TRASHED("FOLDER.TRASHED", BoxResource.getResourceType(BoxFolder.class)),
272
273    /** Triggered when a {@link BoxFolder} gets restored. */
274    FOLDER_RESTORED("FOLDER.RESTORED", BoxResource.getResourceType(BoxFolder.class)),
275
276    /** Triggered when a {@link BoxFolder} gets deleted. */
277    FOLDER_DELETED("FOLDER.DELETED", BoxResource.getResourceType(BoxFolder.class)),
278
279    /** Triggered when a {@link BoxFolder} is renamed. */
280    FOLDER_RENAMED("FOLDER.RENAMED", BoxResource.getResourceType(BoxFolder.class)),
281
282    // BoxFile related triggers.
283
284    /** Triggered when a {@link BoxFile} gets uploaded. */
285    FILE_UPLOADED("FILE.UPLOADED", BoxResource.getResourceType(BoxFolder.class)),
286
287    /** Triggered when a {@link BoxFile} gets copied. */
288    FILE_COPIED(
289        "FILE.COPIED",
290        BoxResource.getResourceType(BoxFolder.class),
291        BoxResource.getResourceType(BoxFile.class)),
292
293    /** Triggered when a {@link BoxFile} gets copied. */
294    FILE_MOVED(
295        "FILE.MOVED",
296        BoxResource.getResourceType(BoxFolder.class),
297        BoxResource.getResourceType(BoxFile.class)),
298
299    /** Triggered when a {@link BoxFile} is previewed. */
300    FILE_PREVIEWED(
301        "FILE.PREVIEWED",
302        BoxResource.getResourceType(BoxFolder.class),
303        BoxResource.getResourceType(BoxFile.class)),
304
305    /** Triggered when a {@link BoxFile} is downloaded. */
306    FILE_DOWNLOADED(
307        "FILE.DOWNLOADED",
308        BoxResource.getResourceType(BoxFolder.class),
309        BoxResource.getResourceType(BoxFile.class)),
310
311    /** Triggered when a {@link BoxFile} gets locked. */
312    FILE_LOCKED(
313        "FILE.LOCKED",
314        BoxResource.getResourceType(BoxFolder.class),
315        BoxResource.getResourceType(BoxFile.class)),
316
317    /** Triggered when a {@link BoxFile} gets unlocked. */
318    FILE_UNLOCKED(
319        "FILE.UNLOCKED",
320        BoxResource.getResourceType(BoxFolder.class),
321        BoxResource.getResourceType(BoxFile.class)),
322
323    /** Triggered when a {@link BoxFile} is trashed. Do not include file versions for now. */
324    FILE_TRASHED(
325        "FILE.TRASHED",
326        BoxResource.getResourceType(BoxFolder.class),
327        BoxResource.getResourceType(BoxFile.class)),
328
329    /** Triggered when a {@link BoxFile} gets restored. */
330    FILE_RESTORED(
331        "FILE.RESTORED",
332        BoxResource.getResourceType(BoxFolder.class),
333        BoxResource.getResourceType(BoxFile.class)),
334
335    /** Triggered when a {@link BoxFile} is permanently deleted. */
336    FILE_DELETED(
337        "FILE.DELETED",
338        BoxResource.getResourceType(BoxFolder.class),
339        BoxResource.getResourceType(BoxFile.class)),
340
341    /** Triggered when a {@link BoxFile} is renamed. */
342    FILE_RENAMED(
343        "FILE.RENAMED",
344        BoxResource.getResourceType(BoxFolder.class),
345        BoxResource.getResourceType(BoxFile.class)),
346
347    /** Triggered when a {@link BoxComment} was created. */
348    COMMENT_CREATED(
349        "COMMENT.CREATED",
350        BoxResource.getResourceType(BoxFolder.class),
351        BoxResource.getResourceType(BoxFile.class)),
352
353    /** Triggered when a {@link BoxComment} was updated. */
354    COMMENT_UPDATED(
355        "COMMENT.UPDATED",
356        BoxResource.getResourceType(BoxFolder.class),
357        BoxResource.getResourceType(BoxFile.class)),
358
359    /** Triggered when a {@link BoxComment} was deleted. */
360    COMMENT_DELETED(
361        "COMMENT.DELETED",
362        BoxResource.getResourceType(BoxFolder.class),
363        BoxResource.getResourceType(BoxFile.class)),
364
365    /** Triggered when a {@link BoxTaskAssignment} is created. */
366    TASK_ASSIGNMENT_CREATED(
367        "TASK_ASSIGNMENT.CREATED",
368        BoxResource.getResourceType(BoxFolder.class),
369        BoxResource.getResourceType(BoxFile.class)),
370
371    /** Triggered when a {@link BoxTaskAssignment} is updated. */
372    TASK_ASSIGNMENT_UPDATED(
373        "TASK_ASSIGNMENT.UPDATED",
374        BoxResource.getResourceType(BoxFolder.class),
375        BoxResource.getResourceType(BoxFile.class)),
376
377    /**
378     * Triggered when a metadata template is associated to a {@link BoxFile} or {@link BoxFolder}.
379     */
380    METADATA_INSTANCE_CREATED(
381        "METADATA_INSTANCE.CREATED",
382        BoxResource.getResourceType(BoxFolder.class),
383        BoxResource.getResourceType(BoxFile.class)),
384
385    /**
386     * Triggered when a field is updated in the metadata on a {@link BoxFile} or {@link BoxFolder}.
387     */
388    METADATA_INSTANCE_UPDATED(
389        "METADATA_INSTANCE.UPDATED",
390        BoxResource.getResourceType(BoxFolder.class),
391        BoxResource.getResourceType(BoxFile.class)),
392
393    /**
394     * Triggered when a metadata template is removed from a {@link BoxFile} or {@link BoxFolder}.
395     */
396    METADATA_INSTANCE_DELETED(
397        "METADATA_INSTANCE.DELETED",
398        BoxResource.getResourceType(BoxFolder.class),
399        BoxResource.getResourceType(BoxFile.class)),
400
401    /** Triggered when a {@link BoxWebHook} is deleted. */
402    WEBHOOK_DELETED("WEBHOOK.DELETED"),
403
404    /** Triggered when a {@link BoxCollaboration} is created. */
405    COLLABORATION_CREATED("COLLABORATION.CREATED", BoxResource.getResourceType(BoxFolder.class)),
406
407    /** Triggered when a {@link BoxCollaboration} is accepted. */
408    COLLABORATION_ACCEPTED("COLLABORATION.ACCEPTED", BoxResource.getResourceType(BoxFolder.class)),
409
410    /** Triggered when a {@link BoxCollaboration} is rejected. */
411    COLLABORATION_REJECTED("COLLABORATION.REJECTED", BoxResource.getResourceType(BoxFolder.class)),
412
413    /** Triggered when a {@link BoxCollaboration} is removed. */
414    COLLABORATION_REMOVED("COLLABORATION.REMOVED", BoxResource.getResourceType(BoxFolder.class)),
415
416    /** Triggered when a {@link BoxCollaboration} is updated. */
417    COLLABORATION_UPDATED("COLLABORATION.UPDATED", BoxResource.getResourceType(BoxFolder.class)),
418
419    /** Triggered when a {@link BoxSharedLink} is created. */
420    SHARED_LINK_CRATED(
421        "SHARED_LINK.CREATED",
422        BoxResource.getResourceType(BoxFolder.class),
423        BoxResource.getResourceType(BoxFile.class)),
424
425    /** Triggered when a {@link BoxSharedLink} is updated. */
426    SHARED_LINK_UPDATED(
427        "SHARED_LINK.UPDATED",
428        BoxResource.getResourceType(BoxFolder.class),
429        BoxResource.getResourceType(BoxFile.class)),
430
431    /** Triggered when a {@link BoxSharedLink} is deleted. */
432    SHARED_LINK_DELETED(
433        "SHARED_LINK.DELETED",
434        BoxResource.getResourceType(BoxFolder.class),
435        BoxResource.getResourceType(BoxFile.class)),
436
437    /** Triggered when {@link BoxSignRequest} is completed. */
438    SIGN_REQUEST_COMPLETED(
439        "SIGN_REQUEST.COMPLETED",
440        BoxResource.getResourceType(BoxFolder.class),
441        BoxResource.getResourceType(BoxFile.class)),
442    /** Triggered when {@link BoxFile} is declined. */
443    SIGN_REQUEST_DECLINED(
444        "SIGN_REQUEST.DECLINED",
445        BoxResource.getResourceType(BoxFolder.class),
446        BoxResource.getResourceType(BoxFile.class)),
447    /** Triggered when {@link BoxFile} is expired. */
448    SIGN_REQUEST_EXPIRED(
449        "SIGN_REQUEST.EXPIRED",
450        BoxResource.getResourceType(BoxFolder.class),
451        BoxResource.getResourceType(BoxFile.class)),
452    /** Triggered when a signer's email is bounced. */
453    SIGN_REQUEST_SIGNER_EMAIL_BOUNCED(
454        "SIGN_REQUEST.SIGNER_EMAIL_BOUNCED",
455        BoxResource.getResourceType(BoxFolder.class),
456        BoxResource.getResourceType(BoxFile.class)),
457    /** Triggered when the signature request is signed. */
458    SIGN_REQUEST_SIGNER_SIGNED(
459        "SIGN_REQUEST.SIGNER_SIGNED",
460        BoxResource.getResourceType(BoxFolder.class),
461        BoxResource.getResourceType(BoxFile.class)),
462    /** Triggered when the signature is requested from the signer. */
463    SIGN_REQUEST_SIGNATURE_REQUESTED(
464        "SIGN_REQUEST.SIGNATURE_REQUESTED",
465        BoxResource.getResourceType(BoxFolder.class),
466        BoxResource.getResourceType(BoxFile.class)),
467    /** Triggered when the signature request could not be processed. */
468    SIGN_REQUEST_ERROR_FINALIZING(
469        "SIGN_REQUEST.ERROR_FINALIZING",
470        BoxResource.getResourceType(BoxFolder.class),
471        BoxResource.getResourceType(BoxFile.class));
472
473    /** @see #getValue() */
474    private final String value;
475
476    /** @see #getTypes() */
477    private final String[] types;
478
479    /**
480     * Constructor.
481     *
482     * @param value {@link #getValue()}
483     * @param types {@link #getTypes()}
484     */
485    Trigger(String value, String... types) {
486      this.value = value;
487      this.types = types;
488    }
489
490    /**
491     * @param value value to get the Trigger enum value for
492     * @return Trigger for given value
493     */
494    public static Trigger fromValue(String value) {
495      for (Trigger trigger : Trigger.values()) {
496        if (trigger.getValue().equals(value)) {
497          return trigger;
498        }
499      }
500      throw new IllegalArgumentException("No Trigger for value: " + value);
501    }
502
503    /** @return {@link String} representation for {@link Trigger}. */
504    public String getValue() {
505      return this.value;
506    }
507
508    /** @return Supported types for a web-hook. */
509    public String[] getTypes() {
510      return this.types;
511    }
512  }
513
514  /** WebHook target - file or folder. */
515  public static class Target {
516
517    /** @see #getType() */
518    private final String type;
519
520    /** @see #getId() */
521    private final String id;
522
523    /**
524     * Constructor.
525     *
526     * @param type {@link #getType()}
527     * @param id {@link #getId()}
528     */
529    public Target(String type, String id) {
530      this.type = type;
531      this.id = id;
532    }
533
534    /**
535     * @return Type of target.
536     * @see BoxResourceType
537     */
538    public String getType() {
539      return this.type;
540    }
541
542    /** @return {@link BoxResource#getID()} */
543    public String getId() {
544      return this.id;
545    }
546  }
547
548  /** Contains information for a {@link BoxWebHook} instance. */
549  public class Info extends BoxResource.Info {
550
551    /** @see #getTarget() */
552    private Target target;
553
554    /** @see #getAddress() */
555    private URL address;
556
557    /** @see #getTriggers() */
558    private Set<Trigger> triggers;
559
560    /** @see #getCreatedBy() */
561    private BoxUser.Info createdBy;
562
563    /** @see #getCreatedAt() */
564    private Date createdAt;
565
566    /** Constructs an Info object with current target. */
567    public Info() {
568      super();
569      this.target = BoxWebHook.this.getInfo().getTarget();
570    }
571
572    /**
573     * Constructs an Info object by parsing information from a JSON string.
574     *
575     * @param json the JSON string to parse.
576     */
577    public Info(String json) {
578      this(Json.parse(json).asObject());
579    }
580
581    /**
582     * Constructor.
583     *
584     * @param jsonObject a parsed JSON object
585     */
586    public Info(JsonObject jsonObject) {
587      super(jsonObject);
588
589      if (jsonObject.get(JSON_KEY_TARGET) != null) {
590        JsonObject targetObject = jsonObject.get(JSON_KEY_TARGET).asObject();
591        String targetType = targetObject.get(JSON_KEY_TARGET_TYPE).asString();
592        String targetId = targetObject.get(JSON_KEY_TARGET_ID).asString();
593        this.target = new Target(targetType, targetId);
594      }
595
596      if (jsonObject.get(JSON_KEY_TRIGGERS) != null) {
597        this.triggers =
598            new HashSet<>(
599                CollectionUtils.map(
600                    jsonObject.get(JSON_KEY_TRIGGERS).asArray().values(), JSON_VALUE_TO_TRIGGER));
601      }
602      if (jsonObject.get(JSON_KEY_ADDRESS) != null) {
603        try {
604          this.address = new URL(jsonObject.get(JSON_KEY_ADDRESS).asString());
605        } catch (MalformedURLException e) {
606          throw new RuntimeException(e);
607        }
608      }
609
610      if (jsonObject.get(JSON_KEY_CREATED_BY) != null) {
611        JsonObject userJSON = jsonObject.get(JSON_KEY_CREATED_BY).asObject();
612        if (this.createdBy == null) {
613          BoxUser user = new BoxUser(getAPI(), userJSON.get(JSON_KEY_TARGET_ID).asString());
614          this.createdBy = user.new Info(userJSON);
615        } else {
616          this.createdBy.update(userJSON);
617        }
618      }
619
620      if (jsonObject.get(JSON_KEY_CREATED_AT) != null) {
621        try {
622          this.createdAt = BoxDateFormat.parse(jsonObject.get(JSON_KEY_CREATED_AT).asString());
623        } catch (ParseException e) {
624          assert false : "A ParseException indicates a bug in the SDK.";
625        }
626      }
627    }
628
629    /** {@inheritDoc} */
630    @Override
631    public BoxWebHook getResource() {
632      return BoxWebHook.this;
633    }
634
635    /** @return WebHook target / {@link BoxResource}. */
636    public Target getTarget() {
637      return this.target;
638    }
639
640    /** @return {@link URL} where the notification should send to. */
641    public URL getAddress() {
642      return this.address;
643    }
644
645    /**
646     * Setter for {@link #getAddress()}.
647     *
648     * @param address {@link #getAddress()}
649     * @return itself
650     */
651    public Info setAddress(URL address) {
652      if (address == null) {
653        throw new IllegalArgumentException("Address cannot be null");
654      }
655      if (this.address == null || !this.address.equals(address)) {
656        this.address = address;
657        this.addPendingChange(JSON_KEY_ADDRESS, address.toExternalForm());
658      }
659
660      return this;
661    }
662
663    /** @return Events this webhook is interested in. */
664    public Set<Trigger> getTriggers() {
665      return this.triggers;
666    }
667
668    /**
669     * Sets {@link #getTriggers()}.
670     *
671     * @param triggers {@link #getTriggers()}
672     * @return itself
673     */
674    public Info setTriggers(BoxWebHook.Trigger... triggers) {
675      return this.setTriggers(new HashSet<>(Arrays.asList(triggers)));
676    }
677
678    /**
679     * Setter for {@link #getTriggers()}.
680     *
681     * @param triggers {@link #getTriggers()}
682     * @return itself
683     */
684    public Info setTriggers(Set<BoxWebHook.Trigger> triggers) {
685      validateTriggers(this.target.getType(), triggers);
686
687      JsonArray oldValue;
688      if (this.triggers != null) {
689        oldValue = toJsonArray(CollectionUtils.map(this.triggers, TRIGGER_TO_VALUE));
690      } else {
691        oldValue = null;
692      }
693      JsonArray newValue = toJsonArray(CollectionUtils.map(triggers, TRIGGER_TO_VALUE));
694
695      if (!newValue.equals(oldValue)) {
696        this.triggers = Collections.unmodifiableSet(triggers);
697        this.addPendingChange(JSON_KEY_TRIGGERS, newValue);
698      }
699
700      return this;
701    }
702
703    /** @return Info about the user who created this webhook. */
704    public BoxUser.Info getCreatedBy() {
705      return this.createdBy;
706    }
707
708    /** @return the time this webhook was created. */
709    public Date getCreatedAt() {
710      return this.createdAt;
711    }
712
713    /** {@inheritDoc} */
714    @Override
715    void parseJSONMember(JsonObject.Member member) {
716      super.parseJSONMember(member);
717      String memberName = member.getName();
718      JsonValue value = member.getValue();
719      try {
720        if (memberName.equals(JSON_KEY_TARGET)) {
721          String targetType = value.asObject().get(JSON_KEY_TARGET_TYPE).asString();
722          String targetId = value.asObject().get(JSON_KEY_TARGET_ID).asString();
723          this.target = new Target(targetType, targetId);
724        } else if (memberName.equals(JSON_KEY_TRIGGERS)) {
725          this.triggers =
726              new HashSet<>(CollectionUtils.map(value.asArray().values(), JSON_VALUE_TO_TRIGGER));
727        } else if (memberName.equals(JSON_KEY_ADDRESS)) {
728          this.address = new URL(value.asString());
729        } else if (memberName.equals(JSON_KEY_CREATED_BY)) {
730          JsonObject userJSON = value.asObject();
731          if (this.createdBy == null) {
732            String userID = userJSON.get(JSON_KEY_ID).asString();
733            BoxUser user = new BoxUser(getAPI(), userID);
734            this.createdBy = user.new Info(userJSON);
735          } else {
736            this.createdBy.update(userJSON);
737          }
738        } else if (memberName.equals("created_at")) {
739          this.createdAt = BoxDateFormat.parse(value.asString());
740        }
741      } catch (Exception e) {
742        throw new BoxDeserializationException(memberName, value.toString(), e);
743      }
744    }
745  }
746}