Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#IP-184] Add payee management to Send Message #158

Merged
merged 2 commits into from
Oct 12, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14.17.5
14.16.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this downgrade?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this is the version supported by Azure :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagined that. 👍

10 changes: 9 additions & 1 deletion CreateMessage/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import * as t from "io-ts";

import { FiscalCode } from "@pagopa/io-functions-commons/dist/generated/definitions/FiscalCode";
import { EUCovidCert } from "@pagopa/io-functions-commons/dist/generated/definitions/EUCovidCert";
import { NewMessage as ApiNewMessage } from "@pagopa/io-functions-commons/dist/generated/definitions/NewMessage";
import { CreatedMessageEvent } from "@pagopa/io-functions-commons/dist/src/models/created_message_event";
import {
Message,
Expand Down Expand Up @@ -85,6 +84,8 @@ import { ServiceId } from "@pagopa/io-functions-commons/dist/generated/definitio
import { Option } from "fp-ts/lib/Option";
import { Either } from "fp-ts/lib/Either";
import { TaskEither } from "fp-ts/lib/TaskEither";
import { PaymentDataWithRequiredPayee } from "@pagopa/io-functions-commons/dist/generated/definitions/PaymentDataWithRequiredPayee";
import { NewMessage as ApiNewMessage } from "@pagopa/io-functions-commons/dist/generated/definitions/NewMessage";
import { ApiNewMessageWithContentOf, ApiNewMessageWithDefaults } from "./types";

/**
Expand Down Expand Up @@ -522,6 +523,13 @@ export function CreateMessage(
AzureAllowBodyPayloadMiddleware(
ApiNewMessageWithContentOf(t.interface({ eu_covid_cert: EUCovidCert })),
new Set([UserGroup.ApiMessageWriteEUCovidCert])
),
// Ensures only users in ApiMessageWriteWithPayee group can send payment messages with payee payload
AzureAllowBodyPayloadMiddleware(
ApiNewMessageWithContentOf(
t.interface({ payment_data: PaymentDataWithRequiredPayee })
),
new Set([UserGroup.ApiMessageWriteWithPayee])
)
);
return wrapRequestHandler(
Expand Down
91 changes: 91 additions & 0 deletions StoreMessageContentActivity/__tests__/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
manualProfileServicePreferencesSettings
} from "../../__mocks__/mocks";
import { getStoreMessageContentActivityHandler } from "../handler";
import { PaymentData } from "../../generated/definitions/PaymentData";
import { OrganizationFiscalCode } from "@pagopa/ts-commons/lib/strings";

const mockContext = {
// eslint-disable no-console
Expand Down Expand Up @@ -73,13 +75,36 @@ const aFutureOptOutEmailSwitchDate = new Date(lastUpdateTimestamp + 10);

const aPastOptOutEmailSwitchDate = new Date(lastUpdateTimestamp - 10);

const anOrgFiscalCode = "01111111111" as OrganizationFiscalCode;

const aPaymentData = {
amount: 1000,
invalid_after_due_date: false,
notice_number: "177777777777777777"
}

const aPaymentDataWithPayee = {
...aPaymentData,
payee: {
fiscal_code: anOrgFiscalCode
}
}

const aCreatedMessageEvent: CreatedMessageEvent = {
content: aMessageContent,
message: aNewMessageWithoutContent,
senderMetadata: aCreatedMessageEventSenderMetadata,
serviceVersion: 1 as NonNegativeNumber
};
const aMessageContentWithPaymentData = {
...aMessageContent,
payment_data: aPaymentData
}

const aMessageContentWithPaymentDataWithPayee = {
...aMessageContent,
payment_data: aPaymentDataWithPayee
}
const aRetrievedProfileWithAValidTimestamp = {
...aRetrievedProfile,
_ts: lastUpdateTimestamp
Expand Down Expand Up @@ -204,6 +229,72 @@ describe("getStoreMessageContentActivityHandler", () => {
}
);

it.each`
scenario | profileResult | storageResult | upsertResult | preferenceResult | messageEvent | optOutEmailSwitchDate | optInEmailEnabled | expectedMessagePaymentData
${"with original payment message with payee"} | ${aRetrievedProfileWithAValidTimestamp} | ${aBlobResult} | ${aRetrievedMessage} | ${O.some(aRetrievedServicePreference)} | ${{...aCreatedMessageEvent, content: aMessageContentWithPaymentDataWithPayee}} | ${aPastOptOutEmailSwitchDate} | ${false} | ${aPaymentDataWithPayee}
${"with overridden payee if no payee is provided"} | ${aRetrievedProfileWithAValidTimestamp} | ${aBlobResult} | ${aRetrievedMessage} | ${O.some(aRetrievedServicePreference)} | ${{...aCreatedMessageEvent, content: aMessageContentWithPaymentData}} | ${aPastOptOutEmailSwitchDate} | ${false} | ${{...aPaymentData, payee: {fiscal_code: aCreatedMessageEvent.senderMetadata.organizationFiscalCode}}}
${"with a no payment message"} | ${aRetrievedProfileWithAValidTimestamp} | ${aBlobResult} | ${aRetrievedMessage} | ${O.none} | ${aCreatedMessageEvent} | ${aPastOptOutEmailSwitchDate} | ${false} | ${undefined}
`(
"should succeed with $scenario",
async ({
profileResult,
storageResult,
upsertResult,
preferenceResult,
messageEvent,
optOutEmailSwitchDate,
optInEmailEnabled,
expectedMessagePaymentData,
// mock implementation must be set only if we expect the function to be called, otherwise it will interfere with other tests
// we use "not-called" to determine such
skipPreferenceMock = preferenceResult === "not-called"
}) => {
findLastVersionByModelIdMock.mockImplementationOnce(() =>
TE.of(O.some(profileResult))
);
storeContentAsBlobMock.mockImplementationOnce(() =>
TE.of(O.some(storageResult))
);
upsertMessageMock.mockImplementationOnce(() =>
TE.of(O.some(upsertResult))
);
!skipPreferenceMock &&
findServicePreferenceMock.mockImplementationOnce(() =>
TE.of(preferenceResult)
);

const storeMessageContentActivityHandler = getStoreMessageContentActivityHandler(
{
lProfileModel,
lMessageModel,
lBlobService: {} as any,
lServicePreferencesModel,
optOutEmailSwitchDate,
isOptInEmailEnabled: optInEmailEnabled,
telemetryClient: mockTelemetryClient
}
);

const result = await storeMessageContentActivityHandler(
mockContext,
messageEvent
);

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

const msgEvt = messageEvent as CreatedMessageEvent;
// success means message has been stored and status has been updated
expect(storeContentAsBlobMock).toHaveBeenCalledWith(
{} as any,
msgEvt.message.id,
{
...msgEvt.content,
payment_data: expectedMessagePaymentData
}
);
}
);

it.each`
scenario | failureReason | profileResult | preferenceResult | messageEvent
${"activity input cannot be decoded"} | ${"BAD_DATA"} | ${"not-called"} | ${"not-called"} | ${{}}
Expand Down
31 changes: 29 additions & 2 deletions StoreMessageContentActivity/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ import { isBefore } from "date-fns";
import { UTCISODateFromString } from "@pagopa/ts-commons/lib/dates";
import { CosmosErrors } from "@pagopa/io-functions-commons/dist/src/utils/cosmosdb_model";
import { TaskEither } from "fp-ts/lib/TaskEither";
import { pipe } from "fp-ts/lib/function";
import { flow, pipe } from "fp-ts/lib/function";
import { PaymentDataWithRequiredPayee } from "@pagopa/io-functions-commons/dist/generated/definitions/PaymentDataWithRequiredPayee";
import { initTelemetryClient } from "../utils/appinsights";
import { toHash } from "../utils/crypto";
import { PaymentData } from "../generated/definitions/PaymentData";

export const SuccessfulStoreMessageContentActivityResult = t.interface({
blockedInboxOrChannels: t.readonlyArray(BlockedInboxOrChannel),
Expand Down Expand Up @@ -228,13 +230,38 @@ const createMessageOrThrow = async (
const newMessageWithoutContent = createdMessageEvent.message;
const logPrefix = `StoreMessageContentActivity|MESSAGE_ID=${newMessageWithoutContent.id}`;

// If a message is a payment message, we must override payee if it is not specified by sender
const messagePaymentData = pipe(
createdMessageEvent.content.payment_data,
O.fromPredicate(PaymentData.is),
O.chain(
flow(
O.fromPredicate(PaymentDataWithRequiredPayee.is),
O.getOrElse(() =>
PaymentDataWithRequiredPayee.encode({
...createdMessageEvent.content.payment_data,
payee: {
fiscal_code:
createdMessageEvent.senderMetadata.organizationFiscalCode
}
})
),
O.some
)
),
O.toUndefined
);

// Save the content of the message to the blob storage.
// In case of a retry this operation will overwrite the message content with itself
// (this is fine as we don't know if the operation succeeded at first)
const errorOrAttachment = await lMessageModel.storeContentAsBlob(
lBlobService,
newMessageWithoutContent.id,
createdMessageEvent.content
{
...createdMessageEvent.content,
payment_data: messagePaymentData
}
)();

if (E.isLeft(errorOrAttachment)) {
Expand Down
2 changes: 1 addition & 1 deletion __mocks__/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
OrganizationFiscalCode
} from "@pagopa/ts-commons/lib/strings";

import { CIDR } from "../generated/definitions/CIDR";

import { MessageBodyMarkdown } from "@pagopa/io-functions-commons/dist/generated/definitions/MessageBodyMarkdown";
import { MessageSubject } from "@pagopa/io-functions-commons/dist/generated/definitions/MessageSubject";
Expand All @@ -38,6 +37,7 @@ import {
import { CreatedMessageEventSenderMetadata } from "@pagopa/io-functions-commons/dist/src/models/created_message_sender_metadata";

import { MessageContent } from "@pagopa/io-functions-commons/dist/generated/definitions/MessageContent";
import { CIDR } from "@pagopa/io-functions-commons/dist/generated/definitions/CIDR";

export const aFiscalCode = "AAABBB01C02D345D" as FiscalCode;
export const anotherFiscalCode = "AAABBB01C02D345W" as FiscalCode;
Expand Down
Loading