diff --git a/infrastructure/terraform/components/api/README.md b/infrastructure/terraform/components/api/README.md
index 2f28d7fbd..0b97fdbbd 100644
--- a/infrastructure/terraform/components/api/README.md
+++ b/infrastructure/terraform/components/api/README.md
@@ -37,6 +37,7 @@ No requirements.
| [get\_letter](#module\_get\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a |
| [get\_letter\_data](#module\_get\_letter\_data) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a |
| [get\_letters](#module\_get\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a |
+| [get\_status](#module\_get\_status) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a |
| [kms](#module\_kms) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-kms.zip | n/a |
| [letter\_status\_update](#module\_letter\_status\_update) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a |
| [letter\_status\_updates\_queue](#module\_letter\_status\_updates\_queue) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-sqs.zip | n/a |
diff --git a/infrastructure/terraform/components/api/iam_role_api_gateway_execution_role.tf b/infrastructure/terraform/components/api/iam_role_api_gateway_execution_role.tf
index d6ff0cb43..8acebc8b8 100644
--- a/infrastructure/terraform/components/api/iam_role_api_gateway_execution_role.tf
+++ b/infrastructure/terraform/components/api/iam_role_api_gateway_execution_role.tf
@@ -54,6 +54,7 @@ data "aws_iam_policy_document" "api_gateway_execution_policy" {
module.get_letters.function_arn,
module.patch_letter.function_arn,
module.post_letters.function_arn,
+ module.get_status.function_arn,
module.post_mi.function_arn
]
}
diff --git a/infrastructure/terraform/components/api/locals.tf b/infrastructure/terraform/components/api/locals.tf
index a6621a3d8..fec749a36 100644
--- a/infrastructure/terraform/components/api/locals.tf
+++ b/infrastructure/terraform/components/api/locals.tf
@@ -11,6 +11,7 @@ locals {
GET_LETTER_LAMBDA_ARN = module.get_letter.function_arn
GET_LETTERS_LAMBDA_ARN = module.get_letters.function_arn
GET_LETTER_DATA_LAMBDA_ARN = module.get_letter_data.function_arn
+ GET_STATUS_LAMBDA_ARN = module.get_status.function_arn
PATCH_LETTER_LAMBDA_ARN = module.patch_letter.function_arn
POST_LETTERS_LAMBDA_ARN = module.post_letters.function_arn
POST_MI_LAMBDA_ARN = module.post_mi.function_arn
diff --git a/infrastructure/terraform/components/api/module_lambda_get_status.tf b/infrastructure/terraform/components/api/module_lambda_get_status.tf
new file mode 100644
index 000000000..37e33d5f8
--- /dev/null
+++ b/infrastructure/terraform/components/api/module_lambda_get_status.tf
@@ -0,0 +1,76 @@
+module "get_status" {
+ source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip"
+
+ function_name = "get_status"
+ description = "Healthcheck for service"
+
+ aws_account_id = var.aws_account_id
+ component = var.component
+ environment = var.environment
+ project = var.project
+ region = var.region
+ group = var.group
+
+ log_retention_in_days = var.log_retention_in_days
+ kms_key_arn = module.kms.key_arn
+
+ iam_policy_document = {
+ body = data.aws_iam_policy_document.get_status_lambda.json
+ }
+
+ function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"]
+ function_code_base_path = local.aws_lambda_functions_dir_path
+ function_code_dir = "api-handler/dist"
+ function_include_common = true
+ handler_function_name = "getStatus"
+ runtime = "nodejs22.x"
+ memory = 128
+ timeout = 5
+ log_level = var.log_level
+
+ force_lambda_code_deploy = var.force_lambda_code_deploy
+ enable_lambda_insights = false
+
+ send_to_firehose = true
+ log_destination_arn = local.destination_arn
+ log_subscription_role_arn = local.acct.log_subscription_role_arn
+
+ lambda_env_vars = merge(local.common_lambda_env_vars, {})
+}
+
+data "aws_iam_policy_document" "get_status_lambda" {
+ statement {
+ sid = "KMSPermissions"
+ effect = "Allow"
+
+ actions = [
+ "kms:Decrypt",
+ "kms:GenerateDataKey",
+ ]
+
+ resources = [
+ module.kms.key_arn, ## Requires shared kms module
+ ]
+ }
+
+ statement {
+ sid = "AllowDynamoDBAccess"
+ effect = "Allow"
+
+ actions = [
+ "dynamodb:DescribeTable"
+ ]
+
+ resources = [
+ aws_dynamodb_table.letters.arn,
+ "${aws_dynamodb_table.letters.arn}/index/supplierStatus-index"
+ ]
+ }
+
+
+ statement {
+ sid = "S3ListAllMyBuckets"
+ actions = ["s3:ListAllMyBuckets"]
+ resources = ["arn:aws:s3:::*"]
+ }
+}
diff --git a/infrastructure/terraform/components/api/resources/spec.tmpl.json b/infrastructure/terraform/components/api/resources/spec.tmpl.json
index 76ed2dd1f..5d3337807 100644
--- a/infrastructure/terraform/components/api/resources/spec.tmpl.json
+++ b/infrastructure/terraform/components/api/resources/spec.tmpl.json
@@ -23,6 +23,34 @@
},
"openapi": "3.0.1",
"paths": {
+ "/_status": {
+ "get": {
+ "operationId": "getStatusId",
+ "responses": {
+ "200": {
+ "description": "OK"
+ },
+ "500": {
+ "description": "Server error"
+ }
+ },
+ "summary": "Healthcheck endpoint",
+ "x-amazon-apigateway-integration": {
+ "contentHandling": "CONVERT_TO_TEXT",
+ "credentials": "${APIG_EXECUTION_ROLE_ARN}",
+ "httpMethod": "POST",
+ "passthroughBehavior": "WHEN_NO_TEMPLATES",
+ "responses": {
+ ".*": {
+ "statusCode": "200"
+ }
+ },
+ "timeoutInMillis": 29000,
+ "type": "AWS_PROXY",
+ "uri": "arn:aws:apigateway:${AWS_REGION}:lambda:path/2015-03-31/functions/${GET_STATUS_LAMBDA_ARN}/invocations"
+ }
+ }
+ },
"/letters": {
"get": {
"description": "Returns 200 OK with paginated letter ids.",
diff --git a/internal/datastore/src/__test__/heathcheck.test.ts b/internal/datastore/src/__test__/heathcheck.test.ts
new file mode 100644
index 000000000..ade1b1b18
--- /dev/null
+++ b/internal/datastore/src/__test__/heathcheck.test.ts
@@ -0,0 +1,38 @@
+import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
+import { DBHealthcheck } from "../healthcheck";
+import { createTables, DBContext, deleteTables, setupDynamoDBContainer } from "./db";
+
+// Database tests can take longer, especially with setup and teardown
+jest.setTimeout(30000);
+
+describe('DBHealthcheck', () => {
+
+ let db: DBContext;
+
+ beforeAll(async () => {
+ db = await setupDynamoDBContainer();
+ });
+
+ beforeEach(async () => {
+ await createTables(db);
+ });
+
+ afterEach(async () => {
+ await deleteTables(db);
+ });
+
+ it('passes when the database is available', async () => {
+ const dbHealthCheck = new DBHealthcheck(db.docClient, db.config);
+ await dbHealthCheck.check();
+ });
+
+ it('fails when the database is unavailable', async () => {
+ const realFunction = db.docClient.send;
+ db.docClient.send = jest.fn().mockImplementation(() => { throw new Error('Failed to send')});
+
+ const dbHealthCheck = new DBHealthcheck(db.docClient, db.config);
+ await expect(dbHealthCheck.check()).rejects.toThrow();
+
+ db.docClient.send = realFunction;
+ });
+});
diff --git a/internal/datastore/src/healthcheck.ts b/internal/datastore/src/healthcheck.ts
new file mode 100644
index 000000000..39c91c619
--- /dev/null
+++ b/internal/datastore/src/healthcheck.ts
@@ -0,0 +1,13 @@
+import { DescribeTableCommand } from "@aws-sdk/client-dynamodb";
+import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
+import { LetterRepositoryConfig } from "./letter-repository";
+
+export class DBHealthcheck {
+ constructor(readonly ddbClient: DynamoDBDocumentClient,
+ readonly config: LetterRepositoryConfig) {}
+
+ async check(): Promise {
+ await this.ddbClient.send(new DescribeTableCommand({
+ TableName: this.config.lettersTableName}));
+ }
+}
diff --git a/internal/datastore/src/index.ts b/internal/datastore/src/index.ts
index 31f2b7547..5073399b2 100644
--- a/internal/datastore/src/index.ts
+++ b/internal/datastore/src/index.ts
@@ -2,4 +2,5 @@ export * from './types';
export * from './mi-repository';
export * from './letter-repository';
export * from './supplier-repository';
+export * from './healthcheck';
export * from './types';
diff --git a/lambdas/api-handler/src/config/__tests__/deps.test.ts b/lambdas/api-handler/src/config/__tests__/deps.test.ts
index e56fe2c54..ceb2e294b 100644
--- a/lambdas/api-handler/src/config/__tests__/deps.test.ts
+++ b/lambdas/api-handler/src/config/__tests__/deps.test.ts
@@ -1,4 +1,3 @@
-
import type { Deps } from '../deps';
describe('createDependenciesContainer', () => {
@@ -40,6 +39,7 @@ describe('createDependenciesContainer', () => {
jest.mock('@internal/datastore', () => ({
LetterRepository: jest.fn(),
MIRepository: jest.fn(),
+ DBHealthcheck: jest.fn()
}));
// Env
diff --git a/lambdas/api-handler/src/config/deps.ts b/lambdas/api-handler/src/config/deps.ts
index 58739aba8..d0528492a 100644
--- a/lambdas/api-handler/src/config/deps.ts
+++ b/lambdas/api-handler/src/config/deps.ts
@@ -3,7 +3,7 @@ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
import { SQSClient } from "@aws-sdk/client-sqs";
import pino from 'pino';
-import { LetterRepository, MIRepository } from '../../../../internal/datastore';
+import { LetterRepository, MIRepository, DBHealthcheck } from '@internal/datastore';
import { envVars, EnvVars } from "../config/env";
export type Deps = {
@@ -11,6 +11,7 @@ export type Deps = {
sqsClient: SQSClient;
letterRepo: LetterRepository;
miRepo: MIRepository;
+ dbHealthcheck: DBHealthcheck;
logger: pino.Logger;
env: EnvVars
};
@@ -20,6 +21,7 @@ function createDocumentClient(): DynamoDBDocumentClient {
return DynamoDBDocumentClient.from(ddbClient);
}
+
function createLetterRepository(log: pino.Logger, envVars: EnvVars): LetterRepository {
const config = {
@@ -30,6 +32,15 @@ function createLetterRepository(log: pino.Logger, envVars: EnvVars): LetterRepos
return new LetterRepository(createDocumentClient(), log, config);
}
+function createDBHealthcheck(envVars: EnvVars): DBHealthcheck {
+ const config = {
+ lettersTableName: envVars.LETTERS_TABLE_NAME,
+ lettersTtlHours: envVars.LETTER_TTL_HOURS
+ };
+
+ return new DBHealthcheck(createDocumentClient(), config);
+}
+
function createMIRepository(log: pino.Logger, envVars: EnvVars): MIRepository {
const config = {
@@ -49,6 +60,7 @@ export function createDependenciesContainer(): Deps {
sqsClient: new SQSClient(),
letterRepo: createLetterRepository(log, envVars),
miRepo: createMIRepository(log, envVars),
+ dbHealthcheck: createDBHealthcheck(envVars),
logger: log,
env: envVars
};
diff --git a/lambdas/api-handler/src/handlers/__tests__/get_status.test.ts b/lambdas/api-handler/src/handlers/__tests__/get_status.test.ts
new file mode 100644
index 000000000..8fae2419e
--- /dev/null
+++ b/lambdas/api-handler/src/handlers/__tests__/get_status.test.ts
@@ -0,0 +1,73 @@
+import { S3Client } from "@aws-sdk/client-s3";
+import { DBHealthcheck } from "@internal/datastore/src";
+import pino from "pino";
+import { Deps } from "../../config/deps";
+import { makeApiGwEvent } from "./utils/test-utils";
+import { mockDeep } from "jest-mock-extended";
+import { Context } from "aws-lambda";
+import { createGetStatusHandler } from "../get-status";
+
+describe('API Lambda handler', () => {
+ it('passes if S3 and DynamoDB are available', async() => {
+
+ const event = makeApiGwEvent({path: '/_status',
+ headers: undefined
+ });
+
+ const getLetterDataHandler = createGetStatusHandler(getMockedDeps());
+ const result = await getLetterDataHandler(event, mockDeep(), jest.fn());
+
+ expect(result).toEqual({
+ statusCode: 200,
+ body: JSON.stringify({ code: 200 }, null, 2)
+ });
+ });
+
+ it('fails if S3 is unavailable', async() => {
+ const mockedDeps = getMockedDeps();
+ mockedDeps.s3Client.send = jest.fn().mockRejectedValue(new Error('unexpected error'));
+
+ const event = makeApiGwEvent({path: '/_status',
+ headers: undefined
+ });
+
+ const getLetterDataHandler = createGetStatusHandler(mockedDeps);
+ const result = await getLetterDataHandler(event, mockDeep(), jest.fn());
+
+ expect(result).toEqual({
+ statusCode: 500,
+ body: JSON.stringify({ code: 500 }, null, 2)
+ });
+ });
+
+
+ it('fails if DynamoDB is unavailable', async() => {
+ const mockedDeps = getMockedDeps();
+ mockedDeps.dbHealthcheck.check = jest.fn().mockRejectedValue(new Error('unexpected error'));
+
+ const event = makeApiGwEvent({path: '/_status',
+ headers: {'Nhsd-Correlation-Id': 'correlationId'}
+ });
+
+ const getLetterDataHandler = createGetStatusHandler(mockedDeps);
+ const result = await getLetterDataHandler(event, mockDeep(), jest.fn());
+
+ expect(result).toEqual({
+ statusCode: 500,
+ body: JSON.stringify({ code: 500 }, null, 2)
+ });
+ });
+
+
+ function getMockedDeps(): jest.Mocked {
+ return {
+ s3Client: { send: jest.fn()} as unknown as S3Client,
+ dbHealthcheck: {check: jest.fn()} as unknown as DBHealthcheck,
+ logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger,
+ env: {
+ SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
+ APIM_CORRELATION_HEADER: 'nhsd-correlation-id'
+ }
+ } as Deps;
+ }
+});
diff --git a/lambdas/api-handler/src/handlers/__tests__/patch-letter.test.ts b/lambdas/api-handler/src/handlers/__tests__/patch-letter.test.ts
index 968758306..232e5adb8 100644
--- a/lambdas/api-handler/src/handlers/__tests__/patch-letter.test.ts
+++ b/lambdas/api-handler/src/handlers/__tests__/patch-letter.test.ts
@@ -47,17 +47,17 @@ describe('patchLetter API Handler', () => {
});
const mockedDeps: jest.Mocked = {
- s3Client: {} as unknown as S3Client,
- letterRepo: {} as unknown as LetterRepository,
- logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger,
- env: {
- SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
- APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
- LETTERS_TABLE_NAME: 'LETTERS_TABLE_NAME',
- LETTER_TTL_HOURS: 12960,
- DOWNLOAD_URL_TTL_SECONDS: 60
- } as unknown as EnvVars
- } as Deps;
+ s3Client: {} as unknown as S3Client,
+ letterRepo: {} as unknown as LetterRepository,
+ logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger,
+ env: {
+ SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
+ APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
+ LETTERS_TABLE_NAME: 'LETTERS_TABLE_NAME',
+ LETTER_TTL_HOURS: 12960,
+ DOWNLOAD_URL_TTL_SECONDS: 60
+ } as unknown as EnvVars
+ } as Deps;
it('returns 202 Accepted', async () => {
const event = makeApiGwEvent({
diff --git a/lambdas/api-handler/src/handlers/__tests__/post-mi.test.ts b/lambdas/api-handler/src/handlers/__tests__/post-mi.test.ts
index d7d942e14..10897fb76 100644
--- a/lambdas/api-handler/src/handlers/__tests__/post-mi.test.ts
+++ b/lambdas/api-handler/src/handlers/__tests__/post-mi.test.ts
@@ -4,7 +4,7 @@ import { makeApiGwEvent } from "./utils/test-utils";
import { PostMIRequest, PostMIResponse } from "../../contracts/mi";
import * as miService from '../../services/mi-operations';
import pino from 'pino';
-import { MIRepository } from "../../../../../internal/datastore/src";
+import { MIRepository } from "@internal/datastore/src";
import { Deps } from "../../config/deps";
import { EnvVars } from "../../config/env";
import { createPostMIHandler } from "../post-mi";
diff --git a/lambdas/api-handler/src/handlers/get-status.ts b/lambdas/api-handler/src/handlers/get-status.ts
new file mode 100644
index 000000000..81bbf9038
--- /dev/null
+++ b/lambdas/api-handler/src/handlers/get-status.ts
@@ -0,0 +1,38 @@
+import { APIGatewayProxyHandler } from "aws-lambda";
+import { Deps } from "../config/deps";
+import { ListBucketsCommand, S3Client } from "@aws-sdk/client-s3";
+import { mapErrorToResponse } from "../mappers/error-mapper";
+
+export function createGetStatusHandler(deps: Deps): APIGatewayProxyHandler {
+
+ return async(_) => {
+
+ try {
+ await deps.dbHealthcheck.check();
+ await s3HealthCheck(deps.s3Client);
+
+ deps.logger.info({
+ description: 'Healthcheck passed'
+ });
+
+ return {
+ statusCode: 200,
+ body: JSON.stringify({ code: 200 }, null, 2)
+ };
+ } catch (error) {
+ deps.logger.error({ err: error }, 'Status endpoint error, services not available');
+ return {
+ statusCode: 500,
+ body: JSON.stringify({ code: 500 }, null, 2)
+ };
+ }
+ }
+}
+
+
+async function s3HealthCheck(s3Client: S3Client) {
+ const command: ListBucketsCommand = new ListBucketsCommand({
+ MaxBuckets: 1
+ });
+ await s3Client.send(command);
+}
diff --git a/lambdas/api-handler/src/index.ts b/lambdas/api-handler/src/index.ts
index 010d44b1b..d657f746b 100644
--- a/lambdas/api-handler/src/index.ts
+++ b/lambdas/api-handler/src/index.ts
@@ -6,6 +6,7 @@ import { createPatchLetterHandler } from "./handlers/patch-letter";
import { createPostLettersHandler } from "./handlers/post-letters";
import { createLetterStatusUpdateHandler } from "./handlers/letter-status-update";
import { createPostMIHandler } from "./handlers/post-mi";
+import { createGetStatusHandler } from "./handlers/get-status";
const container = createDependenciesContainer();
@@ -17,3 +18,4 @@ export const letterStatusUpdate = createLetterStatusUpdateHandler(container);
export const postLetters = createPostLettersHandler(container);
export const postMI = createPostMIHandler(container);
+export const getStatus = createGetStatusHandler(container);
diff --git a/lambdas/api-handler/src/mappers/__tests__/error-mapper.test.ts b/lambdas/api-handler/src/mappers/__tests__/error-mapper.test.ts
index 8a2d2c772..f3c8491dd 100644
--- a/lambdas/api-handler/src/mappers/__tests__/error-mapper.test.ts
+++ b/lambdas/api-handler/src/mappers/__tests__/error-mapper.test.ts
@@ -49,7 +49,7 @@ describe("processError", () => {
});
it("should map generic Error to InternalServerError response", () => {
- const err = new Error("Something broke");
+ const err = new Error("Low level error message");
const res = processError(err, 'correlationId', { info: jest.fn(), error: jest.fn() } as unknown as Logger);
@@ -58,7 +58,7 @@ describe("processError", () => {
"errors": [
{
"code": "NOTIFY_INTERNAL_SERVER_ERROR",
- "detail": "Something broke",
+ "detail": "Unexpected error",
"id": "correlationId",
"links": {
"about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier"
diff --git a/lambdas/api-handler/src/mappers/__tests__/mi-mapper.test.ts b/lambdas/api-handler/src/mappers/__tests__/mi-mapper.test.ts
index be1a55c15..e36555fe5 100644
--- a/lambdas/api-handler/src/mappers/__tests__/mi-mapper.test.ts
+++ b/lambdas/api-handler/src/mappers/__tests__/mi-mapper.test.ts
@@ -1,4 +1,4 @@
-import { MIBase } from "../../../../../internal/datastore/src";
+import { MIBase } from "@internal/datastore/src";
import { IncomingMI, PostMIRequest } from "../../contracts/mi";
import { mapToMI, mapToPostMIResponse } from "../mi-mapper";
diff --git a/lambdas/api-handler/src/mappers/error-mapper.ts b/lambdas/api-handler/src/mappers/error-mapper.ts
index 180e0c8f9..d2fc98661 100644
--- a/lambdas/api-handler/src/mappers/error-mapper.ts
+++ b/lambdas/api-handler/src/mappers/error-mapper.ts
@@ -18,7 +18,7 @@ export function logAndMapToApiError(error: unknown, correlationId: string | unde
return mapToApiError(ApiErrorCode.NotFound, error.detail, correlationId);
} else if (error instanceof Error) {
logger.error({ err: error }, `Internal server error correlationId=${correlationId}`);
- return mapToApiError(ApiErrorCode.InternalServerError, error.message, correlationId);
+ return mapToApiError(ApiErrorCode.InternalServerError, "Unexpected error", correlationId);
} else {
logger.error({ err: error }, `Internal server error (non-Error thrown) correlationId=${correlationId}`);
return mapToApiError(ApiErrorCode.InternalServerError, "Unexpected error", correlationId);
diff --git a/lambdas/api-handler/src/mappers/mi-mapper.ts b/lambdas/api-handler/src/mappers/mi-mapper.ts
index 83848e1a2..1dc980d5a 100644
--- a/lambdas/api-handler/src/mappers/mi-mapper.ts
+++ b/lambdas/api-handler/src/mappers/mi-mapper.ts
@@ -1,4 +1,4 @@
-import { MIBase } from "../../../../internal/datastore/src";
+import { MIBase } from "@internal/datastore/src";
import { IncomingMI, PostMIRequest as PostMIRequest, PostMIResponse, PostMIResponseSchema } from "../contracts/mi";
export function mapToMI(request: PostMIRequest, supplierId: string): IncomingMI {
diff --git a/lambdas/api-handler/src/services/mi-operations.ts b/lambdas/api-handler/src/services/mi-operations.ts
index 2c574b676..d1cc207c1 100644
--- a/lambdas/api-handler/src/services/mi-operations.ts
+++ b/lambdas/api-handler/src/services/mi-operations.ts
@@ -1,4 +1,4 @@
-import { MIRepository } from "../../../../internal/datastore/src/mi-repository";
+import { MIRepository } from "@internal/datastore/src/mi-repository";
import { IncomingMI, PostMIResponse } from "../contracts/mi";
import { mapToPostMIResponse } from "../mappers/mi-mapper";