8000 Ensure Required Annotations Based on Presence of Specified Annotation… by mnhock · Pull Request #103 · enofex/taikai · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Ensure Required Annotations Based on Presence of Specified Annotation… #103

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

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ The default mode is `WITHOUT_TESTS`, which excludes test classes from the import
| General | `classesAnnotatedWithShouldResideInPackage` | Classes annotated with a specific annotation should reside in a specified package. |
| General | `classesShouldResideOutsidePackage` | Classes matching specific naming patterns should reside outside a specified package. |
| General | `classesShouldBeAnnotatedWith` | Classes matching specific naming patterns should be annotated with a specified annotation. |
| General | `classesShouldBeAnnotatedWithAll` | Classes annotated with a specific annotation should be annotated with a specified annotations. |
| General | `classesShouldNotBeAnnotatedWith` | Classes matching specific naming patterns should not be annotated with a specified annotation. |
| General | `classesShouldBeAssignableTo` | Classes matching specific naming patterns should be assignable to a certain type. |
| General | `classesShouldImplement` | Classes matching specific naming patterns should implement to a interface. |
Expand Down Expand Up @@ -261,6 +262,18 @@ Taikai.builder()
.check();
```

- **Classes Annotated with a Specified Annotation Should Be Annotated with Specified Annotations**: Ensure that classes annotated with a specific annotations should be annotated with the specified annotations.

```java
Taikai.builder()
.namespace("com.company.project")
.java(java -> java
.classesShouldNotBeAnnotatedWithAll(Modifying.class, List.of(Transactional.class, Query.class))
.classesShouldNotBeAnnotatedWithAll("org.springframework.data.jpa.repository.Modifying", List.of("org.springframework.transaction.annotation.Transactional", "org.springframework.data.jpa.repository.Query"))
.build()
.check();
```

- **Classes Should Not Be Annotated with Specified Annotation**: Ensure that classes matching a specific regex pattern are not annotated with the specified annotation.

```java
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/com/enofex/taikai/internal/DescribedPredicates.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.properties.CanBeAnnotated;
import java.util.Collection;

/**
* Internal utility class for defining general DescribedPredicate used in architectural rules.
Expand Down Expand Up @@ -35,6 +36,25 @@ public boolean test(CanBeAnnotated canBeAnnotated) {
};
}

/**
* Creates a predicate that checks if an element is annotated with all the specified annotations.
*
* @param annotations the collection of annotations to check for
* @param isMetaAnnotated true if the annotations should be meta-annotated, false otherwise
* @return a described predicate for the annotation check
*/
public static DescribedPredicate<CanBeAnnotated> annotatedWithAll(Collection<String> annotations,
boolean isMetaAnnotated) {
return new DescribedPredicate<>("annotated with all of %s".formatted(annotations)) {
@Override
public boolean test(CanBeAnnotated canBeAnnotated) {
return annotations.stream().allMatch(annotation ->
isMetaAnnotated ? canBeAnnotated.isMetaAnnotatedWith(annotation)
: canBeAnnotated.isAnnotatedWith(annotation));
}
};
}

/**
* Creates a predicate that checks if a class is final.
*
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/enofex/taikai/java/JavaConfigurer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static com.enofex.taikai.TaikaiRule.Configuration.defaultConfiguration;
import static com.enofex.taikai.internal.ArchConditions.notBePublicButNotStatic;
import static com.enofex.taikai.internal.DescribedPredicates.annotatedWithAll;
import static com.enofex.taikai.internal.DescribedPredicates.areFinal;
import static com.enofex.taikai.java.Deprecations.notUseDeprecatedAPIs;
import static com.enofex.taikai.java.HashCodeAndEquals.implementHashCodeAndEquals;
Expand All @@ -11,6 +12,7 @@
import static com.enofex.taikai.java.SerialVersionUID.namedSerialVersionUID;
import static com.enofex.taikai.java.UtilityClasses.havePrivateConstructor;
import static com.enofex.taikai.java.UtilityClasses.utilityClasses;
import static com.tngtech.archunit.lang.conditions.ArchConditions.be;
import static com.tngtech.archunit.lang.conditions.ArchConditions.beFinal;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;
Expand All @@ -24,6 +26,7 @@
import com.enofex.taikai.configures.Customizer;
import com.enofex.taikai.configures.DisableableConfigurer;
import java.lang.annotation.Annotation;
import java.util.Collection;

public class JavaConfigurer extends AbstractConfigurer {

Expand Down Expand Up @@ -113,6 +116,29 @@ public JavaConfigurer classesShouldResideOutsidePackage(String regex, String pac
regex, packageIdentifier)), configuration));
}


public JavaConfigurer classesShouldBeAnnotatedWithAll(Class<? extends Annotation> annotationType,
Collection<Class<? extends Annotation>> requiredAnnotationTypes) {
return classesShouldBeAnnotatedWithAll(annotationType.getName(),
requiredAnnotationTypes.stream().map(Class::getName).toList(), defaultConfiguration());
}

public JavaConfigurer classesShouldBeAnnotatedWithAll(String annotationType,
Collection<String> requiredAnnotationTypes) {
return classesShouldBeAnnotatedWithAll(annotationType, requiredAnnotationTypes,
defaultConfiguration());
}

public JavaConfigurer classesShouldBeAnnotatedWithAll(String annotationType,
Collection<String> requiredAnnotationTypes, Configuration configuration) {
return addRule(TaikaiRule.of(classes()
.that().areMetaAnnotatedWith(annotationType)
.should(be(annotatedWithAll(requiredAnnotationTypes, true)))
.as("Classes annotated with %s should be annotated with %s".formatted(
annotationType, String.join(", ", requiredAnnotationTypes))),
configuration));
}

public JavaConfigurer classesShouldBeAnnotatedWith(String regex,
Class<? extends Annotation> annotationType) {
return classesShouldBeAnnotatedWith(regex, annotationType.getName(), defaultConfiguration());
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/com/enofex/taikai/java/NamingConfigurer.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public NamingConfigurer packagesShouldMatch(String packageIdentifier) {
return packagesShouldMatch(packageIdentifier, defaultConfiguration());
}

public NamingConfigurer packagesShouldMatch(String packageIdentifier, Configuration configuration) {
public NamingConfigurer packagesShouldMatch(String packageIdentifier,
Configuration configuration) {
return addRule(TaikaiRule.of(classes()
.should().resideInAPackage(packageIdentifier)
.as("Package names should match %s".formatted(packageIdentifier)),
Expand All @@ -52,6 +53,11 @@ public NamingConfigurer classesAnnotatedWithShouldMatch(
return classesAnnotatedWithShouldMatch(annotationType.getName(), regex, defaultConfiguration());
}

public NamingConfigurer classesAnnotatedWithShouldMatch(
Class<? extends Annotation> annotationType, String regex, Configuration configuration) {
return classesAnnotatedWithShouldMatch(annotationType.getName(), regex, configuration);
}

public NamingConfigurer classesAnnotatedWithShouldMatch(String annotationType, String regex) {
return classesAnnotatedWithShouldMatch(annotationType, regex, defaultConfiguration());
}
Expand Down
Loading
0