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