feat: follow-up changes on BEP #22641
Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
@@ -19,14 +19,17 @@ exports.up = async function up(knex) {
|
||||
table.uuid('id').primary();
|
||||
table.string('userRef').notNullable();
|
||||
table.string('title').notNullable();
|
||||
table.text('description').notNullable();
|
||||
table.text('description').nullable();
|
||||
table.text('severity').notNullable();
|
||||
table.text('link').notNullable();
|
||||
table.text('origin').notNullable();
|
||||
table.text('scope').nullable();
|
||||
table.text('topic').nullable();
|
||||
table.datetime('created').defaultTo(knex.fn.now()).notNullable();
|
||||
table.datetime('updated').nullable();
|
||||
table.datetime('read').nullable();
|
||||
table.datetime('done').nullable();
|
||||
table.boolean('saved').defaultTo(false).notNullable();
|
||||
table.datetime('saved').nullable();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ export class DatabaseNotificationsStore implements NotificationsStore {
|
||||
}
|
||||
}
|
||||
|
||||
if ('ids' in options && options.ids) {
|
||||
if (options.ids) {
|
||||
query.whereIn('id', options.ids);
|
||||
}
|
||||
|
||||
@@ -131,13 +131,15 @@ export class DatabaseNotificationsStore implements NotificationsStore {
|
||||
};
|
||||
}
|
||||
|
||||
async getExistingTopicNotification(options: {
|
||||
async getExistingScopeNotification(options: {
|
||||
user_ref: string;
|
||||
topic: string;
|
||||
scope: string;
|
||||
origin: string;
|
||||
}) {
|
||||
const query = this.db('notifications')
|
||||
.where('userRef', options.user_ref)
|
||||
.where('topic', options.topic)
|
||||
.where('scope', options.scope)
|
||||
.where('origin', options.origin)
|
||||
.select('*')
|
||||
.limit(1);
|
||||
|
||||
@@ -156,11 +158,12 @@ export class DatabaseNotificationsStore implements NotificationsStore {
|
||||
.where('id', options.id)
|
||||
.where('userRef', options.notification.userRef);
|
||||
const rows = await query.update({
|
||||
title: options.notification.title,
|
||||
description: options.notification.description,
|
||||
link: options.notification.link,
|
||||
topic: options.notification.topic,
|
||||
title: options.notification.payload.title,
|
||||
description: options.notification.payload.description,
|
||||
link: options.notification.payload.link,
|
||||
topic: options.notification.payload.topic,
|
||||
updated: options.notification.created,
|
||||
severity: options.notification.payload.severity,
|
||||
read: null,
|
||||
done: null,
|
||||
});
|
||||
@@ -205,11 +208,11 @@ export class DatabaseNotificationsStore implements NotificationsStore {
|
||||
|
||||
async markSaved(options: NotificationModifyOptions): Promise<void> {
|
||||
const notificationQuery = this.getNotificationsBaseQuery(options);
|
||||
await notificationQuery.update({ saved: true });
|
||||
await notificationQuery.update({ saved: new Date() });
|
||||
}
|
||||
|
||||
async markUnsaved(options: NotificationModifyOptions): Promise<void> {
|
||||
const notificationQuery = this.getNotificationsBaseQuery(options);
|
||||
await notificationQuery.update({ saved: false });
|
||||
await notificationQuery.update({ saved: null });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import {
|
||||
/** @public */
|
||||
export type NotificationGetOptions = {
|
||||
user_ref: string;
|
||||
|
||||
ids?: string[];
|
||||
type?: NotificationType;
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
@@ -40,9 +42,10 @@ export interface NotificationsStore {
|
||||
|
||||
saveNotification(notification: Notification): Promise<void>;
|
||||
|
||||
getExistingTopicNotification(options: {
|
||||
getExistingScopeNotification(options: {
|
||||
user_ref: string;
|
||||
topic: string;
|
||||
scope: string;
|
||||
origin: string;
|
||||
}): Promise<Notification | null>;
|
||||
|
||||
restoreExistingNotification(options: {
|
||||
|
||||
@@ -209,104 +209,67 @@ export async function createRouter(
|
||||
res.send(status);
|
||||
});
|
||||
|
||||
router.post('/done', async (req, res) => {
|
||||
router.post('/update', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const { ids } = req.body;
|
||||
const { ids, done, read, saved } = req.body;
|
||||
if (!ids || !Array.isArray(ids)) {
|
||||
res.status(400).send();
|
||||
return;
|
||||
}
|
||||
await store.markDone({ user_ref: user, ids });
|
||||
if (done === true) {
|
||||
await store.markDone({ user_ref: user, ids });
|
||||
if (signalService) {
|
||||
await signalService.publish({
|
||||
recipients: [user],
|
||||
message: { action: 'done', notification_ids: ids },
|
||||
channel: 'notifications',
|
||||
});
|
||||
}
|
||||
} else if (done === false) {
|
||||
await store.markUndone({ user_ref: user, ids });
|
||||
if (signalService) {
|
||||
await signalService.publish({
|
||||
recipients: [user],
|
||||
message: { action: 'undone', notification_ids: ids },
|
||||
channel: 'notifications',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (signalService) {
|
||||
await signalService.publish({
|
||||
recipients: [user],
|
||||
message: { action: 'done', notification_ids: ids },
|
||||
channel: 'notifications',
|
||||
});
|
||||
}
|
||||
res.status(200).send({ ids });
|
||||
});
|
||||
if (read === true) {
|
||||
await store.markRead({ user_ref: user, ids });
|
||||
|
||||
router.post('/undo', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const { ids } = req.body;
|
||||
if (!ids || !Array.isArray(ids)) {
|
||||
res.status(400).send();
|
||||
return;
|
||||
}
|
||||
await store.markUndone({ user_ref: user, ids });
|
||||
if (signalService) {
|
||||
await signalService.publish({
|
||||
recipients: [user],
|
||||
message: { action: 'undone', notification_ids: ids },
|
||||
channel: 'notifications',
|
||||
});
|
||||
}
|
||||
res.status(200).send({ ids });
|
||||
});
|
||||
if (signalService) {
|
||||
await signalService.publish({
|
||||
recipients: [user],
|
||||
message: { action: 'mark_read', notification_ids: ids },
|
||||
channel: 'notifications',
|
||||
});
|
||||
}
|
||||
} else if (read === false) {
|
||||
await store.markUnread({ user_ref: user, ids });
|
||||
|
||||
router.post('/read', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const { ids } = req.body;
|
||||
if (!ids || !Array.isArray(ids)) {
|
||||
res.status(400).send();
|
||||
return;
|
||||
if (signalService) {
|
||||
await signalService.publish({
|
||||
recipients: [user],
|
||||
message: { action: 'mark_unread', notification_ids: ids },
|
||||
channel: 'notifications',
|
||||
});
|
||||
}
|
||||
}
|
||||
await store.markRead({ user_ref: user, ids });
|
||||
|
||||
if (signalService) {
|
||||
await signalService.publish({
|
||||
recipients: [user],
|
||||
message: { action: 'mark_read', notification_ids: ids },
|
||||
channel: 'notifications',
|
||||
});
|
||||
if (saved === true) {
|
||||
await store.markSaved({ user_ref: user, ids });
|
||||
} else if (saved === false) {
|
||||
await store.markUnsaved({ user_ref: user, ids });
|
||||
}
|
||||
res.status(200).send({ ids });
|
||||
});
|
||||
|
||||
router.post('/unread', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const { ids } = req.body;
|
||||
if (!ids || !Array.isArray(ids)) {
|
||||
res.status(400).send();
|
||||
return;
|
||||
}
|
||||
await store.markUnread({ user_ref: user, ids });
|
||||
if (signalService) {
|
||||
await signalService.publish({
|
||||
recipients: [user],
|
||||
message: { action: 'mark_unread', notification_ids: ids },
|
||||
channel: 'notifications',
|
||||
});
|
||||
}
|
||||
res.status(200).send({ ids });
|
||||
});
|
||||
|
||||
router.post('/save', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const { ids } = req.body;
|
||||
if (!ids || !Array.isArray(ids)) {
|
||||
res.status(400).send();
|
||||
return;
|
||||
}
|
||||
await store.markSaved({ user_ref: user, ids });
|
||||
res.status(200).send({ ids });
|
||||
});
|
||||
|
||||
router.post('/unsave', async (req, res) => {
|
||||
const user = await getUser(req);
|
||||
const { ids } = req.body;
|
||||
if (!ids || !Array.isArray(ids)) {
|
||||
res.status(400).send();
|
||||
return;
|
||||
}
|
||||
await store.markUnsaved({ user_ref: user, ids });
|
||||
res.status(200).send({ ids });
|
||||
const notifications = await store.getNotifications({ ids, user_ref: user });
|
||||
res.status(200).send(notifications);
|
||||
});
|
||||
|
||||
router.post('/notifications', async (req, res) => {
|
||||
const { receivers, title, description, link, topic } = req.body;
|
||||
const { recipients, origin, payload } = req.body;
|
||||
const notifications = [];
|
||||
let users = [];
|
||||
|
||||
@@ -318,9 +281,17 @@ export async function createRouter(
|
||||
return;
|
||||
}
|
||||
|
||||
const { title, link, description, scope } = payload;
|
||||
|
||||
if (!recipients || !title || !origin || !link) {
|
||||
logger.error(`Invalid notification request received`);
|
||||
res.status(400).send();
|
||||
return;
|
||||
}
|
||||
|
||||
let entityRef = null;
|
||||
if (receivers.entityRef && receivers.type === 'entity') {
|
||||
entityRef = receivers.entityRef;
|
||||
if (recipients.entityRef && recipients.type === 'entity') {
|
||||
entityRef = recipients.entityRef;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -331,13 +302,13 @@ export async function createRouter(
|
||||
return;
|
||||
}
|
||||
|
||||
const baseNotification = {
|
||||
title,
|
||||
description,
|
||||
link,
|
||||
topic,
|
||||
const baseNotification: Omit<Notification, 'id' | 'userRef'> = {
|
||||
payload: {
|
||||
...payload,
|
||||
severity: payload.severity ?? 'normal',
|
||||
},
|
||||
origin,
|
||||
created: new Date(),
|
||||
saved: false,
|
||||
};
|
||||
|
||||
for (const user of users) {
|
||||
@@ -349,10 +320,11 @@ export async function createRouter(
|
||||
const notification = await decorateNotification(userNotification);
|
||||
|
||||
let existingNotification;
|
||||
if (topic) {
|
||||
existingNotification = await store.getExistingTopicNotification({
|
||||
if (scope) {
|
||||
existingNotification = await store.getExistingScopeNotification({
|
||||
user_ref: user,
|
||||
topic,
|
||||
scope,
|
||||
origin,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -7,23 +7,29 @@
|
||||
type Notification_2 = {
|
||||
id: string;
|
||||
userRef: string;
|
||||
title: string;
|
||||
description: string;
|
||||
link: string;
|
||||
topic?: string;
|
||||
created: Date;
|
||||
updated?: Date;
|
||||
saved?: Date;
|
||||
read?: Date;
|
||||
done?: Date;
|
||||
saved: boolean;
|
||||
updated?: Date;
|
||||
origin: string;
|
||||
payload: NotificationPayload;
|
||||
};
|
||||
export { Notification_2 as Notification };
|
||||
|
||||
// @public (undocumented)
|
||||
export type NotificationIds = {
|
||||
ids: string[];
|
||||
export type NotificationPayload = {
|
||||
title: string;
|
||||
description?: string;
|
||||
link: string;
|
||||
severity: NotificationSeverity;
|
||||
topic?: string;
|
||||
scope?: string;
|
||||
icon?: string;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type NotificationSeverity = 'critical' | 'high' | 'normal' | 'low';
|
||||
|
||||
// @public (undocumented)
|
||||
export type NotificationStatus = {
|
||||
unread: number;
|
||||
|
||||
@@ -17,19 +17,32 @@
|
||||
/** @public */
|
||||
export type NotificationType = 'undone' | 'done' | 'saved';
|
||||
|
||||
/** @public */
|
||||
export type NotificationSeverity = 'critical' | 'high' | 'normal' | 'low';
|
||||
|
||||
/** @public */
|
||||
export type NotificationPayload = {
|
||||
title: string;
|
||||
description?: string;
|
||||
link: string;
|
||||
// TODO: Add support for additional links
|
||||
// additionalLinks?: string[];
|
||||
severity: NotificationSeverity;
|
||||
topic?: string;
|
||||
scope?: string;
|
||||
icon?: string;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type Notification = {
|
||||
id: string;
|
||||
userRef: string;
|
||||
title: string;
|
||||
description: string;
|
||||
link: string;
|
||||
topic?: string;
|
||||
created: Date;
|
||||
updated?: Date;
|
||||
saved?: Date;
|
||||
read?: Date;
|
||||
done?: Date;
|
||||
saved: boolean;
|
||||
updated?: Date;
|
||||
origin: string;
|
||||
payload: NotificationPayload;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
@@ -37,8 +50,3 @@ export type NotificationStatus = {
|
||||
unread: number;
|
||||
read: number;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type NotificationIds = {
|
||||
ids: string[];
|
||||
};
|
||||
|
||||
@@ -15,15 +15,18 @@ import { NotificationService } from '@backstage/plugin-notifications-node';
|
||||
|
||||
function makeCreateEnv(config: Config) {
|
||||
// ...
|
||||
const notificationService = DefaultNotificationService.create({
|
||||
const defaultNotificationService = DefaultNotificationService.create({
|
||||
logger: root.child({ type: 'plugin' }),
|
||||
discovery,
|
||||
tokenManager,
|
||||
signalService,
|
||||
});
|
||||
|
||||
// ...
|
||||
return (plugin: string): PluginEnvironment => {
|
||||
// ...
|
||||
const notificationService = defaultNotificationService.forPlugin(plugin);
|
||||
|
||||
return {
|
||||
// ...
|
||||
notificationService,
|
||||
@@ -55,4 +58,4 @@ a user, the notification will be sent to only that user. If it's a group, the no
|
||||
members of the group. If it's some other entity, the notification will be sent to the owner of that entity.
|
||||
|
||||
If the notification has `topic` set and user already has notification with that topic, the existing notification
|
||||
will be updated with the new notification values and moved to inbox as unread.
|
||||
will be updated with the new notification values and moved to inbox as unread.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
```ts
|
||||
import { DiscoveryService } from '@backstage/backend-plugin-api';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { Notification as Notification_2 } from '@backstage/plugin-notifications-common';
|
||||
import { NotificationPayload } from '@backstage/plugin-notifications-common';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
import { SignalService } from '@backstage/plugin-signals-node';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
@@ -19,28 +19,29 @@ export class DefaultNotificationService implements NotificationService {
|
||||
discovery,
|
||||
}: NotificationServiceOptions): DefaultNotificationService;
|
||||
// (undocumented)
|
||||
send(options: NotificationSendOptions): Promise<Notification_2[]>;
|
||||
forPlugin(pluginId: string): NotificationService;
|
||||
// (undocumented)
|
||||
send(notification: NotificationSendOptions): Promise<void>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type NotificationReceivers = {
|
||||
export type NotificationRecipients = {
|
||||
type: 'entity';
|
||||
entityRef: string | string[];
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type NotificationSendOptions = {
|
||||
receivers: NotificationReceivers;
|
||||
title: string;
|
||||
description: string;
|
||||
link: string;
|
||||
topic?: string;
|
||||
recipients: NotificationRecipients;
|
||||
payload: NotificationPayload;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface NotificationService {
|
||||
// (undocumented)
|
||||
send(options: NotificationSendOptions): Promise<Notification_2[]>;
|
||||
forPlugin(pluginId: string): NotificationService;
|
||||
// (undocumented)
|
||||
send(options: NotificationSendOptions): Promise<void>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -30,18 +30,20 @@ export const notificationService = createServiceRef<NotificationService>({
|
||||
createServiceFactory({
|
||||
service,
|
||||
deps: {
|
||||
logger: coreServices.logger,
|
||||
logger: coreServices.rootLogger,
|
||||
discovery: coreServices.discovery,
|
||||
tokenManager: coreServices.tokenManager,
|
||||
pluginMetadata: coreServices.pluginMetadata,
|
||||
signals: signalService,
|
||||
},
|
||||
factory({ logger, discovery, tokenManager, signals }) {
|
||||
factory({ logger, discovery, tokenManager, signals, pluginMetadata }) {
|
||||
// TODO: Convert to use createRootContext
|
||||
return DefaultNotificationService.create({
|
||||
logger,
|
||||
discovery,
|
||||
tokenManager,
|
||||
signalService: signals,
|
||||
});
|
||||
}).forPlugin(pluginMetadata.getId());
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Notification } from '@backstage/plugin-notifications-common';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import { NotificationService } from './NotificationService';
|
||||
import { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { SignalService } from '@backstage/plugin-signals-node';
|
||||
import { NotificationPayload } from '@backstage/plugin-notifications-common';
|
||||
|
||||
/** @public */
|
||||
export type NotificationServiceOptions = {
|
||||
@@ -28,7 +28,7 @@ export type NotificationServiceOptions = {
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type NotificationReceivers = {
|
||||
export type NotificationRecipients = {
|
||||
type: 'entity';
|
||||
entityRef: string | string[];
|
||||
};
|
||||
@@ -38,11 +38,8 @@ export type NotificationReceivers = {
|
||||
|
||||
/** @public */
|
||||
export type NotificationSendOptions = {
|
||||
receivers: NotificationReceivers;
|
||||
title: string;
|
||||
description: string;
|
||||
link: string;
|
||||
topic?: string;
|
||||
recipients: NotificationRecipients;
|
||||
payload: NotificationPayload;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
@@ -51,6 +48,7 @@ export class DefaultNotificationService implements NotificationService {
|
||||
private readonly logger: LoggerService,
|
||||
private readonly discovery: DiscoveryService,
|
||||
private readonly tokenManager: TokenManager,
|
||||
private readonly pluginId?: string,
|
||||
) {}
|
||||
|
||||
static create({
|
||||
@@ -61,22 +59,36 @@ export class DefaultNotificationService implements NotificationService {
|
||||
return new DefaultNotificationService(logger, discovery, tokenManager);
|
||||
}
|
||||
|
||||
async send(options: NotificationSendOptions): Promise<Notification[]> {
|
||||
forPlugin(pluginId: string): NotificationService {
|
||||
return new DefaultNotificationService(
|
||||
this.logger,
|
||||
this.discovery,
|
||||
this.tokenManager,
|
||||
pluginId,
|
||||
);
|
||||
}
|
||||
|
||||
async send(notification: NotificationSendOptions): Promise<void> {
|
||||
if (!this.pluginId) {
|
||||
throw new Error('Invalid initialization of the NotificationService');
|
||||
}
|
||||
|
||||
try {
|
||||
const baseUrl = await this.discovery.getBaseUrl('notifications');
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
const response = await fetch(`${baseUrl}/notifications`, {
|
||||
await fetch(`${baseUrl}/notifications`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(options),
|
||||
body: JSON.stringify({
|
||||
...notification,
|
||||
origin: `plugin-${this.pluginId}`,
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to send notifications: ${error}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
|
||||
import { NotificationSendOptions } from './DefaultNotificationService';
|
||||
import { Notification } from '@backstage/plugin-notifications-common';
|
||||
|
||||
/** @public */
|
||||
export interface NotificationService {
|
||||
send(options: NotificationSendOptions): Promise<Notification[]>;
|
||||
forPlugin(pluginId: string): NotificationService;
|
||||
|
||||
send(options: NotificationSendOptions): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import { DiscoveryApi } from '@backstage/core-plugin-api';
|
||||
import { FetchApi } from '@backstage/core-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { Notification as Notification_2 } from '@backstage/plugin-notifications-common';
|
||||
import { NotificationIds } from '@backstage/plugin-notifications-common';
|
||||
import { NotificationStatus } from '@backstage/plugin-notifications-common';
|
||||
import { NotificationType } from '@backstage/plugin-notifications-common';
|
||||
import { default as React_2 } from 'react';
|
||||
@@ -34,17 +33,9 @@ export interface NotificationsApi {
|
||||
// (undocumented)
|
||||
getStatus(): Promise<NotificationStatus>;
|
||||
// (undocumented)
|
||||
markDone(ids: string[]): Promise<NotificationIds>;
|
||||
// (undocumented)
|
||||
markRead(ids: string[]): Promise<NotificationIds>;
|
||||
// (undocumented)
|
||||
markSaved(ids: string[]): Promise<NotificationIds>;
|
||||
// (undocumented)
|
||||
markUndone(ids: string[]): Promise<NotificationIds>;
|
||||
// (undocumented)
|
||||
markUnread(ids: string[]): Promise<NotificationIds>;
|
||||
// (undocumented)
|
||||
markUnsaved(ids: string[]): Promise<NotificationIds>;
|
||||
updateNotifications(
|
||||
options: UpdateNotificationsOptions,
|
||||
): Promise<Notification_2[]>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -60,17 +51,9 @@ export class NotificationsClient implements NotificationsApi {
|
||||
// (undocumented)
|
||||
getStatus(): Promise<NotificationStatus>;
|
||||
// (undocumented)
|
||||
markDone(ids: string[]): Promise<NotificationIds>;
|
||||
// (undocumented)
|
||||
markRead(ids: string[]): Promise<NotificationIds>;
|
||||
// (undocumented)
|
||||
markSaved(ids: string[]): Promise<NotificationIds>;
|
||||
// (undocumented)
|
||||
markUndone(ids: string[]): Promise<NotificationIds>;
|
||||
// (undocumented)
|
||||
markUnread(ids: string[]): Promise<NotificationIds>;
|
||||
// (undocumented)
|
||||
markUnsaved(ids: string[]): Promise<NotificationIds>;
|
||||
updateNotifications(
|
||||
options: UpdateNotificationsOptions,
|
||||
): Promise<Notification_2[]>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -94,6 +77,14 @@ export const NotificationsTable: (props: {
|
||||
notifications?: Notification_2[];
|
||||
}) => React_2.JSX.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export type UpdateNotificationsOptions = {
|
||||
ids: string[];
|
||||
done?: boolean;
|
||||
read?: boolean;
|
||||
saved?: boolean;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export function useNotificationsApi<T>(
|
||||
f: (api: NotificationsApi) => Promise<T>,
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
Notification,
|
||||
NotificationIds,
|
||||
NotificationStatus,
|
||||
NotificationType,
|
||||
} from '@backstage/plugin-notifications-common';
|
||||
@@ -34,21 +33,21 @@ export type GetNotificationsOptions = {
|
||||
search?: string;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type UpdateNotificationsOptions = {
|
||||
ids: string[];
|
||||
done?: boolean;
|
||||
read?: boolean;
|
||||
saved?: boolean;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export interface NotificationsApi {
|
||||
getNotifications(options?: GetNotificationsOptions): Promise<Notification[]>;
|
||||
|
||||
getStatus(): Promise<NotificationStatus>;
|
||||
|
||||
markDone(ids: string[]): Promise<NotificationIds>;
|
||||
|
||||
markUndone(ids: string[]): Promise<NotificationIds>;
|
||||
|
||||
markRead(ids: string[]): Promise<NotificationIds>;
|
||||
|
||||
markUnread(ids: string[]): Promise<NotificationIds>;
|
||||
|
||||
markSaved(ids: string[]): Promise<NotificationIds>;
|
||||
|
||||
markUnsaved(ids: string[]): Promise<NotificationIds>;
|
||||
updateNotifications(
|
||||
options: UpdateNotificationsOptions,
|
||||
): Promise<Notification[]>;
|
||||
}
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { GetNotificationsOptions, NotificationsApi } from './NotificationsApi';
|
||||
import {
|
||||
GetNotificationsOptions,
|
||||
NotificationsApi,
|
||||
UpdateNotificationsOptions,
|
||||
} from './NotificationsApi';
|
||||
import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
|
||||
import { ResponseError } from '@backstage/errors';
|
||||
import {
|
||||
Notification,
|
||||
NotificationIds,
|
||||
NotificationStatus,
|
||||
} from '@backstage/plugin-notifications-common';
|
||||
|
||||
@@ -61,50 +64,12 @@ export class NotificationsClient implements NotificationsApi {
|
||||
return await this.request<NotificationStatus>('status');
|
||||
}
|
||||
|
||||
async markDone(ids: string[]): Promise<NotificationIds> {
|
||||
return await this.request<NotificationIds>('done', {
|
||||
async updateNotifications(
|
||||
options: UpdateNotificationsOptions,
|
||||
): Promise<Notification[]> {
|
||||
return await this.request<Notification[]>('update', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ids: ids }),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
async markUndone(ids: string[]): Promise<NotificationIds> {
|
||||
return await this.request<NotificationIds>('undone', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ids: ids }),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
async markRead(ids: string[]): Promise<NotificationIds> {
|
||||
return await this.request<NotificationIds>('read', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ids: ids }),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
async markUnread(ids: string[]): Promise<NotificationIds> {
|
||||
return await this.request<NotificationIds>('unread', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ids: ids }),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
async markSaved(ids: string[]): Promise<NotificationIds> {
|
||||
return await this.request<NotificationIds>('save', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ids: ids }),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
async markUnsaved(ids: string[]): Promise<NotificationIds> {
|
||||
return await this.request<NotificationIds>('unsave', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ids: ids }),
|
||||
body: JSON.stringify(options),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ export const NotificationsTable = (props: {
|
||||
startIcon={<Inbox fontSize="small" />}
|
||||
onClick={() => {
|
||||
notificationsApi
|
||||
.markUndone(selected)
|
||||
.updateNotifications({ ids: selected, done: false })
|
||||
.then(() => props.onUpdate());
|
||||
setSelected([]);
|
||||
}}
|
||||
@@ -162,7 +162,7 @@ export const NotificationsTable = (props: {
|
||||
startIcon={<Check fontSize="small" />}
|
||||
onClick={() => {
|
||||
notificationsApi
|
||||
.markDone(selected)
|
||||
.updateNotifications({ ids: selected, done: true })
|
||||
.then(() => props.onUpdate());
|
||||
setSelected([]);
|
||||
}}
|
||||
@@ -197,16 +197,16 @@ export const NotificationsTable = (props: {
|
||||
<TableCell
|
||||
onClick={() =>
|
||||
notificationsApi
|
||||
.markRead([notification.id])
|
||||
.then(() => navigate(notification.link))
|
||||
.updateNotifications({ ids: [notification.id], read: true })
|
||||
.then(() => navigate(notification.payload.link))
|
||||
}
|
||||
style={{ paddingLeft: 0 }}
|
||||
>
|
||||
<Typography variant="subtitle2">
|
||||
{notification.title}
|
||||
{notification.payload.title}
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
{notification.description}
|
||||
{notification.payload.description}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'right' }}>
|
||||
@@ -214,13 +214,16 @@ export const NotificationsTable = (props: {
|
||||
<RelativeTime value={notification.created} />
|
||||
</Box>
|
||||
<Box className="showOnHover">
|
||||
<Tooltip title={notification.link}>
|
||||
<Tooltip title={notification.payload.link}>
|
||||
<IconButton
|
||||
className={styles.actionButton}
|
||||
onClick={() =>
|
||||
notificationsApi
|
||||
.markRead([notification.id])
|
||||
.then(() => navigate(notification.link))
|
||||
.updateNotifications({
|
||||
ids: [notification.id],
|
||||
read: true,
|
||||
})
|
||||
.then(() => navigate(notification.payload.link))
|
||||
}
|
||||
>
|
||||
<ArrowForwardIcon />
|
||||
@@ -234,13 +237,19 @@ export const NotificationsTable = (props: {
|
||||
onClick={() => {
|
||||
if (notification.read) {
|
||||
notificationsApi
|
||||
.markUndone([notification.id])
|
||||
.updateNotifications({
|
||||
ids: [notification.id],
|
||||
done: false,
|
||||
})
|
||||
.then(() => {
|
||||
props.onUpdate();
|
||||
});
|
||||
} else {
|
||||
notificationsApi
|
||||
.markDone([notification.id])
|
||||
.updateNotifications({
|
||||
ids: [notification.id],
|
||||
done: true,
|
||||
})
|
||||
.then(() => {
|
||||
props.onUpdate();
|
||||
});
|
||||
@@ -262,13 +271,19 @@ export const NotificationsTable = (props: {
|
||||
onClick={() => {
|
||||
if (notification.saved) {
|
||||
notificationsApi
|
||||
.markUnsaved([notification.id])
|
||||
.updateNotifications({
|
||||
ids: [notification.id],
|
||||
saved: false,
|
||||
})
|
||||
.then(() => {
|
||||
props.onUpdate();
|
||||
});
|
||||
} else {
|
||||
notificationsApi
|
||||
.markSaved([notification.id])
|
||||
.updateNotifications({
|
||||
ids: [notification.id],
|
||||
saved: true,
|
||||
})
|
||||
.then(() => {
|
||||
props.onUpdate();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user