Add specific exceptions into the link extraction process
authorFrank Bessou <frank.bessou@logilab.fr>
Tue, 18 Apr 2017 10:30:56 +0200
changeset 117 493b8bda31e8
parent 116 9ccdace2a88c
child 118 bdb941dcdd40
Add specific exceptions into the link extraction process An exception is thrown when the 'Link' header is not present and another is thrown when the 'describedby' link is not present in the 'Link' header.
package.json
src/Api.js
test/index.js
--- a/package.json	Thu Apr 13 14:47:13 2017 +0200
+++ b/package.json	Tue Apr 18 10:30:56 2017 +0200
@@ -23,6 +23,7 @@
     "babel-preset-react": "^6.5.0",
     "babel-register": "^6.7.2",
     "chai": "^3.5.0",
+    "chai-as-promised": "^6.0.0",
     "diff": "^3.2.0",
     "enzyme": "^2.4.1",
     "eslint": "^3.11.1",
--- a/src/Api.js	Thu Apr 13 14:47:13 2017 +0200
+++ b/src/Api.js	Tue Apr 18 10:30:56 2017 +0200
@@ -70,8 +70,7 @@
         const dataResponsePromise = this.jsonFetchResponse(resource.url);
         const resourcePromise = dataResponsePromise.then(response => {
             resource.allowedActions = this.extractAllowedActions(response);
-            const links = LinkParser.parse(response.headers.get('Link'));
-            const schemaRoute = links.rel('describedby')[0].uri;
+            const schemaRoute = this.extractSchemaRoute(response);
             const schemaPromise = this.getSchema(schemaRoute);
             const dataPromise = response.json().catch(()=>null);
 
@@ -147,6 +146,19 @@
         });
     }
 
+    extractSchemaRoute(response) {
+        const linkHeader = response.headers.get("Link");
+        if (linkHeader === null) {
+            throw new Error(`"Link" header does not exist on resource's HTTP header`)
+        }
+        try {
+            const links = LinkParser.parse(linkHeader);
+            return links.rel('describedby')[0].uri;
+        } catch (e) {
+            throw new Error(`Cannot find 'describedby' link in ${linkHeader}`);
+        }
+    }
+
     createEntity(etype, attributes, ...files) {
         const url = `/${etype}/`;
         const headers = {};
--- a/test/index.js	Thu Apr 13 14:47:13 2017 +0200
+++ b/test/index.js	Tue Apr 18 10:30:56 2017 +0200
@@ -2,6 +2,10 @@
 /* global describe, it, Jsonary*/
 
 import {expect} from 'chai';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+chai.use(chaiAsPromised);
+
 import {shallow} from 'enzyme';
 import sinon from 'sinon';
 import {assert} from 'sinon';
@@ -402,12 +406,13 @@
     describe('getResource', () => {
 
         const baseUrl = 'http://example.com';
-        let api;
         let fakeFetch;
 
         function configureBackend(config) {
 
             const noContent = config.noContent || false;
+            const noLink = config.noLink || false;
+            const customLink = config.customLink || null;
             const data = noContent ? undefined : (config.data || {});
             const resourceRoute = config.resourceRoute || '/';
             const schema = null;
@@ -416,10 +421,13 @@
             const options = config.options || {};
 
             fakeFetch = sinon.stub();
-            api = new Api(baseUrl, fakeFetch);
+            const api = new Api(baseUrl, fakeFetch);
             sinon.stub(api, 'jsonFetchResponse').callsFake(() => {
-                const linkHeader = `<${schemaRoute}>; rel=describedby`;
-                const resourceOptions = merge({headers: {Allow: allow, Link: linkHeader}}, options);
+                const resourceOptions = merge({headers: {Allow: allow}}, options);
+                if (!noLink) {
+                    const linkHeader = customLink || `<${schemaRoute}>; rel=describedby`;
+                    merge(resourceOptions, {headers: {Link: linkHeader}});
+                }
                 if (noContent) {
                     merge(resourceOptions, {status: 204});
                 } else {
@@ -458,7 +466,7 @@
                 schemaRoute : '/schema/any',
 
             }
-            configureBackend(config);
+            const api = configureBackend(config);
 
             api.getResource(config.resourceRoute).then(resource => {
                 const schema = resource.data.schemas()[0].data.value();
@@ -468,6 +476,30 @@
                 done();
             }).catch(done);
         });
+
+        context('the resource\'s HTTP "Link" header is not present', () => {
+            it('should reject with a descriptive message', () => {
+                const config = {
+                    resourceRoute: '/',
+                    noLink: true,
+                };
+                const api = configureBackend(config);
+
+                return expect(api.getResource('/')).to.eventually.be.rejectedWith(/"Link" header does not exist./);
+            });
+        });
+
+        context('the resource\'s HTTP "Link" does not contain a describedby link', () => {
+            it('should reject with a descriptive message', () => {
+                const config = {
+                    resourceRoute: '/',
+                    customLink: '</> ; rel="collection"',
+                };
+                const api = configureBackend(config);
+
+                return expect(api.getResource('/')).to.eventually.be.rejectedWith(/Cannot find 'describedby'/);
+            });
+        });
     });
 
     describe('getEntity', () => {