moai-adk 0.35.1__py3-none-any.whl
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.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/__init__.py +10 -0
- moai_adk/__main__.py +199 -0
- moai_adk/cli/__init__.py +6 -0
- moai_adk/cli/commands/__init__.py +17 -0
- moai_adk/cli/commands/analyze.py +116 -0
- moai_adk/cli/commands/doctor.py +272 -0
- moai_adk/cli/commands/init.py +372 -0
- moai_adk/cli/commands/language.py +248 -0
- moai_adk/cli/commands/status.py +104 -0
- moai_adk/cli/commands/update.py +2686 -0
- moai_adk/cli/main.py +13 -0
- moai_adk/cli/prompts/__init__.py +5 -0
- moai_adk/cli/prompts/init_prompts.py +219 -0
- moai_adk/cli/spec_status.py +263 -0
- moai_adk/cli/ui/__init__.py +44 -0
- moai_adk/cli/ui/progress.py +422 -0
- moai_adk/cli/ui/prompts.py +389 -0
- moai_adk/cli/ui/theme.py +129 -0
- moai_adk/cli/worktree/__init__.py +27 -0
- moai_adk/cli/worktree/__main__.py +31 -0
- moai_adk/cli/worktree/cli.py +683 -0
- moai_adk/cli/worktree/exceptions.py +89 -0
- moai_adk/cli/worktree/manager.py +493 -0
- moai_adk/cli/worktree/models.py +65 -0
- moai_adk/cli/worktree/registry.py +422 -0
- moai_adk/core/PHASE2_OPTIMIZATIONS.md +467 -0
- moai_adk/core/__init__.py +1 -0
- moai_adk/core/analysis/__init__.py +9 -0
- moai_adk/core/analysis/session_analyzer.py +400 -0
- moai_adk/core/claude_integration.py +393 -0
- moai_adk/core/command_helpers.py +270 -0
- moai_adk/core/comprehensive_monitoring_system.py +1183 -0
- moai_adk/core/config/__init__.py +19 -0
- moai_adk/core/config/auto_spec_config.py +340 -0
- moai_adk/core/config/migration.py +244 -0
- moai_adk/core/config/unified.py +436 -0
- moai_adk/core/context_manager.py +273 -0
- moai_adk/core/diagnostics/__init__.py +19 -0
- moai_adk/core/diagnostics/slash_commands.py +159 -0
- moai_adk/core/enterprise_features.py +1404 -0
- moai_adk/core/error_recovery_system.py +1902 -0
- moai_adk/core/event_driven_hook_system.py +1371 -0
- moai_adk/core/git/__init__.py +31 -0
- moai_adk/core/git/branch.py +25 -0
- moai_adk/core/git/branch_manager.py +129 -0
- moai_adk/core/git/checkpoint.py +134 -0
- moai_adk/core/git/commit.py +67 -0
- moai_adk/core/git/conflict_detector.py +413 -0
- moai_adk/core/git/event_detector.py +79 -0
- moai_adk/core/git/manager.py +216 -0
- moai_adk/core/hooks/post_tool_auto_spec_completion.py +901 -0
- moai_adk/core/input_validation_middleware.py +1006 -0
- moai_adk/core/integration/__init__.py +22 -0
- moai_adk/core/integration/engine.py +157 -0
- moai_adk/core/integration/integration_tester.py +226 -0
- moai_adk/core/integration/models.py +88 -0
- moai_adk/core/integration/utils.py +211 -0
- moai_adk/core/issue_creator.py +305 -0
- moai_adk/core/jit_context_loader.py +956 -0
- moai_adk/core/jit_enhanced_hook_manager.py +1987 -0
- moai_adk/core/language_config.py +202 -0
- moai_adk/core/language_config_resolver.py +572 -0
- moai_adk/core/language_validator.py +543 -0
- moai_adk/core/mcp/setup.py +116 -0
- moai_adk/core/merge/__init__.py +9 -0
- moai_adk/core/merge/analyzer.py +605 -0
- moai_adk/core/migration/__init__.py +18 -0
- moai_adk/core/migration/alfred_to_moai_migrator.py +383 -0
- moai_adk/core/migration/backup_manager.py +277 -0
- moai_adk/core/migration/custom_element_scanner.py +358 -0
- moai_adk/core/migration/file_migrator.py +209 -0
- moai_adk/core/migration/interactive_checkbox_ui.py +488 -0
- moai_adk/core/migration/selective_restorer.py +470 -0
- moai_adk/core/migration/template_utils.py +74 -0
- moai_adk/core/migration/user_selection_ui.py +338 -0
- moai_adk/core/migration/version_detector.py +139 -0
- moai_adk/core/migration/version_migrator.py +228 -0
- moai_adk/core/performance/__init__.py +6 -0
- moai_adk/core/performance/cache_system.py +316 -0
- moai_adk/core/performance/parallel_processor.py +116 -0
- moai_adk/core/phase_optimized_hook_scheduler.py +879 -0
- moai_adk/core/project/__init__.py +1 -0
- moai_adk/core/project/backup_utils.py +70 -0
- moai_adk/core/project/checker.py +300 -0
- moai_adk/core/project/detector.py +293 -0
- moai_adk/core/project/initializer.py +387 -0
- moai_adk/core/project/phase_executor.py +716 -0
- moai_adk/core/project/validator.py +139 -0
- moai_adk/core/quality/__init__.py +6 -0
- moai_adk/core/quality/trust_checker.py +377 -0
- moai_adk/core/quality/validators/__init__.py +6 -0
- moai_adk/core/quality/validators/base_validator.py +19 -0
- moai_adk/core/realtime_monitoring_dashboard.py +1724 -0
- moai_adk/core/robust_json_parser.py +611 -0
- moai_adk/core/rollback_manager.py +918 -0
- moai_adk/core/session_manager.py +651 -0
- moai_adk/core/skill_loading_system.py +579 -0
- moai_adk/core/spec/confidence_scoring.py +680 -0
- moai_adk/core/spec/ears_template_engine.py +1247 -0
- moai_adk/core/spec/quality_validator.py +687 -0
- moai_adk/core/spec_status_manager.py +478 -0
- moai_adk/core/template/__init__.py +7 -0
- moai_adk/core/template/backup.py +174 -0
- moai_adk/core/template/config.py +191 -0
- moai_adk/core/template/languages.py +43 -0
- moai_adk/core/template/merger.py +233 -0
- moai_adk/core/template/processor.py +1200 -0
- moai_adk/core/template_engine.py +310 -0
- moai_adk/core/template_variable_synchronizer.py +417 -0
- moai_adk/core/unified_permission_manager.py +745 -0
- moai_adk/core/user_behavior_analytics.py +851 -0
- moai_adk/core/version_sync.py +429 -0
- moai_adk/foundation/__init__.py +56 -0
- moai_adk/foundation/backend.py +1027 -0
- moai_adk/foundation/database.py +1115 -0
- moai_adk/foundation/devops.py +1585 -0
- moai_adk/foundation/ears.py +431 -0
- moai_adk/foundation/frontend.py +870 -0
- moai_adk/foundation/git/commit_templates.py +557 -0
- moai_adk/foundation/git.py +376 -0
- moai_adk/foundation/langs.py +484 -0
- moai_adk/foundation/ml_ops.py +1162 -0
- moai_adk/foundation/testing.py +1524 -0
- moai_adk/foundation/trust/trust_principles.py +676 -0
- moai_adk/foundation/trust/validation_checklist.py +1573 -0
- moai_adk/project/__init__.py +0 -0
- moai_adk/project/configuration.py +1084 -0
- moai_adk/project/documentation.py +566 -0
- moai_adk/project/schema.py +447 -0
- moai_adk/statusline/__init__.py +38 -0
- moai_adk/statusline/alfred_detector.py +105 -0
- moai_adk/statusline/config.py +376 -0
- moai_adk/statusline/enhanced_output_style_detector.py +372 -0
- moai_adk/statusline/git_collector.py +190 -0
- moai_adk/statusline/main.py +322 -0
- moai_adk/statusline/metrics_tracker.py +78 -0
- moai_adk/statusline/renderer.py +343 -0
- moai_adk/statusline/update_checker.py +129 -0
- moai_adk/statusline/version_reader.py +741 -0
- moai_adk/templates/.claude/agents/moai/ai-nano-banana.md +714 -0
- moai_adk/templates/.claude/agents/moai/builder-agent.md +474 -0
- moai_adk/templates/.claude/agents/moai/builder-command.md +1172 -0
- moai_adk/templates/.claude/agents/moai/builder-plugin.md +637 -0
- moai_adk/templates/.claude/agents/moai/builder-skill.md +666 -0
- moai_adk/templates/.claude/agents/moai/expert-backend.md +899 -0
- moai_adk/templates/.claude/agents/moai/expert-database.md +777 -0
- moai_adk/templates/.claude/agents/moai/expert-debug.md +401 -0
- moai_adk/templates/.claude/agents/moai/expert-devops.md +720 -0
- moai_adk/templates/.claude/agents/moai/expert-frontend.md +734 -0
- moai_adk/templates/.claude/agents/moai/expert-performance.md +657 -0
- moai_adk/templates/.claude/agents/moai/expert-security.md +513 -0
- moai_adk/templates/.claude/agents/moai/expert-testing.md +733 -0
- moai_adk/templates/.claude/agents/moai/expert-uiux.md +1041 -0
- moai_adk/templates/.claude/agents/moai/manager-claude-code.md +432 -0
- moai_adk/templates/.claude/agents/moai/manager-docs.md +573 -0
- moai_adk/templates/.claude/agents/moai/manager-git.md +1060 -0
- moai_adk/templates/.claude/agents/moai/manager-project.md +891 -0
- moai_adk/templates/.claude/agents/moai/manager-quality.md +624 -0
- moai_adk/templates/.claude/agents/moai/manager-spec.md +809 -0
- moai_adk/templates/.claude/agents/moai/manager-strategy.md +780 -0
- moai_adk/templates/.claude/agents/moai/manager-tdd.md +784 -0
- moai_adk/templates/.claude/agents/moai/mcp-context7.md +458 -0
- moai_adk/templates/.claude/agents/moai/mcp-figma.md +1607 -0
- moai_adk/templates/.claude/agents/moai/mcp-notion.md +789 -0
- moai_adk/templates/.claude/agents/moai/mcp-playwright.md +469 -0
- moai_adk/templates/.claude/agents/moai/mcp-sequential-thinking.md +1032 -0
- moai_adk/templates/.claude/commands/moai/0-project.md +1386 -0
- moai_adk/templates/.claude/commands/moai/1-plan.md +1427 -0
- moai_adk/templates/.claude/commands/moai/2-run.md +943 -0
- moai_adk/templates/.claude/commands/moai/3-sync.md +1324 -0
- moai_adk/templates/.claude/commands/moai/9-feedback.md +314 -0
- moai_adk/templates/.claude/hooks/__init__.py +8 -0
- moai_adk/templates/.claude/hooks/moai/__init__.py +8 -0
- moai_adk/templates/.claude/hooks/moai/lib/__init__.py +85 -0
- moai_adk/templates/.claude/hooks/moai/lib/checkpoint.py +244 -0
- moai_adk/templates/.claude/hooks/moai/lib/common.py +131 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_manager.py +446 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_validator.py +639 -0
- moai_adk/templates/.claude/hooks/moai/lib/example_config.json +104 -0
- moai_adk/templates/.claude/hooks/moai/lib/git_operations_manager.py +590 -0
- moai_adk/templates/.claude/hooks/moai/lib/language_validator.py +317 -0
- moai_adk/templates/.claude/hooks/moai/lib/models.py +102 -0
- moai_adk/templates/.claude/hooks/moai/lib/path_utils.py +28 -0
- moai_adk/templates/.claude/hooks/moai/lib/project.py +768 -0
- moai_adk/templates/.claude/hooks/moai/lib/test_hooks_improvements.py +443 -0
- moai_adk/templates/.claude/hooks/moai/lib/timeout.py +160 -0
- moai_adk/templates/.claude/hooks/moai/lib/unified_timeout_manager.py +530 -0
- moai_adk/templates/.claude/hooks/moai/session_end__auto_cleanup.py +862 -0
- moai_adk/templates/.claude/hooks/moai/session_start__show_project_info.py +1083 -0
- moai_adk/templates/.claude/output-styles/moai/r2d2.md +560 -0
- moai_adk/templates/.claude/output-styles/moai/yoda.md +359 -0
- moai_adk/templates/.claude/settings.json +172 -0
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/SKILL.md +307 -0
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/examples.md +431 -0
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/scripts/batch_generate.py +560 -0
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/scripts/generate_image.py +362 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/SKILL.md +249 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/examples.md +406 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/README.md +44 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/api-documentation.md +130 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/code-documentation.md +152 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/multi-format-output.md +178 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/user-guides.md +147 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/reference.md +328 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +320 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +718 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +464 -0
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +323 -0
- moai_adk/templates/.claude/skills/moai-domain-database/examples.md +830 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/README.md +53 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/mongodb.md +231 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/postgresql.md +169 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/redis.md +262 -0
- moai_adk/templates/.claude/skills/moai-domain-database/reference.md +545 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +497 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +968 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +664 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/SKILL.md +455 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/examples.md +560 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/accessibility-wcag.md +260 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/component-architecture.md +228 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/icon-libraries.md +401 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/theming-system.md +373 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/reference.md +243 -0
- moai_adk/templates/.claude/skills/moai-formats-data/SKILL.md +492 -0
- moai_adk/templates/.claude/skills/moai-formats-data/examples.md +804 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/README.md +98 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/SKILL-MODULARIZATION-TEMPLATE.md +278 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/caching-performance.md +459 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/data-validation.md +485 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/json-optimization.md +374 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/toon-encoding.md +308 -0
- moai_adk/templates/.claude/skills/moai-formats-data/reference.md +585 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/SKILL.md +202 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/examples.md +732 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/best-practices-checklist.md +616 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-custom-slash-commands-official.md +729 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-hooks-official.md +560 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-iam-official.md +635 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-memory-official.md +543 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-settings-official.md +663 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-skills-official.md +113 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sub-agents-official.md +238 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/complete-configuration-guide.md +175 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-examples.md +1674 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-formatting-guide.md +729 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-examples.md +1513 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-formatting-guide.md +1086 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-integration-patterns.md +1100 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference.md +209 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/SKILL.md +441 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/examples.md +1048 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/reference.md +246 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/SKILL.md +420 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/examples.md +358 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/README.md +296 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/agents-reference.md +359 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/commands-reference.md +432 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-patterns.md +757 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/execution-rules.md +687 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/modular-system.md +665 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/progressive-disclosure.md +649 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-first-tdd.md +864 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/token-optimization.md +708 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-framework.md +981 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/reference.md +478 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/SKILL.md +315 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/examples.md +228 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/assumption-matrix.md +80 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/cognitive-bias.md +199 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/first-principles.md +140 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/trade-off-analysis.md +154 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/reference.md +157 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/SKILL.md +364 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/examples.md +1232 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/best-practices.md +261 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/integration-patterns.md +194 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/proactive-analysis.md +229 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/trust5-validation.md +169 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/reference.md +1266 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/scripts/quality-gate.sh +668 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/templates/github-actions-quality.yml +481 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/templates/quality-config.yaml +519 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +649 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +478 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +612 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/SKILL.md +477 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/examples.md +1090 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/reference.md +686 -0
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +376 -0
- moai_adk/templates/.claude/skills/moai-lang-go/examples.md +919 -0
- moai_adk/templates/.claude/skills/moai-lang-go/reference.md +737 -0
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +385 -0
- moai_adk/templates/.claude/skills/moai-lang-java/examples.md +864 -0
- moai_adk/templates/.claude/skills/moai-lang-java/reference.md +291 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +382 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +1006 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +562 -0
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +644 -0
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +481 -0
- moai_adk/templates/.claude/skills/moai-lang-python/examples.md +977 -0
- moai_adk/templates/.claude/skills/moai-lang-python/reference.md +804 -0
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +579 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +687 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +372 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +659 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +504 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +497 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +633 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +423 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +497 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +918 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +672 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +368 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +1089 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +731 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/SKILL.md +300 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/advanced-patterns.md +465 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/examples.md +270 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/optimization.md +440 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/reference.md +228 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/SKILL.md +319 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/advanced-patterns.md +336 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/examples.md +592 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-deployment-patterns.md +182 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-patterns.md +17 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/configuration.md +57 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/content-architecture-optimization.md +162 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/deployment.md +52 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/framework-core-configuration.md +186 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/i18n-setup.md +55 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/mdx-components.md +52 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/optimization.md +303 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/reference.md +379 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/SKILL.md +372 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/examples.md +575 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/advanced-patterns.md +394 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/optimization.md +278 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-components.md +457 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-theming.md +373 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/reference.md +74 -0
- moai_adk/templates/.claude/skills/moai-mcp-figma/SKILL.md +402 -0
- moai_adk/templates/.claude/skills/moai-mcp-figma/advanced-patterns.md +607 -0
- moai_adk/templates/.claude/skills/moai-mcp-notion/SKILL.md +300 -0
- moai_adk/templates/.claude/skills/moai-mcp-notion/advanced-patterns.md +537 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/SKILL.md +291 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/SKILL.md +390 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/SKILL.md +398 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/SKILL.md +379 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/SKILL.md +358 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/SKILL.md +467 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/SKILL.md +377 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/SKILL.md +466 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/SKILL.md +482 -0
- moai_adk/templates/.claude/skills/moai-plugin-builder/SKILL.md +474 -0
- moai_adk/templates/.claude/skills/moai-plugin-builder/examples.md +621 -0
- moai_adk/templates/.claude/skills/moai-plugin-builder/migration.md +341 -0
- moai_adk/templates/.claude/skills/moai-plugin-builder/reference.md +463 -0
- moai_adk/templates/.claude/skills/moai-plugin-builder/validation.md +373 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/SKILL.md +275 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/adaptive-mfa.md +233 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/akamai-integration.md +215 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/application-credentials.md +280 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/attack-protection-log-events.md +225 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/attack-protection-overview.md +140 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/bot-detection.md +144 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/breached-password-detection.md +187 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/brute-force-protection.md +189 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/certifications.md +282 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/compliance-overview.md +263 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/continuous-session-protection.md +307 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/customize-mfa.md +178 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/dpop-implementation.md +283 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/fapi-implementation.md +259 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/gdpr-compliance.md +313 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/guardian-configuration.md +269 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/highly-regulated-identity.md +272 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/jwt-fundamentals.md +248 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/mdl-verification.md +211 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/mfa-api-management.md +278 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/mfa-factors.md +226 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/mfa-overview.md +174 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/mtls-sender-constraining.md +316 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/ropg-flow-mfa.md +217 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/security-center.md +325 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/security-guidance.md +277 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/state-parameters.md +178 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/step-up-authentication.md +251 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/suspicious-ip-throttling.md +240 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/tenant-access-control.md +180 -0
- moai_adk/templates/.claude/skills/moai-security-auth0/modules/webauthn-fido.md +235 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/SKILL.md +449 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/advanced-patterns.md +379 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/examples.md +544 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/optimization.md +286 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/reference.md +307 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/README.md +190 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/SKILL.md +390 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/__init__.py +520 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/complete_workflow_demo_fixed.py +574 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_project_setup.py +317 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_workflow_demo.py +663 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/config-migration-example.json +190 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/question-examples.json +175 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/quick_start.py +196 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples.md +547 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/__init__.py +17 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/advanced-patterns.md +158 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/ask_user_integration.py +340 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/batch_questions.py +713 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/config_manager.py +538 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/documentation_manager.py +1336 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/language_initializer.py +730 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/migration_manager.py +608 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/template_optimizer.py +1005 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/reference.md +275 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/config-schema.json +316 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/tab_schema.json +1434 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/config-template.json +71 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/product-template.md +44 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/structure-template.md +48 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/tech-template.md +92 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/config-manager-setup.json +109 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/language-initializer.json +228 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/menu-project-config.json +130 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/project-batch-questions.json +97 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/spec-workflow-setup.json +150 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/test_integration_simple.py +436 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/SKILL.md +534 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/examples.md +900 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/reference.md +704 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/SKILL.md +377 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/examples.md +552 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/code-templates.md +124 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/feedback-templates.md +100 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/template-optimizer.md +138 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/reference.md +346 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/LICENSE.txt +202 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/SKILL.md +456 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/advanced-patterns.md +576 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/ai-powered-testing.py +294 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/console_logging.py +35 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/element_discovery.py +40 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/static_html_automation.py +34 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples.md +672 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/README.md +220 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/ai-debugging.md +845 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review.md +1416 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization.md +1234 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/smart-refactoring.md +1243 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7.md +1260 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/optimization.md +505 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/reference/playwright-best-practices.md +57 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/reference.md +440 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/scripts/with_server.py +218 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/templates/alfred-integration.md +376 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/workflows/enterprise-testing-workflow.py +571 -0
- moai_adk/templates/.claude/skills/moai-worktree/SKILL.md +411 -0
- moai_adk/templates/.claude/skills/moai-worktree/examples.md +606 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/integration-patterns.md +982 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/parallel-development.md +778 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-commands.md +646 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-management.md +782 -0
- moai_adk/templates/.claude/skills/moai-worktree/reference.md +357 -0
- moai_adk/templates/.git-hooks/pre-commit +128 -0
- moai_adk/templates/.git-hooks/pre-push +365 -0
- moai_adk/templates/.github/workflows/ci-universal.yml +513 -0
- moai_adk/templates/.github/workflows/security-secrets-check.yml +179 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +337 -0
- moai_adk/templates/.gitignore +222 -0
- moai_adk/templates/.mcp.json +13 -0
- moai_adk/templates/.moai/config/config.yaml +58 -0
- moai_adk/templates/.moai/config/questions/_schema.yaml +174 -0
- moai_adk/templates/.moai/config/questions/tab0-init.yaml +251 -0
- moai_adk/templates/.moai/config/questions/tab1-user.yaml +107 -0
- moai_adk/templates/.moai/config/questions/tab2-project.yaml +79 -0
- moai_adk/templates/.moai/config/questions/tab3-git.yaml +632 -0
- moai_adk/templates/.moai/config/questions/tab4-quality.yaml +182 -0
- moai_adk/templates/.moai/config/questions/tab5-system.yaml +96 -0
- moai_adk/templates/.moai/config/sections/git-strategy.yaml +116 -0
- moai_adk/templates/.moai/config/sections/language.yaml +11 -0
- moai_adk/templates/.moai/config/sections/project.yaml +13 -0
- moai_adk/templates/.moai/config/sections/quality.yaml +17 -0
- moai_adk/templates/.moai/config/sections/system.yaml +24 -0
- moai_adk/templates/.moai/config/sections/user.yaml +5 -0
- moai_adk/templates/.moai/config/statusline-config.yaml +92 -0
- moai_adk/templates/.moai/scripts/setup-glm.py +136 -0
- moai_adk/templates/CLAUDE.md +642 -0
- moai_adk/utils/__init__.py +30 -0
- moai_adk/utils/banner.py +38 -0
- moai_adk/utils/common.py +294 -0
- moai_adk/utils/link_validator.py +241 -0
- moai_adk/utils/logger.py +147 -0
- moai_adk/utils/safe_file_reader.py +206 -0
- moai_adk/utils/timeout.py +160 -0
- moai_adk/utils/toon_utils.py +256 -0
- moai_adk/version.py +22 -0
- moai_adk-0.35.1.dist-info/METADATA +3018 -0
- moai_adk-0.35.1.dist-info/RECORD +502 -0
- moai_adk-0.35.1.dist-info/WHEEL +4 -0
- moai_adk-0.35.1.dist-info/entry_points.txt +3 -0
- moai_adk-0.35.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,864 @@
|
|
|
1
|
+
# Java 21 Production Examples
|
|
2
|
+
|
|
3
|
+
## Complete REST API Implementation
|
|
4
|
+
|
|
5
|
+
### Spring Boot 3.3 User Service
|
|
6
|
+
|
|
7
|
+
UserController.java:
|
|
8
|
+
```java
|
|
9
|
+
@RestController
|
|
10
|
+
@RequestMapping("/api/v1/users")
|
|
11
|
+
@RequiredArgsConstructor
|
|
12
|
+
@Validated
|
|
13
|
+
public class UserController {
|
|
14
|
+
private final UserService userService;
|
|
15
|
+
|
|
16
|
+
@GetMapping
|
|
17
|
+
public ResponseEntity<Page<UserDto>> listUsers(
|
|
18
|
+
@RequestParam(defaultValue = "0") int page,
|
|
19
|
+
@RequestParam(defaultValue = "20") int size,
|
|
20
|
+
@RequestParam(required = false) String search) {
|
|
21
|
+
var pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
|
|
22
|
+
var users = search != null
|
|
23
|
+
? userService.searchUsers(search, pageable)
|
|
24
|
+
: userService.findAll(pageable);
|
|
25
|
+
return ResponseEntity.ok(users.map(UserDto::from));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@GetMapping("/{id}")
|
|
29
|
+
public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
|
|
30
|
+
return userService.findById(id)
|
|
31
|
+
.map(UserDto::from)
|
|
32
|
+
.map(ResponseEntity::ok)
|
|
33
|
+
.orElse(ResponseEntity.notFound().build());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@PostMapping
|
|
37
|
+
public ResponseEntity<UserDto> createUser(
|
|
38
|
+
@Valid @RequestBody CreateUserRequest request) {
|
|
39
|
+
var user = userService.create(request);
|
|
40
|
+
var location = URI.create("/api/v1/users/" + user.getId());
|
|
41
|
+
return ResponseEntity.created(location).body(UserDto.from(user));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@PutMapping("/{id}")
|
|
45
|
+
public ResponseEntity<UserDto> updateUser(
|
|
46
|
+
@PathVariable Long id,
|
|
47
|
+
@Valid @RequestBody UpdateUserRequest request) {
|
|
48
|
+
return userService.update(id, request)
|
|
49
|
+
.map(UserDto::from)
|
|
50
|
+
.map(ResponseEntity::ok)
|
|
51
|
+
.orElse(ResponseEntity.notFound().build());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@DeleteMapping("/{id}")
|
|
55
|
+
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
|
|
56
|
+
return userService.delete(id)
|
|
57
|
+
? ResponseEntity.noContent().build()
|
|
58
|
+
: ResponseEntity.notFound().build();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@ExceptionHandler(DuplicateEmailException.class)
|
|
62
|
+
public ResponseEntity<ProblemDetail> handleDuplicateEmail(DuplicateEmailException ex) {
|
|
63
|
+
var problem = ProblemDetail.forStatusAndDetail(
|
|
64
|
+
HttpStatus.CONFLICT, ex.getMessage());
|
|
65
|
+
problem.setTitle("Duplicate Email");
|
|
66
|
+
return ResponseEntity.status(HttpStatus.CONFLICT).body(problem);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
UserService.java:
|
|
72
|
+
```java
|
|
73
|
+
@Service
|
|
74
|
+
@RequiredArgsConstructor
|
|
75
|
+
@Transactional(readOnly = true)
|
|
76
|
+
@Slf4j
|
|
77
|
+
public class UserService {
|
|
78
|
+
private final UserRepository userRepository;
|
|
79
|
+
private final PasswordEncoder passwordEncoder;
|
|
80
|
+
private final ApplicationEventPublisher eventPublisher;
|
|
81
|
+
|
|
82
|
+
public Page<User> findAll(Pageable pageable) {
|
|
83
|
+
return userRepository.findAll(pageable);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public Page<User> searchUsers(String query, Pageable pageable) {
|
|
87
|
+
return userRepository.findByNameContainingIgnoreCaseOrEmailContainingIgnoreCase(
|
|
88
|
+
query, query, pageable);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public Optional<User> findById(Long id) {
|
|
92
|
+
return userRepository.findById(id);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@Transactional
|
|
96
|
+
public User create(CreateUserRequest request) {
|
|
97
|
+
log.info("Creating user with email: {}", request.email());
|
|
98
|
+
|
|
99
|
+
if (userRepository.existsByEmail(request.email())) {
|
|
100
|
+
throw new DuplicateEmailException(request.email());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
var user = User.builder()
|
|
104
|
+
.name(request.name())
|
|
105
|
+
.email(request.email())
|
|
106
|
+
.passwordHash(passwordEncoder.encode(request.password()))
|
|
107
|
+
.status(UserStatus.PENDING)
|
|
108
|
+
.build();
|
|
109
|
+
|
|
110
|
+
var saved = userRepository.save(user);
|
|
111
|
+
eventPublisher.publishEvent(new UserCreatedEvent(saved.getId(), saved.getEmail()));
|
|
112
|
+
|
|
113
|
+
log.info("User created with id: {}", saved.getId());
|
|
114
|
+
return saved;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@Transactional
|
|
118
|
+
public Optional<User> update(Long id, UpdateUserRequest request) {
|
|
119
|
+
return userRepository.findById(id)
|
|
120
|
+
.map(user -> {
|
|
121
|
+
user.setName(request.name());
|
|
122
|
+
if (request.email() != null && !request.email().equals(user.getEmail())) {
|
|
123
|
+
if (userRepository.existsByEmail(request.email())) {
|
|
124
|
+
throw new DuplicateEmailException(request.email());
|
|
125
|
+
}
|
|
126
|
+
user.setEmail(request.email());
|
|
127
|
+
}
|
|
128
|
+
return userRepository.save(user);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@Transactional
|
|
133
|
+
public boolean delete(Long id) {
|
|
134
|
+
if (!userRepository.existsById(id)) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
userRepository.deleteById(id);
|
|
138
|
+
eventPublisher.publishEvent(new UserDeletedEvent(id));
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
User.java (Entity):
|
|
145
|
+
```java
|
|
146
|
+
@Entity
|
|
147
|
+
@Table(name = "users", indexes = {
|
|
148
|
+
@Index(name = "idx_users_email", columnList = "email"),
|
|
149
|
+
@Index(name = "idx_users_status", columnList = "status")
|
|
150
|
+
})
|
|
151
|
+
@Getter @Setter
|
|
152
|
+
@NoArgsConstructor
|
|
153
|
+
@AllArgsConstructor
|
|
154
|
+
@Builder
|
|
155
|
+
public class User {
|
|
156
|
+
@Id
|
|
157
|
+
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
158
|
+
private Long id;
|
|
159
|
+
|
|
160
|
+
@Column(nullable = false, length = 100)
|
|
161
|
+
private String name;
|
|
162
|
+
|
|
163
|
+
@Column(nullable = false, unique = true, length = 255)
|
|
164
|
+
private String email;
|
|
165
|
+
|
|
166
|
+
@Column(nullable = false)
|
|
167
|
+
private String passwordHash;
|
|
168
|
+
|
|
169
|
+
@Enumerated(EnumType.STRING)
|
|
170
|
+
@Column(nullable = false, length = 20)
|
|
171
|
+
private UserStatus status;
|
|
172
|
+
|
|
173
|
+
@CreatedDate
|
|
174
|
+
@Column(nullable = false, updatable = false)
|
|
175
|
+
private Instant createdAt;
|
|
176
|
+
|
|
177
|
+
@LastModifiedDate
|
|
178
|
+
private Instant updatedAt;
|
|
179
|
+
|
|
180
|
+
@Version
|
|
181
|
+
private Long version;
|
|
182
|
+
|
|
183
|
+
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
|
|
184
|
+
@Builder.Default
|
|
185
|
+
private List<Order> orders = new ArrayList<>();
|
|
186
|
+
|
|
187
|
+
public void addOrder(Order order) {
|
|
188
|
+
orders.add(order);
|
|
189
|
+
order.setUser(this);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public void removeOrder(Order order) {
|
|
193
|
+
orders.remove(order);
|
|
194
|
+
order.setUser(null);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@PrePersist
|
|
198
|
+
protected void onCreate() {
|
|
199
|
+
createdAt = Instant.now();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
@PreUpdate
|
|
203
|
+
protected void onUpdate() {
|
|
204
|
+
updatedAt = Instant.now();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
public enum UserStatus {
|
|
209
|
+
PENDING, ACTIVE, SUSPENDED, DELETED
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
UserRepository.java:
|
|
214
|
+
```java
|
|
215
|
+
public interface UserRepository extends JpaRepository<User, Long> {
|
|
216
|
+
Optional<User> findByEmail(String email);
|
|
217
|
+
|
|
218
|
+
boolean existsByEmail(String email);
|
|
219
|
+
|
|
220
|
+
@Query("SELECT u FROM User u WHERE u.status = :status")
|
|
221
|
+
List<User> findByStatus(@Param("status") UserStatus status);
|
|
222
|
+
|
|
223
|
+
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders WHERE u.id = :id")
|
|
224
|
+
Optional<User> findByIdWithOrders(@Param("id") Long id);
|
|
225
|
+
|
|
226
|
+
Page<User> findByNameContainingIgnoreCaseOrEmailContainingIgnoreCase(
|
|
227
|
+
String name, String email, Pageable pageable);
|
|
228
|
+
|
|
229
|
+
@Modifying
|
|
230
|
+
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
|
|
231
|
+
int updateStatus(@Param("id") Long id, @Param("status") UserStatus status);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
DTOs (Records):
|
|
236
|
+
```java
|
|
237
|
+
public record UserDto(
|
|
238
|
+
Long id,
|
|
239
|
+
String name,
|
|
240
|
+
String email,
|
|
241
|
+
UserStatus status,
|
|
242
|
+
Instant createdAt,
|
|
243
|
+
Instant updatedAt
|
|
244
|
+
) {
|
|
245
|
+
public static UserDto from(User user) {
|
|
246
|
+
return new UserDto(
|
|
247
|
+
user.getId(),
|
|
248
|
+
user.getName(),
|
|
249
|
+
user.getEmail(),
|
|
250
|
+
user.getStatus(),
|
|
251
|
+
user.getCreatedAt(),
|
|
252
|
+
user.getUpdatedAt()
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
public record CreateUserRequest(
|
|
258
|
+
@NotBlank(message = "Name is required")
|
|
259
|
+
@Size(min = 2, max = 100, message = "Name must be between 2 and 100 characters")
|
|
260
|
+
String name,
|
|
261
|
+
|
|
262
|
+
@NotBlank(message = "Email is required")
|
|
263
|
+
@Email(message = "Invalid email format")
|
|
264
|
+
String email,
|
|
265
|
+
|
|
266
|
+
@NotBlank(message = "Password is required")
|
|
267
|
+
@Size(min = 8, message = "Password must be at least 8 characters")
|
|
268
|
+
String password
|
|
269
|
+
) {}
|
|
270
|
+
|
|
271
|
+
public record UpdateUserRequest(
|
|
272
|
+
@NotBlank(message = "Name is required")
|
|
273
|
+
@Size(min = 2, max = 100, message = "Name must be between 2 and 100 characters")
|
|
274
|
+
String name,
|
|
275
|
+
|
|
276
|
+
@Email(message = "Invalid email format")
|
|
277
|
+
String email
|
|
278
|
+
) {}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Virtual Threads Examples
|
|
284
|
+
|
|
285
|
+
### Async Service with Structured Concurrency
|
|
286
|
+
|
|
287
|
+
```java
|
|
288
|
+
@Service
|
|
289
|
+
@RequiredArgsConstructor
|
|
290
|
+
@Slf4j
|
|
291
|
+
public class AsyncUserService {
|
|
292
|
+
private final UserRepository userRepository;
|
|
293
|
+
private final OrderRepository orderRepository;
|
|
294
|
+
private final NotificationService notificationService;
|
|
295
|
+
private final ExternalApiClient externalApiClient;
|
|
296
|
+
|
|
297
|
+
public UserWithDetails fetchUserDetails(Long userId) throws Exception {
|
|
298
|
+
log.info("Fetching user details for userId: {}", userId);
|
|
299
|
+
|
|
300
|
+
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
|
|
301
|
+
Supplier<User> userTask = scope.fork(() ->
|
|
302
|
+
userRepository.findById(userId)
|
|
303
|
+
.orElseThrow(() -> new UserNotFoundException(userId)));
|
|
304
|
+
|
|
305
|
+
Supplier<List<Order>> ordersTask = scope.fork(() ->
|
|
306
|
+
orderRepository.findByUserId(userId));
|
|
307
|
+
|
|
308
|
+
Supplier<List<Notification>> notificationsTask = scope.fork(() ->
|
|
309
|
+
notificationService.getUnreadNotifications(userId));
|
|
310
|
+
|
|
311
|
+
Supplier<UserProfile> profileTask = scope.fork(() ->
|
|
312
|
+
externalApiClient.fetchUserProfile(userId));
|
|
313
|
+
|
|
314
|
+
scope.join().throwIfFailed();
|
|
315
|
+
|
|
316
|
+
return new UserWithDetails(
|
|
317
|
+
userTask.get(),
|
|
318
|
+
ordersTask.get(),
|
|
319
|
+
notificationsTask.get(),
|
|
320
|
+
profileTask.get()
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
public List<UserSummary> processUsersInParallel(List<Long> userIds) {
|
|
326
|
+
log.info("Processing {} users in parallel", userIds.size());
|
|
327
|
+
|
|
328
|
+
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
|
|
329
|
+
var futures = userIds.stream()
|
|
330
|
+
.map(id -> executor.submit(() -> processUser(id)))
|
|
331
|
+
.toList();
|
|
332
|
+
|
|
333
|
+
return futures.stream()
|
|
334
|
+
.map(future -> {
|
|
335
|
+
try {
|
|
336
|
+
return future.get();
|
|
337
|
+
} catch (Exception e) {
|
|
338
|
+
log.error("Failed to process user", e);
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
.filter(Objects::nonNull)
|
|
343
|
+
.toList();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
private UserSummary processUser(Long userId) {
|
|
348
|
+
var user = userRepository.findById(userId).orElseThrow();
|
|
349
|
+
var orderCount = orderRepository.countByUserId(userId);
|
|
350
|
+
return new UserSummary(user.getId(), user.getName(), orderCount);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
public record UserWithDetails(
|
|
355
|
+
User user,
|
|
356
|
+
List<Order> orders,
|
|
357
|
+
List<Notification> notifications,
|
|
358
|
+
UserProfile profile
|
|
359
|
+
) {}
|
|
360
|
+
|
|
361
|
+
public record UserSummary(Long id, String name, int orderCount) {}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Virtual Thread Configuration
|
|
365
|
+
|
|
366
|
+
```java
|
|
367
|
+
@Configuration
|
|
368
|
+
public class VirtualThreadConfig {
|
|
369
|
+
|
|
370
|
+
@Bean
|
|
371
|
+
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
|
|
372
|
+
return protocolHandler -> {
|
|
373
|
+
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
@Bean
|
|
378
|
+
public AsyncTaskExecutor applicationTaskExecutor() {
|
|
379
|
+
return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Testing Examples
|
|
387
|
+
|
|
388
|
+
### Unit Tests with JUnit 5 and Mockito
|
|
389
|
+
|
|
390
|
+
```java
|
|
391
|
+
@ExtendWith(MockitoExtension.class)
|
|
392
|
+
class UserServiceTest {
|
|
393
|
+
@Mock private UserRepository userRepository;
|
|
394
|
+
@Mock private PasswordEncoder passwordEncoder;
|
|
395
|
+
@Mock private ApplicationEventPublisher eventPublisher;
|
|
396
|
+
@InjectMocks private UserService userService;
|
|
397
|
+
|
|
398
|
+
@Nested
|
|
399
|
+
@DisplayName("Create User Tests")
|
|
400
|
+
class CreateUserTests {
|
|
401
|
+
@Test
|
|
402
|
+
@DisplayName("Should create user successfully when email is unique")
|
|
403
|
+
void shouldCreateUserSuccessfully() {
|
|
404
|
+
// Arrange
|
|
405
|
+
var request = new CreateUserRequest("John Doe", "john@example.com", "password123");
|
|
406
|
+
var savedUser = User.builder()
|
|
407
|
+
.id(1L)
|
|
408
|
+
.name("John Doe")
|
|
409
|
+
.email("john@example.com")
|
|
410
|
+
.status(UserStatus.PENDING)
|
|
411
|
+
.build();
|
|
412
|
+
|
|
413
|
+
when(userRepository.existsByEmail("john@example.com")).thenReturn(false);
|
|
414
|
+
when(passwordEncoder.encode("password123")).thenReturn("hashedPassword");
|
|
415
|
+
when(userRepository.save(any(User.class))).thenReturn(savedUser);
|
|
416
|
+
|
|
417
|
+
// Act
|
|
418
|
+
var result = userService.create(request);
|
|
419
|
+
|
|
420
|
+
// Assert
|
|
421
|
+
assertThat(result).isNotNull();
|
|
422
|
+
assertThat(result.getId()).isEqualTo(1L);
|
|
423
|
+
assertThat(result.getName()).isEqualTo("John Doe");
|
|
424
|
+
assertThat(result.getEmail()).isEqualTo("john@example.com");
|
|
425
|
+
assertThat(result.getStatus()).isEqualTo(UserStatus.PENDING);
|
|
426
|
+
|
|
427
|
+
verify(userRepository).save(argThat(user ->
|
|
428
|
+
user.getName().equals("John Doe") &&
|
|
429
|
+
user.getEmail().equals("john@example.com") &&
|
|
430
|
+
user.getPasswordHash().equals("hashedPassword")
|
|
431
|
+
));
|
|
432
|
+
verify(eventPublisher).publishEvent(any(UserCreatedEvent.class));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
@Test
|
|
436
|
+
@DisplayName("Should throw exception when email already exists")
|
|
437
|
+
void shouldThrowExceptionForDuplicateEmail() {
|
|
438
|
+
// Arrange
|
|
439
|
+
var request = new CreateUserRequest("John", "existing@example.com", "password");
|
|
440
|
+
when(userRepository.existsByEmail("existing@example.com")).thenReturn(true);
|
|
441
|
+
|
|
442
|
+
// Act & Assert
|
|
443
|
+
assertThatThrownBy(() -> userService.create(request))
|
|
444
|
+
.isInstanceOf(DuplicateEmailException.class)
|
|
445
|
+
.hasMessageContaining("existing@example.com");
|
|
446
|
+
|
|
447
|
+
verify(userRepository, never()).save(any());
|
|
448
|
+
verify(eventPublisher, never()).publishEvent(any());
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
@Nested
|
|
453
|
+
@DisplayName("Find User Tests")
|
|
454
|
+
class FindUserTests {
|
|
455
|
+
@Test
|
|
456
|
+
@DisplayName("Should return user when found")
|
|
457
|
+
void shouldReturnUserWhenFound() {
|
|
458
|
+
var user = User.builder().id(1L).name("John").build();
|
|
459
|
+
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
|
|
460
|
+
|
|
461
|
+
var result = userService.findById(1L);
|
|
462
|
+
|
|
463
|
+
assertThat(result).isPresent();
|
|
464
|
+
assertThat(result.get().getName()).isEqualTo("John");
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
@Test
|
|
468
|
+
@DisplayName("Should return empty when user not found")
|
|
469
|
+
void shouldReturnEmptyWhenNotFound() {
|
|
470
|
+
when(userRepository.findById(999L)).thenReturn(Optional.empty());
|
|
471
|
+
|
|
472
|
+
var result = userService.findById(999L);
|
|
473
|
+
|
|
474
|
+
assertThat(result).isEmpty();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
@ParameterizedTest
|
|
479
|
+
@ValueSource(strings = {"john", "doe", "example.com"})
|
|
480
|
+
@DisplayName("Should search users by query")
|
|
481
|
+
void shouldSearchUsersByQuery(String query) {
|
|
482
|
+
var pageable = PageRequest.of(0, 10);
|
|
483
|
+
var users = new PageImpl<>(List.of(
|
|
484
|
+
User.builder().id(1L).name("John Doe").email("john@example.com").build()
|
|
485
|
+
));
|
|
486
|
+
when(userRepository.findByNameContainingIgnoreCaseOrEmailContainingIgnoreCase(
|
|
487
|
+
query, query, pageable)).thenReturn(users);
|
|
488
|
+
|
|
489
|
+
var result = userService.searchUsers(query, pageable);
|
|
490
|
+
|
|
491
|
+
assertThat(result.getContent()).hasSize(1);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Integration Tests with TestContainers
|
|
497
|
+
|
|
498
|
+
```java
|
|
499
|
+
@Testcontainers
|
|
500
|
+
@SpringBootTest
|
|
501
|
+
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
|
502
|
+
@Transactional
|
|
503
|
+
class UserRepositoryIntegrationTest {
|
|
504
|
+
|
|
505
|
+
@Container
|
|
506
|
+
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")
|
|
507
|
+
.withDatabaseName("testdb")
|
|
508
|
+
.withUsername("test")
|
|
509
|
+
.withPassword("test");
|
|
510
|
+
|
|
511
|
+
@DynamicPropertySource
|
|
512
|
+
static void configureProperties(DynamicPropertyRegistry registry) {
|
|
513
|
+
registry.add("spring.datasource.url", postgres::getJdbcUrl);
|
|
514
|
+
registry.add("spring.datasource.username", postgres::getUsername);
|
|
515
|
+
registry.add("spring.datasource.password", postgres::getPassword);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
@Autowired
|
|
519
|
+
private UserRepository userRepository;
|
|
520
|
+
|
|
521
|
+
@Autowired
|
|
522
|
+
private TestEntityManager entityManager;
|
|
523
|
+
|
|
524
|
+
@BeforeEach
|
|
525
|
+
void setUp() {
|
|
526
|
+
userRepository.deleteAll();
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
@Test
|
|
530
|
+
@DisplayName("Should save and find user by email")
|
|
531
|
+
void shouldSaveAndFindUserByEmail() {
|
|
532
|
+
// Arrange
|
|
533
|
+
var user = User.builder()
|
|
534
|
+
.name("John Doe")
|
|
535
|
+
.email("john@example.com")
|
|
536
|
+
.passwordHash("hashedPassword")
|
|
537
|
+
.status(UserStatus.ACTIVE)
|
|
538
|
+
.build();
|
|
539
|
+
|
|
540
|
+
// Act
|
|
541
|
+
var saved = userRepository.save(user);
|
|
542
|
+
entityManager.flush();
|
|
543
|
+
entityManager.clear();
|
|
544
|
+
|
|
545
|
+
// Assert
|
|
546
|
+
var found = userRepository.findByEmail("john@example.com");
|
|
547
|
+
assertThat(found).isPresent();
|
|
548
|
+
assertThat(found.get().getName()).isEqualTo("John Doe");
|
|
549
|
+
assertThat(found.get().getId()).isEqualTo(saved.getId());
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
@Test
|
|
553
|
+
@DisplayName("Should check email existence correctly")
|
|
554
|
+
void shouldCheckEmailExistence() {
|
|
555
|
+
var user = User.builder()
|
|
556
|
+
.name("Jane")
|
|
557
|
+
.email("jane@example.com")
|
|
558
|
+
.passwordHash("hash")
|
|
559
|
+
.status(UserStatus.ACTIVE)
|
|
560
|
+
.build();
|
|
561
|
+
userRepository.save(user);
|
|
562
|
+
|
|
563
|
+
assertThat(userRepository.existsByEmail("jane@example.com")).isTrue();
|
|
564
|
+
assertThat(userRepository.existsByEmail("nonexistent@example.com")).isFalse();
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
@Test
|
|
568
|
+
@DisplayName("Should find users by status")
|
|
569
|
+
void shouldFindUsersByStatus() {
|
|
570
|
+
userRepository.saveAll(List.of(
|
|
571
|
+
User.builder().name("Active1").email("a1@test.com").passwordHash("h").status(UserStatus.ACTIVE).build(),
|
|
572
|
+
User.builder().name("Active2").email("a2@test.com").passwordHash("h").status(UserStatus.ACTIVE).build(),
|
|
573
|
+
User.builder().name("Pending").email("p@test.com").passwordHash("h").status(UserStatus.PENDING).build()
|
|
574
|
+
));
|
|
575
|
+
|
|
576
|
+
var activeUsers = userRepository.findByStatus(UserStatus.ACTIVE);
|
|
577
|
+
|
|
578
|
+
assertThat(activeUsers).hasSize(2);
|
|
579
|
+
assertThat(activeUsers).extracting(User::getStatus)
|
|
580
|
+
.containsOnly(UserStatus.ACTIVE);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
@Test
|
|
584
|
+
@DisplayName("Should search users with pagination")
|
|
585
|
+
void shouldSearchUsersWithPagination() {
|
|
586
|
+
userRepository.saveAll(List.of(
|
|
587
|
+
User.builder().name("John Smith").email("john@test.com").passwordHash("h").status(UserStatus.ACTIVE).build(),
|
|
588
|
+
User.builder().name("Jane Doe").email("jane@test.com").passwordHash("h").status(UserStatus.ACTIVE).build(),
|
|
589
|
+
User.builder().name("Bob Johnson").email("bob@test.com").passwordHash("h").status(UserStatus.ACTIVE).build()
|
|
590
|
+
));
|
|
591
|
+
|
|
592
|
+
var pageable = PageRequest.of(0, 10);
|
|
593
|
+
var result = userRepository.findByNameContainingIgnoreCaseOrEmailContainingIgnoreCase(
|
|
594
|
+
"john", "john", pageable);
|
|
595
|
+
|
|
596
|
+
assertThat(result.getContent()).hasSize(2);
|
|
597
|
+
assertThat(result.getContent()).extracting(User::getName)
|
|
598
|
+
.containsExactlyInAnyOrder("John Smith", "Bob Johnson");
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
---
|
|
604
|
+
|
|
605
|
+
## Spring Security Examples
|
|
606
|
+
|
|
607
|
+
### JWT Authentication
|
|
608
|
+
|
|
609
|
+
```java
|
|
610
|
+
@RestController
|
|
611
|
+
@RequestMapping("/api/auth")
|
|
612
|
+
@RequiredArgsConstructor
|
|
613
|
+
public class AuthController {
|
|
614
|
+
private final AuthenticationManager authenticationManager;
|
|
615
|
+
private final JwtTokenProvider tokenProvider;
|
|
616
|
+
private final UserService userService;
|
|
617
|
+
|
|
618
|
+
@PostMapping("/login")
|
|
619
|
+
public ResponseEntity<AuthResponse> login(@Valid @RequestBody LoginRequest request) {
|
|
620
|
+
var authentication = authenticationManager.authenticate(
|
|
621
|
+
new UsernamePasswordAuthenticationToken(request.email(), request.password())
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
SecurityContextHolder.getContext().setAuthentication(authentication);
|
|
625
|
+
var token = tokenProvider.generateToken(authentication);
|
|
626
|
+
|
|
627
|
+
return ResponseEntity.ok(new AuthResponse(token, "Bearer"));
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
@PostMapping("/register")
|
|
631
|
+
public ResponseEntity<UserDto> register(@Valid @RequestBody CreateUserRequest request) {
|
|
632
|
+
var user = userService.create(request);
|
|
633
|
+
return ResponseEntity.status(HttpStatus.CREATED).body(UserDto.from(user));
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
@GetMapping("/me")
|
|
637
|
+
public ResponseEntity<UserDto> getCurrentUser(@AuthenticationPrincipal UserDetails userDetails) {
|
|
638
|
+
return userService.findByEmail(userDetails.getUsername())
|
|
639
|
+
.map(UserDto::from)
|
|
640
|
+
.map(ResponseEntity::ok)
|
|
641
|
+
.orElse(ResponseEntity.notFound().build());
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
public record LoginRequest(
|
|
646
|
+
@NotBlank @Email String email,
|
|
647
|
+
@NotBlank String password
|
|
648
|
+
) {}
|
|
649
|
+
|
|
650
|
+
public record AuthResponse(String token, String type) {}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### Security Configuration
|
|
654
|
+
|
|
655
|
+
```java
|
|
656
|
+
@Configuration
|
|
657
|
+
@EnableWebSecurity
|
|
658
|
+
@EnableMethodSecurity
|
|
659
|
+
@RequiredArgsConstructor
|
|
660
|
+
public class SecurityConfig {
|
|
661
|
+
private final JwtTokenProvider tokenProvider;
|
|
662
|
+
private final UserDetailsService userDetailsService;
|
|
663
|
+
|
|
664
|
+
@Bean
|
|
665
|
+
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
|
666
|
+
return http
|
|
667
|
+
.csrf(AbstractHttpConfigurer::disable)
|
|
668
|
+
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
|
669
|
+
.sessionManagement(session ->
|
|
670
|
+
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
|
671
|
+
.authorizeHttpRequests(auth -> auth
|
|
672
|
+
.requestMatchers("/api/auth/**").permitAll()
|
|
673
|
+
.requestMatchers("/api/public/**").permitAll()
|
|
674
|
+
.requestMatchers("/actuator/health").permitAll()
|
|
675
|
+
.requestMatchers("/api/admin/**").hasRole("ADMIN")
|
|
676
|
+
.anyRequest().authenticated()
|
|
677
|
+
)
|
|
678
|
+
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
|
|
679
|
+
.exceptionHandling(ex -> ex
|
|
680
|
+
.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
|
|
681
|
+
.accessDeniedHandler(new JwtAccessDeniedHandler())
|
|
682
|
+
)
|
|
683
|
+
.build();
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
@Bean
|
|
687
|
+
public JwtAuthenticationFilter jwtAuthenticationFilter() {
|
|
688
|
+
return new JwtAuthenticationFilter(tokenProvider, userDetailsService);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
@Bean
|
|
692
|
+
public PasswordEncoder passwordEncoder() {
|
|
693
|
+
return new BCryptPasswordEncoder(12);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
@Bean
|
|
697
|
+
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
|
|
698
|
+
return config.getAuthenticationManager();
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
@Bean
|
|
702
|
+
public CorsConfigurationSource corsConfigurationSource() {
|
|
703
|
+
var configuration = new CorsConfiguration();
|
|
704
|
+
configuration.setAllowedOrigins(List.of("http://localhost:3000"));
|
|
705
|
+
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
|
706
|
+
configuration.setAllowedHeaders(List.of("*"));
|
|
707
|
+
configuration.setAllowCredentials(true);
|
|
708
|
+
|
|
709
|
+
var source = new UrlBasedCorsConfigurationSource();
|
|
710
|
+
source.registerCorsConfiguration("/**", configuration);
|
|
711
|
+
return source;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
---
|
|
717
|
+
|
|
718
|
+
## Build Configuration Examples
|
|
719
|
+
|
|
720
|
+
### Maven pom.xml
|
|
721
|
+
|
|
722
|
+
```xml
|
|
723
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
724
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
725
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
726
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
|
727
|
+
https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
728
|
+
<modelVersion>4.0.0</modelVersion>
|
|
729
|
+
|
|
730
|
+
<parent>
|
|
731
|
+
<groupId>org.springframework.boot</groupId>
|
|
732
|
+
<artifactId>spring-boot-starter-parent</artifactId>
|
|
733
|
+
<version>3.3.0</version>
|
|
734
|
+
</parent>
|
|
735
|
+
|
|
736
|
+
<groupId>com.example</groupId>
|
|
737
|
+
<artifactId>user-service</artifactId>
|
|
738
|
+
<version>1.0.0</version>
|
|
739
|
+
|
|
740
|
+
<properties>
|
|
741
|
+
<java.version>21</java.version>
|
|
742
|
+
<testcontainers.version>1.19.7</testcontainers.version>
|
|
743
|
+
</properties>
|
|
744
|
+
|
|
745
|
+
<dependencies>
|
|
746
|
+
<dependency>
|
|
747
|
+
<groupId>org.springframework.boot</groupId>
|
|
748
|
+
<artifactId>spring-boot-starter-web</artifactId>
|
|
749
|
+
</dependency>
|
|
750
|
+
<dependency>
|
|
751
|
+
<groupId>org.springframework.boot</groupId>
|
|
752
|
+
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
|
753
|
+
</dependency>
|
|
754
|
+
<dependency>
|
|
755
|
+
<groupId>org.springframework.boot</groupId>
|
|
756
|
+
<artifactId>spring-boot-starter-security</artifactId>
|
|
757
|
+
</dependency>
|
|
758
|
+
<dependency>
|
|
759
|
+
<groupId>org.springframework.boot</groupId>
|
|
760
|
+
<artifactId>spring-boot-starter-validation</artifactId>
|
|
761
|
+
</dependency>
|
|
762
|
+
<dependency>
|
|
763
|
+
<groupId>org.springframework.boot</groupId>
|
|
764
|
+
<artifactId>spring-boot-starter-actuator</artifactId>
|
|
765
|
+
</dependency>
|
|
766
|
+
|
|
767
|
+
<dependency>
|
|
768
|
+
<groupId>org.postgresql</groupId>
|
|
769
|
+
<artifactId>postgresql</artifactId>
|
|
770
|
+
<scope>runtime</scope>
|
|
771
|
+
</dependency>
|
|
772
|
+
<dependency>
|
|
773
|
+
<groupId>org.projectlombok</groupId>
|
|
774
|
+
<artifactId>lombok</artifactId>
|
|
775
|
+
<optional>true</optional>
|
|
776
|
+
</dependency>
|
|
777
|
+
<dependency>
|
|
778
|
+
<groupId>io.jsonwebtoken</groupId>
|
|
779
|
+
<artifactId>jjwt-api</artifactId>
|
|
780
|
+
<version>0.12.5</version>
|
|
781
|
+
</dependency>
|
|
782
|
+
|
|
783
|
+
<dependency>
|
|
784
|
+
<groupId>org.springframework.boot</groupId>
|
|
785
|
+
<artifactId>spring-boot-starter-test</artifactId>
|
|
786
|
+
<scope>test</scope>
|
|
787
|
+
</dependency>
|
|
788
|
+
<dependency>
|
|
789
|
+
<groupId>org.testcontainers</groupId>
|
|
790
|
+
<artifactId>postgresql</artifactId>
|
|
791
|
+
<version>${testcontainers.version}</version>
|
|
792
|
+
<scope>test</scope>
|
|
793
|
+
</dependency>
|
|
794
|
+
</dependencies>
|
|
795
|
+
|
|
796
|
+
<build>
|
|
797
|
+
<plugins>
|
|
798
|
+
<plugin>
|
|
799
|
+
<groupId>org.springframework.boot</groupId>
|
|
800
|
+
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
801
|
+
</plugin>
|
|
802
|
+
</plugins>
|
|
803
|
+
</build>
|
|
804
|
+
</project>
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
### Gradle build.gradle.kts
|
|
808
|
+
|
|
809
|
+
```kotlin
|
|
810
|
+
plugins {
|
|
811
|
+
java
|
|
812
|
+
id("org.springframework.boot") version "3.3.0"
|
|
813
|
+
id("io.spring.dependency-management") version "1.1.4"
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
group = "com.example"
|
|
817
|
+
version = "1.0.0"
|
|
818
|
+
|
|
819
|
+
java {
|
|
820
|
+
toolchain {
|
|
821
|
+
languageVersion = JavaLanguageVersion.of(21)
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
configurations {
|
|
826
|
+
compileOnly {
|
|
827
|
+
extendsFrom(configurations.annotationProcessor.get())
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
repositories {
|
|
832
|
+
mavenCentral()
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
dependencies {
|
|
836
|
+
implementation("org.springframework.boot:spring-boot-starter-web")
|
|
837
|
+
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
|
838
|
+
implementation("org.springframework.boot:spring-boot-starter-security")
|
|
839
|
+
implementation("org.springframework.boot:spring-boot-starter-validation")
|
|
840
|
+
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
|
841
|
+
implementation("io.jsonwebtoken:jjwt-api:0.12.5")
|
|
842
|
+
|
|
843
|
+
runtimeOnly("org.postgresql:postgresql")
|
|
844
|
+
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.5")
|
|
845
|
+
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.5")
|
|
846
|
+
|
|
847
|
+
compileOnly("org.projectlombok:lombok")
|
|
848
|
+
annotationProcessor("org.projectlombok:lombok")
|
|
849
|
+
|
|
850
|
+
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
|
851
|
+
testImplementation("org.springframework.security:spring-security-test")
|
|
852
|
+
testImplementation("org.testcontainers:postgresql")
|
|
853
|
+
testImplementation("org.testcontainers:junit-jupiter")
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
tasks.withType<Test> {
|
|
857
|
+
useJUnitPlatform()
|
|
858
|
+
}
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
---
|
|
862
|
+
|
|
863
|
+
Last Updated: 2025-12-07
|
|
864
|
+
Version: 1.0.0
|