33import dev .openfeature .sdk .OpenFeatureAPI ;
44import dev .openfeature .sdk .providers .memory .Flag ;
55import java .lang .reflect .Method ;
6+ import java .util .AbstractMap ;
67import java .util .Arrays ;
78import java .util .HashMap ;
9+ import java .util .HashSet ;
10+ import java .util .List ;
811import java .util .Map ;
12+ import java .util .Set ;
13+ import java .util .stream .Collectors ;
14+ import java .util .stream .Stream ;
915import org .apache .commons .lang3 .BooleanUtils ;
1016import org .junit .jupiter .api .extension .AfterEachCallback ;
1117import org .junit .jupiter .api .extension .BeforeEachCallback ;
@@ -23,37 +29,127 @@ public class OpenFeatureExtension implements BeforeEachCallback, AfterEachCallba
2329
2430 private static Map <String , Map <String , Flag <?>>> handleExtendedConfiguration (
2531 ExtensionContext extensionContext , Map <String , Map <String , Flag <?>>> configuration ) {
26- PioneerAnnotationUtils .findAllEnclosingRepeatableAnnotations (extensionContext , OpenFeature .class )
27- .forEachOrdered (annotation -> {
28- Map <String , Flag <?>> domainFlags = configuration .getOrDefault (annotation .domain (), new HashMap <>());
29-
30- Arrays .stream (annotation .value ())
31- .filter (flag -> !domainFlags .containsKey (flag .name ()))
32- .forEach (flag -> {
33- Flag .FlagBuilder <?> builder = generateFlagBuilder (flag );
34- domainFlags .put (flag .name (), builder .build ());
35- });
36- configuration .put (annotation .domain (), domainFlags );
37- });
32+ List <OpenFeature > openFeatureAnnotationList = PioneerAnnotationUtils .findAllEnclosingRepeatableAnnotations (
33+ extensionContext , OpenFeature .class )
34+ .collect (Collectors .toList ());
35+ Map <String , Set <String >> nonTypedFlagNamesByDomain = getFlagNamesByDomain (openFeatureAnnotationList );
36+ openFeatureAnnotationList .forEach (annotation -> {
37+ Map <String , Flag <?>> domainFlags = configuration .getOrDefault (annotation .domain (), new HashMap <>());
38+
39+ Arrays .stream (annotation .value ())
40+ .filter (flag -> !domainFlags .containsKey (flag .name ()))
41+ .forEach (flag -> {
42+ Flag .FlagBuilder <?> builder = generateFlagBuilder (flag );
43+ domainFlags .put (flag .name (), builder .build ());
44+ });
45+ addTypedFlags (
46+ annotation ,
47+ domainFlags ,
48+ nonTypedFlagNamesByDomain .getOrDefault (annotation .domain (), new HashSet <>()));
49+ configuration .put (annotation .domain (), domainFlags );
50+ });
3851 return configuration ;
3952 }
4053
54+ private static Map <String , Set <String >> getFlagNamesByDomain (List <OpenFeature > openFeatureList ) {
55+ return openFeatureList .stream ()
56+ .map (o -> {
57+ Set <String > flagNames = Arrays .stream (o .value ())
58+ .map (dev .openfeature .contrib .tools .junitopenfeature .Flag ::name )
59+ .collect (Collectors .toSet ());
60+ return new AbstractMap .SimpleEntry <>(o .domain (), flagNames );
61+ })
62+ .collect (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue , (t1 , t2 ) -> {
63+ t1 .addAll (t2 );
64+ return t1 ;
65+ }));
66+ }
67+
68+ private static void addTypedFlags (OpenFeature annotation , Map <String , Flag <?>> domainFlags , Set <String > flagNames ) {
69+ addBooleanFlags (Arrays .stream (annotation .booleanFlags ()), domainFlags , flagNames );
70+ addStringFlags (Arrays .stream (annotation .stringFlags ()), domainFlags , flagNames );
71+ addIntegerFlags (Arrays .stream (annotation .integerFlags ()), domainFlags , flagNames );
72+ addDoubleFlags (Arrays .stream (annotation .doubleFlags ()), domainFlags , flagNames );
73+ }
74+
75+ private static void addBooleanFlags (
76+ Stream <BooleanFlag > booleanFlags , Map <String , Flag <?>> domainFlags , Set <String > flagNames ) {
77+
78+ booleanFlags .forEach (flag -> addFlag (domainFlags , flagNames , flag .name (), flag .value ()));
79+ }
80+
81+ private static void addStringFlags (
82+ Stream <StringFlag > stringFlags , Map <String , Flag <?>> domainFlags , Set <String > flagNames ) {
83+ stringFlags .forEach (flag -> addFlag (domainFlags , flagNames , flag .name (), flag .value ()));
84+ }
85+
86+ private static void addIntegerFlags (
87+ Stream <IntegerFlag > integerFlags , Map <String , Flag <?>> domainFlags , Set <String > flagNames ) {
88+ integerFlags .forEach (flag -> addFlag (domainFlags , flagNames , flag .name (), flag .value ()));
89+ }
90+
91+ private static void addDoubleFlags (
92+ Stream <DoubleFlag > doubleFlags , Map <String , Flag <?>> domainFlags , Set <String > flagNames ) {
93+ doubleFlags .forEach (flag -> addFlag (domainFlags , flagNames , flag .name (), flag .value ()));
94+ }
95+
96+ private static <T > void addFlag (
97+ Map <String , Flag <?>> domainFlags , Set <String > domainFlagNames , String flagName , T value ) {
98+ if (domainFlagNames .contains (flagName )) {
99+ throw new IllegalArgumentException ("Flag with name " + flagName + " already exists. "
100+ + "There shouldn't be @Flag and @" + value .getClass ().getSimpleName () + "Flag with the same name!" );
101+ }
102+
103+ if (domainFlags .containsKey (flagName )) {
104+ return ;
105+ }
106+ Flag .FlagBuilder <Object > builder =
107+ Flag .builder ().variant (String .valueOf (value ), value ).defaultVariant (String .valueOf (value ));
108+ domainFlags .put (flagName , builder .build ());
109+ }
110+
41111 private static Map <String , Map <String , Flag <?>>> handleSimpleConfiguration (ExtensionContext extensionContext ) {
42112 Map <String , Map <String , Flag <?>>> configuration = new HashMap <>();
43113 String defaultDomain = PioneerAnnotationUtils .findClosestEnclosingAnnotation (
44114 extensionContext , OpenFeatureDefaultDomain .class )
45115 .map (OpenFeatureDefaultDomain ::value )
46116 .orElse ("" );
47- PioneerAnnotationUtils .findAllEnclosingRepeatableAnnotations (
48- extensionContext , dev .openfeature .contrib .tools .junitopenfeature .Flag .class )
49- .forEachOrdered (flag -> {
50- Map <String , Flag <?>> domainFlags = configuration .getOrDefault (defaultDomain , new HashMap <>());
51- if (!domainFlags .containsKey (flag .name ())) {
52- Flag .FlagBuilder <?> builder = generateFlagBuilder (flag );
53- domainFlags .put (flag .name (), builder .build ());
54- configuration .put (defaultDomain , domainFlags );
55- }
56- });
117+ Map <String , Flag <?>> domainFlags = configuration .getOrDefault (defaultDomain , new HashMap <>());
118+ List <dev .openfeature .contrib .tools .junitopenfeature .Flag > flagList =
119+ PioneerAnnotationUtils .findAllEnclosingRepeatableAnnotations (
120+ extensionContext , dev .openfeature .contrib .tools .junitopenfeature .Flag .class )
121+ .collect (Collectors .toList ());
122+ Set <String > flagNames = flagList .stream ()
123+ .map (dev .openfeature .contrib .tools .junitopenfeature .Flag ::name )
124+ .collect (Collectors .toSet ());
125+
126+ flagList .forEach (flag -> {
127+ if (!domainFlags .containsKey (flag .name ())) {
128+ Flag .FlagBuilder <?> builder = generateFlagBuilder (flag );
129+ domainFlags .put (flag .name (), builder .build ());
130+ configuration .put (defaultDomain , domainFlags );
131+ }
132+ });
133+
134+ Stream <BooleanFlag > booleanFlags =
135+ PioneerAnnotationUtils .findAllEnclosingRepeatableAnnotations (extensionContext , BooleanFlag .class );
136+ addBooleanFlags (booleanFlags , domainFlags , flagNames );
137+
138+ Stream <StringFlag > stringFlags =
139+ PioneerAnnotationUtils .findAllEnclosingRepeatableAnnotations (extensionContext , StringFlag .class );
140+ addStringFlags (stringFlags , domainFlags , flagNames );
141+
142+ Stream <IntegerFlag > integerFlags =
143+ PioneerAnnotationUtils .findAllEnclosingRepeatableAnnotations (extensionContext , IntegerFlag .class );
144+ addIntegerFlags (integerFlags , domainFlags , flagNames );
145+
146+ Stream <DoubleFlag > doubleFlags =
147+ PioneerAnnotationUtils .findAllEnclosingRepeatableAnnotations (extensionContext , DoubleFlag .class );
148+ addDoubleFlags (doubleFlags , domainFlags , flagNames );
149+
150+ if (!domainFlags .isEmpty ()) {
151+ configuration .put (defaultDomain , domainFlags );
152+ }
57153
58154 return configuration ;
59155 }
0 commit comments