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