feat: add type definition to signal recipients

follows more the notifications way of setting recipients and is more
future proof

Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
Heikki Hellgren
2024-03-13 13:10:55 +02:00
parent b76d0cd4f9
commit 6c1547a4fb
9 changed files with 41 additions and 18 deletions
+10
View File
@@ -0,0 +1,10 @@
---
'@backstage/plugin-notifications-backend': patch
'@backstage/plugin-signals-backend': minor
'@backstage/plugin-signals-node': minor
---
**BREAKING** Type definition added to signal recipients
Update to use `{type: 'broadcast'}` instead `null` and `{type: 'user', entityRefs: ''}`
instead string entity references
@@ -286,7 +286,7 @@ export async function createRouter(
if (signals) {
await signals.publish<NotificationReadSignal>({
recipients: [user],
recipients: { type: 'user', entityRefs: [user] },
message: { action: 'notification_read', notification_ids: ids },
channel: 'notifications',
});
@@ -296,7 +296,7 @@ export async function createRouter(
if (signals) {
await signals.publish<NotificationReadSignal>({
recipients: [user],
recipients: { type: 'user', entityRefs: [user] },
message: { action: 'notification_unread', notification_ids: ids },
channel: 'notifications',
});
@@ -400,7 +400,7 @@ export async function createRouter(
if (signals) {
await signals.publish<NewNotificationSignal>({
recipients: user,
recipients: { type: 'user', entityRefs: [user] },
message: {
action: 'new_notification',
notification_id: ret.id,
@@ -89,7 +89,7 @@ describe('SignalManager', () => {
await onEvent({
topic: 'signals',
eventPayload: {
recipients: null,
recipients: { type: 'broadcast' },
channel: 'test',
message: { msg: 'test' },
},
@@ -109,7 +109,7 @@ describe('SignalManager', () => {
await onEvent({
topic: 'signals',
eventPayload: {
recipients: null,
recipients: { type: 'broadcast' },
channel: 'test',
message: { msg: 'test' },
},
@@ -152,7 +152,7 @@ describe('SignalManager', () => {
await onEvent({
topic: 'signals',
eventPayload: {
recipients: 'user:default/john.doe',
recipients: { type: 'user', entityRefs: 'user:default/john.doe' },
channel: 'test',
message: { msg: 'test' },
},
@@ -134,8 +134,10 @@ export class SignalManager {
const { channel, recipients, message } = eventPayload;
const jsonMessage = JSON.stringify({ channel, message });
let users: string[] = [];
if (recipients !== null) {
users = Array.isArray(recipients) ? recipients : [recipients];
if (recipients.type === 'user') {
users = Array.isArray(recipients.entityRefs)
? recipients.entityRefs
: [recipients.entityRefs];
}
// Actual websocket message sending
@@ -143,9 +145,10 @@ export class SignalManager {
if (!conn.subscriptions.has(channel)) {
return;
}
// Sending to all users can be done with null
// Sending to all users can be done with broadcast
if (
recipients !== null &&
recipients.type !== 'broadcast' &&
!conn.ownershipEntityRefs.some((ref: string) => users.includes(ref))
) {
return;
@@ -97,7 +97,7 @@ export async function startStandaloneServer(
setInterval(() => {
signals.publish({
recipients: null,
recipients: { type: 'broadcast' },
channel: 'test',
message: { hello: 'world' },
});
+3 -3
View File
@@ -91,13 +91,13 @@ To allow connections from the frontend, you should also install the `@backstage/
Once you have both of the backend plugins installed, you can utilize the signal service by calling the
`publish` method. This will publish the message to all subscribers in the frontend. To send message to
all subscribers, you can use `null` as `recipients` parameter.
all subscribers, you can use `broadcast` type:
```ts
// Periodic sending example
setInterval(async () => {
await signals.publish({
recipients: null,
recipients: { type: 'broadcast' },
channel: 'my_plugin',
message: {
message: 'hello world',
@@ -118,7 +118,7 @@ to work:
eventBroker.publish({
topic: 'signals',
eventPayload: {
recipients: ['user:default/user1'],
recipients: { type: 'user', entityRefs: ['user:default/user1'] },
message: {
message: 'hello world',
},
+8 -1
View File
@@ -21,7 +21,14 @@ export class DefaultSignalsService implements SignalsService {
// @public (undocumented)
export type SignalPayload<TMessage extends JsonObject = JsonObject> = {
recipients: string[] | string | null;
recipients:
| {
type: 'user';
entityRefs: string | string[];
}
| {
type: 'broadcast';
};
channel: string;
message: TMessage;
};
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { DefaultSignalsService } from './DefaultSignalsService';
import { SignalPayload } from './types';
describe('DefaultSignalsService', () => {
const mockEvents = {
@@ -24,9 +25,9 @@ describe('DefaultSignalsService', () => {
const service = DefaultSignalsService.create({ events: mockEvents });
it('should publish signal', () => {
const signal = {
const signal: SignalPayload = {
channel: 'test-channel',
recipients: null,
recipients: { type: 'broadcast' },
message: { msg: 'hello world' },
};
service.publish(signal);
+3 -1
View File
@@ -25,7 +25,9 @@ export type SignalsServiceOptions = {
/** @public */
export type SignalPayload<TMessage extends JsonObject = JsonObject> = {
recipients: string[] | string | null;
recipients:
| { type: 'user'; entityRefs: string | string[] }
| { type: 'broadcast' };
channel: string;
message: TMessage;
};