All files / src/clients/rest rest_client.ts

98.11% Statements 52/53
87.5% Branches 21/24
100% Functions 5/5
98% Lines 49/50

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 933x   3x 3x 3x   3x       3x 3x   15x 15x   15x 1x       3x 17x   17x     17x   17x   17x 17x 10x       10x 10x   18x 18x 18x       18x 18x 18x 18x   18x 10x     18x 18x   9x 9x 9x   9x 9x 9x           10x     17x     3x 17x     3x 18x 18x 18x 18x         3x   3x  
import querystring from 'querystring';
 
import {Context} from '../../context';
import {ShopifyHeader} from '../../base_types';
import {HttpClient} from '../http_client/http_client';
import {RequestParams, GetRequestParams} from '../http_client/types';
import * as ShopifyErrors from '../../error';
 
import {RestRequestReturn, PageInfo} from './types';
 
class RestClient extends HttpClient {
  private static LINK_HEADER_REGEXP = /<([^<]+)>; rel="([^"]+)"/;
 
  public constructor(domain: string, readonly accessToken?: string) {
    super(domain);
 
    if (!Context.IS_PRIVATE_APP && !accessToken) {
      throw new ShopifyErrors.MissingRequiredArgument('Missing access token when creating REST client');
    }
  }
 
  protected async request(params: RequestParams): Promise<RestRequestReturn> {
    params.extraHeaders = {...params.extraHeaders};
 
    params.extraHeaders[ShopifyHeader.AccessToken] = Context.IS_PRIVATE_APP
      ? Context.API_SECRET_KEY : this.accessToken as string;
 
    params.path = this.getRestPath(params.path);
 
    const ret = (await super.request(params)) as RestRequestReturn;
 
    const link = ret.headers.get('link');
    if (params.query && link !== undefined) {
      const pageInfo: PageInfo = {
        limit: params.query.limit.toString(),
      };
 
      Eif (link) {
        const links = link.split(', ');
 
        for (const link of links) {
          const parsedLink = link.match(RestClient.LINK_HEADER_REGEXP);
          Iif (!parsedLink) {
            continue;
          }
 
          const linkRel = parsedLink[2];
          const linkUrl = new URL(parsedLink[1]);
          const linkFields = linkUrl.searchParams.get('fields');
          const linkPageToken = linkUrl.searchParams.get('page_info');
 
          if (!pageInfo.fields && linkFields) {
            pageInfo.fields = linkFields.split(',');
          }
 
          Eif (linkPageToken) {
            switch (linkRel) {
              case 'previous':
                pageInfo.previousPageUrl = parsedLink[1];
                pageInfo.prevPage = this.buildRequestParams(parsedLink[1]);
                break;
              case 'next':
                pageInfo.nextPageUrl = parsedLink[1];
                pageInfo.nextPage = this.buildRequestParams(parsedLink[1]);
                break;
            }
          }
        }
      }
 
      ret.pageInfo = pageInfo;
    }
 
    return ret;
  }
 
  private getRestPath(path: string): string {
    return `/admin/api/${Context.API_VERSION}/${path}.json`;
  }
 
  private buildRequestParams(newPageUrl: string): GetRequestParams {
    const url = new URL(newPageUrl);
    const path = url.pathname.replace(/^\/admin\/api\/[^/]+\/(.*)\.json$/, '$1');
    const query = querystring.decode(url.search.replace(/^\?(.*)/, '$1')) as Record<string, string | number>;
    return {
      path,
      query,
    };
  }
}
 
export {RestClient};