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
15 changes: 13 additions & 2 deletions .vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ export default withMermaid({
)
},
]
}
},
optimizeDeps: {
include: ['mermaid'],
},
},


Expand Down Expand Up @@ -224,7 +227,15 @@ export default withMermaid({
{ text: 'OpenFGA', link: '/reference/components/openfga' },
{ text: 'rebac-authz-webhook', link: '/reference/components/rebac-authz-webhook' },
{ text: 'Kubernetes GraphQL gateway', link: '/reference/components/kubernetes-graphql-gateway' },
{ text: 'Portal', link: '/reference/components/portal' },
{
text: 'Portal',
link: '/reference/components/portal',
collapsed: false,
items: [
{ text: 'Portal UI library', link: '/reference/components/portal/portal-ui-lib' },
{ text: 'Portal server library', link: '/reference/components/portal/portal-server-lib' },
],
},
{ text: 'Marketplace', link: '/reference/components/marketplace' },
{ text: 'virtual-workspaces', link: '/reference/components/virtual-workspaces' },
{ text: 'Observability', link: '/reference/components/observability' },
Expand Down
2 changes: 2 additions & 0 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ Both paths require a Kubernetes operator that reconciles the service's resources
- [rebac-authz-webhook](reference/components/rebac-authz-webhook.md): kcp authorization webhook backed by OpenFGA.
- [Kubernetes GraphQL Gateway](reference/components/kubernetes-graphql-gateway.md): GraphQL interface for kcp resources.
- [Portal](reference/components/portal.md): Consumer-facing web UI.
- [Portal UI library](reference/components/portal/portal-ui-lib.md): Angular library with Platform Mesh portal options and generic UI web components.
- [Portal server library](reference/components/portal/portal-server-lib.md): NestJS library with Platform Mesh portal backend providers.
- [Marketplace](reference/components/marketplace.md): Service discovery and binding for consumers.
- [api-syncagent](reference/components/api-syncagent.md): CRD-based provider integration path.
- [multi-cluster-runtime](reference/components/multi-cluster-runtime.md): Custom controller provider integration path.
Expand Down
2 changes: 2 additions & 0 deletions reference/components/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ The components that run as part of a Platform Mesh installation. Most pages are

- [Kubernetes GraphQL gateway](./kubernetes-graphql-gateway.md)
- [Portal](./portal.md)
- [Portal UI library](./portal/portal-ui-lib.md)
- [Portal server library](./portal/portal-server-lib.md)
- [Marketplace](./marketplace.md)
- [virtual-workspaces](./virtual-workspaces.md)

Expand Down
25 changes: 9 additions & 16 deletions reference/components/marketplace.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Under the hood, installing a provider creates a Kubernetes `APIBinding` in the c
The key capabilities are:

- **Provider catalog** — browsable, searchable, and filterable list of all available service providers
- **Provider details** — full metadata view: description, contacts, documentation, support channels, service level, and verification status
- **Provider details** — full metadata view: description, contacts, documentation, support channels, service level
- **Install** — creates an `APIBinding` in the current workspace with all required permission claims auto-accepted
- **Uninstall** — deletes the `APIBinding` after a confirmation dialog
- **Theme support** — renders provider icons in light or dark variants based on the active SAP Fiori theme
Expand Down Expand Up @@ -38,21 +38,6 @@ MarketplaceEntry resources + APIBinding create/delete

The active `accountId` is forwarded from the Luigi context to every GraphQL request so that `spec.installed` reflects the binding state of the current workspace.

## Technology stack

| Component | Technology |
|---|---|
| Framework | Angular 21 |
| UI components | SAP Fundamental NGX 0.61 |
| Micro-frontend orchestration | Luigi 2.22 |
| State management | NgRx 21 |
| GraphQL client | Apollo Angular / Apollo Client 4 |
| Subscriptions transport | SSE (Server-Sent Events) |
| i18n | Angular localization (English, German) |
| Testing | Vitest 4 |
| Language | TypeScript (ES2022, strict mode) |
| Container | nginx:alpine, served on port 8080 |

## Configuration

The UI reads all runtime configuration from the Luigi node context injected by the Portal. No static environment files are required in production. The relevant context fields are:
Expand All @@ -63,6 +48,14 @@ The UI reads all runtime configuration from the Luigi node context injected by t
| `accountId` | Current workspace scope for install/uninstall operations |
| `token` | Bearer token forwarded to every GraphQL request |
| `analyticsTrackerConfig` | Optional Matomo analytics configuration |
| `uiConfig.filters` | Optional list of `{label, providerMetadataPath}` entries that define the catalog's filter facets |

`uiConfig.filters` drives which filter dropdowns render above the provider catalog and how they behave, without requiring a UI code change:

- Each entry renders one filter control. If the list is empty or absent, no filter row is shown.
- `label` is the filter's display name (e.g. `Category`, `Provider`).
- `providerMetadataPath` is a dot-path resolved against each provider's `ProviderMetadata`, walking through JSON-encoded sub-objects (such as `spec.data`) as needed — e.g. `spec.data.category` or `spec.data.provider`.
- The available options for a filter are the distinct values found at that path across the current catalog. Selecting one or more values narrows the catalog to providers whose resolved value matches a selection; no selection shows every provider.

## Repository

Expand Down
21 changes: 21 additions & 0 deletions reference/components/portal.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,33 @@ Every navigation node is scoped to a **kcp workspace path** (for example `root:o
The backend derives the active workspace path from the authenticated user's context and injects it into the
Luigi `globalContext`, making it available to all child microfrontends without additional round-trips.

## Architecture

The Portal repository is a thin application. Most of its behavior comes from two layers of libraries: the generic OpenMFP portal libraries provide the shell, and the Platform Mesh portal libraries plug in the Platform Mesh–specific implementations.

```
Portal frontend (Angular) Portal backend (NestJS)
↓ ↓
@platform-mesh/portal-ui-lib @platform-mesh/portal-server-lib
↓ ↓
@openmfp/portal-ui-lib (Luigi shell) @openmfp/portal-server-lib (PortalModule)
```

| Layer | Library | Role in the Portal |
|---|---|---|
| Frontend | [Portal UI library](./portal/portal-ui-lib.md) (`@platform-mesh/portal-ui-lib`) | Provides the `portal-options` service implementations passed to `providePortal()` — navigation, header bar, node context processing, routing, and user profile — plus the generic UI web components |
| Backend | [Portal server library](./portal/portal-server-lib.md) (`@platform-mesh/portal-server-lib`) | Provides the providers passed to `PortalModule.create()` — authentication, request and portal context, account entity context, `ContentConfiguration` service providers, and the permissions proxy |

## Repository

- [github.com/platform-mesh/portal](https://github.com/platform-mesh/portal)
- [github.com/platform-mesh/portal-ui-lib](https://github.com/platform-mesh/portal-ui-lib)
- [github.com/platform-mesh/portal-server-lib](https://github.com/platform-mesh/portal-server-lib)

## Related

- [Portal UI library](./portal/portal-ui-lib.md)
- [Portal server library](./portal/portal-server-lib.md)
- [Explore the example MSP](/tutorials/explore-example-msp.md)
- [IAM UI](./iam-ui.md)
- [Marketplace](./marketplace.md)
Expand Down
35 changes: 35 additions & 0 deletions reference/components/portal/portal-server-lib.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Portal server library

## Purpose

The **Portal server library** (`@platform-mesh/portal-server-lib`) is the NestJS library the [Portal](../portal.md) backend is built with. It builds on the generic [`@openmfp/portal-server-lib`](https://www.npmjs.com/package/@openmfp/portal-server-lib), which provides the `PortalModule` that serves the frontend, handles the OAuth2 flow, and assembles the Luigi configuration. The Platform Mesh library supplies the implementations that connect that module to kcp, Keycloak, and the Platform Mesh authorization stack.

## Provided implementations

The library exports its providers from `@platform-mesh/portal-server-lib/portal-options`. The Portal backend passes them to `PortalModule.create()`:

| Export | Implements | Responsibility |
|---|---|---|
| `PMAuthConfigProvider` | `AuthConfigService` | Resolves the organization from the request host, reads the OIDC client from the `IdentityProviderConfiguration` resource in kcp, and returns the authorization and token endpoints from OIDC discovery |
| `PMLogoutService` | `LogoutCallback` | Ends the session at the identity provider on logout |
| `PMRequestContextProvider` | `RequestContextProvider` | Adds the organization and whether the request targets an organization subdomain to the request context |
| `PMPortalContextService` | `PortalContextProvider` | Resolves `${org-name}` and `${org-subdomain}` placeholders in portal context URLs and adds the public kcp workspace URL |
| `AccountEntityContextProvider` | `EntityContextProvider` | Provides the context values for the `account` entity |
| `KubernetesServiceProvidersService` | `ServiceProviderService` | Lists [`ContentConfiguration`](/reference/resources/content-configuration.md) resources for the current organization and account through kcp, and returns them together with the resolved node permissions |
| `ContentConfigurationServiceProvidersService` | `ServiceProviderService` | Alternative service provider that reads `ContentConfiguration` resources through the `contentconfigurations` [virtual workspace](../virtual-workspaces.md) of the [Kubernetes GraphQL gateway](../kubernetes-graphql-gateway.md) |
| `KcpKubernetesService` | — | Kubernetes client for kcp, configured from `KUBECONFIG_KCP` |
| `PermissionsProxyService`, `AuthzWebhookService` | — | Resolve navigation node and resource instance permissions through the `/batch-authz` endpoint of the [rebac-authz-webhook](../rebac-authz-webhook.md), configured by `OPENMFP_PORTAL_CONTEXT_AUTHZ_WEBHOOK_URL`; when unset, permission checks fail open |
| `PermissionsController` | — | Exposes `POST /rest/permissions/resource-check` for instance-level permission checks from the frontend |

Outside an organization subdomain, both service providers return the welcome node configuration instead of `ContentConfiguration` resources.

## Repository

- [github.com/platform-mesh/portal-server-lib](https://github.com/platform-mesh/portal-server-lib)

## Related

- [Portal](../portal.md)
- [Portal UI library](./portal-ui-lib.md)
- [Keycloak](../keycloak.md)
- [rebac-authz-webhook](../rebac-authz-webhook.md)
114 changes: 114 additions & 0 deletions reference/components/portal/portal-ui-lib.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Portal UI library

## Purpose

The **Portal UI library** (`@platform-mesh/portal-ui-lib`) is the Angular library the [Portal](../portal.md) frontend is built with. It builds on the generic [`@openmfp/portal-ui-lib`](https://www.npmjs.com/package/@openmfp/portal-ui-lib), which provides the Luigi-based portal shell, and adds the implementations required for Platform Mesh functionality: kcp workspace-aware navigation, resource access through the [Kubernetes GraphQL gateway](../kubernetes-graphql-gateway.md), permission checks, and a set of reusable web components.

The library ships two artifacts:

- **Angular library** — service implementations, models, and utilities consumed by the Portal frontend at build time
- **Web component bundle** — `platform-mesh-portal-ui-wc.js`, a set of self-registering custom elements that Luigi loads at runtime

## Package contents

The Angular library is split into secondary entry points:

| Entry point | Contents |
|---|---|
| `@platform-mesh/portal-ui-lib/portal-options` | `*ServiceImpl` classes passed to `providePortal()` from `@openmfp/portal-ui-lib`: custom global nodes, header bar (breadcrumbs, namespace selection), Luigi extended global context, navigation redirect strategy, node change hook, node context processing, routing, user profile, and the persistent panel listener |
| `@platform-mesh/portal-ui-lib/services` | Resource and gateway services (Apollo-based GraphQL), instance permissions, account info, logical cluster, kubeconfig Secret, and organization readiness |
| `@platform-mesh/portal-ui-lib/models` | Resource, UI definition, permissions, and account info types |
| `@platform-mesh/portal-ui-lib/utils` | Helpers for JSON paths, GraphQL query building, and resource sanitization |

The web component bundle registers the following custom elements:

| Element | Purpose |
|---|---|
| `generic-list-view` | Generic resource list with create and delete |
| `generic-detail-view` | Generic resource detail page |
| `organization-management` | Organization onboarding and selection |
| `welcome-view` | Welcome page shown outside an organization context |
| `error-component` | Error page |

## Generic UI

The generic UI lets you render list, detail, and create views for any Kubernetes or kcp resource without building a dedicated microfrontend. Instead of shipping UI code, you describe the resource and its views declaratively in the node's [`ContentConfiguration`](/reference/resources/content-configuration.md), and the generic UI web components query the resource through the Kubernetes GraphQL gateway.

### Components

- `generic-list-view` — displays a table of resources, and handles creation and deletion of resources
- `generic-detail-view` — displays an individual resource

### Node configuration

A Luigi node uses a generic UI component by pointing its `url` at the web component bundle and marking it as self-registered:

```json
{
"url": "/assets/platform-mesh-portal-ui-wc.js#generic-list-view",
"webcomponent": { "selfRegistered": true, "type": "module" },
"navigationContext": "accounts"
}
```

A `generic-detail-view` node that is a child of a list view entity inherits its context through Luigi. An independent detail view node provides its own `context.resourceDefinition`.

### Resource definition

The node `context.resourceDefinition` describes the resource and how to render it:

| Field | Purpose |
|---|---|
| `apiGroup`, `version`, `entityCollection`, `entity`, `scope`, `namespace` | Identify the resource in the GraphQL schema |
| `readyCondition` | Optional JSONPath expression and GraphQL properties that decide when a resource is ready |
| `availableWhenNotReady` | Optional; keeps a resource available for navigation and actions while it is not ready (default `false`) |
| `permissionsDefinition` | Optional instance-level and resource-level permission checks that gate the create button, list requests, live-update subscriptions, and edit or delete actions; unknown permissions fail open |
| `ui.logoUrl` | Resource type logo shown in the view header |
| `ui.listView` | Table columns (`fields`), row `actions`, title, description, and optional filter tabs |
| `ui.detailView` | Displayed `fields`, header `actions`, title, description, and `showDownloadKubeconfig` |
| `ui.createView` | Form `fields` for creating and updating resources; for namespaced resources a namespace field is added when no namespace is resolved |

### Field definitions

Every column, form field, and action is a `FieldDefinition`. Field definitions support:

- **Data access** — `property` (with fallback paths), `jsonPathExpression`, and `propertyField` with text transforms
- **Grouping** — multiple values in a single column or row through `group`
- **Rendering** — `uiSettings.displayAs` as `secret`, `boolIcon`, `link`, `tooltip`, `img`, or `button`, plus copy buttons and conditional CSS rules
- **Actions** — `navigate`, `openInModal`, `delete-resource`, and `download-kubeconfig-from-secret-ref`, optionally gated by `requirePermission`
- **Form input** — `required`, static `values`, and `dynamicValuesDefinition` that loads options through a GraphQL query with `{context.<path>}` placeholders resolved from the Luigi context
- **Arrays of objects** — `propertyCollection`, including nested collections, for fields such as `status.conditions`

### Defaults

When neither `listView` nor `detailView` is provided, default views are used. When `createView` is not provided, the view offers no way to create a resource.

For the complete configuration reference and full `ContentConfiguration` examples, see the [generic UI guide](https://github.com/platform-mesh/portal-ui-lib/blob/main/docs/readme-generic-ui.md).

## Angular configuration

A portal application that serves the web component bundle includes the library assets in its `angular.json` build configuration:

```json
{
"assets": [
{
"glob": "**",
"input": "node_modules/@platform-mesh/portal-ui-lib/assets/",
"output": "/assets/"
}
]
}
```

## Repository

- [github.com/platform-mesh/portal-ui-lib](https://github.com/platform-mesh/portal-ui-lib)
- [Generic UI guide](https://github.com/platform-mesh/portal-ui-lib/blob/main/docs/readme-generic-ui.md)

## Related

- [Portal](../portal.md)
- [Portal server library](./portal-server-lib.md)
- [Kubernetes GraphQL gateway](../kubernetes-graphql-gateway.md)
- [ContentConfiguration resource](/reference/resources/content-configuration.md)
Loading