Skip to content

feat: Add NICo Core gRPC passthrough for admin operations#2360

Draft
kfelternv wants to merge 2 commits into
NVIDIA:mainfrom
kfelternv:nico-grpc-passthrough
Draft

feat: Add NICo Core gRPC passthrough for admin operations#2360
kfelternv wants to merge 2 commits into
NVIDIA:mainfrom
kfelternv:nico-grpc-passthrough

Conversation

@kfelternv

Copy link
Copy Markdown
Contributor

No description provided.

Signed-off-by: Kyle Felter <kfelter@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6182b2d1-6144-44b6-bb98-1c1234fde1da

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@kfelternv kfelternv marked this pull request as ready for review June 10, 2026 17:47
@kfelternv kfelternv requested a review from a team as a code owner June 10, 2026 17:47
@github-actions

Copy link
Copy Markdown

🔐 TruffleHog Secret Scan

No secrets or credentials found!

Your code has been scanned for 700+ types of secrets and credentials. All clear! 🎉

🔗 View scan details

🕐 Last updated: 2026-06-10 17:50:35 UTC | Commit: 4971eab

@ajf

ajf commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

I don't see a description (can you write one)? Also, I think it's probably not necessary to put 'nico' in the name of any of this and can just be generic names like backend, core, or whatever.

@ajf

ajf commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

... also I'm sure there's existing REST -> gRPC proxies that already exists? I haven't read the code in depth to grok it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (5)
rest-api/flow/internal/nicoapi/passthrough.go (1)

162-163: ⚡ Quick win

Prefer range-over-integer instead of a C-style counting loop.

This loop can use Go’s range-over-integer form for consistency with repo standards.

Suggested change
-	for i := 0; i < methods.Len(); i++ {
+	for i := range methods.Len() {
 		md := methods.Get(i)

As per coding guidelines, rest-api/**/*.go should prefer range-based iteration over C-style loops when range expresses the loop.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/flow/internal/nicoapi/passthrough.go` around lines 162 - 163,
Replace the C-style counting loop with a range-over-integer form: change the for
loop that uses methods.Len() (currently "for i := 0; i < methods.Len(); i++ { md
:= methods.Get(i) }") to a range-based loop such as "for i := range
make([]struct{}, methods.Len()) { md := methods.Get(i) }" so iteration uses Go's
range form while still obtaining md via methods.Get(i).

Source: Coding guidelines

rest-api/openapi/spec.yaml (2)

357-358: 💤 Low value

Clarify mutation classification with explicit read-only prefixes.

The description currently phrases the classification as a negative ("anything other than Find/Get/List/Search/Lookup/Identify/Determine/Version/Echo"), which requires the reader to invert the logic. Consider rephrasing to state the positive condition directly: "Methods are classified as mutations unless their name begins with one of the following prefixes: Find, Get, List, Search, Lookup, Identify, Determine, Version, or Echo."

This aligns better with the implementation logic in nicopassthrough.IsMutation and makes the rule easier to validate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/openapi/spec.yaml` around lines 357 - 358, Rephrase the OpenAPI
description to state the positive rule: replace the negative phrasing with a
sentence like "Methods are classified as mutations unless their name begins with
one of the following prefixes: Find, Get, List, Search, Lookup, Identify,
Determine, Version, or Echo; mutations additionally require `allowMutation:
true`." Update the text near the existing line in spec.yaml so it matches the
behavior in nicopassthrough.IsMutation and lists those prefixes explicitly for
clarity.

379-387: ⚡ Quick win

Document 400 and 401 status codes for completeness.

While the GET endpoint has less input validation than POST, it still goes through the same authorization and workflow execution paths. Document 400 (for malformed siteId or other request issues) and 401 (for missing/invalid auth token) to match typical REST API contracts and improve discoverability for client developers.

📝 Suggested addition
       responses:
         '200':
           description: OK
           content:
             application/json:
               schema:
                 $ref: '`#/components/schemas/NICoCoreMethodsResponse`'
+        '400':
+          $ref: '`#/components/responses/BadRequestError`'
+        '401':
+          $ref: '`#/components/responses/UnauthorizedError`'
         '403':
           $ref: '`#/components/responses/ForbiddenError`'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/openapi/spec.yaml` around lines 379 - 387, Add explicit '400' and
'401' responses to the same responses block that currently contains '200' ->
$ref: '`#/components/schemas/NICoCoreMethodsResponse`' and '403' ->
'`#/components/responses/ForbiddenError`'; specifically add a '400' entry
referencing a BadRequest response (e.g.
'`#/components/responses/BadRequestError`') for malformed siteId/params and a
'401' entry referencing an Unauthorized response (e.g.
'`#/components/responses/UnauthorizedError`') for missing/invalid auth token — if
those components don't exist, create them under components/responses with
appropriate description and JSON content schema.
rest-api/flow/internal/nicoapi/passthrough_test.go (2)

114-122: ⚡ Quick win

Strengthen method-catalog assertions so missing baseline methods fail the test.

The if v, ok := ...; ok pattern silently passes if FindMachineIds, Version, or CreateVpc are missing. These should be required entries for this contract test.

Suggested test hardening
-	if v, ok := byName["FindMachineIds"]; ok {
-		assert.False(t, v.Mutation, "FindMachineIds should be classified read-only")
-	}
-	if v, ok := byName["Version"]; ok {
-		assert.False(t, v.Mutation, "Version should be classified read-only")
-	}
-	if v, ok := byName["CreateVpc"]; ok {
-		assert.True(t, v.Mutation, "CreateVpc should be classified as a mutation")
-	}
+	require.Contains(t, byName, "FindMachineIds")
+	assert.False(t, byName["FindMachineIds"].Mutation, "FindMachineIds should be classified read-only")
+
+	require.Contains(t, byName, "Version")
+	assert.False(t, byName["Version"].Mutation, "Version should be classified read-only")
+
+	require.Contains(t, byName, "CreateVpc")
+	assert.True(t, byName["CreateVpc"].Mutation, "CreateVpc should be classified as a mutation")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/flow/internal/nicoapi/passthrough_test.go` around lines 114 - 122,
The test currently uses optional checks like `if v, ok :=
byName["FindMachineIds"]; ok { ... }` which silently passes if an entry is
missing; update the assertions to require the entries exist and then assert
their mutation flags. For each symbol `FindMachineIds`, `Version`, and
`CreateVpc` in the `byName` map, first assert existence (e.g., assert.True(ok,
"expected method <Name> to be present")) and only then assert `v.Mutation` is
false for `FindMachineIds` and `Version`, and true for `CreateVpc` so missing
baseline methods cause the test to fail.

41-47: ⚡ Quick win

Use range-over-integer for descriptor field iteration.

Line 41 currently uses a C-style counting loop where range over integer is supported in this repo’s Go target. Switching to for i := range fields.Len() keeps the loop idiomatic and reduces index bookkeeping.

As per coding guidelines, “Prefer range-based iteration over C-style for loops … use for i := range n for counting loops.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/flow/internal/nicoapi/passthrough_test.go` around lines 41 - 47,
Replace the C-style index loop in the test (the `for i := 0; i < fields.Len();
i++ {` block) with a range-based index loop so you iterate indices idiomatically
(use `for i := range ...`), keeping the body unchanged (calling `fields.Get(i)`,
checking `fd.Kind()`/`fd.Cardinality()`, and calling `pm.Set(fd,
protoreflect.ValueOfString(f.setValue))`). This preserves the existing logic
with `fields`, `fd`, `pm.Set`, and `f.setValue` while following the repository's
preferred range-over-integer style.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rest-api/flow/cmd/serve.go`:
- Around line 239-243: The Passthrough gRPC client created by
nicoapi.NewPassthrough in doServe is stored to temporalManagerConf.CoreInvoker
but never closed; update the success branch where passthrough is assigned to
call passthrough.Close() on shutdown (e.g., defer passthrough.Close() or
register a cleanup) so the underlying gRPC connection is closed when doServe
exits; specifically add the deferred close in the block that sets
temporalManagerConf.CoreInvoker (referencing NewPassthrough, passthrough,
temporalManagerConf.CoreInvoker, and doServe).

In `@rest-api/flow/internal/nicoapi/passthrough.go`:
- Around line 137-139: Change the JSON decoding in invokeJSON to be strict (stop
discarding unknown fields) so unknown/typoed fields cause an error instead of
silently producing zero values: replace the current
protojson.UnmarshalOptions{DiscardUnknown: true} usage in invokeJSON with
options that do not discard unknown fields (i.e., DiscardUnknown: false or omit
the option) so Unmarshal fails on unknown fields and return that error; also
update the iteration in listMethods to use Go’s idiomatic range-over-integer
form (use for i := range ... or for i := 0; i < methods.Len(); i++ -> for i :=
range make([]struct{}, methods.Len()) style replacement) to match the repo’s
loop style.

In `@rest-api/openapi/spec.yaml`:
- Around line 342-350: The OpenAPI spec for the NICoCore passthrough endpoint is
missing the documented 400 response even though nicocorepassthrough.go returns
400 for invalid request bodies and missing "method"; update the spec under the
endpoint responses to include a '400' response (e.g., reference an existing
BadRequestError response component or add a new
components/responses/BadRequestError and reference it) so clients see the
client-error condition and match the handler behavior.
- Around line 12844-12855: The OpenAPI schema NICoCorePassthroughResponse must
declare its always-populated fields as required; update the
NICoCorePassthroughResponse object to include a required array listing "method"
and "response" so the spec matches the handler that always sets those fields
(see the NICo Core passthrough handler that populates method and response).
- Around line 12856-12867: The OpenAPI schema NICoCoreMethodsResponse is missing
a required constraint for fields that are always set; update the
NICoCoreMethodsResponse schema to include a required array with "service" and
"methods" so both properties are marked mandatory (i.e., add required:
["service","methods"])—this matches the handler that always populates those
fields (see NICoCoreMethodsResponse schema and the nicocore passthrough handler
that constructs the response).
- Around line 12868-12884: Update the NICoCoreMethod schema to mark all six
properties as required so client SDKs reflect the actual contract: add a
required array containing "method", "fullMethod", "inputType", "outputType",
"mutation", and "deprecated" within the NICoCoreMethod object definition (the
schema named NICoCoreMethod) so those fields are validated as present.

---

Nitpick comments:
In `@rest-api/flow/internal/nicoapi/passthrough_test.go`:
- Around line 114-122: The test currently uses optional checks like `if v, ok :=
byName["FindMachineIds"]; ok { ... }` which silently passes if an entry is
missing; update the assertions to require the entries exist and then assert
their mutation flags. For each symbol `FindMachineIds`, `Version`, and
`CreateVpc` in the `byName` map, first assert existence (e.g., assert.True(ok,
"expected method <Name> to be present")) and only then assert `v.Mutation` is
false for `FindMachineIds` and `Version`, and true for `CreateVpc` so missing
baseline methods cause the test to fail.
- Around line 41-47: Replace the C-style index loop in the test (the `for i :=
0; i < fields.Len(); i++ {` block) with a range-based index loop so you iterate
indices idiomatically (use `for i := range ...`), keeping the body unchanged
(calling `fields.Get(i)`, checking `fd.Kind()`/`fd.Cardinality()`, and calling
`pm.Set(fd, protoreflect.ValueOfString(f.setValue))`). This preserves the
existing logic with `fields`, `fd`, `pm.Set`, and `f.setValue` while following
the repository's preferred range-over-integer style.

In `@rest-api/flow/internal/nicoapi/passthrough.go`:
- Around line 162-163: Replace the C-style counting loop with a
range-over-integer form: change the for loop that uses methods.Len() (currently
"for i := 0; i < methods.Len(); i++ { md := methods.Get(i) }") to a range-based
loop such as "for i := range make([]struct{}, methods.Len()) { md :=
methods.Get(i) }" so iteration uses Go's range form while still obtaining md via
methods.Get(i).

In `@rest-api/openapi/spec.yaml`:
- Around line 357-358: Rephrase the OpenAPI description to state the positive
rule: replace the negative phrasing with a sentence like "Methods are classified
as mutations unless their name begins with one of the following prefixes: Find,
Get, List, Search, Lookup, Identify, Determine, Version, or Echo; mutations
additionally require `allowMutation: true`." Update the text near the existing
line in spec.yaml so it matches the behavior in nicopassthrough.IsMutation and
lists those prefixes explicitly for clarity.
- Around line 379-387: Add explicit '400' and '401' responses to the same
responses block that currently contains '200' -> $ref:
'`#/components/schemas/NICoCoreMethodsResponse`' and '403' ->
'`#/components/responses/ForbiddenError`'; specifically add a '400' entry
referencing a BadRequest response (e.g.
'`#/components/responses/BadRequestError`') for malformed siteId/params and a
'401' entry referencing an Unauthorized response (e.g.
'`#/components/responses/UnauthorizedError`') for missing/invalid auth token — if
those components don't exist, create them under components/responses with
appropriate description and JSON content schema.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f8590879-8b29-4b9c-b981-5c0353298cd0

📥 Commits

Reviewing files that changed from the base of the PR and between 205ca89 and 4971eab.

📒 Files selected for processing (16)
  • rest-api/api/pkg/api/handler/nicocorepassthrough.go
  • rest-api/api/pkg/api/handler/nicocorepassthrough_test.go
  • rest-api/api/pkg/api/model/nicocorepassthrough.go
  • rest-api/api/pkg/api/routes.go
  • rest-api/common/pkg/nicopassthrough/nicopassthrough.go
  • rest-api/common/pkg/nicopassthrough/nicopassthrough_test.go
  • rest-api/flow/cmd/serve.go
  • rest-api/flow/internal/nicoapi/passthrough.go
  • rest-api/flow/internal/nicoapi/passthrough_test.go
  • rest-api/flow/internal/task/executor/temporalworkflow/activity/activity_test.go
  • rest-api/flow/internal/task/executor/temporalworkflow/activity/passthrough.go
  • rest-api/flow/internal/task/executor/temporalworkflow/activity/registry.go
  • rest-api/flow/internal/task/executor/temporalworkflow/activity/registry_test.go
  • rest-api/flow/internal/task/executor/temporalworkflow/manager/manager.go
  • rest-api/flow/internal/task/executor/temporalworkflow/workflow/passthrough.go
  • rest-api/openapi/spec.yaml

Comment on lines +239 to +243
if passthrough, err := nicoapi.NewPassthrough(time.Minute); err != nil {
log.Warn().Err(err).Msg("NICo Core gRPC passthrough disabled")
} else {
temporalManagerConf.CoreInvoker = passthrough
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Close the passthrough client on shutdown to avoid leaking the gRPC connection.

On successful initialization, the new Passthrough connection is stored but never explicitly closed in doServe. Add a deferred close in this success path.

Suggested change
 	if passthrough, err := nicoapi.NewPassthrough(time.Minute); err != nil {
 		log.Warn().Err(err).Msg("NICo Core gRPC passthrough disabled")
 	} else {
 		temporalManagerConf.CoreInvoker = passthrough
+		defer passthrough.Close()
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if passthrough, err := nicoapi.NewPassthrough(time.Minute); err != nil {
log.Warn().Err(err).Msg("NICo Core gRPC passthrough disabled")
} else {
temporalManagerConf.CoreInvoker = passthrough
}
if passthrough, err := nicoapi.NewPassthrough(time.Minute); err != nil {
log.Warn().Err(err).Msg("NICo Core gRPC passthrough disabled")
} else {
temporalManagerConf.CoreInvoker = passthrough
defer passthrough.Close()
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/flow/cmd/serve.go` around lines 239 - 243, The Passthrough gRPC
client created by nicoapi.NewPassthrough in doServe is stored to
temporalManagerConf.CoreInvoker but never closed; update the success branch
where passthrough is assigned to call passthrough.Close() on shutdown (e.g.,
defer passthrough.Close() or register a cleanup) so the underlying gRPC
connection is closed when doServe exits; specifically add the deferred close in
the block that sets temporalManagerConf.CoreInvoker (referencing NewPassthrough,
passthrough, temporalManagerConf.CoreInvoker, and doServe).

Comment on lines +137 to +139
if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(reqJSON, in); err != nil {
return nil, fmt.Errorf("decode request for %q: %w", md.Name(), err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="rest-api/flow/internal/nicoapi/passthrough.go"
echo "== File =="
ls -l "$FILE"

echo
echo "== Lines 120-190 =="
nl -ba "$FILE" | sed -n '110,210p'

echo
echo "== Search for DiscardUnknown / UnmarshalOptions usages =="
rg -n "DiscardUnknown|UnmarshalOptions|protojson\.Unmarshal" "$FILE"

echo
echo "== Search for C-style loops in this file =="
rg -n "for\s+[a-zA-Z_]+\s*:=\s*0;|for\s+[a-zA-Z_]+\s*:=.*;.*;.*\-\-|for\s+[a-zA-Z_]+\s*:=.*;.*<.*;|for\s+[a-zA-Z_]+\s*:=.*;.*<=.*;|for\s*.*;.*;.*" "$FILE" || true

Repository: NVIDIA/infra-controller

Length of output: 256


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="rest-api/flow/internal/nicoapi/passthrough.go"

echo "== Lines 110-210 =="
awk 'NR>=110 && NR<=210 {printf "%d\t%s\n", NR, $0}' "$FILE"

echo
echo "== Search for DiscardUnknown / protojson.UnmarshalOptions usages =="
rg -n "DiscardUnknown|protojson\.UnmarshalOptions|protojson\.Unmarshal" "$FILE" || true

echo
echo "== Search for C-style for loops in this file =="
rg -n "for\s+[a-zA-Z_]+\s*:=\s*0;|for\s+[a-zA-Z_]+\s*:=.*;.*;.*\-\-|for\s+[a-zA-Z_]+\s*:=.*;.*<.*;|for\s+[a-zA-Z_]+\s*:=.*;.*<=.*;|for\s*.*;.*;.*" "$FILE" || true

Repository: NVIDIA/infra-controller

Length of output: 2965


Harden passthrough JSON decoding and align loop style

  • invokeJSON decodes with protojson.UnmarshalOptions{DiscardUnknown: true}, which silently drops unknown/typoed fields and can yield unintended zero values instead of failing fast.
  • listMethods uses for i := 0; i < methods.Len(); i++ { ... }; switch to Go’s range-over-integer style.
Suggested change (strict decode)
-		if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(reqJSON, in); err != nil {
+		if err := protojson.Unmarshal(reqJSON, in); err != nil {
 			return nil, fmt.Errorf("decode request for %q: %w", md.Name(), err)
 		}
Suggested change (loop style)
-	for i := 0; i < methods.Len(); i++ {
+	for i := range methods.Len() {
 		md := methods.Get(i)
 		// The passthrough only supports unary RPCs.
 		if md.IsStreamingClient() || md.IsStreamingServer() {
 			continue
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/flow/internal/nicoapi/passthrough.go` around lines 137 - 139, Change
the JSON decoding in invokeJSON to be strict (stop discarding unknown fields) so
unknown/typoed fields cause an error instead of silently producing zero values:
replace the current protojson.UnmarshalOptions{DiscardUnknown: true} usage in
invokeJSON with options that do not discard unknown fields (i.e.,
DiscardUnknown: false or omit the option) so Unmarshal fails on unknown fields
and return that error; also update the iteration in listMethods to use Go’s
idiomatic range-over-integer form (use for i := range ... or for i := 0; i <
methods.Len(); i++ -> for i := range make([]struct{}, methods.Len()) style
replacement) to match the repo’s loop style.

Comment on lines +342 to +350
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/NICoCorePassthroughResponse'
'403':
$ref: '#/components/responses/ForbiddenError'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Document 400 Bad Request status code.

The handler explicitly returns 400 for invalid request body and missing method field (lines 198-199, 201-203 in nicocorepassthrough.go), but the spec only documents 200 and 403 responses. API consumers need to know about client-error conditions.

📝 Suggested addition
       responses:
         '200':
           description: OK
           content:
             application/json:
               schema:
                 $ref: '`#/components/schemas/NICoCorePassthroughResponse`'
+        '400':
+          $ref: '`#/components/responses/BadRequestError`'
         '403':
           $ref: '`#/components/responses/ForbiddenError`'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/NICoCorePassthroughResponse'
'403':
$ref: '#/components/responses/ForbiddenError'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '`#/components/schemas/NICoCorePassthroughResponse`'
'400':
$ref: '`#/components/responses/BadRequestError`'
'403':
$ref: '`#/components/responses/ForbiddenError`'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/openapi/spec.yaml` around lines 342 - 350, The OpenAPI spec for the
NICoCore passthrough endpoint is missing the documented 400 response even though
nicocorepassthrough.go returns 400 for invalid request bodies and missing
"method"; update the spec under the endpoint responses to include a '400'
response (e.g., reference an existing BadRequestError response component or add
a new components/responses/BadRequestError and reference it) so clients see the
client-error condition and match the handler behavior.

Comment on lines +12844 to +12855
NICoCorePassthroughResponse:
type: object
title: NICoCorePassthroughResponse
description: Result of a NICo Core gRPC passthrough invocation.
properties:
method:
type: string
description: The bare method that was invoked.
response:
type: object
additionalProperties: true
description: protojson-encoded response message from NICo Core.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Mark response fields as required.

The handler always populates both method and response fields (see handler/nicocorepassthrough.go:225-228), but the schema declares neither as required. This weakens the API contract and may cause OpenAPI generators to produce optional fields in client SDKs when these are guaranteed to be present.

📝 Proposed fix
     NICoCorePassthroughResponse:
       type: object
       title: NICoCorePassthroughResponse
       description: Result of a NICo Core gRPC passthrough invocation.
+      required:
+        - method
+        - response
       properties:
         method:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/openapi/spec.yaml` around lines 12844 - 12855, The OpenAPI schema
NICoCorePassthroughResponse must declare its always-populated fields as
required; update the NICoCorePassthroughResponse object to include a required
array listing "method" and "response" so the spec matches the handler that
always sets those fields (see the NICo Core passthrough handler that populates
method and response).

Comment on lines +12856 to +12867
NICoCoreMethodsResponse:
type: object
title: NICoCoreMethodsResponse
description: Catalog of invocable NICo Core gRPC methods.
properties:
service:
type: string
description: Fully qualified gRPC service name.
methods:
type: array
items:
$ref: '#/components/schemas/NICoCoreMethod'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Mark catalog response fields as required.

The handler always populates both service and methods fields (see handler/nicocorepassthrough.go:276-279), but the schema omits the required constraint. This gap affects client code generation—SDKs may treat these as optional when they are structurally guaranteed.

📝 Proposed fix
     NICoCoreMethodsResponse:
       type: object
       title: NICoCoreMethodsResponse
       description: Catalog of invocable NICo Core gRPC methods.
+      required:
+        - service
+        - methods
       properties:
         service:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
NICoCoreMethodsResponse:
type: object
title: NICoCoreMethodsResponse
description: Catalog of invocable NICo Core gRPC methods.
properties:
service:
type: string
description: Fully qualified gRPC service name.
methods:
type: array
items:
$ref: '#/components/schemas/NICoCoreMethod'
NICoCoreMethodsResponse:
type: object
title: NICoCoreMethodsResponse
description: Catalog of invocable NICo Core gRPC methods.
required:
- service
- methods
properties:
service:
type: string
description: Fully qualified gRPC service name.
methods:
type: array
items:
$ref: '`#/components/schemas/NICoCoreMethod`'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/openapi/spec.yaml` around lines 12856 - 12867, The OpenAPI schema
NICoCoreMethodsResponse is missing a required constraint for fields that are
always set; update the NICoCoreMethodsResponse schema to include a required
array with "service" and "methods" so both properties are marked mandatory
(i.e., add required: ["service","methods"])—this matches the handler that always
populates those fields (see NICoCoreMethodsResponse schema and the nicocore
passthrough handler that constructs the response).

Comment on lines +12868 to +12884
NICoCoreMethod:
type: object
title: NICoCoreMethod
description: A single invocable NICo Core gRPC method.
properties:
method:
type: string
fullMethod:
type: string
inputType:
type: string
outputType:
type: string
mutation:
type: boolean
deprecated:
type: boolean

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Mark core method metadata fields as required.

Every method entry returned from protobuf reflection will have method, fullMethod, inputType, and outputType populated—these are structural metadata, not optional attributes. The mutation and deprecated flags are always computed as well. Declare all six fields as required to ensure client SDKs reflect the actual contract.

📝 Proposed fix
     NICoCoreMethod:
       type: object
       title: NICoCoreMethod
       description: A single invocable NICo Core gRPC method.
+      required:
+        - method
+        - fullMethod
+        - inputType
+        - outputType
+        - mutation
+        - deprecated
       properties:
         method:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rest-api/openapi/spec.yaml` around lines 12868 - 12884, Update the
NICoCoreMethod schema to mark all six properties as required so client SDKs
reflect the actual contract: add a required array containing "method",
"fullMethod", "inputType", "outputType", "mutation", and "deprecated" within the
NICoCoreMethod object definition (the schema named NICoCoreMethod) so those
fields are validated as present.

@kfelternv

Copy link
Copy Markdown
Contributor Author

@ajf Sorry! This one is still a draft, I switched it to ready for review to try to get coderabbit to review it earlier when I was having problems triggering it.

@kfelternv kfelternv marked this pull request as draft June 10, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants