Initial commit: Add project files
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
using FastEndpoints;
|
||||
namespace KArtSell.Modules.__MODULE__.Features.__FEATURE__;
|
||||
public sealed class Endpoint(IHandler handler) : Endpoint<Request, Response>
|
||||
{
|
||||
public override void Configure() { Post("/api/v1/__MODULE__/__FEATURE__"); Policies("__SLICE_ID__"); }
|
||||
public override async Task HandleAsync(Request request, CancellationToken ct)
|
||||
=> await SendOkAsync(await handler.HandleAsync(request, ct), ct);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace KArtSell.Modules.__MODULE__.Features.__FEATURE__;
|
||||
public interface IHandler { Task<Response> HandleAsync(Request request, CancellationToken ct); }
|
||||
public sealed class Handler : IHandler
|
||||
{
|
||||
public Task<Response> HandleAsync(Request request, CancellationToken ct)
|
||||
=> throw new NotImplementedException("Load approved server-side PIT context, execute a pure policy, persist decision and Outbox in one transaction.");
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# __SLICE_ID__ __MODULE__.__FEATURE__
|
||||
Required before implementation: Requirement/Policy/Data/API/DB/Job/UI/Test IDs, PIT cutoff, state transition, idempotency key, transaction boundary, Outbox event, Golden/boundary/failure/replay tests, metric/alert/runbook/rollback.
|
||||
@@ -0,0 +1,2 @@
|
||||
namespace KArtSell.Modules.__MODULE__.Features.__FEATURE__;
|
||||
public sealed record Request(string IdempotencyKey, string ScopeId, DateTimeOffset AsOf);
|
||||
@@ -0,0 +1,2 @@
|
||||
namespace KArtSell.Modules.__MODULE__.Features.__FEATURE__;
|
||||
public sealed record Response(Guid Id, string Status, string EvidenceHash, DateTimeOffset AsOf);
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace KArtSell.Modules.__MODULE__.Features.__FEATURE__;
|
||||
internal static class Sql
|
||||
{
|
||||
public const string LoadContext = """select /* explicit columns */ 1 where @as_of is not null;""";
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using FluentValidation;
|
||||
namespace KArtSell.Modules.__MODULE__.Features.__FEATURE__;
|
||||
public sealed class Validator : AbstractValidator<Request>
|
||||
{
|
||||
public Validator() { RuleFor(x => x.IdempotencyKey).NotEmpty().MaximumLength(100); RuleFor(x => x.ScopeId).NotEmpty(); RuleFor(x => x.AsOf).NotEmpty(); }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { api } from '@/shared/api/client'
|
||||
import { responseSchema, type RequestDto, type ResponseDto } from './schema'
|
||||
export async function execute(request: RequestDto): Promise<ResponseDto> {
|
||||
const response = await api.post('/api/v1/__MODULE__/__FEATURE__', request, { headers: { 'Idempotency-Key': request.idempotencyKey } })
|
||||
return responseSchema.parse(response.data)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<script setup lang="ts">import { CrudDetailPage } from '@/shared/ui/screen-types'</script>
|
||||
<template><CrudDetailPage title="__FEATURE__" status="IMPLEMENTATION_REQUIRED"><p>__SLICE_ID__ Slice packet. Replace this placeholder only after the Slice Ready Gate is approved.</p></CrudDetailPage></template>
|
||||
@@ -0,0 +1,4 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/vue-query'
|
||||
import { execute } from './api'
|
||||
export const queryKeys = { root: ['__MODULE__','__FEATURE__'] as const }
|
||||
export function useExecuteMutation() { const client=useQueryClient(); return useMutation({ mutationFn: execute, onSuccess: async()=>client.invalidateQueries({queryKey:queryKeys.root}) }) }
|
||||
@@ -0,0 +1,5 @@
|
||||
import { z } from 'zod'
|
||||
export const requestSchema = z.object({ idempotencyKey: z.string().min(1).max(100), scopeId: z.string().min(1), asOf: z.string().datetime() })
|
||||
export const responseSchema = z.object({ id: z.string().uuid(), status: z.string(), evidenceHash: z.string().min(16), asOf: z.string().datetime() })
|
||||
export type RequestDto = z.infer<typeof requestSchema>
|
||||
export type ResponseDto = z.infer<typeof responseSchema>
|
||||
@@ -0,0 +1,3 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { requestSchema } from '../schema'
|
||||
describe('__SLICE_ID__ request contract',()=>{it('rejects missing idempotency evidence',()=>{expect(requestSchema.safeParse({scopeId:'x',asOf:new Date().toISOString()}).success).toBe(false)})})
|
||||
Reference in New Issue
Block a user