import queryString from 'qs';
import Predications from './predications';
/**
* A fluent interace for creating queries against resourceful endpoints
*
* @param {string?} query - (private) Initial query string to append to.
* @param {string?} sort - (private) What to sort the query on
* @param {string} [sortModifier=''] - (private) Sort modifier. Either empty string or '-'
*
* @requires {@link Predications}
*
* @example
* import { where, field } from '@pikselpalette/sequoia-js-client-sdk/lib/query';
*
* endpoint.browse(where(field('startedAt').greaterThanOrEqualTo(2015))
* .and(field('tags').equalTo('showcase'))
* .fields('title', 'mediumSynopsis','duration', 'ref')
* .include('assets').page(1).perPage(24).orderByUpdatedAt().desc().count())
* .then(json => { ... });
*
*/
class Query {
constructor(query) {
this.query = query || '';
this.sort = null;
this.sortModifier = '';
}
/**
* Concatenate a new query with the previous one
*
* @param {string} query - A query string to append (without leading ampersand).
* Usually returned from a call to `field`
*
* @example
* where(field('startedAt').greaterThanOrEqualTo(2015))
* .and(field('tags').equalTo('showcase'))
*
* @since 0.0.2
*
* @returns {Query}
*/
and(query) {
this.query += `&${query}`;
return this;
}
/**
* Will return `totalCount` on the payload
*
* @returns {Query}
*/
count() {
this.query += '&count=true';
return this;
}
/**
* Appends `continue=true` to the query to initiate continuation paging
*
* @returns {Query}
*/
continue() {
this.query += '&continue=true';
return this;
}
/**
* Appends `include=value1,value2` to the query
*
* @param {...string} includes - includes(s) (linked resources) to return in the json response
*
* @example
* where().include('assets', 'categories')
* // appends `include=assets,categories` to the query
*
* @returns {Query}
*/
include(...includes) {
this.query += `&include=${includes.join(',')}`;
return this;
}
/**
* Appends `lang=value` to the query
*
* @param {string} value - ISO 639-1 code for the language you want results returned in
*
* @example
* where().lang('de')
* // appends `lang=de` to the query
*
* @returns {Query}
*/
lang(value) {
this.query += `&lang=${value}`;
return this;
}
/**
* Appends `fields=value1,value2` to the query
*
* @param {...string} fieldName - field(s) to return in the json response
*
* @example
* where().fields('title', 'mediumSynopsis','duration', 'ref')
* // appends `fields=title,mediumSynopsis,duration,ref` to the query
*
* @returns {Query}
*/
fields(...fieldName) {
this.query += `&fields=${fieldName.join(',')}`;
return this;
}
/**
* Appends `page=value` to the query
*
* @param {(string|number)} value - page of results to request
*
* @example
* where().page(9)
* // appends `page=9` to the query
*
* @returns {Query}
*/
page(value) {
this.query += `&page=${value}`;
return this;
}
/**
* Appends `perPage=value` to the query
*
* @param {(string|number)} value - how many items to return per page of results
*
* @example
* where().perPage(24)
* // appends `perPage=24` to the query
*
* @returns {Query}
*/
perPage(value) {
this.query += `&perPage=${value}`;
return this;
}
/**
* Set order to ascending
*
* @returns {Query}
*/
asc() {
this.sortModifier = '';
return this;
}
/**
* Set order to descending
*
* @returns {Query}
*/
desc() {
this.sortModifier = '-';
return this;
}
/**
* Appends `sort=[-]fieldName` to the query, where `[-]` is toggled
* depending on the call to {@link Query#asc} or {@link Query#desc}
*
* @see {@link Query#orderBy}
*
* @returns {Query}
*/
orderBy(fieldName) {
this.sort = fieldName;
return this;
}
/**
* Look through the relationships of a criteria, and include fields from through
* relationships - this makes it so that the end user shouldn't know or care that
* through relationships exist.
*
* @param {relationships} relationships - The relationship definitions that may
* require the addition of fields to support the graphical display of a through relationship
*
* @returns {Query}
*/
addRelatedThroughFields(relationships = {}, allFields = []) {
const parsedQueryString = queryString.parse(this.query, {
ignoreQueryPrefix: true,
allowDots: true
});
const includes = Object.keys(parsedQueryString).includes('include')
? parsedQueryString.include.split(',')
: [];
let fields = Object.keys(parsedQueryString).includes('fields')
? parsedQueryString.fields.split(',')
: [];
const filteredIncludes = includes
.map((include) => {
const { through } = relationships[include] || '';
const throughRelationship = relationships[through] || {};
return throughRelationship.fieldNamePath || false;
})
.filter(fieldNamePath => fieldNamePath);
fields = filteredIncludes.length
? (fields.length ? fields : allFields).concat(filteredIncludes)
: fields;
parsedQueryString.fields = fields.length ? fields.join(',') : undefined;
this.query = queryString.stringify(parsedQueryString, { encode: false, allowDots: true, indices: false });
return this;
}
/**
* Convenience method for ordering by 'owner'
*
* @see {@link Query#orderBy}
*
* @returns {Query}
*/
orderByOwner() {
return this.orderBy('owner');
}
/**
* Convenience method for ordering by 'name'
*
* @see {@link Query#orderBy}
*
* @returns {Query}
*/
orderByName() {
return this.orderBy('name');
}
/**
* Convenience method for ordering by 'createdAt'
*
* @see {@link Query#orderBy}
*
* @returns {Query}
*/
orderByCreatedAt() {
return this.orderBy('createdAt');
}
/**
* Convenience method for ordering by 'createdBy'
*
* @see {@link Query#orderBy}
*
* @returns {Query}
*/
orderByCreatedBy() {
return this.orderBy('createdBy');
}
/**
* Convenience method for ordering by 'updatedAt'
*
* @see {@link Query#orderBy}
*
* @returns {Query}
*/
orderByUpdatedAt() {
return this.orderBy('updatedAt');
}
/**
* Convenience method for ordering by 'updatedBy'
*
* @see {@link Query#orderBy}
*
* @returns {Query}
*/
orderByUpdatedBy() {
return this.orderBy('updatedBy');
}
/**
* Turn the current Query into a string representation of a URI query
*
* @returns {string}
*/
toQueryString() {
let { query } = this;
if (this.sort) {
query += `&sort=${this.sortModifier}${this.sort}`;
}
return query;
}
}
export default Query;
export function where(criteria) {
return new Query(criteria);
}
export function field(value) {
return new Predications(value);
}
export function param(value) {
return new Predications(value, true);
}
export function textSearch(value) {
return `q=${value}`;
}