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, 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     */
272    public BoxGroupMembership.Info addMembership(BoxUser user, Role role) {
273        return this.addMembership(user, role, null);
274    }
275
276    /**
277     * Adds a member to this group with the specified role.
278     * @param  user the member to be added to this group.
279     * @param  role the role of the user in this group. Can be null to assign the default role.
280     * @param  configurablePermissions the configurable permission of the user as a group admin.
281     * Can be null to give all group admin permissions.
282     * @return      info about the new group membership.
283     */
284    public BoxGroupMembership.Info addMembership(BoxUser user, Role role,
285                                                 Map<BoxGroupMembership.Permission, Boolean> configurablePermissions) {
286        BoxAPIConnection api = this.getAPI();
287
288        JsonObject requestJSON = new JsonObject();
289        requestJSON.add("user", new JsonObject().add("id", user.getID()));
290        requestJSON.add("group", new JsonObject().add("id", this.getID()));
291        if (role != null) {
292            requestJSON.add("role", role.toJSONString());
293        }
294
295        if (configurablePermissions != null) {
296            JsonObject configurablePermissionJson = new JsonObject();
297            for (Permission attrKey : configurablePermissions.keySet()) {
298                configurablePermissionJson.set(attrKey.toJSONValue(), configurablePermissions.get(attrKey));
299            }
300            requestJSON.add("configurable_permissions", configurablePermissionJson);
301        }
302
303        URL url = ADD_MEMBERSHIP_URL_TEMPLATE.build(api.getBaseURL());
304        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
305        request.setBody(requestJSON.toString());
306        BoxJSONResponse response = (BoxJSONResponse) request.send();
307        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
308
309        BoxGroupMembership membership = new BoxGroupMembership(api, responseJSON.get("id").asString());
310        return membership.new Info(responseJSON);
311    }
312
313    /**
314     * Gets information about all of the collaborations for this group.
315     * @return a collection of information about the collaborations for this group.
316     */
317    public Collection<BoxCollaboration.Info> getCollaborations() {
318        BoxAPIConnection api = this.getAPI();
319        URL url = COLLABORATIONS_URL_TEMPLATE.build(api.getBaseURL(), this.getID());
320
321        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
322        BoxJSONResponse response = (BoxJSONResponse) request.send();
323        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
324
325        int entriesCount = responseJSON.get("total_count").asInt();
326        Collection<BoxCollaboration.Info> collaborations = new ArrayList<BoxCollaboration.Info>(entriesCount);
327        JsonArray entries = responseJSON.get("entries").asArray();
328        for (JsonValue entry : entries) {
329            JsonObject entryObject = entry.asObject();
330            BoxCollaboration collaboration = new BoxCollaboration(api, entryObject.get("id").asString());
331            BoxCollaboration.Info info = collaboration.new Info(entryObject);
332            collaborations.add(info);
333        }
334
335        return collaborations;
336    }
337
338    /**
339     * Gets information about all of the collaborations for this group.
340     * @param fields the optional fields to retrieve.
341     * @return An iterable of BoxCollaboration.Info instances associated with the item.
342     */
343    public Iterable<BoxCollaboration.Info> getAllCollaborations(String... fields) {
344        final BoxAPIConnection api = this.getAPI();
345        final QueryStringBuilder builder = new QueryStringBuilder();
346        if (fields.length > 0) {
347            builder.appendParam("fields", fields);
348        }
349        return new Iterable<BoxCollaboration.Info>() {
350            public Iterator<BoxCollaboration.Info> iterator() {
351                URL url = COLLABORATIONS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString(),
352                        BoxGroup.this.getID());
353                return new BoxCollaborationIterator(api, url);
354            }
355        };
356    }
357
358    /**
359     * Deletes this group.
360     */
361    public void delete() {
362        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
363        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
364        BoxAPIResponse response = request.send();
365        response.disconnect();
366    }
367
368    /**
369     * Updates the information about this group with any info fields that have been modified locally.
370     * @param info the updated info.
371     */
372    public void updateInfo(BoxGroup.Info info) {
373        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
374        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
375        request.setBody(info.getPendingChanges());
376        BoxJSONResponse response = (BoxJSONResponse) request.send();
377        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
378        info.update(jsonObject);
379    }
380
381    /**
382     * Contains information about a BoxGroup.
383     */
384    public class Info extends BoxCollaborator.Info {
385
386        /**
387         * @see #getProvenance()
388         */
389        private String provenance;
390
391        /**
392         * @see #getExternalSyncIdentifier()
393         */
394        private String externalSyncIdentifier;
395
396        /**
397         * @see #getDescription()
398         */
399        private String description;
400
401        /**
402         * @see #getInvitabilityLevel()
403         */
404        private String invitabilityLevel;
405
406        /**
407         * @see #getMemberViewabilityLevel()
408         */
409        private String memberViewabilityLevel;
410
411        /**
412         * Constructs an empty Info object.
413         */
414        public Info() {
415            super();
416        }
417
418        /**
419         * Constructs an Info object by parsing information from a JSON string.
420         * @param  json the JSON string to parse.
421         */
422        public Info(String json) {
423            super(json);
424        }
425
426        /**
427         * Constructs an Info object using an already parsed JSON object.
428         * @param  jsonObject the parsed JSON object.
429         */
430        Info(JsonObject jsonObject) {
431            super(jsonObject);
432        }
433
434        /**
435         * {@inheritDoc}
436         */
437        @Override
438        public BoxGroup getResource() {
439            return BoxGroup.this;
440        }
441
442        /**
443         * {@inheritDoc}
444         */
445        @Override
446        protected void parseJSONMember(JsonObject.Member member) {
447            super.parseJSONMember(member);
448
449            String memberName = member.getName();
450            JsonValue value = member.getValue();
451            try {
452                if (memberName.equals("description")) {
453                    this.description = value.asString();
454                } else if (memberName.equals("external_sync_identifier")) {
455                    this.externalSyncIdentifier = value.asString();
456                } else if (memberName.equals("invitability_level")) {
457                    this.invitabilityLevel = value.asString();
458                } else if (memberName.equals("member_viewability_level")) {
459                    this.memberViewabilityLevel = value.asString();
460                } else if (memberName.equals("provenance")) {
461                    this.provenance = value.asString();
462                }
463            } catch (Exception e) {
464                throw new BoxDeserializationException(memberName, value.toString(), e);
465            }
466        }
467
468        /**
469         * Gets the description for the group.
470         * @return the description for the group.
471         */
472        public String getDescription() {
473            return this.description;
474        }
475
476        /**
477         * Sets the description for the group.
478         * @param description the description for the group.
479         */
480        public void setDescription(String description) {
481            this.description = description;
482            addPendingChange("description", description);
483        }
484
485        /**
486         * Gets the external_sync_identifier for the group.
487         * @return the external_sync_identifier for the group.
488         */
489        public String getExternalSyncIdentifier() {
490            return this.externalSyncIdentifier;
491        }
492
493        /**
494         * Sets the external_sync_identifier for the group.
495         * @param externalSyncIdentifier the external_sync_identifier for the group.
496         */
497        public void setExternalSyncIdentifier(String externalSyncIdentifier) {
498            this.externalSyncIdentifier = externalSyncIdentifier;
499            addPendingChange("external_sync_identifier", externalSyncIdentifier);
500        }
501
502        /**
503         * Gets the invitability_level for the group.
504         * @return the invitability_level for the group.
505         */
506        public String getInvitabilityLevel() {
507            return this.invitabilityLevel;
508        }
509
510        /**
511         * Sets the invitability_level for the group.
512         * @param invitabilityLevel the invitability_level for the group.
513         */
514        public void setInvitabilityLevel(String invitabilityLevel) {
515            this.invitabilityLevel = invitabilityLevel;
516            addPendingChange("invitability_level", invitabilityLevel);
517        }
518
519        /**
520         * Gets the member_viewability_level for the group.
521         * @return the member_viewability_level for the group.
522         */
523        public String getMemberViewabilityLevel() {
524            return this.memberViewabilityLevel;
525        }
526
527        /**
528         * Sets the member_viewability_level for the group.
529         * @param memberViewabilityLevel the member_viewability_level for the group.
530         */
531        public void setMemberViewabilityLevel(String memberViewabilityLevel) {
532            this.memberViewabilityLevel = memberViewabilityLevel;
533            addPendingChange("member_viewability_level", memberViewabilityLevel);
534        }
535
536        /**
537         * Gets the provenance for the group.
538         * @return the provenance for the group.
539         */
540        public String getProvenance() {
541            return this.provenance;
542        }
543
544        /**
545         * Sets the provenance for the group.
546         * @param provenance the provenance for the group.
547         */
548        public void setProvenance(String provenance) {
549            this.provenance = provenance;
550            addPendingChange("provenance", provenance);
551        }
552    }
553}