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