Skip to content

Commit

Permalink
feat: add provider and limiter service
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Feb 5, 2024
1 parent 707803e commit f1a41d5
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 11 deletions.
15 changes: 15 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* @adonisjs/limiter
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

export * as errors from './src/errors.js'
export { Limiter } from './src/limiter.js'
export { LimiterResponse } from './src/response.js'
export { HttpLimiter } from './src/http_limiter.js'
export { LimiterManager } from './src/limiter_manager.js'
export { defineConfig, stores } from './src/define_config.js'
12 changes: 1 addition & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,12 @@
"main": "build/index.js",
"type": "module",
"files": [
"build/src",
"build/services",
"build/providers",
"build/commands",
"build/factories",
"build/stubs",
"build/configure.js",
"build/configure.d.ts",
"build/index.d.ts",
"build/index.js"
"build"
],
"exports": {
".": "./build/index.js",
"./limiter_provider": "./build/providers/limiter_provider.js",
"./services/main": "./build/services/main.js",
"./throttle_middleware": "./build/src/throttle_middleware.js",
"./types": "./build/src/types.js"
},
"scripts": {
Expand Down
43 changes: 43 additions & 0 deletions providers/limiter_provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* @adonisjs/limiter
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { configProvider } from '@adonisjs/core'
import { ApplicationService } from '@adonisjs/core/types'
import { RuntimeException } from '@adonisjs/core/exceptions'

import { LimiterManager } from '../index.js'
import type { LimiterService } from '../src/types.js'

declare module '@adonisjs/core/types' {
export interface ContainerBindings {
'limiter.manager': LimiterService
}
}

export default class LimiterProvider {
constructor(protected app: ApplicationService) {}

register() {
this.app.container.singleton('limiter.manager', async () => {
const limiterConfigProvider = this.app.config.get('limiter', {})

/**
* Resolve config from the provider
*/
const config = await configProvider.resolve<any>(this.app, limiterConfigProvider)
if (!config) {
throw new RuntimeException(
'Invalid "config/limiter.ts" file. Make sure you are using the "defineConfig" method'
)
}

return new LimiterManager(config)
})
}
}
23 changes: 23 additions & 0 deletions services/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* @adonisjs/limiter
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import app from '@adonisjs/core/services/app'
import { LimiterService } from '../src/types.js'

let limiter: LimiterService

/**
* Returns a singleton instance of the LimiterManager class from the
* container.
*/
await app.booted(async () => {
limiter = await app.container.make('limiter.manager')
})

export { limiter as default }
24 changes: 24 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
* file that was distributed with this source code.
*/

import { ConfigProvider } from '@adonisjs/core/types'
import { LimiterManager } from './limiter_manager.js'
import type { LimiterResponse } from './response.js'

/**
Expand Down Expand Up @@ -220,3 +222,25 @@ export interface LimiterStoreContract {
export type LimiterManagerStoreFactory = (
options: LimiterConsumptionOptions
) => LimiterStoreContract

/**
* A list of known limiters inferred from the user config
*/
export interface LimitersList {}

/**
* Helper method to resolve configured limiters
* inside user app
*/
export type InferLimiters<
T extends ConfigProvider<{ stores: Record<string, LimiterManagerStoreFactory> }>,
> = Awaited<ReturnType<T['resolver']>>['stores']

/**
* Limiter service is a singleton instance of limiter
* manager configured using user app's config
*/
export interface LimiterService
extends LimiterManager<
LimitersList extends Record<string, LimiterManagerStoreFactory> ? LimitersList : never
> {}
88 changes: 88 additions & 0 deletions tests/limiter_provider.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* @adonisjs/limiter
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { test } from '@japa/runner'
import { IgnitorFactory } from '@adonisjs/core/factories'
import type { RedisService } from '@adonisjs/redis/types'

import { createRedis } from './helpers.js'
import { LimiterManager, defineConfig, stores } from '../index.js'

const BASE_URL = new URL('./tmp/', import.meta.url)
const IMPORTER = (filePath: string) => {
if (filePath.startsWith('./') || filePath.startsWith('../')) {
return import(new URL(filePath, BASE_URL).href)
}
return import(filePath)
}

test.group('Limiter provider', () => {
test('register limiter provider', async ({ assert }) => {
const redis = createRedis() as unknown as RedisService

const ignitor = new IgnitorFactory()
.merge({
rcFileContents: {
providers: [() => import('../providers/limiter_provider.js')],
},
})
.withCoreConfig()
.withCoreProviders()
.merge({
config: {
limiter: defineConfig({
default: 'redis',
stores: {
redis: stores.redis({
connectionName: 'local',
}),
},
}),
},
})
.create(BASE_URL, {
importer: IMPORTER,
})

const app = ignitor.createApp('web')
await app.init()
app.container.singleton('redis', () => redis)
await app.boot()

assert.instanceOf(await app.container.make('limiter.manager'), LimiterManager)
})

test('throw error when config is invalid', async () => {
const redis = createRedis() as unknown as RedisService

const ignitor = new IgnitorFactory()
.merge({
rcFileContents: {
providers: [() => import('../providers/limiter_provider.js')],
},
})
.withCoreConfig()
.withCoreProviders()
.merge({
config: {
limiter: {},
},
})
.create(BASE_URL, {
importer: IMPORTER,
})

const app = ignitor.createApp('web')
await app.init()
app.container.singleton('redis', () => redis)
await app.boot()

await app.container.make('limiter.manager')
}).throws('Invalid "config/limiter.ts" file. Make sure you are using the "defineConfig" method')
})

0 comments on commit f1a41d5

Please sign in to comment.