<NME> course_actions.js
<BEF> import {
  ADD_NOTIFICATION, API_FAIL, UPDATE_COURSE, RECEIVE_COURSE, RECEIVE_COURSE_UPDATE,
  PERSISTED_COURSE, DISMISS_SURVEY_NOTIFICATION, TOGGLE_EDITING_SYLLABUS,
  START_SYLLABUS_UPLOAD, SYLLABUS_UPLOAD_SUCCESS, LINKED_TO_SALESFORCE, COURSE_SLUG_EXISTS
} from '../constants';
import API from '../utils/api.js';
import logErrorMessage from '../utils/log_error_message';
import CourseUtils from '../utils/course_utils';

export const fetchCourse = courseSlug => (dispatch) => {
import { toWikiDomain } from '../utils/wiki_utils';

export const fetchCourse = courseSlug => (dispatch) => {
  return API.fetch(courseSlug, 'course')
    .then(data => dispatch({ type: RECEIVE_COURSE, data }))
    .catch(data => dispatch({ type: API_FAIL, data }));
};

export const refetchCourse = courseSlug => (dispatch) => {
  return API.fetch(courseSlug, 'course')
    .then(data => dispatch({ type: RECEIVE_COURSE_UPDATE, data }))
    // These periodic refetches will error if network connection is lost.
    // Missing a refetch is benign. Using `silent`, we don't clutter the
    // interface with error notifications.
    .catch(data => dispatch({ type: API_FAIL, data, silent: true }));
};

export const updateCourse = course => ({ type: UPDATE_COURSE, course });

export const resetCourse = () => (dispatch, getState) => {
  const persistedCourse = getState().persistedCourse;
  dispatch({ type: UPDATE_COURSE, course: { ...persistedCourse } });
};

export const nameHasChanged = () => (_dispatch, getState) => {
  const { course, persistedCourse } = getState();
  if (course.title !== persistedCourse.title) { return true; }
  if (course.term !== persistedCourse.term) { return true; }
  if (course.school !== persistedCourse.school) { return true; }
  return false;
};

const redirectCourse = (newSlug) => {
  if (!newSlug) { return; }
  window.location = `/courses/${newSlug}`;
};

const persistAndRedirect = (course, courseSlug, newSlug, dispatch) => {
  return API.saveCourse({ course }, courseSlug)
    .then(resp => dispatch({ type: PERSISTED_COURSE, data: resp }))
    .then(() => redirectCourse(newSlug))
    .catch(data => dispatch({ type: API_FAIL, data }));
};

export const persistCourse = (courseSlug = null, redirect = false) => (dispatch, getState) => {
  let course = getState().course;

  let newSlug;
  if (redirect) {
    course = CourseUtils.cleanupCourseSlugComponents(course);
    newSlug = CourseUtils.generateTempId(course);
    course.slug = newSlug;
  }
  return persistAndRedirect(course, courseSlug, newSlug, dispatch);
};

export const updateClonedCourse = (course, courseSlug, newSlug) => (dispatch) => {
  // Ensure course name is unique
  return API.fetch(newSlug, 'check')
    .then((resp) => {
      // Course name is all good, so save it.
      if (!resp.course_exists) {
        return persistAndRedirect(course, courseSlug, newSlug, dispatch);
      }
      // Course name is taken, so show a warning.
    .catch(data => ({ type: API_FAIL, data }));
};

const needsUpdatePromise = (courseSlug) => {
  return fetch(`/courses/${courseSlug}/needs_update.json`)
     .then((response) => {
       return response.json();
     })
     .catch((err) => {
       logErrorMessage(err);
       return err;
     });
};

const needsUpdateNotification = (response) => {
  return {
  .catch((err) => {
      return err;
  });
};

const needsUpdateNotification = (response) => {
  return {
    message: response.result,
    closable: true,
    type: 'success'
  };
};

export function needsUpdate(courseSlug) {
  return function (dispatch) {
    return needsUpdatePromise(courseSlug)
      .then(resp => dispatch({ type: ADD_NOTIFICATION, notification: needsUpdateNotification(resp) }))
      .catch(data => dispatch({ type: API_FAIL, data }));
  };
}

export const dismissNotification = id => (dispatch) => {
  return API.dismissNotification(id)
    .then(() => dispatch({ type: DISMISS_SURVEY_NOTIFICATION, id }))
    .catch(data => dispatch({ type: API_FAIL, data }));
};

export const toggleEditingSyllabus = () => {
  return { type: TOGGLE_EDITING_SYLLABUS };
};

export const uploadSyllabus = payload => (dispatch) => {
  dispatch({ type: START_SYLLABUS_UPLOAD });
  return API.uploadSyllabus(payload)
    .then(data => dispatch({ type: SYLLABUS_UPLOAD_SUCCESS, syllabus: data.url }))
    .catch(data => dispatch({ type: API_FAIL, data }));
};

export const linkToSalesforce = (courseId, salesforceId) => (dispatch) => {
  return API.linkToSalesforce(courseId, salesforceId)
    .then(data => dispatch({ type: LINKED_TO_SALESFORCE, data }))
    .catch(data => dispatch({ type: API_FAIL, data }));
};

// Actions not handled by redux, except for failures

export const updateSalesforceRecord = courseId => (dispatch) => {
  return API.updateSalesforceRecord(courseId)
    .then(data => dispatch({ type: 'UPDATED_SALESFORCE_RECORD', data }))
    .catch(data => dispatch({ type: API_FAIL, data }));
};

export const deleteCourse = courseSlug => (dispatch) => {
  return API.deleteCourse(courseSlug)
    .then(data => dispatch({ type: 'DELETED_COURSE', data }))
    .catch(data => dispatch({ type: API_FAIL, data }));
};

export const notifyOverdue = courseSlug => (dispatch) => {
  return API.notifyOverdue(courseSlug)
    .then(data => dispatch({ type: 'NOTIFIED_OVERDUE', data }))
    .catch(data => dispatch({ type: API_FAIL, data }));
};

export const greetStudents = courseId => (dispatch) => {
  return API.greetStudents(courseId)
    .then(data => dispatch({ type: 'GREETED_STUDENTS', data }))
    .catch(data => ({ type: API_FAIL, data }));
};


export const searchPrograms = searchQuery => async (dispatch) => {
  dispatch({ type: FETCH_COURSE_SEARCH_RESULTS });
  const response = await request(`/courses/search.json?search=${searchQuery}`);
  if (!response.ok) {
    const data = await response.text();
    return dispatch({ type: API_FAIL, data });
  }
  const data = await response.json();
  return dispatch({ type: RECEIVE_COURSE_SEARCH_RESULTS, data });
};

export const sortCourseSearchResults = key => ({ type: SORT_COURSE_SEARCH_RESULTS, key: key });

// this fetches the active courses of a particular campaign(which currently is just the default campaign)
export const fetchActiveCampaignCourses = campaign_slug => async (dispatch) => {
  const response = await request(`/campaigns/${campaign_slug}/active_courses.json`);
  if (!response.ok) {
    const data = await response.text();
    return dispatch({ type: API_FAIL, data });
  }
  const data = await response.json();
  return dispatch({ type: RECEIVE_CAMPAIGN_ACTIVE_COURSES, data });
};

export const sortActiveCourses = key => ({ type: SORT_ACTIVE_COURSES, key: key });

// this fetches the active courses across all campaigns
export const fetchActiveCourses = () => async (dispatch) => {
  const response = await request('/active_courses.json');
  if (!response.ok) {
    const data = await response.text();
    return dispatch({ type: API_FAIL, data });
  }
  const data = await response.json();
  return dispatch({ type: RECEIVE_ACTIVE_COURSES, data });
};

export const fetchCoursesFromWiki = wiki => async (dispatch) => {
  const wiki_url = toWikiDomain(wiki);
  const response = await request(`/courses_by_wiki/${wiki_url}.json`);
  if (!response.ok) {
    const data = await response.text();
    return dispatch({ type: API_FAIL, data });
  }
  const data = await response.json();
  return dispatch({ type: RECEIVE_WIKI_COURSES, data });
};

export const sortWikiCourses = key => ({ type: SORT_WIKI_COURSES, key: key });

<MSG> ajax converted to fetch api

<DFF> @@ -4,7 +4,6 @@ import {
   START_SYLLABUS_UPLOAD, SYLLABUS_UPLOAD_SUCCESS, LINKED_TO_SALESFORCE, COURSE_SLUG_EXISTS
 } from '../constants';
 import API from '../utils/api.js';
-import logErrorMessage from '../utils/log_error_message';
 import CourseUtils from '../utils/course_utils';
 
 export const fetchCourse = courseSlug => (dispatch) => {
@@ -76,16 +75,11 @@ export const updateClonedCourse = (course, courseSlug, newSlug) => (dispatch) =>
     .catch(data => ({ type: API_FAIL, data }));
 };
 
-const needsUpdatePromise = (courseSlug) => {
-  return fetch(`/courses/${courseSlug}/needs_update.json`)
-     .then((response) => {
-       return response.json();
-     })
-     .catch((err) => {
-       logErrorMessage(err);
-       return err;
-     });
-};
+const needsUpdatePromise = (courseSlug,dispatch) => {
+  return API.fetch(courseSlug,'needs_update')
+  .then(data => dispatch({ type: RECEIVE_COURSE_UPDATE, data }))
+  .catch(data => dispatch({ type: API_FAIL, data, silent: true }))
+}
 
 const needsUpdateNotification = (response) => {
   return {

{'commit': "diff --git a/app/assets/javascripts/actions/course_actions.js b/app/assets/javascripts/actions/course_actions.js\nindex 89babc7bb..2b396b0a9 100644\n--- a/app/assets/javascripts/actions/course_actions.js\n+++ b/app/assets/javascripts/actions/course_actions.js\n@@ -4,7 +4,6 @@ import {\n   START_SYLLABUS_UPLOAD, SYLLABUS_UPLOAD_SUCCESS, LINKED_TO_SALESFORCE, COURSE_SLUG_EXISTS\n } from '../constants';\n import API from '../utils/api.js';\n-import logErrorMessage from '../utils/log_error_message';\n import CourseUtils from '../utils/course_utils';\n \n export const fetchCourse = courseSlug => (dispatch) => {\n@@ -76,16 +75,11 @@ export const updateClonedCourse = (course, courseSlug, newSlug) => (dispatch) =>\n     .catch(data => ({ type: API_FAIL, data }));\n };\n \n-const needsUpdatePromise = (courseSlug) => {\n-  return fetch(`/courses/${courseSlug}/needs_update.json`)\n-     .then((response) => {\n-       return response.json();\n-     })\n-     .catch((err) => {\n-       logErrorMessage(err);\n-       return err;\n-     });\n-};\n+const needsUpdatePromise = (courseSlug,dispatch) => {\n+  return API.fetch(courseSlug,'needs_update')\n+  .then(data => dispatch({ type: RECEIVE_COURSE_UPDATE, data }))\n+  .catch(data => dispatch({ type: API_FAIL, data, silent: true }))\n+}\n \n const needsUpdateNotification = (response) => {\n   return {\n", 'commit_subject': 'ajax converted to fetch api', 'repo_name': 'WikiEducationFoundation/WikiEduDashboard', 'committer': {'name': 'Sage Ross', 'email': '0bc9f67b2b08dbd979104ac630343507d3de0edf@gmail.com', 'time_sec': '1554836650', 'tz_offset': '-420', 'date': {'seconds': '1554836650'}}, 'commit_tree': 'b821ae9b81fba756edb51ada56f4e8b0353cbdaa', 'commit_message': 'ajax converted to fetch api\n', 'language_name': 'Python', 'license': 'mit', 'hunks': "@@ -4,7 +4,6 @@ import {\n   START_SYLLABUS_UPLOAD, SYLLABUS_UPLOAD_SUCCESS, LINKED_TO_SALESFORCE, COURSE_SLUG_EXISTS\n } from '../constants';\n import API from '../utils/api.js';\n-import logErrorMessage from '../utils/log_error_message';\n import CourseUtils from '../utils/course_utils';\n \n export const fetchCourse = courseSlug => (dispatch) => {\n@@ -76,16 +75,11 @@ export const updateClonedCourse = (course, courseSlug, newSlug) => (dispatch) =>\n     .catch(data => ({ type: API_FAIL, data }));\n };\n \n-const needsUpdatePromise = (courseSlug) => {\n-  return fetch(`/courses/${courseSlug}/needs_update.json`)\n-     .then((response) => {\n-       return response.json();\n-     })\n-     .catch((err) => {\n-       logErrorMessage(err);\n-       return err;\n-     });\n-};\n+const needsUpdatePromise = (courseSlug,dispatch) => {\n+  return API.fetch(courseSlug,'needs_update')\n+  .then(data => dispatch({ type: RECEIVE_COURSE_UPDATE, data }))\n+  .catch(data => dispatch({ type: API_FAIL, data, silent: true }))\n+}\n \n const needsUpdateNotification = (response) => {\n   return {\n", 'addition_count': 5, 'deletion_count': 11, 'src_file': 'a/app/assets/javascripts/actions/course_actions.js', 'tgt_file': 'b/app/assets/javascripts/actions/course_actions.js', 'file_extension': '.js', 'before_file': "import {\n  ADD_NOTIFICATION, API_FAIL, UPDATE_COURSE, RECEIVE_COURSE, RECEIVE_COURSE_UPDATE,\n  PERSISTED_COURSE, DISMISS_SURVEY_NOTIFICATION, TOGGLE_EDITING_SYLLABUS,\n  START_SYLLABUS_UPLOAD, SYLLABUS_UPLOAD_SUCCESS, LINKED_TO_SALESFORCE, COURSE_SLUG_EXISTS\n} from '../constants';\nimport API from '../utils/api.js';\nimport logErrorMessage from '../utils/log_error_message';\nimport CourseUtils from '../utils/course_utils';\n\nexport const fetchCourse = courseSlug => (dispatch) => {\nimport { toWikiDomain } from '../utils/wiki_utils';\n\nexport const fetchCourse = courseSlug => (dispatch) => {\n  return API.fetch(courseSlug, 'course')\n    .then(data => dispatch({ type: RECEIVE_COURSE, data }))\n    .catch(data => dispatch({ type: API_FAIL, data }));\n};\n\nexport const refetchCourse = courseSlug => (dispatch) => {\n  return API.fetch(courseSlug, 'course')\n    .then(data => dispatch({ type: RECEIVE_COURSE_UPDATE, data }))\n    // These periodic refetches will error if network connection is lost.\n    // Missing a refetch is benign. Using `silent`, we don't clutter the\n    // interface with error notifications.\n    .catch(data => dispatch({ type: API_FAIL, data, silent: true }));\n};\n\nexport const updateCourse = course => ({ type: UPDATE_COURSE, course });\n\nexport const resetCourse = () => (dispatch, getState) => {\n  const persistedCourse = getState().persistedCourse;\n  dispatch({ type: UPDATE_COURSE, course: { ...persistedCourse } });\n};\n\nexport const nameHasChanged = () => (_dispatch, getState) => {\n  const { course, persistedCourse } = getState();\n  if (course.title !== persistedCourse.title) { return true; }\n  if (course.term !== persistedCourse.term) { return true; }\n  if (course.school !== persistedCourse.school) { return true; }\n  return false;\n};\n\nconst redirectCourse = (newSlug) => {\n  if (!newSlug) { return; }\n  window.location = `/courses/${newSlug}`;\n};\n\nconst persistAndRedirect = (course, courseSlug, newSlug, dispatch) => {\n  return API.saveCourse({ course }, courseSlug)\n    .then(resp => dispatch({ type: PERSISTED_COURSE, data: resp }))\n    .then(() => redirectCourse(newSlug))\n    .catch(data => dispatch({ type: API_FAIL, data }));\n};\n\nexport const persistCourse = (courseSlug = null, redirect = false) => (dispatch, getState) => {\n  let course = getState().course;\n\n  let newSlug;\n  if (redirect) {\n    course = CourseUtils.cleanupCourseSlugComponents(course);\n    newSlug = CourseUtils.generateTempId(course);\n    course.slug = newSlug;\n  }\n  return persistAndRedirect(course, courseSlug, newSlug, dispatch);\n};\n\nexport const updateClonedCourse = (course, courseSlug, newSlug) => (dispatch) => {\n  // Ensure course name is unique\n  return API.fetch(newSlug, 'check')\n    .then((resp) => {\n      // Course name is all good, so save it.\n      if (!resp.course_exists) {\n        return persistAndRedirect(course, courseSlug, newSlug, dispatch);\n      }\n      // Course name is taken, so show a warning.\n    .catch(data => ({ type: API_FAIL, data }));\n};\n\nconst needsUpdatePromise = (courseSlug) => {\n  return fetch(`/courses/${courseSlug}/needs_update.json`)\n     .then((response) => {\n       return response.json();\n     })\n     .catch((err) => {\n       logErrorMessage(err);\n       return err;\n     });\n};\n\nconst needsUpdateNotification = (response) => {\n  return {\n  .catch((err) => {\n      return err;\n  });\n};\n\nconst needsUpdateNotification = (response) => {\n  return {\n    message: response.result,\n    closable: true,\n    type: 'success'\n  };\n};\n\nexport function needsUpdate(courseSlug) {\n  return function (dispatch) {\n    return needsUpdatePromise(courseSlug)\n      .then(resp => dispatch({ type: ADD_NOTIFICATION, notification: needsUpdateNotification(resp) }))\n      .catch(data => dispatch({ type: API_FAIL, data }));\n  };\n}\n\nexport const dismissNotification = id => (dispatch) => {\n  return API.dismissNotification(id)\n    .then(() => dispatch({ type: DISMISS_SURVEY_NOTIFICATION, id }))\n    .catch(data => dispatch({ type: API_FAIL, data }));\n};\n\nexport const toggleEditingSyllabus = () => {\n  return { type: TOGGLE_EDITING_SYLLABUS };\n};\n\nexport const uploadSyllabus = payload => (dispatch) => {\n  dispatch({ type: START_SYLLABUS_UPLOAD });\n  return API.uploadSyllabus(payload)\n    .then(data => dispatch({ type: SYLLABUS_UPLOAD_SUCCESS, syllabus: data.url }))\n    .catch(data => dispatch({ type: API_FAIL, data }));\n};\n\nexport const linkToSalesforce = (courseId, salesforceId) => (dispatch) => {\n  return API.linkToSalesforce(courseId, salesforceId)\n    .then(data => dispatch({ type: LINKED_TO_SALESFORCE, data }))\n    .catch(data => dispatch({ type: API_FAIL, data }));\n};\n\n// Actions not handled by redux, except for failures\n\nexport const updateSalesforceRecord = courseId => (dispatch) => {\n  return API.updateSalesforceRecord(courseId)\n    .then(data => dispatch({ type: 'UPDATED_SALESFORCE_RECORD', data }))\n    .catch(data => dispatch({ type: API_FAIL, data }));\n};\n\nexport const deleteCourse = courseSlug => (dispatch) => {\n  return API.deleteCourse(courseSlug)\n    .then(data => dispatch({ type: 'DELETED_COURSE', data }))\n    .catch(data => dispatch({ type: API_FAIL, data }));\n};\n\nexport const notifyOverdue = courseSlug => (dispatch) => {\n  return API.notifyOverdue(courseSlug)\n    .then(data => dispatch({ type: 'NOTIFIED_OVERDUE', data }))\n    .catch(data => dispatch({ type: API_FAIL, data }));\n};\n\nexport const greetStudents = courseId => (dispatch) => {\n  return API.greetStudents(courseId)\n    .then(data => dispatch({ type: 'GREETED_STUDENTS', data }))\n    .catch(data => ({ type: API_FAIL, data }));\n};\n\n\nexport const searchPrograms = searchQuery => async (dispatch) => {\n  dispatch({ type: FETCH_COURSE_SEARCH_RESULTS });\n  const response = await request(`/courses/search.json?search=${searchQuery}`);\n  if (!response.ok) {\n    const data = await response.text();\n    return dispatch({ type: API_FAIL, data });\n  }\n  const data = await response.json();\n  return dispatch({ type: RECEIVE_COURSE_SEARCH_RESULTS, data });\n};\n\nexport const sortCourseSearchResults = key => ({ type: SORT_COURSE_SEARCH_RESULTS, key: key });\n\n// this fetches the active courses of a particular campaign(which currently is just the default campaign)\nexport const fetchActiveCampaignCourses = campaign_slug => async (dispatch) => {\n  const response = await request(`/campaigns/${campaign_slug}/active_courses.json`);\n  if (!response.ok) {\n    const data = await response.text();\n    return dispatch({ type: API_FAIL, data });\n  }\n  const data = await response.json();\n  return dispatch({ type: RECEIVE_CAMPAIGN_ACTIVE_COURSES, data });\n};\n\nexport const sortActiveCourses = key => ({ type: SORT_ACTIVE_COURSES, key: key });\n\n// this fetches the active courses across all campaigns\nexport const fetchActiveCourses = () => async (dispatch) => {\n  const response = await request('/active_courses.json');\n  if (!response.ok) {\n    const data = await response.text();\n    return dispatch({ type: API_FAIL, data });\n  }\n  const data = await response.json();\n  return dispatch({ type: RECEIVE_ACTIVE_COURSES, data });\n};\n\nexport const fetchCoursesFromWiki = wiki => async (dispatch) => {\n  const wiki_url = toWikiDomain(wiki);\n  const response = await request(`/courses_by_wiki/${wiki_url}.json`);\n  if (!response.ok) {\n    const data = await response.text();\n    return dispatch({ type: API_FAIL, data });\n  }\n  const data = await response.json();\n  return dispatch({ type: RECEIVE_WIKI_COURSES, data });\n};\n\nexport const sortWikiCourses = key => ({ type: SORT_WIKI_COURSES, key: key });\n", 'message': 'ajax converted to fetch api\n', 'author_name': 'Sage Ross', 'lang': 'js'}