为了账号安全,请及时绑定邮箱和手机立即绑定

如何使用archunit验证方法注释是否使用具有特定值的属性

如何使用archunit验证方法注释是否使用具有特定值的属性

holdtom 2022-06-04 09:57:26
我有一个@Audit注释,它有许多可选属性,我需要useAccount = true对某些包强制使用一个布尔属性。我正在尝试使用 archunit 来完成此验证,这样每当开发人员提交违反规则的代码时,CI 就会破坏并通知团队。这会破坏构建:@Auditpublic myMethod(...) {...}这是正确的方法:@Audit(useAccount = true)public myMethod(...) {...}问题是 Archunit 目前不支持对方法进行断言。我期待做类似的事情:methods().that().resideInAnyPackage("..controllers..", "..service..").and().areAnnotatedWith(Audit.class).should(attributeCheckCondition)然后我的自定义条件attributeCheckCondition将负责查看属性值。当我们检索类时,有没有办法检索方法?无需编写更复杂的谓词和条件?
查看完整描述

3 回答

?
偶然的你

TA贡献1841条经验 获得超3个赞

更新

从 ArchUnit 0.10.0 开始,可以为成员创建规则。


methods().that().areDeclaredInClassesThat().resideInAnyPackage("..controllers..", "..service..").and().areAnnotatedWith(Audit.class).should(attributeCheckCondition)

另请参阅用户指南中的编写成员规则。


原始答案

由于目前没有可用于方法的基本规则定义,因此需要一个中间步骤。ArchUnitClassesTransformer可以将 JavaClasses 转换为其他类型的集合。


ClassesTransformer<JavaMethod> methods = new AbstractClassesTransformer<JavaMethod>("methods") {

    @Override

    public Iterable<JavaMethod> doTransform(JavaClasses javaClasses) {

        Set<JavaMethod> allMethods = new HashSet<>();

        for (JavaClass javaClass : javaClasses) {

            allMethods.addAll(javaClass.getMethods());

        }

        return allMethods;

    }

};

然后ClassesTransformer可以将其用作自定义规则定义的基础。


ArchRule rule = ArchRuleDefinition.all(methods).that(owner(resideInAnyPackage("..controllers..", "..service.."))).and(annotatedWith(Audit.class)).should(haveAttributeValue());

rule.check(javaClasses);

另请参阅用户指南中的自定义概念规则和本期。


查看完整回答
反对 回复 2022-06-04
?
凤凰求蛊

TA贡献1825条经验 获得超4个赞

这是除了@raspacorp(启发了我!)之外的另一个自定义示例。


为了检查@Secured(ROLE)方法注释,我实现了以下规则:


public static class SecuredByRoleArchCondition extends ArchCondition<JavaMethod> {

    private final String[] expectedRoles;


    public SecuredByRoleArchCondition(String[] expectedRoles) {

        super(String.format("accessed by @Secured methods with roles %s", Arrays.toString(expectedRoles)));

        this.expectedRoles = expectedRoles;

    }


    public static SecuredByRoleArchCondition haveSecuredAnnotationWithRoles(String... expectedRoles) {

        return new SecuredByRoleArchCondition(expectedRoles);

    }


    @Override

    public void check(JavaMethod javaMethod, ConditionEvents events) {

        if (!javaMethod.isAnnotatedWith(Secured.class)) {

            String message = String.format("Method %s annotation @Secured(%s) is missing",

                    javaMethod.getFullName(), Arrays.toString(expectedRoles));

            events.add(SimpleConditionEvent.violated(javaMethod, message));

            return;

        }

        String[] annotationRoleValues = javaMethod.getAnnotationOfType(Secured.class).value();

        if (!Arrays.equals(annotationRoleValues, expectedRoles)) {

            String message = String.format("Method %s @Secured with %s has wrong roles, expected %s instead",

                    javaMethod.getFullName(), Arrays.toString(annotationRoleValues), Arrays.toString(expectedRoles));

            events.add(SimpleConditionEvent.violated(javaMethod, message));

        }

    }

}

这是此 archCondition 的示例用法:


@ArchTest

static ArchRule admin_actions_with_post_mapping_should_be_secured_by_ADMIN_WRITE_role =

        methods()

                .that().areDeclaredInClassesThat().resideInAnyPackage(ADMIN_PACKAGES)

                .and().areAnnotatedWith(PostMapping.class)

                .should(haveSecuredAnnotationWithRoles("ADMIN_WRITE"));


查看完整回答
反对 回复 2022-06-04
?
开满天机

TA贡献1786条经验 获得超13个赞

我找到了一种在类上使用自定义谓词和条件的方法,当我这样做时,我没有意识到 Roland 的响应似乎更好,因为它提供了一种从方法角度表达规则断言的方法,这就是为什么我要求。


但是我想在这里发布解决方案,以便对其他人有用。


DescribedPredicate<JavaClass> HAVE_A_METHOD_ANNOTATED_WITH_AUDIT =

    new DescribedPredicate<JavaClass>("have a method annotated with @Audit")

    {

        @Override

        public boolean apply(JavaClass input)

        {

            return input.getMethods().stream().anyMatch(method -> method.isAnnotatedWith(Audit.class));

        }

    };


ArchCondition<JavaClass> ONLY_SET_ATTRIBUTE_USE_ACCOUNT_SET_TO_TRUE =

    new ArchCondition<JavaClass>("only set useAccount attribute to true")

    {

        @Override

        public void check(JavaClass item, ConditionEvents events)

        {

            item.getMethods().stream().filter(method ->

           method.isAnnotatedWith(Audit.class) && !method.getAnnotationOfType(Audit.class)

                                                            .useAccount()

            )

                .forEach(method -> {

                    String message = String.format(

                        "Method %s is annotated with @Audit but useAccount is not set to true",

                        method.getFullName());

                    events.add(SimpleConditionEvent.violated(method, message));

                });

        }

    };

那么规则表示为:


ArchRule ANNOTATION_RULE = classes()

    .that()

    .resideInAnyPackage("..controller..", "..service..")

    .and(HAVE_A_METHOD_ANNOTATED_WITH_AUDIT)

    .should(ONLY_SET_ATTRIBUTE_USE_ACCOUNT_SET_TO_TRUE);


查看完整回答
反对 回复 2022-06-04
  • 3 回答
  • 0 关注
  • 116 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信