diff --git a/src/main/java/com/crowdin/client/teams/TeamsApi.java b/src/main/java/com/crowdin/client/teams/TeamsApi.java index 74d0584c8..6cd4802c6 100644 --- a/src/main/java/com/crowdin/client/teams/TeamsApi.java +++ b/src/main/java/com/crowdin/client/teams/TeamsApi.java @@ -6,6 +6,8 @@ import com.crowdin.client.core.http.exceptions.HttpException; import com.crowdin.client.core.model.*; import com.crowdin.client.teams.model.*; +import com.crowdin.client.users.model.ProjectPermissions; +import com.crowdin.client.users.model.ProjectPermissionsResponseList; import java.util.List; import java.util.Map; @@ -52,7 +54,9 @@ public ResponseList listGroupTeams(ListGroupTeamsParams params) throw } Map> queryParams = HttpRequestConfig.buildUrlParams( - "orderBy", Optional.ofNullable(OrderByField.generateSortParam(params.getOrderByList())) + "orderBy", Optional.ofNullable(OrderByField.generateSortParam(params.getOrderByList())), + "limit", Optional.ofNullable(params.getLimit()), + "offset", Optional.ofNullable(params.getOffset()) ); GroupTeamResponseList response = this.httpClient.get(this.url + "/groups/" + params.getGroupId() + "/teams", new HttpRequestConfig(queryParams), GroupTeamResponseList.class); @@ -99,6 +103,59 @@ public ProjectTeamResources addTeamToProject(Long projectId, AddTeamToProjectReq return this.httpClient.post(this.url + "/projects/" + projectId + "/teams", request, new HttpRequestConfig(), ProjectTeamResources.class); } + /** + * List project teams. For Crowdin Enterprise only + * @param projectId project identifier + * @param params query parameters (orderBy, limit, offset), may be null + * @return list of project teams + * @see + */ + public ResponseList listProjectTeams(Long projectId, ListProjectTeamsParams params) throws HttpException, HttpBadRequestException { + ListProjectTeamsParams query = Optional.ofNullable(params).orElse(new ListProjectTeamsParams()); + Map> queryParams = HttpRequestConfig.buildUrlParams( + "orderBy", Optional.ofNullable(OrderByField.generateSortParam(query.getOrderBy())), + "limit", Optional.ofNullable(query.getLimit()), + "offset", Optional.ofNullable(query.getOffset()) + ); + ProjectTeamResourceResponseList response = this.httpClient.get(this.url + "/projects/" + projectId + "/teams", new HttpRequestConfig(queryParams), ProjectTeamResourceResponseList.class); + return ProjectTeamResourceResponseList.to(response); + } + + /** + * List team project permissions. For Crowdin Enterprise only + * @param teamId team identifier + * @param limit maximum number of items to retrieve (default 25) + * @param offset starting offset in the collection (default 0) + * @return list of team project permissions + * @see + */ + public ResponseList listTeamProjectPermissions(Long teamId, Integer limit, Integer offset) throws HttpException, HttpBadRequestException { + Map> queryParams = HttpRequestConfig.buildUrlParams( + "limit", Optional.ofNullable(limit), + "offset", Optional.ofNullable(offset) + ); + ProjectPermissionsResponseList response = this.httpClient.get(this.url + "/teams/" + teamId + "/projects/permissions", new HttpRequestConfig(queryParams), ProjectPermissionsResponseList.class); + return ProjectPermissionsResponseList.to(response); + } + + /** + * Edit team project permissions. For Crowdin Enterprise only + * @param teamId team identifier + * @param request request object (path format: /{projectId}/roles) + * @return list of updated team project permissions + * @see + */ + public ResponseList editTeamProjectPermissions(Long teamId, List request) throws HttpException, HttpBadRequestException { + ProjectPermissionsResponseList response = this.httpClient.patch(this.url + "/teams/" + teamId + "/projects/permissions", request, new HttpRequestConfig(), ProjectPermissionsResponseList.class); + return ProjectPermissionsResponseList.to(response); + } + /** * @param limit maximum number of items to retrieve (default 25) * @param offset starting offset in the collection (default 0) diff --git a/src/main/java/com/crowdin/client/teams/model/AddTeamToProjectRequest.java b/src/main/java/com/crowdin/client/teams/model/AddTeamToProjectRequest.java index fcc92506c..93007030c 100644 --- a/src/main/java/com/crowdin/client/teams/model/AddTeamToProjectRequest.java +++ b/src/main/java/com/crowdin/client/teams/model/AddTeamToProjectRequest.java @@ -10,14 +10,16 @@ public class AddTeamToProjectRequest { private Long teamId; /** - * @deprecated + * @deprecated deprecated in the API, use {@link #roles} instead */ + @Deprecated private Boolean accessToAllWorkflowSteps; private Boolean managerAccess; private Boolean developerAccess; /** - * @deprecated + * @deprecated deprecated in the API, use {@link #roles} instead */ + @Deprecated private Object permissions; private List roles; } diff --git a/src/main/java/com/crowdin/client/teams/model/ListGroupTeamsParams.java b/src/main/java/com/crowdin/client/teams/model/ListGroupTeamsParams.java index 8153eac59..528f0abc2 100644 --- a/src/main/java/com/crowdin/client/teams/model/ListGroupTeamsParams.java +++ b/src/main/java/com/crowdin/client/teams/model/ListGroupTeamsParams.java @@ -13,4 +13,6 @@ public class ListGroupTeamsParams { @NonNull private final Long groupId; private List orderByList; + private Integer limit; + private Integer offset; } diff --git a/src/main/java/com/crowdin/client/teams/model/ListProjectTeamsParams.java b/src/main/java/com/crowdin/client/teams/model/ListProjectTeamsParams.java new file mode 100644 index 000000000..5109b9cf0 --- /dev/null +++ b/src/main/java/com/crowdin/client/teams/model/ListProjectTeamsParams.java @@ -0,0 +1,14 @@ +package com.crowdin.client.teams.model; + +import com.crowdin.client.core.model.OrderByField; +import com.crowdin.client.core.model.Pagination; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class ListProjectTeamsParams extends Pagination { + private List orderBy; +} diff --git a/src/main/java/com/crowdin/client/teams/model/ProjectTeamResource.java b/src/main/java/com/crowdin/client/teams/model/ProjectTeamResource.java index 55d9e38a4..a7a6ba460 100644 --- a/src/main/java/com/crowdin/client/teams/model/ProjectTeamResource.java +++ b/src/main/java/com/crowdin/client/teams/model/ProjectTeamResource.java @@ -9,6 +9,7 @@ public class ProjectTeamResource { private Long id; + private String name; private boolean hasManagerAccess; private boolean hasDeveloperAccess; /** diff --git a/src/main/java/com/crowdin/client/teams/model/ProjectTeamResourceResponseList.java b/src/main/java/com/crowdin/client/teams/model/ProjectTeamResourceResponseList.java new file mode 100644 index 000000000..211e68d71 --- /dev/null +++ b/src/main/java/com/crowdin/client/teams/model/ProjectTeamResourceResponseList.java @@ -0,0 +1,26 @@ +package com.crowdin.client.teams.model; + +import com.crowdin.client.core.model.Pagination; +import com.crowdin.client.core.model.ResponseList; +import com.crowdin.client.core.model.ResponseObject; +import lombok.Data; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +public class ProjectTeamResourceResponseList { + + private List data; + private Pagination pagination; + + public static ResponseList to(ProjectTeamResourceResponseList projectTeamResourceResponseList) { + return ResponseList.of( + projectTeamResourceResponseList.getData().stream() + .map(ProjectTeamResourceResponseObject::getData) + .map(ResponseObject::of) + .collect(Collectors.toList()), + projectTeamResourceResponseList.getPagination() + ); + } +} diff --git a/src/main/java/com/crowdin/client/teams/model/ProjectTeamResourceResponseObject.java b/src/main/java/com/crowdin/client/teams/model/ProjectTeamResourceResponseObject.java new file mode 100644 index 000000000..c832793cd --- /dev/null +++ b/src/main/java/com/crowdin/client/teams/model/ProjectTeamResourceResponseObject.java @@ -0,0 +1,9 @@ +package com.crowdin.client.teams.model; + +import lombok.Data; + +@Data +public class ProjectTeamResourceResponseObject { + + private ProjectTeamResource data; +} diff --git a/src/main/java/com/crowdin/client/teams/model/ProjectTeamResources.java b/src/main/java/com/crowdin/client/teams/model/ProjectTeamResources.java index 0bac133a2..198f152a7 100644 --- a/src/main/java/com/crowdin/client/teams/model/ProjectTeamResources.java +++ b/src/main/java/com/crowdin/client/teams/model/ProjectTeamResources.java @@ -4,6 +4,8 @@ @Data public class ProjectTeamResources { + private ProjectTeamResource skipped; + private ProjectTeamResource updated; private ProjectTeamResource added; } diff --git a/src/main/java/com/crowdin/client/teams/model/Team.java b/src/main/java/com/crowdin/client/teams/model/Team.java index ee9559d59..0d4f58de4 100644 --- a/src/main/java/com/crowdin/client/teams/model/Team.java +++ b/src/main/java/com/crowdin/client/teams/model/Team.java @@ -10,6 +10,7 @@ public class Team { private Long id; private String name; private Integer totalMembers; + private String webUrl; private Date createdAt; private Date updatedAt; } diff --git a/src/main/java/com/crowdin/client/users/UsersApi.java b/src/main/java/com/crowdin/client/users/UsersApi.java index 9854e1305..a3e8cbbc4 100644 --- a/src/main/java/com/crowdin/client/users/UsersApi.java +++ b/src/main/java/com/crowdin/client/users/UsersApi.java @@ -54,7 +54,9 @@ public ResponseList listGroupManagers(Long groupId, ListGroupManag Map> queryParams = HttpRequestConfig.buildUrlParams( "teamIds", Optional.ofNullable(query.getTeamIds()), - "orderBy", Optional.ofNullable(OrderByField.generateSortParam(query.getOrderBy())) + "orderBy", Optional.ofNullable(OrderByField.generateSortParam(query.getOrderBy())), + "limit", Optional.ofNullable(query.getLimit()), + "offset", Optional.ofNullable(query.getOffset()) ); GroupManagerResponseList response = this.httpClient.get(this.url + "/groups/" + groupId + "/managers", new HttpRequestConfig(queryParams), GroupManagerResponseList.class); return GroupManagerResponseList.to(response); @@ -147,6 +149,7 @@ public ResponseList listProjectMembersEnterprise(Long projectId, * @param request request object * @return project team members response info * @see */ @@ -159,6 +162,7 @@ public ProjectMembersResponse addProjectMember(Long projectId, AddProjectMemberR * @param memberId member identifier * @return project member info * @see */ @@ -175,6 +179,7 @@ public ResponseObject getProjectMemberPermissions(Long projectId, * @param request request object * @return updated project member info * @see */ @@ -188,6 +193,7 @@ public ResponseObject replaceProjectMemberPermissions(Long projec * @param projectId project identifier * @param memberId member identifier * @see */ @@ -351,6 +357,71 @@ public ResponseObject getAuthenticatedUser() throws HttpException, HttpBad return ResponseObject.of(languageResponseObject.getData()); } + /** + * Edit authenticated user. For Crowdin only + * @param request request object (supported paths: /username, /fullName, /timezone, /avatarStorageId) + * @return updated authenticated user + * @see + */ + public ResponseObject editAuthenticatedUser(List request) throws HttpException, HttpBadRequestException { + UserResponseObject userResponseObject = this.httpClient.patch(this.url + "/user", request, new HttpRequestConfig(), UserResponseObject.class); + return ResponseObject.of(userResponseObject.getData()); + } + + /** + * List user project permissions. For Crowdin Enterprise only + * @param userId user identifier + * @param limit maximum number of items to retrieve (default 25) + * @param offset starting offset in the collection (default 0) + * @return list of user project permissions + * @see + */ + public ResponseList listUserProjectPermissions(Long userId, Integer limit, Integer offset) throws HttpException, HttpBadRequestException { + Map> queryParams = HttpRequestConfig.buildUrlParams( + "limit", Optional.ofNullable(limit), + "offset", Optional.ofNullable(offset) + ); + ProjectPermissionsResponseList response = this.httpClient.get(this.url + "/users/" + userId + "/projects/permissions", new HttpRequestConfig(queryParams), ProjectPermissionsResponseList.class); + return ProjectPermissionsResponseList.to(response); + } + + /** + * Edit user project permissions. For Crowdin Enterprise only + * @param userId user identifier + * @param request request object (path format: /{projectId}/roles) + * @return list of updated user project permissions + * @see + */ + public ResponseList editUserProjectPermissions(Long userId, List request) throws HttpException, HttpBadRequestException { + ProjectPermissionsResponseList response = this.httpClient.patch(this.url + "/users/" + userId + "/projects/permissions", request, new HttpRequestConfig(), ProjectPermissionsResponseList.class); + return ProjectPermissionsResponseList.to(response); + } + + /** + * List user project contributions. For Crowdin Enterprise only + * @param userId user identifier + * @param limit maximum number of items to retrieve (default 25) + * @param offset starting offset in the collection (default 0) + * @return list of user project contributions + * @see + */ + public ResponseList listUserProjectContributions(Long userId, Integer limit, Integer offset) throws HttpException, HttpBadRequestException { + Map> queryParams = HttpRequestConfig.buildUrlParams( + "limit", Optional.ofNullable(limit), + "offset", Optional.ofNullable(offset) + ); + UserProjectContributionResponseList response = this.httpClient.get(this.url + "/users/" + userId + "/projects/contributions", new HttpRequestConfig(queryParams), UserProjectContributionResponseList.class); + return UserProjectContributionResponseList.to(response); + } + /** * @param projectId project identifier * @param search search users by firstName, lastName, username, or email (2 or more characters) @@ -393,6 +464,29 @@ public ResponseList listProjectMembers(Long projectId, String search return TeamMemberResponseList.to(teamMemberResponseList); } + /** + * List project members. For Crowdin only + * @param projectId project identifier + * @param params query parameters (search, role, languageId, orderBy, limit, offset) + * @return list of team members + * @see + */ + public ResponseList listProjectMembers(Long projectId, ListProjectMembersParams params) throws HttpException, HttpBadRequestException { + ListProjectMembersParams query = Optional.ofNullable(params).orElse(new ListProjectMembersParams()); + Map> queryParams = HttpRequestConfig.buildUrlParams( + "search", Optional.ofNullable(query.getSearch()), + "role", Optional.ofNullable(query.getRole()), + "languageId", Optional.ofNullable(query.getLanguageId()), + "orderBy", Optional.ofNullable(OrderByField.generateSortParam(query.getOrderBy())), + "limit", Optional.ofNullable(query.getLimit()), + "offset", Optional.ofNullable(query.getOffset()) + ); + TeamMemberResponseList teamMemberResponseList = this.httpClient.get(this.url + "/projects/" + projectId + "/members", new HttpRequestConfig(queryParams), TeamMemberResponseList.class); + return TeamMemberResponseList.to(teamMemberResponseList); + } + /** * @param projectId project identifier * @param memberId member identifier diff --git a/src/main/java/com/crowdin/client/users/model/AddProjectMemberRequest.java b/src/main/java/com/crowdin/client/users/model/AddProjectMemberRequest.java index b19496c9f..583a8b557 100644 --- a/src/main/java/com/crowdin/client/users/model/AddProjectMemberRequest.java +++ b/src/main/java/com/crowdin/client/users/model/AddProjectMemberRequest.java @@ -1,7 +1,10 @@ package com.crowdin.client.users.model; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -9,17 +12,60 @@ public class AddProjectMemberRequest { private List userIds; + /** + * @deprecated the API expects an array of usernames, use {@link #usernameList} instead. + * The legacy setter splits a comma-separated string into {@link #usernameList}. + */ + @Deprecated + @JsonIgnore private String usernames; + @JsonProperty("usernames") + private List usernameList; private List emails; /** - * @deprecated + * @deprecated deprecated in the API, use {@link #roles} instead */ + @Deprecated private Boolean accessToAllWorkflowSteps; private Boolean managerAccess; private Boolean developerAccess; /** - * @deprecated + * @deprecated deprecated in the API, use {@link #roles} instead */ + @Deprecated private Map permissions; private List roles; + /** + * Crowdin only. Invitation message + */ + private String message; + + /** + * @return usernames joined with a comma + * @deprecated use {@link #usernameList} instead + */ + @Deprecated + public String getUsernames() { + return this.usernameList == null ? null : String.join(",", this.usernameList); + } + + /** + * @param usernames comma-separated usernames + * @deprecated use {@link #usernameList} instead + */ + @Deprecated + public void setUsernames(String usernames) { + if (usernames == null) { + this.usernameList = null; + return; + } + List list = new ArrayList<>(); + for (String username : usernames.split(",")) { + String trimmed = username.trim(); + if (!trimmed.isEmpty()) { + list.add(trimmed); + } + } + this.usernameList = list; + } } diff --git a/src/main/java/com/crowdin/client/users/model/GroupManager.java b/src/main/java/com/crowdin/client/users/model/GroupManager.java index 37a2f96f7..d28d9deb4 100644 --- a/src/main/java/com/crowdin/client/users/model/GroupManager.java +++ b/src/main/java/com/crowdin/client/users/model/GroupManager.java @@ -1,13 +1,68 @@ package com.crowdin.client.users.model; import com.crowdin.client.teams.model.GroupTeam; +import com.crowdin.client.teams.model.Team; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import java.util.ArrayList; import java.util.List; @Data public class GroupManager { private Long id; private User user; + /** + * @deprecated the API returns plain team objects, use {@link #teamList} instead + */ + @Deprecated + @JsonIgnore private List teams; + @JsonProperty("teams") + private List teamList; + + /** + * @return teams wrapped into {@link GroupTeam} objects (id is the team id) + * @deprecated use {@link #teamList} instead + */ + @Deprecated + public List getTeams() { + if (this.teamList == null) { + return null; + } + List result = new ArrayList<>(); + for (Team team : this.teamList) { + GroupTeam groupTeam = new GroupTeam(); + groupTeam.setId(team == null ? null : team.getId()); + groupTeam.setTeam(team); + result.add(groupTeam); + } + return result; + } + + /** + * @param teams group teams + * @deprecated use {@link #teamList} instead + */ + @Deprecated + public void setTeams(List teams) { + if (teams == null) { + this.teamList = null; + return; + } + List result = new ArrayList<>(); + for (GroupTeam groupTeam : teams) { + if (groupTeam == null) { + result.add(null); + } else if (groupTeam.getTeam() != null) { + result.add(groupTeam.getTeam()); + } else { + Team team = new Team(); + team.setId(groupTeam.getId()); + result.add(team); + } + } + this.teamList = result; + } } diff --git a/src/main/java/com/crowdin/client/users/model/InviteUserRequest.java b/src/main/java/com/crowdin/client/users/model/InviteUserRequest.java index c954752a3..4be9d259d 100644 --- a/src/main/java/com/crowdin/client/users/model/InviteUserRequest.java +++ b/src/main/java/com/crowdin/client/users/model/InviteUserRequest.java @@ -9,4 +9,5 @@ public class InviteUserRequest { private String firstName; private String lastName; private String timezone; + private Boolean adminAccess; } diff --git a/src/main/java/com/crowdin/client/users/model/LanguagePermission.java b/src/main/java/com/crowdin/client/users/model/LanguagePermission.java index d50ab84e2..92a935353 100644 --- a/src/main/java/com/crowdin/client/users/model/LanguagePermission.java +++ b/src/main/java/com/crowdin/client/users/model/LanguagePermission.java @@ -1,8 +1,30 @@ package com.crowdin.client.users.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; @Data public class LanguagePermission { private Object workflowStepIds; + /** + * Crowdin only. In Crowdin responses a language permission is a plain role string + * (e.g. {@code translator}, {@code proofreader}, {@code denied}) instead of an object. + * Read-only, never sent in requests. + */ + @JsonIgnore + private String role; + + /** + * Creates a language permission from the Crowdin role string representation + * + * @param role role name + * @return language permission + */ + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static LanguagePermission fromRole(String role) { + LanguagePermission permission = new LanguagePermission(); + permission.setRole(role); + return permission; + } } diff --git a/src/main/java/com/crowdin/client/users/model/ListGroupManagersParams.java b/src/main/java/com/crowdin/client/users/model/ListGroupManagersParams.java index 32f78bdfd..277f7cef4 100644 --- a/src/main/java/com/crowdin/client/users/model/ListGroupManagersParams.java +++ b/src/main/java/com/crowdin/client/users/model/ListGroupManagersParams.java @@ -9,4 +9,6 @@ public class ListGroupManagersParams { private List teamIds; private List orderBy; + private Integer limit; + private Integer offset; } diff --git a/src/main/java/com/crowdin/client/users/model/ListProjectMembersParams.java b/src/main/java/com/crowdin/client/users/model/ListProjectMembersParams.java new file mode 100644 index 000000000..a77f76ec9 --- /dev/null +++ b/src/main/java/com/crowdin/client/users/model/ListProjectMembersParams.java @@ -0,0 +1,20 @@ +package com.crowdin.client.users.model; + +import com.crowdin.client.core.model.OrderByField; +import com.crowdin.client.core.model.Pagination; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * Query parameters of List Project Members (Crowdin) + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class ListProjectMembersParams extends Pagination { + private String search; + private String role; + private String languageId; + private List orderBy; +} diff --git a/src/main/java/com/crowdin/client/users/model/ProjectMember.java b/src/main/java/com/crowdin/client/users/model/ProjectMember.java index 70a9fbecf..da7292713 100644 --- a/src/main/java/com/crowdin/client/users/model/ProjectMember.java +++ b/src/main/java/com/crowdin/client/users/model/ProjectMember.java @@ -17,6 +17,10 @@ public class ProjectMember { private String avatarUrl; private boolean isManager; private boolean isDeveloper; + /** + * Enterprise only + */ + private Boolean isAdmin; private ManagerOfGroup managerOfGroup; /** * @deprecated @@ -27,6 +31,10 @@ public class ProjectMember { */ private Map permissions; private List roles; + /** + * @deprecated deprecated in the API, use {@link #roles} instead + */ + @Deprecated private String role; private String timezone; private Date joinedAt; diff --git a/src/main/java/com/crowdin/client/users/model/ProjectMembersResponse.java b/src/main/java/com/crowdin/client/users/model/ProjectMembersResponse.java index 855a692a9..664d8e769 100644 --- a/src/main/java/com/crowdin/client/users/model/ProjectMembersResponse.java +++ b/src/main/java/com/crowdin/client/users/model/ProjectMembersResponse.java @@ -9,6 +9,7 @@ public class ProjectMembersResponse { private List skipped; + private List updated; private List added; private Pagination pagination; } diff --git a/src/main/java/com/crowdin/client/users/model/ProjectPermissions.java b/src/main/java/com/crowdin/client/users/model/ProjectPermissions.java new file mode 100644 index 000000000..aec993cf2 --- /dev/null +++ b/src/main/java/com/crowdin/client/users/model/ProjectPermissions.java @@ -0,0 +1,24 @@ +package com.crowdin.client.users.model; + +import com.crowdin.client.projectsgroups.model.Project; +import com.crowdin.client.teams.model.Team; +import lombok.Data; + +import java.util.List; + +/** + * Project permissions of a user or a team. Shared by + * {@link com.crowdin.client.users.UsersApi#listUserProjectPermissions} and + * {@link com.crowdin.client.teams.TeamsApi#listTeamProjectPermissions} + */ +@Data +public class ProjectPermissions { + + private Long id; + private List roles; + private Project project; + /** + * Teams through which the user has access to the project. Returned only for user project permissions + */ + private List teams; +} diff --git a/src/main/java/com/crowdin/client/users/model/ProjectPermissionsResponseList.java b/src/main/java/com/crowdin/client/users/model/ProjectPermissionsResponseList.java new file mode 100644 index 000000000..0f0ca5032 --- /dev/null +++ b/src/main/java/com/crowdin/client/users/model/ProjectPermissionsResponseList.java @@ -0,0 +1,26 @@ +package com.crowdin.client.users.model; + +import com.crowdin.client.core.model.Pagination; +import com.crowdin.client.core.model.ResponseList; +import com.crowdin.client.core.model.ResponseObject; +import lombok.Data; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +public class ProjectPermissionsResponseList { + + private List data; + private Pagination pagination; + + public static ResponseList to(ProjectPermissionsResponseList projectPermissionsResponseList) { + return ResponseList.of( + projectPermissionsResponseList.getData().stream() + .map(ProjectPermissionsResponseObject::getData) + .map(ResponseObject::of) + .collect(Collectors.toList()), + projectPermissionsResponseList.getPagination() + ); + } +} diff --git a/src/main/java/com/crowdin/client/users/model/ProjectPermissionsResponseObject.java b/src/main/java/com/crowdin/client/users/model/ProjectPermissionsResponseObject.java new file mode 100644 index 000000000..7faae4d34 --- /dev/null +++ b/src/main/java/com/crowdin/client/users/model/ProjectPermissionsResponseObject.java @@ -0,0 +1,9 @@ +package com.crowdin.client.users.model; + +import lombok.Data; + +@Data +public class ProjectPermissionsResponseObject { + + private ProjectPermissions data; +} diff --git a/src/main/java/com/crowdin/client/users/model/ReplaceProjectMemberPermissionsRequest.java b/src/main/java/com/crowdin/client/users/model/ReplaceProjectMemberPermissionsRequest.java index 7235cd6eb..b1b0aa928 100644 --- a/src/main/java/com/crowdin/client/users/model/ReplaceProjectMemberPermissionsRequest.java +++ b/src/main/java/com/crowdin/client/users/model/ReplaceProjectMemberPermissionsRequest.java @@ -9,13 +9,16 @@ public class ReplaceProjectMemberPermissionsRequest { /** - * @deprecated + * @deprecated deprecated in the API, use {@link #roles} instead */ + @Deprecated private Boolean accessToAllWorkflowSteps; private Boolean managerAccess; + private Boolean developerAccess; /** - * @deprecated + * @deprecated deprecated in the API, use {@link #roles} instead */ + @Deprecated private Map permissions; private List roles; } diff --git a/src/main/java/com/crowdin/client/users/model/TeamMember.java b/src/main/java/com/crowdin/client/users/model/TeamMember.java index 32d3d9dbe..12ec27fb2 100644 --- a/src/main/java/com/crowdin/client/users/model/TeamMember.java +++ b/src/main/java/com/crowdin/client/users/model/TeamMember.java @@ -12,6 +12,10 @@ public class TeamMember { private Long id; private String username; private String fullName; + /** + * @deprecated deprecated in the API, use {@link #roles} instead + */ + @Deprecated private String role; /** * @deprecated diff --git a/src/main/java/com/crowdin/client/users/model/User.java b/src/main/java/com/crowdin/client/users/model/User.java index 26375625f..49d84c729 100644 --- a/src/main/java/com/crowdin/client/users/model/User.java +++ b/src/main/java/com/crowdin/client/users/model/User.java @@ -3,6 +3,8 @@ import lombok.Data; import java.util.Date; +import java.util.List; +import java.util.Map; @Data public class User { @@ -10,6 +12,11 @@ public class User { private Long id; private String username; private String email; + private Boolean emailVerified; + /** + * Crowdin only + */ + private String fullName; private String firstName; private String lastName; private Status status; @@ -19,4 +26,46 @@ public class User { private TwoFactor twoFactor; private boolean isAdmin; private String timezone; + /** + * Enterprise only. Keys are field slugs + */ + private Map fields; + /** + * Enterprise only + */ + private JoinDetails joinDetails; + /** + * Enterprise only. {@code enabled} or {@code disabled} + */ + private String deviceVerification; + /** + * Enterprise only + */ + private Integer trustedDevicesCount; + /** + * Enterprise only + */ + private Integer apiTokensCount; + /** + * Enterprise only + */ + private List loginMethods; + /** + * Enterprise only + */ + private List mfaMethods; + + @Data + public static class JoinDetails { + private String type; + private InvitedBy invitedBy; + } + + @Data + public static class InvitedBy { + private Long id; + private String username; + private String fullName; + private String avatarUrl; + } } diff --git a/src/main/java/com/crowdin/client/users/model/UserProjectContribution.java b/src/main/java/com/crowdin/client/users/model/UserProjectContribution.java new file mode 100644 index 000000000..d6f59a8ba --- /dev/null +++ b/src/main/java/com/crowdin/client/users/model/UserProjectContribution.java @@ -0,0 +1,21 @@ +package com.crowdin.client.users.model; + +import com.crowdin.client.projectsgroups.model.Project; +import lombok.Data; + +@Data +public class UserProjectContribution { + + private Long id; + private Contribution translated; + private Contribution approved; + private Contribution voted; + private Contribution commented; + private Project project; + + @Data + public static class Contribution { + private Long strings; + private Long words; + } +} diff --git a/src/main/java/com/crowdin/client/users/model/UserProjectContributionResponseList.java b/src/main/java/com/crowdin/client/users/model/UserProjectContributionResponseList.java new file mode 100644 index 000000000..c6dad2c00 --- /dev/null +++ b/src/main/java/com/crowdin/client/users/model/UserProjectContributionResponseList.java @@ -0,0 +1,26 @@ +package com.crowdin.client.users.model; + +import com.crowdin.client.core.model.Pagination; +import com.crowdin.client.core.model.ResponseList; +import com.crowdin.client.core.model.ResponseObject; +import lombok.Data; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +public class UserProjectContributionResponseList { + + private List data; + private Pagination pagination; + + public static ResponseList to(UserProjectContributionResponseList userProjectContributionResponseList) { + return ResponseList.of( + userProjectContributionResponseList.getData().stream() + .map(UserProjectContributionResponseObject::getData) + .map(ResponseObject::of) + .collect(Collectors.toList()), + userProjectContributionResponseList.getPagination() + ); + } +} diff --git a/src/main/java/com/crowdin/client/users/model/UserProjectContributionResponseObject.java b/src/main/java/com/crowdin/client/users/model/UserProjectContributionResponseObject.java new file mode 100644 index 000000000..229dce38d --- /dev/null +++ b/src/main/java/com/crowdin/client/users/model/UserProjectContributionResponseObject.java @@ -0,0 +1,9 @@ +package com.crowdin.client.users.model; + +import lombok.Data; + +@Data +public class UserProjectContributionResponseObject { + + private UserProjectContribution data; +} diff --git a/src/test/java/com/crowdin/client/teams/TeamsApiExtendedTest.java b/src/test/java/com/crowdin/client/teams/TeamsApiExtendedTest.java new file mode 100644 index 000000000..824696418 --- /dev/null +++ b/src/test/java/com/crowdin/client/teams/TeamsApiExtendedTest.java @@ -0,0 +1,138 @@ +package com.crowdin.client.teams; + +import com.crowdin.client.core.model.OrderByField; +import com.crowdin.client.core.model.PatchOperation; +import com.crowdin.client.core.model.PatchRequest; +import com.crowdin.client.core.model.ResponseList; +import com.crowdin.client.core.model.SortOrder; +import com.crowdin.client.framework.RequestMock; +import com.crowdin.client.framework.TestClient; +import com.crowdin.client.teams.model.*; +import com.crowdin.client.users.model.ProjectPermissions; +import com.crowdin.client.users.model.TranslatorRoleName; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.client.methods.HttpPost; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.*; + +public class TeamsApiExtendedTest extends TestClient { + + private final Long projectId = 12L; + private final Long project2Id = 13L; + private final Long teamId = 1L; + private final Long groupId = 27L; + + @Override + public List getMocks() { + return Arrays.asList( + RequestMock.build(this.url + "/teams/" + teamId, HttpGet.METHOD_NAME, "api/teams/team.json"), + RequestMock.build(this.url + "/projects/" + projectId + "/teams", HttpGet.METHOD_NAME, "api/teams/listProjectTeams.json", new HashMap() {{ + put("orderBy", "id%20desc"); + put("limit", 25); + put("offset", 0); + }}), + RequestMock.build(this.url + "/projects/" + project2Id + "/teams", HttpGet.METHOD_NAME, "api/teams/listProjectTeams.json"), + RequestMock.build(this.url + "/projects/" + projectId + "/teams", HttpPost.METHOD_NAME, "api/teams/addTeamToProjectManagerRequest.json", "api/teams/projectTeamResourcesUpdated.json"), + RequestMock.build(this.url + "/teams/" + teamId + "/projects/permissions", HttpGet.METHOD_NAME, "api/teams/teamProjectPermissions.json", new HashMap() {{ + put("limit", 25); + }}), + RequestMock.build(this.url + "/teams/" + teamId + "/projects/permissions", HttpPatch.METHOD_NAME, "api/teams/editTeamProjectPermissionsRequest.json", "api/teams/teamProjectPermissions.json"), + RequestMock.build(this.url + "/groups/" + groupId + "/teams", HttpGet.METHOD_NAME, "api/teams/listGroupTeams.json", new HashMap() {{ + put("limit", 10); + put("offset", 5); + }}) + ); + } + + @Test + public void getTeamWebUrlTest() { + Team team = this.getTeamsApi().getTeam(teamId).getData(); + assertEquals(teamId, team.getId()); + assertEquals("https://example.crowdin.com/u/teams/1", team.getWebUrl()); + } + + @Test + public void listProjectTeamsTest() { + OrderByField orderBy = new OrderByField(); + orderBy.setFieldName("id"); + orderBy.setOrderBy(SortOrder.DESC); + + ListProjectTeamsParams params = new ListProjectTeamsParams(); + params.setOrderBy(singletonList(orderBy)); + params.setLimit(25); + params.setOffset(0); + + ResponseList response = this.getTeamsApi().listProjectTeams(projectId, params); + assertEquals(1, response.getData().size()); + ProjectTeamResource team = response.getData().get(0).getData(); + assertEquals(teamId, team.getId()); + assertEquals("French", team.getName()); + assertTrue(team.isHasDeveloperAccess()); + assertEquals(TranslatorRoleName.TRANSLATOR, team.getRoles().get(0).getName()); + assertTrue(team.getRoles().get(0).getPermissions().getLanguagesAccess().get("it").isAllContent()); + } + + @Test + public void listProjectTeamsWithoutParamsTest() { + ResponseList response = this.getTeamsApi().listProjectTeams(project2Id, null); + assertEquals(1, response.getData().size()); + } + + @Test + public void addTeamToProjectUpdatedTest() { + AddTeamToProjectRequest request = new AddTeamToProjectRequest(); + request.setTeamId(teamId); + request.setManagerAccess(true); + + ProjectTeamResources response = this.getTeamsApi().addTeamToProject(projectId, request); + assertNotNull(response.getUpdated()); + assertEquals(teamId, response.getUpdated().getId()); + assertEquals("French", response.getUpdated().getName()); + assertTrue(response.getUpdated().isHasManagerAccess()); + } + + @Test + public void listTeamProjectPermissionsTest() { + ResponseList response = this.getTeamsApi().listTeamProjectPermissions(teamId, 25, null); + assertTeamProjectPermissions(response); + } + + @Test + public void editTeamProjectPermissionsTest() { + PatchRequest request = new PatchRequest(); + request.setOp(PatchOperation.REMOVE); + request.setPath("/" + projectId + "/roles"); + + ResponseList response = this.getTeamsApi().editTeamProjectPermissions(teamId, singletonList(request)); + assertTeamProjectPermissions(response); + } + + private void assertTeamProjectPermissions(ResponseList response) { + assertEquals(1, response.getData().size()); + ProjectPermissions permissions = response.getData().get(0).getData(); + assertEquals(projectId, permissions.getId()); + assertEquals(TranslatorRoleName.PROOFREADER, permissions.getRoles().get(0).getName()); + assertTrue(permissions.getRoles().get(0).getPermissions().isAllLanguages()); + assertEquals(projectId, permissions.getProject().getId()); + assertEquals("Knowledge Base", permissions.getProject().getName()); + assertNull(permissions.getTeams()); + } + + @Test + public void listGroupTeamsWithPaginationTest() { + ListGroupTeamsParams params = new ListGroupTeamsParams(groupId); + params.setLimit(10); + params.setOffset(5); + + ResponseList response = this.getTeamsApi().listGroupTeams(params); + assertEquals(1, response.getData().size()); + assertEquals(teamId, response.getData().get(0).getData().getTeam().getId()); + } +} diff --git a/src/test/java/com/crowdin/client/users/UsersApiExtendedTest.java b/src/test/java/com/crowdin/client/users/UsersApiExtendedTest.java new file mode 100644 index 000000000..709401fc5 --- /dev/null +++ b/src/test/java/com/crowdin/client/users/UsersApiExtendedTest.java @@ -0,0 +1,269 @@ +package com.crowdin.client.users; + +import com.crowdin.client.core.model.PatchOperation; +import com.crowdin.client.core.model.PatchRequest; +import com.crowdin.client.core.model.ResponseList; +import com.crowdin.client.core.model.ResponseObject; +import com.crowdin.client.framework.RequestMock; +import com.crowdin.client.framework.TestClient; +import com.crowdin.client.projectsgroups.model.Project; +import com.crowdin.client.teams.model.GroupTeam; +import com.crowdin.client.users.model.*; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.*; + +public class UsersApiExtendedTest extends TestClient { + + private final Long userId = 1L; + private final Long projectId = 8L; + private final Long memberId = 4L; + private final Long groupId = 27L; + + @Override + public List getMocks() { + return Arrays.asList( + RequestMock.build(this.url + "/users/" + userId, HttpGet.METHOD_NAME, "api/users/userEnterpriseDetails.json"), + RequestMock.build(this.url + "/user", HttpPatch.METHOD_NAME, "api/users/editAuthenticatedUserRequest.json", "api/users/authenticatedUserCrowdin.json"), + RequestMock.build(this.url + "/users", HttpPost.METHOD_NAME, "api/users/inviteUserAdminRequest.json", "api/users/userEnterpriseDetails.json"), + RequestMock.build(this.url + "/users/" + userId + "/projects/permissions", HttpGet.METHOD_NAME, "api/users/userProjectPermissions.json", new HashMap() {{ + put("limit", 25); + put("offset", 0); + }}), + RequestMock.build(this.url + "/users/" + userId + "/projects/permissions", HttpPatch.METHOD_NAME, "api/users/editProjectPermissionsRequest.json", "api/users/userProjectPermissions.json"), + RequestMock.build(this.url + "/users/" + userId + "/projects/contributions", HttpGet.METHOD_NAME, "api/users/userProjectContributions.json", new HashMap() {{ + put("limit", 10); + }}), + RequestMock.build(this.url + "/projects/" + projectId + "/members", HttpPost.METHOD_NAME, "api/users/addProjectMemberCrowdinRequest.json", "api/users/addProjectMemberCrowdinResponse.json"), + RequestMock.build(this.url + "/projects/" + projectId + "/members", HttpGet.METHOD_NAME, "api/users/listProjectMembers.json", new HashMap() {{ + put("search", "john"); + put("role", "translator"); + put("languageId", "uk"); + put("orderBy", "id%20desc"); + put("limit", 25); + put("offset", 0); + }}), + RequestMock.build(this.url + "/projects/" + projectId + "/members/" + memberId, HttpPut.METHOD_NAME, "api/users/replaceProjectMemberPermissionsDeveloperRequest.json", "api/users/replaceProjectMemberPermissionsResponse.json"), + RequestMock.build(this.url + "/projects/" + projectId + "/members/" + memberId, HttpGet.METHOD_NAME, "api/users/projectMemberEnterpriseAdmin.json"), + RequestMock.build(this.url + "/groups/" + groupId + "/managers", HttpGet.METHOD_NAME, "api/users/listGroupManagers.json", new HashMap() {{ + put("limit", 25); + put("offset", 0); + }}) + ); + } + + @Test + public void getUserEnterpriseDetailsTest() { + User user = this.getUsersApi().getUser(userId).getData(); + assertEquals(userId, user.getId()); + assertTrue(user.getEmailVerified()); + assertEquals("some value 1", user.getFields().get("some-field-1")); + assertEquals(12, user.getFields().get("some-field-2")); + assertEquals("invite", user.getJoinDetails().getType()); + assertEquals(2L, user.getJoinDetails().getInvitedBy().getId()); + assertEquals("jane_doe", user.getJoinDetails().getInvitedBy().getUsername()); + assertEquals("Jane Doe", user.getJoinDetails().getInvitedBy().getFullName()); + assertEquals("https://example.com/avatar.png", user.getJoinDetails().getInvitedBy().getAvatarUrl()); + assertEquals("enabled", user.getDeviceVerification()); + assertEquals(2, user.getTrustedDevicesCount()); + assertEquals(3, user.getApiTokensCount()); + assertEquals(Arrays.asList("password", "google"), user.getLoginMethods()); + assertEquals(singletonList("totp"), user.getMfaMethods()); + } + + @Test + public void editAuthenticatedUserTest() { + PatchRequest request = new PatchRequest(); + request.setOp(PatchOperation.REPLACE); + request.setPath("/fullName"); + request.setValue("John Smith"); + + ResponseObject response = this.getUsersApi().editAuthenticatedUser(singletonList(request)); + assertEquals(userId, response.getData().getId()); + assertEquals("John Smith", response.getData().getFullName()); + assertTrue(response.getData().getEmailVerified()); + } + + @Test + public void inviteUserWithAdminAccessTest() { + InviteUserRequest request = new InviteUserRequest(); + request.setEmail("john@example.com"); + request.setFirstName("John"); + request.setLastName("Smith"); + request.setTimezone("Europe/Kyiv"); + request.setAdminAccess(true); + + ResponseObject response = this.getUsersApi().inviteUser(request); + assertEquals(userId, response.getData().getId()); + } + + @Test + public void listUserProjectPermissionsTest() { + ResponseList response = this.getUsersApi().listUserProjectPermissions(userId, 25, 0); + assertEquals(1, response.getData().size()); + assertProjectPermissions(response.getData().get(0).getData()); + } + + @Test + public void editUserProjectPermissionsTest() { + Map permissions = new HashMap<>(); + permissions.put("allLanguages", true); + Map role = new HashMap<>(); + role.put("name", "translator"); + role.put("permissions", permissions); + + PatchRequest request = new PatchRequest(); + request.setOp(PatchOperation.REPLACE); + request.setPath("/" + projectId + "/roles"); + request.setValue(singletonList(role)); + + ResponseList response = this.getUsersApi().editUserProjectPermissions(userId, singletonList(request)); + assertEquals(1, response.getData().size()); + assertProjectPermissions(response.getData().get(0).getData()); + } + + private void assertProjectPermissions(ProjectPermissions permissions) { + assertEquals(projectId, permissions.getId()); + assertEquals(TranslatorRoleName.TRANSLATOR, permissions.getRoles().get(0).getName()); + assertEquals(singletonList(882), permissions.getRoles().get(0).getPermissions().getLanguagesAccess().get("it").getWorkflowStepIds()); + Project project = permissions.getProject(); + assertEquals(projectId, project.getId()); + assertEquals(4L, project.getGroupId()); + assertEquals(1, permissions.getTeams().size()); + assertEquals(2L, permissions.getTeams().get(0).getId()); + assertEquals("https://example.crowdin.com/u/teams/2", permissions.getTeams().get(0).getWebUrl()); + } + + @Test + public void listUserProjectContributionsTest() { + ResponseList response = this.getUsersApi().listUserProjectContributions(userId, 10, null); + assertEquals(1, response.getData().size()); + UserProjectContribution contribution = response.getData().get(0).getData(); + assertEquals(projectId, contribution.getId()); + assertEquals(1L, contribution.getTranslated().getStrings()); + assertEquals(2L, contribution.getTranslated().getWords()); + assertEquals(3L, contribution.getApproved().getStrings()); + assertEquals(4L, contribution.getApproved().getWords()); + assertEquals(5L, contribution.getVoted().getStrings()); + assertNull(contribution.getVoted().getWords()); + assertEquals(6L, contribution.getCommented().getStrings()); + assertEquals(projectId, contribution.getProject().getId()); + assertEquals("Knowledge Base", contribution.getProject().getName()); + } + + @Test + public void addProjectMemberCrowdinTest() { + AddProjectMemberRequest request = new AddProjectMemberRequest(); + // legacy comma-separated setter must produce a JSON array on the wire + request.setUsernames("john_smith, jane_doe"); + request.setManagerAccess(false); + request.setDeveloperAccess(true); + request.setMessage("Welcome to the project"); + + assertEquals(Arrays.asList("john_smith", "jane_doe"), request.getUsernameList()); + assertEquals("john_smith,jane_doe", request.getUsernames()); + + ProjectMembersResponse response = this.getUsersApi().addProjectMember(projectId, request); + assertEquals(1, response.getAdded().size()); + assertEquals(userId, response.getAdded().get(0).getData().getId()); + assertEquals(1, response.getUpdated().size()); + assertEquals(3L, response.getUpdated().get(0).getData().getId()); + assertEquals("proofreader", response.getUpdated().get(0).getData().getPermissions().get("uk").getRole()); + + ProjectMember skipped = response.getSkipped().get(0).getData(); + assertEquals("translator", skipped.getRole()); + assertEquals("translator", skipped.getPermissions().get("uk").getRole()); + assertEquals("proofreader", skipped.getPermissions().get("it").getRole()); + assertEquals("denied", skipped.getPermissions().get("en").getRole()); + assertNull(skipped.getPermissions().get("uk").getWorkflowStepIds()); + assertTrue(skipped.getRoles().get(0).getPermissions().isAllLanguages()); + } + + @Test + public void addProjectMemberWithUsernameListTest() { + AddProjectMemberRequest request = new AddProjectMemberRequest(); + request.setUsernameList(new ArrayList<>(Arrays.asList("john_smith", "jane_doe"))); + request.setManagerAccess(false); + request.setDeveloperAccess(true); + request.setMessage("Welcome to the project"); + + ProjectMembersResponse response = this.getUsersApi().addProjectMember(projectId, request); + assertEquals(1, response.getAdded().size()); + } + + @Test + public void listProjectMembersWithParamsTest() { + com.crowdin.client.core.model.OrderByField orderBy = new com.crowdin.client.core.model.OrderByField(); + orderBy.setFieldName("id"); + orderBy.setOrderBy(com.crowdin.client.core.model.SortOrder.DESC); + + ListProjectMembersParams params = new ListProjectMembersParams(); + params.setSearch("john"); + params.setRole("translator"); + params.setLanguageId("uk"); + params.setOrderBy(singletonList(orderBy)); + params.setLimit(25); + params.setOffset(0); + + ResponseList response = this.getUsersApi().listProjectMembers(projectId, params); + assertEquals(1, response.getData().size()); + assertEquals(userId, response.getData().get(0).getData().getId()); + } + + @Test + public void replaceProjectMemberPermissionsWithDeveloperAccessTest() { + ReplaceProjectMemberPermissionsRequest request = new ReplaceProjectMemberPermissionsRequest(); + request.setManagerAccess(false); + request.setDeveloperAccess(true); + request.setRoles(Collections.singletonList(new TranslatorRole() {{ + setName(TranslatorRoleName.TRANSLATOR); + setPermissions(new TranslatorRolePermissions() {{ + setAllLanguages(true); + }}); + }})); + + ResponseObject response = this.getUsersApi().replaceProjectMemberPermissions(projectId, memberId, request); + assertNotNull(response.getData()); + } + + @Test + public void getProjectMemberIsAdminTest() { + ProjectMember member = this.getUsersApi().getProjectMemberPermissions(projectId, memberId).getData(); + assertEquals(userId, member.getId()); + assertTrue(member.getIsAdmin()); + assertTrue(member.isDeveloper()); + assertNull(member.getPermissions()); + } + + @Test + public void listGroupManagersWithPaginationTest() { + ListGroupManagersParams params = new ListGroupManagersParams(); + params.setLimit(25); + params.setOffset(0); + + ResponseList response = this.getUsersApi().listGroupManagers(groupId, params); + GroupManager manager = response.getData().get(0).getData(); + assertEquals(1, manager.getTeamList().size()); + assertEquals(2L, manager.getTeamList().get(0).getId()); + assertEquals("Translators Team", manager.getTeamList().get(0).getName()); + assertEquals(8, manager.getTeamList().get(0).getTotalMembers()); + + // deprecated accessor derives GroupTeam wrappers from the plain team objects + List legacyTeams = manager.getTeams(); + assertEquals(1, legacyTeams.size()); + assertEquals(2L, legacyTeams.get(0).getId()); + assertEquals("Translators Team", legacyTeams.get(0).getTeam().getName()); + } +} diff --git a/src/test/resources/api/teams/addTeamToProjectManagerRequest.json b/src/test/resources/api/teams/addTeamToProjectManagerRequest.json new file mode 100644 index 000000000..cc0daa6d2 --- /dev/null +++ b/src/test/resources/api/teams/addTeamToProjectManagerRequest.json @@ -0,0 +1,4 @@ +{ + "teamId": 1, + "managerAccess": true +} diff --git a/src/test/resources/api/teams/editTeamProjectPermissionsRequest.json b/src/test/resources/api/teams/editTeamProjectPermissionsRequest.json new file mode 100644 index 000000000..c43f0cb35 --- /dev/null +++ b/src/test/resources/api/teams/editTeamProjectPermissionsRequest.json @@ -0,0 +1,6 @@ +[ + { + "op": "remove", + "path": "/12/roles" + } +] diff --git a/src/test/resources/api/teams/listProjectTeams.json b/src/test/resources/api/teams/listProjectTeams.json new file mode 100644 index 000000000..699a5e861 --- /dev/null +++ b/src/test/resources/api/teams/listProjectTeams.json @@ -0,0 +1,37 @@ +{ + "data": [ + { + "data": { + "id": 1, + "name": "French", + "hasManagerAccess": false, + "hasDeveloperAccess": true, + "hasAccessToAllWorkflowSteps": false, + "permissions": { + "it": { + "workflowStepIds": [ + 313 + ] + } + }, + "roles": [ + { + "name": "translator", + "permissions": { + "allLanguages": false, + "languagesAccess": { + "it": { + "allContent": true + } + } + } + } + ] + } + } + ], + "pagination": { + "offset": 0, + "limit": 25 + } +} diff --git a/src/test/resources/api/teams/projectTeamResourcesUpdated.json b/src/test/resources/api/teams/projectTeamResourcesUpdated.json new file mode 100644 index 000000000..b8b541124 --- /dev/null +++ b/src/test/resources/api/teams/projectTeamResourcesUpdated.json @@ -0,0 +1,11 @@ +{ + "skipped": {}, + "updated": { + "id": 1, + "name": "French", + "hasManagerAccess": true, + "hasDeveloperAccess": false, + "roles": [] + }, + "added": {} +} diff --git a/src/test/resources/api/teams/team.json b/src/test/resources/api/teams/team.json index 356b91e2f..ba70ccfdf 100644 --- a/src/test/resources/api/teams/team.json +++ b/src/test/resources/api/teams/team.json @@ -3,6 +3,7 @@ "id": 1, "name": "French", "totalMembers": 8, + "webUrl": "https://example.crowdin.com/u/teams/1", "createdAt": "2019-09-23T09:04:29+00:00", "updatedAt": "2019-09-23T09:04:29+00:00" } diff --git a/src/test/resources/api/teams/teamProjectPermissions.json b/src/test/resources/api/teams/teamProjectPermissions.json new file mode 100644 index 000000000..239201590 --- /dev/null +++ b/src/test/resources/api/teams/teamProjectPermissions.json @@ -0,0 +1,36 @@ +{ + "data": [ + { + "data": { + "id": 12, + "roles": [ + { + "name": "proofreader", + "permissions": { + "allLanguages": true, + "languagesAccess": [] + } + } + ], + "project": { + "id": 12, + "groupId": 4, + "type": 0, + "userId": 6, + "sourceLanguageId": "en", + "targetLanguageIds": [ + "uk" + ], + "name": "Knowledge Base", + "identifier": "1f198a4e907688bc65834a6d5a6000c3", + "workflowId": 3, + "webUrl": "https://example.crowdin.com/u/projects/12" + } + } + } + ], + "pagination": { + "offset": 0, + "limit": 25 + } +} diff --git a/src/test/resources/api/users/addProjectMemberCrowdinRequest.json b/src/test/resources/api/users/addProjectMemberCrowdinRequest.json new file mode 100644 index 000000000..8d98624f8 --- /dev/null +++ b/src/test/resources/api/users/addProjectMemberCrowdinRequest.json @@ -0,0 +1,9 @@ +{ + "usernames": [ + "john_smith", + "jane_doe" + ], + "managerAccess": false, + "developerAccess": true, + "message": "Welcome to the project" +} diff --git a/src/test/resources/api/users/addProjectMemberCrowdinResponse.json b/src/test/resources/api/users/addProjectMemberCrowdinResponse.json new file mode 100644 index 000000000..d92162d62 --- /dev/null +++ b/src/test/resources/api/users/addProjectMemberCrowdinResponse.json @@ -0,0 +1,59 @@ +{ + "skipped": [ + { + "data": { + "id": 2, + "username": "jane_doe", + "fullName": "Jane Doe", + "role": "translator", + "permissions": { + "uk": "translator", + "it": "proofreader", + "en": "denied" + }, + "roles": [ + { + "name": "translator", + "permissions": { + "allLanguages": true, + "languagesAccess": [] + } + } + ], + "avatarUrl": "", + "joinedAt": "2023-09-20T11:34:40+00:00", + "timezone": "Europe/Kyiv" + } + } + ], + "updated": [ + { + "data": { + "id": 3, + "username": "bob", + "fullName": "Bob", + "permissions": { + "uk": "proofreader" + }, + "roles": [] + } + } + ], + "added": [ + { + "data": { + "id": 1, + "username": "john_smith", + "fullName": "John Smith", + "roles": [], + "avatarUrl": "", + "joinedAt": "2023-09-20T11:34:40+00:00", + "timezone": "Europe/Kyiv" + } + } + ], + "pagination": { + "offset": 0, + "limit": 25 + } +} diff --git a/src/test/resources/api/users/authenticatedUserCrowdin.json b/src/test/resources/api/users/authenticatedUserCrowdin.json new file mode 100644 index 000000000..501025453 --- /dev/null +++ b/src/test/resources/api/users/authenticatedUserCrowdin.json @@ -0,0 +1,14 @@ +{ + "data": { + "id": 1, + "username": "john_smith", + "email": "john@example.com", + "emailVerified": true, + "fullName": "John Smith", + "avatarUrl": "", + "createdAt": "2023-09-20T11:34:40+00:00", + "lastSeen": "2023-09-20T11:34:40+00:00", + "twoFactor": "enabled", + "timezone": "Europe/Kyiv" + } +} diff --git a/src/test/resources/api/users/editAuthenticatedUserRequest.json b/src/test/resources/api/users/editAuthenticatedUserRequest.json new file mode 100644 index 000000000..28c9b6ec8 --- /dev/null +++ b/src/test/resources/api/users/editAuthenticatedUserRequest.json @@ -0,0 +1,7 @@ +[ + { + "op": "replace", + "path": "/fullName", + "value": "John Smith" + } +] diff --git a/src/test/resources/api/users/editProjectPermissionsRequest.json b/src/test/resources/api/users/editProjectPermissionsRequest.json new file mode 100644 index 000000000..5253699c4 --- /dev/null +++ b/src/test/resources/api/users/editProjectPermissionsRequest.json @@ -0,0 +1,14 @@ +[ + { + "op": "replace", + "path": "/8/roles", + "value": [ + { + "name": "translator", + "permissions": { + "allLanguages": true + } + } + ] + } +] diff --git a/src/test/resources/api/users/inviteUserAdminRequest.json b/src/test/resources/api/users/inviteUserAdminRequest.json new file mode 100644 index 000000000..1efcc07e8 --- /dev/null +++ b/src/test/resources/api/users/inviteUserAdminRequest.json @@ -0,0 +1,7 @@ +{ + "email": "john@example.com", + "firstName": "John", + "lastName": "Smith", + "timezone": "Europe/Kyiv", + "adminAccess": true +} diff --git a/src/test/resources/api/users/projectMemberEnterpriseAdmin.json b/src/test/resources/api/users/projectMemberEnterpriseAdmin.json new file mode 100644 index 000000000..298794682 --- /dev/null +++ b/src/test/resources/api/users/projectMemberEnterpriseAdmin.json @@ -0,0 +1,16 @@ +{ + "data": { + "id": 1, + "username": "john_smith", + "firstName": "John", + "lastName": "Smith", + "isManager": false, + "isDeveloper": true, + "isAdmin": true, + "managerOfGroup": null, + "accessToAllWorkflowSteps": false, + "permissions": null, + "roles": [], + "givenAccessAt": "2023-09-20T11:34:40+00:00" + } +} diff --git a/src/test/resources/api/users/replaceProjectMemberPermissionsDeveloperRequest.json b/src/test/resources/api/users/replaceProjectMemberPermissionsDeveloperRequest.json new file mode 100644 index 000000000..ccf6d7136 --- /dev/null +++ b/src/test/resources/api/users/replaceProjectMemberPermissionsDeveloperRequest.json @@ -0,0 +1,12 @@ +{ + "managerAccess": false, + "developerAccess": true, + "roles": [ + { + "name": "translator", + "permissions": { + "allLanguages": true + } + } + ] +} diff --git a/src/test/resources/api/users/userEnterpriseDetails.json b/src/test/resources/api/users/userEnterpriseDetails.json new file mode 100644 index 000000000..83d815352 --- /dev/null +++ b/src/test/resources/api/users/userEnterpriseDetails.json @@ -0,0 +1,40 @@ +{ + "data": { + "id": 1, + "username": "john_smith", + "firstName": "John", + "lastName": "Smith", + "avatarUrl": "", + "fields": { + "some-field-1": "some value 1", + "some-field-2": 12 + }, + "isAdmin": true, + "status": "active", + "createdAt": "2023-09-20T11:34:40+00:00", + "lastSeen": "2023-09-20T11:34:40+00:00", + "email": "john@example.com", + "emailVerified": true, + "twoFactor": "enabled", + "timezone": "Europe/Kyiv", + "joinDetails": { + "type": "invite", + "invitedBy": { + "id": 2, + "username": "jane_doe", + "fullName": "Jane Doe", + "avatarUrl": "https://example.com/avatar.png" + } + }, + "deviceVerification": "enabled", + "trustedDevicesCount": 2, + "apiTokensCount": 3, + "loginMethods": [ + "password", + "google" + ], + "mfaMethods": [ + "totp" + ] + } +} diff --git a/src/test/resources/api/users/userProjectContributions.json b/src/test/resources/api/users/userProjectContributions.json new file mode 100644 index 000000000..a9fec1315 --- /dev/null +++ b/src/test/resources/api/users/userProjectContributions.json @@ -0,0 +1,41 @@ +{ + "data": [ + { + "data": { + "id": 8, + "translated": { + "strings": 1, + "words": 2 + }, + "approved": { + "strings": 3, + "words": 4 + }, + "voted": { + "strings": 5 + }, + "commented": { + "strings": 6 + }, + "project": { + "id": 8, + "groupId": 4, + "type": 0, + "userId": 6, + "sourceLanguageId": "en", + "targetLanguageIds": [ + "it" + ], + "name": "Knowledge Base", + "identifier": "1f198a4e907688bc65834a6d5a6000c3", + "workflowId": 3, + "webUrl": "https://example.crowdin.com/u/projects/8" + } + } + } + ], + "pagination": { + "offset": 0, + "limit": 10 + } +} diff --git a/src/test/resources/api/users/userProjectPermissions.json b/src/test/resources/api/users/userProjectPermissions.json new file mode 100644 index 000000000..a6856f241 --- /dev/null +++ b/src/test/resources/api/users/userProjectPermissions.json @@ -0,0 +1,67 @@ +{ + "data": [ + { + "data": { + "id": 8, + "roles": [ + { + "name": "translator", + "permissions": { + "allLanguages": false, + "languagesAccess": { + "it": { + "allContent": false, + "workflowStepIds": [ + 882 + ] + } + } + } + } + ], + "project": { + "id": 8, + "groupId": 4, + "type": 0, + "userId": 6, + "sourceLanguageId": "en", + "targetLanguageIds": [ + "it" + ], + "name": "Knowledge Base", + "identifier": "1f198a4e907688bc65834a6d5a6000c3", + "description": "Vault of all terms and their explanation", + "logo": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8", + "isExternal": false, + "externalType": null, + "workflowId": 3, + "hasCrowdsourcing": false, + "publicDownloads": true, + "createdAt": "2023-09-20T11:34:40+00:00", + "updatedAt": "2023-09-20T11:34:40+00:00", + "lastActivity": "2023-09-20T11:34:40+00:00", + "webUrl": "https://example.crowdin.com/u/projects/8", + "publicUrl": null, + "savingsReportSettingsTemplateId": 1, + "fields": { + "some-field-1": "some value 1" + } + }, + "teams": [ + { + "id": 2, + "name": "Translators Team", + "totalMembers": 8, + "webUrl": "https://example.crowdin.com/u/teams/2", + "createdAt": "2023-09-23T09:04:29+00:00", + "updatedAt": "2023-09-23T09:04:29+00:00" + } + ] + } + } + ], + "pagination": { + "offset": 0, + "limit": 25 + } +}