Skip to content

Commit

Permalink
refactor(platform): consolidate API for configuring the platform
Browse files Browse the repository at this point in the history
This commit eliminates the different ways to configure the platform and removes the separate communication channel for library-internal communication.

closes #39
closes #96

BREAKING CHANGE: Consolidation of the API for configuring the platform host introduced a breaking change. The communication protocol between host and client is not affected by this change.

- API for loading the platform config via a config loader has been removed; to migrate, load the config before starting the platform;
- API for passing an app list to `MicrofrontendPlatform.startHost` has been removed; to migrate, register applications via `MicrofrontendPlatformConfig` object, as follows: `MicrofrontendPlatformConfig.applications`;
- manual registration of the host application has been removed as now done implicitly; to migrate:
  - remove host app from the app list;
  - configure host privileges via `HostConfig` object, as follows:
	- `MicrofrontendPlatformConfig.host.scopeCheckDisabled`
	- `MicrofrontendPlatformConfig.host.intentionCheckDisabled`
	- `MicrofrontendPlatformConfig.host.intentionRegisterApiDisabled`
  - specify message delivery timeout in `MicrofrontendPlatformConfig.host.messageDeliveryTimeout`;
  - provide the host's manifest, if any, via `MicrofrontendPlatformConfig.host.manifest`, either as object literal or as URL;
  - specify the host's symbolic name in `MicrofrontendPlatformConfig.host.symbolicName`; if not specified, defaults to `host`;
- the Activator API can now be disabled by setting the flag `MicrofrontendPlatformConfig.activatorApiDisabled` instead of `PlatformConfig.platformFlags.activatorApiDisabled`;
- the interface `ApplicationManifest` has been renamed to `Manifest`;

- the micro application must now pass its identity (symbolic name) directly as the first argument, rather than via the options object;
- the options object passed to `MicrofrontendPlatform.connectToHost` has been renamed from ` MicroApplicationConfig` to `ConnectOptions` and messaging options are now top-level options; to migrate:
  - set the flag `MicrofrontendPlatformConnectOptions.connect` instead of `MicroApplicationConfig.messaging.enabled` to control if to connect to the platform host;
  - specify 'broker discovery timeout' in `MicrofrontendPlatformConnectOptions.brokerDiscoverTimeout` instead of `MicroApplicationConfig.messaging.brokerDiscoverTimeout`;
  - specify 'message delivery timeout' in `MicrofrontendPlatformConnectOptions.messageDeliveryTimeout` instead of `MicroApplicationConfig.messaging.deliveryTimeout`;

### The following snippets illustrate how a migration could look like:

#### Before migration

```typescript
const applications: ApplicationConfig[] = [
  {symbolicName: 'host', manifestUrl: '/manifest.json'}, // optional
  {symbolicName: 'app1', manifestUrl: 'http://app1/manifest.json'},
  {symbolicName: 'app2', manifestUrl: 'http://app2/manifest.json'},
];
await MicrofrontendPlatform.startHost(applications, {symbolicName: 'host'});
```

#### After migration

```typescript
await MicrofrontendPlatform.startHost({
  host: {
    symbolicName: 'host',
    manifest: '/manifest.json'
  },
  applications: [
   {symbolicName: 'app1', manifestUrl: 'http://app1/manifest.json'},
   {symbolicName: 'app2', manifestUrl: 'http://app2/manifest.json'}
  ]
});
```

#### After migration if inlining the host manifest

```typescript
await MicrofrontendPlatform.startHost({
  host: {
    symbolicName: 'host',
    manifest: {
      name: 'Host Application',
      capabilities: [
        // capabilities of the host application
      ],
      intentions: [
        // intentions of the host application
      ]
    }
  },
  applications: [
   {symbolicName: 'app1', manifestUrl: 'http://app1/manifest.json'},
   {symbolicName: 'app2', manifestUrl: 'http://app2/manifest.json'}
  ],
});
```
  • Loading branch information
danielwiehl authored and mofogasy committed Jan 31, 2022
1 parent f05d643 commit 142ce8e
Show file tree
Hide file tree
Showing 86 changed files with 2,323 additions and 2,234 deletions.
2 changes: 1 addition & 1 deletion apps/microfrontend-platform-devtools/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,6 @@ export function providePlatformInitializerFn(ngZoneMessageClientDecorator: NgZon
});

// Run the microfrontend platform as client app
return zone.runOutsideAngular(() => MicrofrontendPlatform.connectToHost({symbolicName: 'devtools'}));
return zone.runOutsideAngular(() => MicrofrontendPlatform.connectToHost('devtools'));
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

import {NgModule} from '@angular/core';
import {ACTIVATION_CONTEXT, ActivationContext, ContextService, MessageClient, MicroApplicationConfig} from '@scion/microfrontend-platform';
import {ACTIVATION_CONTEXT, ActivationContext, APP_IDENTITY, ContextService, MessageClient} from '@scion/microfrontend-platform';
import {RouterModule} from '@angular/router';
import {Beans} from '@scion/toolkit/bean-manager';

Expand All @@ -26,7 +26,7 @@ import {Beans} from '@scion/toolkit/bean-manager';
export class ActivatorProgressModule {

constructor() {
const symbolicName = Beans.get(MicroApplicationConfig).symbolicName;
const symbolicName = Beans.get<string>(APP_IDENTITY);
const randomDelay = 1000 + Math.floor(Math.random() * 3000); // range: [1s, 4s); >=1s to exceed the 'activatorLoadTimeout' timeout of app3 [app3#activatorLoadTimeout=800ms] @see environment.ts

console.log(`[testing] Delay the readiness signaling of the app '${symbolicName}' by ${randomDelay}ms.`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

import {NgModule} from '@angular/core';
import {ACTIVATION_CONTEXT, ActivationContext, ContextService, MessageClient, MessageHeaders, MicroApplicationConfig} from '@scion/microfrontend-platform';
import {ACTIVATION_CONTEXT, ActivationContext, APP_IDENTITY, ContextService, MessageClient, MessageHeaders} from '@scion/microfrontend-platform';
import {TestingAppTopics} from '../testing-app.topics';
import {RouterModule} from '@angular/router';
import {Beans} from '@scion/toolkit/bean-manager';
Expand All @@ -27,7 +27,7 @@ import {Beans} from '@scion/toolkit/bean-manager';
export class ActivatorReadinessModule {

constructor() {
const symbolicName = Beans.get(MicroApplicationConfig).symbolicName;
const symbolicName = Beans.get<string>(APP_IDENTITY);
const randomDelay = 1000 + Math.floor(Math.random() * 3000); // range: [1s, 4s); >=1s to exceed the 'activatorLoadTimeout' timeout of app3 [app3#activatorLoadTimeout=800ms] @see environment.ts

console.log(`[testing] Delay the readiness signaling of the app '${symbolicName}' by ${randomDelay}ms.`);
Expand All @@ -43,7 +43,7 @@ export class ActivatorReadinessModule {
throw Error('[NullActivatorContextError] Not running in an activator context.');
}

const symbolicName = Beans.get(MicroApplicationConfig).symbolicName;
const symbolicName = Beans.get<string>(APP_IDENTITY);
const pingReply = `${symbolicName} [primary: ${activationContext.primary}, X-APP-NAME: ${activationContext.activator.properties['X-APP-NAME']}]`;

// Subscribe for ping requests.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<header>
<span class="page-title" *ngIf="pageTitle; else logo">{{pageTitle}}</span>
<div class="title">
<img *ngIf="isPlatformHost" class="banner" src="assets/scion-microfrontend-platform-banner.svg" alt="SCION Microfrontend Platform">
<span *ngIf="pageTitle" class="page-title">{{pageTitle}}</span>
</div>
<span class="chip focus-within e2e-focus-within" title="This document or its embedded web content has received focus" *ngIf="isFocusWithin">focus-within</span>
<span class="chip app-name">{{appSymbolicName}}</span>
<span class="chip devtools" *ngIf="isDevtoolsEnabled" (click)="onDevToolsToggle()">
Expand All @@ -17,6 +20,3 @@
<app-devtools></app-devtools>
</ng-template>
</sci-sashbox>
<ng-template #logo>
<img src="assets/scion-microfrontend-platform-banner.svg" alt="SCION Microfrontend Platform" class="banner">
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,58 @@

> header {
flex: none;
display: grid;
display: flex;
margin-bottom: 1.5em;
grid-template-columns: auto repeat(3, max-content);
align-items: start;
column-gap: .25em;
align-items: flex-start;

> span.page-title {
font-size: 1.2rem;
font-weight: bold;
> *:not(:last-child) {
margin-right: .25em;
}

> img.banner {
height: 35px;
}
> div.title {
flex: auto;

> .chip.focus-within {
@include sci-toolkit-styles.chip(var(--sci-color-accent), null, var(--sci-color-accent));
padding: .25em 1.5em;
font-size: 1.1rem;
}
> img.banner {
height: 35px;
}

> .chip.app-name {
@include sci-toolkit-styles.chip(var(--sci-color-accent), var(--sci-color-A50), var(--sci-color-accent));
padding: .25em 1.5em;
font-size: 1.1rem;
font-weight: bold;
> span.page-title {
font-size: 1.2rem;
font-weight: bold;
}
}

> .chip.devtools {
@include sci-toolkit-styles.chip(var(--sci-color-accent), null, var(--sci-color-accent));
padding: .1em 1em;
font-size: 1rem;
font-weight: bold;
display: flex;
align-items: center;
cursor: pointer;
> span.chip {
flex: none;

> button.toggle {
color: var(--sci-color-primary);
&.focus-within {
@include sci-toolkit-styles.chip(var(--sci-color-accent), null, var(--sci-color-accent));
padding: .25em 1.5em;
font-size: 1.1rem;
}

&.enabled {
color: var(--sci-color-accent);
&.app-name {
@include sci-toolkit-styles.chip(var(--sci-color-accent), var(--sci-color-A50), var(--sci-color-accent));
padding: .25em 1.5em;
font-size: 1.1rem;
font-weight: bold;
}

&.devtools {
@include sci-toolkit-styles.chip(var(--sci-color-accent), null, var(--sci-color-accent));
padding: .1em 1em;
font-size: 1rem;
font-weight: bold;
display: flex;
align-items: center;
cursor: pointer;

> button.toggle {
color: var(--sci-color-primary);

&.enabled {
color: var(--sci-color-accent);
}
}
}
}
Expand All @@ -68,9 +78,5 @@
}
}
}

&:not(.top-window) img.banner {
visibility: hidden;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/
import {Component, HostBinding, OnDestroy} from '@angular/core';
import {asapScheduler, Subject} from 'rxjs';
import {ContextService, FocusMonitor, IS_PLATFORM_HOST, MicroApplicationConfig, OUTLET_CONTEXT, OutletContext} from '@scion/microfrontend-platform';
import {APP_IDENTITY, ContextService, FocusMonitor, IS_PLATFORM_HOST, OUTLET_CONTEXT, OutletContext} from '@scion/microfrontend-platform';
import {switchMapTo, takeUntil} from 'rxjs/operators';
import {ActivatedRoute} from '@angular/router';
import {Defined} from '@scion/toolkit/util';
Expand All @@ -29,12 +29,10 @@ export class AppShellComponent implements OnDestroy {
public pageTitle: string;
public isFocusWithin: boolean;
public isDevToolsOpened = false;

@HostBinding('class.top-window')
public isPlatformHost = Beans.get<boolean>(IS_PLATFORM_HOST);

constructor() {
this.appSymbolicName = Beans.get(MicroApplicationConfig).symbolicName;
this.appSymbolicName = Beans.get<string>(APP_IDENTITY);

this.installFocusWithinListener();
this.installRouteActivateListener();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
* SPDX-License-Identifier: EPL-2.0
*/
import {ChangeDetectionStrategy, Component, ElementRef, HostBinding, Injector, Input, ViewChild} from '@angular/core';
import {ManifestService, OutletRouter, SciRouterOutletElement} from '@scion/microfrontend-platform';
import {OutletRouter, SciRouterOutletElement} from '@scion/microfrontend-platform';
import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms';
import {Overlay} from '@angular/cdk/overlay';
import {RouterOutletContextComponent} from '../router-outlet-context/router-outlet-context.component';
import {RouterOutletSettingsComponent} from '../router-outlet-settings/router-outlet-settings.component';
import {ActivatedRoute} from '@angular/router';
import {Beans} from '@scion/toolkit/bean-manager';
import {environment} from '../../environments/environment';

export const URL = 'url';

Expand Down Expand Up @@ -102,11 +103,7 @@ export class BrowserOutletComponent {
}

private readAppEntryPoints(): AppEndpoint[] {
const apps = Beans.get(ManifestService).applications;
return apps.reduce((endpoints, application) => {
const origin = application.origin;
const symbolicName = application.symbolicName;

return Object.values(environment.apps).reduce((endpoints, app) => {
return endpoints.concat(this._activatedRoute.parent.routeConfig.children
.filter(route => !!route.data)
.map(route => {
Expand All @@ -115,11 +112,10 @@ export class BrowserOutletComponent {
.reduce((encoded, paramKey) => encoded.concat(`${paramKey}=${matrixParams.get(paramKey)}`), [])
.join(';');
return {
url: `${origin}/#/${route.path}${matrixParamsEncoded ? `;${matrixParamsEncoded}` : ''}`,
label: `${symbolicName}: ${route.data['pageTitle']}`,
url: `${app.url}/#/${route.path}${matrixParamsEncoded ? `;${matrixParamsEncoded}` : ''}`,
label: `${app.symbolicName}: ${route.data['pageTitle']}`,
};
}));

}, new Array<AppEndpoint>());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/
import {Component} from '@angular/core';
import {FormArray, FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms';
import {Capability, ManifestObjectFilter, ManifestService, MicroApplicationConfig, ParamDefinition} from '@scion/microfrontend-platform';
import {APP_IDENTITY, Capability, ManifestObjectFilter, ManifestService, ParamDefinition} from '@scion/microfrontend-platform';
import {SciParamsEnterComponent} from '@scion/toolkit.internal/widgets';
import {Observable} from 'rxjs';
import {Beans} from '@scion/toolkit/bean-manager';
Expand Down Expand Up @@ -64,7 +64,7 @@ export class RegisterCapabilityComponent {
[APP_SYMBOLIC_NAME]: new FormControl(''),
});

this.capabilities$ = Beans.get(ManifestService).lookupCapabilities$({appSymbolicName: Beans.get(MicroApplicationConfig).symbolicName});
this.capabilities$ = Beans.get(ManifestService).lookupCapabilities$({appSymbolicName: Beans.get<string>(APP_IDENTITY)});
}

public onRegister(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/
import {Component} from '@angular/core';
import {FormArray, FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms';
import {Intention, ManifestObjectFilter, ManifestService, MicroApplicationConfig} from '@scion/microfrontend-platform';
import {APP_IDENTITY, Intention, ManifestObjectFilter, ManifestService} from '@scion/microfrontend-platform';
import {SciParamsEnterComponent} from '@scion/toolkit.internal/widgets';
import {Observable} from 'rxjs';
import {Beans} from '@scion/toolkit/bean-manager';
Expand Down Expand Up @@ -57,7 +57,7 @@ export class RegisterIntentionComponent {
[APP_SYMBOLIC_NAME]: new FormControl(''),
});

this.intentions$ = Beans.get(ManifestService).lookupIntentions$({appSymbolicName: Beans.get(MicroApplicationConfig).symbolicName});
this.intentions$ = Beans.get(ManifestService).lookupIntentions$({appSymbolicName: Beans.get<string>(APP_IDENTITY)});
}

public onRegister(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ export class PlatformInitializer implements OnDestroy {
this.installMessageInterceptors();
this.installIntentInterceptors();

// Read the apps from the environment
const apps: ApplicationConfig[] = Object.values(environment.apps).map(app => {
// Read testing apps to be registered from the environment
const testingAppConfigs: ApplicationConfig[] = Object.values(environment.apps).map(app => {
return {
manifestUrl: `${app.url}/assets/${app.symbolicName}-manifest${manifestClassifier}.json`,
activatorLoadTimeout: app.activatorLoadTimeout,
Expand All @@ -68,9 +68,9 @@ export class PlatformInitializer implements OnDestroy {
};
});

// Load the devtools
// Register devtools app if enabled for this environment
if (environment.devtools) {
apps.push(environment.devtools);
testingAppConfigs.push(environment.devtools);
}

// Log the startup progress (startup-progress.e2e-spec.ts).
Expand All @@ -88,11 +88,11 @@ export class PlatformInitializer implements OnDestroy {
// Run the microfrontend platform as host app
await this._zone.runOutsideAngular(() => {
return MicrofrontendPlatform.startHost({
apps: apps,
applications: testingAppConfigs,
activatorLoadTimeout: environment.activatorLoadTimeout,
activatorApiDisabled: activatorApiDisabled,
properties: Array.from(this._queryParams.keys()).reduce((dictionary, key) => ({...dictionary, [key]: this._queryParams.get(key)}), {}),
platformFlags: {activatorApiDisabled: activatorApiDisabled},
}, {symbolicName: determineAppSymbolicName()});
});
},
);

Expand All @@ -114,7 +114,7 @@ export class PlatformInitializer implements OnDestroy {
});

// Run the microfrontend platform as client app
return this._zone.runOutsideAngular(() => MicrofrontendPlatform.connectToHost({symbolicName: determineAppSymbolicName()}));
return this._zone.runOutsideAngular(() => MicrofrontendPlatform.connectToHost(getCurrentTestingAppSymbolicName()));
}

private installMessageInterceptors(): void {
Expand Down Expand Up @@ -236,7 +236,7 @@ export class PlatformInitializer implements OnDestroy {
/**
* Identifies the currently running app based on the configured apps in the environment and the current URL.
*/
function determineAppSymbolicName(): string {
function getCurrentTestingAppSymbolicName(): string {
const application = Object.values(environment.apps).find(app => new URL(app.url).host === window.location.host);
if (!application) {
throw Error(`[AppError] Application served on wrong URL. Supported URLs are: ${Object.values(environment.apps).map(app => app.url)}`);
Expand Down
Loading

0 comments on commit 142ce8e

Please sign in to comment.