diff --git a/CreateNotificationActivity/handler.ts b/CreateNotificationActivity/handler.ts index 6a498f26..d1aa2fe3 100644 --- a/CreateNotificationActivity/handler.ts +++ b/CreateNotificationActivity/handler.ts @@ -157,7 +157,7 @@ export const getCreateNotificationActivityHandler = ( // whether email notifications are enabled in this user profile - this is // true by default, it's false only for users that have isEmailEnabled = false - // in their profile. We assume it's true when not defined in user's profile. + // in their profile. const isEmailEnabledInProfile = profile.isEmailEnabled !== false; // Check if the email in the user profile is validated. diff --git a/EmailNotificationActivity/__tests__/handler.test.ts b/EmailNotificationActivity/__tests__/handler.test.ts index a47acf89..3810bfbf 100644 --- a/EmailNotificationActivity/__tests__/handler.test.ts +++ b/EmailNotificationActivity/__tests__/handler.test.ts @@ -23,7 +23,7 @@ import { MessageSubject } from "@pagopa/io-functions-commons/dist/generated/defi import { TimeToLiveSeconds } from "@pagopa/io-functions-commons/dist/generated/definitions/TimeToLiveSeconds"; import { NotificationChannelEnum } from "@pagopa/io-functions-commons/dist/generated/definitions/NotificationChannel"; -import * as mail from "@pagopa/io-functions-commons/dist/src/mailer"; +import * as mail from "@pagopa/io-functions-commons/dist/src/mailer/transports"; import { CreatedMessageEventSenderMetadata } from "@pagopa/io-functions-commons/dist/src/models/created_message_sender_metadata"; import { NewNotification, diff --git a/GetSubscriptionsFeed/__tests__/handler.test.ts b/GetSubscriptionsFeed/__tests__/handler.test.ts index 7fbb3262..977fb1fb 100644 --- a/GetSubscriptionsFeed/__tests__/handler.test.ts +++ b/GetSubscriptionsFeed/__tests__/handler.test.ts @@ -5,18 +5,16 @@ import { TableService } from "azure-storage"; import * as dateFmt from "date-fns"; -import * as endOfTomorrow from "date-fns/end_of_tomorrow"; -import * as startOfYesterday from "date-fns/start_of_yesterday"; import { FiscalCodeHash } from "../../generated/definitions/FiscalCodeHash"; import { GetSubscriptionsFeedHandler } from "../handler"; import { anIncompleteService, aValidService } from "../../__mocks__/mocks"; -const tomorrow = endOfTomorrow(); +const tomorrow = dateFmt.endOfTomorrow(); -const yesterday = startOfYesterday(); +const yesterday = dateFmt.startOfYesterday(); -const yesterdayUTC = dateFmt.format(yesterday, "YYYY-MM-DD"); +const yesterdayUTC = dateFmt.format(yesterday, "yyyy-MM-dd"); const userAttrs = { email: "example@mail.com", @@ -82,7 +80,7 @@ describe("GetSubscriptionsFeedHandler", () => { {} as any, {} as any, userAttrs as any, - dateFmt.format(tomorrow, "YYYY-MM-DD") + dateFmt.format(tomorrow, "yyyy-MM-dd") ); expect(result.kind).toBe("IResponseErrorNotFound"); }); diff --git a/StoreMessageContentActivity/__tests__/handler.test.ts b/StoreMessageContentActivity/__tests__/handler.test.ts new file mode 100644 index 00000000..499a0144 --- /dev/null +++ b/StoreMessageContentActivity/__tests__/handler.test.ts @@ -0,0 +1,263 @@ +/* eslint-disable no-console */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { BlockedInboxOrChannelEnum } from "@pagopa/io-functions-commons/dist/generated/definitions/BlockedInboxOrChannel"; +import { ServiceId } from "@pagopa/io-functions-commons/dist/generated/definitions/ServiceId"; +import { CreatedMessageEvent } from "@pagopa/io-functions-commons/dist/src/models/created_message_event"; +import { UTCISODateFromString } from "@pagopa/ts-commons/lib/dates"; +import { NonNegativeNumber } from "@pagopa/ts-commons/lib/numbers"; +import { fromLeft } from "fp-ts/lib/IOEither"; +import { none, some } from "fp-ts/lib/Option"; +import { taskEither } from "fp-ts/lib/TaskEither"; +import { + aCreatedMessageEventSenderMetadata, + aMessageContent, + aNewMessageWithoutContent, + aRetrievedMessage, + aRetrievedProfile +} from "../../__mocks__/mocks"; +import { getStoreMessageContentActivityHandler } from "../handler"; + +const mockContext = { + // eslint-disable no-console + log: { + error: console.error, + info: console.log, + verbose: console.log, + warn: console.warn + } +} as any; + +const findLastVersionByModelIdMock = jest + .fn() + .mockImplementation(() => taskEither.of(some(aRetrievedProfile))); +const profileModelMock = { + findLastVersionByModelId: jest.fn(findLastVersionByModelIdMock) +}; + +const aBlobResult = { + name: "ABlobName" +}; + +const storeContentAsBlobMock = jest + .fn() + .mockImplementation(() => taskEither.of(some(aBlobResult))); +const upsertMessageMock = jest + .fn() + .mockImplementation(() => taskEither.of(aRetrievedMessage)); +const messageModelMock = { + storeContentAsBlob: jest.fn(storeContentAsBlobMock), + upsert: jest.fn(upsertMessageMock) +}; + +const anOptOutEmailSwitchDate = UTCISODateFromString.decode( + "2021-07-08T23:59:59Z" +).getOrElseL(() => fail("wrong date value")); + +const aPastOptOutEmailSwitchDate = UTCISODateFromString.decode( + "2000-07-08T23:59:59Z" +).getOrElseL(() => fail("wrong date value")); + +const aCreatedMessageEvent: CreatedMessageEvent = { + content: aMessageContent, + message: aNewMessageWithoutContent, + senderMetadata: aCreatedMessageEventSenderMetadata, + serviceVersion: 1 as NonNegativeNumber +}; + +const aRetrievedProfileWithAValidTimestamp = { + ...aRetrievedProfile, + _ts: 1625172947000 +}; +describe("getStoreMessageContentActivityHandler", () => { + it("should respond success with a retrieved profile with isEmailEnabled to false", async () => { + const storeMessageContentActivityHandler = getStoreMessageContentActivityHandler( + profileModelMock as any, + messageModelMock as any, + {} as any, + // limit date is after profile timestamp + anOptOutEmailSwitchDate + ); + + const result = await storeMessageContentActivityHandler( + mockContext, + aCreatedMessageEvent + ); + + expect(result.kind).toBe("SUCCESS"); + if (result.kind === "SUCCESS") { + expect(result.blockedInboxOrChannels).toEqual([]); + expect(result.profile).toEqual({ + ...aRetrievedProfile, + isEmailEnabled: false + }); + } + }); + + it("should respond success with a retrieved profile mantaining its original isEmailEnabled property", async () => { + findLastVersionByModelIdMock.mockImplementationOnce(() => + taskEither.of(some(aRetrievedProfileWithAValidTimestamp)) + ); + const storeMessageContentActivityHandler = getStoreMessageContentActivityHandler( + profileModelMock as any, + messageModelMock as any, + {} as any, + // limit date is before profile timestamp + aPastOptOutEmailSwitchDate + ); + + const result = await storeMessageContentActivityHandler( + mockContext, + aCreatedMessageEvent + ); + + expect(result.kind).toBe("SUCCESS"); + if (result.kind === "SUCCESS") { + expect(result.blockedInboxOrChannels).toEqual([]); + expect(result.profile).toEqual(aRetrievedProfileWithAValidTimestamp); + } + }); + + it("should fail if activity input cannot be decoded", async () => { + const storeMessageContentActivityHandler = getStoreMessageContentActivityHandler( + profileModelMock as any, + messageModelMock as any, + {} as any, + aPastOptOutEmailSwitchDate + ); + + const result = await storeMessageContentActivityHandler( + mockContext, + {} as any + ); + + expect(result.kind).toBe("FAILURE"); + if (result.kind === "FAILURE") { + expect(result.reason).toEqual("BAD_DATA"); + } + }); + + it("should throw an Error if there is an error while fetching profile", async () => { + findLastVersionByModelIdMock.mockImplementationOnce(() => + fromLeft("Profile fetch error") + ); + const storeMessageContentActivityHandler = getStoreMessageContentActivityHandler( + profileModelMock as any, + messageModelMock as any, + {} as any, + aPastOptOutEmailSwitchDate + ); + + await expect( + storeMessageContentActivityHandler(mockContext, aCreatedMessageEvent) + ).rejects.toThrow(); + }); + + it("should return a failure if no profile was found", async () => { + findLastVersionByModelIdMock.mockImplementationOnce(() => + taskEither.of(none) + ); + const storeMessageContentActivityHandler = getStoreMessageContentActivityHandler( + profileModelMock as any, + messageModelMock as any, + {} as any, + aPastOptOutEmailSwitchDate + ); + + const result = await storeMessageContentActivityHandler( + mockContext, + aCreatedMessageEvent + ); + + expect(result.kind).toBe("FAILURE"); + if (result.kind === "FAILURE") { + expect(result.reason).toEqual("PROFILE_NOT_FOUND"); + } + }); + + it("should return a failure if inbox is not enabled", async () => { + findLastVersionByModelIdMock.mockImplementationOnce(() => + taskEither.of(some({ ...aRetrievedProfile, isInboxEnabled: false })) + ); + const storeMessageContentActivityHandler = getStoreMessageContentActivityHandler( + profileModelMock as any, + messageModelMock as any, + {} as any, + aPastOptOutEmailSwitchDate + ); + + const result = await storeMessageContentActivityHandler( + mockContext, + aCreatedMessageEvent + ); + + expect(result.kind).toBe("FAILURE"); + if (result.kind === "FAILURE") { + expect(result.reason).toEqual("MASTER_INBOX_DISABLED"); + } + }); + + it("should return a failure if message sender is blocked", async () => { + findLastVersionByModelIdMock.mockImplementationOnce(() => + taskEither.of( + some({ + ...aRetrievedProfile, + blockedInboxOrChannels: { myService: [BlockedInboxOrChannelEnum.INBOX] } + }) + ) + ); + const storeMessageContentActivityHandler = getStoreMessageContentActivityHandler( + profileModelMock as any, + messageModelMock as any, + {} as any, + aPastOptOutEmailSwitchDate + ); + + const result = await storeMessageContentActivityHandler( + mockContext, + { + ...aCreatedMessageEvent, + message: { + ...aNewMessageWithoutContent, + senderServiceId: "myService" as ServiceId + } + } + ); + + expect(result.kind).toBe("FAILURE"); + if (result.kind === "FAILURE") { + expect(result.reason).toEqual("SENDER_BLOCKED"); + } + }); + + it("should throw an Error if message store operation fails", async () => { + storeContentAsBlobMock.mockImplementationOnce(() => + fromLeft(new Error("Error while storing message content")) + ); + const storeMessageContentActivityHandler = getStoreMessageContentActivityHandler( + profileModelMock as any, + messageModelMock as any, + {} as any, + aPastOptOutEmailSwitchDate + ); + + await expect( + storeMessageContentActivityHandler(mockContext, aCreatedMessageEvent) + ).rejects.toThrow(); + }); + + it("should throw an Error if message upsert fails", async () => { + upsertMessageMock.mockImplementationOnce(() => + fromLeft(new Error("Error while upserting message")) + ); + const storeMessageContentActivityHandler = getStoreMessageContentActivityHandler( + profileModelMock as any, + messageModelMock as any, + {} as any, + aPastOptOutEmailSwitchDate + ); + + await expect( + storeMessageContentActivityHandler(mockContext, aCreatedMessageEvent) + ).rejects.toThrow(); + }); +}); diff --git a/StoreMessageContentActivity/handler.ts b/StoreMessageContentActivity/handler.ts index 2c7cd9cc..4f1de35f 100644 --- a/StoreMessageContentActivity/handler.ts +++ b/StoreMessageContentActivity/handler.ts @@ -15,6 +15,8 @@ import { BlobService } from "azure-storage"; import { isLeft } from "fp-ts/lib/Either"; import { fromNullable, isNone } from "fp-ts/lib/Option"; import { readableReport } from "italia-ts-commons/lib/reporters"; +import { isBefore } from "date-fns"; +import { UTCISODateFromString } from "@pagopa/ts-commons/lib/dates"; export const SuccessfulStoreMessageContentActivityResult = t.interface({ blockedInboxOrChannels: t.readonlyArray(BlockedInboxOrChannel), @@ -57,7 +59,8 @@ export type StoreMessageContentActivityResult = t.TypeOf< export const getStoreMessageContentActivityHandler = ( lProfileModel: ProfileModel, lMessageModel: MessageModel, - lBlobService: BlobService + lBlobService: BlobService, + optOutEmailSwitchDate: UTCISODateFromString ) => async ( context: Context, input: unknown @@ -181,6 +184,13 @@ export const getStoreMessageContentActivityHandler = ( // since a Set can't be serialized to JSON blockedInboxOrChannels: Array.from(blockedInboxOrChannels), kind: "SUCCESS", - profile + profile: { + ...profile, + // if profile's timestamp is before email opt out switch limit date we must force isEmailEnabled to false + // eslint-disable-next-line no-underscore-dangle + isEmailEnabled: isBefore(profile._ts, optOutEmailSwitchDate) + ? false + : profile.isEmailEnabled + } }; }; diff --git a/StoreMessageContentActivity/index.ts b/StoreMessageContentActivity/index.ts index 798c2340..38fd9b3a 100644 --- a/StoreMessageContentActivity/index.ts +++ b/StoreMessageContentActivity/index.ts @@ -29,7 +29,8 @@ const blobService = createBlobService(config.QueueStorageConnection); const activityFunctionHandler: AzureFunction = getStoreMessageContentActivityHandler( profileModel, messageModel, - blobService + blobService, + config.OPT_OUT_EMAIL_SWITCH_DATE ); export default activityFunctionHandler; diff --git a/__mocks__/mocks.ts b/__mocks__/mocks.ts index 1e38b6ea..5298b4de 100644 --- a/__mocks__/mocks.ts +++ b/__mocks__/mocks.ts @@ -3,20 +3,41 @@ import { Service, ValidService } from "@pagopa/io-functions-commons/dist/src/models/service"; +import { CosmosResource } from "@pagopa/io-functions-commons/dist/src/utils/cosmosdb_model"; import { NonNegativeInteger, WithinRangeInteger } from "@pagopa/ts-commons/lib/numbers"; import { + EmailString, FiscalCode, NonEmptyString, 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" +import { ServiceId } from "@pagopa/io-functions-commons/dist/generated/definitions/ServiceId" +import { TimeToLiveSeconds } from "@pagopa/io-functions-commons/dist/generated/definitions/TimeToLiveSeconds" + +import { NewMessageWithoutContent, RetrievedMessageWithoutContent } from "@pagopa/io-functions-commons/dist/src/models/message"; +import { CreatedMessageEventSenderMetadata } from "@pagopa/io-functions-commons/dist/src/models/created_message_sender_metadata"; +import { RetrievedProfile } from "@pagopa/io-functions-commons/dist/src/models/profile"; +import { ServicesPreferencesModeEnum } from "@pagopa/io-functions-commons/dist/generated/definitions/ServicesPreferencesMode"; +import { MessageContent } from "@pagopa/io-functions-commons/dist/generated/definitions/MessageContent"; + export const aFiscalCode = "AAABBB01C02D345D" as FiscalCode; export const anotherFiscalCode = "AAABBB01C02D345W" as FiscalCode; +// CosmosResourceMetadata +export const aCosmosResourceMetadata: Omit = { + _etag: "_etag", + _rid: "_rid", + _self: "_self", + _ts: 1 +}; + export const aValidService: ValidService = { serviceId: "01234567890" as NonEmptyString, authorizedRecipients: new Set([aFiscalCode, anotherFiscalCode]), @@ -57,3 +78,73 @@ export const anIncompleteService: Service & { }, version: 1 as NonNegativeInteger }; + +export const legacyProfileServicePreferencesSettings: RetrievedProfile["servicePreferencesSettings"] = { + mode: ServicesPreferencesModeEnum.LEGACY, + version: -1 +}; + +export const aRetrievedProfile: RetrievedProfile = { + ...aCosmosResourceMetadata, + fiscalCode: aFiscalCode, + id: "123" as NonEmptyString, + isEmailEnabled: true, + isEmailValidated: true, + isInboxEnabled: true, + isTestProfile: false, + isWebhookEnabled: false, + kind: "IRetrievedProfile", + servicePreferencesSettings: legacyProfileServicePreferencesSettings, + version: 0 as NonNegativeInteger +}; + +export const aRetrievedMessage: RetrievedMessageWithoutContent = { + ...aCosmosResourceMetadata, + createdAt: new Date(), + fiscalCode: aFiscalCode, + id: "A_MESSAGE_ID" as NonEmptyString, + kind: "IRetrievedMessageWithoutContent", + indexedId: "AN_INDEXED_ID" as NonEmptyString, + senderServiceId: "01234567890" as NonEmptyString, + senderUserId: "A_USER_ID" as NonEmptyString, + timeToLiveSeconds: 604800 +}; + +export const aMessageBodyMarkdown = "test".repeat(80) as MessageBodyMarkdown; + +export const aMessageContent: MessageContent = { + markdown: aMessageBodyMarkdown, + subject: "test".repeat(10) as MessageSubject +}; + + +export const aSerializedNewMessageWithContent = { + content: aMessageContent, + createdAt: new Date().toISOString(), + fiscalCode: aFiscalCode, + id: "A_MESSAGE_ID" as NonEmptyString, + indexedId: "A_MESSAGE_ID" as NonEmptyString, + senderServiceId: "agid" as ServiceId, + senderUserId: "u123" as NonEmptyString, + timeToLiveSeconds: 3600 as TimeToLiveSeconds +}; + +export const aNewMessageWithoutContent: NewMessageWithoutContent = { + createdAt: new Date(), + fiscalCode: aFiscalCode, + id: "A_MESSAGE_ID" as NonEmptyString, + indexedId: "A_MESSAGE_ID" as NonEmptyString, + senderServiceId: "agid" as ServiceId, + senderUserId: "u123" as NonEmptyString, + timeToLiveSeconds: 3600 as TimeToLiveSeconds, + kind: "INewMessageWithoutContent" +}; + +export const aCreatedMessageEventSenderMetadata: CreatedMessageEventSenderMetadata = { + departmentName: "aDepartmentName" as NonEmptyString, + organizationFiscalCode: "01234567890" as OrganizationFiscalCode, + organizationName: "An Organization Name" as NonEmptyString, + requireSecureChannels: false, + serviceName: "A_SERVICE_NAME" as NonEmptyString, + serviceUserEmail: "aaa@mail.com" as EmailString +}; \ No newline at end of file diff --git a/package.json b/package.json index aba921d6..4d622b82 100644 --- a/package.json +++ b/package.json @@ -57,12 +57,13 @@ "typescript": "^3.3.3" }, "dependencies": { - "@azure/cosmos": "^3.7.2", - "@pagopa/io-functions-commons": "^20.5.2", + "@azure/cosmos": "^3.11.5", + "@pagopa/io-functions-commons": "^20.6.6", "@pagopa/ts-commons": "^9.4.1", "applicationinsights": "^1.7.4", - "azure-storage": "^2.10.3", + "azure-storage": "^2.10.4", "cors": "^2.8.4", + "date-fns": "^2.16.1", "documentdb": "^1.12.2", "durable-functions": "^1.4.3", "express": "^4.15.3", diff --git a/utils/__tests__/arbitraries.ts b/utils/__tests__/arbitraries.ts index 09b09fd6..ef11b1fd 100644 --- a/utils/__tests__/arbitraries.ts +++ b/utils/__tests__/arbitraries.ts @@ -18,6 +18,7 @@ import { NonEmptyString, PatternString } from "italia-ts-commons/lib/strings"; +import { legacyProfileServicePreferencesSettings } from "../../__mocks__/mocks"; // // custom fastcheck arbitraries @@ -234,6 +235,7 @@ export const retrievedProfileArb = fc isInboxEnabled: true, kind: "IRetrievedProfile", preferredLanguages: [PreferredLanguageEnum.en_GB], + servicePreferencesSettings: legacyProfileServicePreferencesSettings, version: version as NonNegativeInteger } as RetrievedProfile) ); diff --git a/utils/config.ts b/utils/config.ts index 0c619b98..f8b000d7 100644 --- a/utils/config.ts +++ b/utils/config.ts @@ -11,6 +11,7 @@ import { fromNullable } from "fp-ts/lib/Option"; import * as t from "io-ts"; import { readableReport } from "italia-ts-commons/lib/reporters"; import { NonEmptyString } from "italia-ts-commons/lib/strings"; +import { UTCISODateFromString } from "@pagopa/ts-commons/lib/dates"; import { CommaSeparatedListOf } from "./comma-separated-list"; // global app configuration @@ -33,6 +34,7 @@ export const IConfig = t.intersection([ IO_FUNCTIONS_ADMIN_BASE_URL: NonEmptyString, MESSAGE_CONTAINER_NAME: NonEmptyString, + OPT_OUT_EMAIL_SWITCH_DATE: UTCISODateFromString, QueueStorageConnection: NonEmptyString, @@ -51,9 +53,12 @@ export const IConfig = t.intersection([ MailerConfig ]); +const DEFAULT_OPT_OUT_EMAIL_SWITCH_DATE = "1970-01-01T00:00:00Z"; + // No need to re-evaluate this object for each call const errorOrConfig: t.Validation = IConfig.decode({ ...process.env, + FF_DISABLE_INCOMPLETE_SERVICES: fromNullable( process.env.FF_DISABLE_INCOMPLETE_SERVICES ) @@ -64,6 +69,9 @@ const errorOrConfig: t.Validation = IConfig.decode({ ) .map(_ => _.toLowerCase() === "true") .getOrElse(false), + OPT_OUT_EMAIL_SWITCH_DATE: fromNullable( + process.env.OPT_OUT_EMAIL_SWITCH_DATE + ).getOrElse(DEFAULT_OPT_OUT_EMAIL_SWITCH_DATE), isProduction: process.env.NODE_ENV === "production" }); diff --git a/yarn.lock b/yarn.lock index 8adcb55c..10099754 100644 --- a/yarn.lock +++ b/yarn.lock @@ -41,10 +41,10 @@ "@opentelemetry/api" "1.0.0-rc.0" tslib "^2.0.0" -"@azure/cosmos@^3.11.1", "@azure/cosmos@^3.7.2": - version "3.11.3" - resolved "https://registry.yarnpkg.com/@azure/cosmos/-/cosmos-3.11.3.tgz#422db38bdafaaac50d01dd6a85306f40ef3fb6c2" - integrity sha512-UaY6/87Jf4Pfqi1tEUhwJeaLzhpU0GajB55kU3UXs4bqQK+JPOUJXCe+TJtUD6vRd3RdwmoyoRF8k9cJ6LSQzQ== +"@azure/cosmos@^3.11.5": + version "3.11.5" + resolved "https://registry.yarnpkg.com/@azure/cosmos/-/cosmos-3.11.5.tgz#2a4e0b5a08ed710f630999168c8cb9349449b862" + integrity sha512-emKRLrrQLAzwVyOWo67THkPSWD646puwvUfr8oVhCYSs9OgfTCeNh29XB7uU4YZd3ufcumFrTupSCfH6AodZUA== dependencies: "@azure/core-auth" "^1.3.0" "@azure/core-rest-pipeline" "^1.0.3" @@ -614,24 +614,22 @@ eslint-plugin-sonarjs "^0.5.0" prettier "^2.1.2" -"@pagopa/io-functions-commons@^20.5.2": - version "20.5.2" - resolved "https://registry.yarnpkg.com/@pagopa/io-functions-commons/-/io-functions-commons-20.5.2.tgz#8cce05a52b734e40649c6c9ec7843226319c8d67" - integrity sha512-3L3CML8CmaE6XwPL9+A64Pwpx/UP+6qwlThoWt1eAkCvKyGYSff5Wi8bO+NXTJaaiVr+352PEQ/D5W08WJ1VWg== +"@pagopa/io-functions-commons@^20.6.6": + version "20.6.6" + resolved "https://registry.yarnpkg.com/@pagopa/io-functions-commons/-/io-functions-commons-20.6.6.tgz#973027c4a51fd2f1f4a7218094f144faba40c875" + integrity sha512-SWxJpEW1KfHAE7WMfPUXnX0JOMJrupWNXItKyjx3tNmZw7EzfvRBfi7y4c2ysX0CpkcWq0hewhl8XT8ZEWH//w== dependencies: - "@azure/cosmos" "^3.11.1" - "@pagopa/ts-commons" "^9.4.1" - "@types/node-fetch" "^2.5.6" + "@azure/cosmos" "^3.11.5" + "@pagopa/ts-commons" "^9.5.1" applicationinsights "^1.8.10" - azure-storage "^2.10.3" + azure-storage "^2.10.4" cidr-matcher "^2.1.1" fp-ts "1.17.4" helmet "^4.6.0" helmet-csp "^2.5.1" - io-functions-express "^1.1.0" io-ts "1.8.5" node-fetch "^2.6.1" - nodemailer "^6.5.0" + nodemailer "^6.6.1" nodemailer-sendgrid "^1.0.3" referrer-policy "^1.1.0" rehype-stringify "^3.0.0" @@ -685,6 +683,20 @@ node-fetch "^2.6.0" validator "^10.1.0" +"@pagopa/ts-commons@^9.5.1": + version "9.5.1" + resolved "https://registry.yarnpkg.com/@pagopa/ts-commons/-/ts-commons-9.5.1.tgz#a1285589265560a1a96b4d389da2e37f9209ec58" + integrity sha512-Eqozj079y/oyLZwh6I/WBWsUKJ6rFOj5wGTXaYmVffxFHWXstQiSqw5S6cFAKK5/LA9lL9ro+zk3+lUKi+/Fow== + dependencies: + abort-controller "^3.0.0" + agentkeepalive "^4.1.4" + applicationinsights "^1.8.10" + fp-ts "1.17.4" + io-ts "1.8.5" + json-set-map "^1.1.2" + node-fetch "^2.6.0" + validator "^10.1.0" + "@sendgrid/client@^6.5.5": version "6.5.5" resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-6.5.5.tgz#66cf569445d98a795998a894bb432a9939ead7c3" @@ -876,14 +888,6 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== -"@types/node-fetch@^2.5.6": - version "2.5.10" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132" - integrity sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - "@types/node@*": version "15.0.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.2.tgz#51e9c0920d1b45936ea04341aa3e2e58d339fb67" @@ -1154,7 +1158,7 @@ agent-base@6: dependencies: debug "4" -agentkeepalive@^4.1.2: +agentkeepalive@^4.1.2, agentkeepalive@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== @@ -1459,10 +1463,10 @@ axios@^0.19.0: dependencies: follow-redirects "1.5.10" -azure-storage@^2.10.3: - version "2.10.3" - resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-2.10.3.tgz#c5966bf929d87587d78f6847040ea9a4b1d4a50a" - integrity sha512-IGLs5Xj6kO8Ii90KerQrrwuJKexLgSwYC4oLWmc11mzKe7Jt2E5IVg+ZQ8K53YWZACtVTMBNO3iGuA+4ipjJxQ== +azure-storage@^2.10.4: + version "2.10.4" + resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-2.10.4.tgz#c481d207eabc05f57f019b209f7faa8737435104" + integrity sha512-zlfRPl4js92JC6+79C2EUmNGYjSknRl8pOiHQF78zy+pbOFOHtlBF6BU/OxPeHQX3gaa6NdEZnVydFxhhndkEw== dependencies: browserify-mime "~1.2.9" extend "^3.0.2" @@ -1470,7 +1474,7 @@ azure-storage@^2.10.3: md5.js "1.3.4" readable-stream "~2.0.0" request "^2.86.0" - underscore "~1.8.3" + underscore "^1.12.1" uuid "^3.0.0" validator "~9.4.1" xml2js "0.2.8" @@ -2197,6 +2201,11 @@ date-fns@^1.28.5: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== +date-fns@^2.16.1: + version "2.22.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.22.1.tgz#1e5af959831ebb1d82992bf67b765052d8f0efc4" + integrity sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9, debug@~2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -3900,11 +3909,6 @@ io-functions-express@^0.1.1: resolved "https://registry.yarnpkg.com/io-functions-express/-/io-functions-express-0.1.1.tgz#6ae954fe89ec1d797c5e5d10eca5e77f08ff4a4d" integrity sha512-Co7sovRyB0lxgU6NFh5v72Apude3XbgKQtqA7slryvKoEqLAvlnLU7DtsYVoF62O3ZJdtSHYf9JWuhDc/di1mg== -io-functions-express@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/io-functions-express/-/io-functions-express-1.1.2.tgz#3025242c2096e443a889c7eb8cb7555fb7f1e5c6" - integrity sha512-Gy+g5QQ7k+WnG9TpbgHXObIKrIj05iY/0uK86k+rTyTg6JF9YXVWCnKKmxX2LsJqLpWQtdwOQSwRQhNSfs/rxg== - io-ts@1.8.5: version "1.8.5" resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.8.5.tgz#2e102f7f518abe17b0f7e7ede0db54a4c4ddc188" @@ -4840,7 +4844,7 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -json-set-map@^1.0.2: +json-set-map@^1.0.2, json-set-map@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/json-set-map/-/json-set-map-1.1.2.tgz#536cbc6549d06e8af11f76cb4fbd441ed2389e6e" integrity sha512-x0TGwgcOG21jOa8wV1PWXDpSXUAKa1WuhMSHPBQhXU5Pb+4DdMrxOw5HMAWztVLP8KhSG5Kl5BoAOVF0aW63wA== @@ -5649,10 +5653,10 @@ nodemailer@^4.6.7: resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-4.7.0.tgz#4420e06abfffd77d0618f184ea49047db84f4ad8" integrity sha512-IludxDypFpYw4xpzKdMAozBSkzKHmNBvGanUREjJItgJ2NYcK/s8+PggVhj7c2yGFQykKsnnmv1+Aqo0ZfjHmw== -nodemailer@^6.5.0: - version "6.6.1" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.1.tgz#2a05fbf205b897d71bf43884167b5d4d3bd01b99" - integrity sha512-1xzFN3gqv+/qJ6YRyxBxfTYstLNt0FCtZaFRvf4Sg9wxNGWbwFmGXVpfSi6ThGK6aRxAo+KjHtYSW8NvCsNSAg== +nodemailer@^6.6.1: + version "6.6.2" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.2.tgz#e184c9ed5bee245a3e0bcabc7255866385757114" + integrity sha512-YSzu7TLbI+bsjCis/TZlAXBoM4y93HhlIgo0P5oiA2ua9Z4k+E2Fod//ybIzdJxOlXGRcHIh/WaeCBehvxZb/Q== normalize-package-data@^2.0.0, normalize-package-data@^2.3.2: version "2.5.0" @@ -7829,11 +7833,16 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= -underscore@1.8.3, underscore@~1.8.3: +underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= +underscore@^1.12.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" + integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== + underscore@latest: version "1.9.1" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"