Skip to content

Commit

Permalink
feat(dandi/http-model): add @RequestHeader decorator to allow injec…
Browse files Browse the repository at this point in the history
…ting individual request headers

closes #56
  • Loading branch information
DanielSchaffer committed Feb 5, 2020
1 parent d0f5b8e commit 3ed6434
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 3 deletions.
8 changes: 5 additions & 3 deletions _examples/simple-express-rest-api/src/view/view.controller.ts
@@ -1,8 +1,9 @@
import { Inject } from '@dandi/core'
import { HttpRequest, HttpRequestQueryParamMap, ParamMap } from '@dandi/http'
import { HttpRequest, HttpRequestQueryParamMap, ParamMap, HttpHeader } from '@dandi/http'
import { QueryParam } from '@dandi/http-model'
import { Controller, HttpGet } from '@dandi/mvc'
import { View, ViewResult, ViewResultFactory } from '@dandi/mvc-view'
import { RequestHeader } from '@dandi/http-model/src/request-header.decorator'

@Controller('view')
export class ViewController {
Expand Down Expand Up @@ -52,9 +53,10 @@ export class ViewController {
public cors(
@QueryParam(String) restApiHost: string,
@QueryParam(String) awsHost: string,
@RequestHeader(HttpHeader.host) host: string,
@Inject(HttpRequest) req: HttpRequest,
): { restApiHost: string, restApiPort: number, awsHost: string, search: string, appendSearch: string } {
const [, restApiPort] = req.get('host').split(':')
): { restApiHost: string, restApiPort: string, awsHost: string, search: string, appendSearch: string } {
const [, restApiPort] = host.split(':')
const search = [...Object.entries(req.query)].reduce((result, [key, value]) => {
if (result) {
result += '&'
Expand Down
56 changes: 56 additions & 0 deletions packages/dandi/http-model/src/request-header.decorator.spec.ts
@@ -0,0 +1,56 @@
import { OpinionatedToken } from '@dandi/core'
import { getInjectableParamMetadata, methodTarget, ParamMetadata } from '@dandi/core/internal/util'
import { testHarness } from '@dandi/core/testing'
import {
createHttpRequestScope,
HttpHeader,
HttpRequest,
HttpRequestHeadersAccessor,
HttpRequestHeadersHashAccessor,
MimeType,
requestHeaderToken,
} from '@dandi/http'

import { expect } from 'chai'

import { RequestHeader } from './request-header.decorator'

describe('@RequestHeader', () => {
class TestController {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public testHeader(@RequestHeader(HttpHeader.contentType) contentType: any): any {}
}

const harness = testHarness()

let meta: ParamMetadata<any>

beforeEach(() => {
meta = getInjectableParamMetadata(methodTarget(TestController), 'testHeader', 0)
})
afterEach(() => {
meta = undefined
})

it('sets a token for the specified header', async () => {
expect(meta).to.exist
expect(meta.token).to.exist
expect(meta.token).to.be.instanceOf(OpinionatedToken)
expect(meta.token).to.equal(requestHeaderToken(HttpHeader.contentType))
})

it('creates a provider to handle injecting the header value', async () => {

harness.register(...meta.providers, {
provide: HttpRequestHeadersAccessor,
useValue: HttpRequestHeadersHashAccessor.fromRaw({
[HttpHeader.contentType]: MimeType.applicationJson,
}),
})
const injector = harness.createChild(createHttpRequestScope({} as HttpRequest))

expect(await injector.inject(requestHeaderToken(HttpHeader.contentType))).to.deep.equal({ contentType: MimeType.applicationJson })

})

})
26 changes: 26 additions & 0 deletions packages/dandi/http-model/src/request-header.decorator.ts
@@ -0,0 +1,26 @@
import { MethodTarget } from '@dandi/common'
import { getInjectableParamMetadata, ParamMetadata } from '@dandi/core/internal/util'
import {
HttpRequestHeader, HttpRequestHeaders,
requestHeaderProvider,
requestHeaderToken,
} from '@dandi/http'

export interface RequestHeader<THeaderName extends HttpRequestHeader> extends ParamMetadata<HttpRequestHeaders[THeaderName]> {
headerName: THeaderName
}

export function requestHeaderDecorator<THeaderName extends HttpRequestHeader>(
header: RequestHeader<THeaderName>,
target: MethodTarget<HttpRequestHeaders[THeaderName]>,
propertyName: string,
paramIndex: number,
): void {
const meta = getInjectableParamMetadata<HttpRequestHeaders[THeaderName], RequestHeader<THeaderName>>(target, propertyName, paramIndex)
meta.token = requestHeaderToken<THeaderName>(header.headerName)
meta.providers = [requestHeaderProvider(header.headerName)]
}

export function RequestHeader<THeaderName extends HttpRequestHeader, TTarget>(headerName: THeaderName): ParameterDecorator {
return requestHeaderDecorator.bind(null, { headerName })
}

0 comments on commit 3ed6434

Please sign in to comment.