feat: allow defining signal type
this makes using especially the frontend API much usable as the signal can be typed instead having plain json object to play around with. Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-signals-react': patch
|
||||
'@backstage/plugin-signals-node': patch
|
||||
'@backstage/plugin-signals': patch
|
||||
---
|
||||
|
||||
Allow defining signal type to publish and receive
|
||||
@@ -11,19 +11,23 @@ import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
export class DefaultSignalService implements SignalService {
|
||||
// (undocumented)
|
||||
static create(options: SignalServiceOptions): DefaultSignalService;
|
||||
publish(signal: SignalPayload): Promise<void>;
|
||||
publish<SignalType extends JsonObject = JsonObject>(
|
||||
signal: SignalPayload<SignalType>,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type SignalPayload = {
|
||||
export type SignalPayload<SignalType extends JsonObject = JsonObject> = {
|
||||
recipients: string[] | string | null;
|
||||
channel: string;
|
||||
message: JsonObject;
|
||||
message: SignalType;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface SignalService {
|
||||
publish(signal: SignalPayload): Promise<void>;
|
||||
publish<SignalType extends JsonObject = JsonObject>(
|
||||
signal: SignalPayload<SignalType>,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import { EventBroker } from '@backstage/plugin-events-node';
|
||||
import { SignalPayload, SignalServiceOptions } from './types';
|
||||
import { SignalService } from './SignalService';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
|
||||
/** @public */
|
||||
export class DefaultSignalService implements SignalService {
|
||||
@@ -31,12 +32,12 @@ export class DefaultSignalService implements SignalService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes a message to user refs to specific topic
|
||||
* @param recipients - string or array of user ref strings to publish message to
|
||||
* @param topic - message topic
|
||||
* @param message - message to publish
|
||||
* Publishes a signal to user refs to specific topic
|
||||
* @param signal - Signal to publish
|
||||
*/
|
||||
async publish(signal: SignalPayload) {
|
||||
async publish<SignalType extends JsonObject = JsonObject>(
|
||||
signal: SignalPayload<SignalType>,
|
||||
) {
|
||||
await this.eventBroker?.publish({
|
||||
topic: 'signals',
|
||||
eventPayload: signal,
|
||||
|
||||
@@ -14,11 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { SignalPayload } from './types';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
|
||||
/** @public */
|
||||
export interface SignalService {
|
||||
/**
|
||||
* Publishes a message to user refs to specific topic
|
||||
* Publishes a signal to user refs to specific topic
|
||||
* @param signal - Signal to publish
|
||||
*/
|
||||
publish(signal: SignalPayload): Promise<void>;
|
||||
publish<SignalType extends JsonObject = JsonObject>(
|
||||
signal: SignalPayload<SignalType>,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ export type SignalServiceOptions = {
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type SignalPayload = {
|
||||
export type SignalPayload<SignalType extends JsonObject = JsonObject> = {
|
||||
recipients: string[] | string | null;
|
||||
channel: string;
|
||||
message: JsonObject;
|
||||
message: SignalType;
|
||||
};
|
||||
|
||||
@@ -9,9 +9,9 @@ import { JsonObject } from '@backstage/types';
|
||||
// @public (undocumented)
|
||||
export interface SignalApi {
|
||||
// (undocumented)
|
||||
subscribe(
|
||||
subscribe<SignalType extends JsonObject = JsonObject>(
|
||||
channel: string,
|
||||
onMessage: (message: JsonObject) => void,
|
||||
onMessage: (message: SignalType) => void,
|
||||
): SignalSubscriber;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,10 @@ export interface SignalSubscriber {
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const useSignal: (channel: string) => {
|
||||
lastSignal: JsonObject | null;
|
||||
export const useSignal: <SignalType extends JsonObject = JsonObject>(
|
||||
channel: string,
|
||||
) => {
|
||||
lastSignal: SignalType | null;
|
||||
isSignalsAvailable: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ export interface SignalSubscriber {
|
||||
|
||||
/** @public */
|
||||
export interface SignalApi {
|
||||
subscribe(
|
||||
subscribe<SignalType extends JsonObject = JsonObject>(
|
||||
channel: string,
|
||||
onMessage: (message: JsonObject) => void,
|
||||
onMessage: (message: SignalType) => void,
|
||||
): SignalSubscriber;
|
||||
}
|
||||
|
||||
@@ -19,18 +19,23 @@ import { JsonObject } from '@backstage/types';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
/** @public */
|
||||
export const useSignal = (channel: string) => {
|
||||
export const useSignal = <SignalType extends JsonObject = JsonObject>(
|
||||
channel: string,
|
||||
): { lastSignal: SignalType | null; isSignalsAvailable: boolean } => {
|
||||
const apiHolder = useApiHolder();
|
||||
// Use apiHolder instead useApi in case signalApi is not available in the
|
||||
// backstage instance this is used
|
||||
const signals = apiHolder.get(signalApiRef);
|
||||
const [lastSignal, setLastSignal] = useState<JsonObject | null>(null);
|
||||
const [lastSignal, setLastSignal] = useState<SignalType | null>(null);
|
||||
useEffect(() => {
|
||||
let unsub: null | (() => void) = null;
|
||||
if (signals) {
|
||||
const { unsubscribe } = signals.subscribe(channel, (msg: JsonObject) => {
|
||||
setLastSignal(msg);
|
||||
});
|
||||
const { unsubscribe } = signals.subscribe<SignalType>(
|
||||
channel,
|
||||
(msg: SignalType) => {
|
||||
setLastSignal(msg);
|
||||
},
|
||||
);
|
||||
unsub = unsubscribe;
|
||||
}
|
||||
return () => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { DiscoveryApi } from '@backstage/core-plugin-api';
|
||||
import { IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { SignalApi } from '@backstage/plugin-signals-react';
|
||||
import { SignalSubscriber } from '@backstage/plugin-signals-react';
|
||||
|
||||
// @public (undocumented)
|
||||
export class SignalClient implements SignalApi {
|
||||
@@ -23,12 +24,10 @@ export class SignalClient implements SignalApi {
|
||||
// (undocumented)
|
||||
static readonly DEFAULT_RECONNECT_TIMEOUT_MS: number;
|
||||
// (undocumented)
|
||||
subscribe(
|
||||
subscribe<SignalType extends JsonObject = JsonObject>(
|
||||
channel: string,
|
||||
onMessage: (message: JsonObject) => void,
|
||||
): {
|
||||
unsubscribe: () => void;
|
||||
};
|
||||
onMessage: (message: SignalType) => void,
|
||||
): SignalSubscriber;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { SignalApi } from '@backstage/plugin-signals-react';
|
||||
import { SignalApi, SignalSubscriber } from '@backstage/plugin-signals-react';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
type Subscription = {
|
||||
channel: string;
|
||||
callback: (message: JsonObject) => void;
|
||||
callback: (message: any) => void;
|
||||
};
|
||||
|
||||
const WS_CLOSE_NORMAL = 1000;
|
||||
@@ -62,16 +62,16 @@ export class SignalClient implements SignalApi {
|
||||
private reconnectTimeout: number,
|
||||
) {}
|
||||
|
||||
subscribe(
|
||||
subscribe<SignalType extends JsonObject = JsonObject>(
|
||||
channel: string,
|
||||
onMessage: (message: JsonObject) => void,
|
||||
): { unsubscribe: () => void } {
|
||||
onMessage: (message: SignalType) => void,
|
||||
): SignalSubscriber {
|
||||
const subscriptionId = uuid();
|
||||
const exists = [...this.subscriptions.values()].find(
|
||||
sub => sub.channel === channel,
|
||||
);
|
||||
this.subscriptions.set(subscriptionId, {
|
||||
channel: channel,
|
||||
channel,
|
||||
callback: onMessage,
|
||||
});
|
||||
|
||||
@@ -178,12 +178,14 @@ export class SignalClient implements SignalApi {
|
||||
|
||||
private handleMessage(data: MessageEvent) {
|
||||
try {
|
||||
const json = JSON.parse(data.data) as JsonObject;
|
||||
if (json.channel) {
|
||||
for (const sub of this.subscriptions.values()) {
|
||||
if (sub.channel === json.channel) {
|
||||
sub.callback(json.message as JsonObject);
|
||||
}
|
||||
const json = JSON.parse(data.data);
|
||||
if (!json.channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const sub of this.subscriptions.values()) {
|
||||
if (sub.channel === json.channel) {
|
||||
sub.callback(json.message);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user