Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ PGSQL_DATABASE=widget-layout # notsecret

TEST_MODE=false

BASE_LAYOUTS='[{"name":"landing-landingPage","displayName":"LandingPage","templateConfig":{"sm":[{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":0,"i":"rhel#rhel"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":1,"i":"openshift#openshift"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":2,"i":"ansible#ansible"},{"w":1,"h":5,"maxH":10,"minH":1,"cx":0,"cy":3,"i":"recentlyVisited#recentlyVisited"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":4,"i":"favoriteServices#favoriteServices"},{"w":1,"h":6,"maxH":10,"minH":1,"cx":0,"cy":5,"i":"subscriptions#subscriptions"},{"w":1,"h":13,"maxH":13,"minH":1,"cx":0,"cy":6,"i":"exploreCapabilities#exploreCapabilities"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":7,"i":"openshiftAi#openshiftAi"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":8,"i":"imageBuilder#imageBuilder"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":9,"i":"acs#acs"}],"md":[{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":0,"i":"rhel#rhel"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":1,"i":"openshift#openshift"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":2,"i":"ansible#ansible"},{"w":1,"h":3,"maxH":10,"minH":1,"cx":1,"cy":0,"i":"recentlyVisited#recentlyVisited"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":1,"i":"favoriteServices#favoriteServices"},{"w":1,"h":5,"maxH":10,"minH":1,"cx":1,"cy":2,"i":"subscriptions#subscriptions"},{"w":2,"h":6,"maxH":10,"minH":1,"cx":0,"cy":3,"i":"exploreCapabilities#exploreCapabilities"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":4,"i":"imageBuilder#imageBuilder"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":4,"i":"openshiftAi#openshiftAi"},{"w":2,"h":3,"maxH":10,"minH":1,"cx":1,"cy":3,"i":"acs#acs"}],"lg":[{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":0,"i":"rhel#rhel"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":0,"i":"openshift#openshift"},{"w":2,"h":4,"maxH":10,"minH":1,"cx":0,"cy":1,"i":"ansible#ansible"},{"w":2,"h":6,"maxH":10,"minH":1,"cx":0,"cy":4,"i":"exploreCapabilities#exploreCapabilities"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":2,"cy":0,"i":"recentlyVisited#recentlyVisited"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":2,"cy":2,"i":"favoriteServices#favoriteServices"},{"w":1,"h":6,"maxH":10,"minH":1,"cx":2,"cy":3,"i":"subscriptions#subscriptions"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":4,"i":"openshiftAi#openshiftAi"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":4,"i":"imageBuilder#imageBuilder"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":2,"cy":4,"i":"acs#acs"}],"xl":[{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":0,"i":"rhel#rhel"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":0,"i":"openshift#openshift"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":2,"cy":0,"i":"ansible#ansible"},{"w":3,"h":6,"maxH":10,"minH":1,"cx":0,"cy":2,"i":"exploreCapabilities#exploreCapabilities"},{"w":1,"h":3,"maxH":10,"minH":1,"cx":3,"cy":0,"i":"recentlyVisited#recentlyVisited"},{"w":1,"h":5,"maxH":10,"minH":1,"cx":3,"cy":1,"i":"favoriteServices#favoriteServices"},{"w":1,"h":6,"maxH":10,"minH":1,"cx":3,"cy":2,"i":"subscriptions#subscriptions"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":3,"i":"openshiftAi#openshiftAi"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":3,"i":"imageBuilder#imageBuilder"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":2,"cy":3,"i":"acs#acs"}]},"frontendRef":"landing"}]'
WIDGET_MAPPING='[{"scope":"widgets","module":"./RandomWidget","defaults":{"w":1,"h":1,"maxH":1,"minH":1},"config":{"title":"Random Widget","icon":"CogIcon","headerLink":{"title":"","href":""}},"frontendRef":"widgets"}]'
BASE_LAYOUTS='[{"name":"landing-landingPage","displayName":"LandingPage","templateConfig":{"sm":[{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":0,"i":"landing-./RhelWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":1,"i":"landing-./OpenShiftWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":2,"i":"landing-./AnsibleWidget"},{"w":1,"h":5,"maxH":10,"minH":1,"cx":0,"cy":3,"i":"landing-./RecentlyVisited"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":4,"i":"chrome-./DashboardFavorites"},{"w":1,"h":6,"maxH":10,"minH":1,"cx":0,"cy":5,"i":"subscriptionInventory-./SubscriptionsWidget"},{"w":1,"h":13,"maxH":13,"minH":1,"cx":0,"cy":6,"i":"landing-./ExploreCapabilities"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":7,"i":"landing-./OpenShiftAiWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":8,"i":"landing-./ImageBuilderWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":9,"i":"landing-./AcsWidget"}],"md":[{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":0,"i":"landing-./RhelWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":1,"i":"landing-./OpenShiftWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":2,"i":"landing-./AnsibleWidget"},{"w":1,"h":3,"maxH":10,"minH":1,"cx":1,"cy":0,"i":"landing-./RecentlyVisited"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":1,"i":"chrome-./DashboardFavorites"},{"w":1,"h":5,"maxH":10,"minH":1,"cx":1,"cy":2,"i":"subscriptionInventory-./SubscriptionsWidget"},{"w":2,"h":6,"maxH":10,"minH":1,"cx":0,"cy":3,"i":"landing-./ExploreCapabilities"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":4,"i":"landing-./ImageBuilderWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":4,"i":"landing-./OpenShiftAiWidget"},{"w":2,"h":3,"maxH":10,"minH":1,"cx":1,"cy":3,"i":"landing-./AcsWidget"}],"lg":[{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":0,"i":"landing-./RhelWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":0,"i":"landing-./OpenShiftWidget"},{"w":2,"h":4,"maxH":10,"minH":1,"cx":0,"cy":1,"i":"landing-./AnsibleWidget"},{"w":2,"h":6,"maxH":10,"minH":1,"cx":0,"cy":4,"i":"landing-./ExploreCapabilities"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":2,"cy":0,"i":"landing-./RecentlyVisited"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":2,"cy":2,"i":"chrome-./DashboardFavorites"},{"w":1,"h":6,"maxH":10,"minH":1,"cx":2,"cy":3,"i":"subscriptionInventory-./SubscriptionsWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":4,"i":"landing-./OpenShiftAiWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":4,"i":"landing-./ImageBuilderWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":2,"cy":4,"i":"landing-./AcsWidget"}],"xl":[{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":0,"i":"landing-./RhelWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":0,"i":"landing-./OpenShiftWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":2,"cy":0,"i":"landing-./AnsibleWidget"},{"w":3,"h":6,"maxH":10,"minH":1,"cx":0,"cy":2,"i":"landing-./ExploreCapabilities"},{"w":1,"h":3,"maxH":10,"minH":1,"cx":3,"cy":0,"i":"landing-./RecentlyVisited"},{"w":1,"h":5,"maxH":10,"minH":1,"cx":3,"cy":1,"i":"chrome-./DashboardFavorites"},{"w":1,"h":6,"maxH":10,"minH":1,"cx":3,"cy":2,"i":"subscriptionInventory-./SubscriptionsWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":0,"cy":3,"i":"landing-./OpenShiftAiWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":1,"cy":3,"i":"landing-./ImageBuilderWidget"},{"w":1,"h":4,"maxH":10,"minH":1,"cx":2,"cy":3,"i":"landing-./AcsWidget"}]},"frontendRef":"landing"}]'
WIDGET_MAPPING='[{"scope":"landing","module":"./AcsWidget","defaults":{"w":1,"h":4,"maxH":10,"minH":1},"config":{"title":"Advanced Cluster Security","icon":"ACSIcon","headerLink":{}},"frontendRef":"acs"},{"scope":"landing","module":"./AnsibleWidget","defaults":{"w":1,"h":4,"maxH":10,"minH":1},"config":{"title":"Ansible Automation Platform","icon":"AnsibleIcon","headerLink":{}},"frontendRef":"ansible"},{"scope":"landing","module":"./EdgeWidget","defaults":{"w":1,"h":4,"maxH":10,"minH":1},"config":{"title":"Edge Management","icon":"EdgeIcon","headerLink":{}},"frontendRef":"edge"},{"scope":"landing","module":"./ExploreCapabilities","defaults":{"w":3,"h":5,"maxH":10,"minH":1},"config":{"title":"Explore capabilities","icon":"RocketIcon","headerLink":{}},"frontendRef":"exploreCapabilities"},{"scope":"chrome","module":"./DashboardFavorites","featureFlag":"widget.favoriteServices.enable","defaults":{"w":1,"h":6,"maxH":10,"minH":1},"config":{"title":"My favorite services","icon":"StarIcon","headerLink":{"title":"View all services","href":"/allservices"}},"frontendRef":"favoriteServices"},{"scope":"landing","module":"./ImageBuilderWidget","defaults":{"w":1,"h":4,"maxH":10,"minH":1},"config":{"title":"Image Builder","icon":"RhelIcon","headerLink":{}},"frontendRef":"imageBuilder"},{"scope":"sources","module":"./IntegrationsWidget","defaults":{"w":2,"h":4,"maxH":10,"minH":1},"config":{"title":"Integrations","icon":"IntegrationsIcon","headerLink":{"title":"Explore integrations","href":"/settings/integrations"},"permissions":[{"method":"featureFlag","args":["chrome-service.integrations-widget.enabled",true]},{"method":"loosePermissions","args":[["sources:*:read","integrations:endpoints:read"]]}]},"frontendRef":"integrations"},{"scope":"learningResources","module":"./BookmarkedLearningResourcesWidget","featureFlag":"widget.learningResources.enable","defaults":{"w":2,"h":4,"maxH":10,"minH":1},"config":{"title":"Bookmarked learning resources","icon":"OutlinedBookmarkIcon","headerLink":{"title":"View all","href":"/learning-resources?tab=all","featureFlag":"platform.learning-resources.global-learning-resources"}},"frontendRef":"learningResources"},{"scope":"notifications","module":"./DashboardWidget","featureFlag":"widget.notifications.enable","defaults":{"w":1,"h":3,"maxH":10,"minH":1},"config":{"title":"Events","icon":"BellIcon","headerLink":{"title":"View event log","href":"/settings/notifications/eventlog"},"permissions":[{"method":"isOrgAdmin"}]},"frontendRef":"notificationsEvents"},{"scope":"landing","module":"./OpenShiftWidget","featureFlag":"widget.openshift.enable","defaults":{"w":1,"h":4,"maxH":10,"minH":1},"config":{"title":"Red Hat OpenShift","icon":"OpenShiftIcon","headerLink":{}},"frontendRef":"openshift"},{"scope":"landing","module":"./OpenShiftAiWidget","defaults":{"w":1,"h":4,"maxH":10,"minH":1},"config":{"title":"Red Hat OpenShift AI","icon":"OpenShiftAiIcon","headerLink":{}},"frontendRef":"openshiftAi"},{"scope":"landing","module":"./QuayWidget","defaults":{"w":1,"h":4,"maxH":10,"minH":1},"config":{"title":"Quay.io","icon":"QuayIcon","headerLink":{}},"frontendRef":"quay"},{"scope":"landing","module":"./RecentlyVisited","featureFlag":"widget.recentlyVisited.enable","defaults":{"w":1,"h":7,"maxH":10,"minH":1},"config":{"title":"Recently visited","icon":"HistoryIcon","headerLink":{}},"frontendRef":"recentlyVisited"},{"scope":"landing","module":"./RhelWidget","featureFlag":"widget.rhel.enable","defaults":{"w":1,"h":4,"maxH":10,"minH":1},"config":{"title":"Red Hat Enterprise Linux","icon":"RhelIcon","headerLink":{}},"frontendRef":"rhel"},{"scope":"subscriptionInventory","module":"./SubscriptionsWidget","defaults":{"w":4,"h":4,"maxH":10,"minH":1},"config":{"title":"Subscriptions","icon":"CreditCardIcon","headerLink":{"title":"Manage subscriptions","href":"/subscriptions/inventory"},"permissions":[{"method":"featureFlag","args":["chrome-service.subscriptions-widget.enabled",true]},{"method":"hasPermissions","args":[["subscriptions:products:read"]]}]},"frontendRef":"subscriptions"},{"scope":"landing","module":"./SupportCaseWidget","defaults":{"w":2,"h":4,"maxH":10,"minH":1},"config":{"title":"My support cases","icon":"HeadsetIcon","headerLink":{"title":"Open a support case","href":"https://access.redhat.com/support/cases/#/case/new/get-support?caseCreate=true"}},"frontendRef":"supportCases"}]'
4 changes: 4 additions & 0 deletions api/WidgetMapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ type WidgetMappingRegistry struct {
WidgetMappings map[string]WidgetModuleFederationMetadata `json:"widgetMappings" yaml:"widgetMappings"`
}

type WidgetMappingResponse struct {
Data map[string]WidgetModuleFederationMetadata `json:"data"`
}

func (wc *WidgetModuleFederationMetadata) GetWidgetKey() string {
// make key in format "scope-module<-importName>"
// This will be always unique
Expand Down
149 changes: 98 additions & 51 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,26 @@ For detailed instructions on generating and using identity headers, see [docs/DE
### Success Response
All successful responses return the requested data with appropriate HTTP status codes (200, 201, 204).

### List Response Format
List endpoints return data in a consistent format with metadata:

```json
{
"data": [
/* Array of items */
],
"meta": {
"count": 2
}
}
```

**Endpoints using list format:**
- `GET /` - Dashboard templates list
- `GET /base-templates` - Base templates list

**Note**: The `GET /widget-mapping` endpoint uses a different format with a `data` object containing key-value mappings instead of an array.

### Error Response
```json
{
Expand All @@ -42,48 +62,66 @@ Dashboard templates are user-specific widget layouts that define how widgets are
#### GET `/`
Get all dashboard templates for the authenticated user.

**Query Parameters:**
- `dashboardType` (optional): Filter templates by dashboard type/base template name

**Request:**
```bash
# Get all templates
curl -X GET \
'http://localhost:8080/api/widget-layout/v1/' \
-H 'x-rh-identity: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...'

# Filter by dashboard type
curl -X GET \
'http://localhost:8080/api/widget-layout/v1/?dashboardType=default-dashboard' \
-H 'x-rh-identity: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...'
```

**Response (200 OK):**
```json
[
{
"id": 1,
"userId": "user-123",
"createdAt": "2024-01-01T12:00:00Z",
"updatedAt": "2024-01-01T12:00:00Z",
"templateConfig": {
"sm": [
{
"w": 2,
"h": 2,
"x": 0,
"y": 0,
"i": "widget1",
"static": false,
"maxH": 4,
"minH": 1
}
],
"md": [...],
"lg": [...],
"xl": [...]
},
"templateBase": {
"name": "custom-dashboard-template",
"displayName": "My Custom Dashboard"
},
"default": true
{
"data": [
{
"id": 1,
"userId": "user-123",
"createdAt": "2024-01-01T12:00:00Z",
"updatedAt": "2024-01-01T12:00:00Z",
"templateConfig": {
"sm": [
{
"w": 2,
"h": 2,
"x": 0,
"y": 0,
"i": "widget1",
"static": false,
"maxH": 4,
"minH": 1
}
],
"md": [...],
"lg": [...],
"xl": [...]
},
"templateBase": {
"name": "custom-dashboard-template",
"displayName": "My Custom Dashboard"
},
"default": true
}
],
"meta": {
"count": 1
}
]
}
```

**Auto-Creation Behavior:**
When filtering by `dashboardType`, if the user has no templates of that type but a matching base template exists, the API will automatically create and return a new template for the user with a `404` status code.

**Error Responses:**
- `404` - No templates found (may include auto-created template in response body)
- `500` - Internal server error

#### GET `/{dashboardTemplateId}`
Expand Down Expand Up @@ -344,29 +382,34 @@ curl -X GET \

**Response (200 OK):**
```json
[
{
"name": "default-dashboard",
"displayName": "Default Dashboard",
"templateConfig": {
"sm": [
{
"w": 2,
"h": 2,
"x": 0,
"y": 0,
"i": "insights-dashboard-widget",
"static": false,
"maxH": 4,
"minH": 1
}
],
"md": [...],
"lg": [...],
"xl": [...]
{
"data": [
{
"name": "default-dashboard",
"displayName": "Default Dashboard",
"templateConfig": {
"sm": [
{
"w": 2,
"h": 2,
"x": 0,
"y": 0,
"i": "insights-dashboard-widget",
"static": false,
"maxH": 4,
"minH": 1
}
],
"md": [...],
"lg": [...],
"xl": [...]
}
}
],
"meta": {
"count": 1
}
]
}
```

**Error Responses:**
Expand Down Expand Up @@ -450,6 +493,8 @@ curl -X GET \

Widget mapping provides metadata about available widgets including their configurations, dimensions, and module federation information.

> **📝 Note**: The widget mapping endpoint returns a different format than other list endpoints. It provides a key-value mapping rather than an array with metadata.

#### GET `/widget-mapping`
Get the mapping of all available widgets.

Expand All @@ -462,7 +507,8 @@ curl -X GET \
**Response (200 OK):**
```json
{
"insights@dashboard-widget": {
"data": {
"insights@dashboard-widget": {
"scope": "insights",
"module": "dashboard-widget",
"importName": "DashboardWidget",
Expand Down Expand Up @@ -504,6 +550,7 @@ curl -X GET \
"minH": 1
}
}
}
}
```

Expand Down
Loading