feat: release managed service generation and contract diagnostics
This commit is contained in:
175
examples/managedService/README.md
Normal file
175
examples/managedService/README.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# example.com/managed
|
||||
|
||||
This checked-in example uses a relative local framework replacement (`../..`).
|
||||
Run `go mod tidy && go run .` here. No external database or cloud account is required.
|
||||
The Go-only error catalog is `internal/apierrors/catalog.go`, registered by svc.
|
||||
The generated main and handlers are unmodified. Try:
|
||||
|
||||
```sh
|
||||
curl -i 'http://127.0.0.1:8080/check?kind=fixed'
|
||||
curl -i 'http://127.0.0.1:8080/check?kind=params'
|
||||
curl -i 'http://127.0.0.1:8080/missing'
|
||||
curl -i -H 'X-Signature: valid' --data-binary ' {"hello":1} ' http://127.0.0.1:8080/callback
|
||||
curl -i -F 'file=hello' http://127.0.0.1:8080/upload
|
||||
curl -OJ http://127.0.0.1:8080/download
|
||||
```
|
||||
|
||||
The upload demonstration caps requests at 2048 bytes, so use a small test file;
|
||||
large input is intentionally rejected with HTTP 413. Replace the demonstration
|
||||
`X-Signature` check with the actual provider's signature algorithm and secret.
|
||||
From the framework root, check generated files with:
|
||||
`go run ./cmd/go-omni api generate --dir examples/managedService --check`.
|
||||
|
||||
Contract changes produce REVIEW notices for affected Logic methods and DTO fields.
|
||||
Definite Logic signature problems produce ERROR diagnostics with a file/line and
|
||||
the required signature. Regeneration preserves business code; `--check` continues
|
||||
to fail on unresolved signature errors after generation. Run `go test ./...` too,
|
||||
especially for project type aliases, build tags and changes inside method bodies.
|
||||
|
||||
|
||||
This project is generated from an editable API contract. Business code uses
|
||||
standard context.Context; generated HTTP handlers use Omni's transport-neutral httpx.
|
||||
main.go and HTTP composition are continuously generator-owned.
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
go mod tidy
|
||||
go run .
|
||||
```
|
||||
|
||||
GET /health/live and /health/ready check the running service. GET /openapi.json
|
||||
serves the same document as docs/openapi.json. Bind address defaults to localhost.
|
||||
Set GO_OMNI_TRANSPORT=native to select the native transport without changing services.
|
||||
The greeting starter works immediately; other new operation stubs return 501 until
|
||||
their business logic is implemented. Authentication hooks also fail closed with 501.
|
||||
|
||||
## Develop
|
||||
|
||||
1. Edit api/managed.api and its relative imports.
|
||||
2. Run the same go-omni CLI version as the framework dependency:
|
||||
|
||||
```sh
|
||||
go-omni api validate
|
||||
go-omni api inspect
|
||||
go-omni api generate --dry-run
|
||||
go-omni api generate
|
||||
go-omni api generate --check
|
||||
go-omni api export > openapi.json
|
||||
go test ./...
|
||||
```
|
||||
|
||||
Commands inside a generated project discover the recorded API entry automatically.
|
||||
--dry-run previews create/update/remove actions without writing files.
|
||||
The --check command is read-only and suitable for CI. It fails on stale or missing
|
||||
generated files and missing business scaffolds. go.mod, configuration, Logic,
|
||||
Repository, svc, middleware hook bodies and API files are created once and then
|
||||
preserved byte for byte. main.go, Gen.go files and OpenAPI are owned
|
||||
by the generator; manual edits cause a conflict instead of being overwritten.
|
||||
Commit .goOmni/apiManifest.json alongside the generated files. Do not edit it.
|
||||
Avoid editing files while generation is running; api.lock serializes generators.
|
||||
|
||||
Routes and types that disappear are removed from generated output. Handwritten
|
||||
business files remain: remove/adapt obsolete implementations explicitly if an
|
||||
API change makes their old types or signatures fail to compile. Every regeneration
|
||||
must be followed by go test ./... . Do not delete service files to change contracts.
|
||||
|
||||
## Layout
|
||||
|
||||
```text
|
||||
api/ # editable contract and imported types
|
||||
main.go # GENERATED startup, lifecycle and route registration
|
||||
etc/config.yaml # lowerCamelCase configuration
|
||||
internal/config/ # defaults -> file -> environment
|
||||
internal/svc/ # dependency initialization and cleanup
|
||||
internal/types/typesGen.go # generated request/response types
|
||||
internal/apierrors/errorsGen.go # generated fresh error constructors, when declared
|
||||
internal/handler/<group>/ # generated binding and Logic adapters
|
||||
internal/logic/<group>/ # developer-owned business operations
|
||||
internal/repository/<group>/ # developer-owned persistence contracts
|
||||
internal/middleware/ # named hooks and generated registry
|
||||
docs/openapi.json # generated OpenAPI 3.0.3
|
||||
.goOmni/apiManifest.json # ownership and content hashes
|
||||
```
|
||||
|
||||
The API response type describes business data. Declare response Name (...) and
|
||||
select the App default with @app(defaultResponse: Name) in the entry API.
|
||||
@server(response: Name) and @response Name override it for groups/routes;
|
||||
@response raw returns the business JSON directly. Without a template, the default
|
||||
envelope includes code, message, data and requestId. Generated routes and OpenAPI
|
||||
use the same contract. 404/405 and ordinary App routes use the declared App default.
|
||||
No template is selected by declaration/import order. Without @app, existing framework
|
||||
response defaults apply. An explicit API App default takes precedence over response
|
||||
options in svc, keeping generated routes and OpenAPI consistent.
|
||||
Handlers reject nil successful responses. Body DTOs use application/json and have a 1 MiB limit by default.
|
||||
Use @body(maxBytes: ...) for route limits. @body(mode: raw|multipart; maxBytes: ...)
|
||||
adds a *httpx.Body argument to Logic, without handwritten handlers. Raw is the exact
|
||||
limited request stream; Parts supports sequential multipart reading with no temp files.
|
||||
@produces "application/octet-stream" (or another concrete media type) generates a
|
||||
Logic result of *httpx.Stream; the handler closes Content and streams it unwrapped.
|
||||
Signature verification and storage policy belong to Logic/middleware. A consumed
|
||||
request stream cannot be reread automatically; use one reader owner or explicitly
|
||||
bounded buffering. Stream reads respect configured server timeouts. Multipart parts
|
||||
are not DTO form fields: form tags remain URL query parameters. The optional manual
|
||||
handler/custom binder hooks are preserved, and are needed only for custom protocols.
|
||||
|
||||
Fields require exactly one source tag: json, path, form (URL query only), header.
|
||||
All are required by presence unless optional, omitempty or default is specified.
|
||||
Zero and false are valid present values; binding:"required" rejects scalar zero.
|
||||
Use *T with optional when business logic needs to distinguish missing from zero.
|
||||
Pointers may carry null; non-pointer null is rejected. Unknown/duplicate JSON
|
||||
fields and duplicate scalar query/header values are rejected. Arrays in the query
|
||||
use repeated keys. Enum/range/default and nested fields share the documentation
|
||||
and binding compiler. Responses are expected to honor their declared constraints;
|
||||
they are not validated on every response. Do not return nil slices/maps for a
|
||||
non-nullable collection (initialize empty collections instead).
|
||||
|
||||
Go-owned errors can be registered once through svc.ServiceContext.AppOptions with
|
||||
omni.WithErrors(projecterrors.Catalog()). Generated routes inherit them unless they
|
||||
declare an independent catalog. Do not repeat Go-owned errors in the API contract.
|
||||
Alternatively, declare public business errors in error (...) and reference them with @errors.
|
||||
Use internal/apierrors constructors to share status, code and message with OpenAPI.
|
||||
Auth declarations connect a bearer/header/cookie scheme to a real named middleware.
|
||||
The selected auth runs first; @auth none removes inherited declarative auth.
|
||||
Validation policy remains in the middleware implementation. Public messages must
|
||||
not contain secrets; error causes may be included in server logs.
|
||||
|
||||
svc.New receives the loaded Config and borrowed Logger. Add dependencies there,
|
||||
then immediately register each cleanup with OnClose. A later initialization failure
|
||||
runs registered cleanups in reverse order. Close is idempotent. GlobalMiddleware
|
||||
provides global hooks; Middlewares supplies named API hooks. AppOptions is evaluated
|
||||
once before route registration; logging is configured earlier through Config.
|
||||
The generated main runs HTTP draining before closing svc resources, then the logger.
|
||||
Do not close borrowed resources or attach duplicate cleanup hooks to App. The startup
|
||||
context cancels on signals; long-lived clients/workers that requests depend on need
|
||||
their own lifetime, canceled by OnClose after HTTP draining.
|
||||
|
||||
Named middleware is composed in declaration order (first is outermost). Implement
|
||||
the generated hooks or replace entries in svc; never bypass an auth
|
||||
hook merely to make the server start. Middleware names do not imply a JWT policy.
|
||||
|
||||
This CLI supports a documented go-zero-style subset, not all goctl features.
|
||||
Database tables and repositories cannot be inferred from an HTTP contract. Add
|
||||
business repositories through svc. Database and storage options are
|
||||
composed at creation; the original profile is retained on regeneration.
|
||||
|
||||
## Infrastructure
|
||||
|
||||
Selected database: none; storage: none; logging: stderr/json.
|
||||
MySQL uses database/sql through deps.DB, including InTx and a readiness probe.
|
||||
Set GO_OMNI_DB_DSN; pool settings live in etc/config.yaml and GO_OMNI_DB_*.
|
||||
No tables are invented or migrated automatically from request/response DTOs.
|
||||
The none/memory selection opens no database; implement repositories per business.
|
||||
|
||||
With storage enabled, services receive deps.Files, Uploads, Downloads and
|
||||
UploadLinks capability interfaces. Local signed GET/HEAD/PUT uses /storage/object;
|
||||
set GO_OMNI_STORAGE_SIGNING_KEY (32+ bytes) and GO_OMNI_STORAGE_PUBLIC_URL.
|
||||
Cloud credentials use GO_OMNI_STORAGE_ACCESS_KEY/SECRET_KEY; set bucket and endpoint
|
||||
or R2 accountId as appropriate. Signed URLs grant temporary access: authenticate
|
||||
and authorize before issuing them. No public upload-link route is added.
|
||||
Storage failures during startup close an already-open database; Close is idempotent.
|
||||
|
||||
Database/storage/logging selection is recorded in .goOmni/apiManifest.json.
|
||||
Changing selection through generation flags is rejected on an existing project;
|
||||
edit your owned configuration and initialization explicitly, or generate a fresh
|
||||
project for comparison. New routes still regenerate without repeating these flags.
|
||||
Reference in New Issue
Block a user