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