This repository has been archived by the owner on Jan 15, 2025. It is now read-only.
generated from pagopa/io-functions-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from pagopa/176466130_add_update_cgn_status_act…
…ivity [#176466130] Add Update CGN status activity
- Loading branch information
Showing
5 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(" / ")); | ||
} |