drupal-jsonapi-client
Version:
Making working with Drupal 8's JSON:API easier, faster and cleaner.
669 lines (557 loc) • 23.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _EntityNotFound = _interopRequireDefault(require("./Error/EntityNotFound"));
var _MalformedEntity = _interopRequireDefault(require("./Error/MalformedEntity"));
var _GlobalClient = _interopRequireDefault(require("./GlobalClient"));
var _QueryParameters = _interopRequireDefault(require("./QueryParameters"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var TypeHeaders = {
Accept: 'application/vnd.api+json',
'Content-Type': 'application/vnd.api+json'
};
var Entity =
/*#__PURE__*/
function () {
_createClass(Entity, null, [{
key: "FromResponse",
value: function FromResponse(response) {
var entity = new Entity();
entity._applySerializedData(response);
return entity;
}
/**
* Get a single entity.
*
* @param {string} entityType
* @param {string} entityBundle
* @param {string} entityUuid
* @param {string[]} includeRelationships default = []
* @param {boolean} refreshCache default = false
*/
}, {
key: "Load",
value: function () {
var _Load = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(entityType, entityBundle, entityUuid) {
var includeRelationships,
refreshCache,
queryParameters,
response,
json,
entity,
_args = arguments;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
includeRelationships = _args.length > 3 && _args[3] !== undefined ? _args[3] : [];
refreshCache = _args.length > 4 && _args[4] !== undefined ? _args[4] : false;
if (!(Entity.Cache[entityUuid] && refreshCache === false)) {
_context.next = 4;
break;
}
return _context.abrupt("return", Entity.FromResponse(Entity.Cache[entityUuid]));
case 4:
queryParameters = new _QueryParameters.default(["include=".concat(includeRelationships.join(','))]);
_context.next = 7;
return _GlobalClient.default.send({
url: "/jsonapi/".concat(entityType, "/").concat(entityBundle, "/").concat(entityUuid).concat(includeRelationships.length > 0 ? "?".concat(queryParameters.toString()) : ''),
method: 'GET',
headers: TypeHeaders
});
case 7:
response = _context.sent;
json = response.data;
if (!(json && json.data)) {
_context.next = 14;
break;
}
entity = Entity.FromResponse(json.data);
Entity.Cache[entityUuid] = _objectSpread({}, entity._serialize().data, {
id: entity.entityUuid // Warm EntityCache so future requests for .expand can pull from cache
});
if (json.included) {
json.included.forEach(function (includedData) {
var includedEntity = Entity.FromResponse(includedData);
Entity.Cache[includedEntity.entityUuid] = _objectSpread({}, includedEntity._serialize().data, {
id: includedEntity.entityUuid
});
});
}
return _context.abrupt("return", entity);
case 14:
throw new _EntityNotFound.default("Failed to find entity matching entity type ".concat(entityType, ", entity bundle ").concat(entityBundle, " and uuid ").concat(entityUuid));
case 15:
case "end":
return _context.stop();
}
}
}, _callee);
}));
function Load(_x, _x2, _x3) {
return _Load.apply(this, arguments);
}
return Load;
}()
/**
* Get entities matching provided filters.
*
* @param {object} config
*
* @param {string} config.entityType
* @param {string} config.entityBundle
* @param {Filter|FilterGroup} config.filter default = {}
* @param {number} config.pageOffset default = 0
* @param {number} config.pageLimit default = 50
*/
}, {
key: "LoadMultiple",
value: function () {
var _LoadMultiple = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee2(_ref) {
var entityType, entityBundle, _ref$filter, filter, _ref$include, include, _ref$pageOffset, pageOffset, _ref$pageLimit, pageLimit, filterQuery, queryParameters, response, json;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
entityType = _ref.entityType, entityBundle = _ref.entityBundle, _ref$filter = _ref.filter, filter = _ref$filter === void 0 ? {} : _ref$filter, _ref$include = _ref.include, include = _ref$include === void 0 ? [] : _ref$include, _ref$pageOffset = _ref.pageOffset, pageOffset = _ref$pageOffset === void 0 ? 0 : _ref$pageOffset, _ref$pageLimit = _ref.pageLimit, pageLimit = _ref$pageLimit === void 0 ? 50 : _ref$pageLimit;
filterQuery = typeof filter.query === 'function' ? filter.query() : filter;
queryParameters = new _QueryParameters.default([filterQuery, include.length > 0 ? "include=".concat(include.join(',')) : null, "page[offset]=".concat(pageOffset), "page[limit]=".concat(pageLimit)]);
_context2.next = 5;
return _GlobalClient.default.send({
url: "/jsonapi/".concat(entityType, "/").concat(entityBundle, "?").concat(queryParameters.toString(Number.MAX_SAFE_INTEGER)),
method: 'GET',
headers: TypeHeaders
});
case 5:
response = _context2.sent;
json = response.data;
if (!(json && json.data && json.data.length && json.data.length > 0)) {
_context2.next = 10;
break;
}
// Warm EntityCache so future requests for .expand can pull from cache
if (json.included && json.included.length) {
json.included.forEach(function (includedData) {
var includedEntity = new Entity();
includedEntity._applySerializedData(includedData);
Entity.Cache[includedEntity.entityUuid] = _objectSpread({}, includedEntity._serialize().data, {
id: includedEntity.entityUuid
});
});
}
return _context2.abrupt("return", json.data.map(function (item) {
var entity = new Entity();
entity._applySerializedData(item);
Entity.Cache[entity.entityUuid] = _objectSpread({}, entity._serialize().data, {
id: entity.entityUuid
});
return entity;
}));
case 10:
return _context2.abrupt("return", json.data);
case 11:
case "end":
return _context2.stop();
}
}
}, _callee2);
}));
function LoadMultiple(_x4) {
return _LoadMultiple.apply(this, arguments);
}
return LoadMultiple;
}()
/**
* Delete a remote entity.
*
* @param {string} entityType
* @param {string} entityBundle
* @param {string} entityUuid
*/
}, {
key: "Delete",
value: function () {
var _Delete = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee3(entityType, entityBundle, entityUuid) {
return regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
return _context3.abrupt("return", new Entity(entityType, entityBundle, entityUuid).delete());
case 1:
case "end":
return _context3.stop();
}
}
}, _callee3);
}));
function Delete(_x5, _x6, _x7) {
return _Delete.apply(this, arguments);
}
return Delete;
}()
}]);
function Entity(entityType, entityBundle, entityUuid) {
_classCallCheck(this, Entity);
this.entityType = entityType;
this.entityBundle = entityBundle;
this.entityUuid = entityUuid || null;
this.enforceNew = false;
this._attributes = {};
this._relationships = {};
this._changes = {
attributes: {},
relationships: {} // Setup proxy behaviour for fields
};
return new Proxy(this, {
get: function get(target, key) {
var fieldName = key;
var fieldNameTransformations = {
nid: 'drupal_internal__nid',
vid: 'drupal_internal__vid'
};
if (fieldName in fieldNameTransformations) {
fieldName = fieldNameTransformations[fieldName];
}
if (!(fieldName in target)) {
if (target._hasField(fieldName)) {
return target.get(key);
}
}
return target[key];
},
set: function set(target, key, value) {
var fieldName = key;
var fieldNameTransformations = {
nid: 'drupal_internal__nid',
vid: 'drupal_internal__vid'
};
if (fieldName in fieldNameTransformations) {
fieldName = fieldNameTransformations[fieldName];
}
if (!(fieldName in target)) {
if (target._hasField(fieldName)) {
target.set(fieldName, value);
return true;
}
} // eslint-disable-next-line no-param-reassign
target[fieldName] = value;
return true;
}
});
}
_createClass(Entity, [{
key: "_applySerializedData",
value: function _applySerializedData(jsonApiSerialization) {
var _jsonApiSerialization = jsonApiSerialization.type.split('--'),
_jsonApiSerialization2 = _slicedToArray(_jsonApiSerialization, 2),
entityType = _jsonApiSerialization2[0],
entityBundle = _jsonApiSerialization2[1];
this.entityType = entityType;
this.entityBundle = entityBundle;
this.entityUuid = jsonApiSerialization.id;
this._attributes = jsonApiSerialization.attributes || {};
if (jsonApiSerialization.relationships) {
this._relationships = Object.keys(jsonApiSerialization.relationships).map(function (key) {
return {
data: jsonApiSerialization.relationships[key].data,
_$key: key
};
}).reduce(function (prev, curr) {
var key = curr._$key;
var copy = curr;
delete copy._$key;
return _objectSpread({}, prev, _defineProperty({}, key, copy));
}, {});
} else {
this._relationships = {};
}
}
}, {
key: "_serializeChanges",
value: function _serializeChanges() {
var serialization = {
data: {
type: "".concat(this.entityType, "--").concat(this.entityBundle),
attributes: this._changes.attributes,
relationships: this._changes.relationships
}
};
if (Object.keys(serialization.data.attributes).length === 0) {
delete serialization.data.attributes;
}
if (Object.keys(serialization.data.relationships).length === 0) {
delete serialization.data.relationships;
}
if (this.entityUuid) {
serialization.data.id = this.entityUuid;
}
return serialization;
}
}, {
key: "_serializeChangesForField",
value: function _serializeChangesForField(fieldName) {
return {
data: this.getChange(fieldName)
};
}
}, {
key: "_serialize",
value: function _serialize() {
var withId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var serialization = {
data: {
type: "".concat(this.entityType, "--").concat(this.entityBundle),
attributes: this._attributes,
relationships: this._relationships
}
};
if (Object.keys(serialization.data.attributes).length === 0) {
delete serialization.data.attributes;
}
if (Object.keys(serialization.data.relationships).length === 0) {
delete serialization.data.relationships;
}
if (withId && this.entityUuid) {
serialization.data.id = this.entityUuid;
}
return serialization;
}
}, {
key: "_hasField",
value: function _hasField(fieldName) {
return fieldName in this._attributes || fieldName in this._relationships;
}
/**
* Get field value.
*
* @param {string} fieldName
* @param {boolean} strict default: false
*/
}, {
key: "get",
value: function get(fieldName) {
var strict = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (typeof this._attributes[fieldName] !== 'undefined') {
return this._attributes[fieldName];
}
if (typeof this._relationships[fieldName] !== 'undefined') {
return this._relationships[fieldName];
}
if (strict) {
throw new Error("Failed to find field ".concat(fieldName, "."));
}
return null;
}
/**
* Set field value.
*
* @param {string} fieldName
* @param {any} fieldValue
* @param {boolean} strict default: false
*/
}, {
key: "set",
value: function set(fieldName, fieldValue) {
var strict = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
if (this._attributes[fieldName]) {
this.setAttribute(fieldName, fieldValue);
return true;
}
if (this._relationships[fieldName]) {
this.setRelationship(fieldName, fieldValue);
return true;
}
if (strict) {
throw new Error("Failed to set ".concat(fieldName, " to ").concat(fieldValue, ". Field does not exist."));
}
return false;
}
/**
* Get local changes for this entity.
*
* @param {string} fieldName
*/
}, {
key: "getChange",
value: function getChange(fieldName) {
return this._changes.attributes[fieldName] !== undefined ? this._changes.attributes[fieldName] : this._changes.relationships[fieldName];
}
/**
* Get an expanded representation of a relationship.
*
* @param {string} fieldName
*/
}, {
key: "expand",
value: function () {
var _expand = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee4(fieldName) {
var _this$_relationships$, _this$_relationships$2, entityType, entityBundle;
return regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
if (this._relationships[fieldName]) {
_context4.next = 2;
break;
}
throw new _MalformedEntity.default("Failed to find related entity from field ".concat(fieldName));
case 2:
if (!(this._relationships[fieldName].data && this._relationships[fieldName].data.type && typeof this._relationships[fieldName].data.type === 'string' && this._relationships[fieldName].data.id)) {
_context4.next = 5;
break;
}
_this$_relationships$ = this._relationships[fieldName].data.type.split('--'), _this$_relationships$2 = _slicedToArray(_this$_relationships$, 2), entityType = _this$_relationships$2[0], entityBundle = _this$_relationships$2[1];
return _context4.abrupt("return", Entity.Load(entityType, entityBundle, this._relationships[fieldName].data.id));
case 5:
if (!(this._relationships[fieldName].data && this._relationships[fieldName].data.constructor === Array)) {
_context4.next = 7;
break;
}
return _context4.abrupt("return", Promise.all(this._relationships[fieldName].data.map(function (item) {
if (typeof item.type === 'string' && typeof item.id === 'string') {
var _item$type$split = item.type.split('--'),
_item$type$split2 = _slicedToArray(_item$type$split, 2),
_entityType = _item$type$split2[0],
_entityBundle = _item$type$split2[1];
return Entity.Load(_entityType, _entityBundle, item.id);
}
return null;
}).filter(function (promise) {
return !!promise;
})));
case 7:
throw new _MalformedEntity.default("Related field ".concat(fieldName, " doesn't have sufficient information to expand."));
case 8:
case "end":
return _context4.stop();
}
}
}, _callee4, this);
}));
function expand(_x8) {
return _expand.apply(this, arguments);
}
return expand;
}()
/**
* Set an attribute.
*
* @param {string} fieldName - Drupal machine name for the field
* @param {any} fieldValue - value to send to JSON:API
*/
}, {
key: "setAttribute",
value: function setAttribute(fieldName, fieldValue) {
this._attributes[fieldName] = fieldValue;
this._changes.attributes[fieldName] = fieldValue;
}
/**
* Set a relationship.
*
* @param {string} fieldName - Drupal machine name for the field
* @param {any} fieldValue - value to send to JSON:API
*/
}, {
key: "setRelationship",
value: function setRelationship(fieldName, fieldValue) {
var value = fieldValue;
if (fieldValue instanceof Entity) {
value = {
data: {
type: "".concat(fieldValue.entityType, "--").concat(fieldValue.entityBundle),
id: fieldValue.entityUuid
}
};
}
if (fieldValue.constructor === Array) {
if (fieldValue.every(function (entity) {
return entity instanceof Entity;
})) {
value = {
data: fieldValue.map(function (entity) {
return {
type: "".concat(entity.entityType, "--").concat(entity.entityBundle),
id: entity.entityUuid
};
})
};
}
}
this._relationships[fieldName] = value;
this._changes.relationships[fieldName] = value;
}
/**
* Save this entity.
*/
}, {
key: "save",
value: function save() {
return _GlobalClient.default.send(this.enforceNew === true || !this.entityUuid ? {
url: "/jsonapi/".concat(this.entityType, "/").concat(this.entityBundle),
method: 'POST',
headers: _objectSpread({}, TypeHeaders),
data: JSON.stringify(this._serialize(false))
} : {
url: "/jsonapi/".concat(this.entityType, "/").concat(this.entityBundle, "/").concat(this.entityUuid),
method: 'PATCH',
headers: _objectSpread({}, TypeHeaders),
data: JSON.stringify(this._serializeChanges())
});
}
/**
* Delete this entity.
*/
}, {
key: "delete",
value: function _delete() {
if (!this.entityUuid) {
throw new _MalformedEntity.default('Cannot delete an entity without a UUID.');
}
return _GlobalClient.default.send({
url: "/jsonapi/".concat(this.entityType, "/").concat(this.entityBundle, "/").concat(this.entityUuid),
method: 'DELETE',
headers: _objectSpread({}, TypeHeaders)
});
}
/**
* Create a copy of this entity.
*/
}, {
key: "copy",
value: function copy() {
var withUuid = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var copy = new Entity(this.entityType, this.entityBundle);
copy._attributes = this._attributes;
copy._relationships = this._relationships;
copy._changes = this._changes;
if (withUuid) {
copy.entityUuid = this.entityUuid;
}
return copy;
}
}]);
return Entity;
}();
exports.default = Entity;
_defineProperty(Entity, "Cache", {});