diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3bb77ba --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{js,jsx}] +indent_size = 2 diff --git a/src/api/document.js b/src/api/document.js index c7426df..3f551a6 100644 --- a/src/api/document.js +++ b/src/api/document.js @@ -3,11 +3,6 @@ export default class DocumentAPI { this.client = client; } - list(schema) { - const endpoint = `schemas/${schema}/documents/`; - return this.client.request(endpoint); - } - save(schema, documents) { const endpoint = `schemas/${schema}/documents/`; return this.client.request(endpoint, 'POST', documents); @@ -28,8 +23,14 @@ export default class DocumentAPI { return this.client.request(endpoint, 'PATCH', document); } - remove(schema, document_id) { + delete(schema, document_id) { const endpoint = `schemas/${schema}/documents/${document_id}/`; return this.client.request(endpoint, 'DELETE'); } + + remove(schema, document_id) { + console.warn("The function remove() is deprecated, use delete()."); + + return this.delete(schema, document_id); + } } diff --git a/src/api/file.js b/src/api/file.js index 08645d1..5a4ad17 100644 --- a/src/api/file.js +++ b/src/api/file.js @@ -3,19 +3,26 @@ export default class FileAPI { this.client = client; } - list() { - return this.client.request('files/'); + list(params) { + params = params ? params : {}; + return this.client.request('files/', 'GET', params); } get(file_id) { return this.client.request(`files/${file_id}/`); } - remove(file_id) { - const endpoint = `files/${file_id}/`; + delete(file_id) { + const endpoint = `/files/${file_id}/`; return this.client.request(endpoint, 'DELETE'); } + remove(file_id) { + console.warn("The function remove() is deprecated, use delete()."); + + return this.delete(file_id); + } + save(formData) { return this.client.request('files/', 'POST', formData, true); } diff --git a/src/api/policy.js b/src/api/policy.js index baed3cd..907fa56 100644 --- a/src/api/policy.js +++ b/src/api/policy.js @@ -20,7 +20,7 @@ export default class PolicyAPI { return this.client.request(`policy/${policy_id}/`); } - update(policy_id, description, policy) { + update(policy_id, { description, policy }) { if(!description && !policy) { throw new Error('Must provide a `description` or `policy`'); } @@ -35,15 +35,21 @@ export default class PolicyAPI { payload.policy = policy; } - if(payload) { + if (Object.keys(payload).length) { return this.client.request(`policy/${policy_id}/`, 'PATCH', payload); } } - remove(policy_id) { + delete(policy_id) { return this.client.request(`policy/${policy_id}/`, 'DELETE'); } + remove(policy_id) { + console.warn("The function remove() is deprecated, use delete()."); + + return this.delete(policy_id); + } + checkPermission(action, resource) { var payload = { action, diff --git a/src/api/project.js b/src/api/project.js new file mode 100644 index 0000000..c4f8e2d --- /dev/null +++ b/src/api/project.js @@ -0,0 +1,25 @@ +export default class ProjectAPI { + constructor(client) { + this.client = client; + } + + get() { + return this.client.request(`project`); + } + + update({ name, subdomain }) { + var payload = {}; + + if(name) { + payload.name = name; + } + + if(subdomain) { + payload.subdomain = subdomain; + } + + if (Object.keys(payload).length) { + return this.client.request(`project`, 'PATCH', payload); + } + } +} diff --git a/src/api/role.js b/src/api/role.js index 95bc1c2..b34effd 100644 --- a/src/api/role.js +++ b/src/api/role.js @@ -8,15 +8,16 @@ export default class RoleAPI { return this.client.request(`roles/`, 'POST', payload); } - list() { - return this.client.request(`roles/`); + list(params) { + params = params ? params : {}; + return this.client.request(`roles/`, 'GET', params); } get(role) { return this.client.request(`roles/${role}/`); } - update(role, name, add_users, remove_users) { + update(role, { name, add_users, remove_users }) { var payload = {}; if (name) { @@ -36,7 +37,13 @@ export default class RoleAPI { } } - remove(role) { + delete(role) { return this.client.request(`roles/${role}/`, 'DELETE'); } + + remove(role) { + console.warn("The function remove() is deprecated, use delete()."); + + return this.delete(role); + } } diff --git a/src/api/schema.js b/src/api/schema.js index be0f80e..7faeb79 100644 --- a/src/api/schema.js +++ b/src/api/schema.js @@ -8,15 +8,16 @@ export default class SchemaAPI { return this.client.request('schemas/', 'POST', payload); } - list() { - return this.client.request('schemas/'); + list(params) { + params = params ? params : {}; + return this.client.request('schemas/', 'GET', params); } get(schema) { return this.client.request(`schemas/${schema}/`); } - update(schema, name, fields) { + update(schema, { name, fields }) { var payload = {}; if (name) { @@ -32,7 +33,13 @@ export default class SchemaAPI { } } - remove(schema) { + delete(schema) { return this.client.request(`schemas/${schema}/`, 'DELETE'); } + + remove(schema) { + console.warn("The function remove() is deprecated, use delete()."); + + return this.delete(schema); + } } diff --git a/src/api/task.js b/src/api/task.js new file mode 100644 index 0000000..1603111 --- /dev/null +++ b/src/api/task.js @@ -0,0 +1,25 @@ +export default class TaskAPI { + constructor(client) { + this.client = client; + } + + run(command, { image, token, timeLimit }) { + const payload = { command }; + + if (image) { + payload.image = image; + } + if (token) { + payload.token = token; + } + if (timeLimit) { + payload.time_limit = timeLimit; + } + + return this.client.request(`tasks/`, 'POST', payload); + } + + get(task_id) { + return this.client.request(`tasks/${task_id}/`); + } +} diff --git a/src/api/user.js b/src/api/user.js index bd58f6c..07e200d 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -3,20 +3,21 @@ export default class UserAPI { this.client = client } - list() { - return this.client.request('users/'); + list(params) { + params = params ? params : {}; + return this.client.request('users/', 'GET', params); } create(full_name, email, password) { const payload = { full_name, email, password }; - return this.client.request('users/', 'POST', payload) + return this.client.request('users/', 'POST', payload); } get(user_id) { return this.client.request(`users/${user_id}/`); } - update(user_id, full_name, email, password) { + update(user_id, { full_name, email, password }) { var payload = {}; if (full_name) { @@ -36,7 +37,13 @@ export default class UserAPI { } } - remove(user_id) { + delete(user_id) { return this.client.request(`users/${user_id}/`, 'DELETE'); } + + remove(user_id) { + console.warn("The function remove() is deprecated, use delete()."); + + return this.delete(user_id); + } } diff --git a/src/client.js b/src/client.js index 1e8a15c..9dbab11 100644 --- a/src/client.js +++ b/src/client.js @@ -6,119 +6,123 @@ import 'babel-polyfill'; import DocumentAPI from './api/document'; import FileAPI from './api/file'; import RoleAPI from './api/role'; +import ProjectAPI from './api/project'; import SchemaAPI from './api/schema'; import UserAPI from './api/user'; import PolicyAPI from './api/policy'; +import TaskAPI from './api/task'; export default class Client { - constructor(project, token) { - this.protocol = 'https'; - this.host = 'mntge.com'; - this.project = project; - this.token = token; - - this.documents = new DocumentAPI(this); - this.schemas = new SchemaAPI(this); - this.users = new UserAPI(this); - this.roles = new RoleAPI(this); - this.files = new FileAPI(this); - this.policy = new PolicyAPI(this); - } - - url(endpoint) { - return `${this.protocol}://${this.project}.${this.host}/api/v1/${endpoint}`; - } - - authenticate(email, password) { - return this.request('user/', 'POST', { - username: email, - password: password - }).then(response => { - this.token = response.data.token; - return response; - }); - } - - user() { - if(this.token) { - return this.request('user/'); - } - - return Promise.reject('The current user is not authenticated.') - } - - execute(queries) { - var querySet = {}; - - for(var key in queries) { - if(queries.hasOwnProperty(key)) { - querySet[key] = queries[key].toJS(); - } - } - - return this.request('execute/', 'POST', querySet); - } - - request(endpoint, method, data, file) { - var requestUrl = this.url(endpoint); - - var options = { - method: method && method.toUpperCase() || "GET", - headers: { - accept: 'application/json', - 'X-Requested-With': 'XMLHttpRequest' - } - }; - if (!file) { - options.headers['Content-Type'] = 'application/json'; - } - if (data) { - if (options.method === "GET") { - requestUrl += '?' + querystring.stringify(data); - } else { - if(file) options.body = data; - else options.body = JSON.stringify(data); - } - } - if (this.token) { - options.headers.Authorization = `Token ${this.token}`; - } - if (options.body) { - //Varnish and heroku require a content length! - options.headers['Content-Length'] = getByteLen(options.body); - } - - return this._agent(requestUrl, options).then(function(response) { - if(response.status === 204) { - return; - } - if (!response.ok) { - response.request = _.merge({ - url: requestUrl - }, options); - return Promise.reject(response); - } - if (response.status >= 400) { - return response.text().then(body => { - var errorMessage = body || response.statusText; - try { - errorMessage = JSON.parse(body); - } catch (e) {} - return Promise.reject(errorMessage); - }); - } - return response.json(); - }).then(function(payload) { - if (payload && payload.errors) { - return Promise.reject(payload.errors); - } - return payload; - }); - } - - _agent(...args) { - return fetch(...args); - } + constructor(subdomain, token) { + this.protocol = 'https'; + this.host = 'mntge.com'; + this.subdomain = subdomain; + this.token = token; + + this.documents = new DocumentAPI(this); + this.schemas = new SchemaAPI(this); + this.users = new UserAPI(this); + this.roles = new RoleAPI(this); + this.project = new ProjectAPI(this); + this.files = new FileAPI(this); + this.policy = new PolicyAPI(this); + this.tasks = new TaskAPI(this); + } + + url(endpoint) { + return `${this.protocol}://${this.subdomain}.${this.host}/api/v1/${endpoint}`; + } + + authenticate(email, password) { + return this.request('user/', 'POST', { + username: email, + password: password + }).then(response => { + this.token = response.data.token; + return response; + }); + } + + user() { + if(this.token) { + return this.request('user/'); + } + + return Promise.reject('The current user is not authenticated.'); + } + + execute(queries) { + var querySet = {}; + + for(var key in queries) { + if(queries.hasOwnProperty(key)) { + querySet[key] = queries[key].toJS(); + } + } + + return this.request('execute/', 'POST', querySet); + } + + request(endpoint, method, data, file) { + var requestUrl = this.url(endpoint); + + var options = { + method: method && method.toUpperCase() || "GET", + headers: { + accept: 'application/json', + 'X-Requested-With': 'XMLHttpRequest' + } + }; + if (!file) { + options.headers['Content-Type'] = 'application/json'; + } + if (data) { + if (options.method === "GET") { + requestUrl += '?' + querystring.stringify(data); + } else { + if(file) options.body = data; + else options.body = JSON.stringify(data); + } + } + if (this.token) { + options.headers.Authorization = `Token ${this.token}`; + } + if (options.body) { + //Varnish and heroku require a content length! + options.headers['Content-Length'] = getByteLen(options.body); + } + + return this._agent(requestUrl, options).then(function(response) { + if(response.status === 204) { + return; + } + if (!response.ok) { + response.request = _.merge({ + url: requestUrl + }, options); + return Promise.reject(response); + } + if (response.status >= 400) { + return response.text().then(body => { + var errorMessage = body || response.statusText; + try { + errorMessage = JSON.parse(body); + } catch (e) {} + return Promise.reject(errorMessage); + }); + } + return response.json(); + }).then(function(payload) { + if (payload && payload.errors) { + return Promise.reject(payload.errors); + } + return payload; + }); + } + + _agent(...args) { + return fetch(...args); + } } /** @@ -130,18 +134,18 @@ export default class Client { * @return {int} */ function getByteLen(normal_val) { - // Force string type - normal_val = String(normal_val); - - var byteLen = 0; - for (var i = 0; i < normal_val.length; i++) { - var c = normal_val.charCodeAt(i); - byteLen += c < (1 << 7) ? 1 : - c < (1 << 11) ? 2 : - c < (1 << 16) ? 3 : - c < (1 << 21) ? 4 : - c < (1 << 26) ? 5 : - c < (1 << 31) ? 6 : Number.NaN; - } - return byteLen; + // Force string type + normal_val = String(normal_val); + + var byteLen = 0; + for (var i = 0; i < normal_val.length; i++) { + var c = normal_val.charCodeAt(i); + byteLen += c < (1 << 7) ? 1 : + c < (1 << 11) ? 2 : + c < (1 << 16) ? 3 : + c < (1 << 21) ? 4 : + c < (1 << 26) ? 5 : + c < (1 << 31) ? 6 : Number.NaN; + } + return byteLen; } diff --git a/src/field.js b/src/field.js index 7be6420..ffaa75e 100644 --- a/src/field.js +++ b/src/field.js @@ -1,92 +1,41 @@ export default class Field { - constructor(field) { - this.field = field; - this.filters = []; - } - - toJS() { - return this.filters.map(filter => { - return [this.field, filter]; - }); - } - - eq(value) { - this.filters.push(["$eq", value]); - return this; - } - - ieq(value) { - this.filters.push(["$ieq", value]); - return this; - } - - ne(value) { - this.filters.push(["$ne", value]); - return this; - } - - lt(value) { - this.filters.push(["$lt", value]); - return this; - } - - le(value) { - this.filters.push(["$le", value]); - return this; - } - - gt(value) { - this.filters.push(["$gt", value]); - return this; - } - - ge(value) { - this.filters.push(["$ge", value]); - return this; - } - - inSet(...values) { - this.filters.push(["$in", values]); - return this; - } - - contains(value) { - this.filters.push(["$contains", value]); - return this; - } - - regex(expression) { - this.filters.push(["$regex", expression]); - return this; - } - - starts(value) { - this.filters.push(["$starts", value]); - return this; - } - - istarts(value) { - this.filters.push(["$istarts", value]); - return this; - } - - ends(value) { - this.filters.push(["$ends", value]); - return this; - } - - iends(value) { - this.filters.push(["$iends", value]); - return this; - } - - intersects(value) { - this.filters.push(["$intersects", value]); - return this; - } - - includes(value) { - this.filters.push(["$includes", value]); - return this; - } + constructor(field) { + this.field = field; + } + + _operator(operator, value){ + return [this.field, [`$${operator}`, value]] + } + + eq(value){ return this._operator('eq', value); } + ne(value){ return this._operator('ne', value); } + lt(value){ return this._operator('lt', value); } + le(value){ return this._operator('le', value); } + gt(value){ return this._operator('gt', value); } + ge(value){ return this._operator('ge', value); } + ieq(value){ return this._operator('ieq', value); } + in(value){ return this._operator('in', value); } + match(value){ return this._operator('match', value); } + starts(value){ return this._operator('starts', value); } + istarts(value){ return this._operator('istarts', value); } + ends(value){ return this._operator('ends', value); } + iends(value){ return this._operator('iends', value); } + intersects(value){ return this._operator('intersects', value); } + includes(value){ return this._operator('includes', value); } + + _modifier(modifier){ + return new Field(`${this.field}.$${modifier}`); + } + + date(){ return this._modifier('date'); } + time(){ return this._modifier('time'); } + year(){ return this._modifier('year'); } + month(){ return this._modifier('month'); } + day(){ return this._modifier('day'); } + hours(){ return this._modifier('hours'); } + minutes(){ return this._modifier('minutes'); } + seconds(){ return this._modifier('seconds'); } + day_of_month(){ return this._modifier('day_of_month'); } + day_of_year(){ return this._modifier('day_of_year'); } + timezone(){ return this._modifier('timezone'); } } diff --git a/src/index.js b/src/index.js index 34590b9..3382150 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ import Query from './query'; import Field from './field'; export { - Client, - Field, - Query + Client, + Field, + Query }; diff --git a/src/query.js b/src/query.js index 6dac071..b4a7122 100644 --- a/src/query.js +++ b/src/query.js @@ -1,138 +1,180 @@ import _ from 'lodash'; +import Field from './field' export default class Query { - constructor(schema) { - if (!schema) throw 'A schema name is required'; - - this.schema = schema; - this.terms = []; - } - - toJS() { - return { - $type: 'query', - $schema: this.schema, - $query: this.terms - }; - } - - get(id) { - this.terms.push(['$get', id]); - return this; - } - - getAll(ids, index = 'id') { - this.terms.push(['$get_all', [index, ids]]); - return this; - } - - filter(...filters) { - var filterSet = filters.reduce((filterSet, currentFilter) => { - return filterSet.concat(currentFilter.toJS()); - }, []); - - this.terms.push(['$filter', filterSet]); - return this; - } - - hasFields(...fields) { - this.terms.push(['$has_fields', fields]) - return this; - } - - withFields(...fields) { - this.terms.push(['$with_fields', fields]) - return this; - } - - orderBy(field, ordering = 'asc') { - if(ordering !== 'asc' && ordering !== 'desc') { - throw new Error('ordering must be desc or asc'); - } - - ordering = '$' + ordering; - this.terms.push(['$order_by', field, ordering]) - return this; - } - - skip(num) { - this.terms.push(['$skip', num]); - return this; - } - - limit(num) { - this.terms.push(['$limit', num]); - return this; - } - - slice(start, end) { - this.terms.push(['$slice', [start, end]]); - return this; - } - - nth(num) { - this.terms.push(['$nth', num]); - return this; - } - - sample(num) { - this.terms.push(['$sample', num]); - return this; - } - - pluck(...fields) { - this.terms.push(['$pluck', fields]); - return this; - } - - without(...fields) { - this.terms.push(['$without', fields]); - return this; - } - - group(field) { - this.terms.push(['$group', field]); - return this; - } - - count() { - this.terms.push(['$count']); - return this; - } - - sum(field) { - this.terms.push(['$sum', field]); - return this; - } - - avg(field) { - this.terms.push(['$avg', field]); - return this; - } - - min(field) { - this.terms.push(['$min', field]); - return this; - } - - max(field) { - this.terms.push(['$max', field]); - return this; - } - - between(start, end, index) { - var value = index ? [start, end, index] : [start, end]; - this.terms.push(['$between', value]); - return this; - } - - getIntersecting(geometry, index) { - this.terms.push(['$get_intersecting', [index, geometry]]); - return this; - } - - getNearest(geometry, index) { - this.terms.push(['$get_nearest', [index, geometry]]); - return this; - } + constructor(schema) { + if (!schema) throw 'A schema name is required'; + + this.schema = schema; + this.terms = []; + } + + toJS() { + return { + $type: 'query', + $schema: this.schema, + $query: this.terms + }; + } + + // Selecting Data + + get(id) { + this.terms.push(['$get', {id}]); + return this; + } + + getAll(ids, index = 'id') { + this.terms.push(['$get_all', {index, ids}]); + return this; + } + + filter() { + var params = {}; + + if (_.last(arguments) instanceof Array) { + params.predicate = _.slice(arguments) + } else { + params.predicate = _.slice(arguments, 0, arguments.length - 1); + params.default = _.last(arguments).default; + } + + this.terms.push(['$filter', params]); + return this; + } + + between(options) { + const defaults = { + lowerKey : '$minval', + upperKey : '$maxval' + }; + const params = _.assign(defaults, options); + this.terms.push(['$between', params]); + return this; + } + + // Transformations + + hasFields(...fields) { + this.terms.push(['$has_fields', {fields}]); + return this; + } + + withFields(...fields) { + this.terms.push(['$with_fields', {fields}]); + return this; + } + + orderBy({key, index, ordering}) { + ordering = (ordering === undefined) ? '$asc' : ordering; + if(['asc', 'desc'].indexOf(ordering) !== -1) { + console.warn('asc/desc parameters deprecated. Please use $asc/$desc.'); + ordering = '$' + ordering; + } + + if(['$asc', '$desc'].indexOf(ordering) === -1) { + throw new Error('ordering must be $desc or $asc'); + } + + var params = { ordering }; + + if(key !== undefined) { + params.key = key; + } + + if(index !== undefined) { + params.index = index; + } + + this.terms.push(['$order_by', params]); + return this; + } + + skip(n) { + this.terms.push(['$skip', {n}]); + return this; + } + + limit(n) { + this.terms.push(['$limit', {n}]); + return this; + } + + slice(startOffset, endOffset) { + this.terms.push(['$slice', {startOffset, endOffset}]); + return this; + } + + nth(n) { + this.terms.push(['$nth', {n}]); + return this; + } + + sample(n) { + this.terms.push(['$sample', {n}]); + return this; + } + + // Manipulation + + pluck(...fields) { + this.terms.push(['$pluck', {fields}]); + return this; + } + + without(...fields) { + this.terms.push(['$without', {fields}]); + return this; + } + + // Aggregation + + group(field) { + this.terms.push(['$group', {field}]); + return this; + } + + count() { + this.terms.push(['$count']); + return this; + } + + sum(field) { + this.terms.push(['$sum', {field}]); + return this; + } + + avg(field) { + this.terms.push(['$avg', {field}]); + return this; + } + + min(field) { + this.terms.push(['$min', {field}]); + return this; + } + + max(field) { + this.terms.push(['$max', {field}]); + return this; + } + + // Geospatial + + getIntersecting(geometry, index) { + this.terms.push(['$get_intersecting', {index, geometry}]); + return this; + } + + getNearest(geometry, index) { + this.terms.push(['$get_nearest', {index, geometry}]); + return this; + } + + // Delete + + delete(durability = 'hard', return_changes = false) { + this.terms.push(['$delete', {durability, return_changes}]); + return this; + } } diff --git a/src/scripting.js b/src/scripting.js index e500d6d..e6ac680 100644 --- a/src/scripting.js +++ b/src/scripting.js @@ -1,27 +1,27 @@ class Script { - constructor(name) { - this.name = name - } - - toJS() { - return { - $type: 'script', - $name: this.name - }; - } + constructor(name) { + this.name = name + } + + toJS() { + return { + $type: 'script', + $name: this.name + }; + } } class RunLua { - constructor(code) { - this.code = code; - } + constructor(code) { + this.code = code; + } - toJS() { - return { - $type: 'lua', - $code: this.code - }; - } + toJS() { + return { + $type: 'lua', + $code: this.code + }; + } } diff --git a/tests/client_test.js b/tests/client_test.js index bbd13a8..731ef32 100644 --- a/tests/client_test.js +++ b/tests/client_test.js @@ -1,6 +1,6 @@ import expect from 'expect.js'; import querystring from 'querystring'; -import {Client, Query} from '../src/index'; +import Client from '../src/client'; import {EventEmitter} from 'events'; var emitter = new EventEmitter(); @@ -94,7 +94,7 @@ describe('Client', () => { }); client.authenticate('test@example.com', 'letmein').then((response) => { - expect(response).to.be.eql({data: {token: 'USER_TOKEN'}}); + expect(response).to.eql({data: {token: 'USER_TOKEN'}}); expect(client.token).to.eql('USER_TOKEN'); done(); }); @@ -118,7 +118,7 @@ describe('Client', () => { client.authenticate('fake@example.com', 'invalid').then(response => { expect().fail(); }, error => { - expect(error).to.eql([{'detail': 'Incorrect authentication credentials.'}]) + expect(error).to.eql([{'detail': 'Incorrect authentication credentials.'}]); expect(client.token).to.be(undefined); done(); }); @@ -170,7 +170,7 @@ describe('Client', () => { return client.request('#').then(response => { expect().fail(); }, error => { - expect(error).to.be.eql({errors: ['something went wrong']}); + expect(error).to.eql({errors: ['something went wrong']}); done(); }); }); @@ -187,7 +187,7 @@ describe('Client', () => { return client.request('#').then(response => { expect.fail(); }, error => { - expect(error).to.be.eql(['something went wrong']); + expect(error).to.eql(['something went wrong']); }); }); @@ -203,7 +203,7 @@ describe('Client', () => { expect.fail(); }, error => { return error.text().then(text => { - expect(text).to.be.eql('something went wrong'); + expect(text).to.eql('something went wrong'); }); }); }); diff --git a/tests/field_test.js b/tests/field_test.js new file mode 100644 index 0000000..47d0dd7 --- /dev/null +++ b/tests/field_test.js @@ -0,0 +1,115 @@ +import expect from 'expect.js'; +import {Field} from '../src/index'; + +describe('Field', () => { + let field; + + beforeEach(function() { + field = new Field('x'); + }); + + describe('#eq()', () => { + it('should equal a filter with a value of $eq', () => { + let expectedFilter = ['x', ['$eq', 'y']]; + expect(field.eq('y')).to.eql(expectedFilter); + }); + }); + + describe('#ieq()', () => { + it('should equal a filter with a value of $ieq', () => { + let expectedFilter = ['x', ['$ieq', 'y']]; + expect(field.ieq('y')).to.eql(expectedFilter); + }); + }); + + describe('#ne()', () => { + it('should equal a filter with a value of $ne', () => { + let expectedFilter = ['x', ['$ne', 'y']]; + expect(field.ne('y')).to.eql(expectedFilter); + }); + }); + + describe('#lt()', () => { + it('should equal a filter with a value of $lt', () => { + let expectedFilter = ['x', ['$lt', 'y']]; + expect(field.lt('y')).to.eql(expectedFilter); + }); + }); + + describe('#le()', () => { + it('should equal a filter with a value of $le', () => { + let expectedFilter = ['x', ['$le', 'y']]; + expect(field.le('y')).to.eql(expectedFilter); + }); + }); + + describe('#gt()', () => { + it('should equal a filter with a value of $gt', () => { + let expectedFilter = ['x', ['$gt', 'y']]; + expect(field.gt('y')).to.eql(expectedFilter); + }); + }); + + describe('#ge()', () => { + it('should equal a filter with a value of $ge', () => { + let expectedFilter = ['x', ['$ge', 'y']]; + expect(field.ge('y')).to.eql(expectedFilter); + }); + }); + + describe('#in()', () => { + it('should equal a filter with a value of $in', () => { + let expectedFilter = ['x', ['$in', 'y']]; + expect(field.in('y')).to.eql(expectedFilter); + }); + }); + + describe('#match()', () => { + it('should equal a filter with a value of $match', () => { + let expectedFilter = ['x', ['$match', 'y']]; + expect(field.match('y')).to.eql(expectedFilter); + }); + }); + + describe('#starts()', () => { + it('should equal a filter with a value of $starts', () => { + let expectedFilter = ['x', ['$starts', 'y']]; + expect(field.starts('y')).to.eql(expectedFilter); + }); + }); + + describe('#istarts()', () => { + it('should equal a filter with a value of $istarts', () => { + let expectedFilter = ['x', ['$istarts', 'y']]; + expect(field.istarts('y')).to.eql(expectedFilter); + }); + }); + + describe('#ends()', () => { + it('should equal a filter with a value of $ends', () => { + let expectedFilter = ['x', ['$ends', 'y']]; + expect(field.ends('y')).to.eql(expectedFilter); + }); + }); + + describe('#iends()', () => { + it('should equal a filter with a value of $iends', () => { + let expectedFilter = ['x', ['$iends', 'y']]; + expect(field.iends('y')).to.eql(expectedFilter); + }); + }); + + describe('#intersects()', () => { + it('should equal a filter with a value of $intersects', () => { + let expectedFilter = ['x', ['$intersects', 'y']]; + expect(field.intersects('y')).to.eql(expectedFilter); + }); + }); + + describe('#includes()', () => { + it('should equal a filter with a value of $includes', () => { + let expectedFilter = ['x', ['$includes', 'y']]; + expect(field.includes('y')).to.eql(expectedFilter); + }); + }); +}); diff --git a/tests/query_test.js b/tests/query_test.js index 5aaa1f6..0149e73 100644 --- a/tests/query_test.js +++ b/tests/query_test.js @@ -1,196 +1,295 @@ import expect from 'expect.js'; import {Query} from '../src/index'; +import {Field} from '../src/index'; var query; +var field; describe('Query', () => { - beforeEach(function() { - query = new Query('testSchema'); - }); - - describe('initialization', () => { - it('should require a schema name', () => { - expect(query.schema).to.be('testSchema'); - }); - }); - - describe('#toJS()', () => { - it('should return a query object', () => { - expect(query.toJS()).to.eql({ - $type: 'query', - $schema: 'testSchema', - $query: [] - }); - }); - }); - - describe('#get()', () => { - it('sets get', () => { - query.get('1234'); - expect(query.terms).to.eql([['$get', '1234']]); - }); - }); - - describe('#getAll()', () => { - it('sets getAll', () => { - query.getAll(['1234', 'abcd']); - expect(query.terms).to.eql([['$get_all', ['id', ['1234', 'abcd']]]]); - }); - }); - - describe('#hasFields()', () => { - it('sets hasFields', () => { - query.hasFields('title', 'rating'); - expect(query.terms).to.eql([['$has_fields', ['title', 'rating']]]); - }); - }); - - describe('#withFields()', () => { - it('sets withFields', () => { - query.withFields('title', 'rating'); - expect(query.terms).to.eql([['$with_fields', ['title', 'rating']]]); - }); - }); - - describe('#orderBy()', () => { - context('when no direction supplied', () => { - it('sets order_by and default direction', () => { - query.orderBy('rating'); - expect(query.terms).to.eql([['$order_by', 'rating', '$asc']]); - }); - }); - - context('when direction is ascending', () => { - it('sets order_by and ascending direction', () => { - query.orderBy('rating', 'asc'); - expect(query.terms).to.eql([['$order_by', 'rating', '$asc']]); - }); - }); - - context('when direction is descending', () => { - it('sets order_by and descending direction', () => { - query.orderBy('rating', 'desc'); - expect(query.terms).to.eql([['$order_by', 'rating', '$desc']]); - }); - }); - - context('when direction is invalid', () => { - it('should throw an exception', () => { - expect(query.orderBy).withArgs('rating', -1).to.throwException(); - }); - }); - }); - - describe('#skip()', () => { - it('sets skip', () => { - query.skip(10); - expect(query.terms).to.eql([['$skip', 10]]); - }); - }); - - describe('#limit()', () => { - it('sets limit', () => { - query.limit(10); - expect(query.terms).to.eql([['$limit', 10]]); - }); - }); - - describe('#slice()', () => { - it('sets slice', () => { - query.slice(10, 20); - expect(query.terms).to.eql([['$slice', [10, 20]]]); - }); - }); - - describe('#nth()', () => { - it('sets nth', () => { - query.nth(5); - expect(query.terms).to.eql([['$nth', 5]]); - }); - }); - - describe('#sample()', () => { - it('sets sample', () => { - query.sample(20); - expect(query.terms).to.eql([['$sample', 20]]); - }); - }); - - describe('#pluck()', () => { - it('sets pluck', () => { - query.pluck('name', 'rank', 'rating'); - expect(query.terms).to.eql([['$pluck', ['name', 'rank', 'rating']]]); - }); - }); - - describe('#without()', () => { - it('sets without', () => { - query.without('votes', 'rank'); - expect(query.terms).to.eql([['$without', ['votes', 'rank']]]); - }); - }); - - describe('#count()', () => { - it('sets count', () => { - query.count(); - expect(query.terms).to.eql([['$count']]); - }); - }); - - describe('#sum()', () => { - it('sets sum', () => { - query.sum('rank'); - expect(query.terms).to.eql([['$sum', 'rank']]); - }); - }); - - describe('#avg()', () => { - it('sets avg', () => { - query.avg('rank'); - expect(query.terms).to.eql([['$avg', 'rank']]); - }); - }); - - describe('#min()', () => { - it('sets min', () => { - query.min('rating'); - expect(query.terms).to.eql([['$min', 'rating']]); - }); - }); - - describe('#max()', () => { - it('sets max', () => { - query.max('rating'); - expect(query.terms).to.eql([['$max', 'rating']]); - }); - }); - - describe('#between()', () => { - it('sets between', () => { - query.between(0, 10, 'rank'); - expect(query.terms).to.eql([['$between', [0, 10, 'rank']]]); - }); - }); - - describe('#getIntersecting()', () => { - it('sets get_intersecting', () => { - var point = { - 'type': 'Point', - 'coordinates': [-120.34589052200315, 36.12704320788633] - }; - query.getIntersecting(point, 'location'); - expect(query.terms).to.eql([['$get_intersecting', ['location', point]]]); - }); - }); - - describe('#getNearest()', () => { - it('sets get_nearest', () => { - var point = { - 'type': 'Point', - 'coordinates': [-120.34589052200315, 36.12704320788633] - }; - query.getNearest(point, 'location'); - expect(query.terms).to.eql([['$get_nearest', ['location', point]]]); - }); - }); + beforeEach(function() { + query = new Query('testSchema'); + field = new Field('x'); + }); + + describe('initialization', () => { + it('should require a schema name', () => { + expect(query.schema).to.be('testSchema'); + }); + }); + + describe('#toJS()', () => { + it('should return a query object', () => { + expect(query.toJS()).to.eql({ + $type: 'query', + $schema: 'testSchema', + $query: [] + }); + }); + }); + + describe('#get()', () => { + it('sets get', () => { + query.get('1234'); + expect(query.terms).to.eql([['$get', {'id': '1234'}]]); + }); + }); + + describe('#getAll()', () => { + it('sets getAll', () => { + query.getAll(['1234', 'abcd']); + expect(query.terms).to.eql([['$get_all', {'ids': ['1234', 'abcd'], 'index': 'id'}]]); + }); + }); + + describe('#filter()', () => { + context('when ge', () => { + var expected = { + $type: 'query', + $schema: 'testSchema', + $query: [ + ['$filter', {'predicate': + [['rank', ['$ge', 3]]], + }] + ] + }; + + it('sets filter', () => { + expect(query.filter(new Field('rank').ge(3)).toJS()).to.eql(expected); + }); + }); + + context('when multiple', () => { + var expected = { + $type: 'query', + $schema: 'testSchema', + $query: [ + ['$filter', {'predicate': + [['year', ['$ge', 1990]], + ['year', ['$lt', 2000]]], + }] + ] + }; + + it('sets filter', () => { + expect(query.filter( + new Field('year').ge(1990), + new Field('year').lt(2000) + ).toJS()).to.eql(expected); + }); + }); + + context('when or', () => { + var expected = { + $type: 'query', + $schema: 'testSchema', + $query: [ + ['$filter', {'predicate': + [['year', ['$lt', 1990]], + ['year', ['$ge', 2000]]], + }] + ] + }; + + it('sets filter', () => { + expect(query.filter( + new Field('year').lt(1990), + new Field('year').ge(2000) + ).toJS()).to.eql(expected); + }); + }); + + context('when default is set to true', () => { + var expected = { + $type: 'query', + $schema: 'testSchema', + $query: [ + ['$filter', { + 'predicate':[ + ['year', ['$eq', 2000]] + ], + 'default': true, + }] + ] + }; + + it('sets filter', () => { + expect(query.filter(new Field('year').eq(2000), {default: true}).toJS()).to.eql(expected); + }); + }); + }); + + describe('#hasFields()', () => { + it('sets hasFields', () => { + query.hasFields('title', 'rating'); + expect(query.terms).to.eql([['$has_fields', {fields: ['title', 'rating']}]]); + }); + }); + + describe('#withFields()', () => { + it('sets withFields', () => { + query.withFields('title', 'rating'); + expect(query.terms).to.eql([['$with_fields', {fields: ['title', 'rating']}]]); + }); + }); + + describe('#orderBy()', () => { + context('when no direction supplied', () => { + it('sets order_by and default direction', () => { + query.orderBy({key: 'rating'}); + expect(query.terms).to.eql([['$order_by', {key: 'rating', ordering: '$asc'}]]); + }); + }); + + context('when direction is ascending', () => { + it('sets order_by and ascending direction', () => { + query.orderBy({key: 'rating', ordering: '$asc'}); + expect(query.terms).to.eql([['$order_by', {key: 'rating', ordering: '$asc'}]]); + }); + }); + + context('when direction is descending', () => { + it('sets order_by and descending direction', () => { + query.orderBy({key: 'rating', ordering: '$desc'}); + expect(query.terms).to.eql([['$order_by', {key: 'rating', ordering: '$desc'}]]); + }); + }); + + context('when direction is invalid', () => { + it('should throw an exception', () => { + expect(query.orderBy).withArgs('rating', -1).to.throwException(); + }); + }); + }); + + describe('#skip()', () => { + it('sets skip', () => { + query.skip(10); + expect(query.terms).to.eql([['$skip', {'n': 10}]]); + }); + }); + + describe('#limit()', () => { + it('sets limit', () => { + query.limit(10); + expect(query.terms).to.eql([['$limit', {'n': 10}]]); + }); + }); + + describe('#slice()', () => { + it('sets slice', () => { + query.slice(10, 20); + expect(query.terms).to.eql([['$slice', {'startOffset': 10, 'endOffset': 20}]]); + }); + }); + + describe('#nth()', () => { + it('sets nth', () => { + query.nth(5); + expect(query.terms).to.eql([['$nth', {'n': 5}]]); + }); + }); + + describe('#sample()', () => { + it('sets sample', () => { + query.sample(20); + expect(query.terms).to.eql([['$sample', {'n': 20}]]); + }); + }); + + describe('#pluck()', () => { + it('sets pluck', () => { + query.pluck('name', 'rank', 'rating'); + expect(query.terms).to.eql([['$pluck', {'fields': ['name', 'rank', 'rating']}]]); + }); + }); + + describe('#without()', () => { + it('sets without', () => { + query.without('votes', 'rank'); + expect(query.terms).to.eql([['$without', {'fields': ['votes', 'rank']}]]); + }); + }); + + describe('#group()', () => { + it('sets group', () => { + query.group('rank'); + expect(query.terms).to.eql([['$group', {'field': 'rank'}]]); + }); + }); + + describe('#count()', () => { + it('sets count', () => { + query.count(); + expect(query.terms).to.eql([['$count']]); + }); + }); + + describe('#sum()', () => { + it('sets sum', () => { + query.sum('rank'); + expect(query.terms).to.eql([['$sum', {'field': 'rank'}]]); + }); + }); + + describe('#avg()', () => { + it('sets avg', () => { + query.avg('rank'); + expect(query.terms).to.eql([['$avg', {'field': 'rank'}]]); + }); + }); + + describe('#min()', () => { + it('sets min', () => { + query.min('rating'); + expect(query.terms).to.eql([['$min', {'field': 'rating'}]]); + }); + }); + + describe('#max()', () => { + it('sets max', () => { + query.max('rating'); + expect(query.terms).to.eql([['$max', {'field': 'rating'}]]); + }); + }); + + describe('#between()', () => { + it('sets between', () => { + var params = { + lowerKey: 0, + upperKey: 10, + index: 'rank', + }; + query.between(params); + expect(query.terms).to.eql([['$between', params]]); + }); + }); + + describe('#getIntersecting()', () => { + it('sets get_intersecting', () => { + var point = { + 'coordinates': [-120.34589052200315, 36.12704320788633], + 'type': 'Point' + }; + query.getIntersecting(point, 'location'); + expect(query.terms).to.eql([['$get_intersecting', {index: 'location', geometry: point}]]); + }); + }); + + describe('#getNearest()', () => { + it('sets get_nearest', () => { + var point = { + 'type': 'Point', + 'coordinates': [-120.34589052200315, 36.12704320788633] + }; + query.getNearest(point, 'location'); + expect(query.terms).to.eql([['$get_nearest', {index: 'location', geometry: point}]]); + }); + }); + + describe('#delete()', () => { + it('sets delete', () => { + query.delete('hard', false); + expect(query.terms).to.eql([['$delete', {durability: 'hard', return_changes: false}]]); + }); + }); });