Skip to content

fix(admin): avoid encoded slash admin URLs#310

Merged
SantiagoDePolonia merged 2 commits intomainfrom
fix/no-url-encoding
May 8, 2026
Merged

fix(admin): avoid encoded slash admin URLs#310
SantiagoDePolonia merged 2 commits intomainfrom
fix/no-url-encoding

Conversation

@SantiagoDePolonia
Copy link
Copy Markdown
Contributor

@SantiagoDePolonia SantiagoDePolonia commented May 8, 2026

Summary

  • Move slash-capable admin identifiers out of URL path params and into JSON bodies for model overrides, pricing overrides, aliases, guardrails, and budgets.
  • Update dashboard mutations, e2e snippets, README, and generated OpenAPI docs for the body-based admin API shape.
  • Add cache-busting query params for all embedded dashboard CSS/JS assets, including JS modules.

Tests

  • Pre-commit hook suite: make test-race, dashboard JavaScript unit tests, hot-path performance guard, make lint, mint validate
  • go test ./internal/admin ./internal/server ./cmd/gomodel
  • go test -tags e2e ./tests/e2e -run TestBudgetAdminEndpointsSQLite_E2E
  • go test -tags integration ./tests/integration -run '^$'
  • node --test internal/admin/dashboard/static/js/modules/aliases.test.cjs internal/admin/dashboard/static/js/modules/budgets.test.cjs internal/admin/dashboard/static/js/modules/guardrails.test.cjs internal/admin/dashboard/static/js/modules/model-pricing-overrides.test.cjs
  • node --test internal/admin/dashboard/static/js/modules/dashboard-layout.test.cjs internal/admin/dashboard/static/js/modules/timezone-layout.test.cjs

Summary by CodeRabbit

  • Refactor

    • Admin write/delete endpoints (budgets, aliases, guardrails, model overrides, and model pricing overrides) now accept identifiers in JSON request bodies instead of URL path parameters; dashboard asset versioning now includes multiple JS/CSS assets.
  • Documentation

    • OpenAPI/README updated to reflect the new collection-style admin endpoint contracts and tightened request schemas.
  • Tests

    • End-to-end and unit tests updated to exercise the new request shapes and routes.

@mintlify
Copy link
Copy Markdown

mintlify Bot commented May 8, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
gomodel 🟢 Ready View Preview May 8, 2026, 2:41 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR consolidates five admin resource endpoints (budgets, model overrides, model pricing overrides, aliases, guardrails) from path-parameterized routes to collection-style base routes that accept identifiers in JSON request bodies. The changes span API contracts, backend handlers, frontend client code, and comprehensive test coverage across unit, integration, and E2E layers.

Changes

Admin API Endpoint Consolidation

Layer / File(s) Summary
API Contracts & Documentation
README.md, docs/openapi.json, cmd/gomodel/docs/docs.go
OpenAPI specification and Swagger docs updated to reflect collection-style endpoint paths and new request/response schemas with required fields for budget, override, and alias identifiers.
OpenAPI postprocess script
tools/openapi-postprocess.mjs
Adds budget-key oneOf constraint and enforces required properties for admin request schemas.
Request DTOs & Validation
internal/admin/handler_budgets.go, internal/admin/handler_aliases.go, internal/admin/handler_guardrails.go, internal/admin/handler_model_overrides.go, internal/admin/handler_model_pricing_overrides.go, cmd/gomodel/docs/docs.go
Binding structures updated with required fields and JSON tags; new deletion request types introduced; upsert request schemas tightened.
Error Handling & Normalization
internal/admin/errors.go
New helpers normalizeModelOverrideSelector and normalizeModelPricingOverrideSelector enforce trimming and validation and enforce selector length for pricing overrides.
Route Registration
internal/admin/routes.go, internal/admin/routes_test.go
Budget, alias, guardrail, and model override routes changed from parameterized to collection base paths.
Budget Handlers
internal/admin/handler_budgets.go
UpsertBudget and DeleteBudget extract identifiers from JSON body; new budgetRequestKey/budgetKeyPeriodSeconds helpers with whitespace trimming and validation; removed path-param helper.
Alias Handlers
internal/admin/handler_aliases.go
UpsertAlias and DeleteAlias bind alias name from JSON body with explicit not-found error mapping.
Guardrail Handlers
internal/admin/handler_guardrails.go
UpsertGuardrail and DeleteGuardrail read guardrail name from request body instead of URL path.
Model Override Handlers
internal/admin/handler_model_overrides.go
UpsertModelOverride and DeleteModelOverride bind selector from body and use normalization helpers; map store NotFound to 404.
Model Pricing Override Handlers
internal/admin/handler_model_pricing_overrides.go
Handlers refactored to accept selector in JSON body with normalization (length cap) and direct not-found error mapping.
Dashboard Asset Versioning
internal/admin/dashboard/dashboard.go, internal/admin/dashboard/dashboard_test.go
New buildFrontendAssetVersions() helper discovers CSS/JS assets via glob and hashes them; tests assert versioned script URL presence and base-path prefixing.
Frontend Client Mutations
internal/admin/dashboard/static/js/modules/*.js
Alias, budget, guardrail, and model override modules use collection endpoints with JSON bodies; identifiers sent in request payloads (name/selector/budget_key).
Frontend Module Tests
internal/admin/dashboard/static/js/modules/*.test.cjs
New tests verify mutations send identifiers in JSON bodies for budgets, aliases, overrides, and guardrails.
Handler Unit Tests
internal/admin/*_test.go
Tests refactored to use JSON request bodies; new test TestBudgetEndpointsUpsertAcceptsNumericPeriodString added for period string parsing.
Integration & E2E Tests / Release scenarios
tests/e2e/budget_test.go, tests/e2e/release-e2e-scenarios.md, tests/integration/workflows_guardrails_test.go
E2E and integration tests updated to use collection endpoints; release scenario helpers refactored to send JSON bodies rather than encoded path segments.
Miscellaneous Test Updates
internal/admin/handler_test.go, internal/server/http_test.go
Base-path and error-logging tests updated for new endpoint patterns.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • ENTERPILOT/GoModel#306: Directly related—both modify model-pricing-overrides handlers, routes, and docs using the same collection-style endpoint approach.
  • ENTERPILOT/GoModel#276: Directly related—both modify budget endpoint contracts and admin budget routes.
  • ENTERPILOT/GoModel#294: Directly related—both refactor admin handler files, routes, errors, and OpenAPI/docs.

Poem

🐰 REST endpoints now gather 'round,
No path params tangling in the ground!
JSON bodies carry what we need,
Collections bloom where selectors seed! 📚✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.66% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(admin): avoid encoded slash admin URLs' clearly and specifically describes the main change: refactoring admin API endpoints to use JSON request bodies instead of encoded slash-capable identifiers in URL paths.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/no-url-encoding

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 8, 2026

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 8, 2026

Greptile Summary

This PR migrates admin write/delete endpoints for budgets, aliases, guardrails, model overrides, and model pricing overrides from URL path parameters to JSON request bodies, solving the encoded-slash problem for identifiers like openai/ or /teams/acme. Dashboard mutations, OpenAPI docs, README, and JS modules are all updated to match.

  • Route refactor: PUT and DELETE handlers for five resource types now accept their identifier fields (name, selector, user_path, budget_key) in the JSON body; matching routes in routes.go collapse from per-resource parameterised paths to shared collection-level paths.
  • Dashboard cache-busting: buildFrontendAssetVersions now discovers and hashes all CSS, JS, and JS module files automatically instead of a fixed subset.
  • Validation parity gap: budgetRequestKey (called by both UpsertBudget and DeleteBudget) passes an empty user_path straight to budget.NormalizeUserPath, which silently normalises it to \"/\". Every other refactored handler explicitly rejects an empty identifier. The same silent promotion occurs in ResetBudget (line 181) for the reset-one endpoint.

Confidence Score: 4/5

The refactor is mechanically correct and well-covered by tests, but the budget handlers do not guard against an empty user_path, which silently routes write and delete operations to the root '/' budget — a data-integrity risk for any caller that accidentally omits the field.

The alias, guardrail, and model-override handlers all explicitly reject an empty identifier after the refactor, but budgetRequestKey (used by UpsertBudget and DeleteBudget) and the inline call in ResetBudget skip that check. An omitted user_path will silently affect the global root budget rather than returning 400, which can corrupt budget state for deployments that rely on the root limit. The rest of the change is straightforward and the JS clients validate inputs client-side before sending.

internal/admin/handler_budgets.go — the budgetRequestKey helper and the ResetBudget handler both need an explicit user_path emptiness check.

Important Files Changed

Filename Overview
internal/admin/handler_budgets.go UpsertBudget and DeleteBudget omit empty user_path guard in budgetRequestKey; ResetBudget similarly lacks a guard before calling budget.NormalizeUserPath — both silently target the root '/' budget when user_path is omitted.
internal/admin/handler_aliases.go Clean migration to body-based identifier; explicit empty-name guard preserved.
internal/admin/handler_guardrails.go Clean migration to body-based identifier; explicit empty-name guard preserved with mutex protection.
internal/admin/handler_model_overrides.go Empty selector is rejected by normalizeModelOverrideSelector; migration is correct.
internal/admin/handler_model_pricing_overrides.go Empty selector rejected by normalizeModelPricingOverrideSelector; migration is correct.
internal/admin/routes.go Routes correctly collapsed to collection-level paths; no orphaned or duplicate registrations.
internal/admin/dashboard/dashboard.go buildFrontendAssetVersions now globs all JS module files for cache-busting; logic is clean.
internal/admin/dashboard/static/js/modules/budgets.js PUT uses budget_key wrapper correctly; DELETE sends budget_key; reset-one sends period_seconds at top level matching resetBudgetRequest shape.

Sequence Diagram

sequenceDiagram
    participant Client as Dashboard or API Client
    participant Echo as Echo Router
    participant Handler as Admin Handler
    participant Store as Budget or Override Store

    Note over Client,Store: Before this PR — identifier in URL path
    Client->>Echo: PUT /admin/api/v1/budgets/teams-acme/daily
    Echo->>Handler: Param(user_path), Param(period)
    Handler->>Store: Upsert(userPath, period, amount)

    Note over Client,Store: After this PR — identifier in JSON body
    Client->>Echo: PUT /admin/api/v1/budgets with body
    Echo->>Handler: Bind(req)
    Handler->>Handler: budgetRequestKey(req.UserPath, req.BudgetKey)
    alt user_path is omitted or empty
        Handler-->>Client: 200 OK silently targets root budget
    else user_path provided
        Handler->>Store: Upsert(userPath, periodSeconds, amount)
        Store-->>Client: 200 budgetListResponse
    end
Loading

Reviews (2): Last reviewed commit: "fix(admin): align request schemas with h..." | Re-trigger Greptile

Comment thread internal/admin/handler_budgets.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@internal/admin/handler_budgets.go`:
- Around line 243-254: Change the body contract to include an explicit budget
key object and enforce its either/or requirement: add a new field BudgetKey
`json:"budget_key" binding:"required"` (type struct { Period string
`json:"period,omitempty"`; PeriodSeconds int64 `json:"period_seconds,omitempty"`
}) to both upsertBudgetRequest and deleteBudgetRequest so the generated OpenAPI
publishes the key object, then add validation in the corresponding handlers
(e.g., upsertBudgetHandler and deleteBudgetHandler) to return 400 if neither
BudgetKey.Period nor BudgetKey.PeriodSeconds is provided (or if both are
provided, if you want exclusive) — do not rely on implicit tags alone so the
runtime and schema agree.

In `@internal/admin/handler_guardrails.go`:
- Around line 15-25: Remove the incorrect Gin `binding:"required"` tags from the
request structs so they don't mislead readers: edit the upsertGuardrailRequest
and deleteGuardrailRequest definitions to drop `binding:"required"` from the
Name fields (and any other `binding` tags), leaving only valid JSON tags; keep
the existing manual validation in the handler functions (the
strings.TrimSpace/empty checks in the upsert/delete handlers) or replace them
with Echo-compatible validation (register a custom Validator and use `validate`
tags) if you prefer automatic validation.

In `@internal/admin/handler_model_pricing_overrides.go`:
- Around line 45-59: The OpenAPI comments for the admin model-pricing overrides
endpoints (e.g., the UpsertModelPricingOverride handler documented at the top of
internal/admin/handler_model_pricing_overrides.go and the other operation around
lines 91-104) are missing a 502 Bad Gateway response even though
pricingOverrideWriteError returns http.StatusBadGateway for non-validation write
failures; update both Swagger comment blocks to add a `@Failure` 502 {object}
core.GatewayError (or equivalent) entry so the generated spec documents the Bad
Gateway error path produced by pricingOverrideWriteError.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 733bc42f-261d-4a66-807d-a66d1352ee1c

📥 Commits

Reviewing files that changed from the base of the PR and between 91ac883 and 126e3eb.

📒 Files selected for processing (31)
  • README.md
  • cmd/gomodel/docs/docs.go
  • docs/openapi.json
  • internal/admin/dashboard/dashboard.go
  • internal/admin/dashboard/dashboard_test.go
  • internal/admin/dashboard/static/js/modules/aliases.js
  • internal/admin/dashboard/static/js/modules/aliases.test.cjs
  • internal/admin/dashboard/static/js/modules/budgets.js
  • internal/admin/dashboard/static/js/modules/budgets.test.cjs
  • internal/admin/dashboard/static/js/modules/guardrails.js
  • internal/admin/dashboard/static/js/modules/guardrails.test.cjs
  • internal/admin/dashboard/static/js/modules/model-pricing-overrides.js
  • internal/admin/dashboard/static/js/modules/model-pricing-overrides.test.cjs
  • internal/admin/errors.go
  • internal/admin/handler_aliases.go
  • internal/admin/handler_aliases_test.go
  • internal/admin/handler_budgets.go
  • internal/admin/handler_budgets_test.go
  • internal/admin/handler_guardrails.go
  • internal/admin/handler_guardrails_test.go
  • internal/admin/handler_model_overrides.go
  • internal/admin/handler_model_overrides_test.go
  • internal/admin/handler_model_pricing_overrides.go
  • internal/admin/handler_model_pricing_overrides_test.go
  • internal/admin/handler_test.go
  • internal/admin/routes.go
  • internal/admin/routes_test.go
  • internal/server/http_test.go
  • tests/e2e/budget_test.go
  • tests/e2e/release-e2e-scenarios.md
  • tests/integration/workflows_guardrails_test.go

Comment thread internal/admin/handler_budgets.go
Comment thread internal/admin/handler_guardrails.go
Comment thread internal/admin/handler_model_pricing_overrides.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/openapi.json (1)

233-1210: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add the alias admin mutations to the generated OpenAPI too.

This refreshes the body-based contracts for budgets and model overrides, but the generated spec still has no /admin/api/v1/aliases entries at all. The release E2E matrix in this PR already switched alias create/delete to PUT/DELETE /admin/api/v1/aliases with name in the JSON body, so SDK/docs consumers will still see an incomplete admin API after merge.

🤖 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 `@docs/openapi.json` around lines 233 - 1210, The OpenAPI spec is missing the
admin alias mutation endpoints; add a new path entry for "/admin/api/v1/aliases"
implementing at minimum PUT and DELETE operations that mirror other admin
mutation patterns: use requestBody JSON schemas (e.g. admin.upsertAliasRequest
for PUT and admin.deleteAliasRequest for DELETE), include appropriate responses
(200 or 204 and standard error responses referencing core.GatewayError), and
include the security section with BearerAuth; follow the existing style of
"/admin/api/v1/budgets" and "/admin/api/v1/model-overrides" for tags ("admin"),
summaries, descriptions, and response blocks so SDK/docs consumers see the
create/delete alias mutations (PUT/DELETE with name in the body).
🤖 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 `@docs/openapi.json`:
- Around line 412-421: Update the components schema admin.resetBudgetsRequest so
it requires the reset confirmation token (the exact property name used by the
handler / TestResetBudgetsEndpoint) instead of allowing an empty object or
arbitrary string: add a properties block with the confirmation field (string),
include that field in the required array, and set additionalProperties to false
so generated clients cannot send {} or other shapes the server will reject.

In `@tools/openapi-postprocess.mjs`:
- Around line 185-193: The budget schemas are missing a required "user_path"
property causing client requests to be rejected at runtime; update the
postprocess to call ensureRequiredProperty("admin.upsertBudgetRequest",
"user_path") and ensureRequiredProperty("admin.deleteBudgetRequest",
"user_path") (in the same block alongside the existing ensureRequiredProperty
calls and before applyBudgetKeySchemaConstraints()) so the generated OpenAPI
schemas mark user_path as required for upsert and delete handlers.

---

Outside diff comments:
In `@docs/openapi.json`:
- Around line 233-1210: The OpenAPI spec is missing the admin alias mutation
endpoints; add a new path entry for "/admin/api/v1/aliases" implementing at
minimum PUT and DELETE operations that mirror other admin mutation patterns: use
requestBody JSON schemas (e.g. admin.upsertAliasRequest for PUT and
admin.deleteAliasRequest for DELETE), include appropriate responses (200 or 204
and standard error responses referencing core.GatewayError), and include the
security section with BearerAuth; follow the existing style of
"/admin/api/v1/budgets" and "/admin/api/v1/model-overrides" for tags ("admin"),
summaries, descriptions, and response blocks so SDK/docs consumers see the
create/delete alias mutations (PUT/DELETE with name in the body).
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6b7b6db9-6df2-43df-8c44-964aeb06e529

📥 Commits

Reviewing files that changed from the base of the PR and between 126e3eb and 408567a.

📒 Files selected for processing (13)
  • cmd/gomodel/docs/docs.go
  • docs/openapi.json
  • internal/admin/dashboard/static/js/modules/budgets.js
  • internal/admin/dashboard/static/js/modules/budgets.test.cjs
  • internal/admin/handler_aliases.go
  • internal/admin/handler_budgets.go
  • internal/admin/handler_budgets_test.go
  • internal/admin/handler_guardrails.go
  • internal/admin/handler_model_overrides.go
  • internal/admin/handler_model_pricing_overrides.go
  • tests/e2e/budget_test.go
  • tests/e2e/release-e2e-scenarios.md
  • tools/openapi-postprocess.mjs

Comment thread docs/openapi.json
Comment on lines +412 to +421
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/admin.resetBudgetsRequest"
}
}
},
"description": "Reset confirmation",
"required": true
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

Tighten the reset-all schema to require the confirmation token.

admin.resetBudgetsRequest currently allows {} and arbitrary strings here, but the handler contract is stricter: TestResetBudgetsEndpoint shows the endpoint returns 400 unless the caller sends the reset confirmation token. Please encode that in the schema as well, otherwise generated clients will advertise requests the server rejects.

🤖 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 `@docs/openapi.json` around lines 412 - 421, Update the components schema
admin.resetBudgetsRequest so it requires the reset confirmation token (the exact
property name used by the handler / TestResetBudgetsEndpoint) instead of
allowing an empty object or arbitrary string: add a properties block with the
confirmation field (string), include that field in the required array, and set
additionalProperties to false so generated clients cannot send {} or other
shapes the server will reject.

Comment on lines +185 to +193
ensureRequiredProperty("admin.upsertBudgetRequest", "amount");
ensureRequiredProperty("admin.upsertBudgetRequest", "budget_key");
ensureRequiredProperty("admin.deleteBudgetRequest", "budget_key");
ensureRequiredProperty("admin.upsertModelOverrideRequest", "selector");
ensureRequiredProperty("admin.deleteModelOverrideRequest", "selector");
ensureRequiredProperty("admin.upsertModelPricingOverrideRequest", "selector");
ensureRequiredProperty("admin.upsertModelPricingOverrideRequest", "pricing");
ensureRequiredProperty("admin.deleteModelPricingOverrideRequest", "selector");
applyBudgetKeySchemaConstraints();
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

user_path is still optional in budget schemas, but runtime requires it.

The post-process step enforces budget_key (and amount for upsert), but not user_path. Handlers validate user_path, so generated clients can produce requests that always fail with 400.

💡 Proposed fix
 ensureRequiredProperty("admin.upsertBudgetRequest", "amount");
 ensureRequiredProperty("admin.upsertBudgetRequest", "budget_key");
+ensureRequiredProperty("admin.upsertBudgetRequest", "user_path");
 ensureRequiredProperty("admin.deleteBudgetRequest", "budget_key");
+ensureRequiredProperty("admin.deleteBudgetRequest", "user_path");
 ensureRequiredProperty("admin.upsertModelOverrideRequest", "selector");
 ensureRequiredProperty("admin.deleteModelOverrideRequest", "selector");
 ensureRequiredProperty("admin.upsertModelPricingOverrideRequest", "selector");
🤖 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 `@tools/openapi-postprocess.mjs` around lines 185 - 193, The budget schemas are
missing a required "user_path" property causing client requests to be rejected
at runtime; update the postprocess to call
ensureRequiredProperty("admin.upsertBudgetRequest", "user_path") and
ensureRequiredProperty("admin.deleteBudgetRequest", "user_path") (in the same
block alongside the existing ensureRequiredProperty calls and before
applyBudgetKeySchemaConstraints()) so the generated OpenAPI schemas mark
user_path as required for upsert and delete handlers.

Comment on lines +357 to 361
func budgetRequestKey(rawUserPath string, key *budgetKeyRequest) (string, int64, error) {
userPath, err := budget.NormalizeUserPath(rawUserPath)
if err != nil {
return "", 0, fmt.Errorf("invalid user_path path parameter: %w", err)
return "", 0, err
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Missing empty user_path guard — when the caller omits or sends an empty user_path, budget.NormalizeUserPath("") returns "/" without an error, so both UpsertBudget and DeleteBudget will silently operate on the global root budget instead of returning 400. Every other refactored handler (aliases, guardrails, model-overrides) rejects an empty identifier explicitly. The same issue exists in ResetBudget at line 181 where budget.NormalizeUserPath(req.UserPath) is called directly with no prior emptiness check.

Suggested change
func budgetRequestKey(rawUserPath string, key *budgetKeyRequest) (string, int64, error) {
userPath, err := budget.NormalizeUserPath(rawUserPath)
if err != nil {
return "", 0, fmt.Errorf("invalid user_path path parameter: %w", err)
return "", 0, err
}
func budgetRequestKey(rawUserPath string, key *budgetKeyRequest) (string, int64, error) {
if strings.TrimSpace(rawUserPath) == "" {
return "", 0, errors.New("user_path is required")
}
userPath, err := budget.NormalizeUserPath(rawUserPath)
if err != nil {
return "", 0, err
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's intentional

@SantiagoDePolonia SantiagoDePolonia merged commit d6b7732 into main May 8, 2026
19 checks passed
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