Skip to content
This repository has been archived by the owner on Jan 15, 2025. It is now read-only.

Commit

Permalink
Merge pull request #9 from pagopa/176466130_add_update_cgn_status_act…
Browse files Browse the repository at this point in the history
…ivity

[#176466130] Add Update CGN status activity
  • Loading branch information
AleDore authored Jan 13, 2021
2 parents 830e5e2 + d5c9365 commit 9908550
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 0 deletions.
127 changes: 127 additions & 0 deletions UpdateCgnStatusActivity/__tests__/handler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/* tslint:disable: no-any */
import { none, some } from "fp-ts/lib/Option";
import { fromLeft, taskEither } from "fp-ts/lib/TaskEither";
import { toCosmosErrorResponse } from "io-functions-commons/dist/src/utils/cosmosdb_model";
import { FiscalCode, NonEmptyString } from "italia-ts-commons/lib/strings";
import { context } from "../../__mocks__/durable-functions";
import {
CgnPendingStatus,
StatusEnum
} from "../../generated/definitions/CgnPendingStatus";
import {
CgnRevokedStatus,
StatusEnum as RevokedStatusEnum
} from "../../generated/definitions/CgnRevokedStatus";
import { UserCgn } from "../../models/user_cgn";
import { ActivityInput, getUpdateCgnStatusActivityHandler } from "../handler";

const now = new Date();
const aFiscalCode = "RODFDS82S10H501T" as FiscalCode;
const aRevokationRequest = {
motivation: "aMotivation" as NonEmptyString
};

const aUserCgnRevokedStatus: CgnRevokedStatus = {
motivation: aRevokationRequest.motivation,
revokation_date: now,
status: RevokedStatusEnum.REVOKED
};

const aRevokedUserCgn: UserCgn = {
fiscalCode: aFiscalCode,
id: "ID" as NonEmptyString,
status: aUserCgnRevokedStatus
};

const aUserCgnPendingStatus: CgnPendingStatus = {
status: StatusEnum.PENDING
};

const findLastVersionByModelIdMock = jest.fn();
const updateMock = jest.fn();

const userCgnModelMock = {
findLastVersionByModelId: findLastVersionByModelIdMock,
update: updateMock
};

const anActivityInput: ActivityInput = {
cgnStatus: aUserCgnRevokedStatus,
fiscalCode: aFiscalCode
};
describe("UpdateCgnStatusActivity", () => {
beforeEach(() => {
jest.clearAllMocks();
});
it("should return failure if an error occurs during UserCgn retrieve", async () => {
findLastVersionByModelIdMock.mockImplementationOnce(() =>
fromLeft(toCosmosErrorResponse(new Error("query error")))
);
const updateCgnStatusActivityHandler = getUpdateCgnStatusActivityHandler(
userCgnModelMock as any
);
const response = await updateCgnStatusActivityHandler(
context,
anActivityInput
);
expect(response.kind).toBe("FAILURE");
if (response.kind === "FAILURE") {
expect(response.reason).toBe(
"Cannot retrieve userCgn for the provided fiscalCode"
);
}
});

it("should return failure if no UserCgn was found", async () => {
findLastVersionByModelIdMock.mockImplementationOnce(() =>
taskEither.of(none)
);
const updateCgnStatusActivityHandler = getUpdateCgnStatusActivityHandler(
userCgnModelMock as any
);
const response = await updateCgnStatusActivityHandler(
context,
anActivityInput
);
expect(response.kind).toBe("FAILURE");
if (response.kind === "FAILURE") {
expect(response.reason).toBe(
"No userCgn found for the provided fiscalCode"
);
}
});
it("should return failure if userCgn' s update fails", async () => {
findLastVersionByModelIdMock.mockImplementationOnce(() =>
taskEither.of(some(aRevokedUserCgn))
);
updateMock.mockImplementationOnce(() =>
fromLeft(new Error("Cannot update userCgn"))
);
const updateCgnStatusActivityHandler = getUpdateCgnStatusActivityHandler(
userCgnModelMock as any
);
const response = await updateCgnStatusActivityHandler(
context,
anActivityInput
);
expect(response.kind).toBe("FAILURE");
if (response.kind === "FAILURE") {
expect(response.reason).toBe("Cannot update userCgn");
}
});

it("should return success if userCgn' s update success", async () => {
findLastVersionByModelIdMock.mockImplementationOnce(() =>
taskEither.of(some({ ...aRevokedUserCgn, status: aUserCgnPendingStatus }))
);
updateMock.mockImplementationOnce(() => taskEither.of(aRevokedUserCgn));
const updateCgnStatusActivityHandler = getUpdateCgnStatusActivityHandler(
userCgnModelMock as any
);
const response = await updateCgnStatusActivityHandler(
context,
anActivityInput
);
expect(response.kind).toBe("SUCCESS");
});
});
10 changes: 10 additions & 0 deletions UpdateCgnStatusActivity/function.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"bindings": [
{
"name": "name",
"type": "activityTrigger",
"direction": "in"
}
],
"scriptFile": "../dist/UpdateCgnStatusActivity/index.js"
}
92 changes: 92 additions & 0 deletions UpdateCgnStatusActivity/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Context } from "@azure/functions";
import { fromOption, toError } from "fp-ts/lib/Either";
import { identity } from "fp-ts/lib/function";
import { fromEither } from "fp-ts/lib/TaskEither";
import * as t from "io-ts";
import { FiscalCode } from "italia-ts-commons/lib/strings";
import { CgnStatus } from "../generated/definitions/CgnStatus";
import { UserCgnModel } from "../models/user_cgn";
import { errorsToError } from "../utils/conversions";

export const ActivityInput = t.interface({
cgnStatus: CgnStatus,
fiscalCode: FiscalCode
});

export type ActivityInput = t.TypeOf<typeof ActivityInput>;

// Activity result
const ActivityResultSuccess = t.interface({
kind: t.literal("SUCCESS")
});

type ActivityResultSuccess = t.TypeOf<typeof ActivityResultSuccess>;

const ActivityResultFailure = t.interface({
kind: t.literal("FAILURE"),
reason: t.string
});

type ActivityResultFailure = t.TypeOf<typeof ActivityResultFailure>;

export const ActivityResult = t.taggedUnion("kind", [
ActivityResultSuccess,
ActivityResultFailure
]);

export type ActivityResult = t.TypeOf<typeof ActivityResult>;

const failure = (context: Context, logPrefix: string) => (
err: Error,
description: string = ""
) => {
const logMessage =
description === ""
? `${logPrefix}|FAILURE=${err.message}`
: `${logPrefix}|${description}|FAILURE=${err.message}`;
context.log.info(logMessage);
return ActivityResultFailure.encode({
kind: "FAILURE",
reason: err.message
});
};

const success = () =>
ActivityResultSuccess.encode({
kind: "SUCCESS"
});

export const getUpdateCgnStatusActivityHandler = (
userCgnModel: UserCgnModel,
logPrefix: string = "UpdateCgnStatusActivity"
) => (context: Context, input: unknown): Promise<ActivityResult> => {
const fail = failure(context, logPrefix);
return fromEither(ActivityInput.decode(input))
.mapLeft(errs => fail(errorsToError(errs), "Cannot decode Activity Input"))
.chain(activityInput =>
userCgnModel
.findLastVersionByModelId([activityInput.fiscalCode])
.mapLeft(() =>
fail(new Error("Cannot retrieve userCgn for the provided fiscalCode"))
)
.chain(maybeUserCgn =>
fromEither(
fromOption(
fail(new Error("No userCgn found for the provided fiscalCode"))
)(maybeUserCgn)
)
)
.map(userCgn => ({
...userCgn,
status: activityInput.cgnStatus
}))
)
.chain(_ =>
userCgnModel.update(_).bimap(
err => fail(toError(err), "Cannot update userCgn"),
() => success()
)
)
.fold<ActivityResult>(identity, identity)
.run();
};
18 changes: 18 additions & 0 deletions UpdateCgnStatusActivity/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { USER_CGN_COLLECTION_NAME, UserCgnModel } from "../models/user_cgn";
import { getConfigOrThrow } from "../utils/config";
import { cosmosdbClient } from "../utils/cosmosdb";
import { getUpdateCgnStatusActivityHandler } from "./handler";

const config = getConfigOrThrow();

const userCgnsContainer = cosmosdbClient
.database(config.COSMOSDB_NAME)
.container(USER_CGN_COLLECTION_NAME);

const userCgnModel = new UserCgnModel(userCgnsContainer);

const updateCgnStatusActivityHandler = getUpdateCgnStatusActivityHandler(
userCgnModel
);

export default updateCgnStatusActivityHandler;
6 changes: 6 additions & 0 deletions utils/conversions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Errors } from "io-ts";
import { errorsToReadableMessages } from "italia-ts-commons/lib/reporters";

export function errorsToError(errors: Errors): Error {
return new Error(errorsToReadableMessages(errors).join(" / "));
}

0 comments on commit 9908550

Please sign in to comment.