Skip to content

Commit

Permalink
Adds GetMessages
Browse files Browse the repository at this point in the history
  • Loading branch information
Federico Feroldi committed Aug 23, 2019
1 parent 3ba6de0 commit 0ab21d0
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 0 deletions.
69 changes: 69 additions & 0 deletions GetMessages/__tests__/handler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// tslint:disable:no-any no-duplicate-string no-big-function

jest.mock("winston");

import { right } from "fp-ts/lib/Either";
import { none, some } from "fp-ts/lib/Option";

import { FiscalCode, NonEmptyString } from "italia-ts-commons/lib/strings";

import {
NewMessageWithoutContent,
RetrievedMessageWithoutContent
} from "io-functions-commons/dist/src/models/message";

import { ServiceId } from "io-functions-commons/dist/generated/definitions/ServiceId";
import { TimeToLiveSeconds } from "io-functions-commons/dist/generated/definitions/TimeToLiveSeconds";

import { response as MockResponse } from "jest-mock-express";

import { GetMessagesHandler } from "../handler";

const aFiscalCode = "FRLFRC74E04B157I" as FiscalCode;

const aNewMessageWithoutContent: NewMessageWithoutContent = {
createdAt: new Date(),
fiscalCode: aFiscalCode,
id: "A_MESSAGE_ID" as NonEmptyString,
indexedId: "A_MESSAGE_ID" as NonEmptyString,
isPending: true,
kind: "INewMessageWithoutContent",
senderServiceId: "test" as ServiceId,
senderUserId: "u123" as NonEmptyString,
timeToLiveSeconds: 3600 as TimeToLiveSeconds
};

const aRetrievedMessageWithoutContent: RetrievedMessageWithoutContent = {
...aNewMessageWithoutContent,
_self: "xyz",
_ts: 1,
kind: "IRetrievedMessageWithoutContent"
};

describe("GetMessagesHandler", () => {
it("should respond with the messages for the recipient", async () => {
const mockIterator = {
executeNext: jest
.fn()
.mockImplementationOnce(() =>
Promise.resolve(right(some([aRetrievedMessageWithoutContent])))
)
.mockImplementationOnce(() => Promise.resolve(right(none)))
};

const mockMessageModel = {
findMessages: jest.fn(() => mockIterator)
};

const getMessagesHandler = GetMessagesHandler(mockMessageModel as any);

const result = await getMessagesHandler(aFiscalCode);

expect(result.kind).toBe("IResponseSuccessJsonIterator");

const mockResponse = MockResponse();
await result.apply(mockResponse);

expect(mockIterator.executeNext).toHaveBeenCalledTimes(2);
});
});
20 changes: 20 additions & 0 deletions GetMessages/function.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"route": "v1/messages/{fiscalcode}",
"methods": [
"get"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
}
],
"scriptFile": "../dist/GetMessages/index.js"
}
73 changes: 73 additions & 0 deletions GetMessages/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as express from "express";

import { IResponseErrorValidation } from "italia-ts-commons/lib/responses";
import { FiscalCode } from "italia-ts-commons/lib/strings";

import {
filterResultIterator,
mapResultIterator
} from "io-functions-commons/dist/src/utils/documentdb";
import { retrievedMessageToPublic } from "io-functions-commons/dist/src/utils/messages";
import { FiscalCodeMiddleware } from "io-functions-commons/dist/src/utils/middlewares/fiscalcode";
import {
withRequestMiddlewares,
wrapRequestHandler
} from "io-functions-commons/dist/src/utils/request_middleware";
import {
IResponseErrorQuery,
IResponseSuccessJsonIterator,
ResponseJsonIterator
} from "io-functions-commons/dist/src/utils/response";

import { MessageModel } from "io-functions-commons/dist/src/models/message";

import { CreatedMessageWithoutContent } from "io-functions-commons/dist/generated/definitions/CreatedMessageWithoutContent";

/**
* Type of a GetMessages handler.
*
* GetMessages expects a FiscalCode as input and returns the Messages
* as output or a Validation error.
*
* TODO: add full results and paging
*/
type IGetMessagesHandler = (
fiscalCode: FiscalCode
) => Promise<
| IResponseSuccessJsonIterator<CreatedMessageWithoutContent>
| IResponseErrorValidation
| IResponseErrorQuery
>;

/**
* Handles requests for getting all message for a recipient.
*/
export function GetMessagesHandler(
messageModel: MessageModel
): IGetMessagesHandler {
return async fiscalCode => {
const retrievedMessagesIterator = messageModel.findMessages(fiscalCode);
const validMessagesIterator = filterResultIterator(
retrievedMessagesIterator,
// isPending is true when the message has been received from the sender
// but it's still being processed
message => message.isPending !== true
);
const publicExtendedMessagesIterator = mapResultIterator(
validMessagesIterator,
retrievedMessageToPublic
);
return ResponseJsonIterator(publicExtendedMessagesIterator);
};
}

/**
* Wraps a GetMessages handler inside an Express request handler.
*/
export function GetMessages(
messageModel: MessageModel
): express.RequestHandler {
const handler = GetMessagesHandler(messageModel);
const middlewaresWrap = withRequestMiddlewares(FiscalCodeMiddleware);
return wrapRequestHandler(middlewaresWrap(handler));
}
66 changes: 66 additions & 0 deletions GetMessages/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Context } from "@azure/functions";

import * as express from "express";
import * as winston from "winston";

import { DocumentClient as DocumentDBClient } from "documentdb";

import * as documentDbUtils from "io-functions-commons/dist/src/utils/documentdb";
import { getRequiredStringEnv } from "io-functions-commons/dist/src/utils/env";
import { secureExpressApp } from "io-functions-commons/dist/src/utils/express";
import { AzureContextTransport } from "io-functions-commons/dist/src/utils/logging";
import { setAppContext } from "io-functions-commons/dist/src/utils/middlewares/context_middleware";

import {
MESSAGE_COLLECTION_NAME,
MessageModel
} from "io-functions-commons/dist/src/models/message";

import createAzureFunctionHandler from "io-functions-express/dist/src/createAzureFunctionsHandler";

import { GetMessages } from "./handler";

// Setup Express
const app = express();
secureExpressApp(app);

const cosmosDbUri = getRequiredStringEnv("CUSTOMCONNSTR_COSMOSDB_URI");
const cosmosDbKey = getRequiredStringEnv("CUSTOMCONNSTR_COSMOSDB_KEY");
const cosmosDbName = getRequiredStringEnv("COSMOSDB_NAME");
const messageContainerName = getRequiredStringEnv("MESSAGE_CONTAINER_NAME");

const documentDbDatabaseUrl = documentDbUtils.getDatabaseUri(cosmosDbName);
const messagesCollectionUrl = documentDbUtils.getCollectionUri(
documentDbDatabaseUrl,
MESSAGE_COLLECTION_NAME
);

const documentClient = new DocumentDBClient(cosmosDbUri, {
masterKey: cosmosDbKey
});

const messageModel = new MessageModel(
documentClient,
messagesCollectionUrl,
messageContainerName
);

app.get("/api/v1/messages/:fiscalcode", GetMessages(messageModel));

const azureFunctionHandler = createAzureFunctionHandler(app);

// tslint:disable-next-line: no-let
let logger: Context["log"] | undefined;
const contextTransport = new AzureContextTransport(() => logger, {
level: "debug"
});
winston.add(contextTransport);

// Binds the express app to an Azure Function handler
function httpStart(context: Context): void {
logger = context.log;
setAppContext(app, context);
azureFunctionHandler(context);
}

export default httpStart;

0 comments on commit 0ab21d0

Please sign in to comment.