Skip to content
This repository has been archived by the owner on May 20, 2022. It is now read-only.

Commit

Permalink
feat: add a new package
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidJFelix committed Mar 6, 2019
1 parent 8b67277 commit 43869be
Show file tree
Hide file tree
Showing 17 changed files with 351 additions and 167 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# [2.2.0](https://github.com/DavidJFelix/serverless-compose/compare/v2.1.11...v2.2.0) (2019-03-06)


### Features

* add a new package ([9c1976a](https://github.com/DavidJFelix/serverless-compose/commit/9c1976a))





## [2.1.11](https://github.com/DavidJFelix/serverless-compose/compare/v2.1.10...v2.1.11) (2019-03-05)

**Note:** Version bump only for package root
Expand Down
3 changes: 1 addition & 2 deletions lerna.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"version": "2.1.11",
"version": "2.2.0",
"npmClient": "yarn",
"npmClientArgs": "--frozen-lockfile",
"useWorkspaces": true
}
11 changes: 11 additions & 0 deletions packages/serverless-compose-aws-api-gateway-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# [2.2.0](https://github.com/DavidJFelix/serverless-compose/compare/v2.1.11...v2.2.0) (2019-03-06)


### Features

* add a new package ([9c1976a](https://github.com/DavidJFelix/serverless-compose/commit/9c1976a))
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const base = require('../../jest.config.base.js')

module.exports = {
...base,
name: 'serverless-compose-aws-api-gateway-utils',
displayName: 'serverless-compose-aws-api-gateway-utils',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "serverless-compose-aws-api-gateway-utils",
"version": "2.2.0",
"main": "dist/index.js",
"license": "Apache-2.0",
"dependencies": {
"serverless-compose": "^2.2.0"
}
}
41 changes: 41 additions & 0 deletions packages/serverless-compose-aws-api-gateway-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {APIGatewayProxyEvent, APIGatewayProxyResult} from 'aws-lambda'
import {mappingMiddleware, Middleware, Context} from 'serverless-compose'

import {toHeaderCase, remapKeys} from './utils'

export const HeaderNormalizingMiddleware: Middleware<
APIGatewayProxyEvent,
APIGatewayProxyResult,
Context,
APIGatewayProxyEvent,
APIGatewayProxyResult
> = mappingMiddleware<
APIGatewayProxyEvent,
APIGatewayProxyResult,
APIGatewayProxyEvent,
APIGatewayProxyResult
>(
async (event, context) => {
return {
event: {
...event,
headers: remapKeys(event.headers, key => key.toLowerCase()),
multiValueHeaders: remapKeys(event.multiValueHeaders, key =>
key.toLowerCase(),
),
},
context,
}
},
async result => {
return {
...result,
headers: result.headers
? remapKeys(result.headers, key => toHeaderCase(key))
: {},
multiValueHeaders: result.multiValueHeaders
? remapKeys(result.multiValueHeaders, key => toHeaderCase(key))
: {},
}
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {remapKeys, toHeaderCase} from './utils'

describe('utils', () => {
test('toHeaderCase converts strings', () => {
// given
let allLower = 'abc'
let allUpper = 'ABC'
let lowerDash = 'ab-c'
let mixed = 'x-ConTeNT-Type'

// when
allLower = toHeaderCase(allLower)
allUpper = toHeaderCase(allUpper)
lowerDash = toHeaderCase(lowerDash)
mixed = toHeaderCase(mixed)

// then
expect(allLower).toEqual('Abc')
expect(allUpper).toEqual('Abc')
expect(lowerDash).toEqual('Ab-C')
expect(mixed).toEqual('X-Content-Type')
})

test('remapKeys changes key strings', () => {
// given
let single: {[name: string]: string} = {a: 'b'}
let singleNumber: {[name: string]: number} = {a: 1}
let multi: {[name: string]: string} = {a: 'b', b: 'c'}
let multiMixed: {[name: string]: string | number} = {a: 1, b: 'c'}

// when
single = remapKeys(single, key => key + '1')
singleNumber = remapKeys(singleNumber, key => key + '1')
multi = remapKeys(multi, key => key + '1')
multiMixed = remapKeys(multiMixed, key => key + '1')

// then
expect(single).toEqual({a1: 'b'})
expect(singleNumber).toEqual({a1: 1})
expect(multi).toEqual({a1: 'b', b1: 'c'})
expect(multiMixed).toEqual({a1: 1, b1: 'c'})
})
})
21 changes: 21 additions & 0 deletions packages/serverless-compose-aws-api-gateway-utils/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export function toHeaderCase(header: string): string {
return header
.split('-')
.map(
segment =>
segment.charAt(0).toUpperCase() + segment.substr(1).toLowerCase(),
)
.join('-')
}

export function remapKeys<T>(
obj: {[name: string]: T},
mapFn: (val: string) => string,
): {[name: string]: T} {
return {
...Object.keys(obj).reduce(
(acc, key) => ({...acc, [mapFn(key)]: obj[key]}),
{},
),
}
}
14 changes: 14 additions & 0 deletions packages/serverless-compose-aws-api-gateway-utils/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"module": "commonjs",
"outDir": "dist",
"target": "es2017",
"esModuleInterop": true,
"noImplicitAny": true,
"declaration": true,
"sourceMap": true,
"lib": ["es2017", "dom", "esnext"]
},
"include": ["src/*.ts"],
"exclude": ["node_modules"]
}
11 changes: 11 additions & 0 deletions packages/serverless-compose-request-identifier/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# [2.2.0](https://github.com/DavidJFelix/serverless-compose/compare/v2.1.11...v2.2.0) (2019-03-06)


### Features

* add a new package ([9c1976a](https://github.com/DavidJFelix/serverless-compose/commit/9c1976a))





## [2.1.11](https://github.com/DavidJFelix/serverless-compose/compare/v2.1.10...v2.1.11) (2019-03-05)

**Note:** Version bump only for package serverless-compose-request-identifier
Expand Down
4 changes: 2 additions & 2 deletions packages/serverless-compose-request-identifier/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "serverless-compose-request-identifier",
"version": "2.1.11",
"version": "2.2.0",
"author": "David J. Felix <felix.davidj@gmail.com>",
"homepage": "https://github.com/DavidJFelix/serverless-compose#readme",
"license": "Apache-2.0",
Expand All @@ -14,7 +14,7 @@
"url": "https://github.com/DavidJFelix/serverless-compose/issues"
},
"dependencies": {
"serverless-compose": "^2.1.11",
"serverless-compose": "^2.2.0",
"uuid": "^3.3.2"
},
"devDependencies": {
Expand Down
166 changes: 5 additions & 161 deletions packages/serverless-compose-request-identifier/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,161 +1,5 @@
import {Context, Middleware, Handler} from 'serverless-compose'
import {APIGatewayProxyEvent, APIGatewayProxyResult} from 'aws-lambda'
import uuidv4 from 'uuid/v4'

// A Mixin for Lambda Context.
// Using Typescript Intersection types you can define a handler context as `Context & RequestIdentifierContextMixin`
export type RequestIdentifierContextMixin = {
requestIdentity: RequestIdentifierContext
}

export type RequestIdentifierContext = {
correlationId: string
parentId?: string
requestId: string
sessionId: string
spanId: string
traceId: string
}

const byteToHex: string[] = []
for (var i = 0; i < 256; ++i) {
byteToHex[i] = (i + 0x100).toString(16).substr(1)
}

function bytesToUuid(bytes: Array<number>): string {
const hex = bytesToHexString(bytes)
return [
hex.slice(0, 8),
'-',
hex.slice(8, 12),
'-',
hex.slice(12, 16),
'-',
hex.slice(16, 24),
'-',
hex.slice(24, 36),
].join('')
}

function bytesToHexString(bytes: Array<number>): string {
return bytes.map(byte => byteToHex[byte]).join('')
}

function convertMaybeUuidToHexString(uuid?: string): string | void {
if (uuid === undefined || uuid === null || uuid === '') {
return
}

// FIXME: handle case where it's not a UUID
return uuid.replace('-', '')
}

function convertMaybeHexStringToUuid(hexString?: string): string | void {
if (hexString === undefined || hexString === null || hexString === '') {
return
}

// FIXME: handle case where its not a Hex string suitable for a UUID
return [
hexString.slice(0, 8),
'-',
hexString.slice(8, 12),
'-',
hexString.slice(12, 16),
'-',
hexString.slice(16, 24),
'-',
hexString.slice(24, 36),
].join('')
}

// I hate this name too. Sorry.
export const DefaultAPIGatewayProxyRequestIdentifyingMiddleware: Middleware<
APIGatewayProxyEvent,
APIGatewayProxyResult,
Context,
APIGatewayProxyEvent,
APIGatewayProxyResult,
Context & RequestIdentifierContextMixin
> = (
next: Handler<
APIGatewayProxyEvent,
APIGatewayProxyResult,
Context & RequestIdentifierContextMixin
>,
) => async (event: APIGatewayProxyEvent, context: Context & Object) => {
const trace = new Array<number>(16)
uuidv4(null, trace)

const span = new Array<number>(16)
uuidv4(null, span)

const correlationId: string | undefined =
event.headers['correlation-id'] || event.headers['x-correlation-id']

const parentId: string | undefined =
event.headers['parent-id'] ||
event.headers['x-parent-id'] ||
event.headers['x-b3-parentspanid']

const requestId: string | undefined =
event.headers['request-id'] || event.headers['x-request-id']

const sessionId: string | undefined =
event.headers['session-id'] || event.headers['x-session-id']

const spanId: string | undefined =
event.headers['span-id'] ||
event.headers['x-span-id'] ||
event.headers['x-b3-spanid']

const traceId: string | undefined =
event.headers['trace-id'] ||
event.headers['x-trace-id'] ||
event.headers['x-b3-traceid']

const requestIdentifierContext: RequestIdentifierContext = {
correlationId:
correlationId ||
convertMaybeHexStringToUuid(traceId) ||
bytesToUuid(trace),
...(parentId && {parentId}),
requestId:
requestId ||
correlationId ||
convertMaybeHexStringToUuid(traceId) ||
bytesToUuid(trace),
sessionId:
sessionId ||
correlationId ||
convertMaybeHexStringToUuid(traceId) ||
bytesToUuid(trace),
spanId: spanId || bytesToHexString(span.slice(0, 8)),
traceId:
traceId ||
convertMaybeUuidToHexString(correlationId) ||
bytesToHexString(trace),
}
const result = await next(event, {
...context,
requestIdentity: requestIdentifierContext,
})
const headers = {
'correlation-id': requestIdentifierContext.correlationId,
'x-correlation-id': requestIdentifierContext.correlationId,
'parent-id': requestIdentifierContext.parentId,
'x-parent-id': requestIdentifierContext.parentId,
'x-b3-parentspanid': requestIdentifierContext.parentId,
'request-id': requestIdentifierContext.requestId,
'x-request-id': requestIdentifierContext.requestId,
'session-id': requestIdentifierContext.sessionId,
'x-session-id': requestIdentifierContext.sessionId,
'span-id': requestIdentifierContext.spanId,
'x-span-id': requestIdentifierContext.spanId,
'x-b3-spanid': requestIdentifierContext.spanId,
'trace-id': requestIdentifierContext.traceId,
'x-trace-id': requestIdentifierContext.traceId,
'x-b3-traceid': requestIdentifierContext.traceId,
}
return {...result, headers: {...(result.headers || {}), ...headers}}
}
export {
DefaultAPIGatewayProxyRequestIdentifyingMiddleware,
RequestIdentifierContext,
RequestIdentifierContextMixin,
} from './middleware'
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
DefaultAPIGatewayProxyRequestIdentifyingMiddleware,
RequestIdentifierContextMixin,
} from '.'
} from './middleware'
import {Context, Handler} from 'serverless-compose'
import {
APIGatewayProxyEvent,
Expand Down
Loading

0 comments on commit 43869be

Please sign in to comment.