diff --git a/GetServices/__tests__/handler.test.ts b/GetServices/__tests__/handler.test.ts index aba555f0..5183658b 100644 --- a/GetServices/__tests__/handler.test.ts +++ b/GetServices/__tests__/handler.test.ts @@ -2,15 +2,18 @@ import { left, right } from "fp-ts/lib/Either"; -import { aRetrievedService } from "../../__mocks__/mocks"; +import { none, some } from "fp-ts/lib/Option"; +import { aRetrievedService, aSeralizedService } from "../../__mocks__/mocks"; import { GetServicesHandler } from "../handler"; describe("GetServices", () => { it("Should return a query error when a database error occurs", async () => { const mockServiceModel = { - getCollectionIterator: jest.fn(() => { - return Promise.resolve(left({})); - }) + getCollectionIterator: jest.fn(() => + Promise.resolve({ + executeNext: () => Promise.resolve(left(new Error())) + }) + ) }; const getServicesHandler = GetServicesHandler(mockServiceModel as any); @@ -23,14 +26,33 @@ describe("GetServices", () => { ); expect(mockServiceModel.getCollectionIterator).toHaveBeenCalledWith(); - expect(response.kind).toBe("IResponseSuccessJsonIterator"); + expect(response.kind).toBe("IResponseErrorQuery"); }); it("Should return the collection of services from the database", async () => { const mockServiceModel = { getCollectionIterator: jest.fn(() => Promise.resolve({ - executeNext: () => Promise.resolve(right([aRetrievedService])) + executeNext: jest + .fn() + .mockImplementationOnce(() => + Promise.resolve( + right( + some([ + aRetrievedService, + { + ...aRetrievedService, + version: 3 + }, + { + ...aRetrievedService, + version: 2 + } + ]) + ) + ) + ) + .mockImplementationOnce(() => Promise.resolve(right(none))) }) ) }; @@ -45,6 +67,18 @@ describe("GetServices", () => { ); expect(mockServiceModel.getCollectionIterator).toHaveBeenCalledWith(); - expect(response.kind).toBe("IResponseSuccessJsonIterator"); + expect(response.kind).toBe("IResponseSuccessJson"); + if (response.kind === "IResponseSuccessJson") { + expect(response.value).toEqual({ + items: [ + { + ...aSeralizedService, + service_metadata: undefined, + version: 3 + } + ], + page_size: 1 + }); + } }); }); diff --git a/GetServices/handler.ts b/GetServices/handler.ts index 93647f08..31422fde 100644 --- a/GetServices/handler.ts +++ b/GetServices/handler.ts @@ -3,7 +3,11 @@ import { Context } from "@azure/functions"; import * as express from "express"; import { ServiceModel } from "io-functions-commons/dist/src/models/service"; -import { mapResultIterator } from "io-functions-commons/dist/src/utils/documentdb"; +import { + IFoldableResultIterator, + iteratorToValue, + reduceResultIterator +} from "io-functions-commons/dist/src/utils/documentdb"; import { AzureApiAuthMiddleware, IAzureApiAuthorization, @@ -23,34 +27,63 @@ import { wrapRequestHandler } from "io-functions-commons/dist/src/utils/request_middleware"; import { - IResponseSuccessJsonIterator, - ResponseJsonIterator + IResponseErrorQuery, + ResponseErrorQuery } from "io-functions-commons/dist/src/utils/response"; import { checkSourceIpForHandler, clientIPAndCidrTuple as ipTuple } from "io-functions-commons/dist/src/utils/source_ip_check"; +import { collect, StrMap } from "fp-ts/lib/StrMap"; +import { + IResponseSuccessJson, + ResponseSuccessJson +} from "italia-ts-commons/lib/responses"; import { Service as ApiService } from "../generated/definitions/Service"; import { retrievedServiceToApiService } from "../utils/conversions"; +type IGetServicesHandlerResult = + | IResponseErrorQuery + | IResponseSuccessJson<{ items: readonly ApiService[]; page_size: number }>; + type IGetServicesHandler = ( context: Context, auth: IAzureApiAuthorization, clientIp: ClientIp, userAttributes: IAzureUserAttributes -) => Promise>; +) => Promise; export function GetServicesHandler( serviceModel: ServiceModel ): IGetServicesHandler { return async (_, __, ___, ____) => { const allRetrievedServicesIterator = await serviceModel.getCollectionIterator(); - const allServicesIterator = mapResultIterator( - allRetrievedServicesIterator, - retrievedServiceToApiService + const allServicesIterator: IFoldableResultIterator< + Record + > = reduceResultIterator(allRetrievedServicesIterator, (prev, curr) => { + // keep only the latest version + const isNewer = + !prev[curr.serviceId] || curr.version > prev[curr.serviceId].version; + return { + ...prev, + ...(isNewer + ? { [curr.serviceId]: retrievedServiceToApiService(curr) } + : {}) + }; + }); + return (await iteratorToValue(allServicesIterator, {})).fold< + IGetServicesHandlerResult + >( + error => ResponseErrorQuery("Cannot get services", error), + services => { + const items = collect(new StrMap(services), (_____, v) => v); + return ResponseSuccessJson({ + items, + page_size: items.length + }); + } ); - return ResponseJsonIterator(allServicesIterator); }; }