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        final QueryStringBuilder builder = new QueryStringBuilder();
152        if (name == null || name.trim().isEmpty()) {
153            throw new BoxAPIException("Searching groups by name requires a non NULL or non empty name");
154        } else {
155            builder.appendParam("name", name);
156        }
157
158        return new Iterable<BoxGroup.Info>() {
159            public Iterator<BoxGroup.Info> iterator() {
160                URL url = GROUPS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString());
161                return new BoxGroupIterator(api, url);
162            }
163        };
164    }
165
166    /**
167     * Gets information about this group.
168     * @return info about this group.
169     */
170    public Info getInfo() {
171        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
172        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
173        BoxJSONResponse response = (BoxJSONResponse) request.send();
174        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
175        return new Info(responseJSON);
176    }
177
178    /**
179     * Gets information about this group.
180     * @param fields the fields to retrieve.
181     * @return info about this group.
182     */
183    public Info getInfo(String ... fields) {
184        QueryStringBuilder builder = new QueryStringBuilder();
185        if (fields.length > 0) {
186            builder.appendParam("fields", fields);
187        }
188        URL url = GROUP_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID());
189        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
190        BoxJSONResponse response = (BoxJSONResponse) request.send();
191        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
192        return new Info(responseJSON);
193    }
194
195    /**
196     * Gets information about all of the group memberships for this group.
197     * Does not support paging.
198     * @return a collection of information about the group memberships for this group.
199     */
200    public Collection<BoxGroupMembership.Info> getMemberships() {
201        final BoxAPIConnection api = this.getAPI();
202        final String groupID = this.getID();
203
204        Iterable<BoxGroupMembership.Info> iter = new Iterable<BoxGroupMembership.Info>() {
205            public Iterator<BoxGroupMembership.Info> iterator() {
206                URL url = MEMBERSHIPS_URL_TEMPLATE.build(api.getBaseURL(), groupID);
207                return new BoxGroupMembershipIterator(api, url);
208            }
209        };
210
211        // We need to iterate all results because this method must return a Collection. This logic should be removed in
212        // the next major version, and instead return the Iterable directly.
213        Collection<BoxGroupMembership.Info> memberships = new ArrayList<BoxGroupMembership.Info>();
214        for (BoxGroupMembership.Info membership : iter) {
215            memberships.add(membership);
216        }
217        return memberships;
218    }
219
220    /**
221     * Gets information about all of the group memberships for this group as iterable with paging support.
222     * @param fields the fields to retrieve.
223     * @return an iterable with information about the group memberships for this group.
224     */
225    public Iterable<BoxGroupMembership.Info> getAllMemberships(String ... fields) {
226        final QueryStringBuilder builder = new QueryStringBuilder();
227        if (fields.length > 0) {
228            builder.appendParam("fields", fields);
229        }
230        return new Iterable<BoxGroupMembership.Info>() {
231            public Iterator<BoxGroupMembership.Info> iterator() {
232                URL url = MEMBERSHIPS_URL_TEMPLATE.buildWithQuery(
233                        BoxGroup.this.getAPI().getBaseURL(), builder.toString(), BoxGroup.this.getID());
234                return new BoxGroupMembershipIterator(BoxGroup.this.getAPI(), url);
235            }
236        };
237    }
238
239    /**
240     * Adds a member to this group with the default role.
241     * @param  user the member to be added to this group.
242     * @return      info about the new group membership.
243     */
244    public BoxGroupMembership.Info addMembership(BoxUser user) {
245        return this.addMembership(user, null);
246    }
247
248    /**
249     * Adds a member to this group with the specified role.
250     * @param  user the member to be added to this group.
251     * @param  role the role of the user in this group. Can be null to assign the default role.
252     * @return      info about the new group membership.
253     */
254    public BoxGroupMembership.Info addMembership(BoxUser user, Role role) {
255        BoxAPIConnection api = this.getAPI();
256
257        JsonObject requestJSON = new JsonObject();
258        requestJSON.add("user", new JsonObject().add("id", user.getID()));
259        requestJSON.add("group", new JsonObject().add("id", this.getID()));
260        if (role != null) {
261            requestJSON.add("role", role.toJSONString());
262        }
263
264        URL url = ADD_MEMBERSHIP_URL_TEMPLATE.build(api.getBaseURL());
265        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
266        request.setBody(requestJSON.toString());
267        BoxJSONResponse response = (BoxJSONResponse) request.send();
268        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
269
270        BoxGroupMembership membership = new BoxGroupMembership(api, responseJSON.get("id").asString());
271        return membership.new Info(responseJSON);
272    }
273
274    /**
275     * Gets information about all of the collaborations for this group.
276     * @return a collection of information about the collaborations for this group.
277     */
278    public Collection<BoxCollaboration.Info> getCollaborations() {
279        BoxAPIConnection api = this.getAPI();
280        URL url = COLLABORATIONS_URL_TEMPLATE.build(api.getBaseURL(), this.getID());
281
282        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
283        BoxJSONResponse response = (BoxJSONResponse) request.send();
284        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
285
286        int entriesCount = responseJSON.get("total_count").asInt();
287        Collection<BoxCollaboration.Info> collaborations = new ArrayList<BoxCollaboration.Info>(entriesCount);
288        JsonArray entries = responseJSON.get("entries").asArray();
289        for (JsonValue entry : entries) {
290            JsonObject entryObject = entry.asObject();
291            BoxCollaboration collaboration = new BoxCollaboration(api, entryObject.get("id").asString());
292            BoxCollaboration.Info info = collaboration.new Info(entryObject);
293            collaborations.add(info);
294        }
295
296        return collaborations;
297    }
298
299    /**
300     * Deletes this group.
301     */
302    public void delete() {
303        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
304        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
305        BoxAPIResponse response = request.send();
306        response.disconnect();
307    }
308
309    /**
310     * Updates the information about this group with any info fields that have been modified locally.
311     * @param info the updated info.
312     */
313    public void updateInfo(BoxGroup.Info info) {
314        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
315        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
316        request.setBody(info.getPendingChanges());
317        BoxJSONResponse response = (BoxJSONResponse) request.send();
318        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
319        info.update(jsonObject);
320    }
321
322    /**
323     * Contains information about a BoxGroup.
324     */
325    public class Info extends BoxCollaborator.Info {
326
327        /**
328         * @see #getProvenance()
329         */
330        private String provenance;
331
332        /**
333         * @see #getExternalSyncIdentifier()
334         */
335        private String externalSyncIdentifier;
336
337        /**
338         * @see #getDescription()
339         */
340        private String description;
341
342        /**
343         * @see #getInvitabilityLevel()
344         */
345        private String invitabilityLevel;
346
347        /**
348         * @see #getMemberViewabilityLevel()
349         */
350        private String memberViewabilityLevel;
351
352        /**
353         * Constructs an empty Info object.
354         */
355        public Info() {
356            super();
357        }
358
359        /**
360         * Constructs an Info object by parsing information from a JSON string.
361         * @param  json the JSON string to parse.
362         */
363        public Info(String json) {
364            super(json);
365        }
366
367        /**
368         * Constructs an Info object using an already parsed JSON object.
369         * @param  jsonObject the parsed JSON object.
370         */
371        Info(JsonObject jsonObject) {
372            super(jsonObject);
373        }
374
375        /**
376         * {@inheritDoc}
377         */
378        @Override
379        public BoxGroup getResource() {
380            return BoxGroup.this;
381        }
382
383        /**
384         * {@inheritDoc}
385         */
386        @Override
387        protected void parseJSONMember(JsonObject.Member member) {
388            super.parseJSONMember(member);
389
390            String memberName = member.getName();
391            JsonValue value = member.getValue();
392            try {
393                if (memberName.equals("description")) {
394                    this.description = value.asString();
395                } else if (memberName.equals("external_sync_identifier")) {
396                    this.externalSyncIdentifier = value.asString();
397                } else if (memberName.equals("invitability_level")) {
398                    this.invitabilityLevel = value.asString();
399                } else if (memberName.equals("member_viewability_level")) {
400                    this.memberViewabilityLevel = value.asString();
401                } else if (memberName.equals("provenance")) {
402                    this.provenance = value.asString();
403                }
404            } catch (Exception e) {
405                throw new BoxDeserializationException(memberName, value.toString(), e);
406            }
407        }
408
409        /**
410         * Gets the description for the group.
411         * @return the description for the group.
412         */
413        public String getDescription() {
414            return this.description;
415        }
416
417        /**
418         * Sets the description for the group.
419         * @param description the description for the group.
420         */
421        public void setDescription(String description) {
422            this.description = description;
423            addPendingChange("description", description);
424        }
425
426        /**
427         * Gets the external_sync_identifier for the group.
428         * @return the external_sync_identifier for the group.
429         */
430        public String getExternalSyncIdentifier() {
431            return this.externalSyncIdentifier;
432        }
433
434        /**
435         * Sets the external_sync_identifier for the group.
436         * @param externalSyncIdentifier the external_sync_identifier for the group.
437         */
438        public void setExternalSyncIdentifier(String externalSyncIdentifier) {
439            this.externalSyncIdentifier = externalSyncIdentifier;
440            addPendingChange("external_sync_identifier", externalSyncIdentifier);
441        }
442
443        /**
444         * Gets the invitability_level for the group.
445         * @return the invitability_level for the group.
446         */
447        public String getInvitabilityLevel() {
448            return this.invitabilityLevel;
449        }
450
451        /**
452         * Sets the invitability_level for the group.
453         * @param invitabilityLevel the invitability_level for the group.
454         */
455        public void setInvitabilityLevel(String invitabilityLevel) {
456            this.invitabilityLevel = invitabilityLevel;
457            addPendingChange("invitability_level", invitabilityLevel);
458        }
459
460        /**
461         * Gets the member_viewability_level for the group.
462         * @return the member_viewability_level for the group.
463         */
464        public String getMemberViewabilityLevel() {
465            return this.memberViewabilityLevel;
466        }
467
468        /**
469         * Sets the member_viewability_level for the group.
470         * @param memberViewabilityLevel the member_viewability_level for the group.
471         */
472        public void setMemberViewabilityLevel(String memberViewabilityLevel) {
473            this.memberViewabilityLevel = memberViewabilityLevel;
474            addPendingChange("member_viewability_level", memberViewabilityLevel);
475        }
476
477        /**
478         * Gets the provenance for the group.
479         * @return the provenance for the group.
480         */
481        public String getProvenance() {
482            return this.provenance;
483        }
484
485        /**
486         * Sets the provenance for the group.
487         * @param provenance the provenance for the group.
488         */
489        public void setProvenance(String provenance) {
490            this.provenance = provenance;
491            addPendingChange("provenance", provenance);
492        }
493    }
494}