Skip to content

Match API response format to chrome service#26

Merged
Hyperkid123 merged 2 commits intoRedHatInsights:masterfrom
Hyperkid123:read-feo-widgets
Aug 5, 2025
Merged

Match API response format to chrome service#26
Hyperkid123 merged 2 commits intoRedHatInsights:masterfrom
Hyperkid123:read-feo-widgets

Conversation

@Hyperkid123
Copy link
Copy Markdown
Contributor

@Hyperkid123 Hyperkid123 commented Aug 4, 2025

This will ensure easy frontend migration.

Migration example is in this draft PR: RedHatInsights/widget-layout#180

Summary by Sourcery

Align list endpoints with Chrome service API by enveloping responses in a consistent data/meta format, adding dashboardType filtering with auto-creation behavior, and updating service, server, specs, tests, and documentation accordingly.

New Features:

  • Support filtering user dashboard templates via a dashboardType query parameter.
  • Automatically fork and return a base dashboard template when a user has none matching a requested type.

Enhancements:

  • Wrap dashboard templates, base templates, and widget mappings in a data/meta list response envelope.
  • Extend service.GetUserTemplates and server handlers to accept filtering parameters and return appropriate HTTP status codes.
  • Introduce a Permission schema and include permissions in widget mapping responses.
  • Update OpenAPI spec with new query parameters, response schemas, and list formats.

Documentation:

  • Document the new list response format, dashboardType filtering, and auto-creation behavior in API.md.
  • Add a widget migration guide (docs/WIDGET_MIGRATION.md) for updating widget IDs and CSS selectors.

Tests:

  • Adapt existing tests and add new ones to validate the data/meta envelope, filtering logic, and auto-creation flow for dashboard templates.
  • Update server and service tests to assert the new response structures for base templates and widget mappings.

@Hyperkid123 Hyperkid123 requested a review from a team August 4, 2025 11:24
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Aug 4, 2025

Reviewer's Guide

This PR aligns API list responses with the Chrome service format by wrapping list endpoints in a data/meta structure, implements dashboardType filtering with auto-creation of missing templates, updates OpenAPI and server handlers accordingly, introduces a Permission schema for widget mappings, and adds a detailed widget migration guide.

Sequence diagram for GET / with dashboardType filtering and auto-creation

sequenceDiagram
    actor User
    participant API as API Server
    participant Service as Service Layer
    participant DB as Database

    User->>API: GET /?dashboardType=typeX
    API->>Service: GetUserTemplates(userId, dashboardType=typeX)
    Service->>DB: Query templates for userId and typeX
    alt No templates found
        Service->>Service: ForkBaseTemplate(typeX, userId)
        Service->>Service: ChangeDefaultTemplate(newTemplateId, userId)
        Service-->>API: [newTemplate], 404
    else Templates found
        Service-->>API: [templates], 200
    end
    API->>User: { data: [...], meta: { count }, status }
Loading

Class diagram for updated API response types

classDiagram
    class DashboardTemplateListResponse {
        +DashboardTemplate[] Data
        +ListResponseMeta Meta
    }
    class ListResponseMeta {
        +int Count
    }
    class BaseWidgetDashboardTemplateListResponse {
        +BaseWidgetDashboardTemplate[] Data
        +ListResponseMeta Meta
    }
    class WidgetMappingResponse {
        +map<string, WidgetModuleFederationMetadata> Data
    }
    DashboardTemplateListResponse --> ListResponseMeta
    BaseWidgetDashboardTemplateListResponse --> ListResponseMeta
    WidgetMappingResponse --> WidgetModuleFederationMetadata
Loading

File-Level Changes

Change Details Files
Unified list response format for all list endpoints
  • Define new list response types with data and meta fields
  • Wrap template, base template, and widget mapping handlers in data/meta wrappers
  • Update encoding logic to use the returned status code when auto-creating
pkg/server/server.go
api/WidgetMapping.go
spec/openapi.yaml
pkg/server/get_widgets_test.go
pkg/server/get_base_templates_test.go
pkg/server/get_widget_mapping_test.go
docs/API.md
Add dashboardType filter and auto-create behavior to GetUserTemplates
  • Extend GetUserTemplates signature to accept filter params
  • Apply SQL filter on TemplateBase.Name when dashboardType is provided
  • Fork and set default template from registry when none exist, returning 404
pkg/service/DashboardTemplate.go
pkg/service/DashboardTemplate_test.go
pkg/server/server.go
spec/openapi.yaml
docs/API.md
pkg/server/get_widgets_test.go
Enhance OpenAPI spec with new list schemas and query parameters
  • Add ListResponseMeta and list response schemas for templates and base templates
  • Reference DashboardTemplateListResponse and BaseWidgetDashboardTemplateListResponse
  • Introduce optional dashboardType query parameter under getWidgetLayout
spec/openapi.yaml
Introduce Permission schema and update widget mapping API
  • Add Permission type with method and args fields
  • Switch widget mapping response to use Permission objects
  • Define WidgetMappingResponse wrapper for mappings
api/WidgetMapping.go
pkg/service/WidgetMapping_test.go
spec/openapi.yaml
Add comprehensive widget migration documentation
  • Provide ID mapping table from Chrome service to FEO identifiers
  • Outline CSS selector migration best practices and escape rules
  • Include phased migration checklist
docs/WIDGET_MIGRATION.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey @Hyperkid123 - I've reviewed your changes - here's some feedback:

  • Consider extracting the common list‐response wrapping logic (i.e. building {data, meta}) into a shared helper to avoid duplication across GetWidgetLayout, GetBaseWidgetDashboardTemplates, and GetWidgetMapping.
  • The service now returns 404 when auto‐creating a template—please double‐check that this non-standard use of 404 aligns with your API design and consider returning 201 or 200 with a flag instead to prevent client confusion.
  • Tests write directly to the shared database without explicit cleanup; consider wrapping each test in a transaction or truncating relevant tables to guarantee isolation and avoid flaky failures.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider extracting the common list‐response wrapping logic (i.e. building {data, meta}) into a shared helper to avoid duplication across GetWidgetLayout, GetBaseWidgetDashboardTemplates, and GetWidgetMapping.
- The service now returns 404 when auto‐creating a template—please double‐check that this non-standard use of 404 aligns with your API design and consider returning 201 or 200 with a flag instead to prevent client confusion.
- Tests write directly to the shared database without explicit cleanup; consider wrapping each test in a transaction or truncating relevant tables to guarantee isolation and avoid flaky failures.

## Individual Comments

### Comment 1
<location> `pkg/service/DashboardTemplate.go:46` </location>
<code_context>
+func GetUserTemplates(id identity.XRHID, params api.GetWidgetLayoutParams) ([]api.DashboardTemplate, int, error) {
 	var templates []api.DashboardTemplate
-	err := database.DB.Where(api.DashboardTemplate{UserId: id.Identity.User.UserID}).Find(&templates).Error
+	where := api.DashboardTemplate{UserId: id.Identity.User.UserID}
+	if params.DashboardType != nil {
+		where.TemplateBase.Name = *params.DashboardType
+	}
</code_context>

<issue_to_address>
Potentially incorrect use of struct for dynamic filtering.

Struct-based Where may not filter nested fields like TemplateBase.Name. Use map[string]interface{} or chain Where calls for accurate filtering.
</issue_to_address>

### Comment 2
<location> `pkg/service/DashboardTemplate.go:44` </location>
<code_context>
 }

-func GetUserTemplates(id identity.XRHID) ([]api.DashboardTemplate, int, error) {
+func GetUserTemplates(id identity.XRHID, params api.GetWidgetLayoutParams) ([]api.DashboardTemplate, int, error) {
 	var templates []api.DashboardTemplate
-	err := database.DB.Where(api.DashboardTemplate{UserId: id.Identity.User.UserID}).Find(&templates).Error
</code_context>

<issue_to_address>
Consider extracting the 'fork and set default' logic into a helper function to keep GetUserTemplates focused on querying.

Here’s one way to flatten out `GetUserTemplates` by moving the “fork & default” path into its own helper.  You don’t change any behavior, you just keep the main function focused on querying:

```go
// new helper for the "no rows + type" case
func getOrCreateTemplatesForType(
  userID string,
  dashboardType string,
) ([]api.DashboardTemplate, int, error) {
  // fork
  tmpl, status, err := ForkBaseTemplate(dashboardType, id)
  if err != nil {
    logrus.Errorf("fork failed for %s/%s: %v", userID, dashboardType, err)
    return nil, status, err
  }

  // set default
  tmpl, status, err = ChangeDefaultTemplate(int64(tmpl.ID), id)
  if err != nil {
    logrus.Errorf("set-default failed for %s/%s: %v", userID, dashboardType, err)
    return nil, status, err
  }

  return []api.DashboardTemplate{tmpl}, http.StatusNotFound, nil
}
```

Then simplify `GetUserTemplates`:

```go
func GetUserTemplates(id identity.XRHID, params api.GetWidgetLayoutParams) ([]api.DashboardTemplate, int, error) {
  var templates []api.DashboardTemplate
  where := api.DashboardTemplate{UserId: id.Identity.User.UserID}
  if params.DashboardType != nil {
    where.TemplateBase.Name = *params.DashboardType
  }

  res := database.DB.Where(where).Find(&templates)

  // delegate the zero‐row & type‐provided case
  if res.Error == nil && res.RowsAffected == 0 && params.DashboardType != nil {
    return getOrCreateTemplatesForType(id.Identity.User.UserID, *params.DashboardType)
  }

  if _, status, err := handleServiceError(
    res.Error,
    fmt.Sprintf("No dashboard templates found for user %s", id.Identity.User.UserID),
    "Failed to retrieve dashboard templates for user %s: %v", http.StatusNotFound,
    nil, []api.DashboardTemplate{},
  ); err != nil {
    return nil, status, err
  }

  return templates, http.StatusOK, nil
}
```

This pulls the multi-step “fork & default” logic out of the main flow and reduces the cyclomatic branches in `GetUserTemplates`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +46 to +47
where := api.DashboardTemplate{UserId: id.Identity.User.UserID}
if params.DashboardType != nil {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Potentially incorrect use of struct for dynamic filtering.

Struct-based Where may not filter nested fields like TemplateBase.Name. Use map[string]interface{} or chain Where calls for accurate filtering.

}

func GetUserTemplates(id identity.XRHID) ([]api.DashboardTemplate, int, error) {
func GetUserTemplates(id identity.XRHID, params api.GetWidgetLayoutParams) ([]api.DashboardTemplate, int, error) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (complexity): Consider extracting the 'fork and set default' logic into a helper function to keep GetUserTemplates focused on querying.

Here’s one way to flatten out GetUserTemplates by moving the “fork & default” path into its own helper. You don’t change any behavior, you just keep the main function focused on querying:

// new helper for the "no rows + type" case
func getOrCreateTemplatesForType(
  userID string,
  dashboardType string,
) ([]api.DashboardTemplate, int, error) {
  // fork
  tmpl, status, err := ForkBaseTemplate(dashboardType, id)
  if err != nil {
    logrus.Errorf("fork failed for %s/%s: %v", userID, dashboardType, err)
    return nil, status, err
  }

  // set default
  tmpl, status, err = ChangeDefaultTemplate(int64(tmpl.ID), id)
  if err != nil {
    logrus.Errorf("set-default failed for %s/%s: %v", userID, dashboardType, err)
    return nil, status, err
  }

  return []api.DashboardTemplate{tmpl}, http.StatusNotFound, nil
}

Then simplify GetUserTemplates:

func GetUserTemplates(id identity.XRHID, params api.GetWidgetLayoutParams) ([]api.DashboardTemplate, int, error) {
  var templates []api.DashboardTemplate
  where := api.DashboardTemplate{UserId: id.Identity.User.UserID}
  if params.DashboardType != nil {
    where.TemplateBase.Name = *params.DashboardType
  }

  res := database.DB.Where(where).Find(&templates)

  // delegate the zero‐row & type‐provided case
  if res.Error == nil && res.RowsAffected == 0 && params.DashboardType != nil {
    return getOrCreateTemplatesForType(id.Identity.User.UserID, *params.DashboardType)
  }

  if _, status, err := handleServiceError(
    res.Error,
    fmt.Sprintf("No dashboard templates found for user %s", id.Identity.User.UserID),
    "Failed to retrieve dashboard templates for user %s: %v", http.StatusNotFound,
    nil, []api.DashboardTemplate{},
  ); err != nil {
    return nil, status, err
  }

  return templates, http.StatusOK, nil
}

This pulls the multi-step “fork & default” logic out of the main flow and reduces the cyclomatic branches in GetUserTemplates.

Copy link
Copy Markdown
Contributor

@justinorringer justinorringer left a comment

Choose a reason for hiding this comment

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

Looking good!

@Hyperkid123
Copy link
Copy Markdown
Contributor Author

The tekton job will not work until we are onboarded

@Hyperkid123 Hyperkid123 merged commit 28a7e48 into RedHatInsights:master Aug 5, 2025
2 of 3 checks passed
@Hyperkid123 Hyperkid123 deleted the read-feo-widgets branch August 5, 2025 09:33
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