# All files / api common.ts

100% Statements 28/28
100% Branches 6/6
100% Functions 8/8
100% Lines 24/24

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
    110
    111
    112

    9x

    9x

    12x
    12x

    9x

    7x

    9x

    3x
    3x
    3x

    12x
    12x

    9x

    7x

    7x
    7x
    7x

    9x

    16x

    16x

    5x

    11x

    7x

    7x

     /**
     * @license
     * Copyright 2019 Google LLC
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    import { FirebaseError } from '@firebase/util';
    import { GenerateAuthTokenResponse } from '../interfaces/api-response';
    import { AppConfig } from '../interfaces/app-config';
    import {
      CompletedAuthToken,
      RegisteredInstallationEntry,
      RequestStatus
    } from '../interfaces/installation-entry';
    import {
      INSTALLATIONS_API_URL,
      INTERNAL_AUTH_VERSION
    } from '../util/constants';
    import { ERROR_FACTORY, ErrorCode } from '../util/errors';

    export function getInstallationsEndpoint({ projectId }: AppConfig): string {
      return `${INSTALLATIONS_API_URL}/projects/${projectId}/installations`;
    }

    export function extractAuthTokenInfoFromResponse(
      response: GenerateAuthTokenResponse
    ): CompletedAuthToken {
      return {
        token: response.token,
        requestStatus: RequestStatus.COMPLETED,
        expiresIn: getExpiresInFromResponseExpiresIn(response.expiresIn),
        creationTime: Date.now()
      };
    }

    export async function getErrorFromResponse(
      requestName: string,
      response: Response
    ): Promise<FirebaseError> {
      const responseJson: ErrorResponse = await response.json();
      const errorData = responseJson.error;
      return ERROR_FACTORY.create(ErrorCode.REQUEST_FAILED, {
        requestName,
        serverCode: errorData.code,
        serverMessage: errorData.message,
        serverStatus: errorData.status
      });
    }

    export function getHeaders({ apiKey }: AppConfig): Headers {
      return new Headers({
        'Content-Type': 'application/json',
        Accept: 'application/json',
        'x-goog-api-key': apiKey
      });
    }

    export function getHeadersWithAuth(
      appConfig: AppConfig,
      { refreshToken }: RegisteredInstallationEntry
    ): Headers {
      const headers = getHeaders(appConfig);
      headers.append('Authorization', getAuthorizationHeader(refreshToken));
      return headers;
    }

    export interface ErrorResponse {
      error: {
        code: number;
        message: string;
        status: string;
      };
    }

    /**
     * Calls the passed in fetch wrapper and returns the response.
     * If the returned response has a status of 5xx, re-runs the function once and
     * returns the response.
     */
    export async function retryIfServerError(
      fn: () => Promise<Response>
    ): Promise<Response> {
      const result = await fn();

      if (result.status >= 500 && result.status < 600) {
        // Internal Server Error. Retry request.
        return fn();
      }

      return result;
    }

    function getExpiresInFromResponseExpiresIn(responseExpiresIn: string): number {
      // This works because the server will never respond with fractions of a second.
      return Number(responseExpiresIn.replace('s', '000'));
    }

    function getAuthorizationHeader(refreshToken: string): string {
      return `${INTERNAL_AUTH_VERSION} ${refreshToken}`;
    }