javi-forge 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ci-local/ci-local.sh +20 -8
- package/package.json +1 -1
- package/ai-config/.skillignore +0 -15
- package/ai-config/AUTO_INVOKE.md +0 -300
- package/ai-config/agents/_TEMPLATE.md +0 -93
- package/ai-config/agents/business/api-designer.md +0 -1657
- package/ai-config/agents/business/business-analyst.md +0 -1331
- package/ai-config/agents/business/product-strategist.md +0 -206
- package/ai-config/agents/business/project-manager.md +0 -178
- package/ai-config/agents/business/requirements-analyst.md +0 -1277
- package/ai-config/agents/business/technical-writer.md +0 -1679
- package/ai-config/agents/creative/ux-designer.md +0 -205
- package/ai-config/agents/data-ai/ai-engineer.md +0 -487
- package/ai-config/agents/data-ai/analytics-engineer.md +0 -953
- package/ai-config/agents/data-ai/data-engineer.md +0 -173
- package/ai-config/agents/data-ai/data-scientist.md +0 -672
- package/ai-config/agents/data-ai/mlops-engineer.md +0 -814
- package/ai-config/agents/data-ai/prompt-engineer.md +0 -772
- package/ai-config/agents/development/angular-expert.md +0 -620
- package/ai-config/agents/development/backend-architect.md +0 -795
- package/ai-config/agents/development/database-specialist.md +0 -212
- package/ai-config/agents/development/frontend-specialist.md +0 -686
- package/ai-config/agents/development/fullstack-engineer.md +0 -668
- package/ai-config/agents/development/golang-pro.md +0 -338
- package/ai-config/agents/development/java-enterprise.md +0 -400
- package/ai-config/agents/development/javascript-pro.md +0 -422
- package/ai-config/agents/development/nextjs-pro.md +0 -474
- package/ai-config/agents/development/python-pro.md +0 -570
- package/ai-config/agents/development/react-pro.md +0 -487
- package/ai-config/agents/development/rust-pro.md +0 -246
- package/ai-config/agents/development/spring-boot-4-expert.md +0 -326
- package/ai-config/agents/development/typescript-pro.md +0 -336
- package/ai-config/agents/development/vue-specialist.md +0 -605
- package/ai-config/agents/infrastructure/cloud-architect.md +0 -472
- package/ai-config/agents/infrastructure/deployment-manager.md +0 -358
- package/ai-config/agents/infrastructure/devops-engineer.md +0 -455
- package/ai-config/agents/infrastructure/incident-responder.md +0 -519
- package/ai-config/agents/infrastructure/kubernetes-expert.md +0 -705
- package/ai-config/agents/infrastructure/monitoring-specialist.md +0 -674
- package/ai-config/agents/infrastructure/performance-engineer.md +0 -658
- package/ai-config/agents/orchestrator.md +0 -241
- package/ai-config/agents/quality/accessibility-auditor.md +0 -1204
- package/ai-config/agents/quality/code-reviewer-compact.md +0 -123
- package/ai-config/agents/quality/code-reviewer.md +0 -363
- package/ai-config/agents/quality/dependency-manager.md +0 -743
- package/ai-config/agents/quality/e2e-test-specialist.md +0 -1005
- package/ai-config/agents/quality/performance-tester.md +0 -1086
- package/ai-config/agents/quality/security-auditor.md +0 -133
- package/ai-config/agents/quality/test-engineer.md +0 -453
- package/ai-config/agents/specialists/api-designer.md +0 -87
- package/ai-config/agents/specialists/backend-architect.md +0 -73
- package/ai-config/agents/specialists/code-reviewer.md +0 -77
- package/ai-config/agents/specialists/db-optimizer.md +0 -75
- package/ai-config/agents/specialists/devops-engineer.md +0 -83
- package/ai-config/agents/specialists/documentation-writer.md +0 -78
- package/ai-config/agents/specialists/frontend-developer.md +0 -75
- package/ai-config/agents/specialists/performance-analyst.md +0 -82
- package/ai-config/agents/specialists/refactor-specialist.md +0 -74
- package/ai-config/agents/specialists/security-auditor.md +0 -74
- package/ai-config/agents/specialists/test-engineer.md +0 -81
- package/ai-config/agents/specialists/ux-consultant.md +0 -76
- package/ai-config/agents/specialized/agent-generator.md +0 -1190
- package/ai-config/agents/specialized/blockchain-developer.md +0 -149
- package/ai-config/agents/specialized/code-migrator.md +0 -892
- package/ai-config/agents/specialized/context-manager.md +0 -978
- package/ai-config/agents/specialized/documentation-writer.md +0 -1078
- package/ai-config/agents/specialized/ecommerce-expert.md +0 -1756
- package/ai-config/agents/specialized/embedded-engineer.md +0 -1714
- package/ai-config/agents/specialized/error-detective.md +0 -1034
- package/ai-config/agents/specialized/fintech-specialist.md +0 -1659
- package/ai-config/agents/specialized/freelance-project-planner-v2.md +0 -1988
- package/ai-config/agents/specialized/freelance-project-planner-v3.md +0 -2136
- package/ai-config/agents/specialized/freelance-project-planner-v4.md +0 -4503
- package/ai-config/agents/specialized/freelance-project-planner.md +0 -722
- package/ai-config/agents/specialized/game-developer.md +0 -1963
- package/ai-config/agents/specialized/healthcare-dev.md +0 -1620
- package/ai-config/agents/specialized/mobile-developer.md +0 -188
- package/ai-config/agents/specialized/parallel-plan-executor.md +0 -506
- package/ai-config/agents/specialized/plan-executor.md +0 -485
- package/ai-config/agents/specialized/solo-dev-planner-modular/00-INDEX.md +0 -485
- package/ai-config/agents/specialized/solo-dev-planner-modular/01-CORE.md +0 -3493
- package/ai-config/agents/specialized/solo-dev-planner-modular/02-SELF-CORRECTION.md +0 -778
- package/ai-config/agents/specialized/solo-dev-planner-modular/03-PROGRESSIVE-SETUP.md +0 -918
- package/ai-config/agents/specialized/solo-dev-planner-modular/04-DEPLOYMENT.md +0 -1537
- package/ai-config/agents/specialized/solo-dev-planner-modular/05-TESTING.md +0 -2633
- package/ai-config/agents/specialized/solo-dev-planner-modular/06-OPERATIONS.md +0 -5610
- package/ai-config/agents/specialized/solo-dev-planner-modular/INSTALL.md +0 -335
- package/ai-config/agents/specialized/solo-dev-planner-modular/QUICK-REFERENCE.txt +0 -215
- package/ai-config/agents/specialized/solo-dev-planner-modular/README.md +0 -260
- package/ai-config/agents/specialized/solo-dev-planner-modular/START-HERE.md +0 -379
- package/ai-config/agents/specialized/solo-dev-planner-modular/WORKFLOW-DIAGRAM.md +0 -355
- package/ai-config/agents/specialized/solo-dev-planner-modular/solo-dev-planner.md +0 -279
- package/ai-config/agents/specialized/template-writer.md +0 -347
- package/ai-config/agents/specialized/test-runner.md +0 -99
- package/ai-config/agents/specialized/vibekanban-smart-worker.md +0 -244
- package/ai-config/agents/specialized/wave-executor.md +0 -138
- package/ai-config/agents/specialized/workflow-optimizer.md +0 -1114
- package/ai-config/commands/git/changelog.md +0 -32
- package/ai-config/commands/git/ci-local.md +0 -70
- package/ai-config/commands/git/commit.md +0 -35
- package/ai-config/commands/git/fix-issue.md +0 -23
- package/ai-config/commands/git/pr-create.md +0 -42
- package/ai-config/commands/git/pr-review.md +0 -50
- package/ai-config/commands/git/worktree.md +0 -39
- package/ai-config/commands/refactoring/cleanup.md +0 -24
- package/ai-config/commands/refactoring/dead-code.md +0 -40
- package/ai-config/commands/refactoring/extract.md +0 -31
- package/ai-config/commands/testing/e2e.md +0 -30
- package/ai-config/commands/testing/tdd.md +0 -36
- package/ai-config/commands/testing/test-coverage.md +0 -30
- package/ai-config/commands/testing/test-fix.md +0 -24
- package/ai-config/commands/workflow/generate-agents-md.md +0 -85
- package/ai-config/commands/workflow/planning.md +0 -47
- package/ai-config/commands/workflows/compound.md +0 -89
- package/ai-config/commands/workflows/diagnose.md +0 -70
- package/ai-config/commands/workflows/discover.md +0 -86
- package/ai-config/commands/workflows/plan.md +0 -77
- package/ai-config/commands/workflows/review.md +0 -78
- package/ai-config/commands/workflows/work.md +0 -75
- package/ai-config/config.yaml +0 -18
- package/ai-config/hooks/_TEMPLATE.md +0 -96
- package/ai-config/hooks/block-dangerous-commands.md +0 -75
- package/ai-config/hooks/commit-guard.md +0 -90
- package/ai-config/hooks/context-loader.md +0 -73
- package/ai-config/hooks/improve-prompt.md +0 -91
- package/ai-config/hooks/learning-log.md +0 -72
- package/ai-config/hooks/model-router.md +0 -86
- package/ai-config/hooks/secret-scanner.md +0 -64
- package/ai-config/hooks/skill-validator.md +0 -102
- package/ai-config/hooks/task-artifact.md +0 -114
- package/ai-config/hooks/validate-workflow.md +0 -100
- package/ai-config/prompts/base.md +0 -71
- package/ai-config/prompts/modes/debug.md +0 -34
- package/ai-config/prompts/modes/deploy.md +0 -40
- package/ai-config/prompts/modes/research.md +0 -32
- package/ai-config/prompts/modes/review.md +0 -33
- package/ai-config/prompts/review-policy.md +0 -79
- package/ai-config/skills/_TEMPLATE.md +0 -157
- package/ai-config/skills/backend/api-gateway/SKILL.md +0 -254
- package/ai-config/skills/backend/bff-concepts/SKILL.md +0 -239
- package/ai-config/skills/backend/bff-spring/SKILL.md +0 -364
- package/ai-config/skills/backend/chi-router/SKILL.md +0 -396
- package/ai-config/skills/backend/error-handling/SKILL.md +0 -255
- package/ai-config/skills/backend/exceptions-spring/SKILL.md +0 -323
- package/ai-config/skills/backend/fastapi/SKILL.md +0 -302
- package/ai-config/skills/backend/gateway-spring/SKILL.md +0 -390
- package/ai-config/skills/backend/go-backend/SKILL.md +0 -457
- package/ai-config/skills/backend/gradle-multimodule/SKILL.md +0 -274
- package/ai-config/skills/backend/graphql-concepts/SKILL.md +0 -352
- package/ai-config/skills/backend/graphql-spring/SKILL.md +0 -398
- package/ai-config/skills/backend/grpc-concepts/SKILL.md +0 -283
- package/ai-config/skills/backend/grpc-spring/SKILL.md +0 -445
- package/ai-config/skills/backend/jwt-auth/SKILL.md +0 -412
- package/ai-config/skills/backend/notifications-concepts/SKILL.md +0 -259
- package/ai-config/skills/backend/recommendations-concepts/SKILL.md +0 -261
- package/ai-config/skills/backend/search-concepts/SKILL.md +0 -263
- package/ai-config/skills/backend/search-spring/SKILL.md +0 -375
- package/ai-config/skills/backend/spring-boot-4/SKILL.md +0 -172
- package/ai-config/skills/backend/websockets/SKILL.md +0 -532
- package/ai-config/skills/data-ai/ai-ml/SKILL.md +0 -423
- package/ai-config/skills/data-ai/analytics-concepts/SKILL.md +0 -195
- package/ai-config/skills/data-ai/analytics-spring/SKILL.md +0 -340
- package/ai-config/skills/data-ai/duckdb-analytics/SKILL.md +0 -440
- package/ai-config/skills/data-ai/langchain/SKILL.md +0 -238
- package/ai-config/skills/data-ai/mlflow/SKILL.md +0 -302
- package/ai-config/skills/data-ai/onnx-inference/SKILL.md +0 -290
- package/ai-config/skills/data-ai/powerbi/SKILL.md +0 -352
- package/ai-config/skills/data-ai/pytorch/SKILL.md +0 -274
- package/ai-config/skills/data-ai/scikit-learn/SKILL.md +0 -321
- package/ai-config/skills/data-ai/vector-db/SKILL.md +0 -301
- package/ai-config/skills/database/graph-databases/SKILL.md +0 -218
- package/ai-config/skills/database/graph-spring/SKILL.md +0 -361
- package/ai-config/skills/database/pgx-postgres/SKILL.md +0 -512
- package/ai-config/skills/database/redis-cache/SKILL.md +0 -343
- package/ai-config/skills/database/sqlite-embedded/SKILL.md +0 -388
- package/ai-config/skills/database/timescaledb/SKILL.md +0 -320
- package/ai-config/skills/docs/api-documentation/SKILL.md +0 -293
- package/ai-config/skills/docs/docs-spring/SKILL.md +0 -377
- package/ai-config/skills/docs/mustache-templates/SKILL.md +0 -190
- package/ai-config/skills/docs/technical-docs/SKILL.md +0 -447
- package/ai-config/skills/frontend/astro-ssr/SKILL.md +0 -441
- package/ai-config/skills/frontend/frontend-design/SKILL.md +0 -54
- package/ai-config/skills/frontend/frontend-web/SKILL.md +0 -368
- package/ai-config/skills/frontend/mantine-ui/SKILL.md +0 -396
- package/ai-config/skills/frontend/tanstack-query/SKILL.md +0 -439
- package/ai-config/skills/frontend/zod-validation/SKILL.md +0 -417
- package/ai-config/skills/frontend/zustand-state/SKILL.md +0 -350
- package/ai-config/skills/infrastructure/chaos-engineering/SKILL.md +0 -244
- package/ai-config/skills/infrastructure/chaos-spring/SKILL.md +0 -378
- package/ai-config/skills/infrastructure/devops-infra/SKILL.md +0 -435
- package/ai-config/skills/infrastructure/docker-containers/SKILL.md +0 -420
- package/ai-config/skills/infrastructure/kubernetes/SKILL.md +0 -456
- package/ai-config/skills/infrastructure/opentelemetry/SKILL.md +0 -546
- package/ai-config/skills/infrastructure/traefik-proxy/SKILL.md +0 -474
- package/ai-config/skills/infrastructure/woodpecker-ci/SKILL.md +0 -315
- package/ai-config/skills/mobile/ionic-capacitor/SKILL.md +0 -504
- package/ai-config/skills/mobile/mobile-ionic/SKILL.md +0 -448
- package/ai-config/skills/prompt-improver/SKILL.md +0 -125
- package/ai-config/skills/quality/ghagga-review/SKILL.md +0 -216
- package/ai-config/skills/references/hooks-patterns/SKILL.md +0 -238
- package/ai-config/skills/references/mcp-servers/SKILL.md +0 -275
- package/ai-config/skills/references/plugins-reference/SKILL.md +0 -110
- package/ai-config/skills/references/skills-reference/SKILL.md +0 -420
- package/ai-config/skills/references/subagent-templates/SKILL.md +0 -193
- package/ai-config/skills/systems-iot/modbus-protocol/SKILL.md +0 -410
- package/ai-config/skills/systems-iot/mqtt-rumqttc/SKILL.md +0 -408
- package/ai-config/skills/systems-iot/rust-systems/SKILL.md +0 -386
- package/ai-config/skills/systems-iot/tokio-async/SKILL.md +0 -324
- package/ai-config/skills/testing/playwright-e2e/SKILL.md +0 -289
- package/ai-config/skills/testing/testcontainers/SKILL.md +0 -299
- package/ai-config/skills/testing/vitest-testing/SKILL.md +0 -381
- package/ai-config/skills/workflow/ci-local-guide/SKILL.md +0 -118
- package/ai-config/skills/workflow/claude-automation-recommender/SKILL.md +0 -299
- package/ai-config/skills/workflow/claude-md-improver/SKILL.md +0 -158
- package/ai-config/skills/workflow/finishing-a-development-branch/SKILL.md +0 -117
- package/ai-config/skills/workflow/git-github/SKILL.md +0 -334
- package/ai-config/skills/workflow/git-github/references/examples.md +0 -160
- package/ai-config/skills/workflow/git-workflow/SKILL.md +0 -214
- package/ai-config/skills/workflow/ide-plugins/SKILL.md +0 -277
- package/ai-config/skills/workflow/ide-plugins-intellij/SKILL.md +0 -401
- package/ai-config/skills/workflow/obsidian-brain-workflow/SKILL.md +0 -199
- package/ai-config/skills/workflow/using-git-worktrees/SKILL.md +0 -100
- package/ai-config/skills/workflow/verification-before-completion/SKILL.md +0 -73
- package/ai-config/skills/workflow/wave-workflow/SKILL.md +0 -178
- package/schemas/agent.schema.json +0 -34
- package/schemas/ai-config.schema.json +0 -28
- package/schemas/plugin.schema.json +0 -62
- package/schemas/skill.schema.json +0 -44
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: bff-spring
|
|
3
|
-
description: >
|
|
4
|
-
Spring Boot BFF implementation. Client detection, response tailoring, service composition.
|
|
5
|
-
Trigger: apigen-bff, BaseBffController, ClientType, TailorForClient, aggregation
|
|
6
|
-
tools:
|
|
7
|
-
- Read
|
|
8
|
-
- Write
|
|
9
|
-
- Edit
|
|
10
|
-
- Bash
|
|
11
|
-
- Grep
|
|
12
|
-
metadata:
|
|
13
|
-
author: apigen-team
|
|
14
|
-
version: "1.0"
|
|
15
|
-
tags: [bff, spring-boot, aggregation, java]
|
|
16
|
-
scope: ["apigen-bff/**"]
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
# BFF Spring Boot (apigen-bff)
|
|
20
|
-
|
|
21
|
-
## Configuration
|
|
22
|
-
|
|
23
|
-
```yaml
|
|
24
|
-
apigen:
|
|
25
|
-
bff:
|
|
26
|
-
enabled: true
|
|
27
|
-
|
|
28
|
-
client-detection:
|
|
29
|
-
header: X-Client-Type
|
|
30
|
-
fallback: WEB
|
|
31
|
-
|
|
32
|
-
rate-limits:
|
|
33
|
-
web:
|
|
34
|
-
requests-per-minute: 1000
|
|
35
|
-
mobile:
|
|
36
|
-
requests-per-minute: 500
|
|
37
|
-
iot:
|
|
38
|
-
requests-per-minute: 100
|
|
39
|
-
|
|
40
|
-
response-tailoring:
|
|
41
|
-
enabled: true
|
|
42
|
-
compress-mobile: true
|
|
43
|
-
|
|
44
|
-
composition:
|
|
45
|
-
timeout: 5s
|
|
46
|
-
parallel-enabled: true
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Client Type Detection
|
|
50
|
-
|
|
51
|
-
```java
|
|
52
|
-
public enum ClientType {
|
|
53
|
-
WEB("web"),
|
|
54
|
-
MOBILE_IOS("ios"),
|
|
55
|
-
MOBILE_ANDROID("android"),
|
|
56
|
-
TABLET("tablet"),
|
|
57
|
-
TV("tv"),
|
|
58
|
-
IOT("iot"),
|
|
59
|
-
PARTNER("partner");
|
|
60
|
-
|
|
61
|
-
public boolean isMobile() {
|
|
62
|
-
return this == MOBILE_IOS || this == MOBILE_ANDROID;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public boolean requiresCompression() {
|
|
66
|
-
return isMobile() || this == IOT;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Annotations
|
|
72
|
-
|
|
73
|
-
```java
|
|
74
|
-
@Target(ElementType.METHOD)
|
|
75
|
-
@Retention(RetentionPolicy.RUNTIME)
|
|
76
|
-
public @interface BffEndpoint {
|
|
77
|
-
ClientType[] clients() default {};
|
|
78
|
-
String[] include() default {};
|
|
79
|
-
boolean aggregate() default false;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
@Target(ElementType.METHOD)
|
|
83
|
-
@Retention(RetentionPolicy.RUNTIME)
|
|
84
|
-
public @interface TailorForClient {
|
|
85
|
-
ClientType value();
|
|
86
|
-
String[] fields() default {};
|
|
87
|
-
boolean compress() default false;
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Base BFF Controller
|
|
92
|
-
|
|
93
|
-
```java
|
|
94
|
-
@RestController
|
|
95
|
-
@RequestMapping("/bff")
|
|
96
|
-
public abstract class BaseBffController {
|
|
97
|
-
|
|
98
|
-
protected final QueryCompositionService compositionService;
|
|
99
|
-
protected final ResponseTailoringService tailoringService;
|
|
100
|
-
protected final CombinedRateLimitService rateLimitService;
|
|
101
|
-
|
|
102
|
-
protected ClientType detectClient(HttpServletRequest request) {
|
|
103
|
-
String clientHeader = request.getHeader("X-Client-Type");
|
|
104
|
-
if (clientHeader != null) {
|
|
105
|
-
return ClientType.fromValue(clientHeader);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
String userAgent = request.getHeader("User-Agent");
|
|
109
|
-
return ClientTypeDetector.fromUserAgent(userAgent);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
protected <T> T tailorResponse(T response, ClientType clientType) {
|
|
113
|
-
return tailoringService.tailor(response, clientType);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## Query Composition Service
|
|
119
|
-
|
|
120
|
-
```java
|
|
121
|
-
@Service
|
|
122
|
-
public class QueryCompositionServiceImpl implements QueryCompositionService {
|
|
123
|
-
|
|
124
|
-
private final List<ServiceRequest> serviceRequests;
|
|
125
|
-
private final ExecutorService executor;
|
|
126
|
-
|
|
127
|
-
@Override
|
|
128
|
-
public AggregatedResponse compose(List<String> includes,
|
|
129
|
-
Map<String, Object> params) {
|
|
130
|
-
|
|
131
|
-
List<CompletableFuture<ServiceResult>> futures = includes.stream()
|
|
132
|
-
.map(include -> findServiceRequest(include))
|
|
133
|
-
.map(sr -> CompletableFuture.supplyAsync(
|
|
134
|
-
() -> executeService(sr, params), executor)
|
|
135
|
-
.exceptionally(this::handleFailure))
|
|
136
|
-
.toList();
|
|
137
|
-
|
|
138
|
-
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
|
139
|
-
.orTimeout(5, TimeUnit.SECONDS)
|
|
140
|
-
.join();
|
|
141
|
-
|
|
142
|
-
return aggregateResults(futures);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private ServiceResult handleFailure(Throwable ex) {
|
|
146
|
-
log.warn("Service call failed: {}", ex.getMessage());
|
|
147
|
-
return ServiceResult.failure(ex.getMessage());
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## Response Tailoring Service
|
|
153
|
-
|
|
154
|
-
```java
|
|
155
|
-
@Service
|
|
156
|
-
public class ResponseTailoringServiceImpl implements ResponseTailoringService {
|
|
157
|
-
|
|
158
|
-
private final ObjectMapper objectMapper;
|
|
159
|
-
private final Map<ClientType, Set<String>> fieldMappings;
|
|
160
|
-
|
|
161
|
-
@Override
|
|
162
|
-
@SuppressWarnings("unchecked")
|
|
163
|
-
public <T> T tailor(T response, ClientType clientType) {
|
|
164
|
-
if (response == null) return null;
|
|
165
|
-
|
|
166
|
-
Set<String> allowedFields = fieldMappings.get(clientType);
|
|
167
|
-
if (allowedFields == null || allowedFields.isEmpty()) {
|
|
168
|
-
return response;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Convert to map, filter fields, convert back
|
|
172
|
-
Map<String, Object> map = objectMapper.convertValue(response, Map.class);
|
|
173
|
-
Map<String, Object> filtered = filterFields(map, allowedFields);
|
|
174
|
-
|
|
175
|
-
return (T) objectMapper.convertValue(filtered, response.getClass());
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private Map<String, Object> filterFields(Map<String, Object> source,
|
|
179
|
-
Set<String> allowed) {
|
|
180
|
-
return source.entrySet().stream()
|
|
181
|
-
.filter(e -> allowed.contains(e.getKey()))
|
|
182
|
-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## Aggregated Response Model
|
|
188
|
-
|
|
189
|
-
```java
|
|
190
|
-
public record AggregatedResponse(
|
|
191
|
-
Map<String, Object> data,
|
|
192
|
-
Map<String, ErrorInfo> errors,
|
|
193
|
-
ResponseMetadata metadata
|
|
194
|
-
) {
|
|
195
|
-
public boolean hasErrors() {
|
|
196
|
-
return errors != null && !errors.isEmpty();
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
public static Builder builder() {
|
|
200
|
-
return new Builder();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
public static class Builder {
|
|
204
|
-
private final Map<String, Object> data = new HashMap<>();
|
|
205
|
-
private final Map<String, ErrorInfo> errors = new HashMap<>();
|
|
206
|
-
|
|
207
|
-
public Builder addData(String key, Object value) {
|
|
208
|
-
data.put(key, value);
|
|
209
|
-
return this;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
public Builder addError(String key, String message) {
|
|
213
|
-
errors.put(key, new ErrorInfo(message, null));
|
|
214
|
-
return this;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
public AggregatedResponse build() {
|
|
218
|
-
return new AggregatedResponse(data, errors,
|
|
219
|
-
new ResponseMetadata(Instant.now()));
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
## BFF Endpoint Aspect
|
|
226
|
-
|
|
227
|
-
```java
|
|
228
|
-
@Aspect
|
|
229
|
-
@Component
|
|
230
|
-
public class BffEndpointAspect {
|
|
231
|
-
|
|
232
|
-
private final CombinedRateLimitService rateLimitService;
|
|
233
|
-
|
|
234
|
-
@Around("@annotation(bffEndpoint)")
|
|
235
|
-
public Object handleBffEndpoint(ProceedingJoinPoint joinPoint,
|
|
236
|
-
BffEndpoint bffEndpoint) throws Throwable {
|
|
237
|
-
|
|
238
|
-
HttpServletRequest request = getCurrentRequest();
|
|
239
|
-
ClientType clientType = detectClient(request);
|
|
240
|
-
|
|
241
|
-
// Check client type restriction
|
|
242
|
-
if (bffEndpoint.clients().length > 0 &&
|
|
243
|
-
!Arrays.asList(bffEndpoint.clients()).contains(clientType)) {
|
|
244
|
-
throw new ClientTypeNotAllowedException(clientType);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Apply rate limiting
|
|
248
|
-
rateLimitService.checkLimit(clientType, request);
|
|
249
|
-
|
|
250
|
-
return joinPoint.proceed();
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
## Example BFF Controller
|
|
256
|
-
|
|
257
|
-
```java
|
|
258
|
-
@RestController
|
|
259
|
-
@RequestMapping("/bff/v1")
|
|
260
|
-
public class DashboardBffController extends BaseBffController {
|
|
261
|
-
|
|
262
|
-
private final UserService userService;
|
|
263
|
-
private final OrderService orderService;
|
|
264
|
-
private final NotificationService notificationService;
|
|
265
|
-
|
|
266
|
-
@GetMapping("/dashboard")
|
|
267
|
-
@BffEndpoint(aggregate = true)
|
|
268
|
-
public AggregatedResponse getDashboard(
|
|
269
|
-
@RequestParam(defaultValue = "profile,notifications") String include,
|
|
270
|
-
HttpServletRequest request) {
|
|
271
|
-
|
|
272
|
-
ClientType client = detectClient(request);
|
|
273
|
-
List<String> includes = Arrays.asList(include.split(","));
|
|
274
|
-
|
|
275
|
-
AggregatedResponse.Builder builder = AggregatedResponse.builder();
|
|
276
|
-
|
|
277
|
-
if (includes.contains("profile")) {
|
|
278
|
-
UserDTO profile = userService.getCurrentUser();
|
|
279
|
-
builder.addData("profile", tailorResponse(profile, client));
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (includes.contains("notifications")) {
|
|
283
|
-
int limit = client.isMobile() ? 5 : 20;
|
|
284
|
-
List<NotificationDTO> notifications =
|
|
285
|
-
notificationService.getRecent(limit);
|
|
286
|
-
builder.addData("notifications", notifications);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (includes.contains("orders")) {
|
|
290
|
-
List<OrderDTO> orders = orderService.getRecentOrders();
|
|
291
|
-
builder.addData("orders", tailorResponse(orders, client));
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return builder.build();
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
@GetMapping("/dashboard")
|
|
298
|
-
@TailorForClient(value = ClientType.MOBILE_IOS,
|
|
299
|
-
fields = {"id", "name", "avatarThumb"})
|
|
300
|
-
public MobileDashboardResponse getMobileDashboard() {
|
|
301
|
-
// iOS-specific optimized response
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
## Rate Limiting Service
|
|
307
|
-
|
|
308
|
-
```java
|
|
309
|
-
@Service
|
|
310
|
-
public class CombinedRateLimitServiceImpl implements CombinedRateLimitService {
|
|
311
|
-
|
|
312
|
-
private final Map<ClientType, Bucket> buckets = new ConcurrentHashMap<>();
|
|
313
|
-
private final BffProperties properties;
|
|
314
|
-
|
|
315
|
-
@Override
|
|
316
|
-
public void checkLimit(ClientType clientType, HttpServletRequest request) {
|
|
317
|
-
Bucket bucket = buckets.computeIfAbsent(clientType, this::createBucket);
|
|
318
|
-
|
|
319
|
-
if (!bucket.tryConsume(1)) {
|
|
320
|
-
throw new RateLimitExceededException(
|
|
321
|
-
"Rate limit exceeded for client type: " + clientType);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
private Bucket createBucket(ClientType clientType) {
|
|
326
|
-
int rpm = properties.getRateLimits()
|
|
327
|
-
.getOrDefault(clientType, 100);
|
|
328
|
-
|
|
329
|
-
return Bucket.builder()
|
|
330
|
-
.addLimit(Bandwidth.simple(rpm, Duration.ofMinutes(1)))
|
|
331
|
-
.build();
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
## Testing
|
|
337
|
-
|
|
338
|
-
```java
|
|
339
|
-
@WebMvcTest(DashboardBffController.class)
|
|
340
|
-
class DashboardBffControllerTest {
|
|
341
|
-
|
|
342
|
-
@Autowired
|
|
343
|
-
private MockMvc mockMvc;
|
|
344
|
-
|
|
345
|
-
@MockBean
|
|
346
|
-
private UserService userService;
|
|
347
|
-
|
|
348
|
-
@Test
|
|
349
|
-
void shouldTailorResponseForMobile() throws Exception {
|
|
350
|
-
when(userService.getCurrentUser()).thenReturn(fullUserDto());
|
|
351
|
-
|
|
352
|
-
mockMvc.perform(get("/bff/v1/dashboard")
|
|
353
|
-
.header("X-Client-Type", "ios"))
|
|
354
|
-
.andExpect(status().isOk())
|
|
355
|
-
.andExpect(jsonPath("$.data.profile.email").doesNotExist())
|
|
356
|
-
.andExpect(jsonPath("$.data.profile.id").exists());
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
## Related Skills
|
|
362
|
-
|
|
363
|
-
- `bff-concepts`: BFF pattern concepts
|
|
364
|
-
- `spring-boot-4`: Spring Boot 4.0 patterns
|