Skip to content
This repository has been archived by the owner on Nov 16, 2018. It is now read-only.

Commit

Permalink
Merge pull request #29 from acquia/fetch-embedded
Browse files Browse the repository at this point in the history
Fetch embedded resources
  • Loading branch information
prestonso authored Aug 8, 2016
2 parents 510e7f5 + 81822e2 commit e50b02e
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 3 deletions.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,33 @@ waterwheel.populateResources()

`.getFieldData()` accepts no arguments. This returns the object from Waterwheel-Drupal that contains information about each field in the entity/bundle. For a list of fields, _to be used in `.setField()`_, something like `Object.keys(res.fields)` will work.

### Get Embedded Resources

```javascript
waterwheel.api.node.page.get(1, 'hal_json')
.then(res => waterwheel.fetchEmbedded(res))
.then(res => {
// res
})
.catch(err => {
// err
});

waterwheel.api.node.page.get(1, 'hal_json')
.then(res => waterwheel.fetchEmbedded(res, ['my_field']))
.then(res => {
// res
})
.catch(err => {
// err
});
```
`.fetchEmbedded()` accepts 2 arguments
- `entityJSON`: This should be a HAL+JSON structured object containing an `_embedded` key at the root.
- `includedFields`: Optionally provide a single field as a `string`, or an `array` of `strings` to filter the embedded request by.

When requesting embedded resources duplicates are removed to prevent extra HTTP requests. An array is returned with your original response and any embedded resources. If any of the subsequent requests fail, the promise is rejected.

### Entity Query

To take advantage of the Entity Query support, enable the [EntityQueryAPI](https://www.drupal.org/project/entityqueryapi) module. You do not need to run `.populateResources()` prior to using this functionality.
Expand Down
4 changes: 2 additions & 2 deletions lib/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module.exports = class Entity extends Request {
* The ID of name of the entity being requested.
* @param {object} body
* An object containing the request body to be sent.
* @param {string} [format=json]
* @param {string} [format=application/json]
* The format for the requested content.
* @returns {Promise} A Promise that when fulfilled returns a response containing the content entity in JSON (as of 8.2).
*/
Expand Down Expand Up @@ -71,7 +71,7 @@ module.exports = class Entity extends Request {

/**
* Get field data about the expected bundle
* @param {string} [format=application/json]
* @param {string} [format=json]
* The format of the content being posted.
* @return {promise}
* A promise resolved when the request for field data is complete,
Expand Down
40 changes: 40 additions & 0 deletions lib/waterwheel.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,44 @@ module.exports = class Waterwheel extends Request {
});
}

/**
* Fetch embedded resources from HAL+JSON documents
* @param {object} entityJSON
* An object, usually returned from Drupal, containing _embedded information
* @param {string|array} [includedFields]
* If specified, a series of embedded resources to fetch.
* @return {Promise}
* If no _embedded key is found, a rejection is returned, else a resolved
* promise with all the embedded resources requests completed.
*/
fetchEmbedded(entityJSON, includedFields) {
if (!entityJSON || !entityJSON.hasOwnProperty('_embedded')) {
return Promise.reject('This is probably not HAL+JSON');
}

const fieldsToFilterBy = includedFields ?
(Array.isArray(includedFields) ? includedFields : [includedFields]) : false;

const embeddedResources = entityJSON._embedded;
const embeddedResourcesKeys = Object.keys(embeddedResources);

let links = [];

(fieldsToFilterBy ?
embeddedResourcesKeys.filter(key => fieldsToFilterBy.indexOf(key.split('/').pop()) !== -1) :
embeddedResourcesKeys).forEach(key => {
embeddedResources[key].forEach(ref => {
links.push(ref._links.self.href.split(this.base)[1]);
});
});

// Create a Set from the possibly-duplicate links array.
// Get an array from that set.
links = Array.from(new Set(links));

// Promise.all accepts an array of promises to resolve. The first item
// in this array is the original entity, adjacent to the embedded ones.
return Promise.all([Promise.resolve(entityJSON)].concat(links.map(link => this.issueRequest(methods.get, link))));
}

};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "waterwheel",
"version": "0.7.0",
"version": "0.7.1",
"description": "A generic JavaScript helper library to query and manipulate Drupal 8 via core REST",
"author": "Preston So <preston.so@acquia.com>",
"contributors": [
Expand Down
58 changes: 58 additions & 0 deletions test/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,61 @@ test.cb('Populate Resources', t => {
t.end();
});
});

test('Fetch Embedded - Missing _embedded key', t => {
const waterwheel = new t.context.Waterwheel(t.context.base, t.context.credentials);
return waterwheel.fetchEmbedded({})
.catch(err =>{
t.is(err, 'This is probably not HAL+JSON');
});
});

test('Fetch Embedded - Missing response', t => {
const waterwheel = new t.context.Waterwheel(t.context.base, t.context.credentials);
return waterwheel.fetchEmbedded()
.catch(err =>{
t.is(err, 'This is probably not HAL+JSON');
});
});

test('Fetch Embedded', t => {
requireSubvert.subvert('axios', () => (
Promise.resolve({data: {halExample: 'Some HAL+JSON'}})
));

const waterwheel = new t.context.Waterwheel(t.context.base, t.context.credentials);
const halJSON = require('./sample/hal.example.json');
return waterwheel.fetchEmbedded(halJSON)
.then(res =>{
t.is(res.length, 4);
t.deepEqual(res[1], {halExample: 'Some HAL+JSON'});
});
});

test('Fetch Embedded - Single Field', t => {
requireSubvert.subvert('axios', () => (
Promise.resolve({data: {halExample: 'Some HAL+JSON'}})
));

const waterwheel = new t.context.Waterwheel(t.context.base, t.context.credentials);
const halJSON = require('./sample/hal.example.json');
return waterwheel.fetchEmbedded(halJSON, 'field_actor')
.then(res =>{
t.is(res.length, 3);
t.deepEqual(res[1], {halExample: 'Some HAL+JSON'});
});
});

test('Fetch Embedded - Multiple Fields', t => {
requireSubvert.subvert('axios', () => (
Promise.resolve({data: {halExample: 'Some HAL+JSON'}})
));

const waterwheel = new t.context.Waterwheel(t.context.base, t.context.credentials);
const halJSON = require('./sample/hal.example.json');
return waterwheel.fetchEmbedded(halJSON, ['field_actor', 'revision_uid'])
.then(res =>{
t.is(res.length, 4);
t.deepEqual(res[1], {halExample: 'Some HAL+JSON'});
});
});
141 changes: 141 additions & 0 deletions test/sample/hal.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
{
"_links": {
"self": {
"href": "http://foo.dev/page/bruce-wayne?_format=hal_json"
},
"type": {
"href": "http://foo.dev/rest/type/node/page"
},
"http://foo.dev/rest/relation/node/page/uid": [{
"href": "http://foo.dev/user/1?_format=hal_json",
"lang": "en"
}],
"http://foo.dev/rest/relation/node/page/revision_uid": [{
"href": "http://foo.dev/user/1?_format=hal_json"
}],
"http://foo.dev/rest/relation/node/page/field_actor": [{
"href": "http://foo.dev/node/6?_format=hal_json"
}, {
"href": "http://foo.dev/node/8?_format=hal_json"
}]
},
"nid": [{
"value": "7"
}],
"uuid": [{
"value": "284365c0-fa63-43ac-ae68-d60600873c34"
}],
"vid": [{
"value": "7"
}],
"langcode": [{
"value": "en",
"lang": "en"
}],
"type": [{
"target_id": "page"
}],
"title": [{
"value": "Bruce Wayne",
"lang": "en"
}],
"_embedded": {
"http://foo.dev/rest/relation/node/page/uid": [{
"_links": {
"self": {
"href": "http://foo.dev/user/1?_format=hal_json"
},
"type": {
"href": "http://foo.dev/rest/type/user/user"
}
},
"uuid": [{
"value": "b578592a-a751-4993-9060-53f488597e59"
}],
"lang": "en"
}],
"http://foo.dev/rest/relation/node/page/revision_uid": [{
"_links": {
"self": {
"href": "http://foo.dev/user/1?_format=hal_json"
},
"type": {
"href": "http://foo.dev/rest/type/user/user"
}
},
"uuid": [{
"value": "b578592a-a751-4993-9060-53f488597e59"
}]
}],
"http://foo.dev/rest/relation/node/page/field_actor": [{
"_links": {
"self": {
"href": "http://foo.dev/node/6?_format=hal_json"
},
"type": {
"href": "http://foo.dev/rest/type/node/actor"
}
},
"uuid": [{
"value": "b5c619cd-1dfe-4c9d-afea-760a8eaf27c9"
}]
}, {
"_links": {
"self": {
"href": "http://foo.dev/node/8?_format=hal_json"
},
"type": {
"href": "http://foo.dev/rest/type/node/actor"
}
},
"uuid": [{
"value": "ac1ad040-c05c-45ac-82d7-5b3eda40e451"
}]
}]
},
"status": [{
"value": "1",
"lang": "en"
}],
"created": [{
"value": "1470156620",
"lang": "en"
}],
"changed": [{
"value": "1470170146",
"lang": "en"
}],
"promote": [{
"value": "0"
}],
"sticky": [{
"value": "0",
"lang": "en"
}],
"revision_timestamp": [{
"value": "1470156635"
}],
"revision_translation_affected": [{
"value": "1",
"lang": "en"
}],
"default_langcode": [{
"value": "1",
"lang": "en"
}],
"body": [{
"value": "some body",
"format": "basic_html",
"summary": "",
"lang": "en"
}],
"comment": [{
"status": "2",
"cid": "0",
"last_comment_timestamp": "1470156635",
"last_comment_name": null,
"last_comment_uid": "1",
"comment_count": "0",
"lang": "en"
}]
}

0 comments on commit e50b02e

Please sign in to comment.