001package com.box.sdk;
002
003import java.net.URL;
004import java.util.ArrayList;
005import java.util.Collection;
006import java.util.Iterator;
007import java.util.Map;
008
009import com.box.sdk.BoxGroupMembership.Permission;
010import com.box.sdk.BoxGroupMembership.Role;
011import com.eclipsesource.json.JsonArray;
012import com.eclipsesource.json.JsonObject;
013import com.eclipsesource.json.JsonValue;
014
015/**
016 * Represents a set of Box users.
017 *
018 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked
019 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error
020 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p>
021 */
022@BoxResourceType("group")
023public class BoxGroup extends BoxCollaborator {
024
025    /**
026     * @see #getAllGroups(BoxAPIConnection, String...)
027     */
028    public static final URLTemplate GROUPS_URL_TEMPLATE = new URLTemplate("groups");
029
030    /**
031     * @see #getInfo()
032     */
033    public static final URLTemplate GROUP_URL_TEMPLATE = new URLTemplate("groups/%s");
034
035    /**
036     * @see #getMemberships()
037     */
038    public static final URLTemplate MEMBERSHIPS_URL_TEMPLATE = new URLTemplate("groups/%s/memberships");
039
040    /**
041     * @see #addMembership(BoxUser)
042     */
043    public static final URLTemplate ADD_MEMBERSHIP_URL_TEMPLATE = new URLTemplate("group_memberships");
044
045    /**
046     * @see #getCollaborations()
047     */
048    public static final URLTemplate COLLABORATIONS_URL_TEMPLATE = new URLTemplate("groups/%s/collaborations");
049
050    /**
051     * Constructs a BoxGroup for a group with a given ID.
052     * @param  api the API connection to be used by the group.
053     * @param  id  the ID of the group.
054     */
055    public BoxGroup(BoxAPIConnection api, String id) {
056        super(api, id);
057    }
058
059    /**
060     * Creates a new group with a specified name.
061     * @param  api  the API connection to be used by the group.
062     * @param  name the name of the new group.
063     * @return      info about the created group.
064     */
065    public static BoxGroup.Info createGroup(BoxAPIConnection api, String name) {
066        return createGroup(api, name, null, null, null, null, null);
067    }
068
069    /**
070     * Creates a new group with a specified name.
071     * @param  api  the API connection to be used by the group.
072     * @param  name the name of the new group.
073     * @param provenance the provenance of the new group
074     * @param externalSyncIdentifier the external_sync_identifier of the new group
075     * @param description the description of the new group
076     * @param invitabilityLevel the invitibility_level of the new group
077     * @param memberViewabilityLevel the member_viewability_level of the new group
078     * @return      info about the created group.
079     */
080    public static BoxGroup.Info createGroup(BoxAPIConnection api, String name, String provenance,
081                                            String externalSyncIdentifier, String description,
082                                            String invitabilityLevel, String memberViewabilityLevel) {
083        JsonObject requestJSON = new JsonObject();
084        requestJSON.add("name", name);
085
086        if (provenance != null) {
087            requestJSON.add("provenance", provenance);
088        }
089        if (externalSyncIdentifier != null) {
090            requestJSON.add("external_sync_identifier", externalSyncIdentifier);
091        }
092        if (description != null) {
093            requestJSON.add("description", description);
094        }
095        if (invitabilityLevel != null) {
096            requestJSON.add("invitability_level", invitabilityLevel);
097        }
098        if (memberViewabilityLevel != null) {
099            requestJSON.add("member_viewability_level", memberViewabilityLevel);
100        }
101
102        URL url = GROUPS_URL_TEMPLATE.build(api.getBaseURL());
103        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
104        request.setBody(requestJSON.toString());
105        BoxJSONResponse response = (BoxJSONResponse) request.send();
106        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
107
108        BoxGroup group = new BoxGroup(api, responseJSON.get("id").asString());
109        return group.new Info(responseJSON);
110    }
111
112    /**
113     * Gets an iterable of all the groups in the enterprise.
114     * @param  api the API connection to be used when retrieving the groups.
115     * @return     an iterable containing info about all the groups.
116     */
117    public static Iterable<BoxGroup.Info> getAllGroups(final BoxAPIConnection api) {
118        return new Iterable<BoxGroup.Info>() {
119            public Iterator<BoxGroup.Info> iterator() {
120                URL url = GROUPS_URL_TEMPLATE.build(api.getBaseURL());
121                return new BoxGroupIterator(api, url);
122            }
123        };
124    }
125
126    /**
127     * Gets an iterable of all the groups in the enterprise.
128     * @param  api the API connection to be used when retrieving the groups.
129     * @param fields the fields to retrieve.
130     * @return     an iterable containing info about all the groups.
131     */
132    public static Iterable<BoxGroup.Info> getAllGroups(final BoxAPIConnection api, String ... fields) {
133        final QueryStringBuilder builder = new QueryStringBuilder();
134        if (fields.length > 0) {
135            builder.appendParam("fields", fields);
136        }
137        return new Iterable<BoxGroup.Info>() {
138            public Iterator<BoxGroup.Info> iterator() {
139                URL url = GROUPS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString());
140                return new BoxGroupIterator(api, url);
141            }
142        };
143    }
144
145    /**
146     * Gets an iterable of all the groups in the enterprise that are starting with the given name string.
147     * @param  api the API connection to be used when retrieving the groups.
148     * @param  name the name prefix of the groups. If the groups need to searched by full name that has spaces,
149     *              then the parameter string should have been wrapped with "".
150     * @return     an iterable containing info about all the groups.
151     */
152    public static Iterable<BoxGroup.Info> getAllGroupsByName(final BoxAPIConnection api, String name) {
153        return getAllGroupsByName(api, name, null);
154    }
155
156    /**
157     * Gets an iterable of all the groups in the enterprise that are starting with the given name string.
158     * @param  api the API connection to be used when retrieving the groups.
159     * @param  name the name prefix of the groups. If the groups need to searched by full name that has spaces,
160     *              then the parameter string should have been wrapped with "".
161     * @param fields the fields to retrieve.
162     * @return     an iterable containing info about all the groups.
163     */
164    public static Iterable<BoxGroup.Info> getAllGroupsByName(final BoxAPIConnection api, String name,
165                                                             String... fields) {
166        final QueryStringBuilder builder = new QueryStringBuilder();
167        if (name == null || name.trim().isEmpty()) {
168            throw new BoxAPIException("Searching groups by name requires a non NULL or non empty name");
169        } else {
170            builder.appendParam("filter_term", name);
171            if (fields != null && fields.length > 0) {
172                builder.appendParam("fields", fields);
173            }
174        }
175
176        return new Iterable<BoxGroup.Info>() {
177            public Iterator<BoxGroup.Info> iterator() {
178                URL url = GROUPS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString());
179                return new BoxGroupIterator(api, url);
180            }
181        };
182    }
183
184    /**
185     * Gets information about this group.
186     * @return info about this group.
187     */
188    public Info getInfo() {
189        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
190        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
191        BoxJSONResponse response = (BoxJSONResponse) request.send();
192        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
193        return new Info(responseJSON);
194    }
195
196    /**
197     * Gets information about this group.
198     * @param fields the fields to retrieve.
199     * @return info about this group.
200     */
201    public Info getInfo(String ... fields) {
202        QueryStringBuilder builder = new QueryStringBuilder();
203        if (fields.length > 0) {
204            builder.appendParam("fields", fields);
205        }
206        URL url = GROUP_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID());
207        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
208        BoxJSONResponse response = (BoxJSONResponse) request.send();
209        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
210        return new Info(responseJSON);
211    }
212
213    /**
214     * Gets information about all of the group memberships for this group.
215     * Does not support paging.
216     * @return a collection of information about the group memberships for this group.
217     */
218    public Collection<BoxGroupMembership.Info> getMemberships() {
219        final BoxAPIConnection api = this.getAPI();
220        final String groupID = this.getID();
221
222        Iterable<BoxGroupMembership.Info> iter = new Iterable<BoxGroupMembership.Info>() {
223            public Iterator<BoxGroupMembership.Info> iterator() {
224                URL url = MEMBERSHIPS_URL_TEMPLATE.build(api.getBaseURL(), groupID);
225                return new BoxGroupMembershipIterator(api, url);
226            }
227        };
228
229        // We need to iterate all results because this method must return a Collection. This logic should be removed in
230        // the next major version, and instead return the Iterable directly.
231        Collection<BoxGroupMembership.Info> memberships = new ArrayList<BoxGroupMembership.Info>();
232        for (BoxGroupMembership.Info membership : iter) {
233            memberships.add(membership);
234        }
235        return memberships;
236    }
237
238    /**
239     * Gets information about all of the group memberships for this group as iterable with paging support.
240     * @param fields the fields to retrieve.
241     * @return an iterable with information about the group memberships for this group.
242     */
243    public Iterable<BoxGroupMembership.Info> getAllMemberships(String ... fields) {
244        final QueryStringBuilder builder = new QueryStringBuilder();
245        if (fields.length > 0) {
246            builder.appendParam("fields", fields);
247        }
248        return new Iterable<BoxGroupMembership.Info>() {
249            public Iterator<BoxGroupMembership.Info> iterator() {
250                URL url = MEMBERSHIPS_URL_TEMPLATE.buildWithQuery(
251                        BoxGroup.this.getAPI().getBaseURL(), builder.toString(), BoxGroup.this.getID());
252                return new BoxGroupMembershipIterator(BoxGroup.this.getAPI(), url);
253            }
254        };
255    }
256
257    /**
258     * Adds a member to this group with the default role.
259     * @param  user the member to be added to this group.
260     * @return      info about the new group membership.
261     */
262    public BoxGroupMembership.Info addMembership(BoxUser user) {
263        return this.addMembership(user, (Role) null, null);
264    }
265
266    /**
267     * Adds a member to this group with the specified role.
268     * @param  user the member to be added to this group.
269     * @param  role the role of the user in this group. Can be null to assign the default role.
270     * @return      info about the new group membership.
271     * @deprecated use addMembership(BoxUser user, BoxGroupMembership.GroupRole role) instead.
272     */
273    @Deprecated
274    public BoxGroupMembership.Info addMembership(BoxUser user, Role role) {
275        return this.addMembership(user, role, null);
276    }
277
278    /**
279     * Adds a member to this group with the specified role.
280     * @param  user the member to be added to this group.
281     * @param  role the role of the user in this group. Can be null to assign the default role.
282     * @return      info about the new group membership.
283     */
284    public BoxGroupMembership.Info addMembership(BoxUser user, BoxGroupMembership.GroupRole role) {
285        return this.addMembership(user, role, null);
286    }
287
288    /**
289     * Adds a member to this group with the specified role.
290     * @param  user the member to be added to this group.
291     * @param  role the role of the user in this group. Can be null to assign the default role.
292     * @param  configurablePermissions the configurable permission of the user as a group admin.
293     * Can be null to give all group admin permissions.
294     * @return      info about the new group membership.
295     * @deprecated use {@code addMembership(BoxUser user, GroupRole role,
296     * Map<BoxGroupMembership.Permission, Boolean> configurablePermissions)} instead.
297     */
298    @Deprecated
299    public BoxGroupMembership.Info addMembership(BoxUser user, Role role,
300                                                 Map<BoxGroupMembership.Permission, Boolean> configurablePermissions) {
301        BoxAPIConnection api = this.getAPI();
302
303        JsonObject requestJSON = new JsonObject();
304        requestJSON.add("user", new JsonObject().add("id", user.getID()));
305        requestJSON.add("group", new JsonObject().add("id", this.getID()));
306        if (role != null) {
307            requestJSON.add("role", role.toJSONString());
308        }
309
310        if (configurablePermissions != null) {
311            JsonObject configurablePermissionJson = new JsonObject();
312            for (Permission attrKey : configurablePermissions.keySet()) {
313                configurablePermissionJson.set(attrKey.toJSONValue(), configurablePermissions.get(attrKey));
314            }
315            requestJSON.add("configurable_permissions", configurablePermissionJson);
316        }
317
318        URL url = ADD_MEMBERSHIP_URL_TEMPLATE.build(api.getBaseURL());
319        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
320        request.setBody(requestJSON.toString());
321        BoxJSONResponse response = (BoxJSONResponse) request.send();
322        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
323
324        BoxGroupMembership membership = new BoxGroupMembership(api, responseJSON.get("id").asString());
325        return membership.new Info(responseJSON);
326    }
327
328    /**
329     * Adds a member to this group with the specified role.
330     * @param  user the member to be added to this group.
331     * @param  role the role of the user in this group. Can be null to assign the default role.
332     * @param  configurablePermissions the configurable permission of the user as a group admin.
333     * Can be null to give all group admin permissions.
334     * @return      info about the new group membership.
335     */
336    public BoxGroupMembership.Info addMembership(BoxUser user, BoxGroupMembership.GroupRole role,
337                                                 Map<BoxGroupMembership.Permission, Boolean> configurablePermissions) {
338        BoxAPIConnection api = this.getAPI();
339
340        JsonObject requestJSON = new JsonObject();
341        requestJSON.add("user", new JsonObject().add("id", user.getID()));
342        requestJSON.add("group", new JsonObject().add("id", this.getID()));
343        if (role != null) {
344            requestJSON.add("role", role.toJSONString());
345        }
346
347        if (configurablePermissions != null) {
348            JsonObject configurablePermissionJson = new JsonObject();
349            for (Permission attrKey : configurablePermissions.keySet()) {
350                configurablePermissionJson.set(attrKey.toJSONValue(), configurablePermissions.get(attrKey));
351            }
352            requestJSON.add("configurable_permissions", configurablePermissionJson);
353        }
354
355        URL url = ADD_MEMBERSHIP_URL_TEMPLATE.build(api.getBaseURL());
356        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
357        request.setBody(requestJSON.toString());
358        BoxJSONResponse response = (BoxJSONResponse) request.send();
359        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
360
361        BoxGroupMembership membership = new BoxGroupMembership(api, responseJSON.get("id").asString());
362        return membership.new Info(responseJSON);
363    }
364
365    /**
366     * Gets information about all of the collaborations for this group.
367     * @return a collection of information about the collaborations for this group.
368     */
369    public Collection<BoxCollaboration.Info> getCollaborations() {
370        BoxAPIConnection api = this.getAPI();
371        URL url = COLLABORATIONS_URL_TEMPLATE.build(api.getBaseURL(), this.getID());
372
373        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
374        BoxJSONResponse response = (BoxJSONResponse) request.send();
375        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
376
377        int entriesCount = responseJSON.get("total_count").asInt();
378        Collection<BoxCollaboration.Info> collaborations = new ArrayList<BoxCollaboration.Info>(entriesCount);
379        JsonArray entries = responseJSON.get("entries").asArray();
380        for (JsonValue entry : entries) {
381            JsonObject entryObject = entry.asObject();
382            BoxCollaboration collaboration = new BoxCollaboration(api, entryObject.get("id").asString());
383            BoxCollaboration.Info info = collaboration.new Info(entryObject);
384            collaborations.add(info);
385        }
386
387        return collaborations;
388    }
389
390    /**
391     * Gets information about all of the collaborations for this group.
392     * @param fields the optional fields to retrieve.
393     * @return An iterable of BoxCollaboration.Info instances associated with the item.
394     */
395    public Iterable<BoxCollaboration.Info> getAllCollaborations(String... fields) {
396        final BoxAPIConnection api = this.getAPI();
397        final QueryStringBuilder builder = new QueryStringBuilder();
398        if (fields.length > 0) {
399            builder.appendParam("fields", fields);
400        }
401        return new Iterable<BoxCollaboration.Info>() {
402            public Iterator<BoxCollaboration.Info> iterator() {
403                URL url = COLLABORATIONS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString(),
404                        BoxGroup.this.getID());
405                return new BoxCollaborationIterator(api, url);
406            }
407        };
408    }
409
410    /**
411     * Deletes this group.
412     */
413    public void delete() {
414        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
415        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
416        BoxAPIResponse response = request.send();
417        response.disconnect();
418    }
419
420    /**
421     * Updates the information about this group with any info fields that have been modified locally.
422     * @param info the updated info.
423     */
424    public void updateInfo(BoxGroup.Info info) {
425        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
426        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
427        request.setBody(info.getPendingChanges());
428        BoxJSONResponse response = (BoxJSONResponse) request.send();
429        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
430        info.update(jsonObject);
431    }
432
433    /**
434     * Contains information about a BoxGroup.
435     */
436    public class Info extends BoxCollaborator.Info {
437
438        /**
439         * @see #getProvenance()
440         */
441        private String provenance;
442
443        /**
444         * @see #getExternalSyncIdentifier()
445         */
446        private String externalSyncIdentifier;
447
448        /**
449         * @see #getDescription()
450         */
451        private String description;
452
453        /**
454         * @see #getInvitabilityLevel()
455         */
456        private String invitabilityLevel;
457
458        /**
459         * @see #getMemberViewabilityLevel()
460         */
461        private String memberViewabilityLevel;
462
463        /**
464         * Constructs an empty Info object.
465         */
466        public Info() {
467            super();
468        }
469
470        /**
471         * Constructs an Info object by parsing information from a JSON string.
472         * @param  json the JSON string to parse.
473         */
474        public Info(String json) {
475            super(json);
476        }
477
478        /**
479         * Constructs an Info object using an already parsed JSON object.
480         * @param  jsonObject the parsed JSON object.
481         */
482        Info(JsonObject jsonObject) {
483            super(jsonObject);
484        }
485
486        /**
487         * {@inheritDoc}
488         */
489        @Override
490        public BoxGroup getResource() {
491            return BoxGroup.this;
492        }
493
494        /**
495         * {@inheritDoc}
496         */
497        @Override
498        protected void parseJSONMember(JsonObject.Member member) {
499            super.parseJSONMember(member);
500
501            String memberName = member.getName();
502            JsonValue value = member.getValue();
503            try {
504                if (memberName.equals("description")) {
505                    this.description = value.asString();
506                } else if (memberName.equals("external_sync_identifier")) {
507                    this.externalSyncIdentifier = value.asString();
508                } else if (memberName.equals("invitability_level")) {
509                    this.invitabilityLevel = value.asString();
510                } else if (memberName.equals("member_viewability_level")) {
511                    this.memberViewabilityLevel = value.asString();
512                } else if (memberName.equals("provenance")) {
513                    this.provenance = value.asString();
514                }
515            } catch (Exception e) {
516                throw new BoxDeserializationException(memberName, value.toString(), e);
517            }
518        }
519
520        /**
521         * Gets the description for the group.
522         * @return the description for the group.
523         */
524        public String getDescription() {
525            return this.description;
526        }
527
528        /**
529         * Sets the description for the group.
530         * @param description the description for the group.
531         */
532        public void setDescription(String description) {
533            this.description = description;
534            addPendingChange("description", description);
535        }
536
537        /**
538         * Gets the external_sync_identifier for the group.
539         * @return the external_sync_identifier for the group.
540         */
541        public String getExternalSyncIdentifier() {
542            return this.externalSyncIdentifier;
543        }
544
545        /**
546         * Sets the external_sync_identifier for the group.
547         * @param externalSyncIdentifier the external_sync_identifier for the group.
548         */
549        public void setExternalSyncIdentifier(String externalSyncIdentifier) {
550            this.externalSyncIdentifier = externalSyncIdentifier;
551            addPendingChange("external_sync_identifier", externalSyncIdentifier);
552        }
553
554        /**
555         * Gets the invitability_level for the group.
556         * @return the invitability_level for the group.
557         */
558        public String getInvitabilityLevel() {
559            return this.invitabilityLevel;
560        }
561
562        /**
563         * Sets the invitability_level for the group.
564         * @param invitabilityLevel the invitability_level for the group.
565         */
566        public void setInvitabilityLevel(String invitabilityLevel) {
567            this.invitabilityLevel = invitabilityLevel;
568            addPendingChange("invitability_level", invitabilityLevel);
569        }
570
571        /**
572         * Gets the member_viewability_level for the group.
573         * @return the member_viewability_level for the group.
574         */
575        public String getMemberViewabilityLevel() {
576            return this.memberViewabilityLevel;
577        }
578
579        /**
580         * Sets the member_viewability_level for the group.
581         * @param memberViewabilityLevel the member_viewability_level for the group.
582         */
583        public void setMemberViewabilityLevel(String memberViewabilityLevel) {
584            this.memberViewabilityLevel = memberViewabilityLevel;
585            addPendingChange("member_viewability_level", memberViewabilityLevel);
586        }
587
588        /**
589         * Gets the provenance for the group.
590         * @return the provenance for the group.
591         */
592        public String getProvenance() {
593            return this.provenance;
594        }
595
596        /**
597         * Sets the provenance for the group.
598         * @param provenance the provenance for the group.
599         */
600        public void setProvenance(String provenance) {
601            this.provenance = provenance;
602            addPendingChange("provenance", provenance);
603        }
604    }
605}