All files / src/tools/auth0 index.ts

90.47% Statements 38/42
100% Branches 12/12
91.66% Functions 11/12
90% Lines 36/40

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 1011x   1x 1x 1x                     78x   78x 78x   2098x   2098x 2098x   78x     1x             130x 130x 130x   130x     3770x     3770x   3770x   116x     3764x   3764x   145x           78x   2176x 2176x 2176x                         117x 117x 163x 163x 117x 117x 45x     72x         6x              
import Ajv from 'ajv/lib/ajv';
 
import pagedClient from './client';
import schema from './schema';
import handlers from './handlers';
 
import { Assets, AssetTypes, Auth0APIClient, BaseAuth0APIClient } from '../../types';
import APIHandler from './handlers/default';
import { ConfigFunction } from '../../configFactory';
 
export type Stage = 'load' | 'validate' | 'processChanges';
 
type StageFunction = APIHandler['load']; // Using `load` method as a template for what type stage functions resemble
 
function sortByOrder(toSort: APIHandler[], stage: Stage): APIHandler[] {
  const defaultOrder = 50;
 
  const sorted = [...toSort];
  sorted.sort((a, b) => {
    //@ts-ignore because this doesn't actually work. TODO: apply stage order
    const aOrder = a[stage].order || defaultOrder;
    //@ts-ignore because this doesn't actually work. TODO: apply stage order
    const bOrder = b[stage].order || defaultOrder;
    return aOrder - bOrder;
  });
  return sorted;
}
 
export default class Auth0 {
  client: Auth0APIClient;
  config: ConfigFunction;
  assets: Assets;
  handlers: APIHandler[];
 
  constructor(client: BaseAuth0APIClient, assets: Assets, config: ConfigFunction) {
    this.client = pagedClient(client);
    this.config = config;
    this.assets = assets;
 
    this.handlers = Object.values(handlers)
      .map((handler) => {
        //@ts-ignore because class expects `type` property but gets directly injected into class constructors
        return new handler.default({ client: this.client, config: this.config });
      })
      .filter((handler) => {
        const excludedAssetTypes: undefined | AssetTypes[] = config('AUTH0_EXCLUDED');
 
        if (excludedAssetTypes === undefined) return true;
 
        return !excludedAssetTypes.includes(handler.type as AssetTypes);
      })
      .filter((handler) => {
        const onlyIncludedAssetTypes: undefined | AssetTypes[] = config('AUTH0_INCLUDED_ONLY');
 
        if (onlyIncludedAssetTypes === undefined) return true;
 
        return onlyIncludedAssetTypes.includes(handler.type as AssetTypes);
      });
  }
 
  async runStage(stage: Stage): Promise<void> {
    // Sort by priority
    for (const handler of sortByOrder(this.handlers, stage)) {
      // eslint-disable-line
      try {
        const stageFn: StageFunction = Object.getPrototypeOf(handler)[stage];
        this.assets = {
          ...this.assets,
          ...((await stageFn.apply(handler, [this.assets])) || {}),
        };
      } catch (err) {
        err.type = handler.type;
        err.stage = stage;
        throw err;
      }
    }
  }
 
  async validate(): Promise<void> {
    const ajv = new Ajv({ useDefaults: true, nullable: true });
    const nonNullAssets = Object.keys(this.assets)
      .filter((k) => this.assets[k] != null)
      .reduce((a, k) => ({ ...a, [k]: this.assets[k] }), {});
    const valid = ajv.validate(schema, nonNullAssets);
    if (!valid) {
      throw new Error(`Schema validation failed loading ${JSON.stringify(ajv.errors, null, 4)}`);
    }
 
    await this.runStage('validate');
  }
 
  async loadAssetsFromAuth0(): Promise<void> {
    // Populate assets from auth0 tenant
    await this.runStage('load');
  }
 
  async processChanges(): Promise<void> {
    await this.runStage('processChanges');
  }
}