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}