feat(embed): add the Go router (net/http) - #15
Conversation
09e02ec to
b06e491
Compare
044e201 to
34ceccc
Compare
|
Force-pushed review fixes (Go router):
|
b06e491 to
b760aa8
Compare
34ceccc to
1cb65e7
Compare
|
Force-pushed: 404/409 handling for the Go router — added a typed |
b760aa8 to
e287e68
Compare
|
|
||
| // esc URL-encodes a single path segment so browser-supplied ids can't inject | ||
| // query params or traverse the path (`/`, `?`, `#` are neutralised). | ||
| var esc = url.PathEscape |
| } | ||
|
|
||
| func (c *client) listCatalog(ctx context.Context, kind string, allow []string) ([]CatalogType, error) { | ||
| data, err := c.do(ctx, "GET", "/v1/"+kind+"s", nil) |
There was a problem hiding this comment.
why add s to every kind? why not rely on them passing the correct kindS? Should we make an enum for this so they know what kinds are valid?
| } | ||
|
|
||
| func (c *client) listConnectors(ctx context.Context, org, kind string) ([]ConfiguredConnector, error) { | ||
| data, err := c.do(ctx, "GET", "/v1/"+esc(org)+"/"+kind+"s?limit=1000&offset=0", nil) |
There was a problem hiding this comment.
List endpoints should support pagination with an ease of use Next() method or something so the caller can actually list all of the underlying resources. i believe list pipelines today returns a max of 10 by default since you're not passing any limit params. you can wrap all models in {rows, Pagination{total,limit,offset}}.
You have a couple methods around getting a pipelines status by a component but getting the component itself should already be returning that info in the component_of key.
| } | ||
|
|
||
| func (c *client) listPipelines(ctx context.Context, org string) ([]pipeSummary, error) { | ||
| data, err := c.do(ctx, "GET", "/v2/"+esc(org)+"/pipelines/", nil) |
There was a problem hiding this comment.
This is only ever going to return 10 pipelines without proper pagination. All of our pagination is handled with a simple struct. you could implement a pagination response with a next funct or something to make pagination through returned results simple.
| } | ||
|
|
||
| // statusByInput resolves the pipeline an input feeds, knowing only the input id. | ||
| func (c *client) statusByInput(ctx context.Context, org, inputID string) (PipelineStatus, error) { |
There was a problem hiding this comment.
GET /v1/{org}/outputs/{id}
GET /v1/{org}/inputs/{id}
already returns a component_of value so you shouldn't need to do this at all to get the status of an input.
| "disabled": dis, "conditions": e.Conditions, | ||
| }) | ||
| } | ||
| _, err = c.do(ctx, "PATCH", "/v2/"+esc(org)+"/pipelines/"+esc(pipelineID), map[string]any{ |
There was a problem hiding this comment.
This endpoint should work like a true patch now so you can safely pass just enabled bool and the other field states should be preserved.
| // buildDevNull creates a throwaway dev/null output, then wires the input to it. | ||
| func (c *client) buildDevNull(ctx context.Context, org, inputID, name string) (BuiltPipeline, error) { | ||
| data, err := c.do(ctx, "POST", "/v2/"+esc(org)+"/outputs", map[string]any{ | ||
| "output_type": "dev-null", |
| var page map[string][]struct { | ||
| ID string `json:"id"` | ||
| Type string `json:"type"` | ||
| Name string `json:"name"` | ||
| } | ||
| if err := json.Unmarshal(data, &page); err != nil { | ||
| return nil, err | ||
| } |
There was a problem hiding this comment.
I have a hard time believing this unmarhsall will actually work
| var arr []pipeSummary | ||
| if json.Unmarshal(data, &arr) == nil && len(arr) > 0 { | ||
| return arr, nil | ||
| } | ||
| var obj struct { | ||
| Pipelines []pipeSummary `json:"pipelines"` | ||
| Data []pipeSummary `json:"data"` | ||
| } | ||
| if err := json.Unmarshal(data, &obj); err != nil { | ||
| return nil, err | ||
| } | ||
| if len(obj.Pipelines) > 0 { | ||
| return obj.Pipelines, nil | ||
| } | ||
| return obj.Data, nil |
There was a problem hiding this comment.
This response model doesn't look correct to me either and what is the point of data vs pipelines key they are essentially the same thing. maybe claude was guessing what the key was or something? hah
| | `APIKey` | Long-lived Monad API key (server-side only). | | ||
| | `APIBase` | Monad API base. Optional — defaults to `https://app.monad.com/api` (production). | | ||
| | `FrameOrigin` | Iframe origin returned by `GET /embed/config`. Optional — defaults to production. | | ||
| | `GetCustomerOrgID` | Map the authenticated request → the tenant's Monad team id. Return `("", nil)` or an error to reject (→ 401). | |
There was a problem hiding this comment.
Okay i see what i was missing - this is actually a security boundary. The guide may want to point to that fact. This should convert their bearer token -> monad organization id.
1cb65e7 to
5f893f2
Compare
e287e68 to
a0dd428
Compare
|
Force-pushed: fixed a real |
5f893f2 to
55e229f
Compare
25d2e86 to
3315bdb
Compare
55e229f to
2f59032
Compare
Adds the Go port of the /embed router — embed.Router(embed.Config{...}) on the
standard library net/http, zero third-party dependencies (module
github.com/monad-inc/embed/routers/go). Same contract, idiomatic Go.
Generalizes the conformance CI job to a ts + go matrix; the Go leg boots
`go run ./cmd/conformance` and runs ROUTER=go against the same suite, proving
contract-parity with the TypeScript router.
Local: go vet + go test pass; conformance green for ROUTER=go and ROUTER=ts.
Layer 4 (PRO-403), stacked on the TS router + conformance (PRO-402).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_011ASKA2VrufrNLGGrcFjY7n
Parameterizes the Go conformance server (cmd/conformance) from env with the same knobs as the TS/Python servers, defaulting to the mock fixtures. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_011ASKA2VrufrNLGGrcFjY7n
3315bdb to
0e66ed7
Compare
…[PRO-455] Review of the Go port (#15) surfaced defects the TypeScript reference has too. Validated against the API source rather than the published spec, which is stale. - resolve a connector's pipeline via `GET /v1/{org}/{kind}s/{id}`'s `component_of` instead of scanning every pipeline. The scan sent no `limit`, so past the tenant's tenth pipeline it reported "not connected" for pipelines that exist. - `setEnabled` now PATCHes `{enabled}` alone. PATCH became a true partial update in api#2163; the read-modify-write it replaces silently dropped node `config_overrides` and edge `schema_detection_spec` on every toggle. - drain the connector list instead of one `limit=1000` request - send `type` on output create; `output_type` is deprecated (api#2156) - drop the speculative `data` / `config` response-shape fallbacks — Monad returns neither - map `kind` to its path segment explicitly rather than appending "s"
Layer 4 of the Monad Embed SDK staged rollout (PRO-403). Stacked on #14 (PRO-402) — targets
feature/pro-402; review/merge the stack bottom-up.What this adds
routers/go— the Go port of the/embedrouter:embed.Router(embed.Config{…})on the standard librarynet/http, zero third-party dependencies (modulegithub.com/monad-inc/embed/routers/go). Idiomatic Go; same contract.ts+gomatrix. The Go leg bootsgo run ./cmd/conformanceand runs the same suite withROUTER=go.Verification (local)
go vet+go testpassROUTER=go14/14,ROUTER=ts14/14 (regression)Reviewers judge idiomatic Go; conformance proves contract-parity with the TS router.
Stack
400 → 401 → 402 → 403 → 404 (Python) → 405 (docs/CI).
🤖 Generated with Claude Code
https://claude.ai/code/session_011ASKA2VrufrNLGGrcFjY7n