Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reflection fixes #179

Merged
merged 3 commits into from Oct 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions deployment/pom.xml
Expand Up @@ -21,6 +21,12 @@
<artifactId>quarkus-github-api</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Expand Up @@ -6,6 +6,7 @@
import org.jboss.jandex.DotName;
import org.kohsuke.github.GHAppInstallation;
import org.kohsuke.github.GHAppInstallationToken;
import org.kohsuke.github.GHAuthenticatedAppInstallation;
import org.kohsuke.github.GHAuthorization;
import org.kohsuke.github.GHBlob;
import org.kohsuke.github.GHBranch;
Expand All @@ -27,6 +28,7 @@
import org.kohsuke.github.GHIssue;
import org.kohsuke.github.GHIssueChanges;
import org.kohsuke.github.GHIssueEvent;
import org.kohsuke.github.GHIssueRename;
import org.kohsuke.github.GHKey;
import org.kohsuke.github.GHLabel;
import org.kohsuke.github.GHLabelChanges;
Expand All @@ -38,6 +40,7 @@
import org.kohsuke.github.GHMembership;
import org.kohsuke.github.GHMeta;
import org.kohsuke.github.GHNotificationStream;
import org.kohsuke.github.GHObject;
import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHProjectsV2ItemChanges;
import org.kohsuke.github.GHPullRequest;
Expand All @@ -62,7 +65,6 @@
import org.kohsuke.github.GHWorkflowRun;
import org.kohsuke.github.GitCommit;
import org.kohsuke.github.GitUser;
import org.kohsuke.github.PagedIterable;

final class GitHubApiDotNames {

Expand All @@ -71,7 +73,7 @@ final class GitHubApiDotNames {
private static final DotName GH_MARKETPLACE_ACCOUNT = DotName.createSimple(GHMarketplaceAccount.class.getName());
private static final DotName GH_CONTENT = DotName.createSimple(GHContent.class.getName());
private static final DotName GITHUB_INTERACTIVE_OBJECT = DotName.createSimple("org.kohsuke.github.GitHubInteractiveObject");
private static final DotName PAGED_ITERABLE = DotName.createSimple(PagedIterable.class.getName());
private static final DotName GH_OBJECT = DotName.createSimple(GHObject.class.getName());
private static final DotName GH_HOOK = DotName.createSimple(GHHook.class.getName());
private static final DotName GH_REPOSITORY_TRAFFIC = DotName.createSimple(GHRepositoryTraffic.class.getName());
private static final DotName GH_REPOSITORY_TRAFFIC_DAILY_INFO = DotName
Expand All @@ -82,20 +84,27 @@ final class GitHubApiDotNames {
private static final DotName GH_COMMIT = DotName.createSimple(GHCommit.class.getName());

static final List<DotName> GH_ROOT_OBJECTS = Arrays.asList(GH_EVENT_PAYLOAD, GH_MARKETPLACE_ACCOUNT, GH_CONTENT,
GITHUB_INTERACTIVE_OBJECT,
PAGED_ITERABLE, GH_HOOK, GH_REPOSITORY_TRAFFIC, GH_REPOSITORY_TRAFFIC_DAILY_INFO, GH_KEY, SEARCH_RESULT, GIT_USER,
GH_OBJECT,
GH_HOOK, GH_REPOSITORY_TRAFFIC, GH_REPOSITORY_TRAFFIC_DAILY_INFO, GH_KEY, SEARCH_RESULT, GIT_USER,
GH_COMMIT);

// Simple objects
private static final DotName GH_APP_INSTALLATION_GH_APP_INSTALLATION_REPOSITORY_RESULT = DotName
.createSimple(GHAppInstallation.class.getName() + "$GHAppInstallationRepositoryResult");
private static final DotName GH_APP_INSTALLATION_PAGE = DotName.createSimple("org.kohsuke.github.GHAppInstallationsPage");
private static final DotName GH_APP_INSTALLATION_TOKEN = DotName.createSimple(GHAppInstallationToken.class.getName());
private static final DotName GH_APP_AUTHORIZATION_APP = DotName.createSimple(GHAuthorization.class.getName() + "$App");
private static final DotName GH_ARTIFACTS_PAGE = DotName.createSimple("org.kohsuke.github.GHArtifactsPage");
private static final DotName GH_AUTHENTICATED_APP_INSTALLATION = DotName
.createSimple(GHAuthenticatedAppInstallation.class.getName());
private static final DotName GH_BLOB = DotName.createSimple(GHBlob.class.getName());
private static final DotName GH_BRANCH = DotName.createSimple(GHBranch.class.getName());
private static final DotName GH_BRANCH_COMMIT = DotName.createSimple(GHBranch.Commit.class.getName());
private static final DotName GH_BRANCH_PROTECTION = DotName.createSimple(GHBranchProtection.class.getName());
private static final DotName GH_BRANCH_PROTECTION_BUILDER_RESTRICTIONS = DotName
.createSimple("org.kohsuke.github.GHBranchProtectionBuilder$Restrictions");
private static final DotName GH_BRANCH_PROTECTION_BUILDER_STATUS_CHECKS = DotName
.createSimple("org.kohsuke.github.GHBranchProtectionBuilder$StatusChecks");
private static final DotName GH_BRANCH_PROTECTION_ENFORCE_ADMINS = DotName
.createSimple(GHBranchProtection.EnforceAdmins.class.getName());
private static final DotName GH_BRANCH_PROTECTION_REQUIRED_REVIEWS = DotName
Expand All @@ -114,6 +123,8 @@ final class GitHubApiDotNames {
private static final DotName GH_CHECK_RUN_OUTPUT = DotName.createSimple(GHCheckRun.Output.class.getName());
private static final DotName GH_CHECK_RUNS_PAGE = DotName.createSimple("org.kohsuke.github.GHCheckRunsPage");
private static final DotName GH_CHECK_SUITE_HEAD_COMMIT = DotName.createSimple(GHCheckSuite.HeadCommit.class.getName());
private static final DotName GH_COMMIT_BUILDER_USER_INFO = DotName
.createSimple("org.kohsuke.github.GHCommitBuilder$UserInfo");
private static final DotName GH_COMMIT_FILE = DotName.createSimple(GHCommit.File.class.getName());
private static final DotName GH_COMMIT_PARENT = DotName.createSimple(GHCommit.Parent.class.getName());
private static final DotName GH_COMMIT_SHORT_INFO = DotName.createSimple(GHCommit.ShortInfo.class.getName());
Expand Down Expand Up @@ -142,6 +153,7 @@ final class GitHubApiDotNames {
private static final DotName GH_ISSUE_CHANGES = DotName.createSimple(GHIssueChanges.class.getName());
private static final DotName GH_ISSUE_CHANGES_GH_FROM = DotName
.createSimple(GHIssueChanges.GHFrom.class.getName());
private static final DotName GH_ISSUE_RENAME = DotName.createSimple(GHIssueRename.class.getName());
private static final DotName GH_ISSUE_PULL_REQUEST = DotName.createSimple(GHIssue.PullRequest.class.getName());
private static final DotName GH_ISSUE_EVENT = DotName.createSimple(GHIssueEvent.class.getName());
private static final DotName GH_LABEL = DotName.createSimple(GHLabel.class.getName());
Expand Down Expand Up @@ -181,6 +193,8 @@ final class GitHubApiDotNames {
private static final DotName GH_PULL_REQUEST_COMMIT_DETAIL_TREE = DotName
.createSimple(GHPullRequestCommitDetail.Tree.class.getName());
private static final DotName GH_PULL_REQUEST_FILE_DETAIL = DotName.createSimple(GHPullRequestFileDetail.class.getName());
private static final DotName GH_PULL_REQUEST_REVIEW_BUILDER_DRAFT_REVIEW_COMMENT = DotName
.createSimple("org.kohsuke.github.GHPullRequestReviewBuilder$DraftReviewComment");
private static final DotName GH_RATE_LIMIT = DotName.createSimple(GHRateLimit.class.getName());
private static final DotName GH_RATE_LIMIT_RECORD = DotName.createSimple(GHRateLimit.Record.class.getName());
private static final DotName GH_RATE_LIMIT_UNKNOWN_LIMIT_RECORD = DotName
Expand All @@ -205,6 +219,8 @@ final class GitHubApiDotNames {
private static final DotName GH_TAG_OBJECT = DotName.createSimple(GHTagObject.class.getName());
private static final DotName GH_THREAD_SUBJECT = DotName.createSimple(GHThread.class.getName() + "$Subject");
private static final DotName GH_TREE = DotName.createSimple(GHTree.class.getName());
private static final DotName GH_TREE_BUILDER_TREE_ENTRY = DotName
.createSimple("org.kohsuke.github.GHTreeBuilder$TreeEntry");
private static final DotName GH_TREE_ENTRY = DotName.createSimple(GHTreeEntry.class.getName());
private static final DotName GH_VERIFICATION = DotName.createSimple(GHVerification.class.getName());
private static final DotName GH_WORKFLOW_JOB_STEP = DotName.createSimple(GHWorkflowJob.Step.class.getName());
Expand All @@ -219,15 +235,22 @@ final class GitHubApiDotNames {
private static final DotName GITHUB_RESPONSE = DotName.createSimple("org.kohsuke.github.GitHubResponse");
private static final DotName JSON_RATE_LIMIT = DotName.createSimple("org.kohsuke.github.JsonRateLimit");

static final List<DotName> GH_SIMPLE_OBJECTS = Arrays.asList(GH_APP_INSTALLATION_GH_APP_INSTALLATION_REPOSITORY_RESULT,
GH_APP_INSTALLATION_TOKEN, GH_APP_AUTHORIZATION_APP, GH_ARTIFACTS_PAGE, GH_BLOB,
static final List<DotName> GH_SIMPLE_OBJECTS = Arrays.asList(
GITHUB_INTERACTIVE_OBJECT, // Must be registered for reflection, but not all of its subclasses.
GH_APP_INSTALLATION_GH_APP_INSTALLATION_REPOSITORY_RESULT,
GH_APP_INSTALLATION_PAGE,
GH_APP_INSTALLATION_TOKEN, GH_APP_AUTHORIZATION_APP, GH_ARTIFACTS_PAGE,
GH_AUTHENTICATED_APP_INSTALLATION, GH_BLOB,
GH_BRANCH,
GH_BRANCH_COMMIT, GH_BRANCH_PROTECTION, GH_BRANCH_PROTECTION_ENFORCE_ADMINS, GH_BRANCH_PROTECTION_REQUIRED_REVIEWS,
GH_BRANCH_COMMIT, GH_BRANCH_PROTECTION, GH_BRANCH_PROTECTION_BUILDER_RESTRICTIONS,
GH_BRANCH_PROTECTION_BUILDER_STATUS_CHECKS,
GH_BRANCH_PROTECTION_ENFORCE_ADMINS, GH_BRANCH_PROTECTION_REQUIRED_REVIEWS,
GH_BRANCH_PROTECTION_REQUIRED_SIGNATURES, GH_BRANCH_PROTECTION_REQUIRED_STATUS_CHECKS,
GH_BRANCH_PROTECTION_RESTRICTIONS,
GH_CHECK_RUN_BUILDER_ACTION, GH_CHECK_RUN_BUILDER_ANNOTATION, GH_CHECK_RUN_BUILDER_IMAGE,
GH_CHECK_RUN_BUILDER_OUTPUT,
GH_CHECK_RUN_OUTPUT, GH_CHECK_RUNS_PAGE, GH_CHECK_SUITE_HEAD_COMMIT,
GH_COMMIT_BUILDER_USER_INFO,
GH_COMMIT_FILE, GH_COMMIT_PARENT, GH_COMMIT_SHORT_INFO,
GH_COMMIT_STATS, GH_COMMIT_TREE, GH_COMMIT_USER,
GH_COMMIT_POINTER, GH_COMPARE, GH_COMPARE_INNER_COMMIT, GH_COMPARE_TREE, GH_CONTENT_UPDATE_RESPONSE,
Expand All @@ -236,7 +259,7 @@ final class GitHubApiDotNames {
GH_EVENT_PAYLOAD_COMMENT_CHANGES, GH_EVENT_PAYLOAD_COMMENT_CHANGES_GH_FROM,
GH_EVENT_PAYLOAD_PUSH_PUSHER, GH_EVENT_PAYLOAD_PUSH_PUSH_COMMIT,
GH_GIST_FILE, GH_ISSUE_CHANGES, GH_ISSUE_CHANGES_GH_FROM,
GH_ISSUE_PULL_REQUEST, GH_ISSUE_EVENT,
GH_ISSUE_RENAME, GH_ISSUE_PULL_REQUEST, GH_ISSUE_EVENT,
GH_LABEL, GH_LABEL_CHANGES, GH_LABEL_CHANGES_GH_FROM,
GH_MARKETPLACE_PENDING_CHANGE, GH_MARKETPLACE_PLAN,
GH_MARKETPLACE_PURCHASE, GH_MARKETPLACE_USER_PURCHASE, GH_MEMBERSHIP, GH_META, GH_NOTIFICATION_STREAM,
Expand All @@ -247,11 +270,12 @@ final class GitHubApiDotNames {
GH_PULL_REQUEST_CHANGES_GH_COMMIT_POINTER,
GH_PULL_REQUEST_COMMIT_DETAIL, GH_PULL_REQUEST_COMMIT_DETAIL_COMMIT,
GH_PULL_REQUEST_COMMIT_DETAIL_COMMIT_POINTER, GH_PULL_REQUEST_COMMIT_DETAIL_TREE,
GH_PULL_REQUEST_FILE_DETAIL, GH_RATE_LIMIT, GH_RATE_LIMIT_RECORD, GH_RATE_LIMIT_UNKNOWN_LIMIT_RECORD, GH_REF,
GH_PULL_REQUEST_FILE_DETAIL, GH_PULL_REQUEST_REVIEW_BUILDER_DRAFT_REVIEW_COMMENT,
GH_RATE_LIMIT, GH_RATE_LIMIT_RECORD, GH_RATE_LIMIT_UNKNOWN_LIMIT_RECORD, GH_REF,
GH_REF_GH_OBJECT, GH_REPOSITORY_DISCUSSION_CATEGORY, GH_REPOSITORY_GH_REPO_PERMISSION, GH_REPOSITORY_TOPICS,
GH_REPOSITORY_STATISTICS, GH_REPOSITORY_STATISTICS_CONTRIBUTOR_STATS_WEEK, GH_REPOSITORY_STATISTICS_CODE_FREQUENCY,
GH_REPOSITORY_STATISTICS_PUNCH_CARD_ITEM, GH_STARGAZER, GH_SUBSCRIPTION, GH_TAG, GH_TAG_OBJECT, GH_THREAD_SUBJECT,
GH_TREE, GH_TREE_ENTRY, GH_VERIFICATION,
GH_TREE, GH_TREE_BUILDER_TREE_ENTRY, GH_TREE_ENTRY, GH_VERIFICATION,
GH_WORKFLOW_JOB_STEP, GH_WORKFLOW_JOBS_PAGE, GH_WORKFLOW_RUN_HEAD_COMMIT, GH_WORKFLOW_RUNS_PAGE, GH_WORKFLOWS_PAGE,
GIT_COMMIT, GIT_COMMIT_TREE,
GITHUB_REQUEST, GITHUB_REQUEST_ENTRY, GITHUB_RESPONSE,
Expand Down
@@ -0,0 +1,140 @@
package io.quarkiverse.githubapi.deployment;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.kohsuke.github.GitHub;

import io.quarkus.deployment.index.IndexingUtil;

class GitHubApiDotNamesTest {

// These types will be expected NOT to be registered for reflection, as well as their subtypes.
// Their inner types, however, won't be affected by the exclusion.
// Useful for "helper" types, such as clients, or builders in all but name.
private static final Set<String> EXCLUDED_TYPES = Set.of(
"org.kohsuke.github.AbuseLimitHandler",
"org.kohsuke.github.GHDiscussion$Creator",
"org.kohsuke.github.GHDiscussion$Setter",
"org.kohsuke.github.GHDiscussion$Updater",
"org.kohsuke.github.GHException",
"org.kohsuke.github.GHFileNotFoundException",
"org.kohsuke.github.GHGistUpdater",
"org.kohsuke.github.GHHooks",
"org.kohsuke.github.GHHooks$Context",
"org.kohsuke.github.GHHooks$OrgContext",
"org.kohsuke.github.GHHooks$RepoContext",
"org.kohsuke.github.GHIOException",
"org.kohsuke.github.GHPersonSet",
"org.kohsuke.github.GHReleaseUpdater",
"org.kohsuke.github.GitHub",
"org.kohsuke.github.GitHub$DependentAuthorizationProvider",
"org.kohsuke.github.GitHub$LoginLoadingUserAuthorizationProvider",
"org.kohsuke.github.GitHubAbuseLimitHandler",
"org.kohsuke.github.GitHubClient",
"org.kohsuke.github.GitHubClient$BodyHandler",
"org.kohsuke.github.GitHubClient$GHApiInfo",
"org.kohsuke.github.GitHubClient$RetryRequestException",
"org.kohsuke.github.GitHubConnectorResponseErrorHandler",
"org.kohsuke.github.GitHubPageIterator",
"org.kohsuke.github.GitHubRateLimitChecker",
"org.kohsuke.github.GitHubRateLimitHandler",
"org.kohsuke.github.HttpConnector",
"org.kohsuke.github.HttpException",
"org.kohsuke.github.PagedIterator",
"org.kohsuke.github.RateLimitChecker",
"org.kohsuke.github.RateLimitHandler",
"org.kohsuke.github.Reactable",
"org.kohsuke.github.Refreshable",
"org.kohsuke.github.Requester",
"org.kohsuke.github.TrafficInfo",
"org.kohsuke.github.authorization",
"org.kohsuke.github.connector",
"org.kohsuke.github.example.dataobject",
"org.kohsuke.github.extras",
"org.kohsuke.github.extras.authorization",
"org.kohsuke.github.extras.okhttp3",
"org.kohsuke.github.function",
"org.kohsuke.github.internal");

private static Index ghApiIndex;

@BeforeAll
public static void index() throws IOException {
ghApiIndex = IndexingUtil.indexJar(determineJarLocation(GitHub.class, "github-api"));
}

@Test
public void testNoMissingGitHubClasses() {
Set<DotName> expectedClasses = new TreeSet<>();
for (ClassInfo clazz : ghApiIndex.getKnownClasses()) {
if (shouldBeRegisteredForReflection(clazz)) {
expectedClasses.add(clazz.name());
}
}

// Simulate what happens when we create build items to register classes for reflection
Set<DotName> actualClasses = new TreeSet<>();
actualClasses.addAll(GitHubApiDotNames.GH_SIMPLE_OBJECTS);
for (DotName clazz : GitHubApiDotNames.GH_ROOT_OBJECTS) {
actualClasses.add(clazz);
actualClasses.addAll(ghApiIndex.getAllKnownSubclasses(clazz).stream()
.map(ClassInfo::name).collect(Collectors.toList()));
}

// No idea why this appears in the result, since the class doesn't event exist;
// that's probably a bug in Jandex?
actualClasses.remove(DotName.createSimple("org.kohsuke.github.GHCommit$ShortInfo$Tree"));

assertThat(expectedClasses).isNotEmpty();
assertThat(actualClasses).containsExactlyInAnyOrderElementsOf(expectedClasses);
}

private boolean shouldBeRegisteredForReflection(ClassInfo clazz) {
return !clazz.isAnnotation() && !clazz.isEnum() && !clazz.isSynthetic()
&& !ClassInfo.NestingType.ANONYMOUS.equals(clazz.nestingType())
&& !ClassInfo.NestingType.LOCAL.equals(clazz.nestingType())
&& !isExcluded(clazz);
}

private boolean isExcluded(ClassInfo clazz) {
if (clazz.simpleName().endsWith("Builder") || clazz.simpleName().endsWith("PagedIterable")) {
return true;
}
if (EXCLUDED_TYPES.contains(clazz.name().toString())
|| EXCLUDED_TYPES.contains(clazz.name().packagePrefix())) {
return true;
}
var superClass = ghApiIndex.getClassByName(clazz.superName());
if (superClass != null) {
return isExcluded(superClass);
}
return false;
}

private static Path determineJarLocation(Class<?> classFromJar, String jarName) {
URL url = classFromJar.getProtectionDomain().getCodeSource().getLocation();
if (!url.getProtocol().equals("file")) {
throw new IllegalStateException(jarName + " JAR is not a local file? " + url);
}
try {
return Paths.get(url.toURI());
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
}

}
6 changes: 6 additions & 0 deletions pom.xml
Expand Up @@ -26,6 +26,7 @@
<surefire-plugin.version>3.0.0-M7</surefire-plugin.version>

<github-api.version>1.313</github-api.version>
<assertj.version>3.23.1</assertj.version>
</properties>

<modules>
Expand All @@ -49,6 +50,11 @@
<artifactId>github-api</artifactId>
<version>${github-api.version}</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down