@polymorphism-tech/morph-spec 4.12.0 → 4.13.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/README.md +21 -29
- package/bin/morph-spec.js +41 -24
- package/bin/task-manager.js +962 -1341
- package/bin/validate.js +1 -1
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +10 -11
- package/docs/QUICKSTART.md +15 -15
- package/framework/CLAUDE.md +5 -2
- package/framework/agents/README.md +3 -4
- package/framework/agents/ai-agents/ai-system-architect.md +6 -10
- package/framework/agents/architecture/seo-growth-hacker.md +166 -326
- package/framework/agents/architecture/standards-architect.md +21 -31
- package/framework/agents/backend/dotnet-senior.md +160 -86
- package/framework/agents/backend/ef-modeler.md +2 -6
- package/framework/agents/backend/hangfire-orchestrator.md +8 -9
- package/framework/agents/backend/ms-agent-expert.md +1 -1
- package/framework/agents/frontend/nextjs-expert.md +198 -133
- package/framework/agents/frontend/ui-ux-designer.md +226 -267
- package/framework/agents/infrastructure/infra-architect.md +191 -27
- package/framework/agents/integrations/clerk-auth.md +19 -26
- package/framework/agents/integrations/hangfire-integration.md +1 -4
- package/framework/agents.json +215 -1453
- package/framework/commands/morph-apply.md +4 -4
- package/framework/commands/morph-preflight.md +4 -26
- package/framework/commands/morph-proposal.md +19 -19
- package/framework/commands/morph-status.md +2 -2
- package/framework/commands/morph-troubleshoot.md +6 -13
- package/framework/hooks/README.md +1 -6
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +4 -6
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +12 -7
- package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +2 -2
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +2 -2
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +5 -10
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +63 -30
- package/framework/hooks/claude-code/session-start/resume-context.js +77 -0
- package/framework/hooks/claude-code/statusline.py +4 -5
- package/framework/hooks/claude-code/stop/validate-completion.js +4 -4
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +8 -8
- package/framework/hooks/shared/compact-restore.js +28 -5
- package/framework/hooks/shared/phase-utils.js +4 -8
- package/framework/hooks/shared/state-reader.js +34 -3
- package/framework/phases.json +7 -45
- package/framework/rules/ai-agents-standards.md +15 -0
- package/framework/rules/asaas-standards.md +13 -0
- package/framework/rules/clerk-standards.md +13 -0
- package/framework/rules/csharp-standards.md +8 -4
- package/framework/rules/data-standards.md +18 -0
- package/framework/rules/dotnet-templates.md +87 -0
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/hangfire-standards.md +13 -0
- package/framework/rules/infrastructure-standards.md +27 -16
- package/framework/rules/morph-workflow.md +73 -13
- package/framework/rules/nextjs-standards.md +1 -0
- package/framework/rules/nextjs-templates.md +40 -0
- package/framework/rules/resend-standards.md +12 -0
- package/framework/rules/supabase-standards.md +29 -0
- package/framework/skills/README.md +6 -8
- package/framework/skills/level-0-meta/mcp-registry.json +21 -99
- package/framework/{standards/ai-agents/team-orchestration.md → skills/level-0-meta/morph-agent-teams/SKILL.md} +472 -479
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +28 -1
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +1 -5
- package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +1 -1
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +23 -41
- package/framework/skills/level-0-meta/morph-init/SKILL.md +18 -14
- package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +225 -0
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +20 -0
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +4 -8
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +15 -59
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +7 -19
- package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +8 -8
- package/framework/skills/level-1-workflows/README.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +49 -50
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +58 -124
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +5 -50
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +127 -159
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +4 -3
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +2 -2
- package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +17 -13
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +22 -40
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -2
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +5 -5
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +57 -77
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/framework/squad-templates/backend-only.json +6 -6
- package/framework/squad-templates/frontend-only.json +17 -8
- package/framework/squad-templates/full-stack.json +29 -24
- package/framework/standards/STANDARDS.json +1021 -2369
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +200 -0
- package/framework/standards/backend/dotnet/result-pattern.md +213 -0
- package/framework/standards/backend/dotnet/scalar-ui.md +172 -0
- package/framework/standards/backend/dotnet/scrutor.md +158 -0
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -0
- package/framework/standards/frontend/nextjs/motion-patterns.md +1 -1
- package/framework/standards/infrastructure/supabase/mcp-setup.md +83 -193
- package/framework/standards/integration/mcp/mcp-tools.md +91 -77
- package/framework/templates/IDE-SNIPPETS.md +2 -6
- package/framework/templates/README.md +709 -814
- package/framework/templates/REGISTRY.json +223 -762
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -0
- package/framework/templates/code/dotnet/abstractions/error.cs +60 -0
- package/framework/templates/code/dotnet/abstractions/handler.cs +12 -0
- package/framework/templates/code/dotnet/abstractions/result.cs +74 -0
- package/framework/templates/code/dotnet/backend/repository.cs +148 -141
- package/framework/templates/code/dotnet/constants/api-tags.cs +6 -0
- package/framework/templates/code/dotnet/exceptions/exception-handler.cs +32 -0
- package/framework/templates/code/dotnet/extensions/database-extensions.cs +20 -0
- package/framework/templates/code/dotnet/extensions/handler-registration.cs +38 -0
- package/framework/templates/code/dotnet/extensions/health-checks.cs +24 -0
- package/framework/templates/code/dotnet/extensions/map-endpoints.cs +35 -0
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +29 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -0
- package/framework/templates/code/dotnet/feature/entity.cs.hbs +8 -0
- package/framework/templates/code/dotnet/feature/errors.cs.hbs +8 -0
- package/framework/templates/code/dotnet/feature/handler.cs.hbs +24 -0
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -0
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -0
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -0
- package/framework/templates/code/dotnet/jobs/job.cs +21 -17
- package/framework/templates/code/dotnet/pipelines/logging-decorator.cs +31 -0
- package/framework/templates/code/dotnet/pipelines/validation-decorator.cs +97 -0
- package/framework/templates/code/dotnet/test.cs +192 -106
- package/framework/templates/context/ARCHITECTURE.md +89 -0
- package/framework/templates/context/CONTEXT-FEATURE.md +2 -2
- package/framework/templates/context/CONTEXT.md +180 -181
- package/framework/templates/context/design-system.md +1 -5
- package/framework/templates/docs/onboarding.md +2 -3
- package/framework/templates/docs/proposal.md +171 -182
- package/framework/templates/docs/spec.md +1 -3
- package/framework/templates/docs/ui-components.md +0 -72
- package/framework/templates/examples/design-system-examples.md +4 -4
- package/framework/templates/feature/decisions.md +0 -66
- package/framework/templates/feature/recap.md +1 -2
- package/framework/templates/infrastructure/github/README.md +56 -593
- package/framework/templates/integrations/clerk-config.cs +0 -41
- package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +2 -2
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -2
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +83 -126
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +2 -2
- package/framework/templates/project/validate.js +2 -4
- package/framework/templates/ui/design-system.css +1 -1
- package/framework/workflows/README.md +8 -8
- package/framework/workflows/configs/fast-track.json +51 -0
- package/framework/workflows/configs/full.json +68 -0
- package/framework/workflows/configs/standard.json +7 -3
- package/framework/workflows/docs/STORY-DRIVEN-DEVELOPMENT.md +1 -1
- package/framework/workflows/docs/enforcement-pipeline.md +18 -19
- package/framework/workflows/docs/full-morph.md +69 -76
- package/framework/workflows/docs/standard.md +49 -44
- package/framework/workflows/docs/ui-refresh.md +1 -1
- package/package.json +3 -3
- package/src/commands/agents/dispatch-agents.js +55 -116
- package/src/commands/context/context-refresh.js +79 -0
- package/src/commands/phase/phase-reset.js +1 -1
- package/src/commands/project/doctor.js +10 -32
- package/src/commands/project/init.js +12 -0
- package/src/commands/project/status.js +6 -12
- package/src/commands/scaffold/scaffold.js +131 -0
- package/src/commands/scope/escalate.js +1 -1
- package/src/commands/state/advance-phase.js +15 -29
- package/src/commands/state/state.js +20 -5
- package/src/commands/tasks/task.js +0 -30
- package/src/commands/validation/validate-feature.js +3 -10
- package/src/core/paths/output-schema.js +12 -13
- package/src/core/state/phase-state-machine.js +211 -217
- package/src/core/state/state-manager.js +9 -9
- package/src/lib/agents/agents-manager.js +2 -2
- package/src/lib/agents/output-collector.js +1 -2
- package/src/lib/detectors/design-system-detector.js +4 -4
- package/src/lib/detectors/index.js +1 -11
- package/src/lib/generators/architecture-scanner.js +640 -0
- package/src/lib/generators/context-generator.js +3 -3
- package/src/lib/generators/design-system-generator.js +508 -587
- package/src/lib/generators/index.js +12 -10
- package/src/lib/generators/metadata-extractor.js +386 -387
- package/src/lib/generators/recap-generator.js +4 -4
- package/src/lib/generators/scaffold-generator.js +317 -0
- package/src/lib/generators/settings-generator.js +0 -1
- package/src/lib/phase-chain/phase-validator.js +64 -42
- package/src/lib/stack/stack-profile.js +2 -2
- package/src/lib/standards/digest-builder.js +72 -285
- package/src/lib/tasks/task-parser.js +11 -5
- package/src/lib/validators/architecture/architecture-validator.js +259 -387
- package/src/lib/validators/content/content-validator.js +287 -287
- package/src/lib/validators/css/css-validator.js +340 -352
- package/src/lib/validators/packages/package-validator.js +347 -360
- package/src/lib/validators/shared/issue-counter.js +14 -18
- package/src/lib/validators/validation-runner.js +3 -19
- package/src/scripts/setup-infra.js +1 -1
- package/src/utils/agents-installer.js +24 -3
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +11 -19
- package/src/utils/skills-installer.js +178 -178
- package/framework/agents/architecture/prompt-engineer.md +0 -195
- package/framework/agents/backend/api-designer.md +0 -106
- package/framework/agents/frontend/blazor-builder.md +0 -229
- package/framework/agents/frontend/nextjs-ui-specialist.md +0 -218
- package/framework/agents/infrastructure/azure-architect.md +0 -150
- package/framework/agents/infrastructure/azure-deploy-specialist.md +0 -707
- package/framework/agents/infrastructure/bicep-architect.md +0 -132
- package/framework/agents/infrastructure/container-specialist.md +0 -137
- package/framework/agents/infrastructure/devops-engineer.md +0 -126
- package/framework/agents/integrations/azure-identity.md +0 -148
- package/framework/agents/quality/code-analyzer.md +0 -239
- package/framework/agents/quality/testing-specialist.md +0 -130
- package/framework/hooks/claude-code/notification/approval-reminder.js +0 -57
- package/framework/hooks/claude-code/post-tool-use/context-refresh.js +0 -109
- package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +0 -78
- package/framework/index/troubleshooting-index.json +0 -184
- package/framework/rules/testing-standards.md +0 -14
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +0 -230
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +0 -70
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +0 -256
- package/framework/skills/level-1-workflows/morph-phase-clarify/references/clarifications-example.md +0 -117
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +0 -269
- package/framework/standards/ai-agents/blazor-ui.md +0 -364
- package/framework/standards/architecture/ddd/aggregates.md +0 -120
- package/framework/standards/architecture/ddd/bounded-contexts.md +0 -105
- package/framework/standards/architecture/ddd/complexity-levels.md +0 -108
- package/framework/standards/architecture/ddd/entities.md +0 -99
- package/framework/standards/architecture/ddd/ubiquitous-language.md +0 -58
- package/framework/standards/architecture/ddd/value-objects.md +0 -124
- package/framework/standards/backend/api/minimal-api.md +0 -494
- package/framework/standards/backend/api/rest.md +0 -492
- package/framework/standards/backend/authentication/passkeys.md +0 -428
- package/framework/standards/backend/database/ef-core.md +0 -199
- package/framework/standards/backend/database/migrations.md +0 -393
- package/framework/standards/backend/database/repository-patterns.md +0 -528
- package/framework/standards/backend/dotnet/async.md +0 -366
- package/framework/standards/backend/dotnet/core.md +0 -117
- package/framework/standards/backend/dotnet/di.md +0 -439
- package/framework/standards/backend/dotnet/program-cs-checklist.md +0 -92
- package/framework/standards/context/analytics.md +0 -96
- package/framework/standards/context/bundles.md +0 -110
- package/framework/standards/context/priming.md +0 -78
- package/framework/standards/core/architecture.md +0 -185
- package/framework/standards/core/coding.md +0 -214
- package/framework/standards/core/git-branching-strategy.md +0 -403
- package/framework/standards/core/git.md +0 -185
- package/framework/standards/core/testing.md +0 -295
- package/framework/standards/data/nosql/blob-storage.md +0 -102
- package/framework/standards/data/vector-search/azure-ai-search.md +0 -121
- package/framework/standards/frontend/blazor/design-checklist.md +0 -222
- package/framework/standards/frontend/blazor/fluent-ui-setup.md +0 -595
- package/framework/standards/frontend/blazor/fluent-ui.md +0 -137
- package/framework/standards/frontend/blazor/html-conversion.md +0 -184
- package/framework/standards/frontend/blazor/lifecycle.md +0 -195
- package/framework/standards/frontend/blazor/pitfalls.md +0 -198
- package/framework/standards/frontend/blazor/state.md +0 -191
- package/framework/standards/frontend/design-system/aesthetic-direction.md +0 -266
- package/framework/standards/frontend/design-system/design-trends.md +0 -171
- package/framework/standards/infrastructure/azure/azure.md +0 -624
- package/framework/standards/infrastructure/azure/bicep/bicep-patterns.md +0 -422
- package/framework/standards/infrastructure/azure/devops/azure-devops-setup.md +0 -516
- package/framework/standards/infrastructure/azure/devops/local-development.md +0 -520
- package/framework/standards/infrastructure/azure/services/functions.md +0 -486
- package/framework/standards/infrastructure/azure/services/service-bus.md +0 -459
- package/framework/standards/infrastructure/azure/services/storage.md +0 -407
- package/framework/standards/infrastructure/vercel/vercel-database.md +0 -106
- package/framework/standards/integration/api/graphql.md +0 -91
- package/framework/standards/integration/api/grpc.md +0 -114
- package/framework/standards/integration/api/rest-design.md +0 -95
- package/framework/standards/integration/event-driven/cqrs.md +0 -101
- package/framework/standards/integration/event-driven/event-sourcing.md +0 -124
- package/framework/standards/integration/event-driven/service-bus.md +0 -95
- package/framework/standards/observability/logging.md +0 -131
- package/framework/standards/observability/metrics.md +0 -121
- package/framework/standards/observability/monitoring.md +0 -114
- package/framework/standards/observability/tracing.md +0 -132
- package/framework/standards/workflows/parallel-execution.md +0 -112
- package/framework/standards/workflows/thread-management.md +0 -113
- package/framework/templates/code/dotnet/backend/service.cs +0 -139
- package/framework/templates/code/dotnet/contracts/Commands.cs +0 -74
- package/framework/templates/code/dotnet/contracts/Entities.cs +0 -25
- package/framework/templates/code/dotnet/contracts/Queries.cs +0 -74
- package/framework/templates/code/dotnet/contracts/api-contracts.cs +0 -173
- package/framework/templates/code/dotnet/contracts/contracts-level1.cs +0 -69
- package/framework/templates/code/dotnet/contracts/contracts-level2.cs +0 -86
- package/framework/templates/code/dotnet/contracts/contracts-level3.cs +0 -41
- package/framework/templates/code/dotnet/frontend/component.razor +0 -239
- package/framework/templates/infrastructure/azure/Dockerfile.example +0 -82
- package/framework/templates/infrastructure/azure/README.md +0 -286
- package/framework/templates/infrastructure/azure/app-insights.bicep +0 -63
- package/framework/templates/infrastructure/azure/app-service.bicep +0 -164
- package/framework/templates/infrastructure/azure/container-app-env.bicep +0 -49
- package/framework/templates/infrastructure/azure/container-app.bicep +0 -156
- package/framework/templates/infrastructure/azure/deploy-checklist.md +0 -426
- package/framework/templates/infrastructure/azure/deploy.ps1 +0 -229
- package/framework/templates/infrastructure/azure/deploy.sh +0 -208
- package/framework/templates/infrastructure/azure/key-vault.bicep +0 -91
- package/framework/templates/infrastructure/azure/main.bicep +0 -189
- package/framework/templates/infrastructure/azure/parameters.dev.json +0 -29
- package/framework/templates/infrastructure/azure/parameters.prod.json +0 -29
- package/framework/templates/infrastructure/azure/parameters.staging.json +0 -29
- package/framework/templates/infrastructure/azure/sql-database.bicep +0 -103
- package/framework/templates/infrastructure/azure/storage.bicep +0 -106
- package/framework/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +0 -22
- package/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +0 -61
- package/framework/templates/integrations/azure-identity-config.cs +0 -288
- package/framework/templates/project-structure/dotnet-ddd.md +0 -70
- package/framework/templates/saas/subscription.cs +0 -347
- package/framework/templates/saas/tenant.cs +0 -338
- package/framework/templates/ui/FluentDesignTheme.cs +0 -149
- package/framework/templates/ui/MudTheme.cs +0 -281
- package/framework/workflows/configs/nodejs-cli.json +0 -40
- package/src/commands/standards/context-budget.js +0 -89
- package/src/lib/standards/index.js +0 -7
- package/src/lib/standards/standards-context-injector.js +0 -302
- package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +0 -316
- package/src/lib/validators/blazor/blazor-state-validator.js +0 -279
- package/src/lib/validators/blazor/blazor-validator.js +0 -369
- package/src/lib/validators/blazor/index.js +0 -9
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
namespace {{pascalCase PROJECT_NAME}}.Abstractions.Errors;
|
|
2
|
+
|
|
3
|
+
public record Error(string Code, string? Description = default, ErrorType Type = ErrorType.Failure)
|
|
4
|
+
{
|
|
5
|
+
public static readonly Error None = new(string.Empty);
|
|
6
|
+
public static readonly Error Null = new("Error.NullValue", "The specified result value is null.");
|
|
7
|
+
|
|
8
|
+
public static implicit operator Result(Error error) => Result.Failure(error);
|
|
9
|
+
|
|
10
|
+
public static Error Failure(string code, string description) =>
|
|
11
|
+
new(code, description, ErrorType.Failure);
|
|
12
|
+
|
|
13
|
+
public static Error Unexpected(string code, string description) =>
|
|
14
|
+
new(code, description, ErrorType.Unexpected);
|
|
15
|
+
|
|
16
|
+
public static Error Validation(string code, string description) =>
|
|
17
|
+
new(code, description, ErrorType.Validation);
|
|
18
|
+
|
|
19
|
+
public static Error Conflict(string code, string description) =>
|
|
20
|
+
new(code, description, ErrorType.Conflict);
|
|
21
|
+
|
|
22
|
+
public static Error NotFound(string code, string description) =>
|
|
23
|
+
new(code, description, ErrorType.NotFound);
|
|
24
|
+
|
|
25
|
+
public static Error Unauthorized(string code, string description) =>
|
|
26
|
+
new(code, description, ErrorType.Unauthorized);
|
|
27
|
+
|
|
28
|
+
public static Error Forbidden(string code, string description) =>
|
|
29
|
+
new(code, description, ErrorType.Forbidden);
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public enum ErrorType
|
|
34
|
+
{
|
|
35
|
+
Failure,
|
|
36
|
+
Unexpected,
|
|
37
|
+
Validation,
|
|
38
|
+
Conflict,
|
|
39
|
+
NotFound,
|
|
40
|
+
Unauthorized,
|
|
41
|
+
Forbidden,
|
|
42
|
+
Custom
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public sealed record ValidationError : Error
|
|
46
|
+
{
|
|
47
|
+
public ValidationError(Error[] errors)
|
|
48
|
+
: base(
|
|
49
|
+
"Validation.General",
|
|
50
|
+
"One or more validation errors occurred",
|
|
51
|
+
ErrorType.Validation)
|
|
52
|
+
{
|
|
53
|
+
Errors = errors;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public Error[] Errors { get; }
|
|
57
|
+
|
|
58
|
+
public static ValidationError FromResults(IEnumerable<Result> results) =>
|
|
59
|
+
new(results.Where(r => r.IsFailure).Select(r => r.Error).ToArray());
|
|
60
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
|
|
2
|
+
using {{pascalCase PROJECT_NAME}}.Abstractions.Errors;
|
|
3
|
+
|
|
4
|
+
namespace {{pascalCase PROJECT_NAME}}.Abstractions
|
|
5
|
+
{
|
|
6
|
+
public class Result
|
|
7
|
+
{
|
|
8
|
+
protected internal Result(bool isSuccess, Error error)
|
|
9
|
+
{
|
|
10
|
+
if (isSuccess && error != Error.None ||
|
|
11
|
+
!isSuccess && error == Error.None)
|
|
12
|
+
{
|
|
13
|
+
throw new ArgumentException("Inavlid error", nameof(error));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
IsSuccess = isSuccess;
|
|
17
|
+
Error = error;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public bool IsSuccess { get; }
|
|
21
|
+
|
|
22
|
+
public bool IsFailure => !IsSuccess;
|
|
23
|
+
|
|
24
|
+
public Error Error { get; }
|
|
25
|
+
|
|
26
|
+
public static Result Success()
|
|
27
|
+
{
|
|
28
|
+
return new(true, Error.None);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static Result<TValue> Success<TValue>(TValue value)
|
|
32
|
+
{
|
|
33
|
+
return new(value, true, Error.None);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public static Result Failure(Error error)
|
|
37
|
+
{
|
|
38
|
+
return new(false, error);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public static Result<TValue> Failure<TValue>(Error error)
|
|
42
|
+
{
|
|
43
|
+
return new(default, false, error);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public static Result<TValue> Create<TValue>(TValue? value)
|
|
47
|
+
{
|
|
48
|
+
return value is not null ? Success(value) : Failure<TValue>(Error.Null);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public class Result<TValue> : Result
|
|
53
|
+
{
|
|
54
|
+
private readonly TValue? _value;
|
|
55
|
+
|
|
56
|
+
protected internal Result(TValue? value, bool isSuccess, Error error)
|
|
57
|
+
: base(isSuccess, error)
|
|
58
|
+
{
|
|
59
|
+
_value = value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public TValue Value => IsSuccess
|
|
63
|
+
? _value!
|
|
64
|
+
: throw new InvalidOperationException("The value of a failure result can not be accessed.");
|
|
65
|
+
|
|
66
|
+
// ✅ SUCCESS implicit conversion
|
|
67
|
+
public static implicit operator Result<TValue>(TValue? value) => Create(value);
|
|
68
|
+
|
|
69
|
+
// ✅ ADD THIS (Failure implicit conversion)
|
|
70
|
+
public static implicit operator Result<TValue>(Error error)
|
|
71
|
+
=> Failure<TValue>(error);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
}
|
|
@@ -1,141 +1,148 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// REPOSITORY TEMPLATE
|
|
3
|
-
// Generated by MORPH Framework
|
|
4
|
-
// ============================================================
|
|
5
|
-
|
|
6
|
-
using
|
|
7
|
-
using
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
public
|
|
116
|
-
{
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
1
|
+
// ============================================================
|
|
2
|
+
// REPOSITORY + UNIT OF WORK TEMPLATE (VSA Pattern)
|
|
3
|
+
// Generated by MORPH Framework
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
using System.Data.Common;
|
|
7
|
+
using Microsoft.EntityFrameworkCore;
|
|
8
|
+
using {{pascalCase PROJECT_NAME}}.Database;
|
|
9
|
+
|
|
10
|
+
namespace {{pascalCase PROJECT_NAME}}.Repository;
|
|
11
|
+
|
|
12
|
+
// ============================================================
|
|
13
|
+
// IRepository<T> — Generic repository contract
|
|
14
|
+
// ============================================================
|
|
15
|
+
|
|
16
|
+
public interface IRepository<T> where T : class
|
|
17
|
+
{
|
|
18
|
+
Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default);
|
|
19
|
+
Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default);
|
|
20
|
+
Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
|
|
21
|
+
Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
|
|
22
|
+
Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
|
|
23
|
+
IEnumerable<T> ExecuteQuery(string query, DbParameter[] dbParams);
|
|
24
|
+
Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
|
|
25
|
+
void RemoveRange(IEnumerable<T> entities);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ============================================================
|
|
29
|
+
// IUnitOfWork — Transaction boundary contract
|
|
30
|
+
// ============================================================
|
|
31
|
+
|
|
32
|
+
public interface IUnitOfWork
|
|
33
|
+
{
|
|
34
|
+
int Commit();
|
|
35
|
+
void Rollback();
|
|
36
|
+
Task<int> CommitAsync(CancellationToken cancellationToken = default);
|
|
37
|
+
Task RollbackAsync();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ============================================================
|
|
41
|
+
// Repository<T> — EF Core generic repository implementation
|
|
42
|
+
// ============================================================
|
|
43
|
+
|
|
44
|
+
public class Repository<T> : IRepository<T> where T : class
|
|
45
|
+
{
|
|
46
|
+
private readonly ApplicationDbContext _context;
|
|
47
|
+
private readonly DbSet<T> _dbSet;
|
|
48
|
+
|
|
49
|
+
public Repository(ApplicationDbContext context)
|
|
50
|
+
{
|
|
51
|
+
_context = context;
|
|
52
|
+
_dbSet = _context.Set<T>();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default)
|
|
56
|
+
{
|
|
57
|
+
return await _dbSet.ToListAsync(cancellationToken);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public async Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default)
|
|
61
|
+
{
|
|
62
|
+
return await _dbSet.FindAsync(new[] { id }, cancellationToken);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public async Task<T> AddAsync(T entity, CancellationToken cancellationToken = default)
|
|
66
|
+
{
|
|
67
|
+
await _dbSet.AddAsync(entity, cancellationToken);
|
|
68
|
+
return entity;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public Task UpdateAsync(T entity, CancellationToken cancellationToken = default)
|
|
72
|
+
{
|
|
73
|
+
_dbSet.Update(entity);
|
|
74
|
+
return Task.CompletedTask;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public Task DeleteAsync(T entity, CancellationToken cancellationToken = default)
|
|
78
|
+
{
|
|
79
|
+
_dbSet.Remove(entity);
|
|
80
|
+
return Task.CompletedTask;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public IEnumerable<T> ExecuteQuery(string query, DbParameter[] dbParams)
|
|
84
|
+
{
|
|
85
|
+
return _dbSet
|
|
86
|
+
.FromSqlRaw(query, dbParams)
|
|
87
|
+
.AsNoTracking()
|
|
88
|
+
.AsEnumerable();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public async Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default)
|
|
92
|
+
{
|
|
93
|
+
await _dbSet.AddRangeAsync(entities, cancellationToken);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public void RemoveRange(IEnumerable<T> entities)
|
|
97
|
+
{
|
|
98
|
+
_dbSet.RemoveRange(entities);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ============================================================
|
|
103
|
+
// UnitOfWork — EF Core transaction management
|
|
104
|
+
// ============================================================
|
|
105
|
+
|
|
106
|
+
public class UnitOfWork : IUnitOfWork
|
|
107
|
+
{
|
|
108
|
+
private readonly ApplicationDbContext _dataContext;
|
|
109
|
+
|
|
110
|
+
public UnitOfWork(ApplicationDbContext dataContext)
|
|
111
|
+
{
|
|
112
|
+
_dataContext = dataContext;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public int Commit()
|
|
116
|
+
{
|
|
117
|
+
return _dataContext.SaveChanges();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public async Task<int> CommitAsync(CancellationToken cancellationToken = default)
|
|
121
|
+
{
|
|
122
|
+
return await _dataContext.SaveChangesAsync(cancellationToken);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public void Rollback()
|
|
126
|
+
{
|
|
127
|
+
_dataContext.Dispose();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public async Task RollbackAsync()
|
|
131
|
+
{
|
|
132
|
+
await _dataContext.DisposeAsync();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ============================================================
|
|
137
|
+
// DI REGISTRATION
|
|
138
|
+
// ============================================================
|
|
139
|
+
//
|
|
140
|
+
// In Program.cs or a DI extension method:
|
|
141
|
+
//
|
|
142
|
+
// builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|
143
|
+
// options.UseSqlServer(connectionString));
|
|
144
|
+
//
|
|
145
|
+
// builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
|
146
|
+
// builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
|
|
147
|
+
//
|
|
148
|
+
// ============================================================
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
using Microsoft.AspNetCore.Diagnostics;
|
|
2
|
+
using Microsoft.AspNetCore.Mvc;
|
|
3
|
+
|
|
4
|
+
namespace {{pascalCase PROJECT_NAME}}.Exceptions
|
|
5
|
+
{
|
|
6
|
+
public class CustomExceptionHandler : IExceptionHandler
|
|
7
|
+
{
|
|
8
|
+
public async ValueTask<bool> TryHandleAsync(HttpContext context, Exception exception, CancellationToken cancellationToken)
|
|
9
|
+
{
|
|
10
|
+
int statusCode = exception switch
|
|
11
|
+
{
|
|
12
|
+
UnauthorizedAccessException => StatusCodes.Status401Unauthorized,
|
|
13
|
+
ArgumentException => StatusCodes.Status400BadRequest,
|
|
14
|
+
_ => StatusCodes.Status500InternalServerError
|
|
15
|
+
};
|
|
16
|
+
context.Response.StatusCode = statusCode;
|
|
17
|
+
|
|
18
|
+
var problemDetails = new ProblemDetails
|
|
19
|
+
{
|
|
20
|
+
Status = statusCode,
|
|
21
|
+
Title = "An error occurred",
|
|
22
|
+
Type = exception.GetType().Name,
|
|
23
|
+
Detail = exception.Message,
|
|
24
|
+
Instance = context.Request.Path
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
await context.Response.WriteAsJsonAsync(problemDetails, cancellationToken);
|
|
28
|
+
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
using Microsoft.EntityFrameworkCore;
|
|
2
|
+
using {{pascalCase PROJECT_NAME}}.Database;
|
|
3
|
+
|
|
4
|
+
namespace {{pascalCase PROJECT_NAME}}.Extensions
|
|
5
|
+
{
|
|
6
|
+
public static class DatabaseExtensions
|
|
7
|
+
{
|
|
8
|
+
public static IServiceCollection AddSQLDatabaseConfiguration(this IServiceCollection services,
|
|
9
|
+
IConfiguration configuration)
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
services.AddDbContext<ApplicationDbContext>(options =>
|
|
13
|
+
{
|
|
14
|
+
options.UseNpgsql(configuration.GetConnectionString("connection"));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return services;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
using System.Reflection;
|
|
2
|
+
using {{pascalCase PROJECT_NAME}}.Abstractions;
|
|
3
|
+
using {{pascalCase PROJECT_NAME}}.Pipelines;
|
|
4
|
+
|
|
5
|
+
public static class HandlerRegistrationExtensions
|
|
6
|
+
{
|
|
7
|
+
public static IServiceCollection AddHandlersFromAssembly(
|
|
8
|
+
this IServiceCollection services,
|
|
9
|
+
Assembly assembly)
|
|
10
|
+
{
|
|
11
|
+
var handlerTypes = assembly.GetTypes()
|
|
12
|
+
.Where(t =>
|
|
13
|
+
t.IsClass &&
|
|
14
|
+
!t.IsAbstract &&
|
|
15
|
+
!t.ContainsGenericParameters)
|
|
16
|
+
.ToList();
|
|
17
|
+
|
|
18
|
+
foreach (var implementation in handlerTypes)
|
|
19
|
+
{
|
|
20
|
+
var handlerInterfaces = implementation
|
|
21
|
+
.GetInterfaces()
|
|
22
|
+
.Where(i =>
|
|
23
|
+
i.IsGenericType &&
|
|
24
|
+
i.GetGenericTypeDefinition() == typeof(IHandler<,>));
|
|
25
|
+
|
|
26
|
+
foreach (var handlerInterface in handlerInterfaces)
|
|
27
|
+
{
|
|
28
|
+
services.AddScoped(handlerInterface, implementation);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
services.Decorate(typeof(IHandler<,>), typeof(ValidationDecorator<,>));
|
|
33
|
+
services.Decorate(typeof(IHandler<,>), typeof(LoggingDecorator<,>));
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
return services;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
using HealthChecks.UI.Client;
|
|
2
|
+
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
|
3
|
+
using {{pascalCase PROJECT_NAME}}.Database;
|
|
4
|
+
|
|
5
|
+
namespace {{pascalCase PROJECT_NAME}}.Extensions
|
|
6
|
+
{
|
|
7
|
+
public static class HealthChecksExtensions
|
|
8
|
+
{
|
|
9
|
+
public static IServiceCollection AddHealthChecksConfiguration(this IServiceCollection services)
|
|
10
|
+
{
|
|
11
|
+
services.AddHealthChecks()
|
|
12
|
+
.AddDbContextCheck<ApplicationDbContext>();
|
|
13
|
+
return services;
|
|
14
|
+
}
|
|
15
|
+
public static IEndpointRouteBuilder UseHealthChecks(this IEndpointRouteBuilder app)
|
|
16
|
+
{
|
|
17
|
+
app.MapHealthChecks("health", new HealthCheckOptions
|
|
18
|
+
{
|
|
19
|
+
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
|
20
|
+
});
|
|
21
|
+
return app;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
2
|
+
using System.Net;
|
|
3
|
+
using System.Reflection;
|
|
4
|
+
using {{pascalCase PROJECT_NAME}}.Abstractions;
|
|
5
|
+
|
|
6
|
+
namespace {{pascalCase PROJECT_NAME}}.Extensions
|
|
7
|
+
{
|
|
8
|
+
public static class MapEndpointExtensions
|
|
9
|
+
{
|
|
10
|
+
public static IServiceCollection RegisterApiEndpointsFromAssembly(this IServiceCollection services, Assembly assembly)
|
|
11
|
+
{
|
|
12
|
+
var endpointTypes = assembly.GetTypes()
|
|
13
|
+
.Where(t => t.IsAssignableTo(typeof(IApiEndpoint)) && t is { IsClass: true, IsAbstract: false, IsInterface: false });
|
|
14
|
+
|
|
15
|
+
var serviceDescriptors = endpointTypes
|
|
16
|
+
.Select(type => ServiceDescriptor.Transient(typeof(IApiEndpoint), type))
|
|
17
|
+
.ToArray();
|
|
18
|
+
|
|
19
|
+
services.TryAddEnumerable(serviceDescriptors);
|
|
20
|
+
return services;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public static WebApplication MapApiEndpoints(this WebApplication app)
|
|
24
|
+
{
|
|
25
|
+
var endpoints = app.Services.GetRequiredService<IEnumerable<IApiEndpoint>>();
|
|
26
|
+
|
|
27
|
+
foreach (var endpoint in endpoints)
|
|
28
|
+
{
|
|
29
|
+
endpoint.MapEndpoint(app);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return app;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
using {{pascalCase PROJECT_NAME}}.Abstractions;
|
|
2
|
+
using {{pascalCase PROJECT_NAME}}.Abstractions.Errors;
|
|
3
|
+
|
|
4
|
+
namespace {{pascalCase PROJECT_NAME}}.Extensions;
|
|
5
|
+
|
|
6
|
+
public static class ResultExtensions
|
|
7
|
+
{
|
|
8
|
+
// For non-generic Result
|
|
9
|
+
public static TOut Match<TOut>(
|
|
10
|
+
this Result result,
|
|
11
|
+
Func<TOut> onSuccess,
|
|
12
|
+
Func<Error, TOut> onFailure)
|
|
13
|
+
{
|
|
14
|
+
return result.IsSuccess
|
|
15
|
+
? onSuccess()
|
|
16
|
+
: onFailure(result.Error);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 🔥 THIS is what your endpoint needs
|
|
20
|
+
public static TOut Match<TIn, TOut>(
|
|
21
|
+
this Result<TIn> result,
|
|
22
|
+
Func<TIn, TOut> onSuccess,
|
|
23
|
+
Func<Error, TOut> onFailure)
|
|
24
|
+
{
|
|
25
|
+
return result.IsSuccess
|
|
26
|
+
? onSuccess(result.Value)
|
|
27
|
+
: onFailure(result.Error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
using {{pascalCase PROJECT_NAME}}.Abstractions;
|
|
2
|
+
using {{pascalCase PROJECT_NAME}}.Constants;
|
|
3
|
+
using {{pascalCase PROJECT_NAME}}.Extensions;
|
|
4
|
+
namespace {{pascalCase PROJECT_NAME}}.Features.{{pascalCase FEATURE_NAME}}Feature.{{pascalCase SLICE_NAME}};
|
|
5
|
+
|
|
6
|
+
internal sealed class {{pascalCase SLICE_NAME}}Endpoint : IApiEndpoint
|
|
7
|
+
{
|
|
8
|
+
public void MapEndpoint(WebApplication app)
|
|
9
|
+
{
|
|
10
|
+
app.MapPost("{{kebabCase FEATURE_NAME}}s", async (IHandler<{{pascalCase SLICE_NAME}}Request, Result<{{pascalCase SLICE_NAME}}Response>> handler, {{pascalCase SLICE_NAME}}Request command, CancellationToken cancellationToken) =>
|
|
11
|
+
{
|
|
12
|
+
var result = await handler.HandleAsync(command, cancellationToken);
|
|
13
|
+
return result.Match(
|
|
14
|
+
onSuccess: () => Results.Ok(result.Value),
|
|
15
|
+
onFailure: error => Results.BadRequest(error));
|
|
16
|
+
})
|
|
17
|
+
.WithTags(ApiTags.{{pascalCase FEATURE_NAME}}s)
|
|
18
|
+
.Produces<{{pascalCase SLICE_NAME}}Response>(StatusCodes.Status200OK)
|
|
19
|
+
.Produces(StatusCodes.Status400BadRequest);
|
|
20
|
+
}
|
|
21
|
+
}
|