All files / src/auth/session session-utils.ts

11.94% Statements 8/67
0% Branches 0/41
0% Functions 0/10
15.09% Lines 8/53

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109  24x 24x 24x 24x                                                                                                                 24x 24x                                                                 24x                          
import {Session} from './session';
import type {SessionInterface} from './types';
 
/**
 * Like Object.fromEntries(), but normalizes the keys and filters out null values.
 */
export function sessionFromEntries(
  entries: [string, string | number][],
): SessionInterface {
  const obj = Object.fromEntries(
    entries
      .filter(([_key, value]) => value !== null && value !== undefined)
      // Sanitize keys
      .map(([key, value]) => {
        switch (key.toLowerCase()) {
          case 'isonline':
            return ['isOnline', value];
          case 'accesstoken':
            return ['accessToken', value];
          case 'onlineaccessinfo':
            return ['onlineAccessInfo', value];
          default:
            return [key.toLowerCase(), value];
        }
      })
      // Sanitize values
      .map(([key, value]) => {
        switch (key) {
          case 'isOnline':
            if (typeof value === 'string') {
              return [key, value.toString().toLowerCase() === 'true'];
            } else if (typeof value === 'number') {
              return [key, Boolean(value)];
            }
            return [key, value];
          case 'scope':
            return [key, value.toString()];
          case 'expires':
            return [key, value ? new Date(Number(value) * 1000) : undefined];
          case 'onlineAccessInfo':
            return [
              key,
              {
                // eslint-disable-next-line  @typescript-eslint/naming-convention
                associated_user: {
                  id: Number(value),
                },
              },
            ];
          default:
            return [key, value];
        }
      }),
  ) as any;
 
  Object.setPrototypeOf(obj, Session.prototype);
 
  return obj;
}

const includedKeys = [
  'id',
  'shop',
  'state',
  'isOnline',
  'scope',
  'accessToken',
  'expires',
  'onlineAccessInfo',
];
export function sessionEntries(
  session: SessionInterface,
): [string, string | number][] {
  return (
    Object.entries(session)
      .filter(
        ([key, value]) =>
          includedKeys.includes(key) && value !== undefined && value !== null,
      )
      // Prepare values for db storage
      .map(([key, value]) => {
        switch (key) {
          case 'expires':
            return [
              key,
              value ? Math.floor(value.getTime() / 1000) : undefined,
            ];
          case 'onlineAccessInfo':
            return [key, value?.associated_user?.id];
          default:
            return [key, value];
        }
      })
  );
}
 
export function sessionEqual(
  sessionA: SessionInterface | undefined,
  sessionB: SessionInterface | undefined,
): boolean {
  if (!sessionA) return false;
  if (!sessionB) return false;
  const copyA = sessionEntries(sessionA);
  copyA.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));
  const copyB = sessionEntries(sessionB);
  copyB.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));
  return JSON.stringify(copyA) === JSON.stringify(copyB);
}