Merged PR 64: デバイス登録API実装

## 概要
[Task1571: デバイス登録API実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1571)

- NotificationHubへのデバイス登録APIを実装
  - NotificationHubへの接続
  - 登録処理

## レビューポイント
- ディレクトリ構成に問題は無いか

## UIの変更
- Before/Afterのスクショなど
- スクショ置き場

## 動作確認状況
- ローカルでNotificationHubに接続できるところまで確認

## 補足
- 返却するエラーやサーバー側で設定する部分の登録情報については仮です。
  - 「プロダクト バックログ項目 1389: デスクトップアプリ/モバイルアプリに様々な通知をしたい」で実装・設計する想定
This commit is contained in:
saito.k 2023-04-10 00:41:32 +00:00
parent 382bb4e25f
commit ee2e8dbd5d
12 changed files with 678 additions and 9061 deletions

View File

@ -13,3 +13,5 @@ JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA5IZZNgDew9eGmu
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5IZZNgDew9eGmuFTezwd\nHYLSaJvUPPIKYoiOeVLD1paWNI517Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3\nyCTR6wcWR3PfFJrl9vh5SOo79koZoJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbW\nFJXnDe0DVXYXpJLb4LAlF2XAyYX0SYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qS\nfiL9zWk9dvHoKzSnfSDzDFoFcEoVchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//\nmBNNaDHv83Yuw3mGShT73iJ0JQdkTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GO\nOQIDAQAB\n-----END PUBLIC KEY-----\n"
SENDGRID_API_KEY=xxxxxxxxxxxxxxxx
MAIL_FROM=xxxxx@xxxxx.xxxx
NOTIFICATION_HUB_NAME=ntf-odms-shared
NOTIFICATION_HUB_CONNECT_STRING=XXXXXXXXXXXXXXXXXX

File diff suppressed because it is too large Load Diff

View File

@ -27,15 +27,16 @@
"dependencies": {
"@azure/identity": "^3.1.3",
"@azure/keyvault-secrets": "^4.6.0",
"@azure/notification-hubs": "^1.0.1",
"@microsoft/microsoft-graph-client": "^3.0.5",
"@nestjs/axios": "^0.1.0",
"@nestjs/common": "^8.0.0",
"@nestjs/common": "^9.3.12",
"@nestjs/config": "^2.2.0",
"@nestjs/core": "^8.0.0",
"@nestjs/platform-express": "^8.0.0",
"@nestjs/serve-static": "^2.2.2",
"@nestjs/core": "^9.3.12",
"@nestjs/platform-express": "^9.3.12",
"@nestjs/serve-static": "^3.0.1",
"@nestjs/typeorm": "^9.0.1",
"@openapitools/openapi-generator-cli": "^2.5.1",
"@openapitools/openapi-generator-cli": "^0.0.6",
"@sendgrid/mail": "^7.7.0",
"@types/jsonwebtoken": "^9.0.1",
"@types/uuid": "^8.3.4",
@ -57,10 +58,10 @@
},
"devDependencies": {
"@apidevtools/swagger-cli": "^4.0.4",
"@nestjs/cli": "^8.0.0",
"@nestjs/cli": "^9.3.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/swagger": "^5.2.1",
"@nestjs/testing": "^8.0.0",
"@nestjs/swagger": "^6.3.0",
"@nestjs/testing": "^9.3.12",
"@types/cookie-parser": "^1.4.3",
"@types/express": "^4.17.13",
"@types/express-session": "^1.17.5",

View File

@ -17,7 +17,7 @@ async function bootstrap(): Promise<void> {
controllers: controllers,
providers: mockedProviders,
}).compile();
const app = await testingModule.createNestApplication();
const app = testingModule.createNestApplication();
const options = new DocumentBuilder()
.setTitle('ODMSOpenAPI')

View File

@ -19,6 +19,8 @@ import { AccountsRepositoryModule } from './repositories/accounts/accounts.repos
import { TypeOrmModule } from '@nestjs/typeorm';
import { SendGridModule } from './gateways/sendgrid/sendgrid.module';
import { UsersRepositoryModule } from './repositories/users/users.repository.module';
import { NotificationhubModule } from './gateways/notificationhub/notificationhub.module';
import { NotificationhubService } from './gateways/notificationhub/notificationhub.service';
import { NotificationModule } from './features/notification/notification.module';
@Module({
@ -53,6 +55,7 @@ import { NotificationModule } from './features/notification/notification.module'
inject: [ConfigService],
}),
NotificationModule,
NotificationhubModule,
],
controllers: [
HealthController,
@ -60,7 +63,12 @@ import { NotificationModule } from './features/notification/notification.module'
AccountsController,
UsersController,
],
providers: [AuthService, AccountsService, UsersService],
providers: [
AuthService,
AccountsService,
UsersService,
NotificationhubService,
],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {

View File

@ -1,13 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { NotificationController } from './notification.controller';
import { NotificationService } from './notification.service';
describe('NotificationController', () => {
let controller: NotificationController;
const mockNotificationService = {};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [NotificationController],
}).compile();
providers: [NotificationService],
})
.overrideProvider(NotificationService)
.useValue(mockNotificationService)
.compile();
controller = module.get<NotificationController>(NotificationController);
});

View File

@ -37,6 +37,9 @@ export class NotificationController {
})
@ApiOperation({ operationId: 'register' })
async register(@Body() body: RegisterRequest): Promise<RegisterResponse> {
const { handler, pns } = body;
// XXX 登録処理の前にアクセストークンの認証を行う
await this.notificationService.register(pns, handler);
return {};
}
}

View File

@ -1,8 +1,10 @@
import { Module } from '@nestjs/common';
import { NotificationService } from './notification.service';
import { NotificationController } from './notification.controller';
import { NotificationhubModule } from '../../gateways/notificationhub/notificationhub.module';
@Module({
imports:[NotificationhubModule],
providers: [NotificationService],
controllers: [NotificationController]
})

View File

@ -3,11 +3,14 @@ import { NotificationService } from './notification.service';
describe('NotificationService', () => {
let service: NotificationService;
const mockNotificationService = {};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [NotificationService],
}).compile();
})
.overrideProvider(NotificationService)
.useValue(mockNotificationService)
.compile();
service = module.get<NotificationService>(NotificationService);
});

View File

@ -1,4 +1,32 @@
import { Injectable } from '@nestjs/common';
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { NotificationhubService } from '../../gateways/notificationhub/notificationhub.service';
@Injectable()
export class NotificationService {}
export class NotificationService {
private readonly logger = new Logger(NotificationService.name);
constructor(
private readonly notificationhubService: NotificationhubService,
) {}
/**
* Registers notification service
* @param pns
* @param pnsHandler
* @returns register
*/
async register(pns: string, pnsHandler: string): Promise<{}> {
this.logger.log(`[IN] ${this.register.name}`);
try {
await this.notificationhubService.register(pns, pnsHandler);
} catch (e) {
this.logger.error(`error=${e}`);
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
} finally {
this.logger.log(`[OUT] ${this.register.name}`);
}
return {};
}
}

View File

@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { NotificationhubService } from './notificationhub.service';
import { ConfigModule } from '@nestjs/config';
@Module({
imports:[ConfigModule],
exports: [NotificationhubService],
providers: [NotificationhubService],
})
export class NotificationhubModule {}

View File

@ -0,0 +1,65 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
NotificationHubsClient,
createWindowsRegistrationDescription,
RegistrationDescription,
createAppleRegistrationDescription,
} from '@azure/notification-hubs';
@Injectable()
export class NotificationhubService {
private readonly logger = new Logger(NotificationhubService.name);
private readonly client: NotificationHubsClient;
constructor(private readonly configService: ConfigService) {
this.client = new NotificationHubsClient(
this.configService.get<string>('NOTIFICATION_HUB_CONNECT_STRING'),
this.configService.get<string>('NOTIFICATION_HUB_NAME'),
);
}
/**
* Registers notificationhub service
* @param pns
* @param pnsHandler
* @returns register
*/
async register(pns: string, pnsHandler: string): Promise<{}> {
this.logger.log(`[IN] ${this.register.name}`);
let reg: RegistrationDescription;
//登録情報作成
try {
if (pns === 'wns') {
reg = createWindowsRegistrationDescription({
channelUri: pnsHandler,
tags: [],
// XXX 登録の有効期限を設定したい場合
//expirationTime: '',
});
} else if (pns === 'apns') {
reg = createAppleRegistrationDescription({
deviceToken: pnsHandler,
tags: [],
});
} else {
throw new Error('invalid pns');
}
} catch (e) {
this.logger.error(`error=${e.message}`);
throw e;
} finally {
this.logger.log(`[OUT] ${this.register.name}`);
}
try {
//登録
await this.client.createOrUpdateRegistration(reg);
} catch (e) {
this.logger.error(`error=${e}`);
throw e;
} finally {
this.logger.log(`[OUT] ${this.register.name}`);
}
return {};
}
}