diff --git a/jsapp/js/components/RESTServices.js b/jsapp/js/components/RESTServices.js
deleted file mode 100644
index 4e7d34c06e..0000000000
--- a/jsapp/js/components/RESTServices.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import autoBind from 'react-autobind';
-import DocumentTitle from 'react-document-title';
-import RESTServicesList from './RESTServices/RESTServicesList';
-import RESTServiceLogs from './RESTServices/RESTServiceLogs';
-import './RESTServices.scss';
-
-export default class RESTServices extends React.Component {
- constructor(props){
- super(props);
- autoBind(this);
- }
-
- render() {
- const docTitle = this.props.asset.name || t('Untitled');
- return (
-
-
- {this.props.hookUid &&
-
- }
- {!this.props.hookUid &&
-
- }
-
-
- );
- }
-}
diff --git a/jsapp/js/components/RESTServices/RESTServiceLogs.js b/jsapp/js/components/RESTServices/RESTServiceLogs.js
deleted file mode 100644
index 652854663d..0000000000
--- a/jsapp/js/components/RESTServices/RESTServiceLogs.js
+++ /dev/null
@@ -1,364 +0,0 @@
-import React from 'react';
-import autoBind from 'react-autobind';
-import reactMixin from 'react-mixin';
-import Reflux from 'reflux';
-import alertify from 'alertifyjs';
-import pageState from 'js/pageState.store';
-import bem from 'js/bem';
-import LoadingSpinner from 'js/components/common/loadingSpinner';
-import {actions} from '../../actions';
-import mixins from '../../mixins';
-import {dataInterface} from '../../dataInterface';
-import {formatTime, notify} from 'utils';
-import {
- HOOK_LOG_STATUSES,
- MODAL_TYPES
-} from '../../constants';
-import Button from 'js/components/common/button';
-
-export default class RESTServiceLogs extends React.Component {
- constructor(props){
- super(props);
- this.state = {
- hookName: null,
- isHookActive: false,
- assetUid: props.assetUid,
- hookUid: props.hookUid,
- isLoadingHook: true,
- isLoadingLogs: true,
- logs: [],
- nextPageUrl: null
- };
- autoBind(this);
- }
-
- componentDidMount() {
- this.listenTo(
- actions.hooks.getLogs.completed,
- this.onLogsUpdated
- );
-
- dataInterface.getHook(this.state.assetUid, this.state.hookUid)
- .done((data) => {
- this.setState({
- isLoadingHook: false,
- hookName: data.name,
- isHookActive: data.active
- });
- })
- .fail(() => {
- this.setState({
- isLoadingHook: false
- });
- notify.error(t('Could not load REST Service'));
- });
-
- actions.hooks.getLogs(
- this.state.assetUid,
- this.state.hookUid,
- {
- onComplete: (data) => {
- this.setState({
- isLoadingLogs: false,
- logs: data.results,
- nextPageUrl: data.next,
- totalLogsCount: data.count
- });
- },
- onFail: () => {
- this.setState({
- isLoadingLogs: false
- });
- notify.error(t('Could not load REST Service logs'));
- }
- }
- );
- }
-
- loadMore() {
- this.setState({isLoadingLogs: false});
-
- dataInterface.loadNextPageUrl(this.state.nextPageUrl)
- .done((data) => {
- const newLogs = [].concat(this.state.logs, data.results);
- this.setState({
- isLoadingLogs: false,
- logs: newLogs,
- nextPageUrl: data.next,
- totalLogsCount: data.count
- });
- })
- .fail(() => {
- this.setState({isLoadingLogs: false});
- notify.error(t('Could not load REST Service logs'));
- });
- }
-
- onLogsUpdated(data) {
- this.setState({logs: data.results});
- }
-
- retryAll() {
- const failedLogUids = [];
- this.state.logs.forEach((log) => {
- if (log.status === HOOK_LOG_STATUSES.FAILED) {
- failedLogUids.push(log.uid);
- }
- });
- this.overrideLogsStatus(failedLogUids, HOOK_LOG_STATUSES.PENDING);
-
- actions.hooks.retryLogs(
- this.state.assetUid,
- this.state.hookUid,
- {
- onComplete: (response) => {
- this.overrideLogsStatus(response.pending_uids, HOOK_LOG_STATUSES.PENDING);
- }
- }
- );
- }
-
- retryLog(log) {
- // make sure to allow only retrying failed logs
- if (log.status !== HOOK_LOG_STATUSES.FAILED) {
- return;
- }
-
- this.overrideLogsStatus([log.uid], HOOK_LOG_STATUSES.PENDING);
-
- actions.hooks.retryLog(
- this.state.assetUid,
- this.state.hookUid,
- log.uid, {
- onFail: (response) => {
- if (response.responseJSON && response.responseJSON.detail) {
- this.overrideLogMessage(log.uid, response.responseJSON.detail);
- }
- this.overrideLogsStatus([log.uid], HOOK_LOG_STATUSES.FAILED);
- }
- }
- );
- }
-
- overrideLogMessage(logUid, newMessage) {
- const currentLogs = this.state.logs;
- currentLogs.forEach((currentLog) => {
- if (currentLog.uid === logUid) {
- currentLog.message = newMessage;
- }
- });
- this.setState({
- logs: currentLogs
- });
- }
-
- // useful to mark logs as pending, before BE tells about it
- // NOTE: logUids is an array
- overrideLogsStatus(logUids, newStatus) {
- const currentLogs = this.state.logs;
- currentLogs.forEach((currentLog) => {
- if (logUids.includes(currentLog.uid)) {
- currentLog.status = newStatus;
- }
- });
- this.setState({
- logs: currentLogs
- });
- }
-
- showLogInfo(log) {
- const title = t('Submission Failure Detail (##id##)').replace('##id##', log.submission_id);
- const escapedMessage = $('
').text(log.message).html();
- alertify.alert(title, `${escapedMessage}
`);
- }
-
- openSubmissionModal(log) {
- const currentAsset = this.currentAsset();
- pageState.switchModal({
- type: MODAL_TYPES.SUBMISSION,
- sid: log.submission_id,
- asset: currentAsset,
- ids: [log.submission_id]
- });
- }
-
- hasAnyFailedLogs() {
- let hasAny = false;
- this.state.logs.forEach((log) => {
- if (log.status === HOOK_LOG_STATUSES.FAILED) {
- hasAny = true;
- }
- });
- return hasAny;
- }
-
- hasInfoToDisplay(log) {
- return log.status !== HOOK_LOG_STATUSES.SUCCESS && log.message.length > 0;
- }
-
- /*
- * rendering methods
- */
-
- renderHeader() {
- return (
-
-
- );
- }
-
- renderLoadMoreButton() {
- if (this.state.nextPageUrl === null) {
- return null;
- }
-
- return (
-
- );
- }
-
- renderEmptyView() {
- return (
-
-
- {this.renderHeader()}
-
-
-
- {t('There are no logs yet')}
-
-
-
-
- );
- }
-
- renderListView() {
- return (
-
-
- {this.renderHeader()}
-
-
-
- {t('Submission')}
-
- {t('Status')}
- { this.hasAnyFailedLogs() &&
-
- }
-
- {t('Date')}
-
-
- {this.state.logs.map((log, n) => {
- const rowProps = {
- key: n
- };
- let statusMod = '';
- let statusLabel = '';
- if (log.status === HOOK_LOG_STATUSES.SUCCESS) {
- statusMod = 'success';
- statusLabel = t('Success');
- rowProps.m = 'clickable';
- rowProps.onClick = this.openSubmissionModal.bind(this, log);
- }
- if (log.status === HOOK_LOG_STATUSES.PENDING) {
- statusMod = 'pending';
- statusLabel = t('Pending');
-
- if (log.tries && log.tries > 1) {
- statusLabel = t('Pending (##count##×)').replace('##count##', log.tries);
- }
- }
- if (log.status === HOOK_LOG_STATUSES.FAILED) {
- statusMod = 'failed';
- statusLabel = t('Failed');
- }
-
- return (
-
-
- {log.submission_id}
-
-
-
- {statusLabel}
-
- {log.status === HOOK_LOG_STATUSES.FAILED &&
-
- }
-
- {this.hasInfoToDisplay(log) &&
-
- }
-
-
-
- {formatTime(log.date_modified)}
-
-
- );
- })}
-
-
- {this.renderLoadMoreButton()}
-
-
- );
- }
-
- render() {
- if (this.state.isLoadingHook || (this.state.isLoadingLogs && this.state.logs.length === 0)) {
- return ();
- } else if (this.state.logs.length === 0) {
- return this.renderEmptyView();
- } else {
- return this.renderListView();
- }
- }
-}
-
-reactMixin(RESTServiceLogs.prototype, Reflux.ListenerMixin);
-reactMixin(RESTServiceLogs.prototype, mixins.contextRouter);
diff --git a/jsapp/js/components/RESTServices/RESTServicesForm.js b/jsapp/js/components/RESTServices/RESTServicesForm.js
deleted file mode 100644
index ead92135dc..0000000000
--- a/jsapp/js/components/RESTServices/RESTServicesForm.js
+++ /dev/null
@@ -1,538 +0,0 @@
-import React from 'react';
-import autoBind from 'react-autobind';
-import KoboTagsInput from 'js/components/common/koboTagsInput';
-import bem from 'js/bem';
-import LoadingSpinner from 'js/components/common/loadingSpinner';
-import {dataInterface} from '../../dataInterface';
-import {actions} from '../../actions';
-import WrappedSelect from 'js/components/common/wrappedSelect';
-import Checkbox from 'js/components/common/checkbox';
-import Radio from 'js/components/common/radio';
-import TextBox from 'js/components/common/textBox';
-import {KEY_CODES} from 'js/constants';
-import envStore from 'js/envStore';
-import {notify} from 'js/utils';
-import pageState from 'js/pageState.store';
-import Button from 'js/components/common/button';
-
-const EXPORT_TYPES = {
- json: {
- value: 'json',
- label: t('JSON')
- },
- xml: {
- value: 'xml',
- label: t('XML')
- }
-};
-
-const AUTH_OPTIONS = {
- no_auth: {
- value: 'no_auth',
- label: t('No Authorization')
- },
- basic_auth: {
- value: 'basic_auth',
- label: t('Basic Authorization')
- }
-};
-
-export default class RESTServicesForm extends React.Component {
- constructor(props){
- super(props);
- this.state = {
- isLoadingHook: true,
- isSubmitPending: false,
- assetUid: props.assetUid,
- // will be empty if creating new service
- hookUid: props.hookUid,
- name: '',
- nameError: null,
- endpoint: '',
- endpointError: null,
- type: EXPORT_TYPES.json.value,
- typeOptions: [
- EXPORT_TYPES.json,
- EXPORT_TYPES.xml
- ],
- isActive: true,
- emailNotification: true,
- authLevel: null,
- authOptions: [
- AUTH_OPTIONS.no_auth,
- AUTH_OPTIONS.basic_auth
- ],
- authUsername: '',
- authPassword: '',
- subsetFields: [],
- customHeaders: [
- this.getEmptyHeaderRow()
- ],
- payloadTemplate: '',
- payloadTemplateErrors: []
- };
- autoBind(this);
- }
-
- componentDidMount() {
- if (this.state.hookUid) {
- dataInterface.getHook(this.state.assetUid, this.state.hookUid)
- .done((data) => {
- const stateUpdate = {
- isLoadingHook: false,
- name: data.name,
- endpoint: data.endpoint,
- isActive: data.active,
- emailNotification: data.email_notification,
- subsetFields: data.subset_fields || [],
- type: data.export_type,
- authLevel: AUTH_OPTIONS[data.auth_level] || null,
- customHeaders: this.headersObjToArr(data.settings.custom_headers),
- payloadTemplate: data.payload_template
- };
-
- if (stateUpdate.customHeaders.length === 0) {
- stateUpdate.customHeaders.push(this.getEmptyHeaderRow());
- }
- if (data.settings.username) {
- stateUpdate.authUsername = data.settings.username;
- }
- if (data.settings.password) {
- stateUpdate.authPassword = data.settings.password;
- }
-
- this.setState(stateUpdate);
- })
- .fail(() => {
- this.setState({isSubmitPending: false});
- notify.error(t('Could not load REST Service'));
- });
- } else {
- this.setState({isLoadingHook: false});
- }
- }
-
- /*
- * helpers
- */
-
- getEmptyHeaderRow() {
- return {name: '', value: ''};
- }
-
- headersObjToArr(headersObj) {
- const headersArr = [];
- for (let header in headersObj) {
- if (headersObj.hasOwnProperty(header)) {
- headersArr.push({
- name: header,
- value: headersObj[header]
- });
- }
- }
- return headersArr;
- }
-
- headersArrToObj(headersArr) {
- const headersObj = {};
- for (const header of headersArr) {
- if (header.name) {
- headersObj[header.name] = header.value;
- }
- }
- return headersObj;
- }
-
- /*
- * user input handling
- */
-
- handleNameChange(newName) {
- this.setState({
- name: newName,
- nameError: null
- });
- }
-
- handleEndpointChange(newEndpoint) {
- this.setState({
- endpoint: newEndpoint,
- endpointError: null
- });
- }
-
- handleAuthTypeChange(evt) {this.setState({authLevel: evt});}
-
- handleAuthUsernameChange(newUsername) {this.setState({authUsername: newUsername});}
-
- handleAuthPasswordChange(newPassword) {this.setState({authPassword: newPassword});}
-
- handleActiveChange(isChecked) {this.setState({isActive: isChecked});}
-
- handleEmailNotificationChange(isChecked) {
- this.setState({emailNotification: isChecked});
- }
-
- handleTypeRadioChange(value, name) {this.setState({[name]: value});}
-
- handleCustomHeaderChange(evt) {
- const propName = evt.target.name;
- const propValue = evt.target.value;
- const index = evt.target.dataset.index;
- const newCustomHeaders = this.state.customHeaders;
- if (propName === 'headerName') {
- newCustomHeaders[index].name = propValue;
- }
- if (propName === 'headerValue') {
- newCustomHeaders[index].value = propValue;
- }
- this.setState({customHeaders: newCustomHeaders});
- }
-
- handleCustomWrapperChange(newVal) {
- this.setState({
- payloadTemplate: newVal,
- payloadTemplateErrors: []
- });
- }
-
- /*
- * submitting form
- */
-
- getDataForBackend() {
- let authLevel = AUTH_OPTIONS.no_auth.value;
- if (this.state.authLevel !== null) {
- authLevel = this.state.authLevel.value;
- }
-
- const data = {
- name: this.state.name,
- endpoint: this.state.endpoint,
- active: this.state.isActive,
- subset_fields: this.state.subsetFields,
- email_notification: this.state.emailNotification,
- export_type: this.state.type,
- auth_level: authLevel,
- settings: {
- custom_headers: this.headersArrToObj(this.state.customHeaders)
- },
- payload_template: this.state.payloadTemplate
- };
-
- if (this.state.authUsername) {
- data.settings.username = this.state.authUsername;
- }
- if (this.state.authPassword) {
- data.settings.password = this.state.authPassword;
- }
- return data;
- }
-
- validateForm() {
- let isValid = true;
- if (this.state.name.trim() === '') {
- this.setState({nameError: t('Name required')});
- isValid = false;
- }
- if (this.state.endpoint.trim() === '') {
- this.setState({endpointError: t('URL required')});
- isValid = false;
- }
- return isValid;
- }
-
- onSubmit(evt) {
- evt.preventDefault();
-
- if (!this.validateForm()) {
- notify.error(t('Please enter both name and url of your service.'));
- return;
- }
-
- const callbacks = {
- onComplete: () => {
- pageState.hideModal();
- actions.resources.loadAsset({id: this.state.assetUid});
- },
- onFail: (data) => {
- let payloadTemplateErrors = [];
- if (
- data.responseJSON &&
- data.responseJSON.payload_template &&
- data.responseJSON.payload_template.length !== 0
- ) {
- payloadTemplateErrors = data.responseJSON.payload_template;
- }
- this.setState({
- payloadTemplateErrors: payloadTemplateErrors,
- isSubmitPending: false
- });
- },
- };
-
- this.setState({isSubmitPending: true});
- if (this.state.hookUid) {
- actions.hooks.update(
- this.state.assetUid,
- this.state.hookUid,
- this.getDataForBackend(),
- callbacks
- );
- } else {
- actions.hooks.add(
- this.state.assetUid,
- this.getDataForBackend(),
- callbacks
- );
- }
- return false;
- }
-
- /*
- * handle custom headers
- */
-
- onCustomHeaderInputKeyPress(evt) {
- if (evt.keyCode === KEY_CODES.ENTER && evt.currentTarget.name === 'headerName') {
- evt.preventDefault();
- $(evt.currentTarget).parent().find('input[name="headerValue"]').focus();
- }
- if (evt.keyCode === KEY_CODES.ENTER && evt.currentTarget.name === 'headerValue') {
- evt.preventDefault();
- this.addNewCustomHeaderRow();
- }
- }
-
- addNewCustomHeaderRow(evt) {
- if (evt) {
- evt.preventDefault();
- }
- const newCustomHeaders = this.state.customHeaders;
- newCustomHeaders.push(this.getEmptyHeaderRow());
- this.setState({customHeaders: newCustomHeaders});
- setTimeout(() => {
- $('input[name="headerName"]').last().focus();
- }, 0);
- }
-
- removeCustomHeaderRow(evt) {
- evt.preventDefault();
- const newCustomHeaders = this.state.customHeaders;
- const rowIndex = evt.currentTarget.dataset.index;
- newCustomHeaders.splice(rowIndex, 1);
- if (newCustomHeaders.length === 0) {
- newCustomHeaders.push(this.getEmptyHeaderRow());
- }
- this.setState({customHeaders: newCustomHeaders});
- }
-
- renderCustomHeaders() {
- return (
-
-
-
- {this.state.customHeaders.map((item, n) => {
- // TODO change these inputs into ``es, make sure that that
- // weird onChange handling is turned into something less confusing
- return (
-
-
-
-
-
-
-
- );
- })}
-
-
-
- );
- }
-
- /*
- * handle fields
- */
-
- onSubsetFieldsChange(newValue) {
- this.setState({subsetFields: newValue.split(',')});
- }
-
- renderFieldsSelector() {
- return (
-
-
-
- );
- }
-
- /*
- * rendering
- */
-
- render() {
- const isEditingExistingHook = Boolean(this.state.hookUid);
-
- if (this.state.isLoadingHook) {
- return ();
- } else {
- let submissionPlaceholder = '%SUBMISSION%';
- if (envStore.isReady && envStore.data.submission_placeholder) {
- submissionPlaceholder = envStore.data.submission_placeholder;
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {this.state.authLevel && this.state.authLevel.value === AUTH_OPTIONS.basic_auth.value &&
-
-
-
-
-
- }
-
- {this.renderFieldsSelector()}
-
- {this.renderCustomHeaders()}
-
- {this.state.type === EXPORT_TYPES.json.value &&
-
-
-
- }
-
-
-
-
-
-
- );
- }
- }
-}
diff --git a/jsapp/js/components/RESTServices/RESTServicesList.js b/jsapp/js/components/RESTServices/RESTServicesList.js
deleted file mode 100644
index 01e2cafa26..0000000000
--- a/jsapp/js/components/RESTServices/RESTServicesList.js
+++ /dev/null
@@ -1,223 +0,0 @@
-import React from 'react';
-import autoBind from 'react-autobind';
-import reactMixin from 'react-mixin';
-import Reflux from 'reflux';
-import alertify from 'alertifyjs';
-import {actions} from '../../actions';
-import bem from 'js/bem';
-import LoadingSpinner from 'js/components/common/loadingSpinner';
-import {MODAL_TYPES} from '../../constants';
-import envStore from 'js/envStore';
-import {
- notify,
- escapeHtml,
-} from 'js/utils';
-import pageState from 'js/pageState.store';
-import Button from 'js/components/common/button';
-
-const REST_SERVICES_SUPPORT_URL = 'rest_services.html';
-
-export default class RESTServicesList extends React.Component {
- constructor(props){
- super(props);
- this.state = {
- isLoadingHooks: true,
- assetUid: props.assetUid,
- hooks: []
- };
- autoBind(this);
- }
-
- componentDidMount() {
- this.listenTo(
- actions.hooks.getAll.completed,
- this.onHooksUpdate
- );
-
- actions.hooks.getAll(
- this.state.assetUid,
- {
- onComplete: (data) => {
- this.setState({
- isLoadingHooks: false,
- hooks: data.results
- });
- },
- onFail: (data) => {
- this.setState({
- isLoadingHooks: false
- });
- notify.error(t('Could not load REST Services'));
- }
- }
- );
- }
-
- onHooksUpdate(data) {
- this.setState({
- isLoadingHooks: false,
- hooks: data.results
- })
- }
-
- editHook(hookUid) {
- pageState.showModal({
- assetUid: this.state.assetUid,
- type: MODAL_TYPES.REST_SERVICES,
- hookUid: hookUid
- });
- }
-
- deleteHookSafe(hookUid, hookName) {
- if (this.state.assetUid) {
- const dialog = alertify.dialog('confirm');
- const title = t('Are you sure you want to delete ##target?')
- .replace('##target', escapeHtml(hookName));
- const message = t('You are about to delete ##target. This action cannot be undone.')
- .replace('##target', `${escapeHtml(hookName)}`);
- let dialogOptions = {
- title: title,
- message: message,
- labels: { ok: t('Confirm'), cancel: t('Cancel') },
- onok: () => {
- actions.hooks.delete(this.state.assetUid, hookUid);
- },
- oncancel: () => {
- dialog.destroy();
- }
- };
- dialog.set(dialogOptions).show();
- }
- }
-
- openNewRESTServiceModal() {
- pageState.showModal({
- assetUid: this.state.assetUid,
- // hookUid: not provided intentionally
- type: MODAL_TYPES.REST_SERVICES
- });
- }
-
- getSupportUrl() {
- if (envStore.isReady && envStore.data.support_url) {
- return envStore.data.support_url + REST_SERVICES_SUPPORT_URL;
- }
- }
-
- renderModalButton() {
- return (
-
- );
- }
-
- renderEmptyView() {
- return (
-
-
-
-
-
- {t("This project doesn't have any REST Services yet!")}
-
-
-
- {t('You can use REST Services to automatically post submissions to a third-party application.')}
-
- {t('Learn more')}
-
-
- {this.renderModalButton()}
-
-
- );
- }
-
- renderListView() {
- return (
-
-
-
-
- {t('REST Services: ##number##').replace('##number##', this.state.hooks.length)}
-
-
-
-
- {t('Need help?')}
-
-
-
-
-
- {t('Service Name')}
- {t('Success')}
- {t('Pending')}
- {t('Failed')}
-
-
-
- {this.state.hooks.map((hook) => {
- const logsUrl = `/#/forms/${this.state.assetUid}/settings/rest/${hook.uid}`;
- return (
-
-
-
- {hook.name}
-
- {hook.success_count}
-
- {hook.pending_count}
-
- {hook.failed_count}
-
-
-
-
- );
- })}
-
-
-
- {this.renderModalButton()}
-
- );
- }
-
- render() {
- if (this.state.isLoadingHooks) {
- return ();
- } else if (this.state.hooks.length === 0) {
- return this.renderEmptyView();
- } else {
- return this.renderListView();
- }
- }
-}
-
-reactMixin(RESTServicesList.prototype, Reflux.ListenerMixin);