Skip to content
This repository was archived by the owner on Jul 14, 2026. It is now read-only.

Commit cac6416

Browse files
authored
Merge pull request #73 from bright-room/claude/issue-58-20260309-1809
Close #58: Add WebFlux custom rollout strategy example
2 parents 37a1040 + 411935e commit cac6416

10 files changed

Lines changed: 134 additions & 0 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
plugins {
2+
id("example-base")
3+
id("spotless-java")
4+
}
5+
6+
dependencies {
7+
implementation(project(":spring:spring-webflux"))
8+
implementation(libs.spring.boot.starter.webflux)
9+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package net.brightroom.example.customrolloutstrategy;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class Application {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(Application.class, args);
11+
}
12+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package net.brightroom.example.customrolloutstrategy;
2+
3+
import net.brightroom.endpointgate.core.annotation.EndpointGate;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
import org.springframework.web.bind.annotation.RestController;
6+
import reactor.core.publisher.Mono;
7+
8+
@RestController
9+
public class DemoController {
10+
11+
@GetMapping("/api/feature")
12+
@EndpointGate("gradual-feature")
13+
public Mono<String> feature() {
14+
return Mono.just("You are in the rollout group for gradual-feature!");
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package net.brightroom.example.customrolloutstrategy;
2+
3+
import java.util.Map;
4+
import net.brightroom.endpointgate.core.context.EndpointGateContext;
5+
import net.brightroom.endpointgate.reactive.core.rollout.ReactiveRolloutStrategy;
6+
import org.springframework.context.annotation.Profile;
7+
import org.springframework.stereotype.Component;
8+
import reactor.core.publisher.Mono;
9+
10+
@Component
11+
@Profile("region")
12+
public class RegionBasedReactiveRolloutStrategy implements ReactiveRolloutStrategy {
13+
14+
private static final Map<String, Integer> REGION_WEIGHT =
15+
Map.of(
16+
"us-east", 0,
17+
"eu-west", 50,
18+
"ap-northeast", 80);
19+
20+
@Override
21+
public Mono<Boolean> isInRollout(String gateId, EndpointGateContext context, int percentage) {
22+
String region = context.userIdentifier();
23+
int weight = REGION_WEIGHT.getOrDefault(region, 100);
24+
return Mono.just(weight < percentage);
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package net.brightroom.example.customrolloutstrategy;
2+
3+
import net.brightroom.endpointgate.core.context.EndpointGateContext;
4+
import net.brightroom.endpointgate.spring.webflux.context.ReactiveEndpointGateContextResolver;
5+
import org.springframework.context.annotation.Profile;
6+
import org.springframework.http.server.reactive.ServerHttpRequest;
7+
import org.springframework.stereotype.Component;
8+
import reactor.core.publisher.Mono;
9+
10+
@Component
11+
@Profile("region")
12+
public class RegionReactiveContextResolver implements ReactiveEndpointGateContextResolver {
13+
14+
@Override
15+
public Mono<EndpointGateContext> resolve(ServerHttpRequest request) {
16+
String region = request.getHeaders().getFirst("X-Region");
17+
if (region == null) {
18+
return Mono.empty();
19+
}
20+
if (region.isBlank()) {
21+
return Mono.empty();
22+
}
23+
return Mono.just(new EndpointGateContext(region));
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package net.brightroom.example.customrolloutstrategy;
2+
3+
import java.time.LocalTime;
4+
import java.time.ZoneId;
5+
import net.brightroom.endpointgate.core.context.EndpointGateContext;
6+
import net.brightroom.endpointgate.reactive.core.rollout.DefaultReactiveRolloutStrategy;
7+
import net.brightroom.endpointgate.reactive.core.rollout.ReactiveRolloutStrategy;
8+
import org.springframework.context.annotation.Profile;
9+
import org.springframework.stereotype.Component;
10+
import reactor.core.publisher.Mono;
11+
12+
@Component
13+
@Profile("time-based")
14+
public class TimeBasedReactiveRolloutStrategy implements ReactiveRolloutStrategy {
15+
16+
private static final LocalTime BUSINESS_START = LocalTime.of(9, 0);
17+
private static final LocalTime BUSINESS_END = LocalTime.of(17, 0);
18+
private static final ZoneId ZONE = ZoneId.of("Asia/Tokyo");
19+
20+
private final DefaultReactiveRolloutStrategy defaultStrategy =
21+
new DefaultReactiveRolloutStrategy();
22+
23+
@Override
24+
public Mono<Boolean> isInRollout(String gateId, EndpointGateContext context, int percentage) {
25+
LocalTime now = LocalTime.now(ZONE);
26+
if (now.isBefore(BUSINESS_START)) {
27+
return Mono.just(false);
28+
}
29+
if (!now.isBefore(BUSINESS_END)) {
30+
return Mono.just(false);
31+
}
32+
return defaultStrategy.isInRollout(gateId, context, percentage);
33+
}
34+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# リージョンベースのロールアウト戦略を有効化
2+
# X-Region ヘッダーでリージョンを指定すると、リージョンごとの重みに基づいてロールアウト判定が行われる
3+
# 例: us-east (weight=0) + rollout=60 → 許可、ap-northeast (weight=80) + rollout=60 → 拒否
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# 時間帯ベースのロールアウト戦略を有効化
2+
# 営業時間内 (09:00-17:00 Asia/Tokyo) のみロールアウトを適用し、時間外はアクセスを拒否する
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
endpoint-gate:
2+
default-enabled: true
3+
gates:
4+
gradual-feature:
5+
enabled: true
6+
rollout: 60

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,6 @@ include(
5858
"examples:webflux:basic-usage",
5959
"examples:webflux:functional-endpoint",
6060
"examples:webflux:rollout",
61+
"examples:webflux:custom-rollout-strategy",
6162
"examples:webflux:schedule",
6263
)

0 commit comments

Comments
 (0)