001package com.box.sdk;
002
003import com.box.sdk.BoxGroupMembership.Permission;
004import com.eclipsesource.json.Json;
005import com.eclipsesource.json.JsonArray;
006import com.eclipsesource.json.JsonObject;
007import com.eclipsesource.json.JsonValue;
008import java.net.URL;
009import java.util.ArrayList;
010import java.util.Collection;
011import java.util.Map;
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(String...)
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     *
051     * @param api the API connection to be used by the group.
052     * @param id  the ID of the group.
053     */
054    public BoxGroup(BoxAPIConnection api, String id) {
055        super(api, id);
056    }
057
058    /**
059     * Creates a new group with a specified name.
060     *
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     *
072     * @param api                    the API connection to be used by the group.
073     * @param name                   the name of the new group.
074     * @param provenance             the provenance of the new group
075     * @param externalSyncIdentifier the external_sync_identifier of the new group
076     * @param description            the description of the new group
077     * @param invitabilityLevel      the invitibility_level of the new group
078     * @param memberViewabilityLevel the member_viewability_level of the new group
079     * @return info about the created group.
080     */
081    public static BoxGroup.Info createGroup(BoxAPIConnection api, String name, String provenance,
082                                            String externalSyncIdentifier, String description,
083                                            String invitabilityLevel, String memberViewabilityLevel) {
084        JsonObject requestJSON = new JsonObject();
085        requestJSON.add("name", name);
086
087        if (provenance != null) {
088            requestJSON.add("provenance", provenance);
089        }
090        if (externalSyncIdentifier != null) {
091            requestJSON.add("external_sync_identifier", externalSyncIdentifier);
092        }
093        if (description != null) {
094            requestJSON.add("description", description);
095        }
096        if (invitabilityLevel != null) {
097            requestJSON.add("invitability_level", invitabilityLevel);
098        }
099        if (memberViewabilityLevel != null) {
100            requestJSON.add("member_viewability_level", memberViewabilityLevel);
101        }
102
103        URL url = GROUPS_URL_TEMPLATE.build(api.getBaseURL());
104        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
105        request.setBody(requestJSON.toString());
106        try (BoxJSONResponse response = request.send()) {
107            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
108
109            BoxGroup group = new BoxGroup(api, responseJSON.get("id").asString());
110            return group.new Info(responseJSON);
111        }
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     * @param fields the fields to retrieve.
152     * @return an iterable containing info about all the groups.
153     */
154    public static Iterable<BoxGroup.Info> getAllGroupsByName(
155        final BoxAPIConnection api, String name, String... fields
156    ) {
157        final QueryStringBuilder builder = new QueryStringBuilder();
158        if (name == null || name.trim().isEmpty()) {
159            throw new BoxAPIException("Searching groups by name requires a non NULL or non empty name");
160        } else {
161            builder.appendParam("filter_term", name);
162            if (fields != null && fields.length > 0) {
163                builder.appendParam("fields", fields);
164            }
165        }
166
167        return () -> {
168            URL url = GROUPS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString());
169            return new BoxGroupIterator(api, url);
170        };
171    }
172
173    /**
174     * Gets information about this group.
175     *
176     * @param fields the fields to retrieve.
177     * @return info about this group.
178     */
179    public Info getInfo(String... fields) {
180        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
181        if (fields.length > 0) {
182            QueryStringBuilder builder = new QueryStringBuilder().appendParam("fields", fields);
183            url = GROUP_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID());
184        }
185        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET");
186        try (BoxJSONResponse response = request.send()) {
187            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
188            return new Info(responseJSON);
189        }
190    }
191
192    /**
193     * Gets information about all of the group memberships for this group.
194     * Does not support paging.
195     *
196     * @return a collection of information about the group memberships for this group.
197     */
198    public Collection<BoxGroupMembership.Info> getMemberships() {
199        final BoxAPIConnection api = this.getAPI();
200        final String groupID = this.getID();
201
202        Iterable<BoxGroupMembership.Info> iter = () -> {
203            URL url = MEMBERSHIPS_URL_TEMPLATE.build(api.getBaseURL(), groupID);
204            return new BoxGroupMembershipIterator(api, url);
205        };
206
207        // We need to iterate all results because this method must return a Collection. This logic should be removed in
208        // the next major version, and instead return the Iterable directly.
209        Collection<BoxGroupMembership.Info> memberships = new ArrayList<>();
210        for (BoxGroupMembership.Info membership : iter) {
211            memberships.add(membership);
212        }
213        return memberships;
214    }
215
216    /**
217     * Gets information about all of the group memberships for this group as iterable with paging support.
218     *
219     * @param fields the fields to retrieve.
220     * @return an iterable with information about the group memberships for this group.
221     */
222    public Iterable<BoxGroupMembership.Info> getAllMemberships(String... fields) {
223        final QueryStringBuilder builder = new QueryStringBuilder();
224        if (fields.length > 0) {
225            builder.appendParam("fields", fields);
226        }
227        return () -> {
228            URL url = MEMBERSHIPS_URL_TEMPLATE.buildWithQuery(
229                BoxGroup.this.getAPI().getBaseURL(), builder.toString(), BoxGroup.this.getID());
230            return new BoxGroupMembershipIterator(BoxGroup.this.getAPI(), url);
231        };
232    }
233
234    /**
235     * Adds a member to this group with the default role.
236     *
237     * @param user the member to be added to this group.
238     * @return info about the new group membership.
239     */
240    public BoxGroupMembership.Info addMembership(BoxUser user) {
241        return this.addMembership(user, null, null);
242    }
243
244    /**
245     * Adds a member to this group with the specified role.
246     *
247     * @param user the member to be added to this group.
248     * @param role the role of the user in this group. Can be null to assign the default role.
249     * @return info about the new group membership.
250     */
251    public BoxGroupMembership.Info addMembership(BoxUser user, BoxGroupMembership.GroupRole role) {
252        return this.addMembership(user, role, null);
253    }
254
255    /**
256     * Adds a member to this group with the specified role.
257     *
258     * @param user                    the member to be added to this group.
259     * @param role                    the role of the user in this group. Can be null to assign the default role.
260     * @param configurablePermissions the configurable permission of the user as a group admin.
261     *                                Can be null to give all group admin permissions.
262     * @return info about the new group membership.
263     */
264    public BoxGroupMembership.Info addMembership(BoxUser user, BoxGroupMembership.GroupRole role,
265                                                 Map<BoxGroupMembership.Permission, Boolean> configurablePermissions) {
266        BoxAPIConnection api = this.getAPI();
267
268        JsonObject requestJSON = new JsonObject();
269        requestJSON.add("user", new JsonObject().add("id", user.getID()));
270        requestJSON.add("group", new JsonObject().add("id", this.getID()));
271        if (role != null) {
272            requestJSON.add("role", role.toJSONString());
273        }
274
275        if (configurablePermissions != null) {
276            JsonObject configurablePermissionJson = new JsonObject();
277            for (Permission attrKey : configurablePermissions.keySet()) {
278                configurablePermissionJson.set(attrKey.toJSONValue(), configurablePermissions.get(attrKey));
279            }
280            requestJSON.add("configurable_permissions", configurablePermissionJson);
281        }
282
283        URL url = ADD_MEMBERSHIP_URL_TEMPLATE.build(api.getBaseURL());
284        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
285        request.setBody(requestJSON.toString());
286        try (BoxJSONResponse response = request.send()) {
287            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
288
289            BoxGroupMembership membership = new BoxGroupMembership(api, responseJSON.get("id").asString());
290            return membership.new Info(responseJSON);
291        }
292    }
293
294    /**
295     * Gets information about all of the collaborations for this group.
296     *
297     * @return a collection of information about the collaborations for this group.
298     */
299    public Collection<BoxCollaboration.Info> getCollaborations() {
300        BoxAPIConnection api = this.getAPI();
301        URL url = COLLABORATIONS_URL_TEMPLATE.build(api.getBaseURL(), this.getID());
302
303        BoxJSONRequest request = new BoxJSONRequest(api, url, "GET");
304        try (BoxJSONResponse response = request.send()) {
305            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
306
307            int entriesCount = responseJSON.get("total_count").asInt();
308            Collection<BoxCollaboration.Info> collaborations = new ArrayList<>(entriesCount);
309            JsonArray entries = responseJSON.get("entries").asArray();
310            for (JsonValue entry : entries) {
311                JsonObject entryObject = entry.asObject();
312                BoxCollaboration collaboration = new BoxCollaboration(api, entryObject.get("id").asString());
313                BoxCollaboration.Info info = collaboration.new Info(entryObject);
314                collaborations.add(info);
315            }
316
317            return collaborations;
318        }
319    }
320
321    /**
322     * Gets information about all of the collaborations for this group.
323     *
324     * @param fields the optional fields to retrieve.
325     * @return An iterable of BoxCollaboration.Info instances associated with the item.
326     */
327    public Iterable<BoxCollaboration.Info> getAllCollaborations(String... fields) {
328        final BoxAPIConnection api = this.getAPI();
329        final QueryStringBuilder builder = new QueryStringBuilder();
330        if (fields.length > 0) {
331            builder.appendParam("fields", fields);
332        }
333        return () -> {
334            URL url = COLLABORATIONS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString(),
335                BoxGroup.this.getID());
336            return new BoxCollaborationIterator(api, url);
337        };
338    }
339
340    /**
341     * Deletes this group.
342     */
343    public void delete() {
344        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
345        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
346        request.send().close();
347    }
348
349    /**
350     * Updates the information about this group with any info fields that have been modified locally.
351     *
352     * @param info the updated info.
353     */
354    public void updateInfo(BoxGroup.Info info) {
355        URL url = GROUP_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
356        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
357        request.setBody(info.getPendingChanges());
358        try (BoxJSONResponse response = request.send()) {
359            JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
360            info.update(jsonObject);
361        }
362    }
363
364    /**
365     * Contains information about a BoxGroup.
366     */
367    public class Info extends BoxCollaborator.Info {
368
369        /**
370         * @see #getProvenance()
371         */
372        private String provenance;
373
374        /**
375         * @see #getExternalSyncIdentifier()
376         */
377        private String externalSyncIdentifier;
378
379        /**
380         * @see #getDescription()
381         */
382        private String description;
383
384        /**
385         * @see #getInvitabilityLevel()
386         */
387        private String invitabilityLevel;
388
389        /**
390         * @see #getMemberViewabilityLevel()
391         */
392        private String memberViewabilityLevel;
393
394        /**
395         * Constructs an empty Info object.
396         */
397        public Info() {
398            super();
399        }
400
401        /**
402         * Constructs an Info object by parsing information from a JSON string.
403         *
404         * @param json the JSON string to parse.
405         */
406        public Info(String json) {
407            super(json);
408        }
409
410        /**
411         * Constructs an Info object using an already parsed JSON object.
412         *
413         * @param jsonObject the parsed JSON object.
414         */
415        Info(JsonObject jsonObject) {
416            super(jsonObject);
417        }
418
419        /**
420         * {@inheritDoc}
421         */
422        @Override
423        public BoxGroup getResource() {
424            return BoxGroup.this;
425        }
426
427        /**
428         * {@inheritDoc}
429         */
430        @Override
431        protected void parseJSONMember(JsonObject.Member member) {
432            super.parseJSONMember(member);
433
434            String memberName = member.getName();
435            JsonValue value = member.getValue();
436            try {
437                switch (memberName) {
438                    case "description":
439                        this.description = value.asString();
440                        break;
441                    case "external_sync_identifier":
442                        this.externalSyncIdentifier = value.asString();
443                        break;
444                    case "invitability_level":
445                        this.invitabilityLevel = value.asString();
446                        break;
447                    case "member_viewability_level":
448                        this.memberViewabilityLevel = value.asString();
449                        break;
450                    case "provenance":
451                        this.provenance = value.asString();
452                        break;
453                    default:
454                        break;
455                }
456            } catch (Exception e) {
457                throw new BoxDeserializationException(memberName, value.toString(), e);
458            }
459        }
460
461        /**
462         * Gets the description for the group.
463         *
464         * @return the description for the group.
465         */
466        public String getDescription() {
467            return this.description;
468        }
469
470        /**
471         * Sets the description for the group.
472         *
473         * @param description the description for the group.
474         */
475        public void setDescription(String description) {
476            this.description = description;
477            addPendingChange("description", description);
478        }
479
480        /**
481         * Gets the external_sync_identifier for the group.
482         *
483         * @return the external_sync_identifier for the group.
484         */
485        public String getExternalSyncIdentifier() {
486            return this.externalSyncIdentifier;
487        }
488
489        /**
490         * Sets the external_sync_identifier for the group.
491         *
492         * @param externalSyncIdentifier the external_sync_identifier for the group.
493         */
494        public void setExternalSyncIdentifier(String externalSyncIdentifier) {
495            this.externalSyncIdentifier = externalSyncIdentifier;
496            addPendingChange("external_sync_identifier", externalSyncIdentifier);
497        }
498
499        /**
500         * Gets the invitability_level for the group.
501         *
502         * @return the invitability_level for the group.
503         */
504        public String getInvitabilityLevel() {
505            return this.invitabilityLevel;
506        }
507
508        /**
509         * Sets the invitability_level for the group.
510         *
511         * @param invitabilityLevel the invitability_level for the group.
512         */
513        public void setInvitabilityLevel(String invitabilityLevel) {
514            this.invitabilityLevel = invitabilityLevel;
515            addPendingChange("invitability_level", invitabilityLevel);
516        }
517
518        /**
519         * Gets the member_viewability_level for the group.
520         *
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         *
530         * @param memberViewabilityLevel the member_viewability_level for the group.
531         */
532        public void setMemberViewabilityLevel(String memberViewabilityLevel) {
533            this.memberViewabilityLevel = memberViewabilityLevel;
534            addPendingChange("member_viewability_level", memberViewabilityLevel);
535        }
536
537        /**
538         * Gets the provenance for the group.
539         *
540         * @return the provenance for the group.
541         */
542        public String getProvenance() {
543            return this.provenance;
544        }
545
546        /**
547         * Sets the provenance for the group.
548         *
549         * @param provenance the provenance for the group.
550         */
551        public void setProvenance(String provenance) {
552            this.provenance = provenance;
553            addPendingChange("provenance", provenance);
554        }
555    }
556}