<commit_before>import { merge } from 'rxjs/observable/merge';
import { empty } from 'rxjs/observable/empty';
import {
  tap,
  filter,
  map,
  ignoreElements,
  switchMap,
  catchError
} from 'rxjs/operators';
import { ofType } from 'redux-observable';
import store from 'store';
import uuid from 'uuid/v4';

import { types, onlineStatusChange, isOnlineSelector } from './';
import postUpdate$ from '../../templates/Challenges/utils/postUpdate$';

const key = 'fcc-failed-updates';

function delay(time = 0, fn) {
  return setTimeout(fn, time);
}

function failedUpdateEpic(action$, { getState }) {
  const storeUpdates = action$.pipe(
    ofType(types.updateFailed),
    tap(({ payload }) => {
      const failures = store.get(key) || [];
      payload.id = uuid();
      store.set(key, [...failures, payload]);
    }),
    map(() => onlineStatusChange(false))
  );

  const flushUpdates = action$.pipe(
    ofType(types.fetchUserComplete, types.updateComplete),
    filter(() => store.get(key)),
    filter(() => isOnlineSelector(getState())),
    tap(() => {
      const failures = store.get(key) || [];
      let delayTime = 0;
      const batch = failures.map(update => {
        delayTime += 300;
        // we stagger the updates here so we don't hammer the server
        return delay(delayTime, () =>
          postUpdate$(update)
            .pipe(
              switchMap(response => {
                if (response && response.message) {
                  // the request completed successfully
                  const failures = store.get(key) || [];
                  const newFailures = failures.filter(x => x.id !== update.id);
                  store.set(key, newFailures);
                }
                return empty();
              }),
              catchError(() => empty())
            )
            .toPromise()
        );
      });
      Promise.all(batch)
        .then(() => console.info('progress updates processed where possible'))
        .catch(err =>
          console.warn('unable to process progress updates', err.message)
        );
    }),
    ignoreElements()
  );<commit_msg>fix(response handling): Handle success responses better<commit_after>import { merge } from 'rxjs/observable/merge';
import { empty } from 'rxjs/observable/empty';
import {
  tap,
  filter,
  map,
  ignoreElements,
  switchMap,
  catchError
} from 'rxjs/operators';
import { ofType } from 'redux-observable';
import store from 'store';
import uuid from 'uuid/v4';

import { types, onlineStatusChange, isOnlineSelector } from './';
import postUpdate$ from '../../templates/Challenges/utils/postUpdate$';
import { isGoodXHRStatus } from '../../templates/Challenges/utils';

const key = 'fcc-failed-updates';

function delay(time = 0, fn) {
  return setTimeout(fn, time);
}

function failedUpdateEpic(action$, { getState }) {
  const storeUpdates = action$.pipe(
    ofType(types.updateFailed),
    tap(({ payload = {} }) => {
      if ('endpoint' in payload && 'payload' in payload) {
        const failures = store.get(key) || [];
        payload.id = uuid();
        store.set(key, [...failures, payload]);
      }
    }),
    map(() => onlineStatusChange(false))
  );

  const flushUpdates = action$.pipe(
    ofType(types.fetchUserComplete, types.updateComplete),
    filter(() => store.get(key)),
    filter(() => isOnlineSelector(getState())),
    tap(() => {
      const failures = store.get(key) || [];
      let delayTime = 0;
      const batch = failures.map(update => {
        delayTime += 300;
        // we stagger the updates here so we don't hammer the server
        return delay(delayTime, () =>
          postUpdate$(update)
            .pipe(
              switchMap(response => {
                if (
                  response &&
                  (response.message || isGoodXHRStatus(response.status))
                ) {
                  console.info(`${update.id} succeeded`);
                  // the request completed successfully
                  const failures = store.get(key) || [];
                  const newFailures = failures.filter(x => x.id !== update.id);
                  store.set(key, newFailures);
                }
                return empty();
              }),
              catchError(() => empty())
            )
            .toPromise()
        );
      });
      Promise.all(batch)
        .then(() => console.info('progress updates processed where possible'))
        .catch(err =>
          console.warn('unable to process progress updates', err.message)
        );
    }),
    ignoreElements()
  );
{'fraction_non_alphanumeric': 0.14542268944371442, 'fraction_numerical': 0.002628120893561104, 'mean_word_length': 2.2049122807017545, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 27, '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': '1365242', 'n_tokens_mistral': 1277, 'n_tokens_neox': 1249, 'n_words': 301}