diff --git a/deployment/pom.xml b/deployment/pom.xml index 3779ec1..b74ee10 100644 --- a/deployment/pom.xml +++ b/deployment/pom.xml @@ -21,6 +21,12 @@ quarkus-github-api ${project.version} + + + org.assertj + assertj-core + test + diff --git a/deployment/src/main/java/io/quarkiverse/githubapi/deployment/GitHubApiDotNames.java b/deployment/src/main/java/io/quarkiverse/githubapi/deployment/GitHubApiDotNames.java index f405d7a..54259fa 100644 --- a/deployment/src/main/java/io/quarkiverse/githubapi/deployment/GitHubApiDotNames.java +++ b/deployment/src/main/java/io/quarkiverse/githubapi/deployment/GitHubApiDotNames.java @@ -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; @@ -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; @@ -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; @@ -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 { @@ -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 @@ -82,20 +84,27 @@ final class GitHubApiDotNames { private static final DotName GH_COMMIT = DotName.createSimple(GHCommit.class.getName()); static final List 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 @@ -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()); @@ -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()); @@ -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 @@ -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()); @@ -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 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 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, @@ -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, @@ -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, diff --git a/deployment/src/test/java/io/quarkiverse/githubapi/deployment/GitHubApiDotNamesTest.java b/deployment/src/test/java/io/quarkiverse/githubapi/deployment/GitHubApiDotNamesTest.java new file mode 100644 index 0000000..e845d1c --- /dev/null +++ b/deployment/src/test/java/io/quarkiverse/githubapi/deployment/GitHubApiDotNamesTest.java @@ -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 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 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 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); + } + } + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7cb27d2..2cc9151 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ 3.0.0-M7 1.313 + 3.23.1 @@ -49,6 +50,11 @@ github-api ${github-api.version} + + org.assertj + assertj-core + ${assertj.version} +