UNPKG

@conjoon/extjs-dev-webmailsim

Version:

This Sencha Ext JS NPM package contains mock data for development of @conjoon/extjs-app-webmail]

1,015 lines (789 loc) 32.6 kB
/** * conjoon * extjs-dev-webmailsim * Copyright (C) 2020-2023 Thorsten Suckow-Homberg https://github.com/conjoon/extjs-dev-webmailsim * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * JsonSimlet for MessageItem-data. */ Ext.define("conjoon.dev.cn_mailsim.data.MessageItemSim", { extend: "conjoon.dev.cn_mailsim.data.EmailBaseSim", requires: [ "conjoon.dev.cn_mailsim.data.table.MessageTable" ], doDelete: function (ctx) { const me = this, keys = me.extractCompoundKey(ctx.url), MessageTable = conjoon.dev.cn_mailsim.data.table.MessageTable; /* eslint-disable-next-line no-console*/ console.log("DELETE MessageItem - ",keys); let ret = me.prepareResponseHeader(), found = false, id = keys.id, mailAccountId = keys.mailAccountId, mailFolderId = keys.mailFolderId; if (!id) { /* eslint-disable-next-line no-console*/ console.log("DELETE MessageItem - no numeric id specified."); return {status: 400, success: false}; } let messageItems = MessageTable.getMessageItems(), mi; for (var i in messageItems) { mi = messageItems[i]; if (mi.id === id && mi.mailFolderId === mailFolderId && mi.mailAccountId === mailAccountId) { delete messageItems[i]; found = true; break; } } if (!found) { return {status: 404, success: false}; } ret.responseText = Ext.JSON.encode({success: "A"}); return ret; }, doPost: function (ctx) { const me = this, jsonData = ctx.xhr.options.jsonData, keys = me.extractCompoundKey(ctx.url); let target; if (jsonData) { target = jsonData.data.type; } if (!target) { /** * avoid /MessageItems - append query separator "?" */ if (ctx.url.indexOf("/send") !== -1) { return this.sendMessage(ctx); } throw new Error("expected target with request"); } if (target === "MessageBody") { if (Object.keys(jsonData.data.attributes).includes("textHtml") || Object.keys(jsonData.data.attributes).includes("textPlain")) { return this.postMessageBody(ctx); } } me.beginLog("POST", target, ctx, jsonData); const ret = me.prepareResponseHeader(ctx, me.getExpectedQuery("post.messageitem")), MessageTable = conjoon.dev.cn_mailsim.data.table.MessageTable; if (ret.status !== 200) { return ret; } let draft = me.extractValues(ctx.xhr.options.jsonData); ["from", "to", "cc", "bcc", "replyTo"].forEach(key => { draft[key] && (draft[key] = Ext.JSON.decode(draft[key])); }); if (draft.subject === "TESTFAIL") { ret.status = 400; ret.statusText = "Bad Request"; me.endLog("POST", target, ret); return ret; } draft = MessageTable.createMessageDraft(keys.mailAccountId, keys.mailFolderId, draft); ret.responseText = Ext.JSON.encode({ included: [ me.getIncludedOrDummy(draft.mailAccountId, draft.mailFolderId) ], data: me.toJsonApi({ id: draft.id, mailFolderId: draft.mailFolderId, mailAccountId: draft.mailAccountId }, "MessageDraft")}); /* eslint-disable-next-line no-console*/ me.endLog("POST", target, ret); return ret; }, doPatch: function (ctx) { const me = this, keys = me.extractCompoundKey(ctx.url), ret = me.prepareResponseHeader(), MessageTable = conjoon.dev.cn_mailsim.data.table.MessageTable, values = me.extractValues(ctx.xhr.options.jsonData), target = ctx.xhr.options.jsonData.data.type; let result; if (!target) { throw new Error("\type\" must be available in request body"); } if (Object.keys(values).filter(val => !["flagged", "id", "seen"].includes(val)).length === 0 || target === "MessageBody") { me.beginLog("PATCH", target, ctx, values); if (target === "MessageBody") { result = MessageTable.updateMessageBody(keys.mailAccountId, keys.mailFolderId, keys.id, values); } else { result = MessageTable.updateMessageItem(keys.mailAccountId, keys.mailFolderId, keys.id, values); result = Object.fromEntries(Object.entries(result).filter((entry) => !!values[entry[0]])); } result.recent = false; let retVal = { included: [ me.getIncludedOrDummy(keys.mailAccountId, keys.mailFolderId) ], data: me.toJsonApi(result, target) }; ret.responseText = Ext.JSON.encode(retVal); me.endLog("PATCH", target, ret); return ret; } me.beginLog("PATCH (idChange)", target, ctx, values); if (values.subject === "TESTFAIL") { ret.status = 500; ret.responseText = Ext.JSON.encode({ success: false }); me.endLog("PATCH (idChange)", target, ret); return ret; } let updatedDraft = MessageTable.updateMessageDraft( keys.mailAccountId, keys.mailFolderId, keys.id, values, true ); let draft = MessageTable.getMessageDraft( updatedDraft.mailAccountId, updatedDraft.mailFolderId, updatedDraft.id ); delete values.localId; for (let i in values) { if (draft[i]) { values[i] = draft[i]; } } ret.responseText = Ext.JSON.encode({ included: [ me.getIncludedOrDummy(draft.mailAccountId, draft.mailFolderId) ], data: me.toJsonApi(values, "MessageDraft") }); me.endLog("PATCH (idChange)", target, ret); return ret; }, data: function (ctx) { let me = this, keys = me.extractCompoundKey(ctx.url), id, MessageTable = conjoon.dev.cn_mailsim.data.table.MessageTable, messageItems = MessageTable.getMessageItems(), mailAccountId, mailFolderId, fieldsMailFolder = ctx.params["fields[MailFolder]"] !== undefined ? ctx.params["fields[MailFolder]"].split(",") : undefined, fieldsMessageBody = ctx.params["fields[MessageBody]"] !== undefined ? ctx.params["fields[MessageBody]"].split(",") : undefined, fieldsMessageItem = ctx.params["fields[MessageItem]"] !== undefined ? ctx.params["fields[MessageItem]"].split(",") : undefined, relfieldsMessageItem = ctx.params["relfield:fields[MessageItem]"] !== undefined ? ctx.params["relfield:fields[MessageItem]"].split(",") : undefined, messageItemIds = [], relInclude = ctx.params["include"] ? ctx.params["include"].split(",") : undefined; // translate the parameters here since the Simlet works internally with default values, // as it relies on "start" and "limit" to be available with params.start and params.limit if (ctx.xhr.options.proxy) { ctx.params.start = ctx.params[ctx.xhr.options.proxy.getStartParam()]; ctx.params.limit = ctx.params[ctx.xhr.options.proxy.getLimitParam()]; } fieldsMailFolder = fieldsMailFolder && fieldsMailFolder[0] === "" ? [] : fieldsMailFolder; fieldsMessageBody = fieldsMessageBody && fieldsMessageBody[0] === "" ? [] : fieldsMessageBody; fieldsMessageItem = fieldsMessageItem && fieldsMessageItem[0] === "" ? [] : fieldsMessageItem; if (ctx.params.messageItemIds) { throw new Error("unexpected param messageItemIds"); } if (relfieldsMessageItem && fieldsMessageItem) { throw new Error("relfield extension not allowed to be used with same resource object"); } mailAccountId = keys.mailAccountId; mailFolderId = keys.mailFolderId; let idFilter = null; if (ctx.params.filter) { idFilter = this.toExtJsFilter(JSON.parse(ctx.params.filter)); if (idFilter) { idFilter.some(filter => { if (filter.property === "id") { idFilter = idFilter[0].property === "id" ? idFilter[0].value : []; messageItemIds = idFilter; return true; } }); } } let excludeFields = [], includeFields = []; // sparse fieldsets / relfield extension support let defaultMessageItemFields = Object.keys(Object.entries(messageItems)[0][1]).filter(field => !["cc", "bcc", "replyTo"].includes(field)); if (relfieldsMessageItem) { let allPrefixed = relfieldsMessageItem.every(field => field.indexOf("+") === 0 || field.indexOf("-") === 0); let allNotPrefixed = relfieldsMessageItem.every(field => field.indexOf("+") === -1 && field.indexOf("-") === -1); if (allPrefixed !== true && allNotPrefixed !== true) { throw new Error("relfield extension requires prefixes for all fields, or no prefixes at all"); } if (allNotPrefixed) { // no prefixed: assign relfields to regular sparse fieldsets, let processing continue fieldsMessageItem = relfieldsMessageItem; } else { fieldsMessageItem = defaultMessageItemFields; relfieldsMessageItem.forEach(field => { if (field.indexOf("+") === 0) { let f = field.substring(1); if (!fieldsMessageItem.includes(f)) { fieldsMessageItem.push(f); } } if (field.indexOf("-") === 0) { let f = field.substring(1); fieldsMessageItem = fieldsMessageItem.filter(field => field !== f); } }); } } if (fieldsMessageItem) { // fields[MessageItem]=&... -> exclude all // fields[MessageItem]=subject -> exclude all except subject excludeFields = defaultMessageItemFields.filter( field => !fieldsMessageItem.concat(["mailAccountId", "mailFolderId", "id"]).includes(field) ); } else if (!fieldsMessageItem) { includeFields = defaultMessageItemFields; } if (ctx.url.indexOf("/MessageBody") !== -1) { let mb = me.getMessageBody(keys.mailAccountId, keys.mailFolderId, keys.id, true); if (mb.status && mb.status >= 400) { return mb; } /* eslint-disable-next-line no-console*/ console.log("GET MessageBody/MessageBodyDraft ", ctx.url, keys); return { included: [ me.getIncludedOrDummy(mailAccountId, mailFolderId) ], data: mb }; } if (ctx.params["relfield:fields[MessageItem]"] === "+cc,+bcc,+replyTo,-hasAttachments,-size") { /* eslint-disable-next-line no-console*/ console.log("GET MessageDraft ", ctx.url); id = keys.id; let fitem = MessageTable.getMessageDraft(mailAccountId, mailFolderId, id); if (fitem) { fitem = Object.fromEntries( Object.entries(fitem).filter( data => !fieldsMessageItem.includes(data[1]) )); } let retVal = null; if (!fitem) { retVal = { status: 404, statusText: "Not Found Here" }; //ret.statusText = "Not Found"; //return ret; } else { retVal = { included: [ me.getIncludedOrDummy(mailAccountId, mailFolderId) ], data: me.toJsonApi(fitem, "MessageDraft") }; } /* eslint-disable-next-line no-console*/ console.log("GET MessageDraft, response ", retVal); return retVal; } if (keys.id) { id = keys.id; let item; for (let i in messageItems) { let messageItem = messageItems[i]; if (messageItem.mailAccountId === keys.mailAccountId && messageItem.mailFolderId === keys.mailFolderId && messageItem.id === "" + id) { item = messageItem; break; } } /* eslint-disable-next-line no-console*/ console.log("GET MessageItem ", item); if (!item) { return {status: 404, statusText: "Not Found"}; } return { included: [ me.getIncludedOrDummy(mailAccountId, mailFolderId) ], data: me.toJsonApi(item, "MessageItem") }; } else if (!id) { // get data for either /MessageBodies or /MessageItems if (ctx.params["fields[MailFolder]"] === undefined || (!relInclude || !relInclude.includes("MailFolder"))) { throw new Error("sim expects GET MessageItems to include MailFolders relationship"); } let resourceTarget = "MessageItem"; if (ctx.url.indexOf("/MessageBodies") !== -1) { resourceTarget = "MessageBody"; } /* eslint-disable-next-line no-console*/ console.log(`GET ${resourceTarget} `, ctx, keys); var items = []; for (let i in messageItems) { let messageItem = messageItems[i]; if (messageItem.mailAccountId === keys.mailAccountId && messageItem.mailFolderId === keys.mailFolderId) { if (messageItemIds.length) { if (!messageItemIds.includes(messageItem.id)) { continue; } } if (excludeFields.length) { excludeFields.forEach(field => { delete messageItem[field]; }); } else if (includeFields.length) { let tmpItem = {}; includeFields.forEach(field => { tmpItem[field] = messageItem[field]; }); messageItem = tmpItem; } items.push(messageItem); } } items = this.sortAndFilter(ctx, items); items = items.map(item => me.toJsonApi(item, "MessageItem")); let resourcePage = this.getPage(ctx, items); let incs = []; if (relInclude.includes("MailFolder")) { let mailFolder = me.getIncludedOrDummy(mailAccountId, mailFolderId); mailFolder.attributes.totalMessages = items.length; if (fieldsMailFolder !== undefined && fieldsMailFolder.length === 0) { delete mailFolder.attributes; } else { mailFolder.attributes = Object.fromEntries( Object.entries(mailFolder.attributes).filter(entry => { return fieldsMailFolder.includes(entry[0]) === true; }) ); } incs.push(mailFolder); } if (resourceTarget === "MessageBody" || relInclude.includes("MessageBody")) { let messageBodies = []; resourcePage.forEach(item => { let messageBody = me.toJsonApi(me.getIncludedOrDummy({ entity: "MessageBody", mailAccountId: mailAccountId, mailFolderId: mailFolderId, id: item.id, params: ctx.params }), "MessageBody"); if (fieldsMessageBody !== undefined && fieldsMailFolder.fieldsMessageBody === 0) { delete messageBody.attributes; } messageBodies.push(messageBody); }); if (resourceTarget === "MessageBody") { resourcePage = messageBodies; } else { incs = incs.concat(messageBodies); } } items = { data: resourcePage, included: incs }; /* eslint-disable-next-line no-console*/ console.log(`GET ${resourceTarget} response`, items); if (!ctx.xhr.options.proxy) { // create a proxy mock so that rootProperty is applied to data ctx.xhr.options.proxy = { getReader: () => { return { getTotalProperty: () => items.length, rootProperty: "data", getRootProperty: () => "data" }; } }; } return items; } else { return messageItems; } }, /** * Adds recent items to the list of MessageItems in the MessageTable, if a filter * is detected. * * @param ctx * @returns {*} */ doGet (ctx) { const me = this, MessageTable = conjoon.dev.cn_mailsim.data.table.MessageTable; if (MessageTable.buildRandomNumber(0, 1) === 1 && ctx.url.indexOf("/MessageItems?") !== -1 && ctx.params.filter) { MessageTable.addRecentItems( MessageTable.buildRandomNumber(0, 2), me.extractCompoundKey(ctx.url).mailFolderId ); } // GET MessageItems // if (ctx.url.indexOf("/MessageItems?") !== -1 && ctx.params.include === "MailFolder") { let retVal = me.data(ctx), ret = me.prepareResponseHeader(); if (retVal.status && retVal.status >= 400) { return retVal; } ret.responseText = Ext.JSON.encode(retVal); return ret; }, getMessageBody: function (mailAccountId, mailFolderId, id, autoCreate) { const me = this; let retVal; if (conjoon.dev.cn_mailsim.data.table.MessageTable.peekMessageBody( mailAccountId, mailFolderId, id )) { retVal = me.toJsonApi( conjoon.dev.cn_mailsim.data.table.MessageTable .getMessageBody( mailAccountId, mailFolderId, id ), "MessageBody" ); let entity = "MessageBodyDraft/MessageBody"; /* eslint-disable-next-line no-console*/ console.log("GET " + entity + ", response, ", retVal); return retVal; } retVal = {status: 404, statusText: "Not found"}; /* eslint-disable-next-line no-console*/ console.log("GET MessageBody, response, ", retVal); return retVal; }, postMessageBody: function (ctx) { const me = this, MessageTable = conjoon.dev.cn_mailsim.data.table.MessageTable, key = me.extractCompoundKey(ctx.url); me.beginLog("POST", "MessageBody", ctx, ctx.xhr.options.jsonData); var body = {}, ret = me.prepareResponseHeader(ctx, me.getExpectedQuery("post.messagebody")), newRec; body = me.extractValues(ctx.xhr.options.jsonData); delete body.attributes; if (!body.textPlain && body.textHtml) { body.textPlain = Ext.util.Format.stripTags(body.textHtml); } else if (!body.textHtml) { body.textHtml = body.textPlain; } let draft; // POSTed with meta data, references existing data if (body.id) { newRec = MessageTable.updateMessageBody(key.mailAccountId, key.mailFolderId, body.id, { textPlain: body.textPlain, textHtml: body.textHtml }); } else { draft = MessageTable.createMessageDraft(key.mailAccountId, key.mailFolderId, {}); newRec = MessageTable.updateMessageBody(key.mailAccountId, key.mailFolderId, draft.id, { textPlain: body.textPlain, textHtml: body.textHtml }); } let retVal = { included: [ me.getIncludedOrDummy(newRec.mailAccountId, newRec.mailFolderId) ], data: me.toJsonApi({ id: newRec.id, mailFolderId: newRec.mailFolderId, mailAccountId: newRec.mailAccountId, textPlain: newRec.textPlain, textHtml: newRec.textHtml }, "MessageBody") }; ret.responseText = Ext.JSON.encode(retVal); me.endLog("POST", "MessageBody", ret); return ret; }, /** * extract key/value pairs from submitted jsonData in data.attributes */ extractValues (jsonData) { const values = Object.assign( {}, jsonData.data, jsonData.data.attributes ); delete values.mailFolderId; delete values.mailAccountId; if (values.relationships && values.relationships.MailFolder) { values.mailFolderId = values.relationships.MailFolder.data.id; delete values.relationships; } delete values.type; delete values.attributes; return values; }, /** * Returns a numeric array with the following values: * mailAccountId, mailFolderId, id * * @param url * @returns {*[]} */ extractCompoundKey: function (url) { let pt = url.split("/"), id = pt.pop().split("?")[0], mailFolderId,mailAccountId; if (["MessageItems", "MessageBodies"].includes(id)) { id = undefined; pt.push("foo"); } if (["MessageBodies", "MessageBody", "MessageDraft", "MessageItem", "send"].includes(id)) { id = pt.pop(); } pt.pop(); mailFolderId = pt.pop(); pt.pop(); mailAccountId = pt.pop(); mailAccountId = decodeURIComponent(mailAccountId); if (this.simletAdapter.validateMailAccountId(mailAccountId) !== true) { throw new Error("mailAccountId url/header mismatch"); } return Object.assign({ mailAccountId: mailAccountId, mailFolderId: decodeURIComponent(mailFolderId) }, id ? {id: decodeURIComponent(id)} : {}); }, /** * Sends a Message. * @param ctx * @returns {{}} */ sendMessage: function (ctx) { const me = this, ret = me.prepareResponseHeader(), MessageTable = conjoon.dev.cn_mailsim.data.table.MessageTable, key = me.extractCompoundKey(ctx.url), id = key.id, mailAccountId = key.mailAccountId, mailFolderId = key.mailFolderId, draft = MessageTable.getMessageDraft(mailAccountId, mailFolderId, id); me.beginLog("POST", "Send Message", ctx); if (draft.xCnDraftInfo) { let [accountId, folderId, id] = Ext.decode(atob(draft.xCnDraftInfo)); let items = MessageTable.getMessageItems(); for (let i in items) { if (items[i].mailAccountId === accountId && items[i].id === id && items[i].mailFolderId === folderId) { MessageTable.updateAllItemData( items[i].mailAccountId, items[i].mailFolderId, items[i].id, {answered: true}); break; } } } if (draft.subject === "SENDFAIL") { ret.status = 500; ret.statusText = "Internal Server Error"; return ret; } ret.responseText = Ext.JSON.encode({ }); me.endLog("POST", "Send Message", ret); return ret; }, toJsonApi (item, type) { switch (type) { case "MessageBody": case "MessageBodyDraft": case "MessageDraft": case "MessageItem": item = Object.assign({ type: type, id: item.id, relationships: { MailFolder: { data: { id: item.mailFolderId, type: "MailFolder" } } } }, { attributes: Object.fromEntries(Object.entries(item).filter(entry => { return !["mailFolderId", "mailAccountId", "id"].includes(entry[0]); })) }); break; } if (type === "MessageItem") { item.relationships.MessageBody = { data: {id: item.id, type: "MessageBody"} }; } if (type === "MessageBody") { item.relationships.MessageItem = { data: {id: item.id, type: "MessageItem"} }; } return item; }, getIncludedOrDummy (mailAccountId, mailFolderId) { if (typeof mailAccountId === "object") { const cfg = mailAccountId, entity = cfg.entity, params = cfg.params || {}, MessageTable = conjoon.dev.cn_mailsim.data.table.MessageTable; let body; switch (entity) { case "MessageBody": body = MessageTable.getMessageBody( cfg.mailAccountId, cfg.mailFolderId, cfg.id ); if (params["options[textPlain][length]"]) { body.textPlain = body.textPlain.substr(0, params["options[textPlain][length]"]); } if (params["options[textPlain][textHtml]"]) { body.textHtml = body.textPlain.substr(0, params["options[textHtml][length]"]); } return body; } throw new Error("missing entity"); } const MailFolderTable = conjoon.dev.cn_mailsim.data.table.MailFolderTable; return MailFolderTable.get(mailAccountId, mailFolderId) || MailFolderTable.createDummy(mailAccountId, mailFolderId); }, prepareResponseHeader (ctx, path) { let ret = {status: 200, statusText: "OK"}; if (!ctx && !path) { return ret; } Object.entries(path).every((entry) => { let path = entry[0], value = entry[1]; if (!l8.unchain(path, ctx) === value) { ret = { status: 400, statusText: "Bad Request", responseText: JSON.stringify({ errors: [{code: 400, title: "Bad Request", detail: `${path} is missing in request`}] }) }; return false; } }); return ret; }, sortAndFilter (ctx, items) { function makeSortFn (def, cmp) { var order = def.direction, sign = (order && order.toUpperCase() === "DESC") ? -1 : 1; return function (leftRec, rightRec) { var lhs = leftRec[def.property], rhs = rightRec[def.property], c = (lhs < rhs) ? -1 : ((rhs < lhs) ? 1 : 0); if (c || !cmp) { return c * sign; } return cmp(leftRec, rightRec); }; } function makeSortFns (defs, cmp) { var sortFn, i; for (sortFn = cmp , i = defs && defs.length; i; ) { sortFn = makeSortFn(defs[--i], sortFn); } return sortFn; } let fields = ctx.params.sort ? ctx.params.sort.split(",") : []; fields = fields.map( field => { return {property: field.split("-").pop(), direction: field.indexOf("-") === 0 ? "DESC" : "ASC"}; }); let sortFn = makeSortFns(fields); if (sortFn) { items = Ext.Array.sort(items, sortFn); } if (ctx.params.filter && Ext.decode(ctx.params.filter)) { let filters = new Ext.util.FilterCollection(); const resFilters = this.toExtJsFilter(Ext.decode(ctx.params.filter)); filters.add(this.processFilters(resFilters)); items = Ext.Array.filter(items, filters.getFilterFn()); } return items; }, beginLog (method, target) { /* eslint-disable-next-line no-console*/ console.log("\n-------------\n----BEGIN----\n" + `${method} ${target}`, ... Array.prototype.slice.apply(arguments, [2])); }, endLog (method, target) { /* eslint-disable-next-line no-console*/ console.log(`${method} ${target}`, ... Array.prototype.slice.apply(arguments, [2])); /* eslint-disable-next-line no-console*/ console.log("-------------\n-----END-----"); }, getExpectedQuery (type) { switch (type) { case "post.messagebody": case "post.messageitem": return {"params.include": "MailFolder", "params.fields[MailFolder]": ""}; default: return {}; } }, /** * Filters now available in polish notation: * @example: * { * "in": { * "id": [1, 2, 3] * } * } * @see php-lib-conjoon#8 * * @return {Array} */ toExtJsFilter (apiFiter) { const resFilters = []; Object.entries(apiFiter).forEach((filter) => { const key = filter[0], value = filter[1]; if (["AND", "OR"].includes(key)) { throw new Error("no support for logical operators available"); } resFilters.push({ property: Object.entries(value)[0][0], value: Object.entries(value)[0][1], operator: key }); }); return resFilters; } });