Table of Contents

Communication

Publishing a message

Since version 2.5, you have been able to publish messages to one or more tenants or to a specific version of a cluster. A common use case is the announcement of an upcoming update.

A message is composed of a title and a detail. Publication consists of sending a pub sub with the topic name TenantManagementMessage.

From the tenants screen

You can publish a message to a selection of tenants from the tenants screen using the dedicated button :

From the cluster screen

You can publish a message to a selection of versions from the cluster screen using the dedicated button :

From the API

It is also possible to send a message by calling the POST /api/v1/messages API of the Tenant Management. The request body contains the following properties:

Property Type Description
title Localizable string The title of the message, as an object whose keys are culture codes and whose values are the texts
message Localizable string The content of the message, in the same format as title
tenants Array of { identifier, name } The tenants to whom the message is addressed, by tenant identifier. The optional name is passed as is to the receivers
clusters Array of { name, version } The cluster versions to whom the message is addressed, by cluster name and version
{
  "title": { "en": "Scheduled maintenance", "fr": "Maintenance programmée" },
  "message": { "en": "The application will be unavailable tonight from 10 PM.", "fr": "L'application sera indisponible ce soir à partir de 22 h." },
  "tenants": [{ "identifier": "my-tenant", "name": "My tenant" }],
  "clusters": []
}

The receiver of the NeosNotificationCenter module treats an empty tenants array as all tenants and an empty clusters array as all clusters.

Receiving the message in your cluster

From the module NeosNotificationCenter

The module incorporates a message receiver. It displays messages in the form of warning toast. To find out more, see the documentation on notification center.

From your own server method

To receive the message in your cluster, you must subscribe to the event TenantManagementMessage in a server method. The server method receives a TenantManagementMessageArguments data object, with the Title, Message, Tenants and Clusters properties described in From the API. Each tenant carries an Identifier, and a Name only when the publisher sent one. Each cluster carries a Name and a Version. To find out more about event subscription, see the documentation on pub/sub.

Here is an example of a server method that receives the message and displays it in the console.

// Injected services
private readonly IUserNotification _userNotification;
private readonly IApplicationInfo _applicationInfo;
private readonly INeosTenantInfoAccessor _tenantAccessor;
private readonly INeosLogger<ITenantManagementMessageReception> _logger;

public Task ExecuteAsync(Neos.UserNotifications.Application.Abstractions.DataObjects.TenantManagementMessageArguments tenantManagementMessageArguments, CancellationToken cancellationToken)
{
    // Get information about the current tenant and cluster
    string? currentTenantIdentifier = _tenantAccessor.NeosTenantInfo?.Identifier;
    string currentClusterName = _applicationInfo.ClusterName;
    string currentClusterVersion = _applicationInfo.ClusterVersion;

    // Log the received message and its targeting details
    _logger.LogInformation($"Received message in cluster {currentClusterName}-{currentClusterVersion}/{currentTenantIdentifier} (" +
        $"tenants: {string.Join(", ", tenantManagementMessageArguments.Tenants.Select(t => t.Identifier))}; " +
        $"clusters: {string.Join(", ", tenantManagementMessageArguments.Clusters.Select(c => c.Name + "-" + c.Version))}; " +
        $"title: {tenantManagementMessageArguments.Title}; message: {tenantManagementMessageArguments.Message})");

    if (currentTenantIdentifier != null &&
        // Determine if the message is for the current tenant
        // If no tenant identifiers are specified, the message is for all tenants
        (tenantManagementMessageArguments.Tenants.Length == 0 ||
            tenantManagementMessageArguments.Tenants.Select(t => t.Identifier).Contains(currentTenantIdentifier)) &&
        // Determine if the message is for the current cluster
        // If no cluster names are specified, the message is for all clusters
        (tenantManagementMessageArguments.Clusters.Length == 0 ||
            (tenantManagementMessageArguments.Clusters.Select(t => t.Name).Contains(currentClusterName) &&
            tenantManagementMessageArguments.Clusters.Select(t => t.Version).Contains(currentClusterVersion))))
    {
        // Send notification to all users in the current tenant
        UserNotificationOptions notificationOptions = new UserNotificationOptions(
                tenantManagementMessageArguments.Title,
                tenantManagementMessageArguments.Message,
                UserNotificationCategories.TenantManagementMessageReception)
            .WithSeverity(UserNotificationSeverity.Warning);
        notificationOptions.ToAllUsersInCurrentTenant();
        await _userNotification.SendNotificationAsync(notificationOptions, cancellationToken);
    }
}