<commit_before>import isArrayBuffer from 'lodash/isArrayBuffer'
import isBuffer from 'lodash/isBuffer'
import isNil from 'lodash/isNil'
import isString from 'lodash/isString'
import isTypedArray from 'lodash/isTypedArray'
import * as R from 'ramda'
import * as React from 'react'
import * as redux from 'react-redux'
import { takeEvery, call, put } from 'redux-saga/effects'

import * as Config from 'utils/Config'
import * as SagaInjector from 'utils/SagaInjector'
import defer from 'utils/defer'
import { BaseError } from 'utils/error'
import { actionCreator, createActions } from 'utils/reduxTools'

const REDUX_KEY = 'app/utils/APIConnector'

const actions = createActions(REDUX_KEY, 'API_REQUEST', 'API_RESPONSE')

/**
 * Options object for creating Requests, with some minor additions:
 *   - accepts either `endpoint` or `url` property (to keep all the required
 *     options in one object).
 *
 *   - accepts `json` property (to add appropriate Content-Type header and
 *     parse the response body).
 *
 *   - may accept arbitrary properties which can be accessed inside a
 *     middleware.
 *
 * The rest of the props are passed to the `fetch` call (identical to the
 * `Request` constructor) as they are. See for details:
 * https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
 *
 * @typedef {Object} RequestOptions
 *
 * @property {string} endpoint
 *   An endpoint for request (appended to configured base URL).
 *
 * @property {string} url
 *   An absolute URL for the request.
 *
 * @property {boolean} json
 *   Whether to add JSON-related headers and parse the response body.
 *   Defaults to true.
 */

/**
 * Action creator for API_REQUEST action.
 *
 * @param {RequestOptions|string} options
 *   When string, it is considered an endpoint.
 *
 * @param {utils/defer.Resolver} resolver
 *
 * @returns {redux.Action}
 */
const request = actionCreator(actions.API_REQUEST, (payload, resolver) => ({
  payload: typeof payload === 'string' ? { endpoint: payload } : payload,
  meta: { ...resolver },
}))

/**
 * Action creator for API_RESPONSE action.
 *
 * @param {any} response
 *
 * @params {RequestOptions} requestOpts
 *
 * @returns {redux.Action}
 */
const response = actionCreator(actions.API_RESPONSE, (payload, requestOpts) => ({
  payload,
  meta: { ...requestOpts },
}))

const test = R.ifElse(R.is(RegExp), R.test, R.equals)

export class HTTPError extends BaseError {
  static displayName = 'HTTPError'

  static is = (e, status, msg) => {
    if (!(e instanceof HTTPError)) return false
    if (status && e.status !== status) return false
    if (msg && !(e.json && test(msg)(e.json.message))) return false
    return true
  }

  constructor(resp, text, { message, status } = {}) {
    let json
    try {
      json = JSON.parse(text)
    } catch (e) {
      // ignore it
    }

    super(message || (json && (json.message || json.error)) || resp.statusText, {
      response: resp,
      status: status || resp.status,
      text,
      json,
    })
  }<commit_msg>Show text errors too (#2354)<commit_after>import isArrayBuffer from 'lodash/isArrayBuffer'
import isBuffer from 'lodash/isBuffer'
import isNil from 'lodash/isNil'
import isString from 'lodash/isString'
import isTypedArray from 'lodash/isTypedArray'
import * as R from 'ramda'
import * as React from 'react'
import * as redux from 'react-redux'
import { takeEvery, call, put } from 'redux-saga/effects'

import * as Config from 'utils/Config'
import * as SagaInjector from 'utils/SagaInjector'
import defer from 'utils/defer'
import { BaseError } from 'utils/error'
import { actionCreator, createActions } from 'utils/reduxTools'

const REDUX_KEY = 'app/utils/APIConnector'

const actions = createActions(REDUX_KEY, 'API_REQUEST', 'API_RESPONSE')

/**
 * Options object for creating Requests, with some minor additions:
 *   - accepts either `endpoint` or `url` property (to keep all the required
 *     options in one object).
 *
 *   - accepts `json` property (to add appropriate Content-Type header and
 *     parse the response body).
 *
 *   - may accept arbitrary properties which can be accessed inside a
 *     middleware.
 *
 * The rest of the props are passed to the `fetch` call (identical to the
 * `Request` constructor) as they are. See for details:
 * https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
 *
 * @typedef {Object} RequestOptions
 *
 * @property {string} endpoint
 *   An endpoint for request (appended to configured base URL).
 *
 * @property {string} url
 *   An absolute URL for the request.
 *
 * @property {boolean} json
 *   Whether to add JSON-related headers and parse the response body.
 *   Defaults to true.
 */

/**
 * Action creator for API_REQUEST action.
 *
 * @param {RequestOptions|string} options
 *   When string, it is considered an endpoint.
 *
 * @param {utils/defer.Resolver} resolver
 *
 * @returns {redux.Action}
 */
const request = actionCreator(actions.API_REQUEST, (payload, resolver) => ({
  payload: typeof payload === 'string' ? { endpoint: payload } : payload,
  meta: { ...resolver },
}))

/**
 * Action creator for API_RESPONSE action.
 *
 * @param {any} response
 *
 * @params {RequestOptions} requestOpts
 *
 * @returns {redux.Action}
 */
const response = actionCreator(actions.API_RESPONSE, (payload, requestOpts) => ({
  payload,
  meta: { ...requestOpts },
}))

const test = R.ifElse(R.is(RegExp), R.test, R.equals)

export class HTTPError extends BaseError {
  static displayName = 'HTTPError'

  static is = (e, status, msg) => {
    if (!(e instanceof HTTPError)) return false
    if (status && e.status !== status) return false
    if (msg && !(e.json && test(msg)(e.json.message))) return false
    return true
  }

  constructor(resp, text, { message, status } = {}) {
    let json
    try {
      json = JSON.parse(text)
    } catch (e) {
      json = { message: message || text }
    }

    super(message || (json && (json.message || json.error)) || resp.statusText, {
      response: resp,
      status: status || resp.status,
      text,
      json,
    })
  }
{'fraction_non_alphanumeric': 0.12425742574257426, 'fraction_numerical': 0.0006600660066006601, 'mean_word_length': 3.5640060240963853, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 2, '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': '2210383', 'n_tokens_mistral': 1871, 'n_tokens_neox': 1861, 'n_words': 608}