You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@couchdb.apache.org by GitBox <gi...@apache.org> on 2017/12/13 09:13:33 UTC

[GitHub] garrensmith closed pull request #1034: Add new ajax api

garrensmith closed pull request #1034: Add new ajax api
URL: https://github.com/apache/couchdb-fauxton/pull/1034
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/app/addons/replication/__tests__/api.tests.js b/app/addons/replication/__tests__/api.tests.js
index d1c4c7687..7fe2982d1 100644
--- a/app/addons/replication/__tests__/api.tests.js
+++ b/app/addons/replication/__tests__/api.tests.js
@@ -310,7 +310,7 @@ describe('Replication API', () => {
     });
 
     it('returns true for support', () => {
-      fetchMock.getOnce('/_scheduler/jobs', 200);
+      fetchMock.getOnce('/_scheduler/jobs', {});
       return supportNewApi(true)
       .then(resp => {
         assert.ok(resp);
@@ -318,7 +318,11 @@ describe('Replication API', () => {
     });
 
     it('returns false for no support', () => {
-      fetchMock.getOnce('/_scheduler/jobs', 404);
+      fetchMock.getOnce('/_scheduler/jobs', {
+        status: 404,
+        body: {error: "missing"}
+      });
+
       return supportNewApi(true)
       .then(resp => {
         assert.notOk(resp);
@@ -453,7 +457,7 @@ describe('Replication API', () => {
         .then(docs => {
           assert.deepEqual(docs.length, 1);
           assert.deepEqual(docs[0]._id, "c94d4839d1897105cb75e1251e0003ea");
-          assert.deepEqual(docs[0].stateTime, new Date('2017-03-07T14:46:17'));
+          assert.deepEqual(docs[0].stateTime.toDateString(), (new Date('2017-03-07T14:46:17')).toDateString());
         });
       });
     });
diff --git a/app/addons/replication/api.js b/app/addons/replication/api.js
index 6731828e5..8db120480 100644
--- a/app/addons/replication/api.js
+++ b/app/addons/replication/api.js
@@ -13,25 +13,19 @@
 import 'url-polyfill';
 import Constants from './constants';
 import FauxtonAPI from '../../core/api';
+import {get, post, put} from '../../core/ajax';
 import base64 from 'base-64';
 import _ from 'lodash';
-import 'whatwg-fetch';
 
 let newApiPromise = null;
 export const supportNewApi = (forceCheck) => {
   if (!newApiPromise || forceCheck) {
     newApiPromise = new FauxtonAPI.Promise((resolve) => {
-      fetch('/_scheduler/jobs', {
-        credentials: 'include',
-        headers: {
-            'Accept': 'application/json; charset=utf-8',
-          }
-        })
+      get('/_scheduler/jobs', {raw: true})
       .then(resp => {
         if (resp.status > 202) {
           return resolve(false);
         }
-
         resolve(true);
       });
     });
@@ -292,13 +286,7 @@ export const combineDocsAndScheduler = (docs, schedulerDocs) => {
 export const fetchReplicationDocs = () => {
   return supportNewApi()
   .then(newApi => {
-    const docsPromise = fetch('/_replicator/_all_docs?include_docs=true&limit=100', {
-      credentials: 'include',
-      headers: {
-        'Accept': 'application/json; charset=utf-8',
-      }
-    })
-    .then(res => res.json())
+    const docsPromise = get('/_replicator/_all_docs?include_docs=true&limit=100')
     .then((res) => {
       if (res.error) {
         return [];
@@ -321,13 +309,7 @@ export const fetchReplicationDocs = () => {
 };
 
 export const fetchSchedulerDocs = () => {
-  return fetch('/_scheduler/docs?include_docs=true', {
-    credentials: 'include',
-    headers: {
-      'Accept': 'application/json; charset=utf-8',
-    }
-  })
-  .then(res => res.json())
+  return get('/_scheduler/docs?include_docs=true')
   .then((res) => {
     if (res.error) {
       return [];
@@ -338,20 +320,16 @@ export const fetchSchedulerDocs = () => {
 };
 
 export const checkReplicationDocID = (docId) => {
-  const promise = FauxtonAPI.Deferred();
-  fetch(`/_replicator/${docId}`, {
-    credentials: 'include',
-    headers: {
-      'Accept': 'application/json; charset=utf-8'
-    },
-  }).then(resp => {
-    if (resp.statusText === "Object Not Found") {
-      promise.resolve(false);
-      return;
-    }
-    promise.resolve(true);
+  return new Promise((resolve) => {
+    get(`/_replicator/${docId}`)
+    .then(resp => {
+      if (resp.error === "not_found") {
+        resolve(false);
+        return;
+      }
+      resolve(true);
+    });
   });
-  return promise;
 };
 
 export const parseReplicateInfo = (resp) => {
@@ -379,13 +357,7 @@ export const fetchReplicateInfo = () => {
       return [];
     }
 
-    return fetch('/_scheduler/jobs', {
-      credentials: 'include',
-      headers: {
-        'Accept': 'application/json; charset=utf-8'
-      },
-    })
-    .then(resp => resp.json())
+    return get('/_scheduler/jobs')
     .then(resp => {
       return parseReplicateInfo(resp);
     });
@@ -399,37 +371,18 @@ export const deleteReplicatesApi = (replicates) => {
       cancel: true
     };
 
-    return fetch('/_replicate', {
-      method: 'POST',
-      credentials: 'include',
-      headers: {
-        'Accept': 'application/json; charset=utf-8',
-        'Content-Type': 'application/json'
-      },
-      body: JSON.stringify(data)
-    })
-    .then(resp => resp.json());
+    return post('/_replicate', data);
   });
 
   return FauxtonAPI.Promise.all(promises);
 };
 
 export const createReplicatorDB = () => {
-  return fetch('/_replicator', {
-    method: 'PUT',
-    credentials: 'include',
-    headers: {
-        'Accept': 'application/json; charset=utf-8',
-      }
-    })
+  return put('/_replicator')
     .then(res => {
       if (!res.ok) {
         throw {reason: 'Failed to create the _replicator database.'};
       }
-
-      return res.json();
-    })
-    .then(() => {
       return true;
     });
 };
diff --git a/app/core/__tests__/ajax.test.js b/app/core/__tests__/ajax.test.js
new file mode 100644
index 000000000..e63eb4e18
--- /dev/null
+++ b/app/core/__tests__/ajax.test.js
@@ -0,0 +1,144 @@
+// 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 fetchMock from 'fetch-mock';
+import {
+  json,
+  fetchObserver,
+  get,
+  put,
+  post,
+  deleteRequest
+} from '../ajax';
+import testUtils from "../../../test/mocha/testUtils";
+const assert = testUtils.assert;
+
+describe('Fauxton Ajax', () => {
+  let unsubscribe;
+
+  afterEach(() => {
+    if (unsubscribe) {
+      unsubscribe.unsubscribe();
+      unsubscribe = null;
+    }
+  });
+
+  it('should observe multiple requests', (done) => {
+    fetchMock.getOnce('/testing', {
+      status: 200,
+      body: {
+        ok: true
+      }
+    });
+
+    fetchMock.getOnce('/testing2', {
+      status: 201,
+      body: {
+        ok: true
+      }
+    });
+
+    let count = 0;
+    unsubscribe = fetchObserver.subscribe({
+      next () {
+        count++;
+        if (count === 2) {
+          done();
+        }
+      }
+    });
+
+    json("/testing");
+    json("/testing2");
+  });
+
+  it('Observer should be filterable', (done) => {
+    fetchMock.getOnce('/testing', {
+      status: 200,
+      body: {
+        ok: true
+      }
+    });
+
+    fetchMock.getOnce('/testing-again', {
+      status: 400,
+      body: {
+        hello: 'ok'
+      }
+    });
+
+    unsubscribe = fetchObserver.filter(resp => resp.status === 400).subscribe({
+      next (resp) {
+        done();
+        assert.deepEqual(resp.status, 400);
+      }
+    });
+
+    json("/testing");
+    json("/testing-again");
+  });
+
+  it('can do a GET', () => {
+    fetchMock.getOnce('/testing', {
+      status: 200,
+      body: {
+        ok: true
+      }
+    });
+
+    return get('/testing')
+    .then(resp =>{
+      assert.ok(resp.ok);
+    });
+  });
+
+  it('can do a PUT', () => {
+    fetchMock.putOnce('/testing', {
+      status: 200,
+      body: {
+        ok: true
+      }
+    });
+
+    return put('/testing')
+    .then(resp =>{
+      assert.ok(resp.ok);
+    });
+  });
+
+  it('can do a POST', () => {
+    fetchMock.postOnce('/testing', {
+      status: 200,
+      body: {
+        ok: true
+      }
+    });
+
+    return post('/testing')
+    .then(resp =>{
+      assert.ok(resp.ok);
+    });
+  });
+
+  it('can do a DELETE', () => {
+    fetchMock.deleteOnce('/testing', {
+      status: 200,
+      body: {
+        ok: true
+      }
+    });
+
+    return deleteRequest('/testing')
+    .then(resp =>{
+      assert.ok(resp.ok);
+    });
+  });
+});
diff --git a/app/core/ajax.js b/app/core/ajax.js
new file mode 100644
index 000000000..e7390fb18
--- /dev/null
+++ b/app/core/ajax.js
@@ -0,0 +1,101 @@
+import 'whatwg-fetch';
+import { defaultsDeep } from "lodash";
+import { Subject } from 'rxjs/Subject';
+import 'rxjs/add/operator/filter';
+
+/* Add a multicast observer so that all fetch requests can be observed
+  Some usage examples:
+
+  This logs all status codes
+  fetchObserver.subscribe((resp) => console.log(resp.statusCode));
+
+
+  This only logs all status codes that are great than 201
+  fetchObserver.filter(resp.statusCode > 201).subscribe(resp => console.log(resp.statusCode));
+*/
+export const fetchObserver = new Subject();
+
+
+/**
+ * json - The lowlevel fetch request with some basic headers
+ * that are always needed.
+ *
+ * @param {string}   url          The url for the request
+ * @param {string} [method=GET] The request method
+ * @param {object} [opts={}]    Extra fetch options that will be added.
+ * Any opts added here will override the default ones
+ * Passing in `raw: true` in here will return the raw response instead of a json body response.
+ *
+ * @return {Promise}
+ */
+export const json = (url, method = "GET", opts = {}) => {
+  return fetch(
+    url,
+    defaultsDeep(
+      {
+        method,
+        credentials: "include",
+        headers: {
+          accept: "application/json",
+          "Content-Type": "application/json"
+        }
+      },
+      opts
+    )
+  ).then(resp => {
+    fetchObserver.next(resp);
+    if (opts.raw) {
+      return resp;
+    }
+    return resp.json();
+  });
+};
+
+
+/**
+ * get - Get request
+ *
+ * @param {string}   url Url of request
+ * @param {object} [opts={}] Opts to add to request
+ *
+ * @return {Promise} A promise with the request's response
+ */
+export const get = (url, opts = {}) => {
+  return json(url, "GET", opts);
+};
+
+export const deleteRequest = (url, opts = {}) => {
+  return json(url, "DELETE", opts);
+};
+
+/**
+ * post - Post request
+ *
+ * @param {string}   url  Url of request
+ * @param {object} [body] Body of request
+ * @param {object} [opts={}] Opts to add to request
+ *
+ * @return {Promise} A promise with the request's response
+ */
+export const post = (url, body, opts = {}) => {
+  if (body) {
+    opts.body = JSON.stringify(body);
+  }
+  return json(url, "POST", opts);
+};
+
+/**
+ * put - Put request
+ *
+ * @param {string}   url  Url of request
+ * @param {object} [body] Body of request
+ * @param {object} [opts={}] Opts to add to request
+ *
+ * @return {Promise} A promise with the request's response
+ */
+export const put = (url, body, opts = {}) => {
+  if (body) {
+    opts.body = JSON.stringify(body);
+  }
+  return json(url, "PUT", opts);
+};
diff --git a/package-lock.json b/package-lock.json
index 022caf2ea..9e7f0ad16 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10803,6 +10803,21 @@
       "version": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz",
       "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI="
     },
+    "rxjs": {
+      "version": "5.5.5",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.5.tgz",
+      "integrity": "sha512-D/MfQnPMBk8P8gfwGxvCkuaWBcG58W7dUMT//URPoYzIbDEKT0GezdirkK5whMgKFBATfCoTpxO8bJQGJ04W5A==",
+      "requires": {
+        "symbol-observable": "1.0.1"
+      },
+      "dependencies": {
+        "symbol-observable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
+          "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ="
+        }
+      }
+    },
     "safe-buffer": {
       "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
       "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM="
@@ -11026,7 +11041,6 @@
     },
     "sinon": {
       "version": "git+https://github.com/sinonjs/sinon.git#ec9126c139599cfaed3fb3d06b1b7b4ab32c6302",
-      "integrity": "sha1-u8ocxb8zpcuBuiEK+mfHFv5nlhw=",
       "dev": true,
       "requires": {
         "diff": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz",
@@ -11833,8 +11847,7 @@
       }
     },
     "url-polyfill": {
-      "version": "git+https://github.com/webcomponents/URL.git#812ca9b2baae653290aceb36362d219cb96c0f35",
-      "integrity": "sha1-rot1xy3pzGk2xlYUWBM2vM/h5uY="
+      "version": "git+https://github.com/webcomponents/URL.git#812ca9b2baae653290aceb36362d219cb96c0f35"
     },
     "urls": {
       "version": "https://registry.npmjs.org/urls/-/urls-0.0.4.tgz",
@@ -11910,8 +11923,7 @@
       }
     },
     "visualizeRevTree": {
-      "version": "git+https://github.com/neojski/visualizeRevTree.git#e90e372f381508c9d2c305bd74b84a032005c77d",
-      "integrity": "sha1-fosoe69l5VTUeHwkPnAnzbexVAg="
+      "version": "git+https://github.com/neojski/visualizeRevTree.git#e90e372f381508c9d2c305bd74b84a032005c77d"
     },
     "vm-browserify": {
       "version": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
diff --git a/package.json b/package.json
index 02566f0e8..d986160b6 100644
--- a/package.json
+++ b/package.json
@@ -100,6 +100,7 @@
     "redux": "^3.6.0",
     "redux-thunk": "^2.1.0",
     "request": "^2.54.0",
+    "rxjs": "^5.5.5",
     "semver": "^5.1.0",
     "send": "^0.14.2",
     "style-loader": "^0.13.1",


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services