app/addons/documents/index-editor/actions.js (269 lines of code) (raw):

// Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. import FauxtonAPI from '../../../core/api'; import Documents from '../resources'; import ActionTypes from './actiontypes'; import SidebarActions from '../sidebar/actions'; const selectReduceChanged = (reduceOption) => (dispatch) => { dispatch({ type: ActionTypes.SELECT_REDUCE_CHANGE, reduceSelectedOption: reduceOption }); }; const changeViewName = (name) => (dispatch) => { dispatch({ type: ActionTypes.VIEW_NAME_CHANGE, name: name }); }; const editIndex = (options) => (dispatch) => { dispatch({ type: ActionTypes.EDIT_INDEX, options: options }); }; const dispatchClearIndex = () => { FauxtonAPI.reduxDispatch({ type: ActionTypes.CLEAR_INDEX }); }; const dispatchFetchDesignDocsBeforeEdit = (options) => { options.designDocs.fetch({reset: true}).then(() => { editIndex(options)(FauxtonAPI.reduxDispatch); }, xhr => { let errorMsg = 'Error'; if (xhr.responseJSON && xhr.responseJSON.error === 'not_found') { const databaseName = options.designDocs.database.safeID(); errorMsg = `The ${databaseName} database does not exist`; FauxtonAPI.navigate('/', {trigger: true}); } FauxtonAPI.addNotification({ msg: errorMsg, type: "error", clear: true }); }); }; const shouldRemoveDdocView = (viewInfo) => { return !viewInfo.isNewView && viewInfo.originalDesignDocName === viewInfo.designDocId && viewInfo.originalViewName !== viewInfo.viewName; }; const saveView = (viewInfo, navigateToURL) => (dispatch) => { const designDoc = viewInfo.designDoc; designDoc.setDdocView(viewInfo.viewName, viewInfo.map, viewInfo.reduce); FauxtonAPI.addNotification({ msg: 'Saving View...', type: 'info', clear: true }); // if the view name just changed and it's in the SAME design doc, remove the old one before saving the doc if (shouldRemoveDdocView(viewInfo)) { designDoc.removeDdocView(viewInfo.originalViewName); } designDoc.save().then(function () { FauxtonAPI.addNotification({ msg: 'View Saved.', type: 'success', clear: true }); // if the user just saved an existing view to a different design doc, remove the view // from the old design doc and delete if it's empty if (!viewInfo.isNewView && viewInfo.originalDesignDocName !== viewInfo.designDocId) { const oldDesignDoc = findDesignDoc(viewInfo.designDocs, viewInfo.originalDesignDocName); safeDeleteIndex(oldDesignDoc, viewInfo.designDocs, 'views', viewInfo.originalViewName, { onSuccess: function () { SidebarActions.dispatchUpdateDesignDocs(viewInfo.designDocs); } }); } if (viewInfo.designDocId === 'new-doc') { addDesignDoc(designDoc); } SidebarActions.dispatchUpdateDesignDocs(viewInfo.designDocs); dispatch({ type: ActionTypes.VIEW_SAVED }); FauxtonAPI.navigate(navigateToURL, { trigger: true }); }, (xhr) => { FauxtonAPI.addNotification({ msg: 'Save failed. ' + (xhr.responseJSON ? `Reason: ${xhr.responseJSON.reason}` : ''), type: 'error', clear: true }); }); }; const addDesignDoc = (designDoc) => (dispatch) => { dispatch({ type: ActionTypes.VIEW_ADD_DESIGN_DOC, designDoc: designDoc.toJSON() }); }; const deleteView = (options) => { function onSuccess () { // if the user was on the index that was just deleted, redirect them back to all docs if (options.isOnIndex) { const url = FauxtonAPI.urls('allDocs', 'app', options.database.safeID()); FauxtonAPI.navigate(url); } SidebarActions.dispatchUpdateDesignDocs(options.designDocs); FauxtonAPI.addNotification({ msg: 'The "' + options.indexName + '" view has been deleted.', type: 'info', escape: false, clear: true }); SidebarActions.dispatchHideDeleteIndexModal(); } return safeDeleteIndex(options.designDoc, options.designDocs, 'views', options.indexName, { onSuccess: onSuccess }); }; const cloneView = (params) => { const targetDesignDoc = getDesignDoc(params.designDocs, params.targetDesignDocName, params.newDesignDocName, params.newDesignDocPartitioned, params.database, params.isDbPartitioned); let indexes = targetDesignDoc.get('views'); if (indexes && _.has(indexes, params.newIndexName)) { FauxtonAPI.addNotification({ msg: 'That index name is already used in this design doc. Please enter a new name.', type: 'error', clear: true }); return; } if (!indexes) { indexes = {}; } const sourceDesignDoc = findDesignDoc(params.designDocs, '_design/' + params.sourceDesignDocName); const sourceDesignDocJSON = sourceDesignDoc.toJSON(); // this sets whatever content is in the source index into the target design doc under the new index name indexes[params.newIndexName] = sourceDesignDocJSON.views[params.sourceIndexName]; targetDesignDoc.set({ views: indexes }); targetDesignDoc.save().then(() => { params.onComplete(); FauxtonAPI.addNotification({ msg: 'The index has been cloned.', type: 'success', clear: true }); SidebarActions.dispatchUpdateDesignDocs(params.designDocs); }, (xhr) => { params.onComplete(); const responseText = JSON.parse(xhr.responseText).reason; FauxtonAPI.addNotification({ msg: 'Clone failed: ' + responseText, type: 'error', clear: true }); }); }; const gotoEditViewPage = (databaseName, partitionKey, designDocName, indexName) => { FauxtonAPI.navigate('#' + FauxtonAPI.urls('view', 'edit', encodeURIComponent(databaseName), (partitionKey ? encodeURIComponent(partitionKey) : ''), encodeURIComponent(designDocName), encodeURIComponent(indexName))); }; const updateMapCode = (code) => (dispatch) => { dispatch({ type: ActionTypes.VIEW_UPDATE_MAP_CODE, code: code }); }; const updateReduceCode = (code) => (dispatch) => { dispatch({ type: ActionTypes.VIEW_UPDATE_REDUCE_CODE, code: code }); }; const selectDesignDoc = (designDoc) => (dispatch) => { dispatch({ type: ActionTypes.DESIGN_DOC_CHANGE, options: { value: designDoc } }); }; const updateNewDesignDocName = (designDocName) => (dispatch) => { dispatch({ type: ActionTypes.DESIGN_DOC_NEW_NAME_UPDATED, options: { value: designDocName } }); }; const updateNewDesignDocPartitioned = (isPartitioned) => (dispatch) => { dispatch({ type: ActionTypes.DESIGN_DOC_NEW_PARTITIONED_UPDATED, options: { value: isPartitioned } }); }; // safely deletes an index of any type. It only deletes the actual design doc if there are no // other indexes of any type left in the doc const safeDeleteIndex = (designDoc, designDocs, indexPropName, indexName, options) => { const opts = _.extend({ onSuccess: function () { }, onError: function (xhr) { const responseText = JSON.parse(xhr.responseText).reason; FauxtonAPI.addNotification({ msg: 'Delete failed: ' + responseText, type: 'error', clear: true }); } }, options); const indexes = designDoc.get(indexPropName) || {}; delete indexes[indexName]; const newIndexes = {}; newIndexes[indexPropName] = indexes; designDoc.set(newIndexes); // we either save the design doc with the now-removed index, or we remove it altogether if there are no indexes // of any type left in the design doc const indexTypePropNames = FauxtonAPI.getIndexTypePropNames(); const hasRemainingIndex = _.some(indexTypePropNames, function (propName) { return designDoc.get(propName) && _.keys(designDoc.get(propName)).length > 0; }); let promise; let deleteDesignDoc = false; if (hasRemainingIndex) { promise = designDoc.save(); } else { promise = designDoc.destroy(); deleteDesignDoc = true; } return promise.then(function () { if (deleteDesignDoc) { designDocs.remove(designDoc.id); } opts.onSuccess(); }, opts.onError); }; // ---- helpers ---- const findDesignDoc = (designDocs, designDocName) => { return designDocs.find(function (doc) { return doc.id === designDocName; }).dDocModel(); }; const getDesignDoc = (designDocs, targetDesignDocName, newDesignDocName, newDesignDocPartitioned, database, isDbPartitioned) => { if (targetDesignDocName === 'new-doc') { const doc = { "_id": "_design/" + newDesignDocName, "views": {}, "language": "javascript" }; const dDoc = new Documents.Doc(doc, { database: database }); if (isDbPartitioned) { dDoc.setDDocPartitionedOption(newDesignDocPartitioned); } return dDoc; } const foundDoc = designDocs.find(function (ddoc) { return ddoc.id === targetDesignDocName; }); return (!foundDoc) ? null : foundDoc.dDocModel(); }; export default { helpers: { findDesignDoc, getDesignDoc }, safeDeleteIndex, selectReduceChanged, changeViewName, editIndex, dispatchClearIndex, dispatchFetchDesignDocsBeforeEdit, shouldRemoveDdocView, saveView, addDesignDoc, deleteView, cloneView, gotoEditViewPage, updateMapCode, updateReduceCode, selectDesignDoc, updateNewDesignDocName, updateNewDesignDocPartitioned };