<commit_before>/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */

import { NetworkResponse } from "./NetworkManager";
import { ServerAuthorizationTokenResponse } from "../server/ServerAuthorizationTokenResponse";
import { HeaderNames, CacheSchemaType, ThrottleConstants } from "../utils/Constants";
import { CacheManager } from "../cache/CacheManager";
import { StringUtils } from "../utils/StringUtils";
import { ServerError } from "../error/ServerError";
import { RequestThumbprint } from "./RequestThumbprint";
import { RequestThumbprintValue } from "./RequestThumbprintValue";

export class ThrottlingUtils {

    /**
     * Prepares a RequestThumbprint to be stored as a key.
     * @param thumbprint
     */
    static generateThrottlingStorageKey(thumbprint: RequestThumbprint): string {
        return `${ThrottleConstants.THROTTLE_PREFIX}.${JSON.stringify(thumbprint)}`;
    }

    /**
     * Performs necessary throttling checks before a network request.
     * @param cacheManager
     * @param thumbprint 
     */
    static preProcess(cacheManager: CacheManager, thumbprint: RequestThumbprint): void {
        const key = ThrottlingUtils.generateThrottlingStorageKey(thumbprint);
        const storageValue = cacheManager.getItem(key, CacheSchemaType.THROTTLE) as string;

        if (storageValue) {
            const parsedValue = StringUtils.jsonParseHelper<RequestThumbprintValue>(storageValue);

            if (parsedValue) {
                if (parsedValue.throttleTime < Date.now()) {
                    cacheManager.removeItem(key, CacheSchemaType.THROTTLE);
                    return;
                }
                throw new ServerError(parsedValue.errorCodes.join(" "), parsedValue.errorMessage, parsedValue.subError);
            }
        }
    }

    /**
     * Performs necessary throttling checks after a network request.
     * @param cacheManager
     * @param thumbprint 
     * @param response
     */    
    static postProcess(cacheManager: CacheManager, thumbprint: RequestThumbprint, response: NetworkResponse<ServerAuthorizationTokenResponse>): void {
        if (ThrottlingUtils.checkResponseStatus(response) || ThrottlingUtils.checkResponseForRetryAfter(response)) {
            const thumbprintValue: RequestThumbprintValue = {
                throttleTime: ThrottlingUtils.calculateThrottleTime(parseInt(response.headers.get(HeaderNames.RETRY_AFTER))),
                error: response.body.error,
                errorCodes: response.body.error_codes,
                errorMessage: response.body.error_description,
                subError: response.body.suberror
            };
            cacheManager.setItem(
                ThrottlingUtils.generateThrottlingStorageKey(thumbprint),
                JSON.stringify(thumbprintValue),
                CacheSchemaType.THROTTLE
            );
        }
    }

    /**
     * Checks a NetworkResponse object's status codes against 429 or 5xx
     * @param response
     */
    static checkResponseStatus(response: NetworkResponse<ServerAuthorizationTokenResponse>): boolean {
        return response.status == 429 || response.status >= 500 && response.status < 600;
    }

    /**
     * Checks a NetworkResponse object's RetryAfter header
     * @param response
     */
    static checkResponseForRetryAfter(response: NetworkResponse<ServerAuthorizationTokenResponse>): boolean {
        if (response.headers) {
            return response.headers.has(HeaderNames.RETRY_AFTER) && (response.status < 200 || response.status >= 300);
        }
        return false;
    }
<commit_msg>Remove extra stringify<commit_after>/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */

import { NetworkResponse } from "./NetworkManager";
import { ServerAuthorizationTokenResponse } from "../server/ServerAuthorizationTokenResponse";
import { HeaderNames, CacheSchemaType, ThrottleConstants } from "../utils/Constants";
import { CacheManager } from "../cache/CacheManager";
import { StringUtils } from "../utils/StringUtils";
import { ServerError } from "../error/ServerError";
import { RequestThumbprint } from "./RequestThumbprint";
import { RequestThumbprintValue } from "./RequestThumbprintValue";

export class ThrottlingUtils {

    /**
     * Prepares a RequestThumbprint to be stored as a key.
     * @param thumbprint
     */
    static generateThrottlingStorageKey(thumbprint: RequestThumbprint): string {
        return `${ThrottleConstants.THROTTLE_PREFIX}.${JSON.stringify(thumbprint)}`;
    }

    /**
     * Performs necessary throttling checks before a network request.
     * @param cacheManager
     * @param thumbprint 
     */
    static preProcess(cacheManager: CacheManager, thumbprint: RequestThumbprint): void {
        const key = ThrottlingUtils.generateThrottlingStorageKey(thumbprint);
        const storageValue = cacheManager.getItem(key, CacheSchemaType.THROTTLE) as string;

        if (storageValue) {
            const parsedValue = StringUtils.jsonParseHelper<RequestThumbprintValue>(storageValue);

            if (parsedValue) {
                if (parsedValue.throttleTime < Date.now()) {
                    cacheManager.removeItem(key, CacheSchemaType.THROTTLE);
                    return;
                }
                throw new ServerError(parsedValue.errorCodes.join(" "), parsedValue.errorMessage, parsedValue.subError);
            }
        }
    }

    /**
     * Performs necessary throttling checks after a network request.
     * @param cacheManager
     * @param thumbprint 
     * @param response
     */    
    static postProcess(cacheManager: CacheManager, thumbprint: RequestThumbprint, response: NetworkResponse<ServerAuthorizationTokenResponse>): void {
        if (ThrottlingUtils.checkResponseStatus(response) || ThrottlingUtils.checkResponseForRetryAfter(response)) {
            const thumbprintValue: RequestThumbprintValue = {
                throttleTime: ThrottlingUtils.calculateThrottleTime(parseInt(response.headers.get(HeaderNames.RETRY_AFTER))),
                error: response.body.error,
                errorCodes: response.body.error_codes,
                errorMessage: response.body.error_description,
                subError: response.body.suberror
            };
            cacheManager.setItem(
                ThrottlingUtils.generateThrottlingStorageKey(thumbprint),
                thumbprintValue,
                CacheSchemaType.THROTTLE
            );
        }
    }

    /**
     * Checks a NetworkResponse object's status codes against 429 or 5xx
     * @param response
     */
    static checkResponseStatus(response: NetworkResponse<ServerAuthorizationTokenResponse>): boolean {
        return response.status == 429 || response.status >= 500 && response.status < 600;
    }

    /**
     * Checks a NetworkResponse object's RetryAfter header
     * @param response
     */
    static checkResponseForRetryAfter(response: NetworkResponse<ServerAuthorizationTokenResponse>): boolean {
        if (response.headers) {
            return response.headers.has(HeaderNames.RETRY_AFTER) && (response.status < 200 || response.status >= 300);
        }
        return false;
    }

{'fraction_non_alphanumeric': 0.09303928325292901, 'fraction_numerical': 0.00523776705720193, 'mean_word_length': 2.876068376068376, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3129676', 'n_tokens_mistral': 1880, 'n_tokens_neox': 1841, 'n_words': 400}