Skip to content

Commit

Permalink
fix: BoxCollaboration.getItem() returns BoxItem.Info not `BoxFold…
Browse files Browse the repository at this point in the history
…er.Info` (#1102)

* fix: BoxCollaboration.getItem() should return BoxItem.Info not BoxFolder.Info
Fixes #1101

* fix: Added details on retrying failed requests.
Fixes #1100
  • Loading branch information
antusus authored Sep 20, 2022
1 parent 45e9906 commit 135850d
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 261 deletions.
12 changes: 11 additions & 1 deletion doc/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ api.setProxyPassword("proxyPassword");
```

# Configure retries of calls and timeouts
SDK can retry failed calls when:
- failed writting request body
- when recieved HTTP response code:
- 429 - rate limit exceeded
- 5XX - internal server error
- 400 error with error that `exp` claim has expired. This usially means there is a clock skew.

SDK is using exponnetial strategy to calculate time between retries.
If response contains `Retry-After` header its value will be used as a wait time between calls.
You can check details in `com.box.sdk.BoxAPIRequest.send(com.box.sdk.ProgressListener)` method.

## Maximum retries

Expand Down Expand Up @@ -140,4 +150,4 @@ BoxAPIConnection api = new BoxAPIConnection("YOUR-DEVELOPER-TOKEN");
api.setRevokeURL("https://example.com/revoke");
```

If you use `setRevokeUrl` this URL will be used over the one coming from`setBaseUrl` when doing authentication.
If you use `setRevokeUrl` this URL will be used over the one coming from`setBaseUrl` when doing authentication.
6 changes: 4 additions & 2 deletions doc/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ user.updateInfo(info);

## Delete User

To delete a user call the [`delete(boolean notifyUser, boolean force)`][delete] method.
To delete a user call the [`delete(boolean notifyUser, boolean force)`][deleteWithParams] method or one that
uses API default parameters [`delete()][delete]

The `notifyUser` determines whether the user should receive an email about the deletion,
and the `force` parameter will cause the user to be deleted even if they still have files
Expand All @@ -171,7 +172,8 @@ BoxUser user = new BoxUser(api, "0");
user.delete(false, false);
```

[delete]: https://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxUser.html#delete-boolean-boolean-
[deleteWithParams]: https://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxUser.html#delete-boolean-boolean-
[delete]: https://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxUser.html#delete--

## Invite User

Expand Down
25 changes: 22 additions & 3 deletions src/intTest/java/com/box/sdk/BoxUserIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void getCurrentUserInfoIsCorrect() {
}

@Test(timeout = 10000)
public void createAndDeleteEnterpriseUserSucceeds() {
public void createAndForcefullyDeleteEnterpriseUser() {
BoxAPIConnection api = jwtApiForServiceAccount();
// Since deleting users happens in a separate process in the backend
// it is really an asynchronous call. So we have to use a new user in
Expand All @@ -66,7 +66,26 @@ public void createAndDeleteEnterpriseUserSucceeds() {
assertEquals(NEW_USER_NAME, createdUserInfo.getName());
assertEquals(NEW_USER_LOGIN, createdUserInfo.getLogin());

createdUserInfo.getResource().delete(false, false);
createdUserInfo.getResource().delete(false, true);

Iterable<BoxUser.Info> users = BoxUser.getAllEnterpriseUsers(api, NEW_USER_LOGIN);
assertThat(createListFrom(users), Matchers.hasSize(0));
}

@Test(timeout = 10000)
public void createAndDeleteEnterpriseUser() {
BoxAPIConnection api = jwtApiForServiceAccount();
// Since deleting users happens in a separate process in the backend
// it is really an asynchronous call. So we have to use a new user in
// this test in case the previous user's deletion hasn't completed.

BoxUser.Info createdUserInfo = BoxUser.createEnterpriseUser(api, NEW_USER_LOGIN, NEW_USER_NAME);

assertNotNull(createdUserInfo.getID());
assertEquals(NEW_USER_NAME, createdUserInfo.getName());
assertEquals(NEW_USER_LOGIN, createdUserInfo.getLogin());

createdUserInfo.getResource().delete();

Iterable<BoxUser.Info> users = BoxUser.getAllEnterpriseUsers(api, NEW_USER_LOGIN);
assertThat(createListFrom(users), Matchers.hasSize(0));
Expand All @@ -92,7 +111,7 @@ public void getMembershipsHasCorrectMemberships() {
}

@Test(timeout = 10000)
public void updateInfoSucceeds() {
public void updateUserInfo() {
BoxAPIConnection api = jwtApiForServiceAccount();
final String login = "login3+" + Calendar.getInstance().getTimeInMillis() + "@boz.com";
final String originalName = "original name";
Expand Down
168 changes: 95 additions & 73 deletions src/main/java/com/box/sdk/BoxCollaboration.java
Original file line number Diff line number Diff line change
Expand Up @@ -331,24 +331,25 @@ public enum Role {
}

static Role fromJSONString(String jsonValue) {
if (jsonValue.equals("editor")) {
return EDITOR;
} else if (jsonValue.equals("viewer")) {
return VIEWER;
} else if (jsonValue.equals("previewer")) {
return PREVIEWER;
} else if (jsonValue.equals("uploader")) {
return UPLOADER;
} else if (jsonValue.equals("previewer uploader")) {
return PREVIEWER_UPLOADER;
} else if (jsonValue.equals("viewer uploader")) {
return VIEWER_UPLOADER;
} else if (jsonValue.equals("co-owner")) {
return CO_OWNER;
} else if (jsonValue.equals("owner")) {
return OWNER;
} else {
throw new IllegalArgumentException("The provided JSON value isn't a valid Role.");
switch (jsonValue) {
case "editor":
return EDITOR;
case "viewer":
return VIEWER;
case "previewer":
return PREVIEWER;
case "uploader":
return UPLOADER;
case "previewer uploader":
return PREVIEWER_UPLOADER;
case "viewer uploader":
return VIEWER_UPLOADER;
case "co-owner":
return CO_OWNER;
case "owner":
return OWNER;
default:
throw new IllegalArgumentException("The provided JSON value isn't a valid Role.");
}
}

Expand All @@ -360,7 +361,7 @@ String toJSONString() {
/**
* Contains information about a BoxCollaboration.
*/
public class Info extends BoxResource.Info {
public class Info extends BoxResource.Info {
private BoxUser.Info createdBy;
private Date createdAt;
private Date modifiedAt;
Expand All @@ -369,8 +370,7 @@ public class Info extends BoxResource.Info {
private BoxCollaborator.Info accessibleBy;
private Role role;
private Date acknowledgedAt;
private BoxFolder.Info item;
private BoxFile.Info fileItem;
private BoxItem.Info item;
private String inviteEmail;
private boolean canViewPath;

Expand Down Expand Up @@ -532,7 +532,7 @@ public Date getAcknowledgedAt() {
*
* @return the folder the collaboration is related to.
*/
public BoxFolder.Info getItem() {
public BoxItem.Info getItem() {
return this.item;
}

Expand All @@ -541,70 +541,92 @@ public BoxCollaboration getResource() {
return BoxCollaboration.this;
}

@SuppressWarnings("checkstyle:MissingSwitchDefault")
@Override
protected void parseJSONMember(JsonObject.Member member) {
super.parseJSONMember(member);

String memberName = member.getName();
JsonValue value = member.getValue();
try {
if (memberName.equals("created_by")) {
JsonObject userJSON = value.asObject();
if (this.createdBy == null) {
String userID = userJSON.get("id").asString();
BoxUser user = new BoxUser(getAPI(), userID);
this.createdBy = user.new Info(userJSON);
} else {
this.createdBy.update(userJSON);
}

} else if (memberName.equals("created_at")) {
this.createdAt = BoxDateFormat.parse(value.asString());

} else if (memberName.equals("modified_at")) {
this.modifiedAt = BoxDateFormat.parse(value.asString());

} else if (memberName.equals("expires_at")) {
this.expiresAt = BoxDateFormat.parse(value.asString());

} else if (memberName.equals("status")) {
String statusString = value.asString().toUpperCase();
this.status = Status.valueOf(statusString);

} else if (memberName.equals("accessible_by")) {
JsonObject accessibleByJSON = value.asObject();
if (this.accessibleBy == null) {
this.accessibleBy = this.parseAccessibleBy(accessibleByJSON);
} else {
this.updateAccessibleBy(accessibleByJSON);
}
} else if (memberName.equals("role")) {
this.role = Role.fromJSONString(value.asString());

} else if (memberName.equals("acknowledged_at")) {
this.acknowledgedAt = BoxDateFormat.parse(value.asString());

} else if (memberName.equals("can_view_path")) {
this.canViewPath = value.asBoolean();

} else if (memberName.equals("invite_email")) {
this.inviteEmail = value.asString();

} else if (memberName.equals("item")) {
JsonObject folderJSON = value.asObject();
if (this.item == null) {
String folderID = folderJSON.get("id").asString();
BoxFolder folder = new BoxFolder(getAPI(), folderID);
this.item = folder.new Info(folderJSON);
} else {
this.item.update(folderJSON);
}
switch (memberName) {
case "created_by":
JsonObject userJSON = value.asObject();
if (this.createdBy == null) {
String userID = userJSON.get("id").asString();
BoxUser user = new BoxUser(getAPI(), userID);
this.createdBy = user.new Info(userJSON);
} else {
this.createdBy.update(userJSON);
}
break;
case "created_at":
this.createdAt = BoxDateFormat.parse(value.asString());

break;
case "modified_at":
this.modifiedAt = BoxDateFormat.parse(value.asString());
break;
case "expires_at":
this.expiresAt = BoxDateFormat.parse(value.asString());
break;
case "status":
String statusString = value.asString().toUpperCase();
this.status = Status.valueOf(statusString);

break;
case "accessible_by":
JsonObject accessibleByJSON = value.asObject();
if (this.accessibleBy == null) {
this.accessibleBy = this.parseAccessibleBy(accessibleByJSON);
} else {
this.updateAccessibleBy(accessibleByJSON);
}
break;
case "role":
this.role = Role.fromJSONString(value.asString());
break;
case "acknowledged_at":
this.acknowledgedAt = BoxDateFormat.parse(value.asString());
break;
case "can_view_path":
this.canViewPath = value.asBoolean();
break;
case "invite_email":
this.inviteEmail = value.asString();
break;
case "item":
JsonObject itemJson = value.asObject();
if (this.item == null) {
this.item = selectCollaborationItem(itemJson);
} else {
this.item.update(itemJson);
}
break;
}
} catch (Exception e) {
throw new BoxDeserializationException(memberName, value.toString(), e);
}
}

private BoxItem.Info selectCollaborationItem(JsonObject itemJson) {
String itemId = itemJson.get("id").asString();
String itemType = itemJson.get("type").asString();
switch (itemType) {
case BoxFile.TYPE:
return new BoxFile(getAPI(), itemId).new Info(itemJson);
case BoxFolder.TYPE:
return new BoxFolder(getAPI(), itemId).new Info(itemJson);
default:
throw new IllegalStateException(
String.format(
"Unsupported collaboration item type '%s': JSON %n%s",
itemType,
itemJson
));
}
}

private void updateAccessibleBy(JsonObject json) {
String type = json.get("type").asString();
if ((type.equals("user") && this.accessibleBy instanceof BoxUser.Info)
Expand Down
Loading

0 comments on commit 135850d

Please sign in to comment.