feat(docs): render gRPC requests as read-only documentation pages - #36
Merged
Conversation
sundram-bruno
force-pushed
the
feat/grpc-docs-page
branch
from
August 7, 2026 13:37
8d7e8f9 to
048e4ce
Compare
sundram-bruno
force-pushed
the
feat/grpc-docs-page
branch
from
August 7, 2026 13:45
048e4ce to
ca75921
Compare
sundram-bruno
marked this pull request as draft
August 7, 2026 14:30
gRPC requests previously fell through to the "preview not available" state. They now get a docs page showing the proto file name, the RPC method and its call type, request messages, metadata and auth, with generated grpcURL and JavaScript snippets alongside. The JavaScript snippet is only offered when a proto file is attached, since @grpc/proto-loader builds the client from a file on disk and cannot read a service definition from server reflection. The playground still cannot execute gRPC, so the unsupported-request predicate is split into separate docs and playground checks. Ref: BRU-3719
…ppet The proto folder, proto filename, server address and method name were interpolated into the command unquoted, so a value carrying a space split into several shell words and one carrying shell syntax executed when the reader pasted the command. Headers and the message body were already quoted; these three were missed. Verified against grpcurl 1.9.3: the unquoted form of a spaced proto path fails with "Too many arguments", the quoted form completes the call.
The plaintext flag was read from the url as written. A collection that keeps its address in a variable hides the scheme there, so the flag fell back to assuming no encryption and every such request was sent -plaintext. Against a TLS server grpcurl then waits for the timeout and reports a context deadline, naming neither TLS nor the flag, so the failure reads as an unreachable server. The flag now comes from the variable's value, read through lookup rather than resolve so the show-variables toggle cannot change it. A secret value is never read, and the address itself is still emitted as written, so the token stays hoverable. The Prod environment pointed grpcUrl at port 443 over grpc://, which is the TLS port; it now reads grpcs://.
A heredoc ends at the first line equal to its delimiter, so a streaming message containing a bare EOF line closed it early and handed the rest of the message to the shell as commands. The delimiter is now chosen to avoid every line the messages contain. The service path and method name are written into JavaScript positions no string escape can protect — `new proto.<path>()` and `client.<name>()` — so a method that is not a plain identifier path produced code that both ran arbitrary statements and failed to parse. Both are validated, and anything else yields no snippet, matching the existing no-service behaviour. parseProtoFlags dropped the leading empty segment when splitting, so an absolute /protos/book.proto silently pointed grpcurl at a relative protos. Also folds three copies of the leading-slash strip into one exported helper, renames isClientStreaming to streamsInFor since it covered bidi too, and removes a branch that re-tested the condition it had just used.
A gRPC request carries variables, scripts, assertions and post-response captures in its runtime block, and the converter writes all four, but the docs page dropped the lot. An HTTP request in the same collection showed them, so a scripted gRPC request silently documented less than its neighbour. The collectors already read the generic runtime paths and were only typed to HttpRequest, so they are widened to Item rather than duplicated. Post- response captures live in runtime.actions for every protocol; the published GrpcRequestRuntime omits that field, so it is read structurally until the types catch up. The sample Order Service gains a runtime block so the section renders real content in the docs and the tests assert against it.
The message card re-declared the chevron's transition and rotation, which ChevronArrow already owns. Because the copy sat outside a media query it could override ChevronArrow's prefers-reduced-motion opt-out and animate for a reader who asked for no motion. The card's own open/close transition had no such guard at all, unlike the example card it was modelled on. The snippets memo listed metadata and messages, which the getters rebuild on every render, so both generators re-ran every time; the getters are memoized on the item instead. An omitted ancestry prop defaulted to a fresh array, invalidating four more memos for the same reason. Also documents why the message body mounts during render and why a collapsed body is marked inert, neither of which is apparent from the code, and adds the default export every sibling component has.
Adding an isHttpRequest guard to route gRPC away from the HTTP page turned the previously unconditional fall-through into a bare null, so an item with an unrecognised or missing type rendered a completely blank page — no breadcrumb, no title, nothing to distinguish it from a broken app. Such an item is reachable: navModel routes anything that is not a folder or a script to the request page. Restores the fall-through to RequestContent, which is what shipped before, and covers it so the next type guard cannot bring the blank page back.
sundram-bruno
force-pushed
the
feat/grpc-docs-page
branch
from
August 9, 2026 14:02
4833ed9 to
87cd910
Compare
…t ids The method badge inferred "render as written" from the casing of the value, so any collection storing a mixed-case HTTP method rendered it unchanged — Patch stayed Patch instead of PATCH. The escape hatch exists for gRPC alone, so it is now an explicit prop the gRPC url bar opts into. A message body was pasted straight into a code position in the JavaScript snippet. It is now parsed first: a body that is not JSON becomes a quoted string rather than executable text, while a templated body stays verbatim so its variables survive. Snippet child test ids derive from the testId prop instead of a hardcoded prefix, so the same component can be located under the request, example and gRPC bases; CodeSnippetComponent takes that base as a parameter. Also drops a spec helper that became identical to its neighbour once getRequestAuth was widened, re-homes a test that sat outside its describe, removes an unused e2e locator, and restates the method-colour comment as a timeless fact.
…Tabs hideHeader removed the thead outright, taking the th scope="col" cells with it, so a screen reader lost the column each metadata cell belonged to. The header now stays in the markup and is hidden visually, reusing the same clip technique the table caption already uses. SnippetTabs had no spec of its own and was only exercised through the HTTP wrapper, so the empty, embedded and test-id-derivation branches were unread.
…rry auth A methodType read straight from a collection indexed the icon map without a guard, so a request typed "toString" resolved Object.prototype.toString, passed the truthiness check and rendered undefined as a component — the whole request page threw. The lookup is now an own-property check, the same class of hole as the unrecognised-request-type fall-through fixed earlier on this branch. A message body containing a variable skipped the JSON check and was written into the JavaScript snippet verbatim, so a body could append statements that run when the reader pastes it. The body is now probed with its variables replaced by a JSON-safe value, which keeps a genuine templated body intact and quotes anything that is not a lone JSON document. The page showed an Auth section while both snippets dialled with no credentials. They now carry it through the converter the HTTP snippets already use, so the two protocols express the same auth the same way, including the note for auth that cannot become a header. Also groups the config sections under Configuration in the section nav as the HTTP page does, renames a shadowed local and parseProtoFlags, and composes the parameterised CodeSnippetComponent in the gRPC page object.
The label lookup indexed a plain object with a value read from the collection, the same unguarded shape as the icon map fixed alongside it, so a methodType named after an Object member resolved a function and rendered it as a child. The show-more control in the messages list was the view-more control with different words: same button, same chevron, same rotation and reduced-motion rules. Both now render one ExpandToggle, so the two cannot drift and the reduced-motion opt-out lives in a single place. The message card also mounts its body from the toggle handler rather than during render, which is how the example card it was modelled on does it. On the test side, the messages locators become a component that derives its child ids from a base, the gRPC page object annotates its locators, and the examples component composes the parameterised snippet component instead of naming its ids by hand.
sundram-bruno
marked this pull request as ready for review
August 9, 2026 20:03
vasharma05-bruno
left a comment
Contributor
There was a problem hiding this comment.
Some review comments over all PRs:
- Claude tests are mixed b/w
renderToStaticMarkupanduseRenderToDom. Please useuseRenderToDom. - The svg width and height is not matching the viewbox. Please use the width = height = viewbox's width and height.
- Please use path alias for all your changed files.
- Please use functions from
test-utils/dom.ts - Please use
cxfunction - Please revert the types changed from
HttpRequesttoItem. Item can be a folder/ collection/ request, etc. GrpcMessageCardcomponent changes as discussed.
Narrows the request helpers to HttpRequest | GrpcRequest rather than Item, so the signature carries what the caller actually holds and the casts each call site needed disappear. resolveInheritedAuth and getInheritedAuthSummary stay Item: the folder settings view passes a folder to both, so narrowing them would break it. The four streaming icons take their stroke styling from baseIconProps, and the expand toggle reuses ChevronDownIcon instead of inlining a copy of it, which also lets the icon size come from a prop rather than a css override. The specs this PR added render through useRenderToDom, imports in the files it created use the path alias, class names go through cx, and the two margin-only rules become utility classes. MethodBadge reads capitalizeMethod, defaulting to true, so the gRPC call site says what it wants rather than naming the outcome.
Contributor
Author
|
@vasharma05-bruno addressed the comments, can you please review. |
The expand toggle spec finds its button by test id rather than by a data attribute selector, and imports through the path alias. The field text declared font-weight 400, which is the value it would have inherited anyway.
vasharma05-bruno
suggested changes
Aug 11, 2026
vasharma05-bruno
left a comment
Contributor
There was a problem hiding this comment.
A few things that got left out.
Also, most of the tests are still using markup (html content search), rather than properly searching for elements in a DOM tree using useRenderToDom. The tests are now using useRenderToDom, but is extracting the innerHtml from it.
arpit-bruno
approved these changes
Aug 13, 2026
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.
Description
Ref: BRU-3719
Introduces a read-only documentation page for gRPC requests. Until now a gRPC request in a collection showed "preview not available".
The page shows the proto file name (not shown in case of reflection), the RPC method with its call type, the request messages, metadata and auth, execution context and generates a grpcURL command alongside. A JavaScript snippet is offered too, but only when the request has a proto file attached, since the Node client is built from a file on disk and cannot be built from server reflection.
The playground still cannot execute gRPC, so the unsupported-request check is split into a docs one and a playground one.