syncano-server
Advanced tools
Comparing version 0.8.1 to 0.8.2-0
@@ -61,4 +61,4 @@ 'use strict'; | ||
}; | ||
fetch(_this2.url(), {}, headers).then(function (resp) { | ||
return resolve(resp); | ||
fetch(_this2.url(), {}, headers).then(function (res) { | ||
return resolve(res); | ||
}).catch(function (err) { | ||
@@ -65,0 +65,0 @@ return reject(err); |
338
lib/data.js
@@ -7,2 +7,6 @@ 'use strict'; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var _slicedToArray = function () { function sliceIterator(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"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); | ||
var _createClass = 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
@@ -14,2 +18,18 @@ | ||
var _formData = require('form-data'); | ||
var _formData2 = _interopRequireDefault(_formData); | ||
var _lodash = require('lodash.set'); | ||
var _lodash2 = _interopRequireDefault(_lodash); | ||
var _lodash3 = require('lodash.get'); | ||
var _lodash4 = _interopRequireDefault(_lodash3); | ||
var _lodash5 = require('lodash.merge'); | ||
var _lodash6 = _interopRequireDefault(_lodash5); | ||
var _queryBuilder = require('./query-builder'); | ||
@@ -63,3 +83,47 @@ | ||
} | ||
}, { | ||
key: '_batchBodyBuilder', | ||
value: function _batchBodyBuilder(body) { | ||
var _instance2 = this.instance, | ||
instanceName = _instance2.instanceName, | ||
className = _instance2.className, | ||
apiVersion = _instance2.apiVersion; | ||
var path = '/' + apiVersion + '/instances/' + instanceName + '/classes/' + className + '/objects/'; | ||
return body.reduce(function (data, item) { | ||
var singleRequest = { | ||
method: 'POST', | ||
path: path | ||
}; | ||
if (Array.isArray(item)) { | ||
singleRequest.method = 'PATCH'; | ||
singleRequest.path = '' + path + item[0] + '/'; | ||
singleRequest.body = JSON.stringify(item[1]); | ||
} else if (isNaN(item) === false) { | ||
singleRequest.method = 'DELETE'; | ||
singleRequest.path = '' + path + item + '/'; | ||
} else { | ||
singleRequest.body = JSON.stringify(item); | ||
} | ||
data.requests.push(singleRequest); | ||
return data; | ||
}, { requests: [] }); | ||
} | ||
}, { | ||
key: '_batchFetchObject', | ||
value: function _batchFetchObject(body) { | ||
var instanceName = this.instance.instanceName; | ||
return { | ||
url: (0, _utils.buildInstanceURL)(instanceName) + '/batch/', | ||
method: 'POST', | ||
body: JSON.stringify(this._batchBodyBuilder(body)) | ||
}; | ||
} | ||
/** | ||
@@ -87,3 +151,5 @@ * List objects matching query. | ||
relationships = this.relationships, | ||
instance = this.instance; | ||
instance = this.instance, | ||
mappedFields = this.mappedFields, | ||
_mapFields = this._mapFields; | ||
@@ -97,3 +163,3 @@ var fetch = this.fetch.bind(this); | ||
function request(url) { | ||
fetch(url).then(saveToResult).then(loadNextPage).then(resolveRelatedModels).then(replaceCustomTypesWithValue).then(resolveIfFinished).catch(function (err) { | ||
fetch(url).then(saveToResult).then(loadNextPage).then(resolveRelatedModels).then(replaceCustomTypesWithValue).then(mapResultFields).then(resolveIfFinished).catch(function (err) { | ||
return reject(err); | ||
@@ -222,2 +288,12 @@ }); | ||
function mapResultFields(shouldResolve) { | ||
if (shouldResolve === false) { | ||
return; | ||
} | ||
result = _mapFields(result, mappedFields); | ||
return true; | ||
} | ||
function resolveIfFinished(shouldResolve) { | ||
@@ -235,2 +311,11 @@ if (shouldResolve) { | ||
}, { | ||
key: '_mapFields', | ||
value: function _mapFields(items, fields) { | ||
return fields.length === 0 ? items : items.map(function (item) { | ||
return Object.keys(fields).reduce(function (all, key) { | ||
return (0, _lodash2.default)(all, fields[key] || key, (0, _lodash4.default)(item, key)); | ||
}, {}); | ||
}); | ||
} | ||
}, { | ||
key: '_getRelatedObjects', | ||
@@ -271,3 +356,3 @@ value: function _getRelatedObjects(reference, items) { | ||
/** | ||
* Get first element matching query or throw erro'status', 'in', ['draft', 'published']se} | ||
* Get first element matching query or throw error | ||
* | ||
@@ -293,2 +378,55 @@ * @example {@lang javascript} | ||
/** | ||
* Get the first record matching the attributes or create it. | ||
* | ||
* @example {@lang javascript} | ||
* const post = await data.posts | ||
* .updateOrCreate({name: 'value to match'}, {content: 'value to update'}) | ||
*/ | ||
}, { | ||
key: 'firstOrCreate', | ||
value: function firstOrCreate(attributes) { | ||
var _this4 = this; | ||
var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var query = this._toWhereArray(attributes); | ||
return this.where(query).firstOrFail().catch(function () { | ||
return _this4.create((0, _lodash6.default)(attributes, values)); | ||
}); | ||
} | ||
/** | ||
* Create or update a record matching the attributes, and fill it with values. | ||
* | ||
* @example {@lang javascript} | ||
* const post = await data.posts | ||
* .updateOrCreate({name: 'value to match'}, {content: 'value to update'}) | ||
*/ | ||
}, { | ||
key: 'updateOrCreate', | ||
value: function updateOrCreate(attributes) { | ||
var _this5 = this; | ||
var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var query = this._toWhereArray(attributes); | ||
return this.where(query).firstOrFail().then(function (res) { | ||
return _this5.update(res.id, values); | ||
}).catch(function () { | ||
return _this5.create((0, _lodash6.default)(attributes, values)); | ||
}); | ||
} | ||
}, { | ||
key: '_toWhereArray', | ||
value: function _toWhereArray(attributes) { | ||
return Object.keys(attributes).map(function (key) { | ||
return [key, 'eq', attributes[key]]; | ||
}); | ||
} | ||
/** | ||
* Get single object by id or objects list if ids passed as array. | ||
@@ -331,6 +469,6 @@ * | ||
value: function findOrFail(ids) { | ||
var _this4 = this; | ||
var _this6 = this; | ||
return new Promise(function (resolve, reject) { | ||
_this4.find(ids).then(function (response) { | ||
_this6.find(ids).then(function (response) { | ||
var shouldThrow = Array.isArray(ids) ? response.length !== ids.length : response === null; | ||
@@ -402,2 +540,18 @@ | ||
value: function where(column, operator, value) { | ||
var _this7 = this; | ||
if (Array.isArray(column)) { | ||
column.map(function (_ref2) { | ||
var _ref3 = _slicedToArray(_ref2, 3), | ||
itemColumn = _ref3[0], | ||
itemOperator = _ref3[1], | ||
itemValue = _ref3[2]; | ||
return _this7.where(itemColumn, itemOperator, itemValue); | ||
}); | ||
return this; | ||
} | ||
operator = this._normalizeWhereOperator(operator); | ||
var whereOperator = value ? '_' + operator : '_eq'; | ||
@@ -414,8 +568,57 @@ var whereValue = value === undefined ? operator : value; | ||
var query = Object.assign(currentQuery, nextQuery); | ||
var query = (0, _lodash6.default)({}, currentQuery, nextQuery); | ||
return this.withQuery({ query: JSON.stringify(query) }); | ||
} | ||
}, { | ||
key: '_normalizeWhereOperator', | ||
value: function _normalizeWhereOperator(operator) { | ||
var operators = { | ||
'<': 'lt', | ||
'<=': 'lte', | ||
'>': 'gt', | ||
'>=': 'gte', | ||
'=': 'eq', | ||
'!=': 'neq', | ||
'<>': 'neq' | ||
}; | ||
return operators[operator] || operator; | ||
} | ||
/** | ||
* Whitelist returned keys. | ||
* | ||
* @returns {Promise} | ||
* | ||
* @example {@lang javascript} | ||
* const posts = await data.users.fields('name', 'email as username')->list() | ||
*/ | ||
}, { | ||
key: 'fields', | ||
value: function fields() { | ||
for (var _len = arguments.length, _fields = Array(_len), _key = 0; _key < _len; _key++) { | ||
_fields[_key] = arguments[_key]; | ||
} | ||
if (Array.isArray(_fields[0])) { | ||
_fields = _fields[0]; | ||
} | ||
var fieldsToMap = _fields.map(function (field) { | ||
var _field$match = field.match(/([\w_\-.]*)(\sas\s)?(.*)?/), | ||
_field$match2 = _slicedToArray(_field$match, 4), | ||
from = _field$match2[1], | ||
to = _field$match2[3]; | ||
return _defineProperty({}, from, to); | ||
}); | ||
this.withMappedFields(fieldsToMap); | ||
return this; | ||
} | ||
/** | ||
* Expand references and relationships. | ||
@@ -434,4 +637,4 @@ * | ||
value: function _with() { | ||
for (var _len = arguments.length, models = Array(_len), _key = 0; _key < _len; _key++) { | ||
models[_key] = arguments[_key]; | ||
for (var _len2 = arguments.length, models = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
models[_key2] = arguments[_key2]; | ||
} | ||
@@ -445,2 +648,38 @@ | ||
/** | ||
* Get values of single column. | ||
* | ||
* @returns {Promise} | ||
* | ||
* @example {@lang javascript} | ||
* data.posts.where('id', 10).pluck('title') | ||
*/ | ||
}, { | ||
key: 'pluck', | ||
value: function pluck(column) { | ||
return this.list().then(function (items) { | ||
return items.map(function (item) { | ||
return item[column]; | ||
}); | ||
}); | ||
} | ||
/** | ||
* Get value of single record column field. | ||
* | ||
* @returns {Promise} | ||
* | ||
* @example {@lang javascript} | ||
* data.posts.where('id', 10).value('title') | ||
*/ | ||
}, { | ||
key: 'value', | ||
value: function value(column) { | ||
return this.first().then(function (item) { | ||
return item[column]; | ||
}); | ||
} | ||
/** | ||
* Create new object. | ||
@@ -455,2 +694,6 @@ * | ||
* }) | ||
* data.posts.create([ | ||
* { content: 'Lorem ipsum!' }, | ||
* { content: 'More lorem ipsum!' } | ||
* ]) | ||
*/ | ||
@@ -461,6 +704,17 @@ | ||
value: function create(body) { | ||
return this.fetch(this.url(), { | ||
var headers = null; | ||
var fetchObject = { | ||
url: this.url(), | ||
method: 'POST', | ||
body: JSON.stringify(body) | ||
}); | ||
}; | ||
if (body instanceof _formData2.default) { | ||
fetchObject.body = body; | ||
headers = body.getHeaders(); | ||
} else if (Array.isArray(body)) { | ||
fetchObject = this._batchFetchObject(body); | ||
} | ||
return this.fetch(fetchObject.url, fetchObject, headers); | ||
} | ||
@@ -475,2 +729,11 @@ | ||
* data.posts.update(55, { content: 'No more lorem ipsum!' }) | ||
* data.posts.update([ | ||
* [55, { content: 'No more lorem ipsum!' }], | ||
* [56, { content: 'No more lorem ipsum!' }] | ||
* ]) | ||
* data.posts.update({title: 'Update all posts title'}) | ||
* data.flights | ||
* .where('active', 1) | ||
* .where('destination', 'Warsaw') | ||
* .update({delayed: 1}) | ||
*/ | ||
@@ -481,6 +744,28 @@ | ||
value: function update(id, body) { | ||
return this.fetch(this.url(id), { | ||
var _this8 = this; | ||
var isQueryUpdate = (typeof id === 'undefined' ? 'undefined' : _typeof(id)) === 'object' && id !== null && !Array.isArray(id); | ||
var fetchObject = { | ||
url: this.url(id), | ||
method: 'PATCH', | ||
body: JSON.stringify(body) | ||
}); | ||
}; | ||
if (isQueryUpdate) { | ||
return this.list().then(function (items) { | ||
var ids = items.map(function (item) { | ||
return [item.id, id]; | ||
}); | ||
fetchObject = _this8._batchFetchObject(ids); | ||
return _this8.fetch(fetchObject.url, fetchObject); | ||
}); | ||
} | ||
if (Array.isArray(id)) { | ||
fetchObject = this._batchFetchObject(id); | ||
} | ||
return this.fetch(fetchObject.url, fetchObject); | ||
} | ||
@@ -495,2 +780,5 @@ | ||
* data.posts.delete(55) | ||
* data.posts.delete([55, 56, 57]) | ||
* data.posts.delete() | ||
* data.posts.where('draft', 1).delete() | ||
*/ | ||
@@ -501,5 +789,27 @@ | ||
value: function _delete(id) { | ||
return this.fetch(this.url(id), { | ||
var _this9 = this; | ||
var isQueryDelete = id === undefined; | ||
var fetchObject = { | ||
url: this.url(id), | ||
method: 'DELETE' | ||
}); | ||
}; | ||
if (isQueryDelete) { | ||
return this.list().then(function (items) { | ||
var ids = items.map(function (item) { | ||
return item.id; | ||
}); | ||
fetchObject = _this9._batchFetchObject(ids); | ||
return _this9.fetch(fetchObject.url, fetchObject); | ||
}); | ||
} | ||
if (Array.isArray(id)) { | ||
fetchObject = this._batchFetchObject(id); | ||
} | ||
return this.fetch(fetchObject.url, fetchObject); | ||
} | ||
@@ -506,0 +816,0 @@ }]); |
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.data = exports.response = exports.logger = exports.socket = exports.event = exports.channel = exports.instance = exports.account = exports.users = undefined; | ||
exports._class = exports.data = exports.response = exports.logger = exports.socket = exports.event = exports.channel = exports.instance = exports.account = exports.users = undefined; | ||
@@ -24,3 +24,4 @@ var _server = require('./server'); | ||
response = _ref.response, | ||
data = _ref.data; | ||
data = _ref.data, | ||
_class = _ref._class; | ||
@@ -36,2 +37,3 @@ exports.users = users; | ||
exports.data = data; | ||
exports._class = _class; | ||
exports.default = _server2.default; |
@@ -21,2 +21,4 @@ 'use strict'; | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -34,8 +36,12 @@ | ||
value: function fetch(url, options) { | ||
var headers = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var headersToSend = Object.assign({ | ||
'content-type': 'application/json', | ||
'x-api-key': this.instance.token | ||
}, headers); | ||
return (0, _nodeFetch2.default)(url, _extends({ | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'X-API-KEY': this.instance.token | ||
} | ||
}, options)).then(_utils.checkStatus).then(_utils.parseJSON); | ||
headers: headersToSend | ||
}, options)).then(_utils.parseJSON).then(_utils.checkStatus); | ||
} | ||
@@ -47,5 +53,5 @@ }, { | ||
headers: _extends({ | ||
'Content-Type': 'application/json' | ||
'content-type': 'application/json' | ||
}, headers) | ||
}, options)).then(_utils.checkStatus).then(_utils.parseJSON); | ||
}, options)).then(_utils.parseJSON).then(_utils.checkStatus); | ||
} | ||
@@ -67,2 +73,9 @@ }, { | ||
}, { | ||
key: 'withMappedFields', | ||
value: function withMappedFields(fields) { | ||
this._mappedFields = Object.assign.apply(Object, [{}, this.mappedFields].concat(_toConsumableArray(fields))); | ||
return this; | ||
} | ||
}, { | ||
key: 'query', | ||
@@ -77,2 +90,7 @@ get: function get() { | ||
} | ||
}, { | ||
key: 'mappedFields', | ||
get: function get() { | ||
return this._mappedFields || []; | ||
} | ||
}]); | ||
@@ -79,0 +97,0 @@ |
@@ -51,2 +51,4 @@ 'use strict'; | ||
global.setResponse(new (Function.prototype.bind.apply(global.HttpResponse, [null].concat(args)))()); | ||
process.exit(0); // eslint-disable-line unicorn/no-process-exit | ||
} | ||
@@ -53,0 +55,0 @@ }]); |
@@ -43,2 +43,6 @@ 'use strict'; | ||
var _class2 = require('./class'); | ||
var _class3 = _interopRequireDefault(_class2); | ||
var _settings = require('./settings'); | ||
@@ -74,2 +78,4 @@ | ||
socket.instance = instanceConfig; | ||
var _class = new _class3.default(); | ||
_class.instance = instanceConfig; | ||
@@ -86,2 +92,3 @@ var account = new _account2.default({ accountKey: options.accountKey }); | ||
socket: socket, | ||
_class: _class, | ||
logger: _logger2.default, | ||
@@ -88,0 +95,0 @@ response: _response2.default, |
@@ -6,2 +6,5 @@ 'use strict'; | ||
}); | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
exports.checkStatus = checkStatus; | ||
@@ -16,8 +19,20 @@ exports.parseJSON = parseJSON; | ||
if (response.status >= 200 && response.status < 300) { | ||
return response; | ||
return response.data; | ||
} | ||
var error = new Error(response.statusText); | ||
var error = void 0; | ||
try { | ||
error = new Error(response.data.detail); | ||
} catch (err) { | ||
error = new Error(response.statusText); | ||
} | ||
error.response = response; | ||
error.data = response.data; | ||
error.status = response.status; | ||
error.headers = response.headers; | ||
error.size = response.size; | ||
error.timeout = response.timeout; | ||
error.url = response.url; | ||
@@ -31,3 +46,5 @@ throw error; | ||
if (response.status === 204 || mimetype === null) { | ||
return Promise.resolve(); | ||
return Promise.resolve(_extends({ | ||
data: undefined | ||
}, response)); | ||
} | ||
@@ -37,3 +54,7 @@ | ||
if (/^.*\/.*\+json/.test(mimetype) || /^application\/json/.test(mimetype)) { | ||
return response.json(); | ||
return response.json().then(function (res) { | ||
return _extends({ | ||
data: res | ||
}, response); | ||
}); | ||
} | ||
@@ -43,3 +64,7 @@ | ||
if (/^text\/.*/.test(mimetype) || /^.*\/.*\+xml/.test(mimetype) || mimetype === 'text/plain') { | ||
return response.text(); | ||
return response.text().then(function (res) { | ||
return _extends({ | ||
data: res | ||
}, response); | ||
}); | ||
} | ||
@@ -46,0 +71,0 @@ |
{ | ||
"name": "syncano-server", | ||
"version": "0.8.1", | ||
"version": "0.8.2-0", | ||
"description": "A library to intereact with the Syncano API on a server side", | ||
@@ -28,5 +28,6 @@ "main": "lib/index.js", | ||
"e2e": "mkdir e2e-tests || true && cross-env NODE_ENV=test mocha tests/e2e/*.js --reporter spec --require babel-register --timeout 30000 --slow 8000", | ||
"e2e:single": "mkdir e2e-tests || true && cross-env NODE_ENV=test mocha --reporter spec --require babel-register --timeout 30000 --slow 8000", | ||
"watch": "npm-run-all --parallel watch:*", | ||
"watch:lib": "npm run build:lib -- --watch", | ||
"watch:lib:test": "mocha --watch tests/**/*.js --compilers js:babel-register", | ||
"watch:lib:test": "mocha --watch tests/unit/**/*.js --compilers js:babel-register", | ||
"release:docs": "gh-pages -d docs -m \"Docs v${npm_package_version} [ci skip]\"", | ||
@@ -44,2 +45,6 @@ "release:reset": "git reset --hard $CIRCLE_SHA1", | ||
"dependencies": { | ||
"form-data": "^2.1.4", | ||
"lodash.get": "^4.4.2", | ||
"lodash.set": "^4.3.2", | ||
"lodash.merge": "^4.6.0", | ||
"node-fetch": "2.0.0-alpha.3" | ||
@@ -46,0 +51,0 @@ }, |
@@ -78,3 +78,3 @@ [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo) [![CircleCI](https://circleci.com/gh/Syncano/syncano-server-js/tree/devel.svg?style=shield&circle-token=0340c11444db6f3dc227cf310f4d8ff1bd90dee8)](https://circleci.com/gh/Syncano/syncano-server-js/tree/devel) | ||
// Get first user with given mail | ||
data.users | ||
users | ||
.where('email', 'john.doe@example.com') | ||
@@ -88,3 +88,3 @@ .first() | ||
// Get first user with given mail, throws error if user was not found | ||
data.users | ||
users | ||
.where('email', 'john.doe@example.com') | ||
@@ -156,8 +156,5 @@ .firstOrFail() | ||
The DEBUG environment variable is used to enable logging. | ||
For debug purposes you can use `logger`: | ||
On Windows: | ||
``` | ||
set DEBUG=*,-not_this | ||
``` | ||
Example: | ||
@@ -164,0 +161,0 @@ ```js |
@@ -28,3 +28,3 @@ import QueryBuilder from './query-builder' | ||
fetch(this.url(), {}, headers) | ||
.then(resp => resolve(resp)) | ||
.then(res => resolve(res)) | ||
.catch(err => reject(err)) | ||
@@ -31,0 +31,0 @@ }) |
254
src/data.js
import querystring from 'querystring' | ||
import FormData from 'form-data' | ||
import set from 'lodash.set' | ||
import get from 'lodash.get' | ||
import merge from 'lodash.merge' | ||
import QueryBuilder from './query-builder' | ||
@@ -23,2 +27,39 @@ import {NotFoundError} from './errors' | ||
_batchBodyBuilder(body) { | ||
const {instanceName, className, apiVersion} = this.instance | ||
const path = `/${apiVersion}/instances/${instanceName}/classes/${className}/objects/` | ||
return body.reduce((data, item) => { | ||
const singleRequest = { | ||
method: 'POST', | ||
path | ||
} | ||
if (Array.isArray(item)) { | ||
singleRequest.method = 'PATCH' | ||
singleRequest.path = `${path}${item[0]}/` | ||
singleRequest.body = JSON.stringify(item[1]) | ||
} else if (isNaN(item) === false) { | ||
singleRequest.method = 'DELETE' | ||
singleRequest.path = `${path}${item}/` | ||
} else { | ||
singleRequest.body = JSON.stringify(item) | ||
} | ||
data.requests.push(singleRequest) | ||
return data | ||
}, {requests: []}) | ||
} | ||
_batchFetchObject(body) { | ||
const {instanceName} = this.instance | ||
return { | ||
url: `${buildInstanceURL(instanceName)}/batch/`, | ||
method: 'POST', | ||
body: JSON.stringify(this._batchBodyBuilder(body)) | ||
} | ||
} | ||
/** | ||
@@ -39,3 +80,3 @@ * List objects matching query. | ||
const self = this | ||
const {baseUrl, relationships, instance} = this | ||
const {baseUrl, relationships, instance, mappedFields, _mapFields} = this | ||
const fetch = this.fetch.bind(this) | ||
@@ -53,2 +94,3 @@ const pageSize = this.query.page_size || 0 | ||
.then(replaceCustomTypesWithValue) | ||
.then(mapResultFields) | ||
.then(resolveIfFinished) | ||
@@ -173,2 +215,12 @@ .catch(err => reject(err)) | ||
function mapResultFields(shouldResolve) { | ||
if (shouldResolve === false) { | ||
return | ||
} | ||
result = _mapFields(result, mappedFields) | ||
return true | ||
} | ||
function resolveIfFinished(shouldResolve) { | ||
@@ -186,2 +238,10 @@ if (shouldResolve) { | ||
_mapFields(items, fields) { | ||
return fields.length === 0 ? items : items.map(item => | ||
Object.keys(fields).reduce((all, key) => | ||
set(all, fields[key] || key, get(item, key)) | ||
, {}) | ||
) | ||
} | ||
_getRelatedObjects(reference, items) { | ||
@@ -212,3 +272,3 @@ if (!reference) { | ||
/** | ||
* Get first element matching query or throw erro'status', 'in', ['draft', 'published']se} | ||
* Get first element matching query or throw error | ||
* | ||
@@ -230,2 +290,39 @@ * @example {@lang javascript} | ||
/** | ||
* Get the first record matching the attributes or create it. | ||
* | ||
* @example {@lang javascript} | ||
* const post = await data.posts | ||
* .updateOrCreate({name: 'value to match'}, {content: 'value to update'}) | ||
*/ | ||
firstOrCreate(attributes, values = {}) { | ||
const query = this._toWhereArray(attributes) | ||
return this | ||
.where(query) | ||
.firstOrFail() | ||
.catch(() => this.create(merge(attributes, values))) | ||
} | ||
/** | ||
* Create or update a record matching the attributes, and fill it with values. | ||
* | ||
* @example {@lang javascript} | ||
* const post = await data.posts | ||
* .updateOrCreate({name: 'value to match'}, {content: 'value to update'}) | ||
*/ | ||
updateOrCreate(attributes, values = {}) { | ||
const query = this._toWhereArray(attributes) | ||
return this | ||
.where(query) | ||
.firstOrFail() | ||
.then(res => this.update(res.id, values)) | ||
.catch(() => this.create(merge(attributes, values))) | ||
} | ||
_toWhereArray(attributes) { | ||
return Object.keys(attributes).map(key => [key, 'eq', attributes[key]]) | ||
} | ||
/** | ||
* Get single object by id or objects list if ids passed as array. | ||
@@ -322,2 +419,11 @@ * | ||
where(column, operator, value) { | ||
if (Array.isArray(column)) { | ||
column.map(([itemColumn, itemOperator, itemValue]) => | ||
this.where(itemColumn, itemOperator, itemValue) | ||
) | ||
return this | ||
} | ||
operator = this._normalizeWhereOperator(operator) | ||
const whereOperator = value ? `_${operator}` : '_eq' | ||
@@ -337,3 +443,3 @@ const whereValue = value === undefined ? operator : value | ||
const query = Object.assign(currentQuery, nextQuery) | ||
const query = merge({}, currentQuery, nextQuery) | ||
@@ -343,3 +449,42 @@ return this.withQuery({query: JSON.stringify(query)}) | ||
_normalizeWhereOperator(operator) { | ||
const operators = { | ||
'<': 'lt', | ||
'<=': 'lte', | ||
'>': 'gt', | ||
'>=': 'gte', | ||
'=': 'eq', | ||
'!=': 'neq', | ||
'<>': 'neq' | ||
} | ||
return operators[operator] || operator | ||
} | ||
/** | ||
* Whitelist returned keys. | ||
* | ||
* @returns {Promise} | ||
* | ||
* @example {@lang javascript} | ||
* const posts = await data.users.fields('name', 'email as username')->list() | ||
*/ | ||
fields(...fields) { | ||
if (Array.isArray(fields[0])) { | ||
fields = fields[0] | ||
} | ||
const fieldsToMap = fields | ||
.map(field => { | ||
const [, from,, to] = field.match(/([\w_\-.]*)(\sas\s)?(.*)?/) | ||
return {[from]: to} | ||
}) | ||
this.withMappedFields(fieldsToMap) | ||
return this | ||
} | ||
/** | ||
* Expand references and relationships. | ||
@@ -361,2 +506,26 @@ * | ||
/** | ||
* Get values of single column. | ||
* | ||
* @returns {Promise} | ||
* | ||
* @example {@lang javascript} | ||
* data.posts.where('id', 10).pluck('title') | ||
*/ | ||
pluck(column) { | ||
return this.list().then(items => items.map(item => item[column])) | ||
} | ||
/** | ||
* Get value of single record column field. | ||
* | ||
* @returns {Promise} | ||
* | ||
* @example {@lang javascript} | ||
* data.posts.where('id', 10).value('title') | ||
*/ | ||
value(column) { | ||
return this.first().then(item => item[column]) | ||
} | ||
/** | ||
* Create new object. | ||
@@ -371,8 +540,23 @@ * | ||
* }) | ||
* data.posts.create([ | ||
* { content: 'Lorem ipsum!' }, | ||
* { content: 'More lorem ipsum!' } | ||
* ]) | ||
*/ | ||
create(body) { | ||
return this.fetch(this.url(), { | ||
let headers = null | ||
let fetchObject = { | ||
url: this.url(), | ||
method: 'POST', | ||
body: JSON.stringify(body) | ||
}) | ||
} | ||
if (body instanceof FormData) { | ||
fetchObject.body = body | ||
headers = body.getHeaders() | ||
} else if (Array.isArray(body)) { | ||
fetchObject = this._batchFetchObject(body) | ||
} | ||
return this.fetch(fetchObject.url, fetchObject, headers) | ||
} | ||
@@ -387,8 +571,37 @@ | ||
* data.posts.update(55, { content: 'No more lorem ipsum!' }) | ||
* data.posts.update([ | ||
* [55, { content: 'No more lorem ipsum!' }], | ||
* [56, { content: 'No more lorem ipsum!' }] | ||
* ]) | ||
* data.posts.update({title: 'Update all posts title'}) | ||
* data.flights | ||
* .where('active', 1) | ||
* .where('destination', 'Warsaw') | ||
* .update({delayed: 1}) | ||
*/ | ||
update(id, body) { | ||
return this.fetch(this.url(id), { | ||
const isQueryUpdate = typeof id === 'object' && id !== null && !Array.isArray(id) | ||
let fetchObject = { | ||
url: this.url(id), | ||
method: 'PATCH', | ||
body: JSON.stringify(body) | ||
}) | ||
} | ||
if (isQueryUpdate) { | ||
return this | ||
.list() | ||
.then(items => { | ||
const ids = items.map(item => [item.id, id]) | ||
fetchObject = this._batchFetchObject(ids) | ||
return this.fetch(fetchObject.url, fetchObject) | ||
}) | ||
} | ||
if (Array.isArray(id)) { | ||
fetchObject = this._batchFetchObject(id) | ||
} | ||
return this.fetch(fetchObject.url, fetchObject) | ||
} | ||
@@ -403,7 +616,30 @@ | ||
* data.posts.delete(55) | ||
* data.posts.delete([55, 56, 57]) | ||
* data.posts.delete() | ||
* data.posts.where('draft', 1).delete() | ||
*/ | ||
delete(id) { | ||
return this.fetch(this.url(id), { | ||
const isQueryDelete = id === undefined | ||
let fetchObject = { | ||
url: this.url(id), | ||
method: 'DELETE' | ||
}) | ||
} | ||
if (isQueryDelete) { | ||
return this | ||
.list() | ||
.then(items => { | ||
const ids = items.map(item => item.id) | ||
fetchObject = this._batchFetchObject(ids) | ||
return this.fetch(fetchObject.url, fetchObject) | ||
}) | ||
} | ||
if (Array.isArray(id)) { | ||
fetchObject = this._batchFetchObject(id) | ||
} | ||
return this.fetch(fetchObject.url, fetchObject) | ||
} | ||
@@ -410,0 +646,0 @@ } |
@@ -12,5 +12,6 @@ import Server from './server' | ||
response, | ||
data | ||
data, | ||
_class | ||
} = new Server() | ||
export default Server |
@@ -10,12 +10,14 @@ import nodeFetch from 'node-fetch' | ||
fetch(url, options) { | ||
fetch(url, options, headers = {}) { | ||
const headersToSend = Object.assign({ | ||
'content-type': 'application/json', | ||
'x-api-key': this.instance.token | ||
}, headers) | ||
return nodeFetch(url, { | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'X-API-KEY': this.instance.token | ||
}, | ||
headers: headersToSend, | ||
...options | ||
}) | ||
.then(parseJSON) | ||
.then(checkStatus) | ||
.then(parseJSON) | ||
} | ||
@@ -26,3 +28,3 @@ | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'content-type': 'application/json', | ||
...headers | ||
@@ -32,4 +34,4 @@ }, | ||
}) | ||
.then(parseJSON) | ||
.then(checkStatus) | ||
.then(parseJSON) | ||
} | ||
@@ -45,2 +47,6 @@ | ||
get mappedFields() { | ||
return this._mappedFields || [] | ||
} | ||
withQuery(query) { | ||
@@ -57,2 +63,8 @@ this._query = Object.assign({}, this.query, query) | ||
} | ||
withMappedFields(fields) { | ||
this._mappedFields = Object.assign({}, this.mappedFields, ...fields) | ||
return this | ||
} | ||
} |
@@ -28,2 +28,4 @@ /** | ||
global.setResponse(new global.HttpResponse(...args)) | ||
process.exit(0) // eslint-disable-line unicorn/no-process-exit | ||
} | ||
@@ -30,0 +32,0 @@ } |
@@ -10,2 +10,3 @@ import Data from './data' | ||
import Channel from './channel' | ||
import Class from './class' | ||
import { | ||
@@ -43,2 +44,4 @@ getToken, | ||
socket.instance = instanceConfig | ||
const _class = new Class() | ||
_class.instance = instanceConfig | ||
@@ -55,2 +58,3 @@ const account = new Account({accountKey: options.accountKey}) | ||
socket, | ||
_class, | ||
logger: Logger, | ||
@@ -57,0 +61,0 @@ response: Response, |
@@ -5,8 +5,20 @@ import {getHost, SYNCANO_API_VERSION} from './settings' | ||
if (response.status >= 200 && response.status < 300) { | ||
return response | ||
return response.data | ||
} | ||
const error = new Error(response.statusText) | ||
let error | ||
try { | ||
error = new Error(response.data.detail) | ||
} catch (err) { | ||
error = new Error(response.statusText) | ||
} | ||
error.response = response | ||
error.data = response.data | ||
error.status = response.status | ||
error.headers = response.headers | ||
error.size = response.size | ||
error.timeout = response.timeout | ||
error.url = response.url | ||
@@ -20,3 +32,6 @@ throw error | ||
if (response.status === 204 || mimetype === null) { | ||
return Promise.resolve() | ||
return Promise.resolve({ | ||
data: undefined, | ||
...response | ||
}) | ||
} | ||
@@ -29,3 +44,6 @@ | ||
) { | ||
return response.json() | ||
return response.json().then(res => ({ | ||
data: res, | ||
...response | ||
})) | ||
} | ||
@@ -39,3 +57,6 @@ | ||
) { | ||
return response.text() | ||
return response.text().then(res => ({ | ||
data: res, | ||
...response | ||
})) | ||
} | ||
@@ -42,0 +63,0 @@ |
@@ -0,2 +1,4 @@ | ||
/* eslint-disable no-unused-expressions */ | ||
import {expect} from 'chai' | ||
import Server from '../../src' | ||
@@ -46,3 +48,3 @@ import {getRandomString, createTestInstance, deleteTestInstance} from '../utils' | ||
.then(event => { | ||
expect(event).to.be.empty // eslint-disable-line no-unused-expressions | ||
expect(event).to.be.empty | ||
done() | ||
@@ -59,3 +61,3 @@ }) | ||
.then(event => { | ||
expect(event).to.be.empty // eslint-disable-line no-unused-expressions | ||
expect(event).to.be.empty | ||
done() | ||
@@ -62,0 +64,0 @@ }) |
@@ -0,1 +1,2 @@ | ||
/* eslint-disable no-unused-expressions */ | ||
import {expect} from 'chai' | ||
@@ -30,4 +31,4 @@ | ||
instance.delete(testInstanceName) | ||
.then(resp => { | ||
expect(resp).to.be.empty // eslint-disable-line no-unused-expressions | ||
.then(res => { | ||
expect(res).to.be.empty | ||
done() | ||
@@ -34,0 +35,0 @@ }) |
@@ -37,3 +37,2 @@ import {getRandomString, createTestInstance, deleteTestInstance} from '../utils' | ||
// .then(data => { | ||
// console.log("XXX", data) | ||
// done() | ||
@@ -40,0 +39,0 @@ // }) |
@@ -109,2 +109,20 @@ import nock from 'nock' | ||
describe('#firstOrCreate()', () => { | ||
it('should be a method of the model', () => { | ||
should(data.users).have.property('firstOrCreate').which.is.Function() | ||
}) | ||
it.skip('should be able to fetch single existing object') | ||
it.skip('should create and return object when it was not found') | ||
}) | ||
describe('#updateOrCreate()', () => { | ||
it('should be a method of the model', () => { | ||
should(data.users).have.property('updateOrCreate').which.is.Function() | ||
}) | ||
it.skip('should be able to update existing object') | ||
it.skip('should create object when it was not found') | ||
}) | ||
describe('#find()', () => { | ||
@@ -287,2 +305,4 @@ it('should be a method of the model', () => { | ||
}) | ||
it.skip('should be able to create multiple objects') | ||
}) | ||
@@ -311,2 +331,5 @@ | ||
}) | ||
it.skip('should be able to update multiple objects') | ||
it.skip('should be able to update objects by query') | ||
}) | ||
@@ -331,3 +354,31 @@ | ||
}) | ||
it.skip('should be able to delete objects by array of ids') | ||
it.skip('should be able to delete objects by query') | ||
}) | ||
describe('#fields()', () => { | ||
it('should be a method of the model', () => { | ||
should(data.users).have.property('fields').which.is.Function() | ||
}) | ||
it.skip('should be able to whitelist fields') | ||
it.skip('should be able to map field names') | ||
}) | ||
describe('#pluck()', () => { | ||
it('should be a method of the model', () => { | ||
should(data.users).have.property('pluck').which.is.Function() | ||
}) | ||
it.skip('should be able to take column values') | ||
}) | ||
describe('#value()', () => { | ||
it('should be a method of the model', () => { | ||
should(data.users).have.property('value').which.is.Function() | ||
}) | ||
it.skip('should be able to take column value of single record') | ||
}) | ||
}) |
@@ -17,5 +17,5 @@ import should from 'should/as-function' | ||
const {socket, signal} = Event._splitSignal(`${signalName}`) | ||
should(socket).be.undefined; | ||
should(socket).be.undefined | ||
should(signal).be.equal(signalName) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
900231
66
3753
5
181
14
18
+ Addedform-data@^2.1.4
+ Addedlodash.get@^4.4.2
+ Addedlodash.merge@^4.6.0
+ Addedlodash.set@^4.3.2
+ Addedasynckit@0.4.0(transitive)
+ Addedcombined-stream@1.0.8(transitive)
+ Addeddelayed-stream@1.0.0(transitive)
+ Addedform-data@2.5.2(transitive)
+ Addedlodash.get@4.4.2(transitive)
+ Addedlodash.merge@4.6.2(transitive)
+ Addedlodash.set@4.3.2(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
+ Addedsafe-buffer@5.2.1(transitive)