From 8ba5cf7c2ea3627b9623c142f1e4c7824da6ebb6 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Sat, 25 Jul 2020 01:17:16 -0300 Subject: [PATCH 1/3] Formatting tweaks and fixes --- .../java/org/kohsuke/github/GHRepository.java | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index af704a921..50adf0685 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -68,7 +68,9 @@ public class GHRepository extends GHObject { /* package almost final */ transient GitHub root; private String nodeId, description, homepage, name, full_name; + private String html_url; // this is the UI + /* * The license information makes use of the preview API. * @@ -77,22 +79,30 @@ public class GHRepository extends GHObject { private GHLicense license; private String git_url, ssh_url, clone_url, svn_url, mirror_url; + private GHUser owner; // not fully populated. beware. + private boolean has_issues, has_wiki, fork, has_downloads, has_pages, archived, has_projects; private boolean allow_squash_merge; + private boolean allow_merge_commit; + private boolean allow_rebase_merge; private boolean delete_branch_on_merge; @JsonProperty("private") private boolean _private; + private int forks_count, stargazers_count, watchers_count, size, open_issues_count, subscribers_count; + private String pushed_at; + private Map milestones = new WeakHashMap(); private String default_branch, language; + private Map commits = new WeakHashMap(); @SkipFromToString @@ -1002,8 +1012,9 @@ public class GHRepository extends GHObject { private void edit(String key, String value) throws IOException { Requester requester = root.createRequest(); - if (!key.equals("name")) + if (!key.equals("name")) { requester.with("name", name); // even when we don't change the name, we need to send it in + } requester.with(key, value).method("PATCH").withUrlPath(getApiTailUrl("")).send(); } @@ -1248,8 +1259,9 @@ public class GHRepository extends GHObject { // this API is asynchronous. we need to wait for a bit for (int i = 0; i < 10; i++) { GHRepository r = root.getMyself().getRepository(name); - if (r != null) + if (r != null) { return r; + } try { Thread.sleep(3000); } catch (InterruptedException e) { @@ -1278,8 +1290,9 @@ public class GHRepository extends GHObject { // this API is asynchronous. we need to wait for a bit for (int i = 0; i < 10; i++) { GHRepository r = org.getRepository(name); - if (r != null) + if (r != null) { return r; + } try { Thread.sleep(3000); } catch (InterruptedException e) { @@ -2721,8 +2734,9 @@ public class GHRepository extends GHObject { } String getApiTailUrl(String tail) { - if (tail.length() > 0 && !tail.startsWith("/")) + if (tail.length() > 0 && !tail.startsWith("/")) { tail = '/' + tail; + } return "/repos/" + getOwnerName() + "/" + name + tail; } @@ -2829,8 +2843,9 @@ public class GHRepository extends GHObject { * The IO exception */ void populate() throws IOException { - if (root.isOffline()) + if (root.isOffline()) { return; // can't populate if the root is offline + } final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!"); From 9c7de767e948812f50f2a68ebaa92b837a97e7c7 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Sat, 25 Jul 2020 01:17:16 -0300 Subject: [PATCH 2/3] Fixes modifyCollaborators for multiple users Fixes #868 --- .../java/org/kohsuke/github/GHRepository.java | 14 +++--- .../org/kohsuke/github/GHRepositoryTest.java | 1 + .../__files/users_jimmysombrero2.json | 45 +++++++++++++++++ ...thub-api_collaborators_jimmysombrero2.json | 48 +++++++++++++++++++ .../mappings/users_jimmysombrero2-7.json | 45 +++++++++++++++++ 5 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/__files/users_jimmysombrero2.json create mode 100644 src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/mappings/repos_hub4j-test-org_github-api_collaborators_jimmysombrero2.json create mode 100644 src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/mappings/users_jimmysombrero2-7.json diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 50adf0685..8315b0dcd 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -46,6 +46,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -979,13 +980,12 @@ public class GHRepository extends GHObject { private void modifyCollaborators(@NonNull Collection users, @NonNull String method, @CheckForNull GHOrganization.Permission permission) throws IOException { - Requester requester = root.createRequest().method(method); - - if (permission != null) { - requester = requester.with("permission", permission).inBody(); - } - - for (GHUser user : users) { + // Make sure that the users collection doesn't have any duplicates + for (GHUser user : new LinkedHashSet(users)) { + Requester requester = root.createRequest().method(method); + if (permission != null) { + requester = requester.with("permission", permission).inBody(); + } requester.withUrlPath(getApiTailUrl("collaborators/" + user.getLogin())).send(); } } diff --git a/src/test/java/org/kohsuke/github/GHRepositoryTest.java b/src/test/java/org/kohsuke/github/GHRepositoryTest.java index b7df6d69f..2c579370d 100644 --- a/src/test/java/org/kohsuke/github/GHRepositoryTest.java +++ b/src/test/java/org/kohsuke/github/GHRepositoryTest.java @@ -191,6 +191,7 @@ public class GHRepositoryTest extends AbstractGitHubWireMockTest { List users = new ArrayList(); users.add(user); + users.add(gitHub.getUser("jimmysombrero2")); repo.addCollaborators(users, GHOrganization.Permission.PUSH); GHPersonSet collabs = repo.getCollaborators(); diff --git a/src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/__files/users_jimmysombrero2.json b/src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/__files/users_jimmysombrero2.json new file mode 100644 index 000000000..9d2d78fa7 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/__files/users_jimmysombrero2.json @@ -0,0 +1,45 @@ +{ + "login": "jimmysombrero2", + "id": 12157727, + "node_id": "MDQ6VXNlcjEyMTU3NzI3", + "avatar_url": "https://avatars3.githubusercontent.com/u/12157727?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/jimmysombrero", + "html_url": "https://github.com/jimmysombrero", + "followers_url": "https://api.github.com/users/jimmysombrero/followers", + "following_url": "https://api.github.com/users/jimmysombrero/following{/other_user}", + "gists_url": "https://api.github.com/users/jimmysombrero/gists{/gist_id}", + "starred_url": "https://api.github.com/users/jimmysombrero/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/jimmysombrero/subscriptions", + "organizations_url": "https://api.github.com/users/jimmysombrero/orgs", + "repos_url": "https://api.github.com/users/jimmysombrero/repos", + "events_url": "https://api.github.com/users/jimmysombrero/events{/privacy}", + "received_events_url": "https://api.github.com/users/jimmysombrero/received_events", + "type": "User", + "site_admin": false, + "name": null, + "company": null, + "blog": "", + "location": null, + "email": null, + "hireable": null, + "bio": null, + "public_repos": 4, + "public_gists": 0, + "followers": 1, + "following": 0, + "created_at": "2015-04-28T17:47:19Z", + "updated_at": "2020-02-02T04:43:58Z", + "private_gists": 0, + "total_private_repos": 0, + "owned_private_repos": 0, + "disk_usage": 19, + "collaborators": 0, + "two_factor_authentication": false, + "plan": { + "name": "free", + "space": 976562499, + "collaborators": 0, + "private_repos": 10000 + } +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/mappings/repos_hub4j-test-org_github-api_collaborators_jimmysombrero2.json b/src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/mappings/repos_hub4j-test-org_github-api_collaborators_jimmysombrero2.json new file mode 100644 index 000000000..a538fc44b --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/mappings/repos_hub4j-test-org_github-api_collaborators_jimmysombrero2.json @@ -0,0 +1,48 @@ +{ + "id": "3d80b19e-05f4-4ad9-a610-5f573abc4363", + "name": "repos_hub4j-test-org_github-api_collaborators_jimmysombrero2", + "request": { + "url": "/repos/hub4j-test-org/github-api/collaborators/jimmysombrero2", + "method": "PUT", + "headers": { + "Accept": { + "equalTo": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"permission\":\"push\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "body": "[]", + "headers": { + "Server": "GitHub.com", + "Date": "Sun, 02 Feb 2020 04:59:39 GMT", + "Content-Type": "application/json; charset=utf-8", + "Status": "201 Created", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4892", + "X-RateLimit-Reset": "1580620983", + "X-OAuth-Scopes": "admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete:packages, delete_repo, gist, notifications, read:packages, repo, user, workflow, write:discussion, write:packages", + "X-Accepted-OAuth-Scopes": "", + "X-GitHub-Media-Type": "unknown, github.v3", + "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type", + "Access-Control-Allow-Origin": "*", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Frame-Options": "deny", + "X-Content-Type-Options": "nosniff", + "X-XSS-Protection": "1; mode=block", + "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin", + "Content-Security-Policy": "default-src 'none'", + "X-GitHub-Request-Id": "C903:768E:16C4966:2BA11B0:5E36573B" + } + }, + "uuid": "3d80b19e-05f4-4ad9-a610-5f573abc4363", + "persistent": true, + "insertionIndex": 4 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/mappings/users_jimmysombrero2-7.json b/src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/mappings/users_jimmysombrero2-7.json new file mode 100644 index 000000000..9b27581e4 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHRepositoryTest/wiremock/addCollaborators/mappings/users_jimmysombrero2-7.json @@ -0,0 +1,45 @@ +{ + "id": "6685376c-451b-486d-88cd-502af9a7c5d2", + "name": "users_jimmysombrero2", + "request": { + "url": "/users/jimmysombrero2", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "users_jimmysombrero2.json", + "headers": { + "Server": "GitHub.com", + "Date": "Mon, 03 Feb 2020 03:50:14 GMT", + "Content-Type": "application/json; charset=utf-8", + "Status": "200 OK", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4907", + "X-RateLimit-Reset": "1580704799", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP", + "ETag": "W/\"7a0206b47e995649c88218afeb2266a6\"", + "Last-Modified": "Sun, 02 Feb 2020 04:43:58 GMT", + "X-OAuth-Scopes": "admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete:packages, delete_repo, gist, notifications, read:packages, repo, user, workflow, write:discussion, write:packages", + "X-Accepted-OAuth-Scopes": "", + "X-GitHub-Media-Type": "unknown, github.v3", + "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type", + "Access-Control-Allow-Origin": "*", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Frame-Options": "deny", + "X-Content-Type-Options": "nosniff", + "X-XSS-Protection": "1; mode=block", + "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin", + "Content-Security-Policy": "default-src 'none'", + "X-GitHub-Request-Id": "CDFE:768D:1021273:26DC788:5E379876" + } + }, + "uuid": "6685376c-451b-486d-88cd-502af9a7c5d1", + "persistent": true, + "insertionIndex": 6 +} \ No newline at end of file From fb1adbd1eff55b203fff3bad6185e5e3a9e35d51 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Mon, 27 Jul 2020 13:12:41 -0700 Subject: [PATCH 3/3] Make withUrlPath() overwrite instead of append I had some ideas about having multiple calls to apend to build up paths, but it turns out that idea is pretty bad. `with*()` methods should overwrite when called for the same field. If we want to create and , we can do that later. --- src/main/java/org/kohsuke/github/GHRepository.java | 9 +++++---- src/main/java/org/kohsuke/github/GitHubRequest.java | 8 ++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 8315b0dcd..b4e056cac 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -980,12 +980,13 @@ public class GHRepository extends GHObject { private void modifyCollaborators(@NonNull Collection users, @NonNull String method, @CheckForNull GHOrganization.Permission permission) throws IOException { + Requester requester = root.createRequest().method(method); + if (permission != null) { + requester = requester.with("permission", permission).inBody(); + } + // Make sure that the users collection doesn't have any duplicates for (GHUser user : new LinkedHashSet(users)) { - Requester requester = root.createRequest().method(method); - if (permission != null) { - requester = requester.with("permission", permission).inBody(); - } requester.withUrlPath(getApiTailUrl("collaborators/" + user.getLogin())).send(); } } diff --git a/src/main/java/org/kohsuke/github/GitHubRequest.java b/src/main/java/org/kohsuke/github/GitHubRequest.java index f7b4d2a4d..1148073b2 100644 --- a/src/main/java/org/kohsuke/github/GitHubRequest.java +++ b/src/main/java/org/kohsuke/github/GitHubRequest.java @@ -671,13 +671,9 @@ class GitHubRequest { tailUrlPath += "/" + String.join("/", urlPathItems); } - if (this.urlPath.endsWith("/")) { - tailUrlPath = StringUtils.stripStart(tailUrlPath, "/"); - } else { - tailUrlPath = StringUtils.prependIfMissing(tailUrlPath, "/"); - } + tailUrlPath = StringUtils.prependIfMissing(tailUrlPath, "/"); - this.urlPath += urlPathEncode(tailUrlPath); + this.urlPath = urlPathEncode(tailUrlPath); return (B) this; }