Add reply draft creation and deletion operations - #57
Conversation
Imported from basecamp#31 as an isolated prerequisite so this commit can be skipped when rebasing after that pull request lands.
Add Smithy form traits and generated transport for saving and deleting reply drafts. Keep suffixless draft routes distinct from JSON send routes and make the explicit route-coverage scope fail closed.
Expose SDK-backed create and delete draft workflows, preserve redirect Locations, route immediate replies through generated JSON transport, and cover the browser form contracts in unit and conformance tests.
Spec Change ImpactModel output did not match expected format. Raw output truncated below. |
There was a problem hiding this comment.
Pull request overview
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
This PR models HEY’s browser-compatible reply-draft create/delete flows in Smithy/OpenAPI and regenerates the Go SDK so drafts can be created/discarded via form-encoded transport while preserving redirect Location headers and keeping suffixless draft routes distinct from .json send routes.
Changes:
- Add Smithy/OpenAPI support for
CreateReplyDraft(form-encoded) andDeleteDraft(POST + Rails_method=deleteoverride) and regenerate Go client types. - Update Go routing to keep
.jsonand suffixless routes distinct while providing.jsonalias matching and improved literal-vs-parameter resolution. - Introduce scoped, fail-closed route coverage generation/verification and add unit + conformance tests for draft create/delete semantics (form encoding, CSRF handling, redirect preservation).
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| spec/route-coverage.json | Adds coverage entries for reply-draft creation and draft deletion routes. |
| spec/route-coverage-scope.json | Introduces an explicit allowlist + overrides for route coverage generation. |
| spec/hey.smithy | Models CreateReplyDraft and DeleteDraft operations + supporting shapes/traits usage. |
| spec/hey-traits.smithy | Adds form-url-encoded + form-method-override Smithy traits for downstream OpenAPI tooling. |
| spec/excluded-routes.json | Removes the /messages/{id} DELETE exclusion now that it’s modeled for draft deletion. |
| scripts/generate-url-routes | Preserves .json in generated patterns so .json vs suffixless routes remain distinguishable. |
| scripts/generate-route-coverage | Generates route coverage from OpenAPI with scoped allowlist, overrides, and strict validation. |
| scripts/enhance-openapi-go-types.sh | Converts scoped form operations to application/x-www-form-urlencoded and propagates sensitivity markers. |
| scripts/check-service-drift.sh | Improves normalization so drift checks handle WithFormdataBody* generated variants. |
| openapi.json | Adds the two operations, form requestBodies, and related schemas/metadata extensions. |
| Makefile | Adds route-coverage and route-coverage-check; wires freshness check into check-mvp. |
| go/templates/imports.tmpl | Adds sort import needed for new form-normalization helper. |
| go/templates/client.tmpl | Adds redirect-capturing doer for form requests + Rails array form-key normalization during form marshaling. |
| go/pkg/hey/url.go | Updates router matching to consider .json vs suffixless routes and prefer literal routes over param routes, with alias support. |
| go/pkg/hey/url-routes.json | Regenerates the embedded route table preserving .json patterns and adding new operations. |
| go/pkg/hey/url_test.go | Adds routing tests ensuring .json send routes remain distinct and aliases resolve correctly. |
| go/pkg/hey/services_test.go | Updates addressed-field expectations and adds unit tests for create/delete draft services + Location parsing. |
| go/pkg/hey/messages.go | Fixes addressed fields to be comma-separated strings in JSON payloads. |
| go/pkg/hey/entries.go | Routes CreateReply through generated transport; adds CreateReplyDraft and DeleteDraft service methods. |
| go/pkg/hey/client.go | Wraps generated client HTTP transport to avoid following redirects for form requests (preserving Location). |
| go/pkg/generated/client.gen.go | Regenerates Go client with new operations, form bodies, metadata, and helper logic. |
| conformance/tests/reply-drafts.json | Adds conformance coverage for reply draft creation (form encoding + headers + Location). |
| conformance/tests/draft-deletion.json | Adds conformance coverage for draft deletion (method override + CSRF header + redirect preservation). |
| conformance/runner/go/main.go | Records request bodies and adds assertions for request headers/forms and response headers; wires new operations into executor. |
| behavior-model.json | Adds behavior metadata entries for CreateReplyDraft and DeleteDraft. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
All reported issues were addressed across 25 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 8 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Summary
CreateReplyDraftandDeleteDraftoperations in Smithy and regenerate the Go SDKLocationresponses, keep suffixless draft routes distinct from.jsonsend routes, and route immediate replies through generated transportRelationship to #31
This branch includes the exact patch from #31 as its first isolated commit because reply-draft handling depends on the corrected comma-separated
addressedfields. That commit preserves authorship by @cpinto and is included only as a prerequisite; #31 should still be reviewed and merged independently. If #31 lands first, I will rebase this branch and drop the duplicate commit.Test plan
make checkSummary by cubic
Add browser-compatible reply draft save/delete with form encoding, capture Location without following redirects on form posts (default
http.Clientonly), keep draft form routes distinct from.jsonsend routes, validate and normalize draft Location URLs, and handle draft edit locations.New Features
CreateReplyDraftandDeleteDraftin Smithy as form-encoded (Rails_methodfor delete) and regenerated the Go client.EntriesService.CreateReplyDraftandEntriesService.DeleteDraft; form requests do not auto-follow redirects with the defaulthttp.Client, so callers can readLocation(custom doers and non-form requests keep their behavior).EntriesService.CreateReplyDraftreturnsReplyDraftwithID,Location, andEditURL; accepts/entries/drafts/{id}or/entries/drafts/{id}/edit, validates the draft Location route, and strips empty query strings.Location.Refactors
.jsonpaths; suffixless draft routes remain distinct from.jsonsend routes, and aliases (e.g.,/topics/sent) beat parameter matches.*http.Clientinstance; redirect suppression only applies when the doer is the defaulthttp.Client.Written for commit 66c3588. Summary will update on new commits.