diff --git a/README.md b/README.md index 635e506..77faf35 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,14 @@ +

🥰 Any kinds of contributions are welcome! 🥰

+ +# Installation + +``` +yarn add nestjs-slack-listener +``` + # Usage ## Settings @@ -24,7 +32,7 @@ Import the module at your app module. @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), - SlackHandlerModule.forRootAsync({ + SlackModule.forRootAsync({ useFactory: async (config: ConfigService) => ({ botToken: config.get('SLACK_BOT_TOKEN'), }), diff --git a/package.json b/package.json index 2b22399..205bfd8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nestjs-slack-listener", - "version": "1.0.2", + "version": "1.0.4", "description": "NestJS Slack listeners and handlers", "author": "Chung Hwan Han ", "license": "MIT", @@ -74,7 +74,7 @@ "pre-commit": "^1.2.2", "prettier": "2.6.1", "reflect-metadata": "^0.1.13", - "supertest": "6.2.2", + "supertest": "^6.3.0", "ts-jest": "27.1.4", "ts-node": "10.7.0", "tsc-watch": "5.0.3", @@ -82,17 +82,30 @@ "typescript": "4.6.3" }, "jest": { + "moduleDirectories": [ + "node_modules", + "src" + ], "moduleFileExtensions": [ "js", "json", "ts" ], - "rootDir": "src", - "testRegex": ".spec.ts$", + "roots": [ + "src" + ], + "testRegex": ".*\\.spec\\.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" }, + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], "coverageDirectory": "../coverage", - "testEnvironment": "node" + "testEnvironment": "node", + "moduleNameMapper": { + "gaxios": "gaxios", + "src/(.*)": "/src/$1" + } } } diff --git a/src/handler/index.ts b/src/handler/index.ts deleted file mode 100644 index 28500d8..0000000 --- a/src/handler/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './decorators'; -export * from './interfaces'; -export * from './slack-handler.module'; diff --git a/src/index.ts b/src/index.ts index 68ae53f..b7c3666 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1 @@ -export * from './handler'; +export * from './slack'; diff --git a/src/handler/constant/symbol.ts b/src/slack/constant/symbol.ts similarity index 81% rename from src/handler/constant/symbol.ts rename to src/slack/constant/symbol.ts index 27d980a..2bd1611 100644 --- a/src/handler/constant/symbol.ts +++ b/src/slack/constant/symbol.ts @@ -1,3 +1,4 @@ export const SLACK_CONFIG_OPTIONS = 'SLACK_CONFIG_OPTIONS'; export const SLACK_INTERACTIVITY_HANDLER = 'SLACK_INTERACTIVITY_HANDLER'; export const SLACK_EVENT_HANDLER = 'SLACK_EVENT_HANDLER'; +export const SLACK_CLIENT = 'SLACK_CLIENT'; diff --git a/src/slack/decorators/client.decorator.ts b/src/slack/decorators/client.decorator.ts new file mode 100644 index 0000000..ecbaf08 --- /dev/null +++ b/src/slack/decorators/client.decorator.ts @@ -0,0 +1,4 @@ +import { Inject } from '@nestjs/common'; +import { SLACK_CLIENT } from '../constant/symbol'; + +export const InjectSlackClient = () => Inject(SLACK_CLIENT); diff --git a/src/handler/decorators/command.decorator.ts b/src/slack/decorators/command.decorator.ts similarity index 100% rename from src/handler/decorators/command.decorator.ts rename to src/slack/decorators/command.decorator.ts diff --git a/src/handler/decorators/handler.decorator.ts b/src/slack/decorators/handler.decorator.ts similarity index 100% rename from src/handler/decorators/handler.decorator.ts rename to src/slack/decorators/handler.decorator.ts diff --git a/src/handler/decorators/index.ts b/src/slack/decorators/index.ts similarity index 67% rename from src/handler/decorators/index.ts rename to src/slack/decorators/index.ts index 801a5e1..adae4e3 100644 --- a/src/handler/decorators/index.ts +++ b/src/slack/decorators/index.ts @@ -1,2 +1,3 @@ export * from './command.decorator'; export * from './handler.decorator'; +export * from './client.decorator'; diff --git a/src/handler/handler.explorer.ts b/src/slack/handler.explorer.ts similarity index 100% rename from src/handler/handler.explorer.ts rename to src/slack/handler.explorer.ts diff --git a/src/slack/index.ts b/src/slack/index.ts new file mode 100644 index 0000000..328a995 --- /dev/null +++ b/src/slack/index.ts @@ -0,0 +1,4 @@ +export * from './decorators'; +export * from './interfaces'; +export * from './types/client'; +export * from './slack.module'; diff --git a/src/handler/interfaces/handler.interface.ts b/src/slack/interfaces/handler.interface.ts similarity index 100% rename from src/handler/interfaces/handler.interface.ts rename to src/slack/interfaces/handler.interface.ts diff --git a/src/handler/interfaces/incoming.interface.ts b/src/slack/interfaces/incoming.interface.ts similarity index 100% rename from src/handler/interfaces/incoming.interface.ts rename to src/slack/interfaces/incoming.interface.ts diff --git a/src/handler/interfaces/index.ts b/src/slack/interfaces/index.ts similarity index 100% rename from src/handler/interfaces/index.ts rename to src/slack/interfaces/index.ts diff --git a/src/handler/interfaces/module-options.interface.ts b/src/slack/interfaces/module-options.interface.ts similarity index 100% rename from src/handler/interfaces/module-options.interface.ts rename to src/slack/interfaces/module-options.interface.ts diff --git a/src/slack/slack-client.service.ts b/src/slack/slack-client.service.ts new file mode 100644 index 0000000..c6f8046 --- /dev/null +++ b/src/slack/slack-client.service.ts @@ -0,0 +1,13 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { WebClient } from '@slack/web-api'; +import { SLACK_CONFIG_OPTIONS } from './constant/symbol'; +import { SlackModuleOptions } from './interfaces'; + +@Injectable() +export class SlackClientService { + public readonly client: WebClient; + + constructor(@Inject(SLACK_CONFIG_OPTIONS) options: SlackModuleOptions) { + this.client = new WebClient(options.botToken); + } +} diff --git a/src/handler/slack-handler.controller.ts b/src/slack/slack-handler.controller.ts similarity index 100% rename from src/handler/slack-handler.controller.ts rename to src/slack/slack-handler.controller.ts diff --git a/src/handler/slack-handler.service.ts b/src/slack/slack-handler.service.ts similarity index 100% rename from src/handler/slack-handler.service.ts rename to src/slack/slack-handler.service.ts diff --git a/src/handler/slack-handler.module.ts b/src/slack/slack.module.ts similarity index 71% rename from src/handler/slack-handler.module.ts rename to src/slack/slack.module.ts index f03a57f..b5e8fe6 100644 --- a/src/handler/slack-handler.module.ts +++ b/src/slack/slack.module.ts @@ -1,19 +1,21 @@ import { DynamicModule, Module, OnModuleInit, Provider } from '@nestjs/common'; import { MetadataScanner } from '@nestjs/core'; -import { SLACK_CONFIG_OPTIONS } from './constant/symbol'; +import { SLACK_CLIENT, SLACK_CONFIG_OPTIONS } from './constant/symbol'; import { SlackHandlerExplorer } from './handler.explorer'; import { SlackModuleAsyncOptions, SlackModuleOptions } from './interfaces'; +import { SlackClientService } from './slack-client.service'; import { SlackEventsController } from './slack-handler.controller'; import { SlackHandler } from './slack-handler.service'; @Module({ providers: [MetadataScanner, SlackHandlerExplorer], }) -export class SlackHandlerModule implements OnModuleInit { +export class SlackModule implements OnModuleInit { static forRoot(options: SlackModuleOptions): DynamicModule { const slackServiceProvider = this.createSlackServiceProvider(); + const slackClientProvider = this.createSlackClientProvider(); return { - module: SlackHandlerModule, + module: SlackModule, controllers: [SlackEventsController], providers: [ { @@ -21,20 +23,22 @@ export class SlackHandlerModule implements OnModuleInit { useValue: options, }, slackServiceProvider, + slackClientProvider, ], - exports: [SlackHandler], + exports: [SlackHandler, SLACK_CLIENT], }; } static forRootAsync(options: SlackModuleAsyncOptions): DynamicModule { const slackServiceProvider = this.createSlackServiceProvider(); + const slackClientProvider = this.createSlackClientProvider(); const asyncProviders = this.createAsyncProviders(options); return { - module: SlackHandlerModule, + module: SlackModule, controllers: [SlackEventsController], imports: options.imports || [], - providers: [...asyncProviders, slackServiceProvider], - exports: [SlackHandler], + providers: [...asyncProviders, slackServiceProvider, slackClientProvider], + exports: [SlackHandler, SLACK_CLIENT], }; } @@ -46,6 +50,15 @@ export class SlackHandlerModule implements OnModuleInit { }; } + private static createSlackClientProvider(): Provider { + return { + provide: SLACK_CLIENT, + useFactory: (options: SlackModuleOptions) => + new SlackClientService(options).client, + inject: [SLACK_CONFIG_OPTIONS], + }; + } + private static createAsyncProviders( options: SlackModuleAsyncOptions, ): Provider[] { diff --git a/src/slack/types/client.ts b/src/slack/types/client.ts new file mode 100644 index 0000000..e8cd5dc --- /dev/null +++ b/src/slack/types/client.ts @@ -0,0 +1,3 @@ +import { WebClient } from '@slack/web-api'; + +export type SlackClient = WebClient; diff --git a/src/handler/types/event.ts b/src/slack/types/event.ts similarity index 100% rename from src/handler/types/event.ts rename to src/slack/types/event.ts diff --git a/src/test/slack-handler.module.spec.ts b/src/test/slack-handler.module.spec.ts deleted file mode 100644 index f8a2e74..0000000 --- a/src/test/slack-handler.module.spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -describe('Slack Module Initialization', () => { - describe('forRoot', () => { - it('should be defined', () => { - return; - }); - }); - describe('forRootAsync', () => { - it('should be defined', () => { - return; - }); - }); -}); diff --git a/src/test/slack.module.spec.ts b/src/test/slack.module.spec.ts new file mode 100644 index 0000000..124667b --- /dev/null +++ b/src/test/slack.module.spec.ts @@ -0,0 +1,111 @@ +import { Controller, Get, Injectable, Module } from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import { SlackClient, SlackEventListener, SlackModule } from 'src/slack'; +import { InjectSlackClient } from 'src/slack/decorators'; +import * as request from 'supertest'; + +describe('Slack Module Initialization', () => { + describe('forRoot', () => { + it('should compile only with botToken option', async () => { + @Controller() + @SlackEventListener() + class TestController { + @Get() + get() { + return ''; + } + } + @Module({ + imports: [SlackModule.forRoot({ botToken: 'test' })], + controllers: [TestController], + }) + class TestModule {} + + const app = await NestFactory.create(TestModule); + const server = app.getHttpServer(); + + await app.init(); + await request(server).get('/').expect(200); + await app.close(); + }); + it('should compile including slack client', async () => { + @Injectable() + class TestService { + constructor(@InjectSlackClient() slack: SlackClient) { + expect(slack).toBeDefined(); + } + getBotToken() { + return 'test'; + } + } + @Module({ + imports: [ + SlackModule.forRoot({ + botToken: 'test', + }), + ], + providers: [TestService], + }) + class TestModule {} + + const app = await NestFactory.create(TestModule); + + await app.init(); + await app.close(); + }); + }); + describe('forRootAsync', () => { + it('should compile only with botToken option', async () => { + @Controller() + @SlackEventListener() + class TestController { + @Get() + get() { + return ''; + } + } + @Module({ + imports: [ + SlackModule.forRootAsync({ + useFactory: () => ({ botToken: 'test' }), + }), + ], + controllers: [TestController], + }) + class TestModule {} + + const app = await NestFactory.create(TestModule); + const server = app.getHttpServer(); + + await app.init(); + await request(server).get('/').expect(200); + await app.close(); + }); + + it('should compile including slack client', async () => { + @Injectable() + class TestService { + constructor(@InjectSlackClient() slack: SlackClient) { + expect(slack).toBeDefined(); + } + getBotToken() { + return 'test'; + } + } + @Module({ + imports: [ + SlackModule.forRootAsync({ + useFactory: () => ({ botToken: 'test' }), + }), + ], + providers: [TestService], + }) + class TestModule {} + + const app = await NestFactory.create(TestModule); + + await app.init(); + await app.close(); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 9e6f526..b2de1ab 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,5 +13,5 @@ "noLib": false }, "include": ["src/**/*.ts"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "example"] } diff --git a/yarn.lock b/yarn.lock index 8d665f4..e38325c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1555,7 +1555,7 @@ debug@2.6.9, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -3289,7 +3289,7 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.5.0: +mime@2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -3734,7 +3734,7 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@6.10.3, qs@^6.10.1: +qs@6.10.3: version "6.10.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== @@ -3746,6 +3746,13 @@ qs@6.9.3: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e" integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw== +qs@^6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -3784,15 +3791,6 @@ readable-stream@^2.2.2: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -3870,7 +3868,7 @@ rxjs@^7.x: dependencies: tslib "^2.1.0" -safe-buffer@5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -4091,13 +4089,6 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -4132,30 +4123,29 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -superagent@^7.1.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-7.1.1.tgz#2ab187d38c3078c31c3771c0b751f10163a27136" - integrity sha512-CQ2weSS6M+doIwwYFoMatklhRbx6sVNdB99OEJ5czcP3cng76Ljqus694knFWgOj3RkrtxZqIgpe6vhe0J7QWQ== +superagent@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.0.2.tgz#23e753da16f2a0a5afa2fe2b41cafed5b22a5bde" + integrity sha512-QtYZ9uaNAMexI7XWl2vAXAh0j4q9H7T0WVEI/y5qaUB3QLwxo+voUgCQ217AokJzUTIVOp0RTo7fhZrwhD7A2Q== dependencies: component-emitter "^1.3.0" cookiejar "^2.1.3" - debug "^4.3.3" + debug "^4.3.4" fast-safe-stringify "^2.1.1" form-data "^4.0.0" formidable "^2.0.1" methods "^1.1.2" - mime "^2.5.0" - qs "^6.10.1" - readable-stream "^3.6.0" - semver "^7.3.5" + mime "2.6.0" + qs "^6.11.0" + semver "^7.3.7" -supertest@6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.2.2.tgz#04a5998fd3efaff187cb69f07a169755d655b001" - integrity sha512-wCw9WhAtKJsBvh07RaS+/By91NNE0Wh0DN19/hWPlBOU8tAfOtbZoVSV4xXeoKoxgPx0rx2y+y+8660XtE7jzg== +supertest@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.3.0.tgz#06c21234ff0be9e398ba19f7a75f237930d57442" + integrity sha512-QgWju1cNoacP81Rv88NKkQ4oXTzGg0eNZtOoxp1ROpbS4OHY/eK5b8meShuFtdni161o5X0VQvgo7ErVyKK+Ow== dependencies: methods "^1.1.2" - superagent "^7.1.0" + superagent "^8.0.0" supports-color@^5.3.0: version "5.5.0" @@ -4428,7 +4418,7 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=