teamdigitale/italia-app

View on GitHub
ts/features/services/details/saga/handleUpsertServicePreference.ts

Summary

Maintainability
C
7 hrs
Test Coverage
import * as pot from "@pagopa/ts-commons/lib/pot";
import * as E from "fp-ts/lib/Either";
import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
import { call, put, select } from "typed-redux-saga/macro";
import { ActionType } from "typesafe-actions";
import { PathTraversalSafePathParam } from "../../../../../definitions/backend/PathTraversalSafePathParam";
import { ServicePreference } from "../../../../../definitions/backend/ServicePreference";
import { BackendClient } from "../../../../api/backend";
import { SagaCallReturnType } from "../../../../types/utils";
import { getGenericError, getNetworkError } from "../../../../utils/errors";
import { readablePrivacyReport } from "../../../../utils/reporters";
import { withRefreshApiCall } from "../../../fastLogin/saga/utils";
import { trackPNPushSettings } from "../../../pn/analytics";
import { upsertServicePreference } from "../store/actions/preference";
import {
  serviceMetadataInfoSelector,
  servicePreferencePotSelector
} from "../store/reducers";
import { isServicePreferenceResponseSuccess } from "../types/ServicePreferenceResponse";
import { mapKinds } from "./handleGetServicePreference";

/**
 * Generates the payload for the updating preferences request, if a users disables the inbox flag than the other flags
 * are disabled.
 * If a user activates a disabled inbox flag than webhook is enabled too.
 * @param currentServicePreferenceState
 * @param action
 */
const calculateUpdatingPreference = (
  currentServicePreferenceState: ReturnType<
    typeof servicePreferencePotSelector
  >,
  action: ActionType<typeof upsertServicePreference.request>
): ServicePreference => {
  if (
    pot.isSome(currentServicePreferenceState) &&
    isServicePreferenceResponseSuccess(currentServicePreferenceState.value) &&
    !currentServicePreferenceState.value.value.inbox &&
    action.payload.inbox
  ) {
    return {
      is_inbox_enabled: action.payload.inbox,
      is_webhook_enabled: true,
      is_email_enabled: currentServicePreferenceState.value.value.email,

      // When the `inbox` preference will be re-enabled (from false to true),
      // by default the `can_access_message_read_status` should
      // be enabled too, just like the `is_webhook_enabled`.
      can_access_message_read_status: true,

      settings_version: action.payload
        .settings_version as ServicePreference["settings_version"]
    };
  }

  return {
    is_inbox_enabled: action.payload.inbox,
    is_webhook_enabled: action.payload.inbox ? action.payload.push : false,
    is_email_enabled: action.payload.inbox ? action.payload.email : false,
    can_access_message_read_status: action.payload.inbox
      ? action.payload.can_access_message_read_status
      : false,
    settings_version: action.payload
      .settings_version as ServicePreference["settings_version"]
  };
};

export function* trackPNPushNotificationSettings(
  action: ActionType<typeof upsertServicePreference.request>
) {
  const serviceMetadataInfo = yield* select(
    serviceMetadataInfoSelector,
    action.payload.id
  );

  pipe(
    serviceMetadataInfo,
    O.fromNullable,
    O.chainNullableK(metadata => metadata.customSpecialFlow),
    O.filter(customSpecialFlow => customSpecialFlow === "pn"),
    O.fold(
      () => undefined,
      _ => trackPNPushSettings(action.payload.push)
    )
  );
}

/**
 * saga to handle the update of service preferences after a user specific action
 * @param upsertServicePreferences
 * @param action
 */
export function* handleUpsertServicePreference(
  upsertServicePreferences: BackendClient["upsertServicePreference"],
  action: ActionType<typeof upsertServicePreference.request>
) {
  yield* call(trackPNPushNotificationSettings, action);

  const servicePreferencePot = yield* select(servicePreferencePotSelector);

  const updatingPreference = calculateUpdatingPreference(
    servicePreferencePot,
    action
  );

  try {
    if (!PathTraversalSafePathParam.is(action.payload.id)) {
      yield* put(
        upsertServicePreference.failure({
          id: action.payload.id,
          ...getGenericError(
            new Error(
              "Unable to decode ServiceId to PathTraversalSafePathParam"
            )
          )
        })
      );
      return;
    }

    const response: SagaCallReturnType<typeof upsertServicePreferences> =
      (yield* call(
        withRefreshApiCall,
        upsertServicePreferences({
          service_id: action.payload.id,
          body: updatingPreference
        }),
        action
      )) as unknown as SagaCallReturnType<typeof upsertServicePreferences>;

    if (E.isRight(response)) {
      if (response.right.status === 401) {
        return;
      }

      if (response.right.status === 200) {
        yield* put(
          upsertServicePreference.success({
            id: action.payload.id,
            kind: "success",
            value: {
              inbox: response.right.value.is_inbox_enabled,
              push: response.right.value.is_webhook_enabled,
              email: response.right.value.is_email_enabled,

              // If the optional flag does not exists it will be set
              // as the value of `inbox`.
              can_access_message_read_status:
                response.right.value.can_access_message_read_status ??
                response.right.value.is_inbox_enabled,
              settings_version: response.right.value.settings_version
            }
          })
        );
        return;
      }

      if (mapKinds[response.right.status] !== undefined) {
        yield* put(
          upsertServicePreference.success({
            id: action.payload.id,
            kind: mapKinds[response.right.status]
          })
        );
        return;
      }
      // not handled error codes
      yield* put(
        upsertServicePreference.failure({
          id: action.payload.id,
          ...getGenericError(
            new Error(`response status code ${response.right.status}`)
          )
        })
      );
      return;
    }
    // cannot decode response
    yield* put(
      upsertServicePreference.failure({
        id: action.payload.id,
        ...getGenericError(new Error(readablePrivacyReport(response.left)))
      })
    );
  } catch (e) {
    yield* put(
      upsertServicePreference.failure({
        id: action.payload.id,
        ...getNetworkError(e)
      })
    );
  }
}