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

[#IOPID-1450] Enable strict mode on repo #231

Merged
merged 12 commits into from
Mar 19, 2024
7 changes: 4 additions & 3 deletions CreateDevelopmentProfile/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ export function toExtendedProfile(profile: RetrievedProfile): ExtendedProfile {
accepted_tos_version: profile.acceptedTosVersion,
blocked_inbox_or_channels: profile.blockedInboxOrChannels,
email: profile.email,
is_email_already_taken: undefined,
is_email_enabled: profile.isEmailEnabled,
is_email_validated: profile.isEmailValidated,
// NOTE: We do NOT check email uniqueness in this context
is_email_already_taken: false,
is_email_enabled: profile.isEmailEnabled !== false,
is_email_validated: profile.isEmailValidated !== false,
is_inbox_enabled: profile.isInboxEnabled === true,
is_webhook_enabled: profile.isWebhookEnabled === true,
preferred_languages: profile.preferredLanguages,
Expand Down
2 changes: 1 addition & 1 deletion CreateSubscription/__tests__/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const aFakeApimProductContract: ProductContract = {
const aFakeApimSubscriptionContract: SubscriptionContract = {
allowTracing: false,
createdDate: new Date(),
displayName: null,
displayName: undefined,
endDate: undefined,
expirationDate: undefined,
id: "subscription-id",
Expand Down
3 changes: 3 additions & 0 deletions CreateUser/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ export function CreateUserHandler(
taskResults.apimClient.user.createOrUpdate(
azureApimConfig.apimResourceGroup,
azureApimConfig.apim,
// TODO: Implement a validation step to ensure the existence of `objectId`
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
taskResults.objectId,
{
email: userPayload.email,
Expand Down
2 changes: 1 addition & 1 deletion DeleteUserDataActivity/__tests__/backupAndDelete.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const asyncIteratorOf = <T>(items: T[]): AsyncIterator<T[]> => {
const value = data.shift();
return {
done: typeof value === "undefined",
value: [value]
value: [value!]
gquadrati marked this conversation as resolved.
Show resolved Hide resolved
};
}
};
Expand Down
36 changes: 21 additions & 15 deletions DeleteUserDataActivity/backupAndDelete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import * as crypto from "crypto";

import { BlobService } from "azure-storage";
import { sequenceT } from "fp-ts/lib/Apply";
import * as A from "fp-ts/lib/Array";
import * as RA from "fp-ts/lib/ReadonlyArray";
gquadrati marked this conversation as resolved.
Show resolved Hide resolved
import * as E from "fp-ts/lib/Either";
import * as O from "fp-ts/lib/Option";
import * as TE from "fp-ts/lib/TaskEither";

import { array, flatten, rights } from "fp-ts/lib/Array";
import { MessageContent } from "@pagopa/io-functions-commons/dist/generated/definitions/MessageContent";
import {
RetrievedMessage,
Expand Down Expand Up @@ -40,6 +39,7 @@ import {
QueryFailure
} from "./types";
import {
isCosmosErrors,
saveDataToBlob,
toDocumentDeleteFailure,
toQueryFailure
Expand Down Expand Up @@ -67,18 +67,18 @@ const executeRecursiveBackupAndDelete = <T>(
TE.mapLeft(toQueryFailure),
TE.chainW(e =>
e.done
? TE.of([])
? TE.of<DataFailure, ReadonlyArray<T>>([])
: e.value.some(E.isLeft)
? TE.left(
? TE.left<DataFailure, ReadonlyArray<T>>(
toQueryFailure(new Error("Some elements are not typed correctly"))
)
: TE.of(rights(e.value))
: TE.of<DataFailure, ReadonlyArray<T>>(RA.rights(e.value))
),
// executes backup&delete for this set of items
TE.chainW(items =>
pipe(
items,
A.map((item: T) =>
RA.map((item: T) =>
pipe(
sequenceT(TE.ApplicativeSeq)<
DataFailure,
Expand All @@ -104,8 +104,8 @@ const executeRecursiveBackupAndDelete = <T>(
TE.map(([_, __, nextResults]) => [item, ...nextResults])
)
),
A.sequence(TE.ApplicativePar),
TE.map(flatten)
RA.sequence(TE.ApplicativePar),
TE.map(RA.flatten)
)
)
);
Expand Down Expand Up @@ -365,7 +365,7 @@ const backupAndDeleteMessageView = ({
}): TE.TaskEither<DataFailure, O.Option<RetrievedMessageView>> =>
pipe(
messageViewModel.find([message.id, message.fiscalCode]),
TE.chain(TE.fromOption(() => undefined)),
TE.chainW(TE.fromOption(() => undefined)),
TE.foldW(
_ =>
// unfortunately, a document not found is threated like a query error
Expand Down Expand Up @@ -443,7 +443,7 @@ const backupAndDeleteMessageContent = ({
}): TE.TaskEither<DataFailure, O.Option<MessageContent>> =>
pipe(
messageModel.getContentFromBlob(messageContentBlobService, message.id),
TE.chain(TE.fromOption(() => undefined)),
TE.chainW(TE.fromOption(() => undefined)),
TE.foldW(
_ =>
// unfortunately, a document not found is threated like a query error
Expand Down Expand Up @@ -587,19 +587,25 @@ const backupAndDeleteAllMessagesData = ({
pipe(
messageModel.findMessages(fiscalCode),
TE.mapLeft(toQueryFailure),
TE.chain(iter =>
TE.tryCatch(() => asyncIteratorToArray(iter), toQueryFailure)
TE.chainW(iter =>
TE.tryCatch(
() => asyncIteratorToArray(iter),
err =>
err instanceof Error || isCosmosErrors(err)
? toQueryFailure(err)
: toQueryFailure(E.toError(err))
)
),
TE.map(flatten),
TE.map(RA.flatten),
TE.chainW(results =>
results.some(E.isLeft)
? TE.left(
toQueryFailure(
new Error("Cannot decode some element due to decoding errors")
)
)
: array.sequence(TE.ApplicativeSeq)(
rights(results).map(message => {
: RA.sequence(TE.ApplicativeSeq)(
RA.rights(results).map(message => {
// cast needed because findMessages has a wrong signature
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const retrievedMessage = (message as any) as RetrievedMessageWithoutContent;
Expand Down
18 changes: 18 additions & 0 deletions DeleteUserDataActivity/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as t from "io-ts";
import { Context } from "@azure/functions";
import { BlobService } from "azure-storage";
import * as TE from "fp-ts/lib/TaskEither";
import { CosmosErrors } from "@pagopa/io-functions-commons/dist/src/utils/cosmosdb_model";
import { pipe } from "fp-ts/lib/function";

import { enumType } from "@pagopa/ts-commons/lib/types";
import {
ActivityResultFailure,
BlobCreationFailure,
Expand All @@ -11,6 +14,21 @@ import {
QueryFailure
} from "./types";

// Cosmos Errors
export enum CosmosErrorsTypes {
"COSMOS_EMPTY_RESPONSE" = "COSMOS_EMPTY_RESPONSE",
"COSMOS_CONFLICT_RESPONSE" = "COSMOS_CONFLICT_RESPONSE",
"COSMOS_DECODING_ERROR" = "COSMOS_DECODING_ERROR",
"COSMOS_ERROR_RESPONSE" = "COSMOS_ERROR_RESPONSE"
}

const CosmosErrorsTypesC = t.interface({
kind: enumType<CosmosErrorsTypes>(CosmosErrorsTypes, "kind")
});

export const isCosmosErrors = (error: unknown): error is CosmosErrors =>
CosmosErrorsTypesC.is(error);

/**
* To be used for exhaustive checks
*/
Expand Down
2 changes: 1 addition & 1 deletion ExtractUserDataActivity/__tests__/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const asyncIteratorOf = <T>(items: T[]): AsyncIterator<T[]> => {
const value = data.shift();
return {
done: typeof value === "undefined",
value: [value]
value: [value!]
};
}
};
Expand Down
66 changes: 35 additions & 31 deletions ExtractUserDataActivity/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import * as t from "io-ts";
import { DeferredPromise } from "@pagopa/ts-commons/lib/promises";

import { sequenceS, sequenceT } from "fp-ts/lib/Apply";
import * as A from "fp-ts/lib/Array";
import * as ROA from "fp-ts/lib/ReadonlyArray";
import { flatten, rights } from "fp-ts/lib/Array";

import { Context } from "@azure/functions";

Expand Down Expand Up @@ -221,8 +219,8 @@ export const getAllMessageContents = (
> =>
pipe(
messages,
A.map(_ => _.id),
A.map(messageId =>
ROA.map(_ => _.id),
ROA.map(messageId =>
pipe(
messageModel.getContentFromBlob(messageContentBlobService, messageId),
TE.chainW(
Expand All @@ -241,7 +239,7 @@ export const getAllMessageContents = (
)
)
),
A.sequence(TE.ApplicativePar)
ROA.sequence(TE.ApplicativePar)
);

/**
Expand All @@ -253,8 +251,8 @@ export const getAllMessagesStatuses = (
): TE.TaskEither<ActivityResultQueryFailure, ReadonlyArray<MessageStatus>> =>
pipe(
messages,
A.map(_ => _.id),
A.map(messageId =>
ROA.map(_ => _.id),
ROA.map(messageId =>
pipe(
messageStatusModel.findLastVersionByModelId([messageId]),
TE.mapLeft(failure =>
Expand All @@ -273,7 +271,7 @@ export const getAllMessagesStatuses = (
)
)
),
A.sequence(TE.ApplicativePar)
ROA.sequence(TE.ApplicativePar)
);

/**
Expand All @@ -290,8 +288,8 @@ export const findNotificationsForAllMessages = (
> =>
pipe(
messages,
A.map(m => notificationModel.findNotificationForMessage(m.id)),
A.sequence(TE.ApplicativeSeq),
ROA.map(m => notificationModel.findNotificationForMessage(m.id)),
ROA.sequence(TE.ApplicativeSeq),
TE.mapLeft(e =>
ActivityResultQueryFailure.encode({
kind: "QUERY_FAILURE",
Expand All @@ -304,8 +302,8 @@ export const findNotificationsForAllMessages = (
// We just filter "none" elements
TE.map(
flow(
A.filter(O.isSome),
A.map(maybeNotification => maybeNotification.value)
ROA.filter(O.isSome),
ROA.map(maybeNotification => maybeNotification.value)
)
)
);
Expand All @@ -321,14 +319,16 @@ export const findAllNotificationStatuses = (
notifications,

// compose a query for every supported channel type
A.reduce([], (queries, { id: notificationId }) => [
...queries,
...Object.values(NotificationChannelEnum).map(channel => [
notificationId,
channel
])
]),
A.map(([notificationId, channel]) =>
ROA.reduce(
[] as ReadonlyArray<readonly [NonEmptyString, NotificationChannelEnum]>,
(queries, { id: notificationId }) => [
...queries,
...Object.values(NotificationChannelEnum).map(
channel => [notificationId, channel] as const
)
]
),
ROA.map(([notificationId, channel]) =>
pipe(
notificationStatusModel.findOneNotificationStatusByNotificationChannel(
notificationId,
Expand All @@ -344,13 +344,13 @@ export const findAllNotificationStatuses = (
)
)
),
A.sequence(TE.ApplicativePar),
ROA.sequence(TE.ApplicativePar),

// filter empty results (it might not exist a content for a pair notification/channel)
TE.map(
flow(
A.filter(O.isSome),
A.map(someNotificationStatus => someNotificationStatus.value)
ROA.filter(O.isSome),
ROA.map(someNotificationStatus => someNotificationStatus.value)
)
)
);
Expand Down Expand Up @@ -380,18 +380,18 @@ export const queryAllUserData = (
// step 0: look for the profile
getProfile(profileModel, fiscalCode),
// step 1: get messages, which can be queried by only knowing the fiscal code
TE.chain(profile =>
TE.chainW(profile =>
sequenceS(TE.ApplicativePar)({
// queries all messages for the user
messages: pipe(
messageModel.findMessages(fiscalCode),
TE.chain(iterator =>
TE.chainW(iterator =>
TE.tryCatch(
() => asyncIteratorToArray(iterator),
toCosmosErrorResponse
)
),
TE.map(flatten),
TE.map(ROA.flatten),
TE.mapLeft(_ =>
ActivityResultQueryFailure.encode({
kind: "QUERY_FAILURE",
Expand All @@ -408,7 +408,11 @@ export const queryAllUserData = (
reason: "Some messages cannot be decoded"
})
)
: TE.of(rights(results))
: TE.of(
ROA.rights(results) as ReadonlyArray<
RetrievedMessageWithoutContent
>
)
)
),
messagesView: pipe(
Expand All @@ -428,7 +432,7 @@ export const queryAllUserData = (
TE.chain(iter =>
TE.tryCatch(() => asyncIteratorToArray(iter), toCosmosErrorResponse)
),
TE.map(flatten),
TE.map(ROA.flatten),
TE.mapLeft(_ =>
ActivityResultQueryFailure.encode({
kind: "QUERY_FAILURE",
Expand All @@ -445,7 +449,7 @@ export const queryAllUserData = (
reason: "Some messages cannot be decoded"
})
)
: TE.of(rights(results))
: TE.of(ROA.rights(results))
)
),
profile: TE.of(profile),
Expand All @@ -469,7 +473,7 @@ export const queryAllUserData = (
})
),
// step 2: queries notifications and message contents, which need message data to be queried first
TE.chain(({ profile, messages, messagesView, servicesPreferences }) =>
TE.chainW(({ profile, messages, messagesView, servicesPreferences }) =>
sequenceS(TE.ApplicativePar)({
messageContents: getAllMessageContents(
messageContentBlobService,
Expand All @@ -488,7 +492,7 @@ export const queryAllUserData = (
})
),
// step 3: queries notifications statuses
TE.bind("notificationStatuses", ({ notifications }) =>
TE.bindW("notificationStatuses", ({ notifications }) =>
findAllNotificationStatuses(notificationStatusModel, notifications)
),
TE.map(
Expand Down
4 changes: 2 additions & 2 deletions GetFailedUserDataProcessing/__tests__/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const findEntry = (
PartitionKey: UserDataProcessingChoice;
RowKey: FiscalCode;
}>
) => (choice, fiscalCode) =>
) => (choice: UserDataProcessingChoice, fiscalCode: FiscalCode) =>
entries.length > 0
? entries
.filter(e => e.PartitionKey === choice && e.RowKey === fiscalCode)
Expand Down Expand Up @@ -56,7 +56,7 @@ const storageTableMock = "FailedUserDataProcessing" as NonEmptyString;
const fiscalCode1 = "UEEFON48A55Y758X" as FiscalCode;
const fiscalCode2 = "VEEGON48A55Y758Z" as FiscalCode;

const noFailedRequests = [];
const noFailedRequests: typeof failedRequests = [];

const failedRequests = [
{
Expand Down
Loading
Loading