diff --git a/src/main/java/org/kohsuke/github/GHGraphQLException.java b/src/main/java/org/kohsuke/github/GHGraphQLException.java new file mode 100644 index 0000000000..849c20c64d --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHGraphQLException.java @@ -0,0 +1,31 @@ +package org.kohsuke.github; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A GraphQL request that GitHub answered with errors. + */ +public class GHGraphQLException extends GHIOException { + + /** The Constant serialVersionUID. */ + static final long serialVersionUID = 1L; + + /** The messages of the GraphQL errors. */ + private final List errorMessages; + + GHGraphQLException(List errorMessages) { + super("GraphQL request failed by:" + errorMessages); + this.errorMessages = Collections.unmodifiableList(new ArrayList<>(errorMessages)); + } + + /** + * Gets the messages of the GraphQL errors. + * + * @return the error messages + */ + public List getErrorMessages() { + return errorMessages; + } +} diff --git a/src/main/java/org/kohsuke/github/GHPerson.java b/src/main/java/org/kohsuke/github/GHPerson.java index 52c0557e4e..6b9c581ace 100644 --- a/src/main/java/org/kohsuke/github/GHPerson.java +++ b/src/main/java/org/kohsuke/github/GHPerson.java @@ -8,6 +8,7 @@ import java.time.Instant; import java.util.Collections; import java.util.Date; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TreeMap; @@ -279,6 +280,18 @@ public Instant getUpdatedAt() throws IOException { return super.getUpdatedAt(); } + /** + * Checks if this user or organization has a GitHub Sponsors listing. + * + * @return true if there is a sponsors listing + * @throws IOException + * the io exception + */ + @BetaApi + public boolean hasSponsorsListing() throws IOException { + return GHSponsors.hasSponsorsListing(this); + } + /** * Gets the siteAdmin field. * @@ -291,6 +304,34 @@ public boolean isSiteAdmin() throws IOException { return siteAdmin; } + /** + * Checks if the given account is currently sponsoring this user or organization. + * + * @param sponsorLogin + * the login of the potential sponsor + * @return true if the account has an active sponsorship of this user or organization + * @throws IOException + * the io exception + */ + @BetaApi + public boolean isSponsoredBy(String sponsorLogin) throws IOException { + return GHSponsors.isSponsoredBy(this, sponsorLogin); + } + + /** + * Checks if this user or organization currently sponsors the given account. + * + * @param sponsorableLogin + * the login of the user or organization that may be sponsored + * @return true if this user or organization has an active sponsorship of the account + * @throws IOException + * the io exception + */ + @BetaApi + public boolean isSponsoring(String sponsorableLogin) throws IOException { + return GHSponsors.isSponsoring(this, sponsorableLogin); + } + /** * Lists events for an organization or an user. * @@ -328,6 +369,63 @@ public PagedIterable listRepositories(final int pageSize) { return listRepositories().withPageSize(pageSize); } + /** + * Lists the active, public sponsorships this user or organization receives. + * + * @return an unmodifiable list of active public sponsorships received + * @throws IOException + * the io exception + */ + @BetaApi + public List listSponsorshipsAsMaintainer() throws IOException { + return listSponsorshipsAsMaintainer(true, false); + } + + /** + * Lists the sponsorships this user or organization receives. + * + * @param activeOnly + * if true, only active sponsorships; if false, cancelled sponsorships are included + * @param includePrivate + * if true, private sponsorships are included. Their sponsor is only revealed to the maintainer or an + * admin of the organization; GitHub App installation tokens cannot read them at all and the request + * fails with "Resource not accessible by integration" + * @return an unmodifiable list of sponsorships received + * @throws IOException + * the io exception + */ + @BetaApi + public List listSponsorshipsAsMaintainer(boolean activeOnly, boolean includePrivate) + throws IOException { + return GHSponsors.listSponsorshipsAsMaintainer(this, activeOnly, includePrivate); + } + + /** + * Lists the active sponsorships this user or organization gives. + * + * @return an unmodifiable list of active sponsorships given + * @throws IOException + * the io exception + */ + @BetaApi + public List listSponsorshipsAsSponsor() throws IOException { + return listSponsorshipsAsSponsor(true); + } + + /** + * Lists the sponsorships this user or organization gives. + * + * @param activeOnly + * if true, only active sponsorships; if false, cancelled sponsorships are included + * @return an unmodifiable list of sponsorships given + * @throws IOException + * the io exception + */ + @BetaApi + public List listSponsorshipsAsSponsor(boolean activeOnly) throws IOException { + return GHSponsors.listSponsorshipsAsSponsor(this, activeOnly); + } + /** * Fully populate the data by retrieving missing data. *

diff --git a/src/main/java/org/kohsuke/github/GHSponsors.java b/src/main/java/org/kohsuke/github/GHSponsors.java new file mode 100644 index 0000000000..afa6282ee9 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHSponsors.java @@ -0,0 +1,90 @@ +package org.kohsuke.github; + +import org.kohsuke.github.internal.graphql.response.GHGraphQLResponse; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * GraphQL queries behind the GitHub Sponsors methods of {@link GHPerson}. + */ +class GHSponsors { + + private static final String SPONSORSHIP_PAGE = "{ pageInfo { hasNextPage endCursor } nodes {" + + " sponsorEntity { ... on User { login } ... on Organization { login } }" + + " sponsorable { ... on User { login } ... on Organization { login } }" + + " tier { id name description monthlyPriceInDollars monthlyPriceInCents isOneTime isCustomAmount }" + + " isActive isOneTimePayment privacyLevel createdAt } }"; + + @SuppressWarnings("unchecked") + private static Map asMap(Object value) { + return (Map) value; + } + + private static String escape(String value) { + return value.replace("\\", "\\\\").replace("\"", "\\\""); + } + + private static List listSponsorships(GHPerson person, String connection, String arguments) + throws IOException { + List sponsorships = new ArrayList<>(); + String cursor = null; + do { + String after = cursor == null ? "" : ", after: \"" + escape(cursor) + "\""; + Map page = asMap( + query(person, connection + "(" + arguments + after + ") " + SPONSORSHIP_PAGE).get(connection)); + GHSponsorship[] nodes = GitHubClient.getMappingObjectReader(person.root()) + .forType(GHSponsorship[].class) + .readValue(GitHubClient.getMappingObjectWriter().writeValueAsString(page.get("nodes"))); + Collections.addAll(sponsorships, nodes); + Map pageInfo = asMap(page.get("pageInfo")); + cursor = (boolean) pageInfo.get("hasNextPage") ? (String) pageInfo.get("endCursor") : null; + } while (cursor != null); + return Collections.unmodifiableList(sponsorships); + } + + private static Map query(GHPerson person, String fields) throws IOException { + boolean organization = person instanceof GHOrganization || "Organization".equals(person.getType()); + String type = organization ? "organization" : "user"; + String query = "{ " + type + "(login: \"" + escape(person.login) + "\") { " + fields + " } }"; + Object data = person.root().createGraphQLRequest(query).fetchGraphQL(GHGraphQLResponse.ObjectResponse.class); + return asMap(asMap(data).get(type)); + } + + static boolean hasSponsorsListing(GHPerson person) throws IOException { + return (boolean) query(person, "hasSponsorsListing").get("hasSponsorsListing"); + } + + static boolean isSponsoredBy(GHPerson person, String sponsorLogin) throws IOException { + return (boolean) query(person, "isSponsoredBy(accountLogin: \"" + escape(sponsorLogin) + "\")") + .get("isSponsoredBy"); + } + + static boolean isSponsoring(GHPerson person, String sponsorableLogin) throws IOException { + String query = "{ repositoryOwner(login: \"" + escape(sponsorableLogin) + + "\") { ... on Sponsorable { isSponsoredBy(accountLogin: \"" + escape(person.login) + "\") } } }"; + Object data = person.root().createGraphQLRequest(query).fetchGraphQL(GHGraphQLResponse.ObjectResponse.class); + Map owner = asMap(asMap(data).get("repositoryOwner")); + if (owner == null) { + throw new GHFileNotFoundException(sponsorableLogin); + } + return (boolean) owner.get("isSponsoredBy"); + } + + static List listSponsorshipsAsMaintainer(GHPerson person, boolean activeOnly, boolean includePrivate) + throws IOException { + return listSponsorships(person, + "sponsorshipsAsMaintainer", + "first: 100, activeOnly: " + activeOnly + ", includePrivate: " + includePrivate); + } + + static List listSponsorshipsAsSponsor(GHPerson person, boolean activeOnly) throws IOException { + return listSponsorships(person, "sponsorshipsAsSponsor", "first: 100, activeOnly: " + activeOnly); + } + + private GHSponsors() { + } +} diff --git a/src/main/java/org/kohsuke/github/GHSponsorsTier.java b/src/main/java/org/kohsuke/github/GHSponsorsTier.java new file mode 100644 index 0000000000..c075d12e36 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHSponsorsTier.java @@ -0,0 +1,90 @@ +package org.kohsuke.github; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +/** + * A GitHub Sponsors tier. + * + * @see SponsorsTier + */ +@BetaApi +@JsonNaming(PropertyNamingStrategies.LowerCamelCaseStrategy.class) +@SuppressFBWarnings(value = { "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" }, justification = "JSON API") +public class GHSponsorsTier { + + private String description; + private String id; + private boolean isCustomAmount; + private boolean isOneTime; + private int monthlyPriceInCents; + private int monthlyPriceInDollars; + private String name; + + GHSponsorsTier() { + } + + /** + * Gets the description of this tier. + * + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * Gets the node id of this tier. + * + * @return the node id + */ + public String getId() { + return id; + } + + /** + * Gets the monthly price in cents. + * + * @return the monthly price in cents + */ + public int getMonthlyPriceInCents() { + return monthlyPriceInCents; + } + + /** + * Gets the monthly price in dollars. + * + * @return the monthly price in dollars + */ + public int getMonthlyPriceInDollars() { + return monthlyPriceInDollars; + } + + /** + * Gets the name of this tier. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Whether this tier lets the sponsor choose the amount. + * + * @return true if this is a custom amount tier + */ + public boolean isCustomAmount() { + return isCustomAmount; + } + + /** + * Whether this tier is a one-time payment. + * + * @return true if this is a one-time tier + */ + public boolean isOneTime() { + return isOneTime; + } +} diff --git a/src/main/java/org/kohsuke/github/GHSponsorship.java b/src/main/java/org/kohsuke/github/GHSponsorship.java new file mode 100644 index 0000000000..4fa1040bce --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHSponsorship.java @@ -0,0 +1,103 @@ +package org.kohsuke.github; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +import java.time.Instant; +import java.util.Map; + +/** + * A GitHub Sponsors sponsorship. + * + * @see Sponsorship + */ +@BetaApi +@JsonNaming(PropertyNamingStrategies.LowerCamelCaseStrategy.class) +@SuppressFBWarnings(value = { "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" }, justification = "JSON API") +public class GHSponsorship { + + /** + * Who can see a sponsorship. + */ + public enum PrivacyLevel { + /** Visible only to the sponsor and the maintainer. */ + PRIVATE, + /** Visible to everyone. */ + PUBLIC + } + private String createdAt; + private boolean isActive; + private boolean isOneTimePayment; + private PrivacyLevel privacyLevel; + private Map sponsorEntity; + private Map sponsorable; + + private GHSponsorsTier tier; + + GHSponsorship() { + } + + /** + * Gets the creation date of this sponsorship. + * + * @return the creation date + */ + public Instant getCreatedAt() { + return GitHubClient.parseInstant(createdAt); + } + + /** + * Gets the privacy level of this sponsorship. + * + * @return the privacy level + */ + public PrivacyLevel getPrivacyLevel() { + return privacyLevel; + } + + /** + * Gets the login of the sponsor. + * + * @return the sponsor login, or null if the sponsorship is private + */ + public String getSponsorLogin() { + return sponsorEntity == null ? null : sponsorEntity.get("login"); + } + + /** + * Gets the login of the sponsored user or organization. + * + * @return the sponsorable login + */ + public String getSponsorableLogin() { + return sponsorable == null ? null : sponsorable.get("login"); + } + + /** + * Gets the tier of this sponsorship. + * + * @return the tier, or null unless the authenticated user is the sponsor or the maintainer + */ + public GHSponsorsTier getTier() { + return tier; + } + + /** + * Whether this sponsorship is currently active. + * + * @return true if active + */ + public boolean isActive() { + return isActive; + } + + /** + * Whether this sponsorship is a one-time payment. + * + * @return true if one-time + */ + public boolean isOneTimePayment() { + return isOneTimePayment; + } +} diff --git a/src/main/java/org/kohsuke/github/Requester.java b/src/main/java/org/kohsuke/github/Requester.java index 95f0366ebd..b4ca3b9eb4 100644 --- a/src/main/java/org/kohsuke/github/Requester.java +++ b/src/main/java/org/kohsuke/github/Requester.java @@ -102,6 +102,8 @@ public T fetch(@Nonnull Class type) throws IOException { * @param type * the type * @return an instance of {@code GHGraphQLResponse} + * @throws GHGraphQLException + * if GitHub answered with GraphQL errors. * @throws IOException * if the server returns 4xx/5xx responses. */ @@ -109,7 +111,7 @@ public , S> S fetchGraphQL(@Nonnull Class type T response = fetch(type); if (!response.isSuccessful()) { - throw new IOException("GraphQL request failed by:" + response.getErrorMessages()); + throw new GHGraphQLException(response.getErrorMessages()); } return response.getData(); diff --git a/src/main/resources/META-INF/native-image/org.kohsuke/github-api/reflect-config.json b/src/main/resources/META-INF/native-image/org.kohsuke/github-api/reflect-config.json index bb5b85772f..0d97d598e3 100644 --- a/src/main/resources/META-INF/native-image/org.kohsuke/github-api/reflect-config.json +++ b/src/main/resources/META-INF/native-image/org.kohsuke/github-api/reflect-config.json @@ -7048,6 +7048,51 @@ "allDeclaredMethods": true, "allPublicClasses": true, "allDeclaredClasses": true + }, + { + "name": "org.kohsuke.github.GHSponsorsTier", + "allPublicFields": true, + "allDeclaredFields": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredConstructors": true, + "queryAllPublicMethods": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredMethods": true, + "allPublicClasses": true, + "allDeclaredClasses": true + }, + { + "name": "org.kohsuke.github.GHSponsorship", + "allPublicFields": true, + "allDeclaredFields": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredConstructors": true, + "queryAllPublicMethods": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredMethods": true, + "allPublicClasses": true, + "allDeclaredClasses": true + }, + { + "name": "org.kohsuke.github.GHSponsorship$PrivacyLevel", + "allPublicFields": true, + "allDeclaredFields": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredConstructors": true, + "queryAllPublicMethods": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredMethods": true, + "allPublicClasses": true, + "allDeclaredClasses": true } ] diff --git a/src/main/resources/META-INF/native-image/org.kohsuke/github-api/serialization-config.json b/src/main/resources/META-INF/native-image/org.kohsuke/github-api/serialization-config.json index b4c8f92359..a8dce8213c 100644 --- a/src/main/resources/META-INF/native-image/org.kohsuke/github-api/serialization-config.json +++ b/src/main/resources/META-INF/native-image/org.kohsuke/github-api/serialization-config.json @@ -1402,5 +1402,14 @@ }, { "name": "org.kohsuke.github.GitHubBridgeAdapterObject" + }, + { + "name": "org.kohsuke.github.GHSponsorsTier" + }, + { + "name": "org.kohsuke.github.GHSponsorship" + }, + { + "name": "org.kohsuke.github.GHSponsorship$PrivacyLevel" } ] diff --git a/src/test/java/org/kohsuke/github/GHSponsorsTest.java b/src/test/java/org/kohsuke/github/GHSponsorsTest.java new file mode 100644 index 0000000000..10ecf741b3 --- /dev/null +++ b/src/test/java/org/kohsuke/github/GHSponsorsTest.java @@ -0,0 +1,222 @@ +package org.kohsuke.github; + +import org.junit.Test; + +import java.io.IOException; +import java.time.Instant; +import java.util.List; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThrows; + +/** + * Tests for the GitHub Sponsors methods of {@link GHPerson}. + */ +public class GHSponsorsTest extends AbstractGitHubWireMockTest { + + /** + * Create default GHSponsorsTest instance + */ + public GHSponsorsTest() { + } + + /** + * A user with a Sponsors listing reports one. + * + * @throws IOException + * the io exception + */ + @Test + public void hasSponsorsListing() throws IOException { + GHUser user = gitHub.getUser("sponsor-test-user"); + assertThat(user.hasSponsorsListing(), is(true)); + } + + /** + * A user reports an account it sponsors. + * + * @throws IOException + * the io exception + */ + @Test + public void isSponsoring() throws IOException { + GHUser user = gitHub.getUser("sponsor-test-user"); + assertThat(user.isSponsoring("sponsor-test-org"), is(true)); + } + + /** + * Checking sponsorship of an unknown account fails like a missing REST resource. + * + * @throws IOException + * the io exception + */ + @Test + public void isSponsoringUnknownAccount() throws IOException { + GHUser user = gitHub.getUser("sponsor-test-user"); + assertThrows(GHFileNotFoundException.class, () -> user.isSponsoring("no-such-login")); + } + + /** + * Active public sponsorships received by an organization come back with their tiers. + * + * @throws IOException + * the io exception + */ + @Test + public void listSponsorshipsAsMaintainer() throws IOException { + GHOrganization org = gitHub.getOrganization("sponsor-test-org"); + List sponsorships = org.listSponsorshipsAsMaintainer(); + + assertThat(sponsorships, hasSize(5)); + + GHSponsorship first = sponsorships.get(0); + assertThat(first.getSponsorLogin(), equalTo("tiny-supporter")); + assertThat(first.getSponsorableLogin(), equalTo("sponsor-test-org")); + assertThat(first.isActive(), is(true)); + assertThat(first.isOneTimePayment(), is(false)); + assertThat(first.getPrivacyLevel(), equalTo(GHSponsorship.PrivacyLevel.PUBLIC)); + assertThat(first.getCreatedAt(), equalTo(Instant.parse("2026-05-06T09:56:53Z"))); + + GHSponsorsTier tier = first.getTier(); + assertThat(tier.getId(), equalTo("ST_kwDOAAAAAc4AAAAB")); + assertThat(tier.getName(), equalTo("$1 a month")); + assertThat(tier.getDescription(), equalTo("Say Thanks!")); + assertThat(tier.getMonthlyPriceInDollars(), equalTo(1)); + assertThat(tier.getMonthlyPriceInCents(), equalTo(100)); + assertThat(tier.isOneTime(), is(false)); + assertThat(tier.isCustomAmount(), is(false)); + + GHSponsorship gold = sponsorships.get(2); + assertThat(gold.getSponsorLogin(), equalTo("gold-patron")); + assertThat(gold.getTier().getName(), equalTo("$100 a month")); + assertThat(gold.getTier().getMonthlyPriceInDollars(), equalTo(100)); + assertThat(gold.getTier().getMonthlyPriceInCents(), equalTo(10000)); + } + + /** + * Sponsorships given by a user name the sponsored accounts. + * + * @throws IOException + * the io exception + */ + @Test + public void listSponsorshipsAsSponsor() throws IOException { + GHUser user = gitHub.getUser("sponsor-test-user"); + List sponsorships = user.listSponsorshipsAsSponsor(); + + assertThat(sponsorships, hasSize(2)); + + GHSponsorship monthly = sponsorships.get(0); + assertThat(monthly.getSponsorLogin(), equalTo("sponsor-test-user")); + assertThat(monthly.getSponsorableLogin(), equalTo("beloved-maintainer")); + assertThat(monthly.isActive(), is(true)); + assertThat(monthly.isOneTimePayment(), is(false)); + assertThat(monthly.getTier().getName(), equalTo("$5 a month")); + + GHSponsorship oneTime = sponsorships.get(1); + assertThat(oneTime.getSponsorLogin(), equalTo("sponsor-test-user")); + assertThat(oneTime.getSponsorableLogin(), equalTo("sponsor-test-org")); + assertThat(oneTime.isOneTimePayment(), is(true)); + assertThat(oneTime.getPrivacyLevel(), equalTo(GHSponsorship.PrivacyLevel.PRIVATE)); + assertThat(oneTime.getTier().isOneTime(), is(true)); + assertThat(oneTime.getTier().isCustomAmount(), is(true)); + } + + /** + * Cancelled and private sponsorships are included on request. + * + * @throws IOException + * the io exception + */ + @Test + public void listSponsorshipsIncludingInactive() throws IOException { + GHUser user = gitHub.getUser("sponsor-test-user"); + List sponsorships = user.listSponsorshipsAsMaintainer(false, true); + + assertThat(sponsorships, hasSize(4)); + + long activeCount = sponsorships.stream().filter(GHSponsorship::isActive).count(); + long inactiveCount = sponsorships.stream().filter(s -> !s.isActive()).count(); + assertThat(activeCount, equalTo(1L)); + assertThat(inactiveCount, equalTo(3L)); + + GHSponsorship pastSponsor = sponsorships.get(1); + assertThat(pastSponsor.getSponsorLogin(), equalTo("past-sponsor")); + assertThat(pastSponsor.getSponsorableLogin(), equalTo("sponsor-test-user")); + assertThat(pastSponsor.isActive(), is(false)); + assertThat(pastSponsor.getCreatedAt(), equalTo(Instant.parse("2023-03-10T08:00:00Z"))); + + GHSponsorship hidden = sponsorships.get(3); + assertThat(hidden.getSponsorLogin(), nullValue()); + assertThat(hidden.getSponsorableLogin(), equalTo("sponsor-test-user")); + assertThat(hidden.getPrivacyLevel(), equalTo(GHSponsorship.PrivacyLevel.PRIVATE)); + assertThat(hidden.getTier(), nullValue()); + } + + /** + * Sponsorships spanning several pages are fetched completely. + * + * @throws IOException + * the io exception + */ + @Test + public void listSponsorshipsPaginated() throws IOException { + GHOrganization org = gitHub.getOrganization("sponsor-test-org"); + List sponsorships = org.listSponsorshipsAsMaintainer(); + + assertThat(sponsorships, hasSize(3)); + assertThat(sponsorships.get(0).getSponsorLogin(), equalTo("first-page-one")); + assertThat(sponsorships.get(1).getSponsorLogin(), equalTo("first-page-two")); + assertThat(sponsorships.get(2).getSponsorLogin(), equalTo("second-page-one")); + } + + /** + * A GraphQL error for private sponsorships surfaces as {@link GHGraphQLException}. + * + * @throws IOException + * the io exception + */ + @Test + public void listSponsorshipsPrivateForbidden() throws IOException { + GHOrganization org = gitHub.getOrganization("sponsor-test-org"); + GHGraphQLException e = assertThrows(GHGraphQLException.class, + () -> org.listSponsorshipsAsMaintainer(false, true)); + assertThat(e.getErrorMessages(), contains("Resource not accessible by integration")); + } + + /** + * An organization reports an account that does not sponsor it. + * + * @throws IOException + * the io exception + */ + @Test + public void organizationIsNotSponsoredBy() throws IOException { + GHOrganization org = gitHub.getOrganization("sponsor-test-org"); + assertThat(org.isSponsoredBy("some-random-user"), is(false)); + } + + /** + * An organization reports an account that sponsors it. + * + * @throws IOException + * the io exception + */ + @Test + public void organizationIsSponsoredBy() throws IOException { + GHOrganization org = gitHub.getOrganization("sponsor-test-org"); + assertThat(org.isSponsoredBy("active-sponsor"), is(true)); + } + + /** + * A user reports an account that does not sponsor it. + * + * @throws IOException + * the io exception + */ + @Test + public void userIsNotSponsoredBy() throws IOException { + GHUser user = gitHub.getUser("sponsor-test-user"); + assertThat(user.isSponsoredBy("some-random-user"), is(false)); + } +} diff --git a/src/test/resources/no-reflect-and-serialization-list b/src/test/resources/no-reflect-and-serialization-list index 4ad893272c..5bea019e28 100644 --- a/src/test/resources/no-reflect-and-serialization-list +++ b/src/test/resources/no-reflect-and-serialization-list @@ -7,6 +7,7 @@ org.kohsuke.github.GHDiscussion$Updater org.kohsuke.github.GHException org.kohsuke.github.GHFileNotFoundException org.kohsuke.github.GHGistUpdater +org.kohsuke.github.GHGraphQLException org.kohsuke.github.GHHooks org.kohsuke.github.GHHooks$Context org.kohsuke.github.GHHooks$OrgContext @@ -14,6 +15,7 @@ org.kohsuke.github.GHHooks$RepoContext org.kohsuke.github.GHIOException org.kohsuke.github.GHPersonSet org.kohsuke.github.GHReleaseUpdater +org.kohsuke.github.GHSponsors org.kohsuke.github.GitHub org.kohsuke.github.GitHub$DependentAuthorizationProvider org.kohsuke.github.GitHub$LoginLoadingUserAuthorizationProvider diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/hasSponsorsListing/__files/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/hasSponsorsListing/__files/1-user.json new file mode 100644 index 0000000000..e02ee45421 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/hasSponsorsListing/__files/1-user.json @@ -0,0 +1,19 @@ +{ + "login": "sponsor-test-user", + "id": 12345678, + "node_id": "MDQ6VXNlcjEyMzQ1Njc4", + "avatar_url": "https://avatars.githubusercontent.com/u/12345678?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sponsor-test-user", + "html_url": "https://github.com/sponsor-test-user", + "type": "User", + "site_admin": false, + "name": "Sponsor Test User", + "bio": "Test user for sponsor tests", + "public_repos": 10, + "public_gists": 0, + "followers": 50, + "following": 5, + "created_at": "2020-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/hasSponsorsListing/mappings/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/hasSponsorsListing/mappings/1-user.json new file mode 100644 index 0000000000..12cb64db5a --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/hasSponsorsListing/mappings/1-user.json @@ -0,0 +1,28 @@ +{ + "id": "a1000001-0001-0001-0001-000000000001", + "name": "users_sponsor-test-user", + "request": { + "url": "/users/sponsor-test-user", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-user.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a1000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/hasSponsorsListing/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/hasSponsorsListing/mappings/2-graphql.json new file mode 100644 index 0000000000..43f51cfc8b --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/hasSponsorsListing/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a1000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ user(login: \\\"sponsor-test-user\\\") { hasSponsorsListing } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"user\":{\"hasSponsorsListing\":true}}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a1000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoring/__files/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoring/__files/1-user.json new file mode 100644 index 0000000000..e02ee45421 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoring/__files/1-user.json @@ -0,0 +1,19 @@ +{ + "login": "sponsor-test-user", + "id": 12345678, + "node_id": "MDQ6VXNlcjEyMzQ1Njc4", + "avatar_url": "https://avatars.githubusercontent.com/u/12345678?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sponsor-test-user", + "html_url": "https://github.com/sponsor-test-user", + "type": "User", + "site_admin": false, + "name": "Sponsor Test User", + "bio": "Test user for sponsor tests", + "public_repos": 10, + "public_gists": 0, + "followers": 50, + "following": 5, + "created_at": "2020-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoring/mappings/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoring/mappings/1-user.json new file mode 100644 index 0000000000..12cb64db5a --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoring/mappings/1-user.json @@ -0,0 +1,28 @@ +{ + "id": "a1000001-0001-0001-0001-000000000001", + "name": "users_sponsor-test-user", + "request": { + "url": "/users/sponsor-test-user", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-user.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a1000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoring/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoring/mappings/2-graphql.json new file mode 100644 index 0000000000..b5a3255564 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoring/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a1000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ repositoryOwner(login: \\\"sponsor-test-org\\\") { ... on Sponsorable { isSponsoredBy(accountLogin: \\\"sponsor-test-user\\\") } } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"repositoryOwner\":{\"isSponsoredBy\":true}}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a1000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoringUnknownAccount/__files/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoringUnknownAccount/__files/1-user.json new file mode 100644 index 0000000000..e02ee45421 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoringUnknownAccount/__files/1-user.json @@ -0,0 +1,19 @@ +{ + "login": "sponsor-test-user", + "id": 12345678, + "node_id": "MDQ6VXNlcjEyMzQ1Njc4", + "avatar_url": "https://avatars.githubusercontent.com/u/12345678?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sponsor-test-user", + "html_url": "https://github.com/sponsor-test-user", + "type": "User", + "site_admin": false, + "name": "Sponsor Test User", + "bio": "Test user for sponsor tests", + "public_repos": 10, + "public_gists": 0, + "followers": 50, + "following": 5, + "created_at": "2020-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoringUnknownAccount/mappings/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoringUnknownAccount/mappings/1-user.json new file mode 100644 index 0000000000..12cb64db5a --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoringUnknownAccount/mappings/1-user.json @@ -0,0 +1,28 @@ +{ + "id": "a1000001-0001-0001-0001-000000000001", + "name": "users_sponsor-test-user", + "request": { + "url": "/users/sponsor-test-user", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-user.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a1000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoringUnknownAccount/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoringUnknownAccount/mappings/2-graphql.json new file mode 100644 index 0000000000..9dc1a6070f --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/isSponsoringUnknownAccount/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a1000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ repositoryOwner(login: \\\"no-such-login\\\") { ... on Sponsorable { isSponsoredBy(accountLogin: \\\"sponsor-test-user\\\") } } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"repositoryOwner\":null}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a1000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsMaintainer/__files/1-org.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsMaintainer/__files/1-org.json new file mode 100644 index 0000000000..ca8fe8fe26 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsMaintainer/__files/1-org.json @@ -0,0 +1,30 @@ +{ + "login": "sponsor-test-org", + "id": 98461227, + "node_id": "O_kgDOBd5mKw", + "url": "https://api.github.com/orgs/sponsor-test-org", + "repos_url": "https://api.github.com/orgs/sponsor-test-org/repos", + "events_url": "https://api.github.com/orgs/sponsor-test-org/events", + "hooks_url": "https://api.github.com/orgs/sponsor-test-org/hooks", + "issues_url": "https://api.github.com/orgs/sponsor-test-org/issues", + "members_url": "https://api.github.com/orgs/sponsor-test-org/members{/member}", + "public_members_url": "https://api.github.com/orgs/sponsor-test-org/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/98461227?v=4", + "description": "Test organization for sponsor tests", + "name": "Sponsor Test Org", + "blog": "", + "location": null, + "email": null, + "twitter_username": null, + "is_verified": false, + "has_organization_projects": true, + "has_repository_projects": true, + "public_repos": 5, + "public_gists": 0, + "followers": 10, + "following": 0, + "html_url": "https://github.com/sponsor-test-org", + "created_at": "2022-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "type": "Organization" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsMaintainer/mappings/1-org.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsMaintainer/mappings/1-org.json new file mode 100644 index 0000000000..0370a32e3b --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsMaintainer/mappings/1-org.json @@ -0,0 +1,28 @@ +{ + "id": "a5000001-0001-0001-0001-000000000001", + "name": "orgs_sponsor-test-org", + "request": { + "url": "/orgs/sponsor-test-org", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-org.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a5000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsMaintainer/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsMaintainer/mappings/2-graphql.json new file mode 100644 index 0000000000..29584c663f --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsMaintainer/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a5000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ organization(login: \\\"sponsor-test-org\\\") { sponsorshipsAsMaintainer(first: 100, activeOnly: true, includePrivate: false) { pageInfo { hasNextPage endCursor } nodes { sponsorEntity { ... on User { login } ... on Organization { login } } sponsorable { ... on User { login } ... on Organization { login } } tier { id name description monthlyPriceInDollars monthlyPriceInCents isOneTime isCustomAmount } isActive isOneTimePayment privacyLevel createdAt } } } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"organization\":{\"sponsorshipsAsMaintainer\":{\"pageInfo\":{\"hasNextPage\":false,\"endCursor\":\"Y3Vyc29yOnYyOpHOAAAAAQ==\"},\"nodes\":[{\"sponsorEntity\":{\"login\":\"tiny-supporter\"},\"sponsorable\":{\"login\":\"sponsor-test-org\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAB\",\"name\":\"$1 a month\",\"description\":\"Say Thanks!\",\"monthlyPriceInDollars\":1,\"monthlyPriceInCents\":100,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2026-05-06T09:56:53Z\"},{\"sponsorEntity\":{\"login\":\"bronze-backer\"},\"sponsorable\":{\"login\":\"sponsor-test-org\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAC\",\"name\":\"$5 a month\",\"description\":\"Bronze level\",\"monthlyPriceInDollars\":5,\"monthlyPriceInCents\":500,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2024-06-21T14:56:22Z\"},{\"sponsorEntity\":{\"login\":\"gold-patron\"},\"sponsorable\":{\"login\":\"sponsor-test-org\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAD\",\"name\":\"$100 a month\",\"description\":\"Gold level\",\"monthlyPriceInDollars\":100,\"monthlyPriceInCents\":10000,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2025-12-03T18:06:54Z\"},{\"sponsorEntity\":{\"login\":\"silver-friend\"},\"sponsorable\":{\"login\":\"sponsor-test-org\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAE\",\"name\":\"$10 a month\",\"description\":\"Silver level\",\"monthlyPriceInDollars\":10,\"monthlyPriceInCents\":1000,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2026-02-12T06:37:03Z\"},{\"sponsorEntity\":{\"login\":\"another-backer\"},\"sponsorable\":{\"login\":\"sponsor-test-org\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAF\",\"name\":\"$5 a month\",\"description\":\"Bronze level\",\"monthlyPriceInDollars\":5,\"monthlyPriceInCents\":500,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2026-01-16T21:04:25Z\"}]}}}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a5000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsSponsor/__files/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsSponsor/__files/1-user.json new file mode 100644 index 0000000000..e02ee45421 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsSponsor/__files/1-user.json @@ -0,0 +1,19 @@ +{ + "login": "sponsor-test-user", + "id": 12345678, + "node_id": "MDQ6VXNlcjEyMzQ1Njc4", + "avatar_url": "https://avatars.githubusercontent.com/u/12345678?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sponsor-test-user", + "html_url": "https://github.com/sponsor-test-user", + "type": "User", + "site_admin": false, + "name": "Sponsor Test User", + "bio": "Test user for sponsor tests", + "public_repos": 10, + "public_gists": 0, + "followers": 50, + "following": 5, + "created_at": "2020-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsSponsor/mappings/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsSponsor/mappings/1-user.json new file mode 100644 index 0000000000..1c9ea214d2 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsSponsor/mappings/1-user.json @@ -0,0 +1,28 @@ +{ + "id": "a6000001-0001-0001-0001-000000000001", + "name": "users_sponsor-test-user", + "request": { + "url": "/users/sponsor-test-user", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-user.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a6000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsSponsor/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsSponsor/mappings/2-graphql.json new file mode 100644 index 0000000000..e192a80e66 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsAsSponsor/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a6000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ user(login: \\\"sponsor-test-user\\\") { sponsorshipsAsSponsor(first: 100, activeOnly: true) { pageInfo { hasNextPage endCursor } nodes { sponsorEntity { ... on User { login } ... on Organization { login } } sponsorable { ... on User { login } ... on Organization { login } } tier { id name description monthlyPriceInDollars monthlyPriceInCents isOneTime isCustomAmount } isActive isOneTimePayment privacyLevel createdAt } } } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"user\":{\"sponsorshipsAsSponsor\":{\"pageInfo\":{\"hasNextPage\":false,\"endCursor\":\"Y3Vyc29yOnYyOpHOAAAAAQ==\"},\"nodes\":[{\"sponsorEntity\":{\"login\":\"sponsor-test-user\"},\"sponsorable\":{\"login\":\"beloved-maintainer\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAJ\",\"name\":\"$5 a month\",\"description\":\"Bronze level\",\"monthlyPriceInDollars\":5,\"monthlyPriceInCents\":500,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2025-09-14T10:20:30Z\"},{\"sponsorEntity\":{\"login\":\"sponsor-test-user\"},\"sponsorable\":{\"login\":\"sponsor-test-org\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAK\",\"name\":\"$25 one time\",\"description\":\"Custom one-time amount\",\"monthlyPriceInDollars\":25,\"monthlyPriceInCents\":2500,\"isOneTime\":true,\"isCustomAmount\":true},\"isActive\":true,\"isOneTimePayment\":true,\"privacyLevel\":\"PRIVATE\",\"createdAt\":\"2026-03-02T15:45:00Z\"}]}}}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a6000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsIncludingInactive/__files/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsIncludingInactive/__files/1-user.json new file mode 100644 index 0000000000..e02ee45421 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsIncludingInactive/__files/1-user.json @@ -0,0 +1,19 @@ +{ + "login": "sponsor-test-user", + "id": 12345678, + "node_id": "MDQ6VXNlcjEyMzQ1Njc4", + "avatar_url": "https://avatars.githubusercontent.com/u/12345678?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sponsor-test-user", + "html_url": "https://github.com/sponsor-test-user", + "type": "User", + "site_admin": false, + "name": "Sponsor Test User", + "bio": "Test user for sponsor tests", + "public_repos": 10, + "public_gists": 0, + "followers": 50, + "following": 5, + "created_at": "2020-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsIncludingInactive/mappings/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsIncludingInactive/mappings/1-user.json new file mode 100644 index 0000000000..1c9ea214d2 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsIncludingInactive/mappings/1-user.json @@ -0,0 +1,28 @@ +{ + "id": "a6000001-0001-0001-0001-000000000001", + "name": "users_sponsor-test-user", + "request": { + "url": "/users/sponsor-test-user", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-user.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a6000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsIncludingInactive/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsIncludingInactive/mappings/2-graphql.json new file mode 100644 index 0000000000..1c10fc116e --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsIncludingInactive/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a6000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ user(login: \\\"sponsor-test-user\\\") { sponsorshipsAsMaintainer(first: 100, activeOnly: false, includePrivate: true) { pageInfo { hasNextPage endCursor } nodes { sponsorEntity { ... on User { login } ... on Organization { login } } sponsorable { ... on User { login } ... on Organization { login } } tier { id name description monthlyPriceInDollars monthlyPriceInCents isOneTime isCustomAmount } isActive isOneTimePayment privacyLevel createdAt } } } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"user\":{\"sponsorshipsAsMaintainer\":{\"pageInfo\":{\"hasNextPage\":false,\"endCursor\":\"Y3Vyc29yOnYyOpHOAAAAAQ==\"},\"nodes\":[{\"sponsorEntity\":{\"login\":\"active-sponsor\"},\"sponsorable\":{\"login\":\"sponsor-test-user\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAG\",\"name\":\"$5 a month\",\"description\":\"Bronze level\",\"monthlyPriceInDollars\":5,\"monthlyPriceInCents\":500,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2024-06-21T14:56:22Z\"},{\"sponsorEntity\":{\"login\":\"past-sponsor\"},\"sponsorable\":{\"login\":\"sponsor-test-user\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAH\",\"name\":\"$1 a month\",\"description\":\"Say Thanks!\",\"monthlyPriceInDollars\":1,\"monthlyPriceInCents\":100,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":false,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2023-03-10T08:00:00Z\"},{\"sponsorEntity\":{\"login\":\"another-past-sponsor\"},\"sponsorable\":{\"login\":\"sponsor-test-user\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAI\",\"name\":\"$10 a month\",\"description\":\"Silver level\",\"monthlyPriceInDollars\":10,\"monthlyPriceInCents\":1000,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":false,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2023-08-15T12:30:00Z\"},{\"sponsorEntity\":null,\"sponsorable\":{\"login\":\"sponsor-test-user\"},\"tier\":null,\"isActive\":false,\"isOneTimePayment\":false,\"privacyLevel\":\"PRIVATE\",\"createdAt\":\"2026-04-01T12:00:00Z\"}]}}}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a6000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/__files/1-org.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/__files/1-org.json new file mode 100644 index 0000000000..ca8fe8fe26 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/__files/1-org.json @@ -0,0 +1,30 @@ +{ + "login": "sponsor-test-org", + "id": 98461227, + "node_id": "O_kgDOBd5mKw", + "url": "https://api.github.com/orgs/sponsor-test-org", + "repos_url": "https://api.github.com/orgs/sponsor-test-org/repos", + "events_url": "https://api.github.com/orgs/sponsor-test-org/events", + "hooks_url": "https://api.github.com/orgs/sponsor-test-org/hooks", + "issues_url": "https://api.github.com/orgs/sponsor-test-org/issues", + "members_url": "https://api.github.com/orgs/sponsor-test-org/members{/member}", + "public_members_url": "https://api.github.com/orgs/sponsor-test-org/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/98461227?v=4", + "description": "Test organization for sponsor tests", + "name": "Sponsor Test Org", + "blog": "", + "location": null, + "email": null, + "twitter_username": null, + "is_verified": false, + "has_organization_projects": true, + "has_repository_projects": true, + "public_repos": 5, + "public_gists": 0, + "followers": 10, + "following": 0, + "html_url": "https://github.com/sponsor-test-org", + "created_at": "2022-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "type": "Organization" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/mappings/1-org.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/mappings/1-org.json new file mode 100644 index 0000000000..d4d66803aa --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/mappings/1-org.json @@ -0,0 +1,28 @@ +{ + "id": "a3000001-0001-0001-0001-000000000001", + "name": "orgs_sponsor-test-org", + "request": { + "url": "/orgs/sponsor-test-org", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-org.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a3000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/mappings/2-graphql.json new file mode 100644 index 0000000000..681279fed0 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a7000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ organization(login: \\\"sponsor-test-org\\\") { sponsorshipsAsMaintainer(first: 100, activeOnly: true, includePrivate: false) { pageInfo { hasNextPage endCursor } nodes { sponsorEntity { ... on User { login } ... on Organization { login } } sponsorable { ... on User { login } ... on Organization { login } } tier { id name description monthlyPriceInDollars monthlyPriceInCents isOneTime isCustomAmount } isActive isOneTimePayment privacyLevel createdAt } } } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"organization\":{\"sponsorshipsAsMaintainer\":{\"pageInfo\":{\"hasNextPage\":true,\"endCursor\":\"Y3Vyc29yOnYyOpHOAAAAAg==\"},\"nodes\":[{\"sponsorEntity\":{\"login\":\"first-page-one\"},\"sponsorable\":{\"login\":\"sponsor-test-org\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAX\",\"name\":\"$1 a month\",\"description\":\"Say Thanks!\",\"monthlyPriceInDollars\":1,\"monthlyPriceInCents\":100,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2026-01-01T00:00:00Z\"},{\"sponsorEntity\":{\"login\":\"first-page-two\"},\"sponsorable\":{\"login\":\"sponsor-test-org\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAY\",\"name\":\"$5 a month\",\"description\":\"Bronze level\",\"monthlyPriceInDollars\":5,\"monthlyPriceInCents\":500,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2026-01-01T00:00:00Z\"}]}}}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a7000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/mappings/3-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/mappings/3-graphql.json new file mode 100644 index 0000000000..90c16aeb93 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPaginated/mappings/3-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a7000001-0001-0001-0001-000000000003", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ organization(login: \\\"sponsor-test-org\\\") { sponsorshipsAsMaintainer(first: 100, activeOnly: true, includePrivate: false, after: \\\"Y3Vyc29yOnYyOpHOAAAAAg==\\\") { pageInfo { hasNextPage endCursor } nodes { sponsorEntity { ... on User { login } ... on Organization { login } } sponsorable { ... on User { login } ... on Organization { login } } tier { id name description monthlyPriceInDollars monthlyPriceInCents isOneTime isCustomAmount } isActive isOneTimePayment privacyLevel createdAt } } } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"organization\":{\"sponsorshipsAsMaintainer\":{\"pageInfo\":{\"hasNextPage\":false,\"endCursor\":\"Y3Vyc29yOnYyOpHOAAAAAw==\"},\"nodes\":[{\"sponsorEntity\":{\"login\":\"second-page-one\"},\"sponsorable\":{\"login\":\"sponsor-test-org\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAX\",\"name\":\"$1 a month\",\"description\":\"Say Thanks!\",\"monthlyPriceInDollars\":1,\"monthlyPriceInCents\":100,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2026-01-01T00:00:00Z\"}]}}}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a7000001-0001-0001-0001-000000000003", + "persistent": true, + "insertionIndex": 3 +} diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPrivateForbidden/__files/1-org.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPrivateForbidden/__files/1-org.json new file mode 100644 index 0000000000..ca8fe8fe26 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPrivateForbidden/__files/1-org.json @@ -0,0 +1,30 @@ +{ + "login": "sponsor-test-org", + "id": 98461227, + "node_id": "O_kgDOBd5mKw", + "url": "https://api.github.com/orgs/sponsor-test-org", + "repos_url": "https://api.github.com/orgs/sponsor-test-org/repos", + "events_url": "https://api.github.com/orgs/sponsor-test-org/events", + "hooks_url": "https://api.github.com/orgs/sponsor-test-org/hooks", + "issues_url": "https://api.github.com/orgs/sponsor-test-org/issues", + "members_url": "https://api.github.com/orgs/sponsor-test-org/members{/member}", + "public_members_url": "https://api.github.com/orgs/sponsor-test-org/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/98461227?v=4", + "description": "Test organization for sponsor tests", + "name": "Sponsor Test Org", + "blog": "", + "location": null, + "email": null, + "twitter_username": null, + "is_verified": false, + "has_organization_projects": true, + "has_repository_projects": true, + "public_repos": 5, + "public_gists": 0, + "followers": 10, + "following": 0, + "html_url": "https://github.com/sponsor-test-org", + "created_at": "2022-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "type": "Organization" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPrivateForbidden/mappings/1-org.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPrivateForbidden/mappings/1-org.json new file mode 100644 index 0000000000..d4d66803aa --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPrivateForbidden/mappings/1-org.json @@ -0,0 +1,28 @@ +{ + "id": "a3000001-0001-0001-0001-000000000001", + "name": "orgs_sponsor-test-org", + "request": { + "url": "/orgs/sponsor-test-org", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-org.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a3000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPrivateForbidden/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPrivateForbidden/mappings/2-graphql.json new file mode 100644 index 0000000000..8a22bb8bf4 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/listSponsorshipsPrivateForbidden/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a3000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ organization(login: \\\"sponsor-test-org\\\") { sponsorshipsAsMaintainer(first: 100, activeOnly: false, includePrivate: true) { pageInfo { hasNextPage endCursor } nodes { sponsorEntity { ... on User { login } ... on Organization { login } } sponsorable { ... on User { login } ... on Organization { login } } tier { id name description monthlyPriceInDollars monthlyPriceInCents isOneTime isCustomAmount } isActive isOneTimePayment privacyLevel createdAt } } } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"organization\":{\"sponsorshipsAsMaintainer\":{\"pageInfo\":{\"hasNextPage\":false,\"endCursor\":\"Y3Vyc29yOnYyOpHOAAAAAg==\"},\"nodes\":[{\"sponsorEntity\":{\"login\":\"tiny-supporter\"},\"sponsorable\":{\"login\":\"sponsor-test-org\"},\"tier\":{\"id\":\"ST_kwDOAAAAAc4AAAAB\",\"name\":\"$1 a month\",\"description\":\"Say Thanks!\",\"monthlyPriceInDollars\":1,\"monthlyPriceInCents\":100,\"isOneTime\":false,\"isCustomAmount\":false},\"isActive\":true,\"isOneTimePayment\":false,\"privacyLevel\":\"PUBLIC\",\"createdAt\":\"2026-05-06T09:56:53Z\"},null]}}},\"errors\":[{\"type\":\"FORBIDDEN\",\"path\":[\"organization\",\"sponsorshipsAsMaintainer\",\"nodes\",1],\"locations\":[{\"line\":1,\"column\":112}],\"message\":\"Resource not accessible by integration\"}]}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a3000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsNotSponsoredBy/__files/1-org.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsNotSponsoredBy/__files/1-org.json new file mode 100644 index 0000000000..ca8fe8fe26 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsNotSponsoredBy/__files/1-org.json @@ -0,0 +1,30 @@ +{ + "login": "sponsor-test-org", + "id": 98461227, + "node_id": "O_kgDOBd5mKw", + "url": "https://api.github.com/orgs/sponsor-test-org", + "repos_url": "https://api.github.com/orgs/sponsor-test-org/repos", + "events_url": "https://api.github.com/orgs/sponsor-test-org/events", + "hooks_url": "https://api.github.com/orgs/sponsor-test-org/hooks", + "issues_url": "https://api.github.com/orgs/sponsor-test-org/issues", + "members_url": "https://api.github.com/orgs/sponsor-test-org/members{/member}", + "public_members_url": "https://api.github.com/orgs/sponsor-test-org/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/98461227?v=4", + "description": "Test organization for sponsor tests", + "name": "Sponsor Test Org", + "blog": "", + "location": null, + "email": null, + "twitter_username": null, + "is_verified": false, + "has_organization_projects": true, + "has_repository_projects": true, + "public_repos": 5, + "public_gists": 0, + "followers": 10, + "following": 0, + "html_url": "https://github.com/sponsor-test-org", + "created_at": "2022-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "type": "Organization" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsNotSponsoredBy/mappings/1-org.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsNotSponsoredBy/mappings/1-org.json new file mode 100644 index 0000000000..68ae8dc314 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsNotSponsoredBy/mappings/1-org.json @@ -0,0 +1,28 @@ +{ + "id": "a4000001-0001-0001-0001-000000000001", + "name": "orgs_sponsor-test-org", + "request": { + "url": "/orgs/sponsor-test-org", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-org.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a4000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsNotSponsoredBy/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsNotSponsoredBy/mappings/2-graphql.json new file mode 100644 index 0000000000..ba2b552313 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsNotSponsoredBy/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a4000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ organization(login: \\\"sponsor-test-org\\\") { isSponsoredBy(accountLogin: \\\"some-random-user\\\") } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"organization\":{\"isSponsoredBy\":false}}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a4000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsSponsoredBy/__files/1-org.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsSponsoredBy/__files/1-org.json new file mode 100644 index 0000000000..ca8fe8fe26 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsSponsoredBy/__files/1-org.json @@ -0,0 +1,30 @@ +{ + "login": "sponsor-test-org", + "id": 98461227, + "node_id": "O_kgDOBd5mKw", + "url": "https://api.github.com/orgs/sponsor-test-org", + "repos_url": "https://api.github.com/orgs/sponsor-test-org/repos", + "events_url": "https://api.github.com/orgs/sponsor-test-org/events", + "hooks_url": "https://api.github.com/orgs/sponsor-test-org/hooks", + "issues_url": "https://api.github.com/orgs/sponsor-test-org/issues", + "members_url": "https://api.github.com/orgs/sponsor-test-org/members{/member}", + "public_members_url": "https://api.github.com/orgs/sponsor-test-org/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/98461227?v=4", + "description": "Test organization for sponsor tests", + "name": "Sponsor Test Org", + "blog": "", + "location": null, + "email": null, + "twitter_username": null, + "is_verified": false, + "has_organization_projects": true, + "has_repository_projects": true, + "public_repos": 5, + "public_gists": 0, + "followers": 10, + "following": 0, + "html_url": "https://github.com/sponsor-test-org", + "created_at": "2022-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z", + "type": "Organization" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsSponsoredBy/mappings/1-org.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsSponsoredBy/mappings/1-org.json new file mode 100644 index 0000000000..d4d66803aa --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsSponsoredBy/mappings/1-org.json @@ -0,0 +1,28 @@ +{ + "id": "a3000001-0001-0001-0001-000000000001", + "name": "orgs_sponsor-test-org", + "request": { + "url": "/orgs/sponsor-test-org", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-org.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a3000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsSponsoredBy/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsSponsoredBy/mappings/2-graphql.json new file mode 100644 index 0000000000..8277768bc0 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/organizationIsSponsoredBy/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a3000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ organization(login: \\\"sponsor-test-org\\\") { isSponsoredBy(accountLogin: \\\"active-sponsor\\\") } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"organization\":{\"isSponsoredBy\":true}}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a3000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/userIsNotSponsoredBy/__files/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/userIsNotSponsoredBy/__files/1-user.json new file mode 100644 index 0000000000..e02ee45421 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/userIsNotSponsoredBy/__files/1-user.json @@ -0,0 +1,19 @@ +{ + "login": "sponsor-test-user", + "id": 12345678, + "node_id": "MDQ6VXNlcjEyMzQ1Njc4", + "avatar_url": "https://avatars.githubusercontent.com/u/12345678?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sponsor-test-user", + "html_url": "https://github.com/sponsor-test-user", + "type": "User", + "site_admin": false, + "name": "Sponsor Test User", + "bio": "Test user for sponsor tests", + "public_repos": 10, + "public_gists": 0, + "followers": 50, + "following": 5, + "created_at": "2020-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/userIsNotSponsoredBy/mappings/1-user.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/userIsNotSponsoredBy/mappings/1-user.json new file mode 100644 index 0000000000..60a6d0df30 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/userIsNotSponsoredBy/mappings/1-user.json @@ -0,0 +1,28 @@ +{ + "id": "a2000001-0001-0001-0001-000000000001", + "name": "users_sponsor-test-user", + "request": { + "url": "/users/sponsor-test-user", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-user.json", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4999", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "1", + "X-RateLimit-Resource": "core" + } + }, + "uuid": "a2000001-0001-0001-0001-000000000001", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/userIsNotSponsoredBy/mappings/2-graphql.json b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/userIsNotSponsoredBy/mappings/2-graphql.json new file mode 100644 index 0000000000..965bdc3691 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHSponsorsTest/wiremock/userIsNotSponsoredBy/mappings/2-graphql.json @@ -0,0 +1,35 @@ +{ + "id": "a2000001-0001-0001-0001-000000000002", + "name": "graphql", + "request": { + "url": "/graphql", + "method": "POST", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"query\":\"{ user(login: \\\"sponsor-test-user\\\") { isSponsoredBy(accountLogin: \\\"some-random-user\\\") } }\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "body": "{\"data\":{\"user\":{\"isSponsoredBy\":false}}}", + "headers": { + "Content-Type": "application/json; charset=utf-8", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4998", + "X-RateLimit-Reset": "1700000000", + "X-RateLimit-Used": "2", + "X-RateLimit-Resource": "graphql" + } + }, + "uuid": "a2000001-0001-0001-0001-000000000002", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file