Platform Mesh Portal – How It Uses OpenMFP¶
Executive Summary
The Platform Mesh Portal is a Kubernetes-native, micro-frontend application built on the Open Micro Frontend Platform (OpenMFP). It leverages OpenMFP's Luigi-based architecture to provide a unified, extensible UI for the Platform Mesh service ecosystem, enabling dynamic integration of micro frontends through configuration-driven discovery.
Project Origin & Context¶
Repository & Governance
Repository: github.com/platform-mesh/portal Copyright: SAP SE and Platform Mesh contributors License: Apache-2.0 Status: Alpha (APIs subject to breaking changes)
The Platform Mesh portal was created as the central user interface for the Platform Mesh ecosystem, which is a NeoNephos Foundation project under Linux Foundation Europe. The portal serves as a shell application that orchestrates multiple micro frontends representing different Platform Mesh services (accounts, extensions, security, etc.).
Key Design Goals:
- Provide a unified interface for Platform Mesh services
- Enable teams to develop and deploy micro frontends independently
- Integrate with Kubernetes-native Extension Manager for dynamic micro frontend discovery
- Reuse OpenMFP's proven Luigi-based architecture rather than building from scratch
- Support entity-based navigation (Organizations → Accounts → Projects → Resources)
Development Status
The portal is currently in alpha stage. According to the repository: "APIs and concepts may change on short notice including breaking changes or complete removal of apis." Production use is not recommended.
How Platform Mesh Leverages OpenMFP¶
Platform Mesh uses OpenMFP as its foundational framework, building custom services on top of the OpenMFP libraries to create a Kubernetes-native portal experience.
OpenMFP Libraries Used¶
| Library | Version | Purpose | Platform Mesh Usage |
|---|---|---|---|
@openmfp/portal-ui-lib |
v0.186.12 | Frontend Luigi integration | Core portal UI bootstrapping, configuration services |
@openmfp/portal-server-lib |
v0.163.8 | Backend REST API server | Configuration endpoint, proxy management |
@luigi-project/core |
v2.26.0 | Luigi micro frontend framework | Navigation, routing, context management |
@luigi-project/client |
v2.26.0 | Luigi Client API | Micro frontend communication with shell |
@luigi-project/plugin-auth-oauth2 |
v2.26.0 | OAuth2 authentication | OIDC integration with Keycloak |
Platform Mesh Extensions to OpenMFP¶
Platform Mesh adds custom libraries that extend OpenMFP's functionality:
| Library | Version | Purpose |
|---|---|---|
@platform-mesh/portal-ui-lib |
v0.31.0 | Custom service implementations for Platform Mesh-specific features |
@platform-mesh/portal-server-lib |
v0.6.15 | Kubernetes client integration, Extension Manager API client |
Architecture Overview¶
┌─────────────────────────────────────────────────────────────────────┐
│ Platform Mesh Portal (Shell) │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Frontend (Angular + OpenMFP) │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ @openmfp/portal-ui-lib (PortalComponent) │ │ │
│ │ │ - providePortal(portalOptions) │ │ │
│ │ │ - Luigi Core Bootstrap │ │ │
│ │ │ - Navigation Tree Management │ │ │
│ │ │ - Authentication Handler (OAuth2) │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ ↕ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ Platform Mesh Custom Service Implementations │ │ │
│ │ │ (@platform-mesh/portal-ui-lib) │ │ │
│ │ │ │ │ │
│ │ │ - PMStaticSettingsConfigService │ │ │
│ │ │ - NodeChangeHookConfigServiceImpl │ │ │
│ │ │ - CustomGlobalNodesServiceImpl │ │ │
│ │ │ - NodeContextProcessingServiceImpl │ │ │
│ │ │ - LuigiExtendedGlobalContextConfigServiceImpl │ │ │
│ │ │ - HeaderBarConfigServiceImpl │ │ │
│ │ │ - UserProfileConfigServiceImpl │ │ │
│ │ │ - CustomRoutingConfigServiceImpl │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Backend (NestJS + OpenMFP Server) │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ @openmfp/portal-server-lib (NestJS Module) │ │ │
│ │ │ - /rest/config endpoint │ │ │
│ │ │ - Environment variable injection │ │ │
│ │ │ - Static file serving │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ ↕ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ Platform Mesh Server Extensions │ │ │
│ │ │ (@platform-mesh/portal-server-lib) │ │ │
│ │ │ │ │ │
│ │ │ - Kubernetes Client (@kubernetes/client-node) │ │ │
│ │ │ - Extension Manager API Integration (GraphQL) │ │ │
│ │ │ - Account Controller Integration (REST) │ │ │
│ │ │ - Content Configuration Aggregation │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────┬───────────────────────────────────────┘
│
┌───────────────┴────────────────┐
│ │
▼ ▼
┌───────────────────────┐ ┌───────────────────────────┐
│ Kubernetes Cluster │ │ Platform Mesh Services │
│ │ │ │
│ - Extension CRDs │ │ - Extension Manager │
│ - ConfigMaps │ │ - Account Controller │
│ - Secrets │ │ - Security Operator │
│ - ServiceAccounts │ │ - Other Controllers │
└───────────────────────┘ └───────────────────────────┘
│ │
└────────────────┬───────────────┘
│
▼
┌────────────────────────────────────┐
│ Deployed Micro Frontends │
│ │
│ - Account Management UI │
│ - Extension Catalog UI │
│ - Security Dashboard UI │
│ - Resource Management UI │
│ - Monitoring UI │
└────────────────────────────────────┘
Component Responsibilities¶
Clear Separation of Concerns
OpenMFP Libraries (reused as-is):
- Luigi framework integration
- Base authentication flows
- Configuration service interfaces
- REST API structure
- Static file serving
Platform Mesh Custom Code:
- Kubernetes client integration
- Extension Manager API communication
- Entity model (Organizations, Accounts, Projects)
- Custom navigation logic
- Platform Mesh-specific authentication context
- GraphQL API integration
- Platform Mesh branding and theming
Technical Stack Deep Dive¶
Frontend Stack¶
{
"framework": "Angular v20.3.15",
"language": "TypeScript v5.9.3",
"microFrontendFramework": "Luigi v2.26.0",
"stateManagement": "RxJS v7.8.2",
"uiComponents": "@ui5/webcomponents-ngx v0.5.10",
"utilities": [
"lodash v4.17.23",
"jwt-decode v4.0.0",
"jmespath v0.16.0"
],
"buildTool": "Angular CLI v20.3.13",
"testing": "Jest v29.7.0"
}
Key Frontend Dependencies:
- Angular 20: Latest Angular framework with standalone components
- Luigi Core & Client: SAP's micro frontend orchestrator
- UI5 Web Components: SAP Fiori-compliant UI components
- RxJS: Reactive programming for async operations
- JMESPath: JSON query language for content configuration visibility rules
- JWT Decode: Token parsing for authentication context
Backend Stack¶
{
"framework": "NestJS v11.1.9",
"platform": "Node.js",
"language": "TypeScript v5.9.3",
"httpServer": "Express (via @nestjs/platform-express)",
"kubernetesClient": "@kubernetes/client-node v1.4.0",
"graphqlClient": "graphql-request v7.4.0",
"httpClient": "axios v1.13.2 (@nestjs/axios v4.0.1)",
"validation": "class-validator v0.14.3",
"config": "dotenv v17.2.3",
"testing": "Jest v30.2.0 + supertest + nock"
}
Key Backend Dependencies:
- NestJS: Enterprise-grade Node.js framework with TypeScript and dependency injection
- Kubernetes Client: Direct integration with Kubernetes API for CRD management
- GraphQL Request: Client for Extension Manager GraphQL API
- Axios: HTTP client for REST API calls to Platform Mesh services
- Class Validator: DTO validation for incoming requests
- Dotenv: Environment variable management
Deployment Stack¶
Docker → Multi-stage build
├─ Stage 1: Build Frontend (Angular CLI)
├─ Stage 2: Build Backend (TypeScript compiler)
└─ Final: Serve from single container
├─ Port 4300: Frontend assets
└─ Port 3000: Backend REST API
Frontend Integration: PortalOptions Configuration¶
The core of Platform Mesh's OpenMFP integration is the PortalOptions configuration in frontend/src/main.ts:
const portalOptions: PortalOptions = {
staticSettingsConfigService: PMStaticSettingsConfigService,
nodeChangeHookConfigService: NodeChangeHookConfigServiceImpl,
customGlobalNodesService: CustomGlobalNodesServiceImpl,
nodeContextProcessingService: NodeContextProcessingServiceImpl,
luigiExtendedGlobalContextConfigService:
LuigiExtendedGlobalContextConfigServiceImpl,
headerBarConfigService: HeaderBarConfigServiceImpl,
userProfileConfigService: UserProfileConfigServiceImpl,
routingConfigService: CustomRoutingConfigServiceImpl,
};
bootstrapApplication(PortalComponent, {
providers: [
provideRouter(routes),
providePortal(portalOptions),
provideZonelessChangeDetection(),
],
});
Service Implementations¶
Custom Service Layer
Each service implements an OpenMFP interface and customizes portal behavior for Platform Mesh:
1. PMStaticSettingsConfigService¶
Purpose: Defines static Luigi configuration (navigation structure, routing mode, authentication provider)
Key Configuration:
- Routing: Hash-based routing (
#/organizations/123/accounts/456) - Authentication: OAuth2 plugin with Keycloak integration
- Navigation: Top-level nodes (Home, Organizations, Settings)
- Context: Global context variables (user info, tenant info)
2. NodeChangeHookConfigServiceImpl¶
Purpose: Executes logic when navigation nodes change (e.g., user navigates from one page to another)
Platform Mesh Usage:
- Track current entity context (organization ID, account ID, project ID)
- Fetch dynamic extensions when entering entity contexts
- Update breadcrumbs
- Load entity-specific data
3. CustomGlobalNodesServiceImpl¶
Purpose: Provides additional navigation nodes beyond what's defined in micro frontend configurations
Platform Mesh Usage:
- Inject "Home" node
- Add "Admin" section for cluster administrators
- Provide "Settings" and "User Profile" nodes
- Create "Help" and "Documentation" links
4. NodeContextProcessingServiceImpl¶
Purpose: Processes and enriches Luigi context before passing to micro frontends
Platform Mesh Usage:
- Extract entity IDs from URL path
- Fetch entity details from Account Controller API
- Add user roles and permissions from Security Operator
- Inject tenant-specific configuration
- Add feature toggle states
5. LuigiExtendedGlobalContextConfigServiceImpl¶
Purpose: Manages global context shared across all micro frontends
Platform Mesh Usage:
- User authentication token (JWT)
- User profile (name, email, roles)
- Current tenant/organization
- Cluster connection details
- Feature flags
- Theme preferences
6. HeaderBarConfigServiceImpl¶
Purpose: Configures the portal header bar (logo, title, actions)
Platform Mesh Usage:
- Platform Mesh logo and branding
- Global search integration
- User profile dropdown
- Notification center
- Quick actions (create project, view docs)
7. UserProfileConfigServiceImpl¶
Purpose: Defines user profile menu items and actions
Platform Mesh Usage:
- User settings
- Theme switcher
- Language selector
- Logout action
- Help & Support links
8. CustomRoutingConfigServiceImpl¶
Purpose: Custom routing logic and deep linking
Platform Mesh Usage:
- Entity-aware routing (
/organizations/:orgId/accounts/:accountId) - Default route redirects
- 404 handling
- Access control checks
Backend Integration: Extension Discovery¶
The backend server extends @openmfp/portal-server-lib with Platform Mesh-specific endpoints and integrations.
Extension Manager Integration¶
// Platform Mesh backend fetches extensions from Extension Manager GraphQL API
const extensionsQuery = `
query GetExtensions($entityType: String!, $workspaceId: String!) {
extensions(entityType: $entityType, workspaceId: $workspaceId) {
id
name
version
contentConfigurationUrl
enabled
}
}
`;
// Aggregate content configurations from all extensions
const contentConfigurations = await Promise.all(
extensions.map(ext =>
axios.get(ext.contentConfigurationUrl)
)
);
// Merge into unified Luigi configuration
const luigiConfig = mergeContentConfigurations(contentConfigurations);
Configuration Endpoint¶
OpenMFP Provides: /rest/config endpoint that serves environment variables as JSON
Platform Mesh Extends:
GET /rest/config
{
"extensions": [...], // From Extension Manager
"entities": { // From Account Controller
"organizations": [...],
"accounts": [...]
},
"permissions": {...}, // From Security Operator
"clusterConfig": {...}, // Kubernetes context
"features": {...} // Feature flags
}
Kubernetes Integration¶
import * as k8s from '@kubernetes/client-node';
// Read in-cluster service account
const kc = new k8s.KubeConfig();
kc.loadFromCluster();
const k8sApi = kc.makeApiClient(k8s.CustomObjectsApi);
// Fetch Extension CRDs
const extensions = await k8sApi.listNamespacedCustomObject(
'platform-mesh.io',
'v1',
namespace,
'extensions'
);
Runtime Integration Flow¶
End-to-End Flow
1. Portal Initialization¶
User accesses https://portal.platform-mesh.example.com
↓
Browser loads Portal Angular app (PortalComponent)
↓
Angular calls providePortal(portalOptions)
↓
OpenMFP portal-ui-lib initializes Luigi Core
↓
Luigi requests configuration from backend
↓
GET /rest/config → Backend returns merged configuration
↓
Luigi builds navigation tree from configuration
↓
Portal renders header, navigation, and empty content frame
2. Authentication Flow¶
Luigi detects no auth token
↓
Redirects to OAuth2 provider (Keycloak)
↓
User logs in with credentials
↓
Keycloak redirects back with authorization code
↓
Luigi OAuth2 plugin exchanges code for JWT token
↓
LuigiExtendedGlobalContextConfigServiceImpl stores token
↓
Token injected into global context
↓
All backend requests include Authorization: Bearer <token>
3. Navigation & Entity Context¶
User clicks "Organizations" in navigation
↓
Luigi routes to /#/organizations
↓
NodeChangeHookConfigServiceImpl detects route change
↓
Backend fetches organizations from Account Controller
↓
Organizations list micro frontend loaded in iframe
↓
User clicks specific organization "ACME Corp"
↓
Luigi routes to /#/organizations/acme-123
↓
NodeChangeHookConfigServiceImpl extracts organizationId=acme-123
↓
Backend requests extensions for entityType="organization"
↓
Extension Manager returns applicable extensions
↓
Luigi dynamically adds extension nodes to navigation
↓
NodeContextProcessingServiceImpl enriches context:
{
organizationId: "acme-123",
organization: { name: "ACME Corp", ... },
user: { ... },
permissions: [...]
}
↓
Context passed to micro frontend via Luigi Client
↓
Micro frontend receives context and renders organization dashboard
4. Micro Frontend Communication¶
Account Management Micro Frontend loads
↓
Initializes Luigi Client library
↓
Requests context via LuigiClient.getContext()
↓
Receives:
{
organizationId: "acme-123",
organization: { name: "ACME Corp" },
accountId: "account-456", // if in account context
user: { id: "user-1", roles: ["admin"] },
permissions: { canEdit: true, canDelete: false }
}
↓
Micro frontend makes API call to Account Controller
GET /api/accounts/account-456
Authorization: Bearer <token from global context>
↓
Micro frontend renders account details
↓
User clicks "Edit Account" button
↓
Micro frontend shows confirmation dialog using Luigi UX Manager:
LuigiClient.uxManager().showConfirmationModal({
header: "Edit Account",
body: "Do you want to edit this account?",
buttonConfirm: "Yes",
buttonDismiss: "Cancel"
})
↓
User confirms → Micro frontend saves changes
↓
Micro frontend shows success alert:
LuigiClient.uxManager().showAlert({
text: "Account updated successfully",
type: "success"
})
↓
Micro frontend publishes custom event for other micro frontends:
LuigiClient.publishEvent(new CustomEvent('account-updated', {
detail: { accountId: "account-456" }
}))
↓
Other micro frontends listening to this event refresh their data
Key Integration Points¶
1. Luigi Configuration Merging¶
OpenMFP Behavior: Fetches content configurations from registered URLs and merges them into Luigi navigation tree
Platform Mesh Extension:
- Queries Extension Manager for available extensions
- Filters extensions based on current entity context
- Applies permission checks (Security Operator)
- Merges configurations with conflict resolution
- Adds Platform Mesh global nodes
2. Authentication & Authorization¶
OpenMFP Behavior: Luigi OAuth2 plugin handles OIDC flow
Platform Mesh Extension:
- Integrates with Keycloak (Platform Mesh's IdP)
- Stores JWT in global context
- Passes token to all backend API calls
- Queries Security Operator for fine-grained permissions
- Injects permissions into micro frontend context
3. Entity-Based Navigation¶
OpenMFP Behavior: Supports defineEntity in content configuration
Platform Mesh Extension:
- Defines entity hierarchy (Organization → Account → Project → Resource)
- Fetches entity details from Account Controller
- Dynamically loads entity-specific extensions
- Updates breadcrumbs with entity names
- Resolves entity titles via API calls
4. Development Mode¶
OpenMFP Behavior: Allows registering local micro frontends for testing
Platform Mesh Usage:
- Developer runs micro frontend locally:
npm start(http://localhost:4200) - Opens Platform Mesh portal:
https://portal.platform-mesh.example.com - Clicks user menu → Settings → Development
- Enables "Development Mode"
- Adds local URL:
http://localhost:4200/assets/content-configuration.json - Portal fetches configuration and adds to navigation
- Developer can test integration without deploying
Deployment & Operations¶
Docker Build¶
# Multi-stage build
FROM node:25.6.0 AS builder
# Build frontend
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ ./
RUN npm run build
# Build backend
WORKDIR /app/backend
COPY backend/package*.json ./
RUN npm ci
COPY backend/ ./
RUN npm run build
# Production image
FROM node:25.6.0-slim
WORKDIR /app
# Copy built assets
COPY --from=builder /app/frontend/dist /app/public
COPY --from=builder /app/backend/dist /app/dist
COPY backend/package*.json ./
RUN npm ci --only=production
# Expose ports
EXPOSE 3000 4300
CMD ["node", "dist/main.js"]
Environment Configuration¶
# OpenMFP Configuration (via portal-server-lib)
OPENMFP_PORTAL_CONTEXT_api_base_url=https://api.platform-mesh.example.com
OPENMFP_PORTAL_CONTEXT_cluster_domain=cluster.local
# Platform Mesh Configuration
KUBERNETES_SERVICE_HOST=kubernetes.default.svc
KUBERNETES_SERVICE_PORT=443
EXTENSION_MANAGER_URL=http://extension-manager.platform-mesh.svc.cluster.local/graphql
ACCOUNT_CONTROLLER_URL=http://account-controller.platform-mesh.svc.cluster.local
SECURITY_OPERATOR_URL=http://security-operator.platform-mesh.svc.cluster.local
# Authentication
OIDC_ISSUER=https://keycloak.platform-mesh.example.com/realms/platform-mesh
OIDC_CLIENT_ID=platform-mesh-portal
OIDC_CLIENT_SECRET=<secret>
# Feature Flags
FEATURE_ANALYTICS_ENABLED=false
FEATURE_BETA_EXTENSIONS=true
Kubernetes Deployment¶
apiVersion: apps/v1
kind: Deployment
metadata:
name: platform-mesh-portal
namespace: platform-mesh
spec:
replicas: 3
selector:
matchLabels:
app: portal
template:
metadata:
labels:
app: portal
spec:
serviceAccountName: portal
containers:
- name: portal
image: platform-mesh/portal:latest
ports:
- containerPort: 3000
name: backend
- containerPort: 4300
name: frontend
env:
- name: KUBERNETES_SERVICE_HOST
value: kubernetes.default.svc
envFrom:
- configMapRef:
name: portal-config
- secretRef:
name: portal-secrets
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: portal
namespace: platform-mesh
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: portal-extension-reader
rules:
- apiGroups: ["platform-mesh.io"]
resources: ["extensions"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: portal-extension-reader
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: portal-extension-reader
subjects:
- kind: ServiceAccount
name: portal
namespace: platform-mesh
How Platform Mesh Benefits from OpenMFP¶
Value Delivered
1. Accelerated Development¶
Without OpenMFP: Platform Mesh would need to build from scratch:
- Luigi integration layer (months of work)
- Configuration service architecture
- Authentication integration
- Development mode tooling
- Documentation and examples
With OpenMFP: Platform Mesh gets all this out of the box, focusing only on Platform Mesh-specific logic.
Time Saved: Estimated 6-9 months of development time
2. Community-Driven Improvements¶
OpenMFP is maintained by the broader community. When OpenMFP releases improvements (e.g., better error handling, performance optimizations, new Luigi features), Platform Mesh automatically benefits by upgrading dependencies.
Example: When OpenMFP added support for Luigi v2.26.0 features, Platform Mesh upgraded @openmfp/portal-ui-lib and immediately gained those features.
3. Proven Architecture¶
OpenMFP's patterns are battle-tested across multiple projects. Platform Mesh avoids common pitfalls and architectural mistakes by following OpenMFP's established patterns.
4. Documentation & Learning Resources¶
Developers joining Platform Mesh can refer to OpenMFP documentation, tutorials, and examples. This reduces onboarding time and training costs.
5. Standards & Interoperability¶
By using OpenMFP, Platform Mesh ensures its micro frontends follow industry standards and can potentially be reused in other OpenMFP-based portals.
Differences from Pure OpenMFP¶
Platform Mesh Customizations
While Platform Mesh heavily relies on OpenMFP, it adds several custom layers:
| Aspect | OpenMFP Default | Platform Mesh Customization |
|---|---|---|
| Extension Discovery | Static URLs or manual registration | Dynamic discovery via Extension Manager CRDs in Kubernetes |
| Entity Model | Basic entity support | Rich hierarchy (Org → Account → Project → Resource) with Account Controller integration |
| Authorization | Basic visibility rules | Integration with Security Operator and OpenFGA for fine-grained permissions |
| Configuration Source | Environment variables or config files | Kubernetes ConfigMaps, Secrets, and CRDs |
| Backend API | Simple REST endpoints | GraphQL integration with Extension Manager, REST APIs to multiple controllers |
| Deployment | Docker or static hosting | Kubernetes-native with ServiceAccounts, RBAC, and cluster integration |
| Monitoring | Basic health checks | Integration with Platform Mesh observability stack (Prometheus, Grafana) |
Development Workflow¶
1. Local Development Setup¶
# Clone repository
git clone https://github.com/platform-mesh/portal.git
cd portal
# Install dependencies
npm run npm:install:ui
npm run npm:install:server
# Start development servers (frontend + backend)
npm start
# Frontend: http://localhost:4300
# Backend: http://localhost:3000
2. Creating a New Service Implementation¶
# Example: Add a new configuration service
# 1. Create service in frontend/src/services/
touch frontend/src/services/my-custom-config.service.ts
# 2. Implement OpenMFP service interface
import { MyConfigService } from '@openmfp/portal-ui-lib';
export class MyCustomConfigServiceImpl implements MyConfigService {
getConfig(): Config {
return {
// Custom Platform Mesh configuration
};
}
}
# 3. Register in portalOptions (main.ts)
const portalOptions: PortalOptions = {
// ... existing services
myCustomConfigService: MyCustomConfigServiceImpl,
};
# 4. Test locally
npm start
3. Testing Changes¶
# Lint code
npm run lint
# Auto-fix lint issues
npm run lint:fix
# Run unit tests
npm test
# Run tests with coverage
npm run test:cov
# Build production bundle
npm run build
4. Pre-commit Hooks¶
Platform Mesh uses Husky and lint-staged to enforce quality:
Every commit automatically: - Lints TypeScript files - Formats code with Prettier - Runs affected tests
Contributing to Platform Mesh Portal¶
Contribution Process
1. Fork & Clone¶
# Fork the repository on GitHub
# Clone your fork
git clone https://github.com/<your-username>/portal.git
cd portal
2. Create Feature Branch¶
3. Make Changes¶
- Follow existing code patterns
- Add tests for new functionality
- Update documentation if needed
- Ensure all tests pass
4. Commit & Push¶
# Commits trigger pre-commit hooks (lint-staged)
git commit -m "feat: add new custom service for XYZ"
git push origin feature/my-new-feature
5. Open Pull Request¶
- Open PR against
platform-mesh/portalmain branch - Fill out PR template
- Request review from maintainers
- Address review feedback
- Wait for approval and merge
6. Contributing to OpenMFP Libraries¶
If your change requires modifications to OpenMFP libraries:
- Open issue in
openmfp/<library>repository - Discuss proposed change with OpenMFP maintainers
- Submit PR to OpenMFP repository
- Wait for OpenMFP release
- Update Platform Mesh to use new OpenMFP version
Monitoring & Troubleshooting¶
Development Mode Debugging¶
// Enable Luigi debug logs in browser console
localStorage.setItem('luigi.debug', 'true');
// Inspect current context
LuigiClient.getContext();
// Check loaded extensions
fetch('/rest/config').then(r => r.json()).then(console.log);
Common Issues¶
| Issue | Symptoms | Resolution |
|---|---|---|
| Extensions not loading | Empty navigation | Check Extension Manager API connectivity; verify CRDs exist in Kubernetes |
| Authentication loop | Keeps redirecting to login | Verify Keycloak configuration; check OIDC client ID and secret |
| Blank micro frontend | White iframe | Check CORS headers on micro frontend; verify content-configuration.json is accessible |
| Context not available | Empty getContext() |
Ensure Luigi Client addInitListener is used; check backend context processing |
| Permissions denied | Features hidden | Verify Security Operator integration; check permission assignments in OpenFGA |
Backend Logs¶
# View portal backend logs
kubectl logs -n platform-mesh deployment/platform-mesh-portal -c portal
# Follow logs in real-time
kubectl logs -n platform-mesh deployment/platform-mesh-portal -c portal -f
# Check Extension Manager integration
kubectl logs -n platform-mesh deployment/platform-mesh-portal -c portal | grep "extension-manager"
Health Checks¶
# Backend health
curl http://localhost:3000/health
# Configuration endpoint
curl http://localhost:3000/rest/config
# Kubernetes readiness
kubectl get pod -n platform-mesh -l app=portal
Performance Considerations¶
1. Lazy Loading¶
OpenMFP supports lazy loading of micro frontends. Platform Mesh leverages this with preloadUrl: false:
{
"pathSegment": "analytics",
"viewGroup": {
"preloadUrl": false // Only load when user navigates to this page
}
}
2. Configuration Caching¶
Platform Mesh backend caches Extension Manager responses to avoid repeated GraphQL queries:
// Cache configuration for 5 minutes
const configCache = new NodeCache({ stdTTL: 300 });
async function getExtensions(entityType: string) {
const cacheKey = `extensions:${entityType}`;
let extensions = configCache.get(cacheKey);
if (!extensions) {
extensions = await extensionManagerClient.query(...);
configCache.set(cacheKey, extensions);
}
return extensions;
}
3. Frontend Optimization¶
- Angular Zoneless Change Detection: Reduces overhead
- OnPush Change Detection: Components only update when inputs change
- Lazy Loaded Routes: Split bundles for faster initial load
- Service Worker: Caches static assets (future enhancement)
Future Roadmap¶
Subject to Change
Potential future enhancements to Platform Mesh's OpenMFP integration:
- [ ] Progressive Web App (PWA) support
- [ ] Offline mode for read-only operations
- [ ] Real-time updates via WebSockets
- [ ] Advanced analytics and usage tracking
- [ ] Multi-tenancy improvements
- [ ] Theme customization per organization
- [ ] Plugin marketplace for community extensions
Related Topics¶
- OpenMFP Overview - Core framework concepts
- Luigi Framework - Underlying micro frontend framework
- Extension Manager - Kubernetes operator for micro frontend management
- Account Controller - Entity model and API
- Security Operator - Authorization and permissions
- Keycloak Integration - Identity provider
Further Resources¶
Official Documentation¶
- Platform Mesh Portal Repository
- OpenMFP Website
- Luigi Framework Documentation
- NestJS Documentation
- Angular Documentation
Related Repositories¶
Community¶
- Platform Mesh discussions on GitHub
- OpenMFP Zulip Chat
- NeoNephos Foundation community calls
Contributing & Community¶
How to Get Involved
Submit Issues: github.com/platform-mesh/portal/issues
Pull Requests: Follow CONTRIBUTING.md in the repository
Code of Conduct: All contributors must adhere to the project's CODE_OF_CONDUCT.md
License: Apache-2.0
Sources: