Skip to content
4 changes: 3 additions & 1 deletion .github/workflows/deploy-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ defaults:
working-directory: ./site

permissions:
contents: write
packages: read

jobs:
build:
runs-on: ubuntu-latest

permissions:
contents: write

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Use Node.js
Expand Down
2 changes: 2 additions & 0 deletions site/content/documentation/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ order: 1
title: Documentation
---

{/* Morgan Stanley makes this available to you under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. See the NOTICE file distributed with this work for additional information regarding copyright ownership. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}

<Hero title="ComposeUI">
Open source is more than just code. There is planning before code is written,
the process on how to contribute or release that code, and fostering an
Expand Down
2 changes: 2 additions & 0 deletions site/content/documentation/v0.1.0-alpha.4/examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ order: 1
title: Examples
---

{/* Morgan Stanley makes this available to you under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. See the NOTICE file distributed with this work for additional information regarding copyright ownership. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}

### FCD3 App Directory example

This example contains a JSON file that can be used as the data source for ComposeUI's FDC3 App Directory implementation. The App Directory contains the [js-chart](https://github.com/morganstanley/ComposeUI/tree/main/examples/js-chart) and [js-grid](https://github.com/morganstanley/ComposeUI/tree/main/examples/js-datagrid) examples.
Expand Down
201 changes: 116 additions & 85 deletions site/content/documentation/v0.1.0-alpha.4/message-router.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ order: 2
title: Message Router API documentation
---

{/* Morgan Stanley makes this available to you under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. See the NOTICE file distributed with this work for additional information regarding copyright ownership. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}

The Message Router is responsible for every communication that happens between processes. It is an independent pluggable module that runs under the main ComposeUI process, delivering messages between processes and optionally other devices.
Message Router is an open-source out-of-the-box library that is fast and reliable. It supports JSON and it also uses WebSockets and has a Javascript library, so Web-based applications can be integrated.
Message Router is an open-source out-of-the-box library that is fast and reliable. It supports JSON and it also uses WebSockets and has a Javascript library, so Web-based applications can be integrated.
So basically it is real-time solution that can be easily setup for Pub-Sub messaging.

More details can be found under the [ADR-012](https://github.com/morganstanley/ComposeUI/blob/main/architecture/adr-012-message-router.md).
The Message Router is an ASP.NET Core-based backend service that exposes one or more endpoints that the applications can connect to.

### Setup

Message Router Server can be setup in .NET by adding to the `ServiceCollection` and its configuration like `MessageRouterWebSocketServerOptions` after you have added the nuget package: `MorganStanley.ComposeUI.Messaging.Server`.

Example:

```csharp
IHostBuilder builder = new HostBuilder();

Expand Down Expand Up @@ -46,6 +50,7 @@ where with the `ConfigureServer` method the `MessageRouterWebSocketServerOptions
The server implementation is only available on the .NET side.

Also the Message Router Client can be set up in .NET easily with dependecy injection. You are able to call the `AddMessageRouter()` extension method on a `IServiceCollection` where you can configure `MessageRouterWebSocketOptions` - [reference](https://github.com/morganstanley/ComposeUI/blob/main/src/messaging/dotnet/src/Client/Client/WebSocket/MessageRouterWebSocketOptions.cs), within the `.UseWebSocket()` builder method and the AccessToken with the `UseAccessToken` after you have added the nuget package: `MorganStanley.ComposeUI.Messaging.Client`.

```csharp
var services = new ServiceCollection()
.AddMessageRouter(
Expand All @@ -60,11 +65,13 @@ var services = new ServiceCollection()
```

In Javascript/Typescript you should import the `createMessageRouter` from the `@morgan-stanley/composeui-messaging-client` library.

```javascript
import { createMessageRouter } from "@morgan-stanley/composeui-messaging-client";
import { createMessageRouter } from '@morgan-stanley/composeui-messaging-client';
```

Use `createMessageRouter()` to instantiate the MessageRouter client in a ComposeUI application. It will connect to the MessageRouter hosted by the container when you call `connect()` on the client.

```javascript
let client = createMessageRouter();
```
Expand All @@ -81,140 +88,164 @@ To directly connect to the server, you can call the `ConnectAsync` method from t
In .NET you can connect to the server endpoint, by passing a `CancellationToken` as well which can control the connection lifetime. Not setting that will fallback on its default value which is `CancellationToken.None`.

In Javascript/Typescript you are also able to achieve this by calling the `connect` method which returns a `Promise<void>`.

```javascript
await client.connect();
```

### Usage
### Usage

#### Subscribe to a topic

Use the subscribe method of the client to set a handler on a topic. The message parameter of the handler method contains the payload as a string. The following example parses a JSON payload from the "exampleTopic" topic and logs it to console.

- .NET
```csharp
ValuTask HandleTopicMessage(MessageBuffer? payload)
{
Console.WriteLine(payload.GetString());
return ValueTask.CompletedTask;
}

await _messageRouter.SubscribeAsync(
"exampleTopic",
AsyncObserver.Create<TopicMessage>(x => HandleTopicMessage(x.Payload)));
```
It returns an `IAsyncDisposable` object.
```csharp
ValuTask HandleTopicMessage(MessageBuffer? payload)
{
Console.WriteLine(payload.GetString());
return ValueTask.CompletedTask;
}

await _messageRouter.SubscribeAsync(
"exampleTopic",
AsyncObserver.Create<TopicMessage>(x => HandleTopicMessage(x.Payload)));
```

It returns an `IAsyncDisposable` object.

- Javascript/TypeScript
```javascript
client.subscribe('exampleTopic', (message) => {
const payload = JSON.parse(message.payload);
console.log(payload);
});
```
It returns an `Unsubscribable` object.
```javascript
client.subscribe('exampleTopic', (message) => {
const payload = JSON.parse(message.payload);
console.log(payload);
});
```
It returns an `Unsubscribable` object.

#### Publish a message

Use the publish method of the client to publish a message to a topic. The payload of the message must be a string. The following example creates a JSON string out of an object, and publishes it to the "exampleTopic" topic.
- .NET
```csharp
await _messageRouter.PublishAsync(
'exampleTopic',
MessageBuffer.Factory.CreateJson(payloadObject, _jsonSerializerOptions));
```
where `_jsonSerializerOptions` is your own defined serializer options to be used with the `JsonSerializer`.

- .NET

```csharp
await _messageRouter.PublishAsync(
'exampleTopic',
MessageBuffer.Factory.CreateJson(payloadObject, _jsonSerializerOptions));
```

where `_jsonSerializerOptions` is your own defined serializer options to be used with the `JsonSerializer`.

- Javascript/Typescript
```javascript
await client.publish('exampleTopic', JSON.stringify(payloadObject));
```
```javascript
await client.publish('exampleTopic', JSON.stringify(payloadObject));
```

#### Invoking a service

Use the invoke method of the client to invoke a registered service's handler and get the response from it. The payload must be a string.

- .NET
```csharp
var resultBuffer = await _messageRouter.InvokeAsync(
"exampleTopic",
MessageBuffer.Factory.CreateJson(payloadObject, _jsonSerializerOptions));
```
This results an `MessageBuffer` -see [reference](https://github.com/morganstanley/ComposeUI/blob/main/src/messaging/dotnet/src/Core/MessageBuffer.cs), type where you could call the `ReadJson<T>` extension method to convert the buffer to the expected type that the called service should return eg. `var result = resultBuffer.ReadJson<MyClass>(_jsonSerializerOptions);`.

```csharp
var resultBuffer = await _messageRouter.InvokeAsync(
"exampleTopic",
MessageBuffer.Factory.CreateJson(payloadObject, _jsonSerializerOptions));
```

This results an `MessageBuffer` -see [reference](https://github.com/morganstanley/ComposeUI/blob/main/src/messaging/dotnet/src/Core/MessageBuffer.cs), type where you could call the `ReadJson<T>` extension method to convert the buffer to the expected type that the called service should return eg. `var result = resultBuffer.ReadJson<MyClass>(_jsonSerializerOptions);`.

- Javascript/Typescript
```javascript
const resultBuffer = await this.messageRouterClient.invoke('exampleTopic', JSON.stringify(payloadObject));
const result = <MyClass>JSON.parse(resultBuffer);
```
```javascript
const resultBuffer = await this.messageRouterClient.invoke('exampleTopic', JSON.stringify(payloadObject));
const result = <MyClass>JSON.parse(resultBuffer);
```

#### Registering a service

Use the register service method to register a service by providing its name and its handler. The handler should return the type of `MessageBuffer?` so when a client calls the service they will receive the response from the registered handler function.

- .NET
```csharp
await _messageRouter.RegisterServiceAsync(
"exampleTopic",
(endpoint, payload, context) =>
{
Console.WriteLine("Payload is handled.");
return new ValueTask<MessageBuffer?>(MessageBuffer.Create("Hello"));
});
```

```csharp
await _messageRouter.RegisterServiceAsync(
"exampleTopic",
(endpoint, payload, context) =>
{
Console.WriteLine("Payload is handled.");
return new ValueTask<MessageBuffer?>(MessageBuffer.Create("Hello"));
});
```

- Javascript/Typescript
```javascript
await client.registerService(
"exampleTopic",
() => Promise.resolve(),
{ description: "This is a test service" });
```
```javascript
await client.registerService('exampleTopic', () => Promise.resolve(), {
description: 'This is a test service',
});
```

#### Unregistering a service

Use the unregister service method if you want to remove the service registration.

- .NET
```csharp
await _messageRouter.UnregisterServiceAsync("exampleTopic", cancellationToken);
```

```csharp
await _messageRouter.UnregisterServiceAsync("exampleTopic", cancellationToken);
```

- Javascript/Typescript
```javascript
await client.unregisterService("exampleTopic");
```
```javascript
await client.unregisterService('exampleTopic');
```

#### Registering an endpoint

Use register an endpoint method if you want to register an endpoint by providing a name, handler and optional descriptor - and of course optionally the cancellationToken. The main difference between the RegisterService and RegisterEndpoint is that when we register a service, then we send a register service call to the server so it gets registered on the server side while the endpoint is only registering the endpoint on the client so that endpoint could not be used for another service registered by the client.
The invoke request in this case would be solved "locally".

- .NET
```csharp
await _messageRouter.RegisterEndpointAsync(
"exampleTopic",
(endpoint, payload, context) =>
{
Console.WriteLine("Payload is handled.");
return new ValueTask<MessageBuffer?>(MessageBuffer.Create("Hello"));
});
```

```csharp
await _messageRouter.RegisterEndpointAsync(
"exampleTopic",
(endpoint, payload, context) =>
{
Console.WriteLine("Payload is handled.");
return new ValueTask<MessageBuffer?>(MessageBuffer.Create("Hello"));
});
```

- Javascript/Typescript
```javascript
await client.registerEndpoint("exampleTopic", (endpoint, payload, context) => {
console.log("Payload is handled.");
return Promise.resolve();
});
```
```javascript
await client.registerEndpoint(
'exampleTopic',
(endpoint, payload, context) => {
console.log('Payload is handled.');
return Promise.resolve();
}
);
```

#### Unregistering an endpoint

Use the unregister endpoint method if you want to remove an endpoint.

- NET
```csharp
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(2));
await _messageRouter.UnregisterEndpointAsync("exampleTopic", cancellationTokenSource.Token);
```

```csharp
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(2));
await _messageRouter.UnregisterEndpointAsync("exampleTopic", cancellationTokenSource.Token);
```

- Javascript/Typescript
```javascript
await client.unregisterEndpoint("exampleTopic");
```
```javascript
await client.unregisterEndpoint('exampleTopic');
```

There are some optional values the above mentioned methods/functions eg.: `CancellationToken` or `EndpointDescriptior`, with you can add additional details to the Message Router or control the call, but you'll find every detail when you are using the API as MessageRouter is well documented.
If you are interested explicitely on the API, you can find the source code on GitHub ([Typescript](https://github.com/morganstanley/ComposeUI/blob/main/src/messaging/js/composeui-messaging-client/src/MessageRouter.ts) and [.NET](https://github.com/morganstanley/ComposeUI/blob/main/src/messaging/dotnet/src/Core/IMessageRouter.cs)).

Example code snippets can be found under the [examples folder](https://github.com/morganstanley/ComposeUI/tree/main/examples/js-chart-and-grid-messagerouter).

2 changes: 2 additions & 0 deletions site/content/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ order: 1
title: Homepage
---

{/* Morgan Stanley makes this available to you under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. See the NOTICE file distributed with this work for additional information regarding copyright ownership. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}

<Hero title="ComposeUI">
ComposeUI is a .NET based general UI Container and Unified UI and App host which enables the hosting of Web and desktop content. It supports desktop and web applications in order to provide an evergreen alternative to Electron, OpenFin and similar by the use of WebView2.

Expand Down
Loading