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