diff --git a/.husky/pre-commit b/.husky/pre-commit index 40abead..0a90fbd 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,7 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" +npx tsc npm run build npm run test npm run docs:madge diff --git a/diagrams/code-diagram.svg b/diagrams/code-diagram.svg index e81f74a..8ee2bb5 100644 --- a/diagrams/code-diagram.svg +++ b/diagrams/code-diagram.svg @@ -4,376 +4,388 @@ - - + + G - - + + +application/createQueryStringParamsObjectFromString.ts + +application/createQueryStringParamsObjectFromString.ts + + + application/getExpiryTime.ts - -application/getExpiryTime.ts + +application/getExpiryTime.ts - + application/getParser.ts - -application/getParser.ts + +application/getParser.ts - + domain/services/GitHubParser.ts - -domain/services/GitHubParser.ts + +domain/services/GitHubParser.ts application/getParser.ts->domain/services/GitHubParser.ts - - + + - + domain/services/Result.ts - -domain/services/Result.ts + +domain/services/Result.ts application/getParser.ts->domain/services/Result.ts - - + + - + application/getRequestDTO.ts - -application/getRequestDTO.ts + +application/getRequestDTO.ts - + application/mapResultToUsecase.ts - -application/mapResultToUsecase.ts + +application/mapResultToUsecase.ts - + usecases/approved.ts - -usecases/approved.ts + +usecases/approved.ts application/mapResultToUsecase.ts->usecases/approved.ts - - + + - + usecases/changesRequested.ts - -usecases/changesRequested.ts + +usecases/changesRequested.ts application/mapResultToUsecase.ts->usecases/changesRequested.ts - - + + - + usecases/closed.ts - -usecases/closed.ts + +usecases/closed.ts application/mapResultToUsecase.ts->usecases/closed.ts - - + + - + usecases/commented.ts - -usecases/commented.ts + +usecases/commented.ts application/mapResultToUsecase.ts->usecases/commented.ts - - + + - + usecases/merged.ts - -usecases/merged.ts + +usecases/merged.ts application/mapResultToUsecase.ts->usecases/merged.ts - - + + - + usecases/opened.ts - -usecases/opened.ts + +usecases/opened.ts application/mapResultToUsecase.ts->usecases/opened.ts - - + + - + usecases/pushed.ts - -usecases/pushed.ts + +usecases/pushed.ts application/mapResultToUsecase.ts->usecases/pushed.ts - - + + - + usecases/reviewPickedUp.ts - -usecases/reviewPickedUp.ts + +usecases/reviewPickedUp.ts application/mapResultToUsecase.ts->usecases/reviewPickedUp.ts - - + + - + usecases/reviewTime.ts - -usecases/reviewTime.ts + +usecases/reviewTime.ts application/mapResultToUsecase.ts->usecases/reviewTime.ts - - + + - + usecases/reviewed.ts - -usecases/reviewed.ts + +usecases/reviewed.ts application/mapResultToUsecase.ts->usecases/reviewed.ts - - + + - + domain/services/Metrics.ts - -domain/services/Metrics.ts + +domain/services/Metrics.ts - + infrastructure/adapters/web/AddMetrics.ts - -infrastructure/adapters/web/AddMetrics.ts + +infrastructure/adapters/web/AddMetrics.ts - + infrastructure/frameworks/end.ts - -infrastructure/frameworks/end.ts + +infrastructure/frameworks/end.ts infrastructure/adapters/web/AddMetrics.ts->infrastructure/frameworks/end.ts - - + + - + usecases/addMetric.ts - -usecases/addMetric.ts + +usecases/addMetric.ts infrastructure/adapters/web/AddMetrics.ts->usecases/addMetric.ts - - + + - + usecases/addMetric.ts->application/getParser.ts - - + + - + usecases/addMetric.ts->application/mapResultToUsecase.ts - - + + - + infrastructure/frameworks/getRepo.ts - -infrastructure/frameworks/getRepo.ts + +infrastructure/frameworks/getRepo.ts - + usecases/addMetric.ts->infrastructure/frameworks/getRepo.ts - - + + - + infrastructure/frameworks/getLowerCaseHeaders.ts - -infrastructure/frameworks/getLowerCaseHeaders.ts + +infrastructure/frameworks/getLowerCaseHeaders.ts - + usecases/addMetric.ts->infrastructure/frameworks/getLowerCaseHeaders.ts - - + + - + infrastructure/adapters/web/GetMetrics.ts - -infrastructure/adapters/web/GetMetrics.ts + +infrastructure/adapters/web/GetMetrics.ts - + +infrastructure/adapters/web/GetMetrics.ts->application/createQueryStringParamsObjectFromString.ts + + + + + infrastructure/adapters/web/GetMetrics.ts->application/getRequestDTO.ts - - + + - + infrastructure/adapters/web/GetMetrics.ts->infrastructure/frameworks/end.ts - - + + - + infrastructure/adapters/web/GetMetrics.ts->infrastructure/frameworks/getRepo.ts - - + + - + usecases/getMetrics.ts - -usecases/getMetrics.ts + +usecases/getMetrics.ts - + infrastructure/adapters/web/GetMetrics.ts->usecases/getMetrics.ts - - + + - + infrastructure/repositories/DynamoDbRepository.ts - -infrastructure/repositories/DynamoDbRepository.ts + +infrastructure/repositories/DynamoDbRepository.ts - + infrastructure/frameworks/getRepo.ts->infrastructure/repositories/DynamoDbRepository.ts - - + + - + infrastructure/repositories/LocalRepository.ts - -infrastructure/repositories/LocalRepository.ts + +infrastructure/repositories/LocalRepository.ts - + infrastructure/frameworks/getRepo.ts->infrastructure/repositories/LocalRepository.ts - - + + - + usecases/getMetrics.ts->domain/services/Metrics.ts - - + + - + infrastructure/authorizers/Authorizer.ts - -infrastructure/authorizers/Authorizer.ts + +infrastructure/authorizers/Authorizer.ts - + usecases/authorize.ts - -usecases/authorize.ts + +usecases/authorize.ts - + infrastructure/authorizers/Authorizer.ts->usecases/authorize.ts - - + + - + infrastructure/frameworks/authorization/handleCors.ts - -infrastructure/frameworks/authorization/handleCors.ts + +infrastructure/frameworks/authorization/handleCors.ts - + usecases/authorize.ts->infrastructure/frameworks/authorization/handleCors.ts - - + + - + infrastructure/frameworks/addCustomMetric.ts - -infrastructure/frameworks/addCustomMetric.ts + +infrastructure/frameworks/addCustomMetric.ts - + infrastructure/frameworks/getCleanedItems.ts - -infrastructure/frameworks/getCleanedItems.ts + +infrastructure/frameworks/getCleanedItems.ts - + infrastructure/repositories/DynamoDbRepository.ts->application/getExpiryTime.ts - - + + - + infrastructure/repositories/DynamoDbRepository.ts->infrastructure/frameworks/addCustomMetric.ts - - + + - + infrastructure/repositories/DynamoDbRepository.ts->infrastructure/frameworks/getCleanedItems.ts - - + + diff --git a/package-lock.json b/package-lock.json index 6c91e1f..dd6c4ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gitmetrix", - "version": "2.1.0", + "version": "2.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "gitmetrix", - "version": "2.1.0", + "version": "2.1.1", "license": "MIT", "dependencies": { "@aws-sdk/client-dynamodb": "3", diff --git a/package.json b/package.json index ae2c684..02acb42 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "gitmetrix", "description": "Helps you find your team-level engineering metrics", - "version": "2.1.0", + "version": "2.1.1", "author": "Mikael Vesavuori", "license": "MIT", "keywords": [ @@ -24,7 +24,7 @@ "scripts": { "start": "npx sls offline --reloadHandler", "test": "npm run test:licenses && npm run test:types && npm run test:unit", - "test:types": "npx type-coverage --at-least 98 --strict --ignore-files \"tests/**/*.ts\" --ignore-files \"*.ts\" --ignore-files \"src/application/errors/*.ts\" --ignore-files \"testdata/*.ts\"", + "test:types": "npx type-coverage --at-least 97.5 --strict --ignore-files \"tests/**/*.ts\" --ignore-files \"*.ts\" --ignore-files \"src/application/errors/*.ts\" --ignore-files \"testdata/*.ts\"", "test:licenses": "npx license-compliance --direct --allow 'MIT;ISC;0BSD;BSD-2-Clause;BSD-3-Clause;Apache-2.0;Unlicense;CC0-1.0'", "test:unit": "npx c8 -reporter=lcov ava", "test:createdata": "npx ts-node tests/createTestData.ts", diff --git a/src/application/createQueryStringParamsObjectFromString.ts b/src/application/createQueryStringParamsObjectFromString.ts new file mode 100644 index 0000000..454c9ef --- /dev/null +++ b/src/application/createQueryStringParamsObjectFromString.ts @@ -0,0 +1,17 @@ +/** + * @description Create a conventional object of query string parameters + * from an HTTP API's `event` object that only has query string parameters + * in the shape of a "raw" string. + */ +export function createQueryStringParamsObjectFromString(event: Record) { + const queryStringParameters: Record = {}; + + const parts = event?.rawQueryString?.split('&'); + if (parts && parts.length > 0) + parts.forEach((part: string) => { + const [key, value] = part.split('='); + queryStringParameters[key] = value.replace('%2F', '/'); + }); + + return queryStringParameters; +} diff --git a/src/infrastructure/adapters/web/GetMetrics.ts b/src/infrastructure/adapters/web/GetMetrics.ts index 69efe67..303f107 100644 --- a/src/infrastructure/adapters/web/GetMetrics.ts +++ b/src/infrastructure/adapters/web/GetMetrics.ts @@ -5,7 +5,9 @@ import { EventInput } from '../../../interfaces/Lambda'; import { getMetrics } from '../../../usecases/getMetrics'; +import { createQueryStringParamsObjectFromString } from '../../../application/createQueryStringParamsObjectFromString'; import { getRequestDTO } from '../../../application/getRequestDTO'; + import { getRepo } from '../../frameworks/getRepo'; import { end } from '../../frameworks/end'; @@ -24,7 +26,8 @@ export async function handler(event: EventInput, context: Record) { }); try { - const input = getRequestDTO(event.queryStringParameters || {}); + const queryStringParameters = createQueryStringParamsObjectFromString(event); + const input = getRequestDTO(queryStringParameters); const repository = getRepo(process.env.NODE_ENV === 'test'); const metrics = await getMetrics(repository, input); return end(200, metrics); diff --git a/tests/unit/application/createQueryStringParamsObjectFromString.test.ts b/tests/unit/application/createQueryStringParamsObjectFromString.test.ts new file mode 100644 index 0000000..ebfdf41 --- /dev/null +++ b/tests/unit/application/createQueryStringParamsObjectFromString.test.ts @@ -0,0 +1,25 @@ +import test from 'ava'; + +import { createQueryStringParamsObjectFromString } from '../../../src/application/createQueryStringParamsObjectFromString'; + +/** + * POSITIVE TESTS + */ + +test.serial('It should return a clean object from a query parameter string', (t) => { + const expected = { repo: 'SOMEORG/SOMEREPO', from: '20230220', to: '20230225' }; + + const result = createQueryStringParamsObjectFromString({ + rawQueryString: 'repo=SOMEORG/SOMEREPO&from=20230220&to=20230225' + }); + + t.deepEqual(result, expected); +}); + +test.serial('It should return an empty object for an unknown input', (t) => { + const expected = {}; + + const result = createQueryStringParamsObjectFromString({}); + + t.deepEqual(result, expected); +});