All files / src/interceptors ensure-paging-correct.interceptor.ts

21.62% Statements 8/37
0% Branches 0/25
0% Functions 0/8
19.44% Lines 7/36

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 1081x               1x 1x 1x 1x             1x 1x                                                                                                                                                                                
import {
  bind,
  Interceptor,
  InvocationContext,
  InvocationResult,
  Provider,
  ValueOrPromise,
} from '@loopback/core';
import { HttpErrors } from '@loopback/rest';
import { compact, isEmpty } from 'lodash';
import { Constants } from '@logchain/configs';
import { MAX_ID_VALUE, MIN_ID_VALUE } from './ensure-id-valid.interceptor';
 
/**
 * This class will be bound to the application as an `Interceptor` during
 * `boot`
 */
@bind({ tags: { key: EnsurePagingCorrectInterceptor.BINDING_KEY } })
export class EnsurePagingCorrectInterceptor implements Provider<Interceptor> {
  static readonly BINDING_KEY = `interceptors.${EnsurePagingCorrectInterceptor.name}`;
 
  /*
  constructor() {}
  */
 
  /**
   * This method is used by LoopBack context to produce an interceptor function
   * for the binding.
   *
   * @returns An interceptor function
   */
  value() {
    return this.intercept.bind(this);
  }
 
  /**
   * The logic to intercept an invocation
   * @param invocationCtx - Invocation context
   * @param next - A function to invoke next interceptor or the target method
   */
  async intercept(
    invocationCtx: InvocationContext,
    next: () => ValueOrPromise<InvocationResult>,
  ) {
    const { args } = invocationCtx;
 
    this.setDefaultOffset(args);
    this.setDefaultLimit(args);
    this.setDefaultOrder(args);
    this.processWhereClause(args);
 
    const result = await next();
    return result;
  }
 
  private setDefaultOffset(args: any[]) {
    if (!Number.isSafeInteger(args[0]) || args[0] < 0) {
      args[0] = 0;
    }
  }
 
  private setDefaultLimit(args: any[]) {
    if (!Number.isSafeInteger(args[1]) || args[1] > Constants.DefaultLimit) {
      args[1] = Constants.DefaultLimit;
    }
  }
 
  private setDefaultOrder(args: any[]) {
    if (!Array.isArray(args[2])) {
      args[2] = [args[2]];
    }
    args[2] = compact(args[2]);
    if (args[2].length) {
      args[2].forEach((o: string) => {
        if (!this.validateOrder(o)) {
          throw new HttpErrors.UnprocessableEntity('Invalid order: ' + args[2]);
        }
      });
    } else {
      args[2] = ['id DESC'];
    }
  }
 
  private processWhereClause(args: any[]) {
    if (!isEmpty(args[4])) {
      delete args[4].additionalProp1;
    }
 
    if (isEmpty(args[4])) {
      args[4] = {};
    } else if ('id' in args[4]) {
      const id = Number(args[4].id);
      if (isNaN(id) || id < MIN_ID_VALUE || id > MAX_ID_VALUE) {
        args[4].id = null;
      }
    }
  }
 
 
  /**
   * Validate order string
   * @param order The order
   */
  validateOrder(order: string) {
    return order.match(/^[^\s]+( (ASC|DESC))?$/);
  }
}