Skip to content

Commit

Permalink
Adds GetServicesForRecipient function
Browse files Browse the repository at this point in the history
  • Loading branch information
Federico Feroldi committed Aug 21, 2019
1 parent 9a80da3 commit 321bdf8
Show file tree
Hide file tree
Showing 8 changed files with 1,602 additions and 34 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ __pycache__/
*$py.class

coverage

yarn-error.log
1 change: 1 addition & 0 deletions @types/jest-mock-express.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module "jest-mock-express";
110 changes: 110 additions & 0 deletions GetServicesForRecipient/__tests__/handler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// tslint:disable:no-any

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

import { right } from "fp-ts/lib/Either";
import { NonNegativeNumber } from "italia-ts-commons/lib/numbers";
import {
FiscalCode,
NonEmptyString,
OrganizationFiscalCode
} from "italia-ts-commons/lib/strings";

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

import * as middlewares from "io-functions-commons/dist/src/utils/request_middleware";

import {
NewService,
RetrievedService,
Service,
toAuthorizedCIDRs,
toAuthorizedRecipients
} from "io-functions-commons/dist/src/models/service";

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

import {
GetServicesForRecipient,
GetServicesForRecipientHandler
} from "../handler";

afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});

const anOrganizationFiscalCode = "01234567890" as OrganizationFiscalCode;
const aFiscalCode = "SPNDNL80R13D000X" as FiscalCode;

const aService: Service = {
authorizedCIDRs: toAuthorizedCIDRs([]),
authorizedRecipients: toAuthorizedRecipients([]),
departmentName: "MyDeptName" as NonEmptyString,
isVisible: true,
maxAllowedPaymentAmount: 0 as MaxAllowedPaymentAmount,
organizationFiscalCode: anOrganizationFiscalCode,
organizationName: "MyOrgName" as NonEmptyString,
serviceId: "MySubscriptionId" as NonEmptyString,
serviceName: "MyServiceName" as NonEmptyString
};

const aNewService: NewService = {
...aService,
id: "123" as NonEmptyString,
kind: "INewService",
version: 1 as NonNegativeNumber
};

const aRetrievedService: RetrievedService = {
...aNewService,
_self: "123",
_ts: 123,
kind: "IRetrievedService"
};

const someRetrievedServices: ReadonlyArray<any> = [
aRetrievedService,
{ ...aRetrievedService, id: "124" }
];

describe("GetServicesByRecipientHandler", () => {
it("should get id of the services that notified an existing recipient", async () => {
const mockIterator = {
executeNext: jest.fn()
};
mockIterator.executeNext.mockImplementationOnce(() =>
Promise.resolve(right(some(someRetrievedServices)))
);
mockIterator.executeNext.mockImplementationOnce(() =>
Promise.resolve(right(none))
);

const senderServiceModelMock = {
findSenderServicesForRecipient: jest.fn(() => mockIterator)
};

const getSenderServiceHandler = GetServicesForRecipientHandler(
senderServiceModelMock as any
);
const response = await getSenderServiceHandler(aFiscalCode);
await response.apply(MockResponse());

expect(
senderServiceModelMock.findSenderServicesForRecipient
).toHaveBeenCalledWith(aFiscalCode);
expect(response.kind).toBe("IResponseSuccessJsonIterator");
expect(mockIterator.executeNext).toHaveBeenCalledTimes(2);
});
});

describe("GetServicesByRecipient", () => {
// tslint:disable-next-line:no-duplicate-string
it("should set up authentication middleware", async () => {
const withRequestMiddlewaresSpy = jest
.spyOn(middlewares, "withRequestMiddlewares")
.mockReturnValueOnce(jest.fn());
GetServicesForRecipient({} as any);
expect(withRequestMiddlewaresSpy).toHaveBeenCalledTimes(1);
});
});
20 changes: 20 additions & 0 deletions GetServicesForRecipient/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/profiles/{fiscalcode}/sender-services",
"methods": [
"get"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
}
],
"scriptFile": "../dist/GetServicesForRecipient/index.js"
}
63 changes: 63 additions & 0 deletions GetServicesForRecipient/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as express from "express";

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

import { mapResultIterator } from "io-functions-commons/dist/src/utils/documentdb";
import { RequiredParamMiddleware } from "io-functions-commons/dist/src/utils/middlewares/required_param";
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 { SenderServiceModel } from "io-functions-commons/dist/src/models/sender_service";

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

type IGetSenderServicesHandlerRet =
| IResponseSuccessJsonIterator<ServiceTuple>
| IResponseErrorQuery;

type IGetSenderServicesHandler = (
fiscalCode: FiscalCode
) => Promise<IGetSenderServicesHandlerRet>;

/**
* Returns the serviceId for all the Services that have sent
* at least one notification to the recipient with the provided fiscalCode.
*/
export function GetServicesForRecipientHandler(
senderServiceModel: SenderServiceModel
): IGetSenderServicesHandler {
return async fiscalCode => {
const retrievedServicesIterator = senderServiceModel.findSenderServicesForRecipient(
fiscalCode
);
const senderServicesIterator = mapResultIterator(
retrievedServicesIterator,
service => ({
service_id: service.serviceId,
version: service.version
})
);
return ResponseJsonIterator(senderServicesIterator);
};
}

/**
* Wraps a GetSenderServices handler inside an Express request handler.
*/
export function GetServicesForRecipient(
senderServiceModel: SenderServiceModel
): express.RequestHandler {
const handler = GetServicesForRecipientHandler(senderServiceModel);

const middlewaresWrap = withRequestMiddlewares(
RequiredParamMiddleware("fiscalcode", FiscalCode)
);
return wrapRequestHandler(middlewaresWrap(handler));
}
70 changes: 70 additions & 0 deletions GetServicesForRecipient/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Context } from "@azure/functions";

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

import { DocumentClient as DocumentDBClient } from "documentdb";

import {
SENDER_SERVICE_COLLECTION_NAME,
SenderServiceModel
} from "io-functions-commons/dist/src/models/sender_service";
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 createAzureFunctionHandler from "io-functions-express/dist/src/createAzureFunctionsHandler";

import { GetServicesForRecipient } from "./handler";

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

// Set up CORS (free access to the API from browser clients)
app.use(cors());

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

const documentDbDatabaseUrl = documentDbUtils.getDatabaseUri(cosmosDbName);
const senderServicesCollectionUrl = documentDbUtils.getCollectionUri(
documentDbDatabaseUrl,
SENDER_SERVICE_COLLECTION_NAME
);

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

const senderServiceModel = new SenderServiceModel(
documentClient,
senderServicesCollectionUrl
);

app.get(
"/api/v1/profiles/:fiscalcode/sender-services",
GetServicesForRecipient(senderServiceModel)
);

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;
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"fast-check": "^1.16.0",
"italia-tslint-rules": "^1.1.3",
"jest": "^24.8.0",
"jest-mock-express": "^0.1.1",
"npm-run-all": "^4.1.5",
"prettier": "^1.18.2",
"ts-jest": "^24.0.2",
Expand All @@ -47,4 +48,4 @@
"italia-ts-commons": "^5.1.5",
"winston": "^3.2.1"
}
}
}
Loading

0 comments on commit 321bdf8

Please sign in to comment.