Merged PR 559: ライセンスアラート処理実装(メール内容固定)
## 概要 [Task3021: ライセンスアラート処理実装(メール内容固定)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3021) ライセンスアラート処理を実装しました。 ## レビューポイント 取得している情報に過不足はないか。 処理の構成に問題がないか。 ※redis対応は別タスクとなりますので、adb2cへのアクセス効率はレビュー対象外でお願いします ※メールの内容は別タスクで作成しますので、レビュー対象外でお願いします。 ## UIの変更 なし ## 動作確認状況 ローカルで動作確認済み、UT実施済み ## 補足 UTでメールを送信した、していないを判断する方法が分からず、ひとまずconsoleログの出力の有無で判断しています。
This commit is contained in:
parent
7421203bc4
commit
7c16e7c358
574
dictation_function/package-lock.json
generated
574
dictation_function/package-lock.json
generated
@ -8,6 +8,8 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@azure/functions": "^4.0.0",
|
||||
"@azure/identity": "^3.1.3",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.5",
|
||||
"@sendgrid/mail": "^7.7.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"mysql2": "^2.3.3",
|
||||
@ -38,6 +40,123 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/abort-controller": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz",
|
||||
"integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-auth": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz",
|
||||
"integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-util": "^1.1.0",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-client": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.7.3.tgz",
|
||||
"integrity": "sha512-kleJ1iUTxcO32Y06dH9Pfi9K4U+Tlb111WXEnbt7R/ne+NLRwppZiTGJuTD5VVoxTMK5NTbEtm5t2vcdNCFe2g==",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-auth": "^1.4.0",
|
||||
"@azure/core-rest-pipeline": "^1.9.1",
|
||||
"@azure/core-tracing": "^1.0.0",
|
||||
"@azure/core-util": "^1.0.0",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.12.2.tgz",
|
||||
"integrity": "sha512-wLLJQdL4v1yoqYtEtjKNjf8pJ/G/BqVomAWxcKOR1KbZJyCEnCv04yks7Y1NhJ3JzxbDs307W67uX0JzklFdCg==",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-auth": "^1.4.0",
|
||||
"@azure/core-tracing": "^1.0.1",
|
||||
"@azure/core-util": "^1.3.0",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"form-data": "^4.0.0",
|
||||
"http-proxy-agent": "^5.0.0",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline/node_modules/@tootallnate/once": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline/node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline/node_modules/http-proxy-agent": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
|
||||
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
|
||||
"dependencies": {
|
||||
"@tootallnate/once": "2",
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-tracing": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
|
||||
"integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-util": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.6.1.tgz",
|
||||
"integrity": "sha512-h5taHeySlsV9qxuK64KZxy4iln1BtMYlNt5jbuEFN3UFSAd1EwKg/Gjl5a6tZ/W8t6li3xPnutOx7zbDyXnPmQ==",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/functions": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/functions/-/functions-4.0.1.tgz",
|
||||
@ -50,6 +169,94 @@
|
||||
"node": ">=18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/identity": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@azure/identity/-/identity-3.1.3.tgz",
|
||||
"integrity": "sha512-y0jFjSfHsVPwXSwi3KaSPtOZtJZqhiqAhWUXfFYBUd/+twUBovZRXspBwLrF5rJe0r5NyvmScpQjL+TYDTQVvw==",
|
||||
"deprecated": "Please upgrade to the latest version of this package to get necessary fixes",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-client": "^1.4.0",
|
||||
"@azure/core-rest-pipeline": "^1.1.0",
|
||||
"@azure/core-tracing": "^1.0.0",
|
||||
"@azure/core-util": "^1.0.0",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"@azure/msal-browser": "^2.32.2",
|
||||
"@azure/msal-common": "^9.0.2",
|
||||
"@azure/msal-node": "^1.14.6",
|
||||
"events": "^3.0.0",
|
||||
"jws": "^4.0.0",
|
||||
"open": "^8.0.0",
|
||||
"stoppable": "^1.1.0",
|
||||
"tslib": "^2.2.0",
|
||||
"uuid": "^8.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/logger": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz",
|
||||
"integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-browser": {
|
||||
"version": "2.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.38.3.tgz",
|
||||
"integrity": "sha512-2WuLFnWWPR1IdvhhysT18cBbkXx1z0YIchVss5AwVA95g7CU5CpT3d+5BcgVGNXDXbUU7/5p0xYHV99V5z8C/A==",
|
||||
"deprecated": "A newer major version of this library is available. Please upgrade to the latest available version.",
|
||||
"dependencies": {
|
||||
"@azure/msal-common": "13.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-browser/node_modules/@azure/msal-common": {
|
||||
"version": "13.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.3.1.tgz",
|
||||
"integrity": "sha512-Lrk1ozoAtaP/cp53May3v6HtcFSVxdFrg2Pa/1xu5oIvsIwhxW6zSPibKefCOVgd5osgykMi5jjcZHv8XkzZEQ==",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-common": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-9.1.1.tgz",
|
||||
"integrity": "sha512-we9xR8lvu47fF0h+J8KyXoRy9+G/fPzm3QEa2TrdR3jaVS3LKAyE2qyMuUkNdbVkvzl8Zr9f7l+IUSP22HeqXw==",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-node": {
|
||||
"version": "1.18.4",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.18.4.tgz",
|
||||
"integrity": "sha512-Kc/dRvhZ9Q4+1FSfsTFDME/v6+R2Y1fuMty/TfwqE5p9GTPw08BPbKgeWinE8JRHRp+LemjQbUZsn4Q4l6Lszg==",
|
||||
"deprecated": "A newer major version of this library is available. Please upgrade to the latest available version.",
|
||||
"dependencies": {
|
||||
"@azure/msal-common": "13.3.1",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"uuid": "^8.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "10 || 12 || 14 || 16 || 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-node/node_modules/@azure/msal-common": {
|
||||
"version": "13.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.3.1.tgz",
|
||||
"integrity": "sha512-Lrk1ozoAtaP/cp53May3v6HtcFSVxdFrg2Pa/1xu5oIvsIwhxW6zSPibKefCOVgd5osgykMi5jjcZHv8XkzZEQ==",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
@ -704,6 +911,13 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
|
||||
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
@ -1364,6 +1578,32 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/microsoft-graph-client": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-3.0.5.tgz",
|
||||
"integrity": "sha512-xQADFNLUhE78RzYadFZtOmy/5wBZenSZhVK193m40MTDC5hl1aYMQO1QOJApnKga8WcvMCDCU10taRhuXTOz5w==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@azure/identity": {
|
||||
"optional": true
|
||||
},
|
||||
"@azure/msal-browser": {
|
||||
"optional": true
|
||||
},
|
||||
"buffer": {
|
||||
"optional": true
|
||||
},
|
||||
"stream-browserify": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@npmcli/fs": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz",
|
||||
@ -1780,7 +2020,6 @@
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"debug": "4"
|
||||
},
|
||||
@ -1907,8 +2146,7 @@
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.26.1",
|
||||
@ -2499,6 +2737,11 @@
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-equal-constant-time": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
@ -2954,6 +3197,16 @@
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cluster-key-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@ -2999,7 +3252,6 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
@ -3109,11 +3361,18 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/define-lazy-prop": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
|
||||
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@ -3173,6 +3432,14 @@
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.576",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.576.tgz",
|
||||
@ -3263,6 +3530,14 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/execa": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||
@ -3753,7 +4028,6 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
@ -3870,6 +4144,31 @@
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz",
|
||||
"integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "^1.1.1",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"denque": "^2.1.0",
|
||||
"lodash.defaults": "^4.2.0",
|
||||
"lodash.isarguments": "^3.1.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0",
|
||||
"standard-as-callback": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ioredis"
|
||||
}
|
||||
},
|
||||
"node_modules/ip": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
||||
@ -3895,6 +4194,20 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-docker": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
|
||||
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
|
||||
"bin": {
|
||||
"is-docker": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
@ -3945,6 +4258,17 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-wsl": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
|
||||
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
|
||||
"dependencies": {
|
||||
"is-docker": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
@ -4747,6 +5071,90 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
|
||||
"dependencies": {
|
||||
"jws": "^3.2.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/jwa": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
||||
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/jws": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
|
||||
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
||||
"dependencies": {
|
||||
"jwa": "^1.4.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/semver": {
|
||||
"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"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jwa": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"dependencies": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/kleur": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
|
||||
@ -4783,12 +5191,61 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
|
||||
},
|
||||
"node_modules/lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
|
||||
},
|
||||
"node_modules/lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
||||
},
|
||||
"node_modules/lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
@ -4958,7 +5415,6 @@
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@ -4967,7 +5423,6 @@
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
@ -5601,6 +6056,22 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/open": {
|
||||
"version": "8.4.2",
|
||||
"resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
|
||||
"integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
|
||||
"dependencies": {
|
||||
"define-lazy-prop": "^2.0.0",
|
||||
"is-docker": "^2.1.1",
|
||||
"is-wsl": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||
@ -5896,6 +6367,66 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/redis": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz",
|
||||
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"denque": "^1.5.0",
|
||||
"redis-commands": "^1.7.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-redis"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-commands": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
|
||||
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"redis-errors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/redis/node_modules/denque": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
|
||||
"integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect-metadata": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||
@ -6123,8 +6654,13 @@
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/smart-buffer": {
|
||||
@ -6262,6 +6798,22 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/standard-as-callback": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
|
||||
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/stoppable": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
|
||||
"integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==",
|
||||
"engines": {
|
||||
"node": ">=4",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
@ -6388,8 +6940,8 @@
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/functions": "^4.0.0",
|
||||
"@azure/identity": "^3.1.3",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.5",
|
||||
"@sendgrid/mail": "^7.7.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"mysql2": "^2.3.3",
|
||||
|
||||
74
dictation_function/src/adb2c/adb2c.service.ts
Normal file
74
dictation_function/src/adb2c/adb2c.service.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { ClientSecretCredential } from "@azure/identity";
|
||||
import { Client } from "@microsoft/microsoft-graph-client";
|
||||
import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials";
|
||||
import { AdB2cResponse, AdB2cUser } from "./types/types";
|
||||
import { error } from "console";
|
||||
|
||||
export class Adb2cTooManyRequestsError extends Error {}
|
||||
|
||||
export class AdB2cService {
|
||||
private graphClient: Client;
|
||||
|
||||
constructor() {
|
||||
// ADB2Cへの認証情報
|
||||
if (
|
||||
!process.env.ADB2C_TENANT_ID ||
|
||||
!process.env.ADB2C_CLIENT_ID ||
|
||||
!process.env.ADB2C_CLIENT_SECRET
|
||||
) {
|
||||
throw error;
|
||||
}
|
||||
const credential = new ClientSecretCredential(
|
||||
process.env.ADB2C_TENANT_ID,
|
||||
process.env.ADB2C_CLIENT_ID,
|
||||
process.env.ADB2C_CLIENT_SECRET
|
||||
);
|
||||
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
|
||||
scopes: ["https://graph.microsoft.com/.default"],
|
||||
});
|
||||
|
||||
this.graphClient = Client.initWithMiddleware({ authProvider });
|
||||
}
|
||||
|
||||
/**
|
||||
* Azure AD B2Cからユーザ情報を取得する
|
||||
* @param externalIds 外部ユーザーID
|
||||
* @returns ユーザ情報
|
||||
*/
|
||||
async getUsers(externalIds: string[]): Promise<AdB2cUser[]> {
|
||||
const chunkExternalIds = splitArrayInChunksOfFifteen(externalIds);
|
||||
|
||||
try {
|
||||
const b2cUsers: AdB2cUser[] = [];
|
||||
for (let index = 0; index < chunkExternalIds.length; index++) {
|
||||
const element = chunkExternalIds[index];
|
||||
const res: AdB2cResponse = await this.graphClient
|
||||
.api(`users/`)
|
||||
.select(["id", "displayName", "identities"])
|
||||
.filter(`id in (${element.map((y) => `'${y}'`).join(",")})`)
|
||||
.get();
|
||||
|
||||
b2cUsers.push(...res.value);
|
||||
}
|
||||
|
||||
return b2cUsers;
|
||||
} catch (e) {
|
||||
const { statusCode } = e;
|
||||
if (statusCode === 429) {
|
||||
throw new Adb2cTooManyRequestsError();
|
||||
}
|
||||
|
||||
throw e;
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const splitArrayInChunksOfFifteen = (arr: string[]): string[][] => {
|
||||
const result: string[][] = [];
|
||||
const chunkSize = 15; // SDKの制限数
|
||||
for (let i = 0; i < arr.length; i += chunkSize) {
|
||||
result.push(arr.slice(i, i + chunkSize));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
15
dictation_function/src/adb2c/types/types.ts
Normal file
15
dictation_function/src/adb2c/types/types.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export type AdB2cResponse = {
|
||||
'@odata.context': string;
|
||||
value: AdB2cUser[];
|
||||
};
|
||||
export type AdB2cUser = {
|
||||
id: string;
|
||||
displayName: string;
|
||||
identities?: UserIdentity[];
|
||||
};
|
||||
|
||||
export type UserIdentity = {
|
||||
signInType: string;
|
||||
issuer: string;
|
||||
issuerAssignedId: string;
|
||||
};
|
||||
1
dictation_function/src/common/cache/constants.ts
vendored
Normal file
1
dictation_function/src/common/cache/constants.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export const ADB2C_PREFIX = "adb2c-external-id:"
|
||||
19
dictation_function/src/common/cache/index.ts
vendored
Normal file
19
dictation_function/src/common/cache/index.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
import { ADB2C_PREFIX } from './constants';
|
||||
|
||||
/**
|
||||
* ADB2Cのユーザー格納用のキーを生成する
|
||||
* @param externalId 外部ユーザーID
|
||||
* @returns キャッシュのキー
|
||||
*/
|
||||
export const makeADB2CKey = (externalId: string): string => {
|
||||
return `${ADB2C_PREFIX}${externalId}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* ADB2Cのユーザー格納用のキーから外部ユーザーIDを取得する
|
||||
* @param key キャッシュのキー
|
||||
* @returns 外部ユーザーID
|
||||
*/
|
||||
export const restoreAdB2cID = (key: string): string => {
|
||||
return key.replace(ADB2C_PREFIX, '');
|
||||
}
|
||||
7
dictation_function/src/common/getEnv/getEnv.ts
Normal file
7
dictation_function/src/common/getEnv/getEnv.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const getMailFrom = (): string => {
|
||||
const from = process.env.MAIL_FROM;
|
||||
if (typeof from === "string") {
|
||||
return from;
|
||||
}
|
||||
throw new Error("MAIL_FROM not found");
|
||||
};
|
||||
@ -1,71 +0,0 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { DataSource } from "typeorm";
|
||||
import { User } from "../../entity/user.entity";
|
||||
import { Account } from "../../entity/account.entity";
|
||||
import { ADMIN_ROLES, USER_ROLES } from "../../constants";
|
||||
|
||||
type InitialTestDBState = {
|
||||
tier1Accounts: { account: Account; users: User[] }[];
|
||||
tier2Accounts: { account: Account; users: User[] }[];
|
||||
tier3Accounts: { account: Account; users: User[] }[];
|
||||
tier4Accounts: { account: Account; users: User[] }[];
|
||||
tier5Accounts: { account: Account; users: User[] }[];
|
||||
};
|
||||
|
||||
// 上書きされたら困る項目を除外したAccount型
|
||||
type OverrideAccount = Omit<
|
||||
Account,
|
||||
"id" | "primary_admin_user_id" | "secondary_admin_user_id" | "user"
|
||||
>;
|
||||
|
||||
// 上書きされたら困る項目を除外したUser型
|
||||
type OverrideUser = Omit<
|
||||
User,
|
||||
"id" | "account" | "license" | "userGroupMembers"
|
||||
>;
|
||||
|
||||
type AccountDefault = { [K in keyof OverrideAccount]?: OverrideAccount[K] };
|
||||
type UserDefault = { [K in keyof OverrideUser]?: OverrideUser[K] };
|
||||
|
||||
/**
|
||||
* テスト ユーティリティ: 指定したプロパティを上書きしたユーザーを作成する
|
||||
* @param dataSource データソース
|
||||
* @param defaultUserValue User型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト
|
||||
* @returns 作成したユーザー
|
||||
*/
|
||||
export const makeTestUser = async (
|
||||
datasource: DataSource,
|
||||
defaultUserValue?: UserDefault
|
||||
): Promise<User> => {
|
||||
const d = defaultUserValue;
|
||||
const { identifiers } = await datasource.getRepository(User).insert({
|
||||
account_id: d?.account_id ?? -1,
|
||||
external_id: d?.external_id ?? uuidv4(),
|
||||
role: d?.role ?? `${ADMIN_ROLES.STANDARD} ${USER_ROLES.NONE}`,
|
||||
author_id: d?.author_id,
|
||||
accepted_eula_version: d?.accepted_eula_version ?? "1.0",
|
||||
accepted_dpa_version: d?.accepted_dpa_version ?? "1.0",
|
||||
email_verified: d?.email_verified ?? true,
|
||||
auto_renew: d?.auto_renew ?? true,
|
||||
license_alert: d?.license_alert ?? true,
|
||||
notification: d?.notification ?? true,
|
||||
encryption: d?.encryption ?? true,
|
||||
encryption_password: d?.encryption_password,
|
||||
prompt: d?.prompt ?? true,
|
||||
created_by: d?.created_by ?? "test_runner",
|
||||
created_at: d?.created_at ?? new Date(),
|
||||
updated_by: d?.updated_by ?? "updater",
|
||||
updated_at: d?.updated_at ?? new Date(),
|
||||
});
|
||||
const result = identifiers.pop() as User;
|
||||
|
||||
const user = await datasource.getRepository(User).findOne({
|
||||
where: {
|
||||
id: result.id,
|
||||
},
|
||||
});
|
||||
if (!user) {
|
||||
throw new Error("Unexpected null");
|
||||
}
|
||||
return user;
|
||||
};
|
||||
70
dictation_function/src/common/types/types.ts
Normal file
70
dictation_function/src/common/types/types.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import {
|
||||
LICENSE_EXPIRATION_DAYS,
|
||||
LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
||||
TRIAL_LICENSE_EXPIRATION_DAYS,
|
||||
} from "../../constants";
|
||||
|
||||
// ライセンス算出用に、その日の始まりの時刻(0:00:00.000)の日付を取得する
|
||||
export class DateWithZeroTime extends Date {
|
||||
constructor(...args: any[]) {
|
||||
if (args.length === 0) {
|
||||
super(); // 引数がない場合、現在の日付で初期化
|
||||
} else {
|
||||
super(...(args as [string])); // 引数がある場合、引数をそのままDateクラスのコンストラクタに渡す
|
||||
}
|
||||
this.setHours(0, 0, 0, 0); // 時分秒を"0:00:00.000"に固定
|
||||
}
|
||||
}
|
||||
|
||||
// ライセンス算出用に、その日の終わりの時刻(23:59:59.999)の日付を取得する
|
||||
export class DateWithDayEndTime extends Date {
|
||||
constructor(...args: any[]) {
|
||||
if (args.length === 0) {
|
||||
super(); // 引数がない場合、現在の日付で初期化
|
||||
} else {
|
||||
super(...(args as [string])); // 引数がある場合、引数をそのままDateクラスのコンストラクタに渡す
|
||||
}
|
||||
this.setHours(23, 59, 59, 999); // 時分秒を"23:59:59.999"に固定
|
||||
}
|
||||
}
|
||||
|
||||
// ライセンスの算出用に、閾値となる時刻(23:59:59.999)の日付を取得する
|
||||
export class ExpirationThresholdDate extends Date {
|
||||
constructor(...args: any[]) {
|
||||
if (args.length === 0) {
|
||||
super(); // 引数がない場合、現在の日付で初期化
|
||||
} else {
|
||||
super(...(args as [string])); // 引数がある場合、引数をそのままDateクラスのコンストラクタに渡す
|
||||
}
|
||||
this.setDate(this.getDate() + LICENSE_EXPIRATION_THRESHOLD_DAYS);
|
||||
this.setHours(23, 59, 59, 999); // 時分秒を"23:59:59.999"に固定
|
||||
}
|
||||
}
|
||||
|
||||
// 新規トライアルライセンス発行時の有効期限算出用に、30日後の日付を取得する
|
||||
export class NewTrialLicenseExpirationDate extends Date {
|
||||
constructor(...args: any[]) {
|
||||
if (args.length === 0) {
|
||||
super(); // 引数がない場合、現在の日付で初期化
|
||||
} else {
|
||||
super(...(args as [string])); // 引数がある場合、引数をそのままDateクラスのコンストラクタに渡す
|
||||
}
|
||||
this.setDate(this.getDate() + TRIAL_LICENSE_EXPIRATION_DAYS);
|
||||
this.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
this.setMilliseconds(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 新規ライセンス割り当て時の有効期限算出用に、365日後の日付を取得する
|
||||
export class NewAllocatedLicenseExpirationDate extends Date {
|
||||
constructor(...args: any[]) {
|
||||
if (args.length === 0) {
|
||||
super(); // 引数がない場合、現在の日付で初期化
|
||||
} else {
|
||||
super(...(args as [string])); // 引数がある場合、引数をそのままDateクラスのコンストラクタに渡す
|
||||
}
|
||||
this.setDate(this.getDate() + LICENSE_EXPIRATION_DAYS);
|
||||
this.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
this.setMilliseconds(0);
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,8 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToMany,
|
||||
OneToOne,
|
||||
JoinColumn,
|
||||
} from "typeorm";
|
||||
|
||||
@Entity({ name: "accounts" })
|
||||
@ -65,6 +66,11 @@ export class Account {
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date;
|
||||
|
||||
@OneToMany(() => User, (user) => user.id)
|
||||
user: User[] | null;
|
||||
@OneToOne(() => User, (user) => user.id)
|
||||
@JoinColumn({ name: "primary_admin_user_id" })
|
||||
primaryAdminUser: User | null;
|
||||
|
||||
@OneToOne(() => User, (user) => user.id)
|
||||
@JoinColumn({ name: "secondary_admin_user_id" })
|
||||
secondaryAdminUser: User | null;
|
||||
}
|
||||
|
||||
63
dictation_function/src/entity/license.entity.ts
Normal file
63
dictation_function/src/entity/license.entity.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
} from "typeorm";
|
||||
import { bigintTransformer } from "../common/entity";
|
||||
import { User } from "./user.entity";
|
||||
|
||||
@Entity({ name: "licenses" })
|
||||
export class License {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
expiry_date: Date | null;
|
||||
|
||||
@Column()
|
||||
account_id: number;
|
||||
|
||||
@Column()
|
||||
type: string;
|
||||
|
||||
@Column()
|
||||
status: string;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
allocated_user_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
order_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
deleted_at: Date | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
delete_order_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
created_by: string | null;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
})
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
})
|
||||
updated_at: Date;
|
||||
|
||||
@OneToOne(() => User, (user) => user.license)
|
||||
@JoinColumn({ name: "allocated_user_id" })
|
||||
user: User | null;
|
||||
}
|
||||
@ -4,7 +4,9 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToOne,
|
||||
} from "typeorm";
|
||||
import { License } from "./license.entity";
|
||||
|
||||
@Entity({ name: "users" })
|
||||
export class User {
|
||||
@ -70,4 +72,7 @@ export class User {
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date;
|
||||
|
||||
@OneToOne(() => License, (license) => license.user)
|
||||
license: License | null;
|
||||
}
|
||||
|
||||
347
dictation_function/src/functions/licenseAlert.ts
Normal file
347
dictation_function/src/functions/licenseAlert.ts
Normal file
@ -0,0 +1,347 @@
|
||||
import { app, InvocationContext, Timer } from "@azure/functions";
|
||||
import { Between, DataSource, In, IsNull, MoreThan, Not } from "typeorm";
|
||||
import { User } from "../entity/user.entity";
|
||||
import { Account } from "../entity/account.entity";
|
||||
import {
|
||||
ADB2C_SIGN_IN_TYPE,
|
||||
LICENSE_ALLOCATED_STATUS,
|
||||
TIERS,
|
||||
} from "../constants";
|
||||
import * as dotenv from "dotenv";
|
||||
import { License } from "../entity/license.entity";
|
||||
import {
|
||||
DateWithDayEndTime,
|
||||
DateWithZeroTime,
|
||||
ExpirationThresholdDate,
|
||||
} from "../common/types/types";
|
||||
import { getMailFrom } from "../common/getEnv/getEnv";
|
||||
import { AdB2cService } from "../adb2c/adb2c.service";
|
||||
import { SendGridService } from "../sendgrid/sendgrid.service";
|
||||
|
||||
export async function licenseAlertProcessing(
|
||||
context: InvocationContext,
|
||||
datasource: DataSource,
|
||||
sendgrid: SendGridService,
|
||||
adb2c: AdB2cService
|
||||
) {
|
||||
context.log("[IN]licenseAlertProcessing");
|
||||
const mailFrom = getMailFrom();
|
||||
const accountRepository = datasource.getRepository(Account);
|
||||
|
||||
// 第五のアカウントを取得
|
||||
const accounts = await accountRepository.find({
|
||||
where: {
|
||||
tier: TIERS.TIER5,
|
||||
},
|
||||
relations: {
|
||||
primaryAdminUser: true,
|
||||
secondaryAdminUser: true,
|
||||
},
|
||||
});
|
||||
|
||||
const licenseRepository = datasource.getRepository(License);
|
||||
const currentDate = new DateWithZeroTime();
|
||||
const expiringSoonDate = new ExpirationThresholdDate(currentDate.getTime());
|
||||
const currentDateWithZeroTime = new DateWithZeroTime();
|
||||
const currentDateWithDayEndTime = new DateWithDayEndTime();
|
||||
const sendTargetAccounts = [] as accountInfo[];
|
||||
|
||||
const counts = async () => {
|
||||
for (const account of accounts) {
|
||||
// 有効期限がしきい値より未来または未設定で、割り当て可能なライセンス数の取得を行う
|
||||
const allocatableLicenseWithMargin = await licenseRepository.count({
|
||||
where: [
|
||||
{
|
||||
account_id: account.id,
|
||||
status: In([
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
]),
|
||||
expiry_date: MoreThan(expiringSoonDate),
|
||||
},
|
||||
{
|
||||
account_id: account.id,
|
||||
status: In([
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
]),
|
||||
expiry_date: IsNull(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 有効期限が現在日付からしきい値以内のライセンス数を取得する
|
||||
const expiringSoonLicense = await licenseRepository.count({
|
||||
where: {
|
||||
account_id: account.id,
|
||||
expiry_date: Between(currentDate, expiringSoonDate),
|
||||
status: Not(LICENSE_ALLOCATED_STATUS.DELETED),
|
||||
},
|
||||
});
|
||||
// shortage算出
|
||||
let shortage = allocatableLicenseWithMargin - expiringSoonLicense;
|
||||
shortage = shortage >= 0 ? 0 : Math.abs(shortage);
|
||||
|
||||
// AutoRenewが未チェックかつ、有効期限当日のライセンスが割り当てられているユーザー数を取得
|
||||
const userCount = await licenseRepository.count({
|
||||
where: [
|
||||
{
|
||||
account_id: account.id,
|
||||
expiry_date: Between(
|
||||
currentDateWithZeroTime,
|
||||
currentDateWithDayEndTime
|
||||
),
|
||||
status: LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user: {
|
||||
auto_renew: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
relations: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 上で取得したshortageとユーザー数のどちらかが1以上ならプライマリ、セカンダリ管理者、親企業名を保持する
|
||||
// (shortageとユーザー数のどちらかが1以上 = アラートメールを送る必要がある)
|
||||
let primaryAdminExternalId: string | undefined;
|
||||
let secondaryAdminExternalId: string | undefined;
|
||||
let parentCompanyName: string | undefined;
|
||||
if (shortage !== 0 || userCount !== 0) {
|
||||
primaryAdminExternalId = account.primaryAdminUser
|
||||
? account.primaryAdminUser.external_id
|
||||
: undefined;
|
||||
secondaryAdminExternalId = account.secondaryAdminUser
|
||||
? account.secondaryAdminUser.external_id
|
||||
: undefined;
|
||||
// 第五のアカウントを取得
|
||||
// strictNullChecks対応
|
||||
if (account.parent_account_id) {
|
||||
const parent = await accountRepository.findOne({
|
||||
where: {
|
||||
id: account.parent_account_id,
|
||||
},
|
||||
});
|
||||
parentCompanyName = parent?.company_name;
|
||||
}
|
||||
} else {
|
||||
primaryAdminExternalId = undefined;
|
||||
secondaryAdminExternalId = undefined;
|
||||
parentCompanyName = undefined;
|
||||
}
|
||||
sendTargetAccounts.push({
|
||||
accountId: account.id,
|
||||
companyName: account.company_name,
|
||||
parentCompanyName: parentCompanyName,
|
||||
shortage: shortage,
|
||||
userCountOfLicenseExpiringSoon: userCount,
|
||||
primaryAdminExternalId: primaryAdminExternalId,
|
||||
secondaryAdminExternalId: secondaryAdminExternalId,
|
||||
primaryAdminEmail: undefined,
|
||||
secondaryAdminEmail: undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
await counts();
|
||||
|
||||
// ADB2Cからユーザーを取得する用の外部ID配列を作成
|
||||
const externalIds = [] as string[];
|
||||
sendTargetAccounts.map((x) => {
|
||||
if (x.primaryAdminExternalId) {
|
||||
externalIds.push(x.primaryAdminExternalId);
|
||||
}
|
||||
if (x.secondaryAdminExternalId) {
|
||||
externalIds.push(x.secondaryAdminExternalId);
|
||||
}
|
||||
});
|
||||
const adb2cUsers = await adb2c.getUsers(externalIds);
|
||||
|
||||
// ADB2Cから取得したメールアドレスをRDBから取得した情報にマージ
|
||||
sendTargetAccounts.map((info) => {
|
||||
const primaryAdminUser = adb2cUsers.find(
|
||||
(adb2c) => info.primaryAdminExternalId === adb2c.id
|
||||
);
|
||||
if (primaryAdminUser) {
|
||||
const primaryAdminMail = primaryAdminUser.identities?.find(
|
||||
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS
|
||||
)?.issuerAssignedId;
|
||||
if (primaryAdminMail) {
|
||||
info.primaryAdminEmail = primaryAdminMail;
|
||||
}
|
||||
|
||||
const secondaryAdminUser = adb2cUsers.find(
|
||||
(adb2c) => info.secondaryAdminExternalId === adb2c.id
|
||||
);
|
||||
if (secondaryAdminUser) {
|
||||
const secondaryAdminMail = secondaryAdminUser.identities?.find(
|
||||
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS
|
||||
)?.issuerAssignedId;
|
||||
if (secondaryAdminMail) {
|
||||
info.secondaryAdminEmail = secondaryAdminMail;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const sendMail = async () => {
|
||||
for (const targetAccount of sendTargetAccounts) {
|
||||
// プライマリ管理者が入っているかチェック
|
||||
// 入っていない場合は、アラートメールを送信する必要が無いため、何も処理をせず次のループへ
|
||||
if (targetAccount.primaryAdminExternalId) {
|
||||
// メール送信
|
||||
// strictNullChecks対応
|
||||
if (targetAccount.primaryAdminEmail) {
|
||||
// ライセンス不足メール
|
||||
if (targetAccount.shortage !== 0) {
|
||||
const { subject, text, html } =
|
||||
await sendgrid.createMailContentOfLicenseShortage();
|
||||
// メールを送信
|
||||
try {
|
||||
await sendgrid.sendMail(
|
||||
targetAccount.primaryAdminEmail,
|
||||
mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html
|
||||
);
|
||||
context.log(
|
||||
`Shortage mail send success. mail to :${targetAccount.primaryAdminEmail}`
|
||||
);
|
||||
} catch {
|
||||
context.log(
|
||||
`Shortage mail send failed. mail to :${targetAccount.primaryAdminEmail}`
|
||||
);
|
||||
}
|
||||
|
||||
// セカンダリ管理者が存在する場合、セカンダリ管理者にも送信
|
||||
if (targetAccount.secondaryAdminEmail) {
|
||||
// ライセンス不足メール
|
||||
if (targetAccount.shortage !== 0) {
|
||||
const { subject, text, html } =
|
||||
await sendgrid.createMailContentOfLicenseShortage();
|
||||
// メールを送信
|
||||
try {
|
||||
await sendgrid.sendMail(
|
||||
targetAccount.secondaryAdminEmail,
|
||||
mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html
|
||||
);
|
||||
context.log(
|
||||
`Shortage mail send success. mail to :${targetAccount.secondaryAdminEmail}`
|
||||
);
|
||||
} catch {
|
||||
context.log(
|
||||
`Shortage mail send failed. mail to :${targetAccount.secondaryAdminEmail}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ライセンス失効警告メール
|
||||
if (targetAccount.userCountOfLicenseExpiringSoon !== 0) {
|
||||
const { subject, text, html } =
|
||||
await sendgrid.createMailContentOfLicenseExpiringSoon();
|
||||
// メールを送信
|
||||
try {
|
||||
await sendgrid.sendMail(
|
||||
targetAccount.primaryAdminEmail,
|
||||
mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html
|
||||
);
|
||||
context.log(
|
||||
`Expiring soon mail send success. mail to :${targetAccount.primaryAdminEmail}`
|
||||
);
|
||||
} catch {
|
||||
context.log(
|
||||
`Expiring soon mail send failed. mail to :${targetAccount.primaryAdminEmail}`
|
||||
);
|
||||
}
|
||||
|
||||
// セカンダリ管理者が存在する場合、セカンダリ管理者にも送信
|
||||
if (targetAccount.secondaryAdminEmail) {
|
||||
// ライセンス不足メール
|
||||
if (targetAccount.shortage !== 0) {
|
||||
const { subject, text, html } =
|
||||
await sendgrid.createMailContentOfLicenseExpiringSoon();
|
||||
// メールを送信
|
||||
try {
|
||||
await sendgrid.sendMail(
|
||||
targetAccount.secondaryAdminEmail,
|
||||
mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html
|
||||
);
|
||||
context.log(
|
||||
`Expiring soon mail send success. mail to :${targetAccount.secondaryAdminEmail}`
|
||||
);
|
||||
} catch {
|
||||
context.log(
|
||||
`Expiring soon mail send failed. mail to :${targetAccount.secondaryAdminEmail}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
await sendMail();
|
||||
|
||||
context.log("[OUT]licenseAlertProcessing");
|
||||
}
|
||||
|
||||
export async function licenseAlert(
|
||||
myTimer: Timer,
|
||||
context: InvocationContext
|
||||
): Promise<void> {
|
||||
context.log("[IN]licenseAlert");
|
||||
|
||||
dotenv.config({ path: ".env" });
|
||||
dotenv.config({ path: ".env.local", override: true });
|
||||
const datasource = new DataSource({
|
||||
type: "mysql",
|
||||
host: process.env.DB_HOST,
|
||||
port: Number(process.env.DB_PORT),
|
||||
username: process.env.DB_USERNAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
entities: [User, Account, License],
|
||||
});
|
||||
await datasource.initialize();
|
||||
|
||||
const adb2c = new AdB2cService();
|
||||
const sendgrid = new SendGridService();
|
||||
try {
|
||||
await licenseAlertProcessing(context, datasource, sendgrid, adb2c);
|
||||
} catch (e) {
|
||||
context.log("licenseAlertProcessing failed");
|
||||
context.error(e);
|
||||
} finally {
|
||||
await datasource.destroy();
|
||||
context.log("[OUT]licenseAlert");
|
||||
}
|
||||
}
|
||||
|
||||
app.timer("licenseAlert", {
|
||||
schedule: "0 */1 * * * *",
|
||||
handler: licenseAlert,
|
||||
});
|
||||
|
||||
class accountInfo {
|
||||
accountId: number;
|
||||
companyName: string;
|
||||
parentCompanyName: string | undefined;
|
||||
shortage: number;
|
||||
userCountOfLicenseExpiringSoon: number;
|
||||
primaryAdminExternalId: string | undefined;
|
||||
secondaryAdminExternalId: string | undefined;
|
||||
primaryAdminEmail: string | undefined;
|
||||
secondaryAdminEmail: string | undefined;
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
import { app, InvocationContext, Timer } from "@azure/functions";
|
||||
import { DataSource } from "typeorm";
|
||||
import { User } from "../entity/user.entity";
|
||||
import { SendGridService } from "../sendgrid/sendgrid.service";
|
||||
import * as dotenv from "dotenv";
|
||||
|
||||
// タイマートリガー処理のサンプルです
|
||||
// TODO:開発が進んだら削除すること
|
||||
export async function timerTriggerExample(
|
||||
myTimer: Timer,
|
||||
context: InvocationContext
|
||||
): Promise<void> {
|
||||
context.log("Timer function processed request.");
|
||||
|
||||
dotenv.config({ path: ".env" });
|
||||
const datasource = new DataSource({
|
||||
type: "mysql",
|
||||
host: process.env.DB_HOST,
|
||||
port: Number(process.env.DB_PORT),
|
||||
username: process.env.DB_USERNAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
entities: [User],
|
||||
});
|
||||
|
||||
try {
|
||||
await datasource.initialize();
|
||||
const userRepository = datasource.getRepository(User); // Userエンティティに対応するリポジトリを取得
|
||||
|
||||
// ユーザーを検索
|
||||
const users = await userRepository.find();
|
||||
console.log(users);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
await datasource.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// test実行確認用サンプル
|
||||
// TODO:開発が進んだら削除すること
|
||||
export async function testExample(datasource: DataSource): Promise<User[]> {
|
||||
let users: User[];
|
||||
const userRepository = datasource.getRepository(User); // Userエンティティに対応するリポジトリを取得
|
||||
|
||||
// ユーザーを検索
|
||||
users = await userRepository.find();
|
||||
return users;
|
||||
}
|
||||
|
||||
// test実行確認用サンプル
|
||||
// TODO:開発が進んだら削除すること
|
||||
export async function testSendgridExample(): Promise<string> {
|
||||
const sendgrid = new SendGridService();
|
||||
|
||||
// メールを送信
|
||||
await sendgrid.sendMail(
|
||||
"oura.a89@gmail.com",
|
||||
process.env.MAIL_FROM,
|
||||
"testMail",
|
||||
"test!",
|
||||
"html"
|
||||
);
|
||||
return "sucsess";
|
||||
}
|
||||
|
||||
app.timer("timerTriggerExample", {
|
||||
schedule: "0 */1 * * * *",
|
||||
handler: timerTriggerExample,
|
||||
});
|
||||
@ -1,25 +1,48 @@
|
||||
import sendgrid from "@sendgrid/mail";
|
||||
import { error } from "console";
|
||||
|
||||
export class SendGridService {
|
||||
constructor() {
|
||||
if (!process.env.SENDGRID_API_KEY) {
|
||||
throw error;
|
||||
}
|
||||
sendgrid.setApiKey(process.env.SENDGRID_API_KEY);
|
||||
}
|
||||
/**
|
||||
* メールコンテンツを作成する
|
||||
* メールコンテンツを作成する(ライセンス不足)
|
||||
* @param accountId 認証対象のユーザーが所属するアカウントのID
|
||||
* @param userId 認証対象のユーザーのID
|
||||
* @param email 認証対象のユーザーのメールアドレス
|
||||
* @returns メールのサブジェクトとコンテンツ
|
||||
*/
|
||||
async createMailContent(
|
||||
accountId: number,
|
||||
userId: number,
|
||||
email: string
|
||||
): Promise<{ subject: string; text: string; html: string }> {
|
||||
async createMailContentOfLicenseShortage(): Promise<{
|
||||
subject: string;
|
||||
text: string;
|
||||
html: string;
|
||||
}> {
|
||||
return {
|
||||
subject: "Verify your new account",
|
||||
text: `The verification URL.`,
|
||||
html: `<p>The verification URL.<p>`,
|
||||
subject: "ライセンス在庫不足通知",
|
||||
text: `ライセンス在庫不足通知:本文`,
|
||||
html: `<p>ライセンス在庫不足通知:本文</p>`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* メールコンテンツを作成する(ライセンス不足)
|
||||
* @param accountId 認証対象のユーザーが所属するアカウントのID
|
||||
* @param userId 認証対象のユーザーのID
|
||||
* @param email 認証対象のユーザーのメールアドレス
|
||||
* @returns メールのサブジェクトとコンテンツ
|
||||
*/
|
||||
async createMailContentOfLicenseExpiringSoon(): Promise<{
|
||||
subject: string;
|
||||
text: string;
|
||||
html: string;
|
||||
}> {
|
||||
return {
|
||||
subject: "ライセンス失効警告 ",
|
||||
text: `ライセンス失効警告:本文`,
|
||||
html: `<p>ライセンス失効警告:本文</p>`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
198
dictation_function/src/test/common/utility.ts
Normal file
198
dictation_function/src/test/common/utility.ts
Normal file
@ -0,0 +1,198 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { DataSource } from "typeorm";
|
||||
import { User } from "../../entity/user.entity";
|
||||
import { Account } from "../../entity/account.entity";
|
||||
import { ADMIN_ROLES, USER_ROLES } from "../../constants";
|
||||
import { License } from "../../entity/license.entity";
|
||||
|
||||
type InitialTestDBState = {
|
||||
tier1Accounts: { account: Account; users: User[] }[];
|
||||
tier2Accounts: { account: Account; users: User[] }[];
|
||||
tier3Accounts: { account: Account; users: User[] }[];
|
||||
tier4Accounts: { account: Account; users: User[] }[];
|
||||
tier5Accounts: { account: Account; users: User[] }[];
|
||||
};
|
||||
|
||||
// 上書きされたら困る項目を除外したAccount型
|
||||
type OverrideAccount = Omit<
|
||||
Account,
|
||||
"id" | "primary_admin_user_id" | "secondary_admin_user_id" | "user"
|
||||
>;
|
||||
|
||||
// 上書きされたら困る項目を除外したUser型
|
||||
type OverrideUser = Omit<
|
||||
User,
|
||||
"id" | "account" | "license" | "userGroupMembers"
|
||||
>;
|
||||
|
||||
type AccountDefault = { [K in keyof OverrideAccount]?: OverrideAccount[K] };
|
||||
type UserDefault = { [K in keyof OverrideUser]?: OverrideUser[K] };
|
||||
|
||||
/**
|
||||
* テスト ユーティリティ: 指定したプロパティを上書きしたユーザーを作成する
|
||||
* @param dataSource データソース
|
||||
* @param defaultUserValue User型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト
|
||||
* @returns 作成したユーザー
|
||||
*/
|
||||
export const makeTestUser = async (
|
||||
datasource: DataSource,
|
||||
defaultUserValue?: UserDefault
|
||||
): Promise<User> => {
|
||||
const d = defaultUserValue;
|
||||
const { identifiers } = await datasource.getRepository(User).insert({
|
||||
account_id: d?.account_id ?? -1,
|
||||
external_id: d?.external_id ?? uuidv4(),
|
||||
role: d?.role ?? `${ADMIN_ROLES.STANDARD} ${USER_ROLES.NONE}`,
|
||||
author_id: d?.author_id,
|
||||
accepted_eula_version: d?.accepted_eula_version ?? "1.0",
|
||||
accepted_dpa_version: d?.accepted_dpa_version ?? "1.0",
|
||||
email_verified: d?.email_verified ?? true,
|
||||
auto_renew: d?.auto_renew ?? true,
|
||||
license_alert: d?.license_alert ?? true,
|
||||
notification: d?.notification ?? true,
|
||||
encryption: d?.encryption ?? true,
|
||||
encryption_password: d?.encryption_password,
|
||||
prompt: d?.prompt ?? true,
|
||||
created_by: d?.created_by ?? "test_runner",
|
||||
created_at: d?.created_at ?? new Date(),
|
||||
updated_by: d?.updated_by ?? "updater",
|
||||
updated_at: d?.updated_at ?? new Date(),
|
||||
});
|
||||
const result = identifiers.pop() as User;
|
||||
|
||||
const user = await datasource.getRepository(User).findOne({
|
||||
where: {
|
||||
id: result.id,
|
||||
},
|
||||
});
|
||||
if (!user) {
|
||||
throw new Error("Unexpected null");
|
||||
}
|
||||
return user;
|
||||
};
|
||||
|
||||
/**
|
||||
* テスト ユーティリティ: 指定したプロパティを上書きしたアカウントとその管理者ユーザーを作成する
|
||||
* @param dataSource データソース
|
||||
* @param defaultUserValue Account型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト
|
||||
* @param defaultAdminUserValue User型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト(account_id等の所属関係が破壊される上書きは無視する)
|
||||
* @returns 作成したアカウント
|
||||
*/
|
||||
export const makeTestAccount = async (
|
||||
datasource: DataSource,
|
||||
defaultAccountValue?: AccountDefault,
|
||||
defaultAdminUserValue?: UserDefault,
|
||||
isPrimaryAdminNotExist?: boolean,
|
||||
isSecondaryAdminNotExist?: boolean
|
||||
): Promise<{ account: Account; admin: User }> => {
|
||||
let accountId: number;
|
||||
let userId: number;
|
||||
{
|
||||
const d = defaultAccountValue;
|
||||
const { identifiers } = await datasource.getRepository(Account).insert({
|
||||
tier: d?.tier ?? 1,
|
||||
parent_account_id: d?.parent_account_id ?? undefined,
|
||||
country: d?.country ?? "US",
|
||||
delegation_permission: d?.delegation_permission ?? false,
|
||||
locked: d?.locked ?? false,
|
||||
company_name: d?.company_name ?? "test inc.",
|
||||
verified: d?.verified ?? true,
|
||||
deleted_at: d?.deleted_at ?? "",
|
||||
created_by: d?.created_by ?? "test_runner",
|
||||
created_at: d?.created_at ?? new Date(),
|
||||
updated_by: d?.updated_by ?? "updater",
|
||||
updated_at: d?.updated_at ?? new Date(),
|
||||
});
|
||||
const result = identifiers.pop() as Account;
|
||||
accountId = result.id;
|
||||
}
|
||||
{
|
||||
const d = defaultAdminUserValue;
|
||||
const { identifiers } = await datasource.getRepository(User).insert({
|
||||
external_id: d?.external_id ?? uuidv4(),
|
||||
account_id: accountId,
|
||||
role: d?.role ?? "admin none",
|
||||
author_id: d?.author_id ?? undefined,
|
||||
accepted_eula_version: d?.accepted_eula_version ?? "1.0",
|
||||
accepted_dpa_version: d?.accepted_dpa_version ?? "1.0",
|
||||
email_verified: d?.email_verified ?? true,
|
||||
auto_renew: d?.auto_renew ?? true,
|
||||
license_alert: d?.license_alert ?? true,
|
||||
notification: d?.notification ?? true,
|
||||
encryption: d?.encryption ?? true,
|
||||
encryption_password: d?.encryption_password ?? "password",
|
||||
prompt: d?.prompt ?? true,
|
||||
deleted_at: d?.deleted_at ?? "",
|
||||
created_by: d?.created_by ?? "test_runner",
|
||||
created_at: d?.created_at ?? new Date(),
|
||||
updated_by: d?.updated_by ?? "updater",
|
||||
updated_at: d?.updated_at ?? new Date(),
|
||||
});
|
||||
|
||||
const result = identifiers.pop() as User;
|
||||
userId = result.id;
|
||||
}
|
||||
|
||||
// Accountの管理者を設定する
|
||||
let secondaryAdminUserId: number | null = null;
|
||||
if (isPrimaryAdminNotExist && !isSecondaryAdminNotExist) {
|
||||
secondaryAdminUserId = userId;
|
||||
}
|
||||
await datasource.getRepository(Account).update(
|
||||
{ id: accountId },
|
||||
{
|
||||
primary_admin_user_id: isPrimaryAdminNotExist ? null : userId,
|
||||
secondary_admin_user_id: secondaryAdminUserId,
|
||||
}
|
||||
);
|
||||
|
||||
const account = await datasource.getRepository(Account).findOne({
|
||||
where: {
|
||||
id: accountId,
|
||||
},
|
||||
});
|
||||
|
||||
const admin = await datasource.getRepository(User).findOne({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
});
|
||||
if (!account || !admin) {
|
||||
throw new Error("Unexpected null");
|
||||
}
|
||||
|
||||
return {
|
||||
account: account,
|
||||
admin: admin,
|
||||
};
|
||||
};
|
||||
|
||||
export const createLicense = async (
|
||||
datasource: DataSource,
|
||||
licenseId: number,
|
||||
expiry_date: Date | null,
|
||||
accountId: number,
|
||||
type: string,
|
||||
status: string,
|
||||
allocated_user_id: number | null,
|
||||
order_id: number | null,
|
||||
deleted_at: Date | null,
|
||||
delete_order_id: number | null
|
||||
): Promise<void> => {
|
||||
const { identifiers } = await datasource.getRepository(License).insert({
|
||||
id: licenseId,
|
||||
expiry_date: expiry_date,
|
||||
account_id: accountId,
|
||||
type: type,
|
||||
status: status,
|
||||
allocated_user_id: allocated_user_id,
|
||||
order_id: order_id,
|
||||
deleted_at: deleted_at,
|
||||
delete_order_id: delete_order_id,
|
||||
created_by: "test_runner",
|
||||
created_at: new Date(),
|
||||
updated_by: "updater",
|
||||
updated_at: new Date(),
|
||||
});
|
||||
identifiers.pop() as License;
|
||||
};
|
||||
339
dictation_function/src/test/licenseAlert.spec.ts
Normal file
339
dictation_function/src/test/licenseAlert.spec.ts
Normal file
@ -0,0 +1,339 @@
|
||||
import { DataSource } from "typeorm";
|
||||
import { licenseAlertProcessing } from "../functions/licenseAlert";
|
||||
import { makeTestAccount, createLicense } from "./common/utility";
|
||||
import * as dotenv from "dotenv";
|
||||
import {
|
||||
DateWithDayEndTime,
|
||||
DateWithZeroTime,
|
||||
ExpirationThresholdDate,
|
||||
NewTrialLicenseExpirationDate,
|
||||
} from "../common/types/types";
|
||||
import { AdB2cUser } from "../adb2c/types/types";
|
||||
import { ADB2C_SIGN_IN_TYPE } from "../constants";
|
||||
import { SendGridService } from "../sendgrid/sendgrid.service";
|
||||
import { AdB2cService } from "../adb2c/adb2c.service";
|
||||
import { InvocationContext } from "@azure/functions";
|
||||
|
||||
describe("licenseAlert", () => {
|
||||
dotenv.config({ path: ".env" });
|
||||
dotenv.config({ path: ".env.local", override: true });
|
||||
let source: DataSource | null = null;
|
||||
beforeEach(async () => {
|
||||
source = new DataSource({
|
||||
type: "sqlite",
|
||||
database: ":memory:",
|
||||
logging: false,
|
||||
entities: [__dirname + "/../../**/*.entity{.ts,.js}"],
|
||||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return source.initialize();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (!source) return;
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
|
||||
it("ライセンス在庫不足メールが送信され、ライセンス失効警告メールが送信されないこと", async () => {
|
||||
if (!source) fail();
|
||||
const context = new InvocationContext();
|
||||
const sendgridMock = new SendGridServiceMock() as SendGridService;
|
||||
const adb2cMock = new AdB2cServiceMock() as AdB2cService;
|
||||
// 呼び出し回数でテスト成否を判定
|
||||
const spyShortage = jest.spyOn(
|
||||
sendgridMock,
|
||||
"createMailContentOfLicenseShortage"
|
||||
);
|
||||
const spyExpirySoon = jest.spyOn(
|
||||
sendgridMock,
|
||||
"createMailContentOfLicenseExpiringSoon"
|
||||
);
|
||||
const spySend = jest.spyOn(sendgridMock, "sendMail");
|
||||
|
||||
const currentDate = new DateWithZeroTime();
|
||||
const expiringSoonDate = new ExpirationThresholdDate(currentDate.getTime());
|
||||
const { account, admin } = await makeTestAccount(
|
||||
source,
|
||||
{ tier: 5 },
|
||||
{ external_id: "external_id1" }
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
expiringSoonDate,
|
||||
account.id,
|
||||
"STANDARD",
|
||||
"Allocated",
|
||||
admin.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
await licenseAlertProcessing(context, source, sendgridMock, adb2cMock);
|
||||
expect(spyShortage.mock.calls).toHaveLength(1);
|
||||
expect(spyExpirySoon.mock.calls).toHaveLength(0);
|
||||
expect(spySend.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("ライセンス在庫不足メール、ライセンス失効警告メールが送信されること", async () => {
|
||||
if (!source) fail();
|
||||
const context = new InvocationContext();
|
||||
const sendgridMock = new SendGridServiceMock() as SendGridService;
|
||||
const adb2cMock = new AdB2cServiceMock() as AdB2cService;
|
||||
|
||||
// 呼び出し回数でテスト成否を判定
|
||||
const spyShortage = jest.spyOn(
|
||||
sendgridMock,
|
||||
"createMailContentOfLicenseShortage"
|
||||
);
|
||||
const spyExpirySoon = jest.spyOn(
|
||||
sendgridMock,
|
||||
"createMailContentOfLicenseExpiringSoon"
|
||||
);
|
||||
const spySend = jest.spyOn(sendgridMock, "sendMail");
|
||||
|
||||
const currentDate = new DateWithZeroTime();
|
||||
const expiringSoonDate = new DateWithDayEndTime(currentDate.getTime());
|
||||
const { account, admin } = await makeTestAccount(
|
||||
source,
|
||||
{ tier: 5 },
|
||||
{ external_id: "external_id2", auto_renew: false }
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
expiringSoonDate,
|
||||
account.id,
|
||||
"STANDARD",
|
||||
"Allocated",
|
||||
admin.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
await licenseAlertProcessing(context, source, sendgridMock, adb2cMock);
|
||||
expect(spyShortage.mock.calls).toHaveLength(1);
|
||||
expect(spyExpirySoon.mock.calls).toHaveLength(1);
|
||||
expect(spySend.mock.calls).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("在庫があるため、ライセンス在庫不足メールが送信されないこと", async () => {
|
||||
if (!source) fail();
|
||||
const context = new InvocationContext();
|
||||
const sendgridMock = new SendGridServiceMock() as SendGridService;
|
||||
const adb2cMock = new AdB2cServiceMock() as AdB2cService;
|
||||
|
||||
// 呼び出し回数でテスト成否を判定
|
||||
const spyShortage = jest.spyOn(
|
||||
sendgridMock,
|
||||
"createMailContentOfLicenseShortage"
|
||||
);
|
||||
const spyExpirySoon = jest.spyOn(
|
||||
sendgridMock,
|
||||
"createMailContentOfLicenseExpiringSoon"
|
||||
);
|
||||
const spySend = jest.spyOn(sendgridMock, "sendMail");
|
||||
|
||||
const currentDate = new DateWithZeroTime();
|
||||
const expiringSoonDate = new ExpirationThresholdDate(currentDate.getTime());
|
||||
const expiryDate = new NewTrialLicenseExpirationDate(currentDate.getTime());
|
||||
const { account, admin } = await makeTestAccount(
|
||||
source,
|
||||
{ tier: 5 },
|
||||
{ external_id: "external_id3" }
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
expiringSoonDate,
|
||||
account.id,
|
||||
"STANDARD",
|
||||
"Allocated",
|
||||
admin.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
2,
|
||||
expiryDate,
|
||||
account.id,
|
||||
"STANDARD",
|
||||
"Unallocated",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
await licenseAlertProcessing(context, source, sendgridMock, adb2cMock);
|
||||
expect(spyShortage.mock.calls).toHaveLength(0);
|
||||
expect(spyExpirySoon.mock.calls).toHaveLength(0);
|
||||
expect(spySend.mock.calls).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("AutoRenewがtureのため、ライセンス失効警告メールが送信されないこと", async () => {
|
||||
if (!source) fail();
|
||||
const context = new InvocationContext();
|
||||
const sendgridMock = new SendGridServiceMock() as SendGridService;
|
||||
const adb2cMock = new AdB2cServiceMock() as AdB2cService;
|
||||
|
||||
// 呼び出し回数でテスト成否を判定
|
||||
const spyShortage = jest.spyOn(
|
||||
sendgridMock,
|
||||
"createMailContentOfLicenseShortage"
|
||||
);
|
||||
const spyExpirySoon = jest.spyOn(
|
||||
sendgridMock,
|
||||
"createMailContentOfLicenseExpiringSoon"
|
||||
);
|
||||
const spySend = jest.spyOn(sendgridMock, "sendMail");
|
||||
|
||||
const currentDate = new DateWithZeroTime();
|
||||
const expiringSoonDate = new DateWithDayEndTime(currentDate.getTime());
|
||||
const { account, admin } = await makeTestAccount(
|
||||
source,
|
||||
{ tier: 5 },
|
||||
{ external_id: "external_id4", auto_renew: true }
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
expiringSoonDate,
|
||||
account.id,
|
||||
"STANDARD",
|
||||
"Allocated",
|
||||
admin.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
await licenseAlertProcessing(context, source, sendgridMock, adb2cMock);
|
||||
expect(spyShortage.mock.calls).toHaveLength(1);
|
||||
expect(spyExpirySoon.mock.calls).toHaveLength(0);
|
||||
expect(spySend.mock.calls).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
// テスト用sendgrid
|
||||
export class SendGridServiceMock {
|
||||
/**
|
||||
* メールコンテンツを作成する(ライセンス不足)
|
||||
* @param accountId 認証対象のユーザーが所属するアカウントのID
|
||||
* @param userId 認証対象のユーザーのID
|
||||
* @param email 認証対象のユーザーのメールアドレス
|
||||
* @returns メールのサブジェクトとコンテンツ
|
||||
*/
|
||||
async createMailContentOfLicenseShortage(): Promise<{
|
||||
subject: string;
|
||||
text: string;
|
||||
html: string;
|
||||
}> {
|
||||
return {
|
||||
subject: "ライセンス在庫不足通知",
|
||||
text: `ライセンス在庫不足通知:本文`,
|
||||
html: `<p>ライセンス在庫不足通知:本文</p>`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* メールコンテンツを作成する(ライセンス不足)
|
||||
* @param accountId 認証対象のユーザーが所属するアカウントのID
|
||||
* @param userId 認証対象のユーザーのID
|
||||
* @param email 認証対象のユーザーのメールアドレス
|
||||
* @returns メールのサブジェクトとコンテンツ
|
||||
*/
|
||||
async createMailContentOfLicenseExpiringSoon(): Promise<{
|
||||
subject: string;
|
||||
text: string;
|
||||
html: string;
|
||||
}> {
|
||||
return {
|
||||
subject: "ライセンス失効警告",
|
||||
text: `ライセンス失効警告:本文`,
|
||||
html: `<p>ライセンス失効警告:本文</p>`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* メールを送信する
|
||||
* @param to
|
||||
* @param from
|
||||
* @param subject
|
||||
* @param text
|
||||
* @param html
|
||||
* @returns mail
|
||||
*/
|
||||
async sendMail(
|
||||
to: string,
|
||||
from: string,
|
||||
subject: string,
|
||||
text: string,
|
||||
html: string
|
||||
): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// テスト用adb2c
|
||||
export class AdB2cServiceMock {
|
||||
/**
|
||||
* Azure AD B2Cからユーザ情報を取得する
|
||||
* @param externalIds 外部ユーザーID
|
||||
* @returns ユーザ情報
|
||||
*/
|
||||
async getUsers(externalIds: string[]): Promise<AdB2cUser[]> {
|
||||
const AdB2cMockUsers: AdB2cUser[] = [
|
||||
{
|
||||
id: "external_id1",
|
||||
displayName: "test1",
|
||||
identities: [
|
||||
{
|
||||
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||||
issuer: "issuer",
|
||||
issuerAssignedId: "test1@mail.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "external_id2",
|
||||
displayName: "test2",
|
||||
identities: [
|
||||
{
|
||||
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||||
issuer: "issuer",
|
||||
issuerAssignedId: "test2@mail.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "external_id3",
|
||||
displayName: "test3",
|
||||
identities: [
|
||||
{
|
||||
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||||
issuer: "issuer",
|
||||
issuerAssignedId: "test3@mail.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "external_id4",
|
||||
displayName: "test4",
|
||||
identities: [
|
||||
{
|
||||
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||||
issuer: "issuer",
|
||||
issuerAssignedId: "test4@mail.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
return AdB2cMockUsers;
|
||||
}
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
import { DataSource } from "typeorm";
|
||||
import {
|
||||
testExample,
|
||||
testSendgridExample,
|
||||
} from "../functions/timerTriggerExample";
|
||||
import { makeTestUser } from "../common/test/utility";
|
||||
import * as dotenv from "dotenv";
|
||||
|
||||
describe("timerTriggerExample", () => {
|
||||
dotenv.config({ path: ".env.local", override: true });
|
||||
let source: DataSource | null = null;
|
||||
beforeEach(async () => {
|
||||
source = new DataSource({
|
||||
type: "sqlite",
|
||||
database: ":memory:",
|
||||
logging: false,
|
||||
entities: [__dirname + "/../../**/*.entity{.ts,.js}"],
|
||||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return source.initialize();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (!source) return;
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
|
||||
it("sample test(DB)", async () => {
|
||||
const count = 5;
|
||||
for (let i = 0; i < count; i++) {
|
||||
await makeTestUser(source);
|
||||
}
|
||||
|
||||
const result = await testExample(source);
|
||||
expect(result.length).toEqual(count);
|
||||
});
|
||||
|
||||
it("sample test(sendgrid)", async () => {
|
||||
await testSendgridExample();
|
||||
});
|
||||
});
|
||||
@ -8,6 +8,7 @@
|
||||
"strict": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
"strictNullChecks": true,
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user