Skip to content

Commit 288023c

Browse files
authored
feat: basic object flags (#141)
Signed-off-by: bblandSigned-off-by: bbland1 <104288486+bbland1@users.noreply.github.com>
1 parent b867485 commit 288023c

File tree

28 files changed

+737
-94
lines changed

28 files changed

+737
-94
lines changed

go.sum

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,8 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
66
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
77
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
88
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
9-
dagger.io/dagger v0.18.9 h1:IXZhlGm893LuqYFpo6VHtaCAEP6Qz0VjMhLvyKQVl1Y=
10-
dagger.io/dagger v0.18.9/go.mod h1:e6Y+sAPWh04pHvBf4s3sSiOe1QMoCEcccmMv898RnZA=
119
dagger.io/dagger v0.18.10 h1:Ibyz5LqxjjEHfLMlaU9PJ3xt3ju7p29RWy0lVfvSNU0=
1210
dagger.io/dagger v0.18.10/go.mod h1:VSj+2HMd/EnaCVt7gTY70p8LBW+oQDYjA1XTadr8vBE=
13-
github.com/99designs/gqlgen v0.17.73 h1:A3Ki+rHWqKbAOlg5fxiZBnz6OjW3nwupDHEG15gEsrg=
14-
github.com/99designs/gqlgen v0.17.73/go.mod h1:2RyGWjy2k7W9jxrs8MOQthXGkD3L3oGr0jXW3Pu8lGg=
1511
github.com/99designs/gqlgen v0.17.74 h1:1FuVtkXxOc87xpKio3f6sohREmec+Jvy86PcYOuwgWo=
1612
github.com/99designs/gqlgen v0.17.74/go.mod h1:a+iR6mfRLNRp++kDpooFHiPWYiWX3Yu1BIilQRHgh10=
1713
github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs=
@@ -108,8 +104,6 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej
108104
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
109105
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
110106
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
111-
github.com/pterm/pterm v0.12.80 h1:mM55B+GnKUnLMUSqhdINe4s6tOuVQIetQ3my8JGyAIg=
112-
github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaKGQlHo=
113107
github.com/pterm/pterm v0.12.81 h1:ju+j5I2++FO1jBKMmscgh5h5DPFDFMB7epEjSoKehKA=
114108
github.com/pterm/pterm v0.12.81/go.mod h1:TyuyrPjnxfwP+ccJdBTeWHtd/e0ybQHkOS/TakajZCw=
115109
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -210,15 +204,11 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
210204
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
211205
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
212206
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
213-
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
214-
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
215207
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
216208
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
217209
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
218210
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
219211
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
220-
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
221-
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
222212
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
223213
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
224214
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -246,8 +236,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
246236
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
247237
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
248238
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
249-
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
250-
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
251239
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
252240
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
253241
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -259,8 +247,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:
259247
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw=
260248
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
261249
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
262-
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
263-
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
264250
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
265251
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
266252
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=

internal/cmd/testdata/success_csharp.golden

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,38 @@ namespace TestNamespace
153153
return await _client.GetStringDetailsAsync("greetingMessage", "Hello there!", evaluationContext, options);
154154
}
155155

156+
/// <summary>
157+
/// Allows customization of theme colors.
158+
/// </summary>
159+
/// <remarks>
160+
/// <para>Flag key: themeCustomization</para>
161+
/// <para>Default value: new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build())</para>
162+
/// <para>Type: object</para>
163+
/// </remarks>
164+
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
165+
/// <param name="options">Options for flag evaluation</param>
166+
/// <returns>The flag value</returns>
167+
public async Task<Value> ThemeCustomizationAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
168+
{
169+
return await _client.GetObjectValueAsync("themeCustomization", new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build()), evaluationContext, options);
170+
}
171+
172+
/// <summary>
173+
/// Allows customization of theme colors.
174+
/// </summary>
175+
/// <remarks>
176+
/// <para>Flag key: themeCustomization</para>
177+
/// <para>Default value: new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build())</para>
178+
/// <para>Type: object</para>
179+
/// </remarks>
180+
/// <param name="evaluationContext">Optional context for the flag evaluation</param>
181+
/// <param name="options">Options for flag evaluation</param>
182+
/// <returns>The evaluation details containing the flag value and metadata</returns>
183+
public async Task<FlagEvaluationDetails<Value>> ThemeCustomizationDetailsAsync(EvaluationContext? evaluationContext = null, FlagEvaluationOptions? options = null)
184+
{
185+
return await _client.GetObjectDetailsAsync("themeCustomization", new Value(Structure.Builder().Set("primaryColor", "#007bff").Set("secondaryColor", "#6c757d").Build()), evaluationContext, options);
186+
}
187+
156188
/// <summary>
157189
/// Maximum allowed length for usernames.
158190
/// </summary>

internal/cmd/testdata/success_go.golden

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ type IntProvider func(ctx context.Context, evalCtx openfeature.EvaluationContext
1414
type IntProviderDetails func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.IntEvaluationDetails, error)
1515
type StringProvider func(ctx context.Context, evalCtx openfeature.EvaluationContext) (string, error)
1616
type StringProviderDetails func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.StringEvaluationDetails, error)
17+
type ObjectProvider func(ctx context.Context, evalCtx openfeature.EvaluationContext) (any, error)
18+
type ObjectProviderDetails func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.InterfaceEvaluationDetails, error)
1719

1820
var client openfeature.IClient = nil
1921
// Discount percentage applied to purchases.
@@ -67,6 +69,23 @@ var GreetingMessage = struct {
6769
return client.StringValueDetails(ctx, "greetingMessage", "Hello there!", evalCtx)
6870
},
6971
}
72+
// Allows customization of theme colors.
73+
var ThemeCustomization = struct {
74+
// Value returns the value of the flag ThemeCustomization,
75+
// as well as the evaluation error, if present.
76+
Value ObjectProvider
77+
78+
// ValueWithDetails returns the value of the flag ThemeCustomization,
79+
// the evaluation error, if any, and the evaluation details.
80+
ValueWithDetails ObjectProviderDetails
81+
}{
82+
Value: func(ctx context.Context, evalCtx openfeature.EvaluationContext) (any, error) {
83+
return client.ObjectValue(ctx, "themeCustomization", map[string]any{"primaryColor": "#007bff", "secondaryColor": "#6c757d"}, evalCtx)
84+
},
85+
ValueWithDetails: func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.InterfaceEvaluationDetails, error){
86+
return client.ObjectValueDetails(ctx, "themeCustomization", map[string]any{"primaryColor": "#007bff", "secondaryColor": "#6c757d"}, evalCtx)
87+
},
88+
}
7089
// Maximum allowed length for usernames.
7190
var UsernameMaxLength = struct {
7291
// Value returns the value of the flag UsernameMaxLength,

internal/cmd/testdata/success_java.golden

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,27 @@ public final class OpenFeature {
7171
* Returns the evaluation details containing the flag value and metadata
7272
*/
7373
FlagEvaluationDetails<String> greetingMessageDetails(EvaluationContext ctx);
74+
75+
/**
76+
* Allows customization of theme colors.
77+
* Details:
78+
* - Flag key: themeCustomization
79+
* - Type: Object
80+
* - Default value: Map.of("primaryColor", "#007bff", "secondaryColor", "#6c757d")
81+
* Returns the flag value
82+
*/
83+
Object themeCustomization(EvaluationContext ctx);
7484

85+
/**
86+
* Allows customization of theme colors.
87+
* Details:
88+
* - Flag key: themeCustomization
89+
* - Type: Object
90+
* - Default value: Map.of("primaryColor", "#007bff", "secondaryColor", "#6c757d")
91+
* Returns the evaluation details containing the flag value and metadata
92+
*/
93+
FlagEvaluationDetails<Object> themeCustomizationDetails(EvaluationContext ctx);
94+
7595
/**
7696
* Maximum allowed length for usernames.
7797
* Details:
@@ -131,7 +151,17 @@ public final class OpenFeature {
131151
public FlagEvaluationDetails<String> greetingMessageDetails(EvaluationContext ctx) {
132152
return client.getStringDetails("greetingMessage", "Hello there!", ctx);
133153
}
154+
155+
@Override
156+
public Object themeCustomization(EvaluationContext ctx) {
157+
return client.getObjectValue("themeCustomization", Map.of("primaryColor", "#007bff", "secondaryColor", "#6c757d"), ctx);
158+
}
134159

160+
@Override
161+
public FlagEvaluationDetails<Object> themeCustomizationDetails(EvaluationContext ctx) {
162+
return client.getObjectDetails("themeCustomization", Map.of("primaryColor", "#007bff", "secondaryColor", "#6c757d"), ctx);
163+
}
164+
135165
@Override
136166
public Integer usernameMaxLength(EvaluationContext ctx) {
137167
return client.getIntegerValue("usernameMaxLength", 50, ctx);

internal/cmd/testdata/success_nestjs.golden

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import type {
88
EvaluationContext,
99
EvaluationDetails,
1010
OpenFeatureModuleOptions,
11+
JsonValue
1112
} from "@openfeature/nestjs-sdk";
12-
import { OpenFeatureModule, BooleanFeatureFlag, StringFeatureFlag, NumberFeatureFlag } from "@openfeature/nestjs-sdk";
13+
import { OpenFeatureModule, BooleanFeatureFlag, StringFeatureFlag, NumberFeatureFlag, ObjectFeatureFlag } from "@openfeature/nestjs-sdk";
1314

1415
import type { GeneratedClient } from "./openfeature";
1516
import { getGeneratedClient } from "./openfeature";
@@ -133,7 +134,7 @@ export function DiscountPercentage(props?: TypedFeatureProps): ParameterDecorato
133134
* @Get("/")
134135
* public async handleRequest(
135136
* @EnableFeatureA()
136-
* enableFeatureA: Observable<EvaluationDetails<number>>,
137+
* enableFeatureA: Observable<EvaluationDetails<boolean>>,
137138
* )
138139
* ```
139140
* @param {TypedFeatureProps} props The options for injecting the feature flag.
@@ -158,7 +159,7 @@ export function EnableFeatureA(props?: TypedFeatureProps): ParameterDecorator {
158159
* @Get("/")
159160
* public async handleRequest(
160161
* @GreetingMessage()
161-
* greetingMessage: Observable<EvaluationDetails<number>>,
162+
* greetingMessage: Observable<EvaluationDetails<string>>,
162163
* )
163164
* ```
164165
* @param {TypedFeatureProps} props The options for injecting the feature flag.
@@ -168,6 +169,31 @@ export function GreetingMessage(props?: TypedFeatureProps): ParameterDecorator {
168169
return StringFeatureFlag({ flagKey: "greetingMessage", defaultValue: "Hello there!", ...props });
169170
}
170171

172+
/**
173+
* Gets the {@link EvaluationDetails} for `themeCustomization` from a domain scoped or the default OpenFeature
174+
* client and populates the annotated parameter with the {@link EvaluationDetails} wrapped in an {@link Observable}.
175+
*
176+
* **Details:**
177+
* - flag key: `themeCustomization`
178+
* - description: `Allows customization of theme colors.`
179+
* - default value: `{"primaryColor":"#007bff","secondaryColor":"#6c757d"}`
180+
* - type: `JsonValue`
181+
*
182+
* Usage:
183+
* ```typescript
184+
* @Get("/")
185+
* public async handleRequest(
186+
* @ThemeCustomization()
187+
* themeCustomization: Observable<EvaluationDetails<JsonValue>>,
188+
* )
189+
* ```
190+
* @param {TypedFeatureProps} props The options for injecting the feature flag.
191+
* @returns {ParameterDecorator} The decorator function.
192+
*/
193+
export function ThemeCustomization(props?: TypedFeatureProps): ParameterDecorator {
194+
return ObjectFeatureFlag({ flagKey: "themeCustomization", defaultValue: {"primaryColor":"#007bff","secondaryColor":"#6c757d"}, ...props });
195+
}
196+
171197
/**
172198
* Gets the {@link EvaluationDetails} for `usernameMaxLength` from a domain scoped or the default OpenFeature
173199
* client and populates the annotated parameter with the {@link EvaluationDetails} wrapped in an {@link Observable}.

internal/cmd/testdata/success_nodejs.golden

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
OpenFeature,
44
stringOrUndefined,
55
objectOrUndefined,
6+
JsonValue,
67
} from "@openfeature/server-sdk";
78
import type {
89
EvaluationContext,
@@ -101,6 +102,36 @@ export interface GeneratedClient {
101102
*/
102103
greetingMessageDetails(context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<EvaluationDetails<string>>;
103104

105+
/**
106+
* Allows customization of theme colors.
107+
*
108+
* **Details:**
109+
* - flag key: `themeCustomization`
110+
* - default value: `{"primaryColor":"#007bff","secondaryColor":"#6c757d"}`
111+
* - type: `JsonValue`
112+
*
113+
* Performs a flag evaluation that returns a object.
114+
* @param {EvaluationContext} context The evaluation context used on an individual flag evaluation
115+
* @param {FlagEvaluationOptions} options Additional flag evaluation options
116+
* @returns {Promise<JsonValue>} Flag evaluation response
117+
*/
118+
themeCustomization(context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<JsonValue>;
119+
120+
/**
121+
* Allows customization of theme colors.
122+
*
123+
* **Details:**
124+
* - flag key: `themeCustomization`
125+
* - default value: `{"primaryColor":"#007bff","secondaryColor":"#6c757d"}`
126+
* - type: `JsonValue`
127+
*
128+
* Performs a flag evaluation that a returns an evaluation details object.
129+
* @param {EvaluationContext} context The evaluation context used on an individual flag evaluation
130+
* @param {FlagEvaluationOptions} options Additional flag evaluation options
131+
* @returns {Promise<EvaluationDetails<JsonValue>>} Flag evaluation details response
132+
*/
133+
themeCustomizationDetails(context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<EvaluationDetails<JsonValue>>;
134+
104135
/**
105136
* Maximum allowed length for usernames.
106137
*
@@ -185,6 +216,14 @@ export function getGeneratedClient(domainOrContext?: string | EvaluationContext,
185216
return client.getStringDetails("greetingMessage", "Hello there!", context, options);
186217
},
187218

219+
themeCustomization: (context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<JsonValue> => {
220+
return client.getObjectValue("themeCustomization", {"primaryColor":"#007bff","secondaryColor":"#6c757d"}, context, options);
221+
},
222+
223+
themeCustomizationDetails: (context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<EvaluationDetails<JsonValue>> => {
224+
return client.getObjectDetails("themeCustomization", {"primaryColor":"#007bff","secondaryColor":"#6c757d"}, context, options);
225+
},
226+
188227
usernameMaxLength: (context?: EvaluationContext, options?: FlagEvaluationOptions): Promise<number> => {
189228
return client.getNumberValue("usernameMaxLength", 50, context, options);
190229
},

0 commit comments

Comments
 (0)