Merged PR 259: ルーティング通知登録API実装
## 概要 [Task2218: ルーティング通知登録API実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2218) - NotificationHubへの通知登録を実装しました。 ## レビューポイント - UUIDを使ったインストールIDの組み立てに問題はないか - 対象外 - account関連はフォーマット修正によるものなので対象外です。 ## UIの変更 - なし ## 動作確認状況 - ローカルで確認
This commit is contained in:
parent
e9ab7cc10b
commit
54db2e8ab5
87
dictation_server/package-lock.json
generated
87
dictation_server/package-lock.json
generated
@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"@azure/identity": "^3.1.3",
|
||||
"@azure/keyvault-secrets": "^4.6.0",
|
||||
"@azure/notification-hubs": "^1.0.1",
|
||||
"@azure/notification-hubs": "^1.0.2",
|
||||
"@azure/storage-blob": "^12.14.0",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.5",
|
||||
"@nestjs/axios": "^0.1.0",
|
||||
@ -494,9 +494,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-util": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.2.0.tgz",
|
||||
"integrity": "sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng==",
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz",
|
||||
"integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"tslib": "^2.2.0"
|
||||
@ -624,9 +624,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/notification-hubs": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/notification-hubs/-/notification-hubs-1.0.1.tgz",
|
||||
"integrity": "sha512-bYgWZNr9LjeSBTstuDRW2FFlZWHjnW09c4YLsB81dsaxMFWGN5mAnE5/+r/omEVqztoP5ClQ3j69ZUoZiJLUsQ==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/notification-hubs/-/notification-hubs-1.0.2.tgz",
|
||||
"integrity": "sha512-INtgq8uFQpncwbKm4It8M0GkKIePNDNybhuXs4cQPf5H0i9CbfFEt2c6LtT1AdEzbWfUhjsmU5y0p3YDmecwwg==",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^1.1.0",
|
||||
"@azure/core-client": "^1.6.1",
|
||||
@ -634,24 +634,15 @@
|
||||
"@azure/core-paging": "^1.3.0",
|
||||
"@azure/core-rest-pipeline": "^1.8.1",
|
||||
"@azure/core-tracing": "^1.0.1",
|
||||
"@azure/core-util": "^1.1.0",
|
||||
"@azure/core-util": "^1.3.0",
|
||||
"@azure/core-xml": "^1.3.1",
|
||||
"@azure/logger": "^1.0.3",
|
||||
"tslib": "^2.4.0",
|
||||
"uuid": "^9.0.0"
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/notification-hubs/node_modules/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/storage-blob": {
|
||||
"version": "12.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.14.0.tgz",
|
||||
@ -734,9 +725,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@ -791,9 +782,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@ -5753,9 +5744,9 @@
|
||||
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
|
||||
},
|
||||
"node_modules/fast-xml-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-fbfMDvgBNIdDJLdLOwacjFAPYt67tr31H9ZhWSm45CDAxvd0I6WTlSOUo7K2P/K5sA5JgMKG64PI3DMcaFdWpQ==",
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.6.tgz",
|
||||
"integrity": "sha512-Xo1qV++h/Y3Ng8dphjahnYe+rGHaaNdsYOBWL9Y9GCPKpNKilJtilvWkLcI9f9X2DoKTLsZsGYAls5+JL5jfLA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "paypal",
|
||||
@ -6773,9 +6764,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/istanbul-lib-instrument/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@ -8084,9 +8075,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker/node_modules/semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
@ -8218,9 +8209,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@ -8986,9 +8977,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-package-data/node_modules/semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
@ -9722,9 +9713,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/read-installed/node_modules/semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
@ -10066,9 +10057,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
||||
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
@ -11882,9 +11873,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
"dependencies": {
|
||||
"@azure/identity": "^3.1.3",
|
||||
"@azure/keyvault-secrets": "^4.6.0",
|
||||
"@azure/notification-hubs": "^1.0.1",
|
||||
"@azure/notification-hubs": "^1.0.2",
|
||||
"@azure/storage-blob": "^12.14.0",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.5",
|
||||
"@nestjs/axios": "^0.1.0",
|
||||
|
||||
@ -173,3 +173,12 @@ export const TASK_LIST_SORTABLE_ATTRIBUTES = [
|
||||
* タスク一覧のソート条件(昇順・降順)
|
||||
*/
|
||||
export const SORT_DIRECTIONS = ['ASC', 'DESC'] as const;
|
||||
|
||||
/**
|
||||
* 通知のプラットフォーム種別文字列
|
||||
*/
|
||||
export const PNS = {
|
||||
WNS: 'wns',
|
||||
APNS: 'apns',
|
||||
FCM: 'fcm',
|
||||
};
|
||||
|
||||
@ -1,14 +1,25 @@
|
||||
import { Body, Controller, HttpStatus, Post, UseGuards } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
HttpStatus,
|
||||
Post,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiResponse,
|
||||
ApiOperation,
|
||||
ApiBearerAuth,
|
||||
ApiTags,
|
||||
} from '@nestjs/swagger';
|
||||
import { Request } from 'express';
|
||||
import { ErrorResponse } from '../../common/error/types/types';
|
||||
import { RegisterRequest, RegisterResponse } from './types/types';
|
||||
import { NotificationService } from './notification.service';
|
||||
import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
@ApiTags('notification')
|
||||
@Controller('notification')
|
||||
@ -37,10 +48,18 @@ export class NotificationController {
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiOperation({ operationId: 'register' })
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard)
|
||||
async register(@Body() body: RegisterRequest): Promise<RegisterResponse> {
|
||||
async register(
|
||||
@Req() req: Request,
|
||||
@Body() body: RegisterRequest,
|
||||
): Promise<RegisterResponse> {
|
||||
const { handler, pns } = body;
|
||||
await this.notificationService.register(pns, handler);
|
||||
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken;
|
||||
|
||||
await this.notificationService.register(userId, pns, handler);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
|
||||
import { NotificationService } from './notification.service';
|
||||
import { NotificationController } from './notification.controller';
|
||||
import { NotificationhubModule } from '../../gateways/notificationhub/notificationhub.module';
|
||||
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
|
||||
|
||||
@Module({
|
||||
imports: [NotificationhubModule],
|
||||
imports: [UsersRepositoryModule, NotificationhubModule],
|
||||
providers: [NotificationService],
|
||||
controllers: [NotificationController],
|
||||
})
|
||||
|
||||
@ -1,21 +1,57 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { NotificationService } from './notification.service';
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
import {
|
||||
makeNotificationServiceMock,
|
||||
makeDefaultNotificationHubMockValue,
|
||||
makeDefaultUsersRepositoryMockValue,
|
||||
} from './test/notification.service.mock';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
|
||||
describe('NotificationService', () => {
|
||||
let service: NotificationService;
|
||||
const mockNotificationService = {};
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [NotificationService],
|
||||
})
|
||||
.overrideProvider(NotificationService)
|
||||
.useValue(mockNotificationService)
|
||||
.compile();
|
||||
describe('NotificationService.register', () => {
|
||||
it('ユーザーから渡されたPNSハンドルをNotificationHubに登録できる', async () => {
|
||||
const notificationHubMockValue = makeDefaultNotificationHubMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
|
||||
service = module.get<NotificationService>(NotificationService);
|
||||
const service = await makeNotificationServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
notificationHubMockValue,
|
||||
);
|
||||
|
||||
expect(await service.register('external_id', 'apns', 'handler')).toEqual(
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
it('DBからのユーザー取得に失敗した場合、エラーとなる', async () => {
|
||||
const notificationHubMockValue = makeDefaultNotificationHubMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
usersRepositoryMockValue.findUserByExternalId= new Error("DB failed.")
|
||||
|
||||
const service = await makeNotificationServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
notificationHubMockValue,
|
||||
);
|
||||
|
||||
await expect(service.register('external_id', 'apns', 'handler')).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse("E009999"), HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
it('NotificationHubへの登録に失敗した場合、エラーとなる', async () => {
|
||||
const notificationHubMockValue = makeDefaultNotificationHubMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
notificationHubMockValue.register = new Error("register failed.");
|
||||
|
||||
const service = await makeNotificationServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
notificationHubMockValue,
|
||||
);
|
||||
|
||||
await expect(
|
||||
service.register('external_id', 'apns', 'handler'),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { NotificationhubService } from '../../gateways/notificationhub/notificationhub.service';
|
||||
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
|
||||
import { UserNotFoundError } from '../../repositories/users/errors/types';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@Injectable()
|
||||
export class NotificationService {
|
||||
private readonly logger = new Logger(NotificationService.name);
|
||||
constructor(
|
||||
private readonly usersRepository: UsersRepositoryService,
|
||||
private readonly notificationhubService: NotificationhubService,
|
||||
) {}
|
||||
/**
|
||||
@ -14,10 +18,45 @@ export class NotificationService {
|
||||
* @param pnsHandler
|
||||
* @returns register
|
||||
*/
|
||||
async register(pns: string, pnsHandler: string): Promise<void> {
|
||||
async register(
|
||||
externalId: string,
|
||||
pns: string,
|
||||
pnsHandler: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(`[IN] ${this.register.name}`);
|
||||
|
||||
// ユーザIDからアカウントIDを取得する
|
||||
let userId: number;
|
||||
try {
|
||||
await this.notificationhubService.register(pns, pnsHandler);
|
||||
userId = (await this.usersRepository.findUserByExternalId(externalId)).id;
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
switch (e.constructor) {
|
||||
case UserNotFoundError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010204'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
default:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: 登録毎に新規登録する想定でUUIDを付与している
|
||||
// もしデバイスごとに登録を上書きするようであればUUID部分にデバイス識別子を設定
|
||||
const installationId = `${pns}_${userId}_${uuidv4()}`;
|
||||
this.logger.log(installationId);
|
||||
|
||||
await this.notificationhubService.register(
|
||||
userId,
|
||||
pns,
|
||||
pnsHandler,
|
||||
installationId,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
throw new HttpException(
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { NotificationhubService } from '../../../gateways/notificationhub/notificationhub.service';
|
||||
import { User } from '../../../repositories/users/entity/user.entity';
|
||||
import { NotificationService } from '../notification.service';
|
||||
import { UsersRepositoryService } from '../../../repositories/users/users.repository.service';
|
||||
|
||||
export type UsersRepositoryMockValue = {
|
||||
findUserByExternalId: User | Error;
|
||||
};
|
||||
|
||||
export type NotificationHubMockValue = {
|
||||
register: undefined | Error;
|
||||
};
|
||||
|
||||
export const makeNotificationServiceMock = async (
|
||||
usersRepositoryMockValue: UsersRepositoryMockValue,
|
||||
notificationHubMockValue: NotificationHubMockValue,
|
||||
): Promise<NotificationService> => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [NotificationService],
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
envFilePath: ['.env.local', '.env'],
|
||||
}),
|
||||
],
|
||||
})
|
||||
.useMocker((token) => {
|
||||
switch (token) {
|
||||
case UsersRepositoryService:
|
||||
return makeUsersRepositoryMock(usersRepositoryMockValue);
|
||||
case NotificationhubService:
|
||||
return makeNotificationHubMock(notificationHubMockValue);
|
||||
}
|
||||
})
|
||||
.compile();
|
||||
|
||||
return module.get<NotificationService>(NotificationService);
|
||||
};
|
||||
|
||||
export const makeNotificationHubMock = (value: NotificationHubMockValue) => {
|
||||
const { register } = value;
|
||||
|
||||
return {
|
||||
register:
|
||||
register instanceof Error
|
||||
? jest
|
||||
.fn<Promise<void>, [number, string, string, string]>()
|
||||
.mockRejectedValue(register)
|
||||
: jest
|
||||
.fn<Promise<void>, [number, string, string, string]>()
|
||||
.mockResolvedValue(register),
|
||||
};
|
||||
};
|
||||
|
||||
export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
|
||||
const { findUserByExternalId } = value;
|
||||
|
||||
return {
|
||||
findUserByExternalId:
|
||||
findUserByExternalId instanceof Error
|
||||
? jest.fn<Promise<void>, []>().mockRejectedValue(findUserByExternalId)
|
||||
: jest.fn<Promise<User>, []>().mockResolvedValue(findUserByExternalId),
|
||||
};
|
||||
};
|
||||
|
||||
export const makeDefaultNotificationHubMockValue =
|
||||
(): NotificationHubMockValue => {
|
||||
return { register: undefined };
|
||||
};
|
||||
|
||||
// 個別のテストケースに対応してそれぞれのMockを用意するのは無駄が多いのでテストケース内で個別の値を設定する
|
||||
export const makeDefaultUsersRepositoryMockValue =
|
||||
(): UsersRepositoryMockValue => {
|
||||
const user = new User();
|
||||
user.id = 2;
|
||||
user.external_id = 'external_id';
|
||||
user.account_id = 123;
|
||||
user.role = 'none';
|
||||
user.author_id = undefined;
|
||||
user.accepted_terms_version = '1.0';
|
||||
user.email_verified = true;
|
||||
user.auto_renew = false;
|
||||
user.license_alert = false;
|
||||
user.notification = false;
|
||||
user.deleted_at = undefined;
|
||||
user.created_by = 'test';
|
||||
user.created_at = new Date();
|
||||
user.updated_by = 'test';
|
||||
user.updated_at = new Date();
|
||||
|
||||
return {
|
||||
findUserByExternalId: user,
|
||||
};
|
||||
};
|
||||
@ -1,7 +1,12 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsIn } from 'class-validator';
|
||||
import { PNS } from '../../../constants';
|
||||
|
||||
export class RegisterRequest {
|
||||
@ApiProperty({ description: 'wns or apns' })
|
||||
@IsIn(Object.values(PNS), {
|
||||
message: 'invalid pns',
|
||||
})
|
||||
pns: string;
|
||||
@ApiProperty({ description: 'wnsのチャネルURI or apnsのデバイストークン' })
|
||||
handler: string;
|
||||
|
||||
@ -2,10 +2,11 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import {
|
||||
NotificationHubsClient,
|
||||
createWindowsRegistrationDescription,
|
||||
RegistrationDescription,
|
||||
createAppleRegistrationDescription,
|
||||
createAppleInstallation,
|
||||
createFcmLegacyInstallation,
|
||||
createWindowsInstallation,
|
||||
} from '@azure/notification-hubs';
|
||||
import { PNS } from '../../constants';
|
||||
@Injectable()
|
||||
export class NotificationhubService {
|
||||
private readonly logger = new Logger(NotificationhubService.name);
|
||||
@ -23,25 +24,44 @@ export class NotificationhubService {
|
||||
* @param pnsHandler
|
||||
* @returns register
|
||||
*/
|
||||
async register(pns: string, pnsHandler: string): Promise<void> {
|
||||
async register(
|
||||
userId: number,
|
||||
pns: string,
|
||||
pnsHandler: string,
|
||||
installationId: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(`[IN] ${this.register.name}`);
|
||||
let reg: RegistrationDescription;
|
||||
|
||||
const tag = `user_${userId}`;
|
||||
|
||||
//登録情報作成
|
||||
const installation = {
|
||||
// XXX 登録の有効期限を設定したい場合
|
||||
//expirationTime: '',
|
||||
installationId: installationId,
|
||||
pushChannel: pnsHandler,
|
||||
tags: [tag],
|
||||
};
|
||||
|
||||
try {
|
||||
if (pns === 'wns') {
|
||||
reg = createWindowsRegistrationDescription({
|
||||
channelUri: pnsHandler,
|
||||
tags: [],
|
||||
// XXX [Task2218] 登録の有効期限を設定したい場合等、本実装を別途Taskで行う
|
||||
//expirationTime: '',
|
||||
});
|
||||
} else if (pns === 'apns') {
|
||||
reg = createAppleRegistrationDescription({
|
||||
deviceToken: pnsHandler,
|
||||
tags: [],
|
||||
});
|
||||
} else {
|
||||
throw new Error('invalid pns');
|
||||
switch (pns) {
|
||||
case PNS.WNS:
|
||||
await this.client.createOrUpdateInstallation(
|
||||
createWindowsInstallation(installation),
|
||||
);
|
||||
break;
|
||||
case PNS.APNS:
|
||||
await this.client.createOrUpdateInstallation(
|
||||
createAppleInstallation(installation),
|
||||
);
|
||||
break;
|
||||
case PNS.FCM:
|
||||
await this.client.createOrUpdateInstallation(
|
||||
createFcmLegacyInstallation(installation),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error('invalid pns');
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e.message}`);
|
||||
@ -49,15 +69,5 @@ export class NotificationhubService {
|
||||
} 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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user