feat(dotnet): support multiple configurable REST resources#108
Open
colbytimm wants to merge 6 commits into
Open
feat(dotnet): support multiple configurable REST resources#108colbytimm wants to merge 6 commits into
colbytimm wants to merge 6 commits into
Conversation
Port the multi-resource capability (already in the Python and TypeScript templates) to the .NET template. A `resources` list in copier.yml drives generic, registry-wired layers so one generated project can expose N resources, each with its own endpoint, a storage container (shared when ids match), and a chosen subset of operations (list, get_by_id, create, update=PATCH, replace=PUT [new], delete). - copier.yml: add validated `resources` question (PascalCase name, kebab endpoint, container id, valid ops, unique names/endpoints); default is a single resource reproducing today's CRUD behaviour. - Collapse per-resource, name-templated classes into one generic Item* set (controller/service/repository/interfaces/models) and add ReplaceAsync (full overwrite) across Cosmos/Firestore/DynamoDB. - DependencyInjection builds a per-container IItemController map keyed by container id, resolving each container/collection/table by name. - Per-cloud routing via in-file Jinja loops: Azure one ItemFunctions with looped [Function] methods; GCP Function.cs ResourceRoute dispatch; AWS ItemFunctions handlers + SAM template.yaml functions/tables per resource. - Per-container settings (local.settings.json, template.yaml, env vars). - Retarget unit tests to the generic Item* classes; drop per-op Functions tests. Update README endpoint list to loop over resources/operations. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHtZ26sF1zNNrEh1wDGzBa
Replace the generic Item* layers with per-resource named types (Cat, CatController, CatService, CatRepository, CatDto, ICatController, etc.), mirroring the Go/Python/TypeScript reworks. Controllers and services expose exactly one method per operation each resource declares; repositories stay a full data-access layer. - Models/Interfaces/Controllers/Services/Repositories loop over resources and generate a named class/interface per resource; controller & service methods and their request types are gated per operation. - DependencyInjection registers a per-resource I<Resource>Controller, each bound to its storage container (Cosmos container / Firestore collection / DynamoDB table); resources sharing a container id resolve the same handle. - Azure & AWS ItemFunctions inject per-resource controllers instead of a container-keyed IReadOnlyDictionary; GCP Function dispatches per resource with per-operation 405 gating. - Tests loop per resource with per-operation gating; repository tests stay full. The single-resource default renders named after the project with the five legacy operations, staying behaviourally equivalent to the pre-feature output. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHtZ26sF1zNNrEh1wDGzBa
Add a Publish Example Branches workflow that renders every combination of language (go/python/typescript/dotnet) x cloud (Azure/GCP/AWS) x resources fixture (single/multi) with Copier and force-pushes each generated project to its own examples/<language>-<cloud>-<fixture> branch, so the generated output is browsable without running Copier locally. - Extend the setup-copier-template composite action with optional `resources-fixture` (single|multi) and `output-dir` inputs; existing build pipelines keep their defaults (single / KittenClaws) unchanged. - Add a multi-resource showcase fixture (two resources sharing one container with different operation subsets — Cat via PATCH, Dog via PUT). - Prefer an EXAMPLES_PUBLISH_TOKEN PAT for full-fidelity publishing; without it, fall back to GITHUB_TOKEN and relocate each generated project's .github/workflows to .github/workflows-example (the default token cannot push workflow files). Triggers on pushes to main; temporarily also on this feature branch for verification (the TEMP branch trigger should be removed before/after merge). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHtZ26sF1zNNrEh1wDGzBa
Rename the published branch prefix from examples/ to example/ (example/<language>-<cloud>-<fixture>). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHtZ26sF1zNNrEh1wDGzBa
Add a temporary one-shot job that deletes the deprecated examples/* (plural) branches via the GitHub refs API, following the rename to example/*. The job is a no-op once the old branches are gone and should be removed afterward. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHtZ26sF1zNNrEh1wDGzBa
Add a pull_request trigger and a resolve job so each PR publishes example branches for the language(s) it actually changes, named example/pr-<number>-<language>-<cloud>-<fixture> and rendered from the PR head. Pushes to main still publish the canonical example/<language>-<cloud>-<fixture> set (all four languages). Also drop the temporary feature-branch push trigger and the one-shot legacy-examples prune job (its cleanup is complete). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHtZ26sF1zNNrEh1wDGzBa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports the multi-resource capability (already merged for the Python and TypeScript templates) to the .NET template. A
resourceslist incopier.ymlnow drives the generated layers so a single generated project can expose N resources, each with:list,get_by_id,create,update=PATCH,replace=PUT [new],delete).The default value is a single resource matching the project name with today's CRUD behaviour, so answering only
project_namereproduces the original single-resource project (no PUT). PUT/replace is opt-in.Approach — per-resource named types
Each resource generates its own named types —
Cat,CatController,CatService,CatRepository,CatDto,CreateCatRequest,ICatController, … — rather than a shared genericItem*set. Because Copier cannot fan a resource out into separate files, all per-resource classes for a layer live in one file behind a{% for resource in resources %}loop.{% if "op" in resource.operations %}gating on both the interface and the implementation). Repositories stay a full data-access layer (all six operations) regardless of the subset.ReplaceAsync(full-document overwrite) is implemented across Cosmos DB, Firestore, and DynamoDB.Changes
dotnet/copier.yml— add a validatedresourcesquestion (PascalCase name, kebab-case endpoint, valid container id, ≥1 valid operation, unique names/endpoints).Models(entities/DTOs/requests/validators),Interfaces,Controllers,Services,Repositoriesloop over resources and emit a named class/interface per resource; controller/service methods and their request types are gated per operation.DependencyInjectionregisters a per-resourceI<Resource>Controller, each bound to its storage container (Cosmos container / Firestore collection / DynamoDB table) resolved by name from settings; resources sharing a container id resolve the same handle.ItemFunctionsinjects per-resource controllers and exposes looped[Function]methods (Get,GetList,Create,Update,Replace,Delete) +Health.Function.csinjects per-resource controllers and dispatches by endpoint → method → id-presence, with per-operation405gating.ItemFunctionsinjects per-resource controllers;template.yamlemits oneAWS::Serverless::Functionper resource×operation, one DynamoDB table per distinct container, plus aHealthfunction.CosmosDbContainerName_<Container>, GCPFIRESTORE_COLLECTION_<Container>, AWSDYNAMODB_TABLE_NAME_<CONTAINER>.Verification
Rendered all 3 clouds × 3 fixtures (single default; two resources / separate containers / mixed ops incl. PUT; two resources sharing one container with different op subsets) with
copier copy --trust:.csfiles are brace/paren-balanced; no residual genericItem*domain types leak into output (the host wiring class keeps the internalItemFunctionsname, referenced by the AWS handler strings).405for disabled methods; SAM emits only the chosen functions).No local .NET SDK is available in this environment, so the build/test is validated via the
build-dotnetCI pipeline on this PR.🤖 Generated with Claude Code
Generated by Claude Code