diff --git a/src/main/java/org/kohsuke/github/AbstractBuilder.java b/src/main/java/org/kohsuke/github/AbstractBuilder.java index 17b122aff2..b6e74f07f6 100644 --- a/src/main/java/org/kohsuke/github/AbstractBuilder.java +++ b/src/main/java/org/kohsuke/github/AbstractBuilder.java @@ -97,7 +97,7 @@ protected AbstractBuilder(@Nonnull Class finalReturnType, * if there is an I/O Exception */ @Nonnull - @Preview + @BetaApi @Deprecated public R done() throws IOException { R result; @@ -127,7 +127,7 @@ public R done() throws IOException { * if an I/O error occurs */ @Nonnull - @Preview + @BetaApi @Deprecated protected S with(@Nonnull String name, Object value) throws IOException { requester.with(name, value); @@ -148,7 +148,7 @@ protected S with(@Nonnull String name, Object value) throws IOException { * if an I/O error occurs */ @Nonnull - @Preview + @BetaApi @Deprecated protected S continueOrDone() throws IOException { // This little bit of roughness in this base class means all inheriting builders get to create Updater and diff --git a/src/main/java/org/kohsuke/github/BetaApi.java b/src/main/java/org/kohsuke/github/BetaApi.java new file mode 100644 index 0000000000..1c33b7daa3 --- /dev/null +++ b/src/main/java/org/kohsuke/github/BetaApi.java @@ -0,0 +1,18 @@ +package org.kohsuke.github; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Indicates that the method/class/etc marked is a beta implementation of an sdk feature. + *

+ * These APIs are subject to change and not a part of the backward compatibility commitment. Always used in conjunction + * with 'deprecated' to raise awareness to clients. + *

+ * + */ +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface BetaApi { +} diff --git a/src/main/java/org/kohsuke/github/GHAppCreateTokenBuilder.java b/src/main/java/org/kohsuke/github/GHAppCreateTokenBuilder.java index a1fd02e004..802c04693e 100644 --- a/src/main/java/org/kohsuke/github/GHAppCreateTokenBuilder.java +++ b/src/main/java/org/kohsuke/github/GHAppCreateTokenBuilder.java @@ -19,7 +19,7 @@ public class GHAppCreateTokenBuilder { protected final Requester builder; private final String apiUrlTail; - @Preview + @BetaApi @Deprecated GHAppCreateTokenBuilder(GitHub root, String apiUrlTail) { this.root = root; @@ -27,7 +27,7 @@ public class GHAppCreateTokenBuilder { this.builder = root.createRequest(); } - @Preview + @BetaApi @Deprecated GHAppCreateTokenBuilder(GitHub root, String apiUrlTail, Map permissions) { this(root, apiUrlTail); @@ -43,7 +43,7 @@ public class GHAppCreateTokenBuilder { * Array containing the repositories Ids * @return a GHAppCreateTokenBuilder */ - @Preview + @BetaApi @Deprecated public GHAppCreateTokenBuilder repositoryIds(List repositoryIds) { this.builder.with("repository_ids", repositoryIds); @@ -58,7 +58,7 @@ public GHAppCreateTokenBuilder repositoryIds(List repositoryIds) { * Map containing the permission names and types. * @return a GHAppCreateTokenBuilder */ - @Preview + @BetaApi @Deprecated public GHAppCreateTokenBuilder permissions(Map permissions) { Map retMap = new HashMap<>(); diff --git a/src/main/java/org/kohsuke/github/GHAppInstallation.java b/src/main/java/org/kohsuke/github/GHAppInstallation.java index 407504efbc..d9dc3b4197 100644 --- a/src/main/java/org/kohsuke/github/GHAppInstallation.java +++ b/src/main/java/org/kohsuke/github/GHAppInstallation.java @@ -344,7 +344,7 @@ public void deleteInstallation() throws IOException { * @return a GHAppCreateTokenBuilder instance * @deprecated Use {@link GHAppInstallation#createToken()} instead. */ - @Preview + @BetaApi @Deprecated public GHAppCreateTokenBuilder createToken(Map permissions) { return new GHAppCreateTokenBuilder(root, @@ -361,7 +361,7 @@ public GHAppCreateTokenBuilder createToken(Map permiss * * @return a GHAppCreateTokenBuilder instance */ - @Preview + @BetaApi @Deprecated public GHAppCreateTokenBuilder createToken() { return new GHAppCreateTokenBuilder(root, String.format("/app/installations/%d/access_tokens", getId())); diff --git a/src/main/java/org/kohsuke/github/GHBranch.java b/src/main/java/org/kohsuke/github/GHBranch.java index 799ea71aa9..702b24c225 100644 --- a/src/main/java/org/kohsuke/github/GHBranch.java +++ b/src/main/java/org/kohsuke/github/GHBranch.java @@ -78,7 +78,7 @@ public String getName() { * * @return true if the push to this branch is restricted via branch protection. */ - @Preview + @Preview(Previews.LUKE_CAGE) @Deprecated public boolean isProtected() { return protection; diff --git a/src/main/java/org/kohsuke/github/GHCommitComment.java b/src/main/java/org/kohsuke/github/GHCommitComment.java index 321067a0da..72d6dee949 100644 --- a/src/main/java/org/kohsuke/github/GHCommitComment.java +++ b/src/main/java/org/kohsuke/github/GHCommitComment.java @@ -121,7 +121,7 @@ public void update(String body) throws IOException { this.body = body; } - @Preview + @Preview(SQUIRREL_GIRL) @Deprecated public GHReaction createReaction(ReactionContent content) throws IOException { return owner.root.createRequest() diff --git a/src/main/java/org/kohsuke/github/GHLabel.java b/src/main/java/org/kohsuke/github/GHLabel.java index 4bf573498f..61c8124398 100644 --- a/src/main/java/org/kohsuke/github/GHLabel.java +++ b/src/main/java/org/kohsuke/github/GHLabel.java @@ -132,7 +132,7 @@ static Collection toNames(Collection labels) { * @throws IOException * the io exception */ - @Preview + @BetaApi @Deprecated static Creator create(GHRepository repository) throws IOException { return new Creator(repository); @@ -179,7 +179,7 @@ static PagedIterable readAll(@Nonnull final GHRepository repository) th * * @return a {@link Updater} */ - @Preview + @BetaApi @Deprecated public Updater update() { return new Updater(this); @@ -190,7 +190,7 @@ public Updater update() { * * @return a {@link Setter} */ - @Preview + @BetaApi @Deprecated public Setter set() { return new Setter(this); @@ -227,7 +227,7 @@ public int hashCode() { * * {@link #done()} is called automatically after the property is set. */ - @Preview + @BetaApi @Deprecated public static class Setter extends GHLabelBuilder { private Setter(@Nonnull GHLabel base) { @@ -241,7 +241,7 @@ private Setter(@Nonnull GHLabel base) { * * Consumer must call {@link #done()} to commit changes. */ - @Preview + @BetaApi @Deprecated public static class Updater extends GHLabelBuilder { private Updater(@Nonnull GHLabel base) { @@ -255,7 +255,7 @@ private Updater(@Nonnull GHLabel base) { * * Consumer must call {@link #done()} to create the new instance. */ - @Preview + @BetaApi @Deprecated public static class Creator extends GHLabelBuilder { private Creator(@Nonnull GHRepository repository) { diff --git a/src/main/java/org/kohsuke/github/GHLabelBuilder.java b/src/main/java/org/kohsuke/github/GHLabelBuilder.java index dabdb6ccd2..6633d607f9 100644 --- a/src/main/java/org/kohsuke/github/GHLabelBuilder.java +++ b/src/main/java/org/kohsuke/github/GHLabelBuilder.java @@ -38,21 +38,21 @@ protected GHLabelBuilder(@Nonnull Class intermediateReturnType, } @Nonnull - @Preview + @BetaApi @Deprecated public S name(String value) throws IOException { return with("name", value); } @Nonnull - @Preview + @BetaApi @Deprecated public S color(String value) throws IOException { return with("color", value); } @Nonnull - @Preview + @BetaApi @Deprecated public S description(String value) throws IOException { return with("description", value); diff --git a/src/main/java/org/kohsuke/github/GHRelease.java b/src/main/java/org/kohsuke/github/GHRelease.java index e2eb532c8e..7ab145bcbe 100644 --- a/src/main/java/org/kohsuke/github/GHRelease.java +++ b/src/main/java/org/kohsuke/github/GHRelease.java @@ -260,7 +260,6 @@ public GHAsset uploadAsset(String filename, InputStream stream, String contentTy * existing logic in place for backwards compatibility. */ @Deprecated - @Preview public List assets() { return assets; } diff --git a/src/main/java/org/kohsuke/github/GHRepositoryStatistics.java b/src/main/java/org/kohsuke/github/GHRepositoryStatistics.java index 1b561438cb..0f20a86835 100644 --- a/src/main/java/org/kohsuke/github/GHRepositoryStatistics.java +++ b/src/main/java/org/kohsuke/github/GHRepositoryStatistics.java @@ -60,7 +60,7 @@ public PagedIterable getContributorStats() throws IOException, * @throws InterruptedException * the interrupted exception */ - @Preview + @BetaApi @Deprecated @SuppressWarnings("SleepWhileInLoop") @SuppressFBWarnings(value = { "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" }, justification = "JSON API") diff --git a/src/main/java/org/kohsuke/github/GitUser.java b/src/main/java/org/kohsuke/github/GitUser.java index 3162ff04fc..30194800fc 100644 --- a/src/main/java/org/kohsuke/github/GitUser.java +++ b/src/main/java/org/kohsuke/github/GitUser.java @@ -42,8 +42,6 @@ public String getEmail() { * * @return GitHub username */ - @Preview - @Deprecated @CheckForNull public String getUsername() { return username; diff --git a/src/main/java/org/kohsuke/github/Preview.java b/src/main/java/org/kohsuke/github/Preview.java index 240130a9bd..2986683067 100644 --- a/src/main/java/org/kohsuke/github/Preview.java +++ b/src/main/java/org/kohsuke/github/Preview.java @@ -25,6 +25,6 @@ * * @return The API preview media type. */ - public Previews[] value() default {}; + public Previews[] value(); } diff --git a/src/test/java/org/kohsuke/github/ArchTests.java b/src/test/java/org/kohsuke/github/ArchTests.java index 662ee76422..ae1d7f1412 100644 --- a/src/test/java/org/kohsuke/github/ArchTests.java +++ b/src/test/java/org/kohsuke/github/ArchTests.java @@ -1,5 +1,7 @@ package org.kohsuke.github; +import com.tngtech.archunit.base.DescribedPredicate; +import com.tngtech.archunit.core.domain.JavaAnnotation; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.ClassFileImporter; import com.tngtech.archunit.core.importer.ImportOption; @@ -7,6 +9,8 @@ import org.junit.BeforeClass; import org.junit.Test; +import static com.tngtech.archunit.lang.conditions.ArchConditions.beAnnotatedWith; +import static com.tngtech.archunit.lang.conditions.ArchConditions.not; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods; @@ -19,6 +23,17 @@ public class ArchTests { .withImportOption(new ImportOption.DoNotIncludeJars()) .importPackages("org.kohsuke.github"); + private static final DescribedPredicate> previewAnnotationWithNoMediaType = new DescribedPredicate>( + "preview has no required media types defined") { + + @Override + public boolean apply(JavaAnnotation javaAnnotation) { + boolean isPreview = javaAnnotation.getRawType().isEquivalentTo(Preview.class); + Object[] values = (Object[]) javaAnnotation.getProperties().get("value"); + return isPreview && values != null && values.length < 1; + } + }; + @BeforeClass public static void beforeClass() { assertTrue(classFiles.size() > 0); @@ -33,12 +48,14 @@ public void testPreviewsAreFlaggedAsDeprecated() { .areAnnotatedWith(Preview.class) .should() .beAnnotatedWith(Deprecated.class) + .andShould(not(beAnnotatedWith(previewAnnotationWithNoMediaType))) .because(reason); ArchRule methodRule = methods().that() .areAnnotatedWith(Preview.class) .should() .beAnnotatedWith(Deprecated.class) + .andShould(not(beAnnotatedWith(previewAnnotationWithNoMediaType))) .because(reason); ArchRule enumFieldsRule = fields().that() @@ -48,6 +65,39 @@ public void testPreviewsAreFlaggedAsDeprecated() { .areAnnotatedWith(Preview.class) .should() .beAnnotatedWith(Deprecated.class) + .andShould(not(beAnnotatedWith(previewAnnotationWithNoMediaType))) + .because(reason); + + classRule.check(classFiles); + enumFieldsRule.check(classFiles); + methodRule.check(classFiles); + + } + + @Test + public void testBetaApisAreFlaggedAsDeprecated() { + + String reason = "all beta APIs must be annotated as @Deprecated until they are promoted to stable"; + + ArchRule classRule = classes().that() + .areAnnotatedWith(BetaApi.class) + .should() + .beAnnotatedWith(Deprecated.class) + .because(reason); + + ArchRule methodRule = methods().that() + .areAnnotatedWith(BetaApi.class) + .should() + .beAnnotatedWith(Deprecated.class) + .because(reason); + + ArchRule enumFieldsRule = fields().that() + .areDeclaredInClassesThat() + .areEnums() + .and() + .areAnnotatedWith(BetaApi.class) + .should() + .beAnnotatedWith(Deprecated.class) .because(reason); classRule.check(classFiles);