From 8189e5d2e302ec34c3e062e1a441a5009dd3c729 Mon Sep 17 00:00:00 2001 From: xmlking Date: Thu, 22 Nov 2018 16:36:08 -0800 Subject: [PATCH] feat(notifications): added notify, notifyAll API --- .../src/app/push/dto/send-notification.dto.ts | 5 ++++ .../push/dto/send-topic-notification.dto.ts | 19 ++++++++++++++ apps/api/src/app/push/push.controller.ts | 25 ++++++++++++++++++- apps/api/src/app/push/push.service.ts | 14 ++++++++++- .../lib/services/push-notification.service.ts | 20 +++++++++------ libs/core/src/lib/state/eventbus.ts | 9 +++++++ 6 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 apps/api/src/app/push/dto/send-topic-notification.dto.ts diff --git a/apps/api/src/app/push/dto/send-notification.dto.ts b/apps/api/src/app/push/dto/send-notification.dto.ts index 0888df667..e43dbc215 100644 --- a/apps/api/src/app/push/dto/send-notification.dto.ts +++ b/apps/api/src/app/push/dto/send-notification.dto.ts @@ -7,6 +7,11 @@ export class SendNotificationDto { @IsString() id: string; + @ApiModelProperty({ type: String }) + @IsNotEmpty() + @IsString() + title: string; + @ApiModelProperty({ type: String }) @IsNotEmpty() @IsString() diff --git a/apps/api/src/app/push/dto/send-topic-notification.dto.ts b/apps/api/src/app/push/dto/send-topic-notification.dto.ts new file mode 100644 index 000000000..8d20e89bf --- /dev/null +++ b/apps/api/src/app/push/dto/send-topic-notification.dto.ts @@ -0,0 +1,19 @@ +import { ApiModelProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class SendTopicNotificationDto { + @ApiModelProperty({ type: String }) + @IsNotEmpty() + @IsString() + topic: string; + + @ApiModelProperty({ type: String }) + @IsNotEmpty() + @IsString() + title: string; + + @ApiModelProperty({ type: String }) + @IsNotEmpty() + @IsString() + body: string; +} diff --git a/apps/api/src/app/push/push.controller.ts b/apps/api/src/app/push/push.controller.ts index c38044b74..07995c341 100644 --- a/apps/api/src/app/push/push.controller.ts +++ b/apps/api/src/app/push/push.controller.ts @@ -7,6 +7,7 @@ import { PushService } from './push.service'; import { CreateSubscriptionDto } from './dto/create-subscription.dto'; import { UpdateSubscriptionDto } from './dto/update-subscription.dto'; import { SendNotificationDto } from './dto/send-notification.dto'; +import { SendTopicNotificationDto } from './dto/send-topic-notification.dto'; @ApiOAuth2Auth(['read']) @ApiUseTags('Sumo', 'Push') @@ -80,7 +81,7 @@ export class PushController extends CrudController { @Post('notify') notify(@Body() notif: SendNotificationDto) { const notification = { - title: 'NGX WebApp Notification', + title: notif.title, body: notif.body, icon: 'assets/icons/icon-72x72.png', data: { @@ -89,4 +90,26 @@ export class PushController extends CrudController { }; return this.pushService.notify(notif.id, notification as any); } + + @ApiOperation({ title: 'Send Push Notifications to all subscribers to a topic' }) + @ApiResponse({ + status: HttpStatus.CREATED, + description: 'Push Notifications has been successfully sent.', + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: 'Invalid input, The response body may contain clues as to what went wrong', + }) + @Post('notifyAll') + notifyAll(@Body() notif: SendTopicNotificationDto) { + const notification = { + title: notif.title, + body: notif.body, + icon: 'assets/icons/icon-72x72.png', + data: { + click_url: '/dashboard', + }, + }; + return this.pushService.notifyAll(notif.topic, notification as any); + } } diff --git a/apps/api/src/app/push/push.service.ts b/apps/api/src/app/push/push.service.ts index bcd5c3e10..2750df525 100644 --- a/apps/api/src/app/push/push.service.ts +++ b/apps/api/src/app/push/push.service.ts @@ -2,7 +2,7 @@ import { BadRequestException, Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { PushSubscription, sendNotification, setVapidDetails, WebPushError } from 'web-push'; import { CrudService } from '../core'; -import { FindConditions, Repository } from 'typeorm'; +import { Any, FindConditions, Repository } from 'typeorm'; import { Subscription } from './subscription.entity'; import { environment as env } from '@env-api/environment'; @@ -31,6 +31,18 @@ export class PushService extends CrudService { return this._sendNotification({ endpoint, keys: { p256dh, auth } }, notification); } + async notifyAll(topic: string, notification: Notification) { + // FIXME: https://github.com/typeorm/typeorm/issues/3150 + const subscriptions = await this.findAndCount({ topics: Any([topic]) } ); + // console.log(subscriptions); + if (subscriptions[1] > 0) { + subscriptions[0].forEach( sub => { + const { endpoint, p256dh, auth } = sub; + this._sendNotification({ endpoint, keys: { p256dh, auth } }, notification); + }); + } + } + private async _sendNotification(subscription: PushSubscription, notification: Notification) { try { await sendNotification(subscription, JSON.stringify({ notification })); diff --git a/libs/core/src/lib/services/push-notification.service.ts b/libs/core/src/lib/services/push-notification.service.ts index 81d749f45..dd8f460b8 100644 --- a/libs/core/src/lib/services/push-notification.service.ts +++ b/libs/core/src/lib/services/push-notification.service.ts @@ -13,14 +13,7 @@ export class PushNotificationService { private readonly entityPath = 'push'; private readonly existingSubscription: PushSubscription; - constructor(private readonly swPush: SwPush, private httpClient: HttpClient) { - if (this.swPush.isEnabled) { - // subscribe for new messages for testing - this.swPush.messages.subscribe(message => { - console.log('received push notification', message); - }); - } - } + constructor(private readonly swPush: SwPush, private httpClient: HttpClient) {} async register() { if (!this.swPush.isEnabled) { @@ -60,6 +53,17 @@ export class PushNotificationService { } } + async notify(id: string) { + await this.httpClient + .post(`${this.baseUrl}/${this.entityPath}/notify`, { + id: encodeURIComponent(id), + title: 'NGX WebApp Notification', + body: 'test body 321', + }) + .pipe(catchError(this.handleError)) + .toPromise(); + } + private handleError(error: HttpErrorResponse) { if (error.error instanceof ErrorEvent) { // A client-side or network error occurred. Handle it accordingly. diff --git a/libs/core/src/lib/state/eventbus.ts b/libs/core/src/lib/state/eventbus.ts index 3ffa87a7d..d2f2be107 100644 --- a/libs/core/src/lib/state/eventbus.ts +++ b/libs/core/src/lib/state/eventbus.ts @@ -1,5 +1,6 @@ import { Actions, ofActionErrored, ofActionSuccessful, Store } from '@ngxs/store'; import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core'; +import { SwPush } from '@angular/service-worker'; import { Login, LoginSuccess } from '@ngx-starter-kit/auth'; import { AuthenticateWebSocket, @@ -25,6 +26,7 @@ export class EventBus { private actions$: Actions, private store: Store, private router: Router, + private readonly swPush: SwPush, private analytics: GoogleAnalyticsService, private pageTitle: PageTitleService, private rendererFactory: RendererFactory2, @@ -67,6 +69,13 @@ export class EventBus { }); } + if (this.swPush.isEnabled) { + // subscribe for new messages for testing + this.swPush.messages.subscribe(message => { + console.log('received push notification', message); + }); + } + this.actions$.pipe(ofActionSuccessful(Login)).subscribe(action => console.log('Login........Action Successful')); this.actions$.pipe(ofActionErrored(Login)).subscribe(action => console.log('Login........Action Errored')); this.actions$.pipe(ofActionSuccessful(LoginSuccess)).subscribe((action: LoginSuccess) => {