rapiq
Version:
A tiny library which provides utility types/functions for request and response query handling.
1,511 lines (1,466 loc) • 58.1 kB
JavaScript
import { createMerger, isObject as isObject$1, distinctArray } from 'smob';
/*
* Copyright (c) 2021-2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ // -----------------------------------------------------------
var Parameter = /*#__PURE__*/ function(Parameter) {
Parameter["FILTERS"] = "filters";
Parameter["FIELDS"] = "fields";
Parameter["PAGINATION"] = "pagination";
Parameter["RELATIONS"] = "relations";
Parameter["SORT"] = "sort";
return Parameter;
}({});
// -----------------------------------------------------------
var URLParameter = /*#__PURE__*/ function(URLParameter) {
URLParameter["FILTERS"] = "filter";
URLParameter["FIELDS"] = "fields";
URLParameter["PAGINATION"] = "page";
URLParameter["RELATIONS"] = "include";
URLParameter["SORT"] = "sort";
return URLParameter;
}({});
// -----------------------------------------------------------
const DEFAULT_ID = '__DEFAULT__';
/*
* Copyright (c) 2021-2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ function isObject(item) {
return !!item && typeof item === 'object' && !Array.isArray(item);
}
function extendObject(target, source) {
const keys = Object.keys(source);
for(let i = 0; i < keys.length; i++){
target[keys[i]] = source[keys[i]];
}
return target;
}
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function toFlatObject(data, options = {}) {
const prefixParts = options.prefixParts || [];
let output = {};
const keys = Object.keys(data);
for(let i = 0; i < keys.length; i++){
const key = keys[i];
if (isObject(data[key])) {
output = {
...output,
...toFlatObject(data[key], {
...options,
prefixParts: [
...prefixParts,
key
]
})
};
continue;
}
const destinationKey = [
...prefixParts,
key
].join('.');
if (options.transformer) {
const result = options.transformer(data[key], output, destinationKey);
if (result) {
continue;
}
}
if (options.validator) {
const result = options.validator(data[key], destinationKey);
if (!result) {
continue;
}
}
output[destinationKey] = data[key];
}
return output;
}
function buildKeyPath(key, prefix) {
if (typeof prefix === 'string') {
return `${prefix}.${key}`;
}
return key;
}
function toKeyPathArray(input, options, prefix) {
options = options || {};
const output = [];
if (options.transformer) {
const result = options.transformer(input, output, prefix);
if (result) {
return output;
}
}
if (Array.isArray(input)) {
for(let i = 0; i < input.length; i++){
if (options.transformer) {
const result = options.transformer(input[i], output, prefix);
if (result) {
return output;
}
}
if (Array.isArray(input[i])) {
for(let j = 0; j < input[i].length; j++){
const key = buildKeyPath(input[i][j], prefix);
output.push(key);
}
continue;
}
if (typeof input[i] === 'string') {
output.push(buildKeyPath(input[i], prefix));
continue;
}
if (isObject(input[i])) {
const keys = Object.keys(input[i]);
for(let j = 0; j < keys.length; j++){
const value = buildKeyPath(keys[j], prefix);
const data = toKeyPathArray(input[i][keys[j]], options, value);
if (data.length === 0) {
output.push(value);
} else {
output.push(...data);
}
}
}
}
return output;
}
if (isObject(input)) {
const keys = Object.keys(input);
for(let i = 0; i < keys.length; i++){
const value = buildKeyPath(keys[i], prefix);
const data = toKeyPathArray(input[keys[i]], options, value);
if (data.length === 0) {
output.push(value);
} else {
output.push(...data);
}
}
return output;
}
if (typeof input === 'string') {
const value = buildKeyPath(input, prefix);
output.push(value);
return output;
}
return output;
}
function groupArrayByKeyPath(input) {
const pathItems = {};
for(let i = 0; i < input.length; i++){
const parts = input[i].split('.');
let key;
let name;
if (parts.length === 1) {
key = DEFAULT_ID;
name = input[i];
} else {
name = parts.pop();
key = parts.join('.');
}
if (!Object.prototype.hasOwnProperty.call(pathItems, key)) {
pathItems[key] = [];
}
pathItems[key].push(name);
}
return pathItems;
}
function applyMapping(name, map, onlyKey) {
if (typeof map === 'undefined') {
return name;
}
const keys = Object.keys(map);
if (keys.length === 0) {
return name;
}
let parts = name.split('.');
const output = [];
let run = true;
while(run){
const value = parts.shift();
if (typeof value === 'undefined') {
run = false;
break;
}
if (hasOwnProperty(map, value)) {
output.push(map[value]);
} else {
let found = false;
const rest = [];
const copy = [
...parts
];
while(copy.length > 0){
const key = [
value,
...copy
].join('.');
if (hasOwnProperty(map, key)) {
output.push(map[key]);
found = true;
break;
} else {
const last = copy.pop();
if (last) {
rest.unshift(last);
}
}
}
if (found) {
parts = rest;
} else {
output.push(value);
}
}
}
if (onlyKey) {
return output.pop() || name;
}
if (output.length === 0) {
return name;
}
return output.join('.');
}
/*
* Copyright (c) 2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ function parseKey(field) {
const parts = field.split('.');
const name = parts.pop();
return {
name,
path: parts.length > 0 ? parts.join('.') : undefined
};
}
const merge = createMerger({
clone: true,
inPlace: false,
array: true,
arrayDistinct: true
});
function isPathAllowedByRelations(path, includes) {
if (typeof path === 'undefined' || typeof includes === 'undefined') {
return true;
}
return includes.some((include)=>include.key === path);
}
function buildKeyWithPath(name, path) {
let details;
if (isObject$1(name)) {
details = name;
} else {
details = {
name,
path
};
}
return details.path || path ? `${details.path || path}.${details.name}` : details.name;
}
function serializeAsURI(data, options = {}) {
// Loop through the data object
const keys = Object.keys(data);
if (keys.length === 0) {
return '';
}
const prefixParts = options.prefixParts || [];
// Create a query array to hold the key/value pairs
const query = [];
for(let i = 0; i < keys.length; i++){
let value = data[keys[i]];
if (isObject$1(value)) {
query.push(...serializeAsURI(value, {
...options,
prefixParts: [
...prefixParts,
keys[i]
]
}));
continue;
}
if (Array.isArray(value)) {
value = value.map((el)=>`${el}`).filter(Boolean).join(',');
}
if (value) {
const destinationKey = [
...prefixParts,
keys[i]
].reduce((acc, curr)=>`${acc}[${curr}]`, '');
// Encode each key and value, concatenate them into a string, and push them to the array
query.push(`${encodeURIComponent(destinationKey)}=${encodeURIComponent(value)}`);
}
}
return query.join('&');
}
function flattenParseAllowedOption(input) {
if (typeof input === 'undefined') {
return [];
}
return toKeyPathArray(input);
}
function isPathCoveredByParseAllowedOption(input, path) {
const paths = Array.isArray(path) ? path : [
path
];
const items = toKeyPathArray(input);
for(let i = 0; i < items.length; i++){
if (paths.indexOf(items[i]) !== -1) {
return true;
}
}
return false;
}
class FieldsOptionsContainer {
initDefault() {
if (typeof this.options.default === 'undefined') {
this.default = {};
this.defaultIsUndefined = true;
return;
}
this.default = groupArrayByKeyPath(flattenParseAllowedOption(this.options.default));
this.defaultIsUndefined = false;
}
initAllowed() {
if (typeof this.options.allowed === 'undefined') {
if (typeof this.options.default !== 'undefined') {
const items = toFlatObject(this.options.default, {
validator (input) {
if (!Array.isArray(input)) {
return false;
}
return !input.some((el)=>typeof el !== 'string');
}
});
if (items.length > 0) {
this.allowed = items;
this.allowedIsUndefined = false;
return;
}
}
this.allowed = {};
this.allowedIsUndefined = true;
return;
}
this.allowed = groupArrayByKeyPath(flattenParseAllowedOption(this.options.allowed));
this.allowedIsUndefined = false;
}
initItems() {
this.items = merge(this.default || {}, this.allowed || {});
this.keys = Object.keys(this.items);
}
initReverseMapping() {
if (typeof this.options.mapping === 'undefined') {
return;
}
this.reverseMapping = this.buildReverseRecord(this.options.mapping);
}
buildReverseRecord(record) {
const keys = Object.keys(record);
const output = {};
for(let i = 0; i < keys.length; i++){
output[record[keys[i]]] = keys[i];
}
return output;
}
constructor(input = {}){
this.options = input;
this.allowed = {};
this.allowedIsUndefined = true;
this.default = {};
this.defaultIsUndefined = true;
this.items = {};
this.keys = [];
this.reverseMapping = {};
this.initDefault();
this.initAllowed();
this.initItems();
this.initReverseMapping();
}
}
/*
* Copyright (c) 2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ var FieldOperator = /*#__PURE__*/ function(FieldOperator) {
FieldOperator["INCLUDE"] = "+";
FieldOperator["EXCLUDE"] = "-";
return FieldOperator;
}({});
/*
* Copyright (c) 2023.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ var ErrorCode = /*#__PURE__*/ function(ErrorCode) {
ErrorCode["NONE"] = "none";
ErrorCode["INPUT_INVALID"] = "inputInvalid";
ErrorCode["KEY_INVALID"] = "keyInvalid";
ErrorCode["KEY_PATH_INVALID"] = "keyPathInvalid";
ErrorCode["KEY_NOT_ALLOWED"] = "keyNotAllowed";
ErrorCode["KEY_VALUE_INVALID"] = "keyValueInvalid";
return ErrorCode;
}({});
class BaseError extends Error {
constructor(input){
if (typeof input === 'string') {
super(input);
this.code = ErrorCode.NONE;
} else {
super(input.message);
this.code = input.code || ErrorCode.NONE;
}
}
}
class BuildError extends BaseError {
constructor(message){
if (isObject(message)) {
message.message = 'A building error has occurred.';
}
super(message || 'A building error has occurred.');
}
}
class ParseError extends BaseError {
static inputInvalid() {
return new this({
message: 'The shape of the input is not valid.',
code: ErrorCode.INPUT_INVALID
});
}
static keyNotAllowed(name) {
return new this({
message: `The key ${name} is not covered by allowed/default options.`,
code: ErrorCode.KEY_NOT_ALLOWED
});
}
static keyInvalid(key) {
return new this({
message: `The key ${key} is invalid.`,
code: ErrorCode.KEY_INVALID
});
}
static keyPathInvalid(key) {
return new this({
message: `The key path ${key} is invalid.`,
code: ErrorCode.KEY_PATH_INVALID
});
}
static keyValueInvalid(key) {
return new this({
message: `The value of the key ${key} is invalid.`,
code: ErrorCode.KEY_VALUE_INVALID
});
}
constructor(message){
if (isObject(message)) {
message.message = message.message || 'A parsing error has occurred.';
}
super(message || 'A parsing error has occurred.');
}
}
class FieldsBuildError extends BuildError {
}
class FieldsParseError extends ParseError {
}
function buildFieldDomainRecords(data) {
if (typeof data === 'undefined') {
return {};
}
let domainFields = {};
if (Array.isArray(data)) {
domainFields[DEFAULT_ID] = data;
} else {
domainFields = data;
}
return domainFields;
}
/*
* Copyright (c) 2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ function parseFieldsInput(input) {
let output = [];
if (typeof input === 'string') {
output = input.split(',');
} else if (Array.isArray(input)) {
for(let i = 0; i < input.length; i++){
if (typeof input[i] === 'string') {
output.push(input[i]);
}
}
}
return output;
}
/*
* Copyright (c) 2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ function isValidFieldName(input) {
return /^[a-zA-Z_][a-zA-Z0-9_]*$/gu.test(input);
}
function parseQueryFields(input, options) {
let container;
if (options instanceof FieldsOptionsContainer) {
container = options;
} else {
container = new FieldsOptionsContainer(options);
}
// If it is an empty array nothing is allowed
if ((!container.allowedIsUndefined || !container.defaultIsUndefined) && container.keys.length === 0) {
return [];
}
let data = {
[DEFAULT_ID]: []
};
if (isObject$1(input)) {
data = input;
} else if (typeof input === 'string' || Array.isArray(input)) {
data = {
[DEFAULT_ID]: input
};
} else if (container.options.throwOnFailure) {
throw FieldsParseError.inputInvalid();
}
let { keys } = container;
if (keys.length > 0 && hasOwnProperty(data, DEFAULT_ID)) {
data = {
[keys[0]]: data[DEFAULT_ID]
};
} else {
keys = distinctArray([
...keys,
...Object.keys(data)
]);
}
const output = [];
for(let i = 0; i < keys.length; i++){
const path = keys[i];
if (path !== DEFAULT_ID && !isPathAllowedByRelations(path, container.options.relations)) {
if (container.options.throwOnFailure) {
throw FieldsParseError.keyPathInvalid(path);
}
continue;
}
let fields = [];
if (hasOwnProperty(data, path)) {
fields = parseFieldsInput(data[path]);
} else if (hasOwnProperty(container.reverseMapping, path) && hasOwnProperty(data, container.reverseMapping[path])) {
fields = parseFieldsInput(data[container.reverseMapping[path]]);
}
const transformed = {
default: [],
included: [],
excluded: []
};
if (fields.length > 0) {
for(let j = 0; j < fields.length; j++){
let operator;
const character = fields[j].substring(0, 1);
if (character === FieldOperator.INCLUDE) {
operator = FieldOperator.INCLUDE;
} else if (character === FieldOperator.EXCLUDE) {
operator = FieldOperator.EXCLUDE;
}
if (operator) {
fields[j] = fields[j].substring(1);
}
fields[j] = applyMapping(fields[j], container.options.mapping, true);
let isValid;
if (hasOwnProperty(container.items, path)) {
isValid = container.items[path].indexOf(fields[j]) !== -1;
} else {
isValid = isValidFieldName(fields[j]);
}
if (!isValid) {
if (container.options.throwOnFailure) {
throw FieldsParseError.keyNotAllowed(fields[j]);
}
continue;
}
if (operator === FieldOperator.INCLUDE) {
transformed.included.push(fields[j]);
} else if (operator === FieldOperator.EXCLUDE) {
transformed.excluded.push(fields[j]);
} else {
transformed.default.push(fields[j]);
}
}
}
if (transformed.default.length === 0 && hasOwnProperty(container.default, path)) {
transformed.default = container.default[path];
}
if (transformed.included.length === 0 && transformed.default.length === 0 && hasOwnProperty(container.allowed, path)) {
transformed.default = container.allowed[path];
}
transformed.default = Array.from(new Set([
...transformed.default,
...transformed.included
]));
for(let j = 0; j < transformed.excluded.length; j++){
const index = transformed.default.indexOf(transformed.excluded[j]);
if (index !== -1) {
transformed.default.splice(index, 1);
}
}
if (transformed.default.length > 0) {
for(let j = 0; j < transformed.default.length; j++){
let destPath;
if (path !== DEFAULT_ID) {
destPath = path;
} else if (container.options.defaultPath) {
destPath = container.options.defaultPath;
}
output.push({
key: transformed.default[j],
...destPath ? {
path: destPath
} : {}
});
}
}
}
return output;
}
/*
* Copyright (c) 2022-2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ var FilterComparisonOperator = /*#__PURE__*/ function(FilterComparisonOperator) {
FilterComparisonOperator["EQUAL"] = "$eq";
FilterComparisonOperator["NOT_EQUAL"] = "$ne";
FilterComparisonOperator["LIKE"] = "$l";
FilterComparisonOperator["NOT_LIKE"] = "$nl";
FilterComparisonOperator["LESS_THAN_EQUAL"] = "$lte";
FilterComparisonOperator["LESS_THAN"] = "$lt";
FilterComparisonOperator["GREATER_THAN_EQUAL"] = "$gte";
FilterComparisonOperator["GREATER_THAN"] = "$gt";
FilterComparisonOperator["IN"] = "$in";
FilterComparisonOperator["NOT_IN"] = "$nin";
return FilterComparisonOperator;
}({});
var FilterInputOperatorValue = /*#__PURE__*/ function(FilterInputOperatorValue) {
FilterInputOperatorValue["NEGATION"] = "!";
FilterInputOperatorValue["LIKE"] = "~";
FilterInputOperatorValue["LESS_THAN_EQUAL"] = "<=";
FilterInputOperatorValue["LESS_THAN"] = "<";
FilterInputOperatorValue["MORE_THAN_EQUAL"] = ">=";
FilterInputOperatorValue["MORE_THAN"] = ">";
FilterInputOperatorValue["IN"] = ",";
return FilterInputOperatorValue;
}({});
/*
* Copyright (c) 2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ function transformFilterValue(input) {
if (typeof input === 'string') {
input = input.trim();
const lower = input.toLowerCase();
if (lower === 'true') {
return true;
}
if (lower === 'false') {
return false;
}
if (lower === 'null') {
return null;
}
if (input.length === 0) {
return input;
}
const num = Number(input);
if (!Number.isNaN(num)) {
return num;
}
const parts = input.split(',');
if (parts.length > 1) {
return transformFilterValue(parts);
}
}
if (Array.isArray(input)) {
for(let i = 0; i < input.length; i++){
input[i] = transformFilterValue(input[i]);
}
return input.filter((n)=>n === 0 || n === null || !!n);
}
if (typeof input === 'undefined' || input === null) {
return null;
}
return input;
}
function matchOperator(key, value, position) {
if (typeof value === 'string') {
switch(position){
case 'start':
{
if (value.substring(0, key.length) === key) {
return value.substring(key.length);
}
break;
}
case 'end':
{
if (value.substring(0 - key.length) === key) {
return value.substring(0, value.length - key.length - 1);
}
break;
}
}
return undefined;
}
if (Array.isArray(value)) {
let match = false;
for(let i = 0; i < value.length; i++){
const output = matchOperator(key, value[i], position);
if (typeof output !== 'undefined') {
match = true;
value[i] = output;
}
}
if (match) {
return value;
}
}
return undefined;
}
function parseFilterValue(input) {
if (typeof input === 'string' && input.includes(FilterInputOperatorValue.IN)) {
input = input.split(FilterInputOperatorValue.IN);
}
let negation = false;
let value = matchOperator(FilterInputOperatorValue.NEGATION, input, 'start');
if (typeof value !== 'undefined') {
negation = true;
input = value;
}
if (Array.isArray(input)) {
return {
value: input,
operator: negation ? FilterComparisonOperator.NOT_IN : FilterComparisonOperator.IN
};
}
value = matchOperator(FilterInputOperatorValue.LIKE, input, 'start');
if (typeof value !== 'undefined') {
return {
value,
operator: negation ? FilterComparisonOperator.NOT_LIKE : FilterComparisonOperator.LIKE
};
}
value = matchOperator(FilterInputOperatorValue.LESS_THAN_EQUAL, input, 'start');
if (typeof value !== 'undefined') {
return {
value,
operator: FilterComparisonOperator.LESS_THAN_EQUAL
};
}
value = matchOperator(FilterInputOperatorValue.LESS_THAN, input, 'start');
if (typeof value !== 'undefined') {
return {
value,
operator: FilterComparisonOperator.LESS_THAN
};
}
value = matchOperator(FilterInputOperatorValue.MORE_THAN_EQUAL, input, 'start');
if (typeof value !== 'undefined') {
return {
value,
operator: FilterComparisonOperator.GREATER_THAN_EQUAL
};
}
value = matchOperator(FilterInputOperatorValue.MORE_THAN, input, 'start');
if (typeof value !== 'undefined') {
return {
value,
operator: FilterComparisonOperator.GREATER_THAN
};
}
return {
value: input,
operator: negation ? FilterComparisonOperator.NOT_EQUAL : FilterComparisonOperator.EQUAL
};
}
class FiltersOptionsContainer {
initDefault() {
if (!this.options.default) {
this.default = {};
this.defaultKeys = [];
this.defaultOutput = this.buildParseOutput();
return;
}
this.default = toFlatObject(this.options.default);
this.defaultKeys = Object.keys(this.default);
this.defaultOutput = this.buildParseOutput();
}
initAllowed() {
if (typeof this.options.allowed === 'undefined') {
if (typeof this.options.default !== 'undefined') {
const flatten = toFlatObject(this.options.default);
const allowed = Object.keys(flatten);
if (allowed.length > 0) {
this.allowed = allowed;
this.allowedIsUndefined = false;
return;
}
}
this.allowed = [];
this.allowedIsUndefined = true;
return;
}
this.allowed = flattenParseAllowedOption(this.options.allowed);
this.allowedIsUndefined = false;
}
buildParseOutput(input = {}) {
const inputKeys = Object.keys(input || {});
if (!this.options.defaultByElement && inputKeys.length > 0) {
return Object.values(input);
}
if (this.defaultKeys.length > 0) {
const output = [];
for(let i = 0; i < this.defaultKeys.length; i++){
const keyDetails = parseKey(this.defaultKeys[i]);
if (this.options.defaultByElement && inputKeys.length > 0) {
const keyWithPath = buildKeyWithPath(keyDetails);
if (hasOwnProperty(input, keyWithPath)) {
continue;
}
}
if (this.options.defaultByElement || inputKeys.length === 0) {
let path;
if (keyDetails.path) {
path = keyDetails.path;
} else if (this.options.defaultPath) {
path = this.options.defaultPath;
}
output.push(this.transformParseOutputElement({
...path ? {
path
} : {},
key: keyDetails.name,
value: this.default[this.defaultKeys[i]]
}));
}
}
return input ? [
...Object.values(input),
...output
] : output;
}
return input ? Object.values(input) : [];
}
// ^([0-9]+(?:\.[0-9]+)*){0,1}([a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]+)*){0,1}$
transformParseOutputElement(element) {
if (hasOwnProperty(element, 'path') && (typeof element.path === 'undefined' || element.path === null)) {
delete element.path;
}
if (element.operator) {
return element;
}
if (typeof element.value === 'string') {
element = {
...element,
...parseFilterValue(element.value)
};
} else {
element.operator = FilterComparisonOperator.EQUAL;
}
element.value = transformFilterValue(element.value);
return element;
}
constructor(input = {}){
this.options = input;
this.allowed = [];
this.allowedIsUndefined = true;
this.default = {};
this.defaultKeys = [];
this.defaultOutput = [];
this.initDefault();
this.initAllowed();
}
}
function transformFiltersBuildInput(input) {
return toFlatObject(input, {
transformer: (input, output, key)=>{
if (typeof input === 'undefined') {
output[key] = null;
return true;
}
if (Array.isArray(input)) {
// preserve null values
const data = [];
for(let i = 0; i < input.length; i++){
if (input[i] === null) {
input[i] = 'null';
}
if (typeof input[i] === 'number') {
input[i] = `${input[i]}`;
}
if (typeof input[i] === 'string') {
data.push(input[i]);
}
}
output[key] = data.join(',');
return true;
}
if (isObject(input)) {
const tmp = transformFiltersBuildInput(input);
extendObject(output, tmp);
}
return undefined;
}
});
}
class FiltersBuildError extends BuildError {
}
class FiltersParseError extends ParseError {
}
function parseQueryFilters(data, options) {
let container;
if (options instanceof FiltersOptionsContainer) {
container = options;
} else {
container = new FiltersOptionsContainer(options);
}
// If it is an empty array nothing is allowed
if (!container.allowedIsUndefined && container.allowed.length === 0) {
return container.defaultOutput;
}
/* istanbul ignore next */ if (!isObject(data)) {
if (container.options.throwOnFailure) {
throw FiltersParseError.inputInvalid();
}
return container.defaultOutput;
}
const { length } = Object.keys(data);
if (length === 0) {
return container.defaultOutput;
}
const items = {};
// transform to appreciate data format & validate input
const keys = Object.keys(data);
for(let i = 0; i < keys.length; i++){
const value = data[keys[i]];
if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean' && typeof value !== 'undefined' && value !== null && !Array.isArray(value)) {
if (container.options.throwOnFailure) {
throw FiltersParseError.keyValueInvalid(keys[i]);
}
continue;
}
keys[i] = applyMapping(keys[i], container.options.mapping);
const fieldDetails = parseKey(keys[i]);
if (container.allowedIsUndefined && !isValidFieldName(fieldDetails.name)) {
if (container.options.throwOnFailure) {
throw FiltersParseError.keyInvalid(fieldDetails.name);
}
continue;
}
if (typeof fieldDetails.path !== 'undefined' && !isPathAllowedByRelations(fieldDetails.path, container.options.relations)) {
if (container.options.throwOnFailure) {
throw FiltersParseError.keyPathInvalid(fieldDetails.path);
}
continue;
}
const fullKey = buildKeyWithPath(fieldDetails);
if (!container.allowedIsUndefined && container.allowed && !isPathCoveredByParseAllowedOption(container.allowed, [
keys[i],
fullKey
])) {
if (container.options.throwOnFailure) {
throw FiltersParseError.keyInvalid(fieldDetails.name);
}
continue;
}
const filter = container.transformParseOutputElement({
key: fieldDetails.name,
value: value
});
if (container.options.validate) {
if (Array.isArray(filter.value)) {
const output = [];
for(let j = 0; j < filter.value.length; j++){
if (container.options.validate(filter.key, filter.value[j])) {
output.push(filter.value[j]);
} else if (container.options.throwOnFailure) {
throw FiltersParseError.keyValueInvalid(fieldDetails.name);
}
}
filter.value = output;
if (filter.value.length === 0) {
continue;
}
} else if (!container.options.validate(filter.key, filter.value)) {
if (container.options.throwOnFailure) {
throw FiltersParseError.keyValueInvalid(fieldDetails.name);
}
continue;
}
}
if (typeof filter.value === 'string' && filter.value.length === 0) {
if (container.options.throwOnFailure) {
throw FiltersParseError.keyValueInvalid(fieldDetails.name);
}
continue;
}
if (Array.isArray(filter.value) && filter.value.length === 0) {
if (container.options.throwOnFailure) {
throw FiltersParseError.keyValueInvalid(fieldDetails.name);
}
continue;
}
if (fieldDetails.path || container.options.defaultPath) {
filter.path = fieldDetails.path || container.options.defaultPath;
}
items[fullKey] = filter;
}
return container.buildParseOutput(items);
}
class PaginationBuildError extends BuildError {
}
class PaginationParseError extends ParseError {
static limitExceeded(limit) {
return new this({
message: `The pagination limit must not exceed the value of ${limit}.`
});
}
}
// --------------------------------------------------
function finalizePagination(data, options) {
if (typeof options.maxLimit !== 'undefined') {
if (typeof data.limit === 'undefined' || data.limit > options.maxLimit) {
if (options.throwOnFailure) {
throw PaginationParseError.limitExceeded(options.maxLimit);
}
data.limit = options.maxLimit;
}
}
if (typeof data.limit !== 'undefined' && typeof data.offset === 'undefined') {
data.offset = 0;
}
return data;
}
/**
* Transform pagination data to an appreciate data format.
*
* @param data
* @param options
*/ function parseQueryPagination(data, options = {}) {
const output = {};
if (!isObject$1(data)) {
if (options.throwOnFailure) {
throw PaginationParseError.inputInvalid();
}
return finalizePagination(output, options);
}
let { limit, offset } = data;
if (typeof limit !== 'undefined') {
limit = parseInt(limit, 10);
if (!Number.isNaN(limit) && limit > 0) {
output.limit = limit;
} else if (options.throwOnFailure) {
throw PaginationParseError.keyValueInvalid('limit');
}
}
if (typeof offset !== 'undefined') {
offset = parseInt(offset, 10);
if (!Number.isNaN(offset) && offset >= 0) {
output.offset = offset;
} else if (options.throwOnFailure) {
throw PaginationParseError.keyValueInvalid('offset');
}
}
return finalizePagination(output, options);
}
class RelationsBuildError extends BuildError {
}
class RelationsParseError extends ParseError {
}
/*
* Copyright (c) 2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ function includeParents(data) {
for(let i = 0; i < data.length; i++){
const parts = data[i].split('.');
while(parts.length > 0){
parts.pop();
if (parts.length > 0) {
const value = parts.join('.');
if (data.indexOf(value) === -1) {
data.unshift(value);
}
}
}
}
return data;
}
/*
* Copyright (c) 2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ function isValidRelationPath(input) {
return /^[a-zA-Z0-9_-]+([.]*[a-zA-Z0-9_-])*$/gu.test(input);
}
// --------------------------------------------------
function parseQueryRelations(input, options = {}) {
// If it is an empty array nothing is allowed
if (Array.isArray(options.allowed) && options.allowed.length === 0) {
return [];
}
options.mapping = options.mapping || {};
options.pathMapping = options.pathMapping || {};
if (typeof options.includeParents === 'undefined') {
options.includeParents = true;
}
let items = [];
if (typeof input === 'string') {
items = input.split(',');
} else if (Array.isArray(input)) {
for(let i = 0; i < input.length; i++){
if (typeof input[i] === 'string') {
items.push(input[i]);
} else {
throw RelationsParseError.inputInvalid();
}
}
} else if (options.throwOnFailure) {
throw RelationsParseError.inputInvalid();
}
if (items.length === 0) {
return [];
}
const mappingKeys = Object.keys(options.mapping);
if (mappingKeys.length > 0) {
for(let i = 0; i < items.length; i++){
items[i] = applyMapping(items[i], options.mapping);
}
}
for(let j = items.length - 1; j >= 0; j--){
let isValid;
if (options.allowed) {
isValid = isPathCoveredByParseAllowedOption(options.allowed, items[j]);
} else {
isValid = isValidRelationPath(items[j]);
}
if (!isValid) {
if (options.throwOnFailure) {
throw RelationsParseError.keyInvalid(items[j]);
}
items.splice(j, 1);
}
}
if (options.includeParents) {
if (Array.isArray(options.includeParents)) {
const parentIncludes = items.filter((item)=>item.includes('.') && options.includeParents.filter((parent)=>item.startsWith(parent)).length > 0);
items.unshift(...includeParents(parentIncludes));
} else {
items = includeParents(items);
}
}
items = Array.from(new Set(items));
return items.map((key)=>{
const parts = key.split('.');
let value;
if (options.pathMapping && hasOwnProperty(options.pathMapping, key)) {
value = options.pathMapping[key];
} else {
value = parts.pop();
}
return {
key,
value
};
});
}
/*
* Copyright (c) 2021-2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/ var SortDirection = /*#__PURE__*/ function(SortDirection) {
SortDirection["ASC"] = "ASC";
SortDirection["DESC"] = "DESC";
return SortDirection;
}({});
function transformSortBuildInput(input) {
if (typeof input === 'undefined') {
return {};
}
if (typeof input === 'string') {
return input.split(',').reduce((acc, curr)=>{
if (curr.startsWith('-')) {
acc[curr.slice(1)] = SortDirection.DESC;
} else {
acc[curr] = SortDirection.ASC;
}
return acc;
}, {});
}
if (Array.isArray(input)) {
let output = {};
for(let i = 0; i < input.length; i++){
output = {
...output,
...transformSortBuildInput(input[i])
};
}
return output;
}
if (isObject(input)) {
return toFlatObject(input, {
transformer: (input, output)=>{
if (isObject(input)) {
const tmp = transformSortBuildInput(input);
extendObject(output, tmp);
return true;
}
return undefined;
}
});
}
return {};
}
class SortOptionsContainer {
buildDefaultDomainFields() {
if (!this.options.default) {
this.default = {};
this.defaultKeys = [];
this.defaultOutput = this.buildParseOutput();
return;
}
this.default = toFlatObject(this.options.default);
this.defaultKeys = Object.keys(this.default);
this.defaultOutput = this.buildParseOutput();
}
buildAllowedDomainFields() {
if (typeof this.options.allowed === 'undefined') {
if (typeof this.options.default !== 'undefined') {
const flatten = toFlatObject(this.options.default);
const allowed = Object.keys(flatten);
if (allowed.length > 0) {
this.allowed = allowed;
this.allowedIsUndefined = false;
return;
}
}
this.allowed = [];
this.allowedIsUndefined = true;
return;
}
this.allowed = flattenParseAllowedOption(this.options.allowed);
this.allowedIsUndefined = false;
}
buildParseOutput() {
if (this.default) {
const output = [];
const flatten = toFlatObject(this.default);
const keys = Object.keys(flatten);
for(let i = 0; i < keys.length; i++){
const fieldDetails = parseKey(keys[i]);
let path;
if (fieldDetails.path) {
path = fieldDetails.path;
} else if (this.options.defaultPath) {
path = this.options.defaultPath;
}
output.push({
key: fieldDetails.name,
...path ? {
path
} : {},
value: flatten[keys[i]]
});
}
return output;
}
return [];
}
constructor(input = {}){
this.options = input;
this.allowed = [];
this.allowedIsUndefined = true;
this.default = {};
this.defaultKeys = [];
this.defaultOutput = [];
this.buildDefaultDomainFields();
this.buildAllowedDomainFields();
}
}
class SortBuildError extends BuildError {
}
class SortParseError extends ParseError {
}
function parseSortValue(value) {
let direction = SortDirection.ASC;
if (value.substring(0, 1) === '-') {
direction = SortDirection.DESC;
value = value.substring(1);
}
return {
direction,
value
};
}
// --------------------------------------------------
function isMultiDimensionalArray(arr) {
if (!Array.isArray(arr)) {
return false;
}
return arr.length > 0 && Array.isArray(arr[0]);
}
/**
* Transform sort data to appreciate data format.
* @param data
* @param options
*/ function parseQuerySort(data, options) {
let container;
if (options instanceof SortOptionsContainer) {
container = options;
} else {
container = new SortOptionsContainer(options);
}
// If it is an empty array nothing is allowed
if (!container.allowedIsUndefined && container.allowed.length === 0) {
return container.defaultOutput;
}
/* istanbul ignore next */ if (typeof data !== 'string' && !Array.isArray(data) && !isObject$1(data)) {
if (container.options.throwOnFailure) {
throw SortParseError.inputInvalid();
}
return container.defaultOutput;
}
let parts = [];
if (typeof data === 'string') {
parts = data.split(',');
}
if (Array.isArray(data)) {
parts = data.filter((item)=>typeof item === 'string');
}
if (isObject$1(data)) {
const keys = Object.keys(data);
for(let i = 0; i < keys.length; i++){
/* istanbul ignore next */ if (!hasOwnProperty(data, keys[i]) || typeof keys[i] !== 'string' || typeof data[keys[i]] !== 'string') {
if (container.options.throwOnFailure) {
throw SortParseError.keyValueInvalid(keys[i]);
}
continue;
}
const fieldPrefix = data[keys[i]].toLowerCase() === 'desc' ? '-' : '';
parts.push(fieldPrefix + keys[i]);
}
}
const items = {};
let matched = false;
for(let i = 0; i < parts.length; i++){
const { value, direction } = parseSortValue(parts[i]);
parts[i] = value;
const key = applyMapping(parts[i], container.options.mapping);
const fieldDetails = parseKey(key);
if (container.allowedIsUndefined && !isValidFieldName(fieldDetails.name)) {
if (container.options.throwOnFailure) {
throw SortParseError.keyInvalid(fieldDetails.name);
}
continue;
}
if (!isPathAllowedByRelations(fieldDetails.path, container.options.relations) && typeof fieldDetails.path !== 'undefined') {
if (container.options.throwOnFailure) {
throw SortParseError.keyPathInvalid(fieldDetails.path);
}
continue;
}
const keyWithAlias = buildKeyWithPath(fieldDetails);
if (!container.allowedIsUndefined && container.allowed && !isMultiDimensionalArray(container.options.allowed) && !isPathCoveredByParseAllowedOption(container.allowed, [
key,
keyWithAlias
])) {
if (container.options.throwOnFailure) {
throw SortParseError.keyNotAllowed(fieldDetails.name);
}
continue;
}
matched = true;
let path;
if (fieldDetails.path) {
path = fieldDetails.path;
} else if (container.options.defaultPath) {
path = container.options.defaultPath;
}
items[keyWithAlias] = {
key: fieldDetails.name,
...path ? {
path
} : {},
value: direction
};
}
if (!matched) {
return container.defaultOutput;
}
if (isMultiDimensionalArray(container.options.allowed)) {
// eslint-disable-next-line no-labels,no-restricted-syntax
outerLoop: for(let i = 0; i < container.allowed.length; i++){
const temp = [];
const keyPaths = flattenParseAllowedOption(container.options.allowed[i]);
for(let j = 0; j < keyPaths.length; j++){
let keyWithAlias = keyPaths[j];
let key;
const parts = keyWithAlias.split('.');
if (parts.length > 1) {
key = parts.pop();
} else {
key = keyWithAlias;
keyWithAlias = buildKeyPath(key, container.options.defaultPath);
}
if (hasOwnProperty(items, key) || hasOwnProperty(items, keyWithAlias)) {
const item = hasOwnProperty(items, key) ? items[key] : items[keyWithAlias];
temp.push(item);
} else {
continue outerLoop;
}
}
return temp;
}
// if we get no match, the sort data is invalid.
return [];
}
return Object.values(items);
}
class QueryBuilder {
// --------------------------------------------------
add(input) {
if (typeof input[Parameter.FIELDS] !== 'undefined') {
this.addFields(input[Parameter.FIELDS]);
}
if (typeof input[URLParameter.FIELDS] !== 'undefined') {
this.addFields(input[URLParameter.FIELDS]);
}
if (typeof input[Parameter.FILTERS] !== 'undefined') {
this.addFilters(input[Parameter.FILTERS]);
}
if (typeof input[URLParameter.FILTERS] !== 'undefined') {
this.addFilters(input[URLParameter.FILTERS]);
}
if (typeof input[Parameter.PAGINATION] !== 'undefined') {
this.addPagination(input[Parameter.PAGINATION]);
}
if (typeof input[URLParameter.PAGINATION] !== 'undefined') {
this.addPagination(input[URLParameter.PAGINATION]);
}
if (typeof input[Parameter.RELATIONS] !== 'undefined') {
this.addRelations(input[Parameter.RELATIONS]);
}
if (typeof input[URLParameter.RELATIONS] !== 'undefined') {
this.addRelations(input[URLParameter.RELATIONS]);
}
if (typeof input[Parameter.SORT] !== 'undefined') {
this.addSort(input[Parameter