Liking cljdoc? Tell your friends :D

wagoe.platform.core.rpc

Pure RPC envelope handling for the remote-port adapter.

A cross-module call goes through a protocol (ports.clj). Slicing a module into its own process means implementing that same protocol with something that makes a network call instead — the seam is already there, only the adapter is missing (BOU-90, scaling.adoc → Functional decomposition).

This namespace is the wire contract and nothing else: building an envelope, reading one, and turning a remote failure into the error shape callers already handle. No I/O — see wagoe.platform.shell.rpc.client and …rpc.server for that.

FC/IS: pure. Everything here is data in, data out.

Pure RPC envelope handling for the remote-port adapter.

A cross-module call goes through a protocol (`ports.clj`). Slicing a module
into its own process means implementing that same protocol with something
that makes a network call instead — the seam is already there, only the
adapter is missing (BOU-90, scaling.adoc → Functional decomposition).

This namespace is the wire contract and nothing else: building an envelope,
reading one, and turning a remote failure into the error shape callers
already handle. No I/O — see `wagoe.platform.shell.rpc.client` and
`…rpc.server` for that.

FC/IS: pure. Everything here is data in, data out.
raw docstring

classify-exceptionclj

(classify-exception e)

Map a client exception to an :rpc/* type.

A timeout and a refused connection are both 'no answer' to a naive reader, and they need different operator responses — one is a slow service, the other a missing one. The distinction that matters most is whether the call could have executed, because that is what decides if a retry is safe.

Deliberately conservative: anything not recognised as definitely-never-sent is :rpc/remote-error, which the default policy does not retry.

Map a client exception to an `:rpc/*` type.

A timeout and a refused connection are both 'no answer' to a naive reader,
and they need different operator responses — one is a slow service, the
other a missing one. The distinction that matters most is whether the call
could have executed, because that is what decides if a retry is safe.

Deliberately conservative: anything not recognised as definitely-never-sent
is `:rpc/remote-error`, which the default policy does not retry.
sourceraw docstring

context->headersclj

(context->headers envelope)

HTTP headers carrying envelope's context.

The auth token is sent as a bearer credential unless it already names a scheme, so a caller that holds a raw token and one that holds a full Authorization value both work.

HTTP headers carrying `envelope`'s context.

The auth token is sent as a bearer credential unless it already names a
scheme, so a caller that holds a raw token and one that holds a full
Authorization value both work.
sourceraw docstring

context-headersclj

Envelope key → HTTP header.

x-correlation-id is the name the logging schema and the interceptor pipeline already use, so a request keeps one id across the hop rather than starting a new trace on the far side.

Envelope key → HTTP header.

`x-correlation-id` is the name the logging schema and the interceptor
pipeline already use, so a request keeps one id across the hop rather than
starting a new trace on the far side.
sourceraw docstring

envelope-problemclj

(envelope-problem {:keys [operation args] :as envelope})

Why envelope cannot be invoked, or nil if it can.

The server is network-facing, so it is reachable by anything that can post to it — including something sending a body that is not an envelope at all. Naming the problem lets the handler answer with an error envelope; without this, reading :operation off a malformed body throws, and the caller gets a 500 carrying nothing that says what was wrong with their request.

Why `envelope` cannot be invoked, or nil if it can.

The server is network-facing, so it is reachable by anything that can post
to it — including something sending a body that is not an envelope at all.
Naming the problem lets the handler answer with an error envelope; without
this, reading `:operation` off a malformed body throws, and the caller gets
a 500 carrying nothing that says what was wrong with their request.
sourceraw docstring

envelope-schemaclj

Malli schema for a request envelope.

:operation is the protocol method name as a keyword, so the wire format does not depend on the caller's namespace aliases. :args is a vector because a protocol method is positional — naming the arguments would make the envelope depend on the parameter names in a docstring.

Malli schema for a request envelope.

`:operation` is the protocol method name as a keyword, so the wire format
does not depend on the caller's namespace aliases. `:args` is a vector
because a protocol method is positional — naming the arguments would make
the envelope depend on the parameter names in a docstring.
sourceraw docstring

headers->contextclj

(headers->context headers)

The context an inbound envelope should carry, read from headers.

Header lookup is case-insensitive: Ring lower-cases incoming header names, but a caller constructing a request by hand may not, and losing the correlation-id to a capital letter would be silent.

The context an inbound envelope should carry, read from `headers`.

Header lookup is case-insensitive: Ring lower-cases incoming header names,
but a caller constructing a request by hand may not, and losing the
correlation-id to a capital letter would be silent.
sourceraw docstring

min-service-key-lengthclj

Shortest key rpc-handler will accept, matching the JWT secret rule.

Shortest key `rpc-handler` will accept, matching the JWT secret rule.
sourceraw docstring

plain-dataclj

(plain-data data)

data with every entry a wire format cannot carry removed.

Dropped rather than stringified: a caller reading :status 402 and a caller reading :status "#object[...]" both branch on it, and the second looks like data while being noise.

`data` with every entry a wire format cannot carry removed.

Dropped rather than stringified: a caller reading `:status 402` and a caller
reading `:status "#object[...]"` both branch on it, and the second looks
like data while being noise.
sourceraw docstring

plain-value?clj

(plain-value? v)

Whether v is data a wire format can carry.

ex-data is written for a local reader and may hold anything — an HTTP response object, a connection, another exception. Sending it unfiltered would fail at encode time and turn a typed domain error into a transport error, which is the opposite of preserving it.

Whether `v` is data a wire format can carry.

`ex-data` is written for a local reader and may hold anything — an HTTP
response object, a connection, another exception. Sending it unfiltered
would fail at encode time and turn a typed domain error into a transport
error, which is the opposite of preserving it.
sourceraw docstring

request-envelopeclj

(request-envelope operation args context)

Build a request envelope for operation with positional args.

context carries the values that must survive the hop — correlation-id, tenant, auth. They are already threaded through the interceptor pipeline in-process; across a network they ride headers, and this is what puts them there. Absent keys are omitted rather than sent as nil, so the receiving side can tell 'not propagated' from 'explicitly none'.

Returns a map conforming to envelope-schema.

Build a request envelope for `operation` with positional `args`.

`context` carries the values that must survive the hop — correlation-id,
tenant, auth. They are already threaded through the interceptor pipeline
in-process; across a network they ride headers, and this is what puts them
there. Absent keys are omitted rather than sent as nil, so the receiving
side can tell 'not propagated' from 'explicitly none'.

Returns a map conforming to `envelope-schema`.
sourceraw docstring

response->resultclj

(response->result operation body)

Unwrap a response envelope into what the protocol method should return.

A well-formed {:result …} is returned as-is. Anything else is a transport failure: a body that is not an envelope means the far side is not the service we think it is, which is worth saying rather than letting a nil propagate into the caller's logic.

Unwrap a response envelope into what the protocol method should return.

A well-formed `{:result …}` is returned as-is. Anything else is a transport
failure: a body that is not an envelope means the far side is not the
service we think it is, which is worth saying rather than letting a nil
propagate into the caller's logic.
sourceraw docstring

response-schemaclj

Malli schema for a response envelope.

A remote call has two failure modes and they are not the same thing: the operation ran and returned an error (:result carrying whatever the protocol returns), or the call never completed (:error). Collapsing them would make a network partition indistinguishable from a declined payment.

Malli schema for a response envelope.

A remote call has two failure modes and they are not the same thing: the
operation ran and returned an error (`:result` carrying whatever the
protocol returns), or the call never completed (`:error`). Collapsing them
would make a network partition indistinguishable from a declined payment.
sourceraw docstring

revive-errorclj

(revive-error error)

Restore the keywords JSON flattened to strings.

Callers branch on :type — and so does the client's own retry policy. JSON has no keywords, so a type built as :rpc/timeout arrives as "rpc/timeout", and every set-membership or case test against it misses without saying so: the branch simply never fires. This is the boundary where that has to be undone, because it is the last place that knows the value came off a wire.

Restore the keywords JSON flattened to strings.

Callers branch on `:type` — and so does the client's own retry policy. JSON
has no keywords, so a type built as `:rpc/timeout` arrives as
`"rpc/timeout"`, and every set-membership or `case` test against it misses
without saying so: the branch simply never fires. This is the boundary where
that has to be undone, because it is the last place that knows the value
came off a wire.
sourceraw docstring

service-key-headerclj

Header carrying the service's credential.

Distinct from authorization, which carries the end user's token through the hop. They authenticate different things: one says which service is calling, the other says on whose behalf. Sharing a header would mean a caller with a valid user token could invoke any port method directly.

Header carrying the *service's* credential.

Distinct from `authorization`, which carries the end user's token through
the hop. They authenticate different things: one says which service is
calling, the other says on whose behalf. Sharing a header would mean a
caller with a valid user token could invoke any port method directly.
sourceraw docstring

service-key-matches?clj

(service-key-matches? expected presented)

Whether presented is expected, compared in constant time.

= on strings returns as soon as two characters differ, so the time it takes leaks how much of a guess was right — enough, over many attempts, to recover the key a character at a time.

Whether `presented` is `expected`, compared in constant time.

`=` on strings returns as soon as two characters differ, so the time it
takes leaks how much of a guess was right — enough, over many attempts, to
recover the key a character at a time.
sourceraw docstring

service-key-problemclj

(service-key-problem service-key)

Why service-key is unusable, or nil if it is fine.

Why `service-key` is unusable, or nil if it is fine.
sourceraw docstring

service-urlclj

(service-url base-url path)

Join a service root and an endpoint path into one URL.

(str base-url path) is wrong for values people actually configure. A root copied from an env var or a config file often ends in a slash, and http://payments:3001/ + /rpc is //rpc, which the server does not serve — the call 404s against a healthy service, and the URL looks right in every log that prints it.

Normalised here rather than matched loosely on the server: an endpoint that answers /rpc, //rpc and /rpc/ alike is one an operator cannot reason about from an access log, and path confusion is not a property to want on something that invokes port methods.

Join a service root and an endpoint path into one URL.

`(str base-url path)` is wrong for values people actually configure. A root
copied from an env var or a config file often ends in a slash, and
`http://payments:3001/` + `/rpc` is `//rpc`, which the server does not
serve — the call 404s against a healthy service, and the URL looks right in
every log that prints it.

Normalised here rather than matched loosely on the server: an endpoint that
answers `/rpc`, `//rpc` and `/rpc/` alike is one an operator cannot reason
about from an access log, and path confusion is not a property to want on
something that invokes port methods.
sourceraw docstring

thrown-errorclj

(thrown-error operation e)

The error envelope for an exception the remote implementation threw.

In-process, a protocol method that throws propagates: the platform's HTTP boundary reads :type out of ex-data and maps it to a status, and logs a warning when there is none. If that became a returned value across the hop, a caller's try/catch would stop firing and the error map would flow on into business logic as though it were a result — the failure mode this adapter exists to avoid.

So the throw is carried, not flattened: :rpc/thrown marks it, and the client raises it again on the near side. The original :type is kept, so the same HTTP status comes out the far end.

The error envelope for an exception the remote implementation threw.

In-process, a protocol method that throws propagates: the platform's HTTP
boundary reads `:type` out of `ex-data` and maps it to a status, and logs a
warning when there is none. If that became a returned value across the hop,
a caller's `try`/`catch` would stop firing and the error map would flow on
into business logic as though it were a result — the failure mode this
adapter exists to avoid.

So the throw is carried, not flattened: `:rpc/thrown` marks it, and the
client raises it again on the near side. The original `:type` is kept, so
the same HTTP status comes out the far end.
sourceraw docstring

transport-errorclj

(transport-error type operation message & [status])

The error map for a call that did not complete.

Shaped like the {:error {:type … :message …}} the codebase already returns from adapters, so a caller handling a local failure needs no new branch for a remote one. :type distinguishes the cases that need different responses:

:rpc/unavailable — could not reach the service (connect refused, DNS) :rpc/timeout — reached it, no answer in time :rpc/remote-error — it answered with a non-2xx :rpc/protocol — it answered with something that is not an envelope

operation may be nil: a request that never named one still needs an answer, and it is the one case where there is no operation to report.

The error map for a call that did not complete.

Shaped like the `{:error {:type … :message …}}` the codebase already returns
from adapters, so a caller handling a local failure needs no new branch for
a remote one. `:type` distinguishes the cases that need different responses:

  :rpc/unavailable  — could not reach the service (connect refused, DNS)
  :rpc/timeout      — reached it, no answer in time
  :rpc/remote-error — it answered with a non-2xx
  :rpc/protocol     — it answered with something that is not an envelope

`operation` may be nil: a request that never named one still needs an
answer, and it is the one case where there is no operation to report.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close