Skip to content

feat: support search, filtering and grouping for list default configs#879

Open
ayushjain17 wants to merge 2 commits intomainfrom
feat/default-config-filter-2
Open

feat: support search, filtering and grouping for list default configs#879
ayushjain17 wants to merge 2 commits intomainfrom
feat/default-config-filter-2

Conversation

@ayushjain17
Copy link
Collaborator

@ayushjain17 ayushjain17 commented Feb 13, 2026

Problem

Describe the problem you are trying to solve here

Solution

Provide a brief summary of your solution so that reviewers can understand your code

Environment variable changes

What ENVs need to be added or changed

Pre-deployment activity

Things needed to be done before deploying this change (if any)

Post-deployment activity

Things needed to be done after deploying this change (if any)

API changes

Endpoint Method Request body Response Body
API GET/POST, etc request response

Possible Issues in the future

Describe any possible issues that could occur because of this change

Summary by CodeRabbit

  • New Features

    • Added grouped listing of default configurations with hierarchical structure (Group/Config variants)
    • Added sorting capabilities for default configs by Key, Created At, or Last Modified At in ascending or descending order
    • Added search functionality to find default configs by key pattern
    • Enhanced default config filtering to support multiple names and prefix-based filtering
  • Improvements

    • Extended default config queries with new sort and search parameters across all SDK clients

@ayushjain17 ayushjain17 requested a review from a team as a code owner February 13, 2026 11:41
@semanticdiff-com
Copy link

semanticdiff-com bot commented Feb 13, 2026

Review changes with  SemanticDiff

Changed Files
File Status
  crates/frontend/src/components/pagination.rs  98% smaller
  crates/frontend/src/pages/function.rs  93% smaller
  crates/frontend/src/query_updater.rs  88% smaller
  crates/superposition_sdk/src/client/list_default_configs.rs  39% smaller
  crates/frontend/src/pages/default_config_list/filter.rs  36% smaller
  crates/frontend/src/pages/default_config_list.rs  35% smaller
  clients/javascript/sdk/src/commands/ListDefaultConfigsCommand.ts  28% smaller
  crates/frontend/src/pages/default_config.rs  19% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/model/ListDefaultConfigsInput.java  16% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java  15% smaller
  crates/superposition_types/src/api/default_config.rs  13% smaller
  crates/superposition_sdk/src/operation/list_default_configs.rs  11% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java  10% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java  10% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java  10% smaller
  crates/context_aware_config/src/api/default_config/handlers.rs  8% smaller
  crates/frontend/src/api.rs  7% smaller
  clients/python/sdk/superposition_sdk/serialize.py  4% smaller
  clients/python/sdk/superposition_sdk/models.py  2% smaller
  clients/javascript/sdk/src/models/models_0.ts  2% smaller
  clients/javascript/sdk/src/protocols/Aws_restJson1.ts  2% smaller
  docs/docs/api/Superposition.openapi.json  1% smaller
  tests/src/default_config.test.ts  1% smaller
  clients/python/sdk/superposition_sdk/config.py  1% smaller
  clients/haskell/sdk/Io/Superposition/Command/ListGroupedDefaultConfigs.hs Unsupported file format
  clients/haskell/sdk/Io/Superposition/Model/DefaultConfigSortOn.hs Unsupported file format
  clients/haskell/sdk/Io/Superposition/Model/GroupedDefaultConfig.hs Unsupported file format
  clients/haskell/sdk/Io/Superposition/Model/ListDefaultConfigsInput.hs Unsupported file format
  clients/haskell/sdk/Io/Superposition/Model/ListGroupedDefaultConfigsInput.hs Unsupported file format
  clients/haskell/sdk/Io/Superposition/Model/ListGroupedDefaultConfigsOutput.hs Unsupported file format
  clients/haskell/sdk/SuperpositionSDK.cabal Unsupported file format
  clients/java/sdk/src/main/java/io/juspay/superposition/model/DefaultConfig.java  0% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/model/DefaultConfigSortOn.java  0% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/model/GroupedDefaultConfig.java  0% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/model/ListGroupedDefaultConfigs.java  0% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/model/ListGroupedDefaultConfigsInput.java  0% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/model/ListGroupedDefaultConfigsOutput.java  0% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSchemas.java  0% smaller
  clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSerde.java  0% smaller
  clients/javascript/sdk/src/Superposition.ts  0% smaller
  clients/javascript/sdk/src/SuperpositionClient.ts  0% smaller
  clients/javascript/sdk/src/commands/ListGroupedDefaultConfigsCommand.ts  0% smaller
  clients/javascript/sdk/src/commands/index.ts  0% smaller
  clients/python/sdk/superposition_sdk/_private/schemas.py  0% smaller
  clients/python/sdk/superposition_sdk/client.py  0% smaller
  clients/python/sdk/superposition_sdk/deserialize.py  0% smaller
  crates/context_aware_config/src/api/default_config.rs  0% smaller
  crates/context_aware_config/src/api/default_config/helpers.rs  0% smaller
  crates/frontend/src/pages/compare_overrides.rs  0% smaller
  crates/frontend/src/pages/default_config_list/types.rs  0% smaller
  crates/frontend/tailwind.config.js  0% smaller
  crates/superposition_sdk/src/client.rs  0% smaller
  crates/superposition_sdk/src/client/customize.rs  0% smaller
  crates/superposition_sdk/src/client/list_grouped_default_configs.rs  0% smaller
  crates/superposition_sdk/src/error_meta.rs  0% smaller
  crates/superposition_sdk/src/operation.rs  0% smaller
  crates/superposition_sdk/src/operation/list_default_configs/_list_default_configs_input.rs Unsupported file format
  crates/superposition_sdk/src/operation/list_default_configs/builders.rs Unsupported file format
  crates/superposition_sdk/src/operation/list_grouped_default_configs.rs  0% smaller
  crates/superposition_sdk/src/operation/list_grouped_default_configs/_list_grouped_default_configs_input.rs Unsupported file format
  crates/superposition_sdk/src/operation/list_grouped_default_configs/_list_grouped_default_configs_output.rs Unsupported file format
  crates/superposition_sdk/src/operation/list_grouped_default_configs/builders.rs Unsupported file format
  crates/superposition_sdk/src/protocol_serde.rs  0% smaller
  crates/superposition_sdk/src/protocol_serde/shape_grouped_default_config.rs  0% smaller
  crates/superposition_sdk/src/protocol_serde/shape_list_grouped_default_config_out.rs Unsupported file format
  crates/superposition_sdk/src/protocol_serde/shape_list_grouped_default_configs.rs  0% smaller
  crates/superposition_sdk/src/serde_util.rs  0% smaller
  crates/superposition_sdk/src/types.rs  0% smaller
  crates/superposition_sdk/src/types/_default_config_sort_on.rs  0% smaller
  crates/superposition_sdk/src/types/_grouped_default_config.rs  0% smaller
  docs/docs/api/list-default-configs.api.mdx Unsupported file format
  docs/docs/api/list-grouped-default-configs.api.mdx Unsupported file format
  docs/docs/api/sidebar.ts  0% smaller
  smithy/models/default-config.smithy Unsupported file format
  smithy/patches/python.patch Unsupported file format

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

Important

Review skipped

Auto incremental 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.

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:

  • 🔍 Trigger review

Walkthrough

Introduces a new ListGroupedDefaultConfigs operation for listing default configurations with optional grouping, filtering (by name list, prefix, search), and sorting (by key, created date, last modified date). Extends all SDK clients, adds frontend UI with grouping toggle, search, sorting columns, and implements backend logic for both grouped and ungrouped list responses.

Changes

Cohort / File(s) Summary
Smithy API Definition
smithy/models/default-config.smithy, docs/docs/api/Superposition.openapi.json, docs/docs/api/list-default-configs.api.mdx, docs/docs/api/list-grouped-default-configs.api.mdx, docs/docs/api/sidebar.ts
Added ListGroupedDefaultConfigs operation with GET /default-config?grouped=true, DefaultConfigSortOn enum (KEY, CREATED_AT, LAST_MODIFIED_AT), GroupedDefaultConfig union (Group/Config), and ListGroupedDefaultConfigOut list type. Extended ListDefaultConfigs input with StringList name, sort_by, sort_on, and search parameters.
Haskell SDK
clients/haskell/sdk/Io/Superposition/Command/ListGroupedDefaultConfigs.hs, clients/haskell/sdk/Io/Superposition/Model/DefaultConfigSortOn.hs, clients/haskell/sdk/Io/Superposition/Model/GroupedDefaultConfig.hs, clients/haskell/sdk/Io/Superposition/Model/ListGroupedDefaultConfigsInput.hs, clients/haskell/sdk/Io/Superposition/Model/ListGroupedDefaultConfigsOutput.hs, clients/haskell/sdk/SuperpositionSDK.cabal
Added new modules for ListGroupedDefaultConfigs command, DefaultConfigSortOn enum, GroupedDefaultConfig union, and corresponding input/output types with JSON serialization, builder patterns, and HTTP request construction.
Java SDK
clients/java/sdk/src/main/java/io/juspay/superposition/client/Superposition*.java, clients/java/sdk/src/main/java/io/juspay/superposition/model/DefaultConfigSortOn.java, clients/java/sdk/src/main/java/io/juspay/superposition/model/GroupedDefaultConfig.java, clients/java/sdk/src/main/java/io/juspay/superposition/model/ListGroupedDefaultConfigs*.java, clients/java/sdk/src/main/java/io/juspay/superposition/model/ListDefaultConfigsInput.java, clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSchemas.java, clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSerde.java
Added async and sync client methods for listGroupedDefaultConfigs, DefaultConfigSortOn enum-like class, GroupedDefaultConfig union with builder pattern, input/output types with Smithy integration. Updated ListDefaultConfigsInput to use List for name field and added sort_by, sort_on, search parameters.
JavaScript SDK
clients/javascript/sdk/src/Superposition.ts, clients/javascript/sdk/src/SuperpositionClient.ts, clients/javascript/sdk/src/commands/ListGroupedDefaultConfigsCommand.ts, clients/javascript/sdk/src/commands/index.ts, clients/javascript/sdk/src/commands/ListDefaultConfigsCommand.ts, clients/javascript/sdk/src/models/models_0.ts, clients/javascript/sdk/src/protocols/Aws_restJson1.ts
Added ListGroupedDefaultConfigsCommand with input/output types, DefaultConfigSortOn enum, GroupedDefaultConfig union with visitor pattern, and serialization/deserialization functions. Updated ListDefaultConfigsInput to use string array for name and added sorting/search fields.
Python SDK
clients/python/sdk/superposition_sdk/schemas.py, clients/python/sdk/superposition_sdk/client.py, clients/python/sdk/superposition_sdk/config.py, clients/python/sdk/superposition_sdk/deserialize.py, clients/python/sdk/superposition_sdk/models.py, clients/python/sdk/superposition_sdk/serialize.py
Added ListGroupedDefaultConfigs operation with input/output models, DefaultConfigSortOn enum, GroupedDefaultConfig union with deserialization helpers. Updated ListDefaultConfigsInput.name to list type and added sort_by, sort_on, search fields. Extended schema definitions and protocol serialization/deserialization.
Rust SDK
crates/superposition_sdk/src/client.rs, crates/superposition_sdk/src/client/list_grouped_default_configs.rs, crates/superposition_sdk/src/client/list_default_configs.rs, crates/superposition_sdk/src/error_meta.rs, crates/superposition_sdk/src/operation.rs, crates/superposition_sdk/src/operation/list_default_configs.rs, crates/superposition_sdk/src/operation/list_grouped_default_configs.rs, crates/superposition_sdk/src/protocol_serde.rs, crates/superposition_sdk/src/types.rs, crates/superposition_sdk/src/types/_default_config_sort_on.rs, crates/superposition_sdk/src/types/_grouped_default_config.rs, crates/superposition_sdk/src/serde_util.rs
Added list_grouped_default_configs client method and operation orchestration. DefaultConfigSortOn enum with forward-compatible variant handling. GroupedDefaultConfig union with variant accessors. Protocol serialization/deserialization for grouped operations. Updated list_default_configs to handle Vec for name and added sort_by, sort_on, search fields.
Backend API Implementation
crates/context_aware_config/src/api/default_config.rs, crates/context_aware_config/src/api/default_config/handlers.rs, crates/context_aware_config/src/api/default_config/helpers.rs
Introduced list_grouped_configs handler returning grouped/config responses. Added helper functions for unflattening dot-separated keys into hierarchical InternalStructure, sorting by creation/modification dates, and building grouped/ungrouped response data. Enhanced list_handler to support grouped flag, name as list, search, and sort_by/sort_on parameters.
Frontend Type Definitions and API
crates/superposition_types/src/api/default_config.rs, crates/frontend/src/api.rs, crates/frontend/src/query_updater.rs, crates/frontend/src/pages/default_config.rs
Added SortOn enum (Key, CreatedAt, LastModifiedAt), DefaultConfigFilters with name (CommaSeparatedStringQParams), grouped, prefix, sort_by, sort_on, search fields. Implemented init_with_grouping, set_search, set_name methods. Added ListDefaultConfigResponse enum (Group/Config). Refactored use_update_url_query to accept slice of key-value pairs. Added From<&DefaultConfigFilters> for CreatePageParams.
Frontend UI Components
crates/frontend/src/pages/default_config_list.rs, crates/frontend/src/pages/default_config_list/filter.rs, crates/frontend/src/pages/default_config_list/types.rs, crates/frontend/src/pages/compare_overrides.rs, crates/frontend/src/pages/function.rs, crates/frontend/tailwind.config.js, crates/frontend/src/pages/pagination.rs
Deleted PageParams type. Completely refactored DefaultConfigList component to support grouped/ungrouped toggle, column-based sorting (Key, CreatedAt, LastModifiedAt), debounced search, prefix filtering, and pagination. Updated FilterSummary to use ListPills component for multiple name values. Restructured query parameter handling throughout pages. Added lg+ Tailwind breakpoint.
Tests
tests/src/default_config.test.ts
Added comprehensive test suite for grouped and ungrouped list operations, including filtering by prefix, search functionality, and data structure validation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement, API, frontend, backend

Suggested reviewers

  • knutties
  • mahatoankitkumar
  • Datron

🐰 A rabbit's ode to grouping:

Scattered configs once lay flat and bare,
Now nestled in groups, organized with care—
Sort by key, by date, or what you please,
Search and filter makes the task a breeze!
With grouped views and sorted lists so grand,
This SDK now hops across every land! 🌿

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.93% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: support search, filtering and grouping for list default configs' accurately and concisely describes the main changes across all modified files, focusing on the primary feature addition.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/default-config-filter-2

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.

Copy link
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: 11

Caution

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

⚠️ Outside diff range comments (2)
crates/frontend/src/query_updater.rs (1)

152-182: ⚠️ Potential issue | 🔴 Critical

Bug: location.search includes the leading ?, which breaks param matching and produces malformed URLs.

Two issues in this implementation:

  1. ? prefix not stripped: location.search returns the query string with a leading ? (as evidenced by use_query_string() at Line 147 stripping it). Splitting "?page=1&sort=asc" on "&" yields ["?page=1", "sort=asc"]. Now starts_with("page=") never matches "?page=1", so the param is appended as a duplicate instead of updated. The final result also gets a double ??.

  2. Empty query string: Splitting "" on "&" yields [""]. After pushing a new param, joining gives "&param=value", producing "?&param=value".

Proposed fix
 pub fn use_update_url_query() -> impl Fn(&[(&str, Option<String>)]) -> String {
     let location = use_location();
     move |input: &[(&str, Option<String>)]| {
-        let mut params = location
+        let search = location
             .search
-            .get_untracked()
-            .split("&")
+            .get_untracked();
+        let search = search.strip_prefix('?').unwrap_or(&search);
+        let mut params: Vec<String> = search
+            .split('&')
             .map(String::from)
+            .filter(|s| !s.is_empty())
             .collect::<Vec<_>>();
docs/docs/api/Superposition.openapi.json (1)

1231-1263: ⚠️ Potential issue | 🟡 Minor

Clarify the semantic difference between search and prefix parameters, or align filter parameters between list endpoints.

The ungrouped endpoint /default-config exposes search (string) while the grouped endpoint /default-config?grouped=true uses prefix (string) instead. Both Smithy and OpenAPI spec confirm this divergence exists. If search and prefix serve different purposes by design, this should be documented to explain why the parameters differ between modes. If they serve the same purpose, align the parameter names to prevent confusion when SDKs or UI code switches between grouped and ungrouped modes.

🤖 Fix all issues with AI agents
In
`@clients/java/sdk/src/main/java/io/juspay/superposition/model/ListGroupedDefaultConfigsInput.java`:
- Around line 219-231: The toBuilder() method in ListGroupedDefaultConfigsInput
calls builder.count(this.count), builder.page(this.page) and
builder.all(this.all) but those fields are nullable Integer/Boolean and the
Builder setters expect primitives, causing NPE on unboxing; update the
toBuilder() in ListGroupedDefaultConfigsInput to guard nullable fields (count,
page, all) with null checks and only call the primitive setters when the
corresponding field is non-null (e.g., if (this.count != null)
builder.count(this.count);), or alternatively modify the Builder API to accept
boxed Integer/Boolean and use those setters; apply the same null-safe pattern
for similar generated Input classes (e.g., ListDefaultConfigsInput) where
toBuilder() currently unboxes nullable fields.

In `@clients/javascript/sdk/src/protocols/Aws_restJson1.ts`:
- Around line 1626-1629: The query map in Aws_restJson1.ts currently uses sparse
array literals like "[, input[_sb]!]" for entries keyed by symbols such as _n,
_sb, _so, and _s; replace these sparse tuples with explicit condition/value
functions to avoid sparse arrays (e.g., emit [() => input[_sb] !== void 0, () =>
input[_sb]!]) so each entry becomes a pair of functions (condition and value)
rather than a sparse array; update the entries for _n, _sb, _so, and _s (and the
similar block at lines ~1775-1782) to use the () => checks and () => value form.

In `@clients/python/sdk/superposition_sdk/_private/schemas.py`:
- Around line 7493-7504: The LIST_GROUPED_DEFAULT_CONFIGS operation currently
embeds the grouped query param in the smithy.api#http trait's "uri"
("/default-config?grouped=true"); change the http trait value to use uri
"/default-config" and instead add an `@httpQuery`("grouped") member on the
operation's input structure (so that the grouped flag is modeled as a query
member and serialized/percent-encoded correctly), then regenerate the SDK so the
Schema (LIST_GROUPED_DEFAULT_CONFIGS) and generated client code reflect the new
`@httpQuery` binding rather than a query string in the http URI.

In `@clients/python/sdk/superposition_sdk/models.py`:
- Around line 7774-7778: The list literal assigned to effective_auth_schemes is
missing a comma between its ShapeID elements causing a SyntaxError; update the
list in models.py (the effective_auth_schemes assignment) to separate entries
with a comma and correct indentation, e.g. ensure
ShapeID("smithy.api#httpBasicAuth"), ShapeID("smithy.api#httpBearerAuth") are
both elements in the list and the closing bracket/parenthesis matches the
surrounding code structure.

In `@crates/context_aware_config/src/api/default_config/handlers.rs`:
- Around line 459-466: The sort currently treats None dates as Less which places
"unknown date" groups first; change the comparator in group_entries.sort_by (the
closure using a_date, b_date and apply_sort_order) so that None is considered
greater than Some (i.e., sorts last) regardless of ascending/descending; update
the match arms involving (None, Some(_)) and (Some(_), None) to return
Ordering::Greater and Ordering::Less respectively (and keep the tie-breaker on
a_key when both None) so apply_sort_order(sort_by, ord) yields the UX of unknown
dates appearing after known dates.
- Around line 581-597: The in-memory pagination block (building
PaginatedResponse when show_all is false) uses offset and count unsafely —
casting a negative offset to usize and allowing count==0 leads to wraparound and
infinite total_pages; clamp or sanitize the incoming pagination params (page and
count) to be at least 1 before computing offset and count, then recompute offset
= (page - 1) * count and use checked/constrained arithmetic when deriving
start/end and total_pages; apply the same sanitization for the DB pagination
path (the other handler that builds PaginatedResponse) so both in-memory and DB
code paths use non-zero, non-negative page/count values and avoid usize casts of
negative offsets.
- Around line 643-648: The search string used in the ilike pattern isn't
escaping SQL LIKE metacharacters, so '%' or '_' in filters.search will act as
wildcards; before building the pattern in handlers.rs, escape backslashes, '%'
and '_' in filters.search (e.g. replace '\' -> '\\', '%' -> '\%', '_' -> '\_'),
then build the pattern as format!("%{}%", escaped_search) and use that pattern
in builder.filter(dsl::key.ilike(pattern)); ensure the escape sequences are
applied to filters.search (the same variable referenced in the current if-let
branch) so unwanted wildcard matches are prevented.

In `@crates/frontend/src/pages/default_config_list.rs`:
- Around line 101-112: The closure redirect_url currently calls
use_update_url_query() inside the closure body which violates Leptos' hook call
timing; hoist the call to use_update_url_query() to component initialization
(top-level of the component function) and store its returned function/handle in
a variable (e.g., update_query_fn), then have redirect_url capture and call that
variable along with pagination_params_rws.get() and the provided prefix; this
ensures use_update_url_query() is invoked during component setup rather than
lazily inside redirect_url.

In `@crates/superposition_sdk/src/operation/list_default_configs.rs`:
- Around line 154-174: The tests and callers still send name as a single string
but the ListDefaultConfigs request now expects name to be an array and
serializes each element with query.push_kv("name", ...); update all usages
(tests, frontend callers, any direct API calls) that build the request for
ListDefaultConfigs (the _input.name field in
crates/superposition_sdk/src/operation/list_default_configs.rs) to pass name as
an array of strings (e.g., ["testConfigKey"]) instead of a single string so the
generated loop that calls query.push_kv("name", ...) emits one query param per
element.

In `@docs/docs/api/list-grouped-default-configs.api.mdx`:
- Around line 1-12: The front-matter field "api" in
docs/docs/api/list-grouped-default-configs.api.mdx contains a large blob that
Gitleaks flagged as a potential secret; verify whether this is a generated,
non-sensitive artifact and if so move the blob out of source-controlled
front-matter (e.g., into a generated artifacts directory or CI-generated file)
or add it to your approved allowlist for generated payloads, otherwise remove or
rotate the secret and update the "api" front-matter in the
ListGroupedDefaultConfigs doc accordingly.

In `@docs/docs/api/Superposition.openapi.json`:
- Around line 1544-1652: The OpenAPI path key "/default-config?grouped=true" is
invalid because paths must not contain query strings; update the spec by moving
the grouped flag into a query parameter on the existing "/default-config" path
(add a "grouped" query parameter with type boolean to the operation with
operationId "ListGroupedDefaultConfigs" and keep its responses/schema
referencing "ListGroupedDefaultConfigsResponseContent"), or alternatively expose
a separate path like "/default-config/grouped" and keep the operationId and
responses as-is; ensure you remove the "?grouped=true" from the path key so the
spec validates.
🧹 Nitpick comments (12)
crates/frontend/src/pages/default_config_list/filter.rs (3)

24-32: filter_index: Vec::remove panics on out-of-bounds index.

If index >= items.0.len(), items.0.remove(index) will panic at runtime. While this is unlikely in practice (the index comes from pill rendering), a bounds check would be defensive.

🛡️ Optional defensive fix
     fn filter_index<T: Display + FromStr + Clone>(
         items: &Option<CommaSeparatedQParams<T>>,
         index: usize,
     ) -> Option<CommaSeparatedQParams<T>> {
         items.clone().and_then(|mut items| {
-            items.0.remove(index);
+            if index < items.0.len() {
+                items.0.remove(index);
+            }
             (!items.is_empty()).then_some(items)
         })
     }

123-137: No-op on_delete callback allocated on every render cycle.

Since deletable=false, the Callback::new(|_: String| {}) is never invoked. Consider whether GrayPill can accept an Option<Callback> for on_delete to avoid the allocation, or hoist the callback to a let binding outside the closure to create it once.


150-163: Parsing logic is correct; minor simplification possible.

The nested .then(|| ...).flatten() can be simplified. Consider:

♻️ Optional simplification
                         on:change=move |event| {
                             let names = event_target_value(&event);
-                            let names = (!names.is_empty())
-                                .then(|| {
-                                    let name_list: Vec<String> = names
-                                        .split(',')
-                                        .map(|s| s.trim().to_string())
-                                        .filter(|s| !s.is_empty())
-                                        .collect();
-                                    (!name_list.is_empty())
-                                        .then_some(CommaSeparatedQParams(name_list))
-                                })
-                                .flatten();
+                            let name_list: Vec<String> = names
+                                .split(',')
+                                .map(|s| s.trim().to_string())
+                                .filter(|s| !s.is_empty())
+                                .collect();
+                            let names = (!name_list.is_empty())
+                                .then_some(CommaSeparatedQParams(name_list));
                             filters_buffer_rws.update(|filter| filter.set_name(names));
                         }

The outer !names.is_empty() guard is redundant — splitting an empty string by ',' yields [""], which is filtered out by .filter(|s| !s.is_empty()), resulting in an empty Vec and None from then_some.

crates/superposition_types/src/api/default_config.rs (1)

50-69: set_search doesn't manage grouped state, unlike set_name.

set_name internally manages grouped (clears it when prefix is None), but set_search leaves grouped untouched, requiring callers to separately set it. This asymmetry can lead to subtle bugs if set_search is called from a new call-site without also updating grouped. Consider having set_search also manage the grouped field internally for consistency.

♻️ Suggested change
     pub fn set_search(&mut self, search: Option<String>) {
+        self.grouped = search.is_none().then_some(true);
         self.search = search;
         self.name = None;
     }
crates/frontend/src/pages/default_config_list.rs (3)

32-49: Sort direction flips even when switching to a different column.

When clicking a new column header (e.g., switching from Key to CreatedAt), flip() inverts the previous column's direction instead of starting with a default (e.g., ascending). A common UX pattern is to reset to ascending when the sorted column changes, and only flip when re-clicking the same column.

♻️ Suggested change
 fn sort_callback(
     sort_on: SortOn,
     filters_rws: RwSignal<DefaultConfigFilters>,
     pagination_params_rws: RwSignal<PaginationParams>,
 ) -> Callback<()> {
     Callback::new(move |_| {
         let filters = filters_rws.get();
-        let sort_by = filters.sort_by.unwrap_or_default().flip();
+        let sort_by = if filters.sort_on == Some(sort_on) {
+            filters.sort_by.unwrap_or_default().flip()
+        } else {
+            SortBy::default()
+        };
 
         let new_filters = DefaultConfigFilters {
             sort_on: Some(sort_on),
             sort_by: Some(sort_by),
             ..filters
         };
         pagination_params_rws.update(|f| f.reset_page());
         filters_rws.set(new_filters);
     })
 }

248-250: Debug logging left in the search handler.

This logging::log! call fires on every debounced keystroke. Consider removing it or gating it behind a debug-only flag to keep the browser console clean in production.


260-282: Toggling grouping resets all filters, which may surprise users.

Line 266 resets the entire DefaultConfigFilters to default() before toggling grouped. This clears prefix, search, sort_by, sort_on, and name. If a user has navigated into a prefix and then toggles grouping, they lose their prefix context. If this is intentional, a brief comment explaining the rationale would help future readers.

crates/frontend/src/api.rs (1)

464-506: Borrow DefaultConfigFilters to match the list() function signature.

The to_query_param() trait method takes &self (line 19 of crates/superposition_types/src/custom_query.rs), and the function body only reads from the filters parameter—it never requires ownership. Changing the signature to borrow the parameter maintains consistency with the similar list() function (line 438) which borrows filters.

♻️ Suggested signature change
-    pub async fn list_grouped(
-        pagination: &PaginationParams,
-        filters: DefaultConfigFilters,
+    pub async fn list_grouped(
+        pagination: &PaginationParams,
+        filters: &DefaultConfigFilters,
         workspace: &str,
         org_id: &str,
     ) -> Result<PaginatedResponse<ListDefaultConfigResponse>, String> {
crates/context_aware_config/src/api/default_config/helpers.rs (1)

60-74: Avoid consuming the map in get_from_unflattened_map.

Taking ownership forces clones at call sites; a borrowed signature keeps reuse cheap and avoids unnecessary allocations.

♻️ Proposed refactor
-pub(super) fn get_from_unflattened_map(
-    unflattened_map: InternalStructure,
-    flattened_key: &str,
-) -> Option<InternalStructure> {
-    let mut current_value = Some(unflattened_map);
+pub(super) fn get_from_unflattened_map(
+    unflattened_map: &InternalStructure,
+    flattened_key: &str,
+) -> Option<&InternalStructure> {
+    let mut current_value = Some(unflattened_map);
@@
-    for part in flattened_key.split('.') {
-        current_value = current_value.and_then(|v| v.sub_keys.get(part).cloned())
-    }
+    for part in flattened_key.split('.') {
+        current_value = current_value.and_then(|v| v.sub_keys.get(part))
+    }
     current_value
 }

Update tests to pass &unflattened and compare against references.

clients/python/sdk/superposition_sdk/serialize.py (1)

1827-1835: Guard against name being a string.

If callers pass a single string, this will expand into per-character query params. Consider normalizing to a list to preserve backward compatibility (and apply the same in list_grouped_default_configs).

♻️ Proposed guard
-    if input.name is not None:
-        query_params.extend(("name", e) for e in input.name)
+    if input.name is not None:
+        names = [input.name] if isinstance(input.name, str) else input.name
+        query_params.extend(("name", e) for e in names)
crates/context_aware_config/src/api/default_config/handlers.rs (1)

537-539: list_grouped_configs fetches the entire default_configs table into memory.

This loads all rows without any DB-level filtering, then builds the tree and filters/paginates in Rust. While this is inherent to the unflattening design, it becomes a scalability concern for workspaces with a large number of default config keys (high memory use, slow response times).

Consider at least applying the name filter at the DB level (e.g., WHERE key LIKE ANY('{name1.%, name2.%}')) to reduce the initial data set when name filters are provided, even if the tree still needs to be built from the filtered set.

clients/python/sdk/superposition_sdk/models.py (1)

7676-7676: Redundant Union wrapper with | syntax.

Union[A | B | C] mixes two union syntaxes. Either use Union[A, B, C] or just A | B | C. Both work, but combining them is non-idiomatic.

Suggested fix
-GroupedDefaultConfig = Union[GroupedDefaultConfigGroup | GroupedDefaultConfigConfig | GroupedDefaultConfigUnknown]
+GroupedDefaultConfig = GroupedDefaultConfigGroup | GroupedDefaultConfigConfig | GroupedDefaultConfigUnknown

Comment on lines +7493 to +7504
LIST_GROUPED_DEFAULT_CONFIGS = Schema(
id=ShapeID("io.superposition#ListGroupedDefaultConfigs"),
shape_type=ShapeType.OPERATION,
traits=[
Trait.new(id=ShapeID("smithy.api#tags"), value=(
"Default Configuration",
)),
Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({
"method": "GET",
"uri": "/default-config?grouped=true",
})),
Trait.new(id=ShapeID("smithy.api#readonly")),
Copy link
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

🌐 Web query:

Smithy @http trait uri query string parameters best practice

💡 Result:

In Smithy, the best practice is: keep @http(uri: ...) for the path template only, and model query strings using HTTP binding traits on input members.

Recommended patterns

1) Put path params in uri using labels, and bind them with @httpLabel

  • @http(uri: "/things/{thingId}") defines the path pattern.
  • The uri must start with / and must not end with ?. (smithy.io)

2) Model explicit query parameters with @httpQuery("name")

Use this when you know the query keys ahead of time (typed, documented, stable API). Example: color, shape, size. (smithy.io)

3) Use @httpQueryParams only for “bag of query params” maps

Use this when you need arbitrary query keys (e.g., filters/tags), by binding a map<string, string> (or map<string, list<string>>) to the query string. Notes:

  • Only one member in the input can be @httpQueryParams.
  • It’s only considered on top-level input members of an operation.
  • Avoid mixing @httpQueryParams with other @httpQuery members unless you’re sure there’s no key overlap (Smithy even warns this can conflict). (smithy.io)

4) Avoid encoding dynamic query params directly in @http(uri: "...?...")

Smithy allows literal query-string requirements in the uri pattern (e.g., “this key must be present”), but the query portion must not contain labels, so it’s not the right tool for parameter binding. Prefer @httpQuery / @httpQueryParams for actual parameters. (smithy.io)

Quick rule of thumb

  • Stable, documented query keys@httpQuery
  • Arbitrary/dynamic query keys@httpQueryParams
  • Don’t try to “template” query params inside @http(uri: ...) (labels aren’t allowed there). (smithy.io)

Citations:


🌐 Web query:

Smithy httpQuery trait vs embedding query in uri

💡 Result:

In Smithy, you should model query-string parameters using @httpQuery (or @httpQueryParams), not by embedding ?foo={foo} into the @http trait’s uri.

  • @httpQuery is the standard binding for query parameters. It binds an input structure member to a named query-string parameter and Smithy tooling is expected to handle percent-encoding/decoding automatically. [1]
  • @httpQuery only applies to top-level members of an operation input (so it’s unambiguous what comes from the query string). [1]
  • @httpLabel is for path segments only, i.e., values substituted into the @http(uri: "/{label}") path—not the query string. [1]
  • For “arbitrary” query keys/values, use @httpQueryParams to bind a map into the query string. [1]

Practical takeaway: keep @http(uri: ...) as a clean path template, and represent query-string fields with @httpQuery / @httpQueryParams so generators/validators/serializers consistently understand the API shape. [1]

Sources:
[1] Smithy 2.0 Spec – HTTP bindings (traits httpLabel, httpQuery, httpQueryParams, encoding rules, top-level input rules) https://smithy.io/2.0/spec/http-bindings.html


🏁 Script executed:

# Search for Smithy `@http` usage patterns in the codebase
rg "@http|@httpQuery" -A 3 -B 1 | head -100

Repository: juspay/superposition

Length of output: 5737


🏁 Script executed:

# Check the file structure and look for other `@http` definitions
cd clients/python/sdk && rg "uri.*\?" -A 2 -B 2 | head -50

Repository: juspay/superposition

Length of output: 486


Model the grouped query parameter as an @httpQuery member instead of embedding it in the uri.

Smithy's @http uri is a path template and should not include query strings. Per Smithy specification, query parameters must be bound using @httpQuery on input structure members, which ensures proper serialization, percent-encoding, and API clarity. Update the Smithy model to move grouped to a @httpQuery member, change the uri to /default-config, and regenerate the SDK.

Suggested model update
-                "uri": "/default-config?grouped=true",
+                "uri": "/default-config",

Then add @httpQuery("grouped") to the input structure member.

🤖 Prompt for AI Agents
In `@clients/python/sdk/superposition_sdk/_private/schemas.py` around lines 7493 -
7504, The LIST_GROUPED_DEFAULT_CONFIGS operation currently embeds the grouped
query param in the smithy.api#http trait's "uri"
("/default-config?grouped=true"); change the http trait value to use uri
"/default-config" and instead add an `@httpQuery`("grouped") member on the
operation's input structure (so that the grouped flag is modeled as a query
member and serialized/percent-encoded correctly), then regenerate the SDK so the
Schema (LIST_GROUPED_DEFAULT_CONFIGS) and generated client code reflect the new
`@httpQuery` binding rather than a query string in the http URI.

Comment on lines +459 to +466
group_entries.sort_by(|(a_key, a_date), (b_key, b_date)| {
let ord = match (a_date, b_date) {
(Some(a_dt), Some(b_dt)) => a_dt.cmp(b_dt),
(None, Some(_)) => Ordering::Less,
(Some(_), None) => Ordering::Greater,
(None, None) => a_key.cmp(b_key),
};
apply_sort_order(sort_by, ord)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

None dates sort before present dates in ascending order — verify this is intentional.

When a group has no leaf configs (so date_for returns None), (None, Some(_)) => Ordering::Less places it before groups that do have dates in ascending sort. This means "unknown date" groups appear first. The more common convention is to sort None last regardless of direction. Confirm this is the desired UX.

🤖 Prompt for AI Agents
In `@crates/context_aware_config/src/api/default_config/handlers.rs` around lines
459 - 466, The sort currently treats None dates as Less which places "unknown
date" groups first; change the comparator in group_entries.sort_by (the closure
using a_date, b_date and apply_sort_order) so that None is considered greater
than Some (i.e., sorts last) regardless of ascending/descending; update the
match arms involving (None, Some(_)) and (Some(_), None) to return
Ordering::Greater and Ordering::Less respectively (and keep the tie-breaker on
a_key when both None) so apply_sort_order(sort_by, ord) yields the UX of unknown
dates appearing after known dates.

Comment on lines +643 to 648
if let Some(ref config_names) = filters.name {
builder = builder.filter(dsl::key.eq_any(config_names.0.clone()));
} else if let Some(ref search) = filters.search {
let pattern = format!("%{}%", search);
builder = builder.filter(dsl::key.ilike(pattern));
}
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 13, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

LIKE wildcard characters in search are not escaped.

If a user passes % or _ in the search string, these are LIKE pattern wildcards and will produce unintended matches. For example, searching for a_b would match a.b, axb, etc.

🛡️ Proposed fix to escape LIKE metacharacters
         } else if let Some(ref search) = filters.search {
-            let pattern = format!("%{}%", search);
+            let escaped = search.replace('\\', "\\\\").replace('%', "\\%").replace('_', "\\_");
+            let pattern = format!("%{}%", escaped);
             builder = builder.filter(dsl::key.ilike(pattern));
         }
🤖 Prompt for AI Agents
In `@crates/context_aware_config/src/api/default_config/handlers.rs` around lines
643 - 648, The search string used in the ilike pattern isn't escaping SQL LIKE
metacharacters, so '%' or '_' in filters.search will act as wildcards; before
building the pattern in handlers.rs, escape backslashes, '%' and '_' in
filters.search (e.g. replace '\' -> '\\', '%' -> '\%', '_' -> '\_'), then build
the pattern as format!("%{}%", escaped_search) and use that pattern in
builder.filter(dsl::key.ilike(pattern)); ensure the escape sequences are applied
to filters.search (the same variable referenced in the current if-let branch) so
unwanted wildcard matches are prevented.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

suggest a better way to allow substring match only

Copy link
Contributor

Choose a reason for hiding this comment

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

@ayushjain17 Here are better approaches for substring-only matching:

Recommended: Escape LIKE Wildcards with a Helper Function

The most practical approach is to escape LIKE metacharacters, but wrap it in a reusable helper:

// Add this helper function near the top of the file or in a utilities module
fn escape_like_pattern(input: &str) -> String {
    input
        .replace('\\', "\\\\")  // Escape backslash first
        .replace('%', "\\%")     // Escape percent
        .replace('_', "\\_")     // Escape underscore
}

// Then use it in your query:
} else if let Some(ref search) = filters.search {
    let escaped = escape_like_pattern(search);
    let pattern = format!("%{}%", escaped);
    builder = builder.filter(dsl::key.ilike(pattern));
}

Pros:

  • Simple and maintainable
  • Works with existing Diesel query builder
  • Reusable across codebase
  • No performance overhead
  • Familiar SQL LIKE semantics

Alternative: Use PostgreSQL position() Function

For a wildcard-free approach:

use diesel::dsl::sql;
use diesel::sql_types::Bool;

} else if let Some(ref search) = filters.search {
    // Escape single quotes for SQL string literal
    let safe_search = search.replace('\'', "''");
    let search_lower = safe_search.to_lowercase();
    
    builder = builder.filter(
        sql::<Bool>(&format!(
            "position('{}' in lower(key)) > 0",
            search_lower
        ))
    );
}

Pros:

  • No wildcard confusion
  • Explicit substring search

Cons:

  • Requires raw SQL (less type-safe)
  • Still need to escape SQL string delimiters (single quotes)

Alternative: Use Diesel's .contains() (if available)

Check if Diesel supports a contains helper for your column type - this would be the cleanest:

} else if let Some(ref search) = filters.search {
    builder = builder.filter(dsl::key.contains(search));
}

Note: This may not be available for all column types or may require additional traits.


My recommendation: Use the escaped LIKE pattern with a helper function (first option). It's the most maintainable, works well with Diesel's query builder, and clearly communicates intent without requiring raw SQL.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

should I support LIKE wildcards ?

Copy link
Contributor

Choose a reason for hiding this comment

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

@ayushjain17 I recommend NOT supporting LIKE wildcards by default. Here's why:

Reasons to Escape Wildcards (Simple Substring Search)

👍 Better for most users:

  • Users searching for a_b expect to find literally a_b, not axb or a.b
  • More intuitive behavior (matches Google, VS Code search, etc.)
  • No learning curve - just type what you want to find

👍 Safer:

  • Prevents accidental broad matches (e.g., searching a% matching everything starting with a)
  • Avoids performance issues from unintended leading wildcards

👍 Predictable:

  • Config keys are structured (e.g., app.feature.setting)
  • Substring search is sufficient for most use cases

When Wildcards Might Be Useful

⚠️ Limited benefits:

  • Power users wanting pattern-based searches
  • But regex or structured filters (prefix, name array) already provide this

⚠️ Downsides:

  • Adds complexity - users need to know about %, _, and escaping
  • Could confuse non-technical users
  • % at the start of patterns prevents index usage (slow on large datasets)

My Recommendation

Use escaped substring search (option 1 from my previous message):

fn escape_like_pattern(input: &str) -> String {
    input
        .replace('\\', "\\\\")
        .replace('%', "\\%")
        .replace('_', "\\_")
}

// In query:
let escaped = escape_like_pattern(search);
let pattern = format!("%{}%", escaped);
builder = builder.filter(dsl::key.ilike(pattern));

Why: Simple, safe, and matches user expectations. You already have prefix and name filters for structured searches, so search can focus on simple substring matching.

Future: If power users request wildcard support later, you could add a separate pattern parameter or a flag like enable_wildcards=true.

Comment on lines 101 to 112
let redirect_url = move |prefix: Option<String>| -> String {
let get_updated_query = use_update_url_query();
get_updated_query("prefix", prefix)
let page_params = pagination_params_rws.get();

get_updated_query(&[
("prefix", prefix),
("name", None),
("page", page_params.page.map(|_| "1".to_string())),
("count", page_params.count.map(|c| c.to_string())),
("all", page_params.all.map(|a| a.to_string())),
])
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

use_update_url_query() is invoked inside a regular closure, not at component top-level.

In Leptos, use_* hooks are conventionally called during component initialization to ensure they have access to the correct reactive owner/scope. Calling use_update_url_query() lazily inside redirect_url (which is invoked from the view) may work if it only reads router context, but it's fragile and could break if the hook's internals change.

Consider hoisting the call:

♻️ Suggested change
+    let get_updated_query = use_update_url_query();
     let redirect_url = move |prefix: Option<String>| -> String {
-        let get_updated_query = use_update_url_query();
         let page_params = pagination_params_rws.get();
 
         get_updated_query(&[
🤖 Prompt for AI Agents
In `@crates/frontend/src/pages/default_config_list.rs` around lines 101 - 112, The
closure redirect_url currently calls use_update_url_query() inside the closure
body which violates Leptos' hook call timing; hoist the call to
use_update_url_query() to component initialization (top-level of the component
function) and store its returned function/handle in a variable (e.g.,
update_query_fn), then have redirect_url capture and call that variable along
with pagination_params_rws.get() and the provided prefix; this ensures
use_update_url_query() is invoked during component setup rather than lazily
inside redirect_url.

Comment on lines +1544 to +1652
"/default-config?grouped=true": {
"get": {
"description": "Retrieves a paginated list of all default config entries in the workspace, including their values, schemas, and metadata.",
"operationId": "ListGroupedDefaultConfigs",
"parameters": [
{
"name": "count",
"in": "query",
"description": "Number of items to be returned in each page.",
"schema": {
"type": "number",
"description": "Number of items to be returned in each page."
}
},
{
"name": "page",
"in": "query",
"description": "Page number to retrieve, starting from 1.",
"schema": {
"type": "number",
"description": "Page number to retrieve, starting from 1."
}
},
{
"name": "all",
"in": "query",
"description": "If true, returns all requested items, ignoring pagination parameters page and count.",
"schema": {
"type": "boolean",
"description": "If true, returns all requested items, ignoring pagination parameters page and count."
}
},
{
"name": "name",
"in": "query",
"style": "form",
"schema": {
"type": "array",
"items": {
"type": "string"
}
},
"explode": true
},
{
"name": "prefix",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "sort_by",
"in": "query",
"description": "Sort order enumeration for list operations.",
"schema": {
"$ref": "#/components/schemas/SortBy"
}
},
{
"name": "sort_on",
"in": "query",
"schema": {
"$ref": "#/components/schemas/DefaultConfigSortOn"
}
},
{
"name": "x-org-id",
"in": "header",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "x-workspace",
"in": "header",
"schema": {
"type": "string"
},
"required": true
}
],
"responses": {
"200": {
"description": "ListGroupedDefaultConfigs 200 response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ListGroupedDefaultConfigsResponseContent"
}
}
}
},
"500": {
"description": "InternalServerError 500 response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InternalServerErrorResponseContent"
}
}
}
}
},
"tags": [
"Default Configuration"
]
}
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 13, 2026

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
# Identify OpenAPI paths that include a query string (invalid per OpenAPI).
python - <<'PY'
import json
from pathlib import Path

spec = json.loads(Path("docs/docs/api/Superposition.openapi.json").read_text())
bad = [p for p in spec.get("paths", {}) if "?" in p]
print("Paths containing '?':", bad)
PY

Repository: juspay/superposition

Length of output: 118


OpenAPI paths must not include query strings.

Line 1544 defines "/default-config?grouped=true" as a path key. OpenAPI path items cannot contain a query string, so this spec will fail validation and most SDK generators. Model grouping as a query parameter on /default-config or expose a dedicated path like /default-config/grouped if the backend uses a separate route.

🤖 Prompt for AI Agents
In `@docs/docs/api/Superposition.openapi.json` around lines 1544 - 1652, The
OpenAPI path key "/default-config?grouped=true" is invalid because paths must
not contain query strings; update the spec by moving the grouped flag into a
query parameter on the existing "/default-config" path (add a "grouped" query
parameter with type boolean to the operation with operationId
"ListGroupedDefaultConfigs" and keep its responses/schema referencing
"ListGroupedDefaultConfigsResponseContent"), or alternatively expose a separate
path like "/default-config/grouped" and keep the operationId and responses
as-is; ensure you remove the "?grouped=true" from the path key so the spec
validates.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

smithy based generators worked pretty fine with it, can you please verify the generation for all the languages, I have verified for typescript and haskell

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

smithy rust also works fine

Copy link
Contributor

Choose a reason for hiding this comment

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

Oops, something went wrong! Please try again later. 🐰 💔

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

even for java as well as python it is fine

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== OpenAPI: detect invalid path keys containing '?' =="
python - <<'PY'
import json, sys
from pathlib import Path
p = Path("docs/docs/api/Superposition.openapi.json")
data = json.loads(p.read_text())
bad = [k for k in data.get("paths", {}) if "?" in k]
print("Paths containing '?':", bad)
PY

echo
echo "== Java: presence of operation and wiring =="
rg -nP --glob='clients/java/sdk/**' -C2 'listGroupedDefaultConfigs\s*\(' || true
echo
echo "-- SuperpositionClient.java declarations (context) --"
rg -n --glob='clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java' -C3 'listGroupedDefaultConfigs' || true
echo
echo "-- Model class exists --"
rg -n --glob='clients/java/sdk/src/main/java/io/juspay/superposition/model/**' 'class ListGroupedDefaultConfigs\b|class ListGroupedDefaultConfigsInput\b|class ListGroupedDefaultConfigsOutput\b' || true

echo
echo "== Python: client method + serde hooks =="
rg -n --glob='clients/python/sdk/**' -C2 'def list_grouped_default_configs\(|_serialize_list_grouped_default_configs|_deserialize_list_grouped_default_configs' || true

echo
echo "== JS/TS: command, client, protocol serde =="
rg -n --glob='clients/javascript/sdk/**' -C2 'class ListGroupedDefaultConfigsCommand|se_ListGroupedDefaultConfigsCommand|de_ListGroupedDefaultConfigsCommand|listGroupedDefaultConfigs\(' || true

echo
echo "== Rust: client method, builders, error type =="
rg -n --glob='crates/superposition_sdk/**' -C2 'pub fn list_grouped_default_configs\(|ListGroupedDefaultConfigsFluentBuilder|ListGroupedDefaultConfigsError' || true

echo
echo "== Haskell: command module + function =="
rg -n --glob='clients/haskell/**' -C2 'module Io\.Superposition\.Command\.ListGroupedDefaultConfigs|listGroupedDefaultConfigs\s*::' || true

echo
echo "== Tests referencing the new command (sanity) =="
rg -n --glob='tests/**' -C2 'ListGroupedDefaultConfigsCommand' || true

Length of output: 46915


@ayushjain17 Thanks — I double‑checked across the repo, and Smithy/Rust (and the other SDKs) look good.

What I verified:

  • OpenAPI doc: still has a non‑compliant path key with a query string Paths containing '?': ['/default-config?grouped=true'] (docs/docs/api/Superposition.openapi.json, Line 1544).
  • Java: SuperpositionAsyncClient, SuperpositionClient, and SuperpositionClientImpl all expose listGroupedDefaultConfigs(...), and the operation/input/output model classes exist.
  • Python: list_grouped_default_configs(...) is wired; serializer builds GET /default-config with grouped=true; deserializer present.
  • JS/TS: ListGroupedDefaultConfigsCommand + se_/de_ functions and client overloads present.
  • Rust: client method, fluent builder, error type, and serde are in place.
  • Haskell: Io.Superposition.Command.ListGroupedDefaultConfigs module exports listGroupedDefaultConfigs.
  • Tests: default_config.test.ts exercises ListGroupedDefaultConfigsCommand.

Net: generation is in place for Java, Python, JS/TS, Rust, and Haskell.

Optional doc-only follow-up to make the OpenAPI spec validator-friendly (doesn’t affect Smithy or the SDKs):

  • Replace the path key "/default-config?grouped=true" with "/default-config" and add a boolean query param grouped. To keep both shapes in one operation, set 200.response.content.schema.data.items to oneOf [DefaultConfigResponse, GroupedDefaultConfig], or
  • If you prefer separate operations in OAS, expose a docs alias like "/default-config/grouped" (only if/when the backend supports it).

Want me to open a small follow-up issue to track the OpenAPI doc fix?

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "==> OpenAPI paths containing '?' (should be empty):"
python - <<'PY'
import json
from pathlib import Path
p = Path("docs/docs/api/Superposition.openapi.json")
d = json.loads(p.read_text())
bad = [k for k in d.get("paths", {}) if "?" in k]
print(bad)
if "/default-config?grouped=true" in d.get("paths", {}):
    op = d["paths"]["/default-config?grouped=true"].get("get", {})
    print("operationId at /default-config?grouped=true:", op.get("operationId"))
PY

echo
echo "==> Where is '?grouped=true' referenced?"
rg -n --hidden -S '\?grouped=true' || true

echo
echo "==> Files mentioning 'ListGroupedDefaultConfigs' (count and by extension):"
count=$(rg -l 'ListGroupedDefaultConfigs' | wc -l | tr -d ' ')
echo "Total files: $count"
echo "By extension:"
rg -l 'ListGroupedDefaultConfigs' | awk -F. '{print $NF}' | sed 's/[^a-zA-Z0-9].*$//' | sort | uniq -c | sort -nr

echo
echo "==> Sample files:"
rg -l 'ListGroupedDefaultConfigs' | sed -n '1,20p'

Length of output: 2042


@coderabbit-user Thanks for confirming. I ran a quick repo-wide check:

  • Generation is present across languages: 37 files mention ListGroupedDefaultConfigs (9 rs, 8 ts, 7 java, 6 py, 3 hs, plus smithy/mdx/json). Generation “works” in all SDKs you listed.
  • The only problematic spot is the OpenAPI doc: docs/docs/api/Superposition.openapi.json defines a path key "/default-config?grouped=true". OpenAPI 3.x forbids query strings in path keys; queries must be modeled as parameters. This will fail standard OpenAPI validators and some doc tooling, even if Smithy-based SDKs generate fine.

Proposed minimal, spec-safe doc fix (no server/code changes):

  • Remove the "/default-config?grouped=true" path object.
  • Add a boolean grouped query parameter to GET /default-config (and optionally prefix with a note “used when grouped=true”).
  • Make the 200 response use oneOf to document both shapes.

Example patch (excerpt):

{
  "paths": {
    "/default-config": {
      "get": {
        "parameters": [
          { "name": "count", "in": "query", "schema": { "type": "number" } },
          { "name": "page", "in": "query", "schema": { "type": "number" } },
          { "name": "all", "in": "query", "schema": { "type": "boolean" } },
          { "name": "name", "in": "query", "style": "form", "explode": true,
            "schema": { "type": "array", "items": { "type": "string" } } },
          { "name": "sort_by", "in": "query", "schema": { "$ref": "#/components/schemas/SortBy" } },
          { "name": "sort_on", "in": "query", "schema": { "$ref": "#/components/schemas/DefaultConfigSortOn" } },
          { "name": "search", "in": "query", "schema": { "type": "string" } },

          { "name": "grouped", "in": "query",
            "description": "If true, returns grouped default configs.",
            "schema": { "type": "boolean" } },

          { "name": "prefix", "in": "query",
            "description": "Only applicable when grouped=true.",
            "schema": { "type": "string" } },

          { "name": "x-org-id", "in": "header", "required": true, "schema": { "type": "string" } },
          { "name": "x-workspace", "in": "header", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "List default configs (grouped or ungrouped)",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/ListDefaultConfigsResponseContent" },
                    { "$ref": "#/components/schemas/ListGroupedDefaultConfigsResponseContent" }
                  ]
                }
              }
            }
          }
        }
      }
    }
    /* remove the "/default-config?grouped=true" entry */
  }
}

And in docs/docs/api/list-grouped-default-configs.api.mdx, switch the endpoint display to:

  • path={"/default-config"}
  • Show grouped=true in the request params section.

If you’d like, I can prep a small follow-up to make these doc-only adjustments and run an OpenAPI validation pass. Would you prefer I:

  • update this PR with the doc fix, or
  • open a separate “Docs: make ListGroupedDefaultConfigs OpenAPI-compliant” issue/PR?

@ayushjain17 ayushjain17 force-pushed the feat/default-config-filter-2 branch from dfe2db1 to e3ff1a1 Compare February 13, 2026 16:32
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.

1 participant