Generalize store quering functions

This commit is contained in:
Zaid Abdulla committed 2018-12-26 00:22:07 -08:00
1 parent 93c6a4dfcd
commit 9b827c5f5e
26 files changed
+1362 -321

No files matched your search

+45 -19
View File
@@ -3,41 +3,67 @@
import m from "mithril";
import layout from "./layout";
import * as store from "./store";
import * as overviewPage from "./overview-page";
import * as devicesPage from "./devices-page";
import * as devicePage from "./device-page";
import * as errorPage from "./error-page";
let state;
let lastRenderTimestamp = 0;
let pageVisitTimestamp = 0;
let fulfillTimeout = null;
function fulfill() {
clearTimeout(fulfillTimeout);
fulfillTimeout = setTimeout(() => {
store.fulfill(lastRenderTimestamp, pageVisitTimestamp).then(updated => {
if (updated) m.redraw();
});
}, 100);
}
function pagify(pageName, page) {
const component = {
render: () => {
if (state && state.error) return m(errorPage.component, state);
else return m(layout, { page: pageName }, m(page.component, state));
lastRenderTimestamp = Date.now();
let c;
if (state && state.error) c = m(errorPage.component, state);
else c = m(layout, { page: pageName }, m(page.component, state));
fulfill();
return c;
}
};
if (page.init)
component.onmatch = args => {
return new Promise(resolve => {
page
.init(args)
.then(st => {
if (!st) return m.route.set("/");
state = st;
resolve();
})
.catch(err => {
state = { error: err.message };
resolve();
});
});
};
component.onmatch = args => {
pageVisitTimestamp = Date.now();
if (!page.init) {
fulfill();
return;
}
return new Promise(resolve => {
page
.init(args)
.then(st => {
if (!st) return m.route.set("/");
state = st;
resolve();
fulfill();
})
.catch(err => {
state = { error: err.message };
resolve();
});
});
};
return component;
}
m.route(document.body, "/overview", {
"/overview": pagify("overview", overviewPage),
"/devices": pagify("devices", devicesPage)
"/devices": pagify("devices", devicesPage),
"/devices/:id": pagify("devices", devicePage)
});
+16
View File
@@ -0,0 +1,16 @@
"use strict";
import parameter from "./components/parameter";
import parameterList from "./components/parameter-list";
import parameterTable from "./components/parameter-table";
import overviewDot from "./components/overview-dot";
function get(name) {
if (name === "parameter") return parameter;
else if (name === "parameter-list") return parameterList;
else if (name === "parameter-table") return parameterTable;
else if (name === "overview-dot") return overviewDot;
else throw new Error(`No such component '${name}'`);
}
export { get };
+37
View File
@@ -0,0 +1,37 @@
"use strict";
import m from "mithril";
import * as config from "../config";
import * as store from "../store";
const CHARTS = config.get("ui.overview.charts");
const component = {
view: vnode => {
const device = vnode.attrs.device;
const chart = CHARTS[vnode.attrs.chart];
for (let slice of Object.values(chart.slices)) {
const filter = store.unpackFilter(slice.filter);
if (filter.test(device)) {
const dot = m(
"svg",
{
width: "1em",
height: "1em",
xmlns: "http://www.w3.org/2000/svg",
"xmlns:xlink": "http://www.w3.org/1999/xlink"
},
m("circle", {
cx: "0.5em",
cy: "0.5em",
r: "0.4em",
fill: slice.color
})
);
return m("span.overview-dot", dot, `${slice.label}`);
}
}
}
};
export default component;
+27
View File
@@ -0,0 +1,27 @@
"use strict";
import m from "mithril";
import * as components from "../components";
const component = {
view: vnode => {
const device = vnode.attrs.device;
const parameters = vnode.attrs.parameters;
const filtered = Object.values(parameters).filter(
p => p.parameter in device
);
const rows = filtered.map(p =>
m(
"tr",
m("th", p.label),
m(
"td",
m(components.get("parameter"), Object.assign({ device: device }, p))
)
)
);
return m("table.parameter-list", rows);
}
};
export default component;
+42
View File
@@ -0,0 +1,42 @@
"use strict";
import m from "mithril";
import * as components from "../components";
const component = {
view: vnode => {
const device = vnode.attrs.device;
const object = vnode.attrs.object;
const parameters = vnode.attrs.parameters;
if (!device[object]) return null;
const instances = new Set();
for (let p in device)
if (p.startsWith(object))
instances.add(p.slice(0, p.indexOf(".", object.length + 1)));
const thead = m(
"thead",
m("tr", Object.values(parameters).map(p => m("th", p.label)))
);
const rows = [];
for (let i of instances) {
const row = Object.values(parameters).map(p =>
m(
"td",
m(
components.get("parameter"),
Object.assign({ device: device }, p, {
parameter: `${i}.${p.parameter}`
})
)
)
);
rows.push(m("tr", row));
}
return m("table.parameter-table", thead, m("tbody", rows));
}
};
export default component;
+30
View File
@@ -0,0 +1,30 @@
"use strict";
import m from "mithril";
import * as components from "../components";
const component = {
view: vnode => {
const param = vnode.attrs.device[vnode.attrs.parameter];
if (!param || !param.value) return m("span.na", "N/A");
let value = param.value[0];
if (param.value[1] === "xsd:dateTime")
value = new Date(value).toISOString();
let meta;
if (vnode.attrs.meta) {
const metaAttrs = Object.assign(
{ device: vnode.attrs.device, parameter: vnode.attrs.parameter },
vnode.attrs.meta
);
meta = m("span", [
" (",
m(components.get(metaAttrs.type), metaAttrs),
")"
]);
}
return [m("span", { title: param.valueTimestamp }, value), meta];
}
};
export default component;
+35 -64
View File
@@ -1,69 +1,40 @@
"use strict";
export default {
pageSize: 10,
filter: [
{
label: "Serial number",
parameter: "DeviceID.SerialNumber",
type: "string"
},
{
label: "Product class",
parameter: "DeviceID.ProductClass",
type: "string"
},
{
label: "Tag",
parameter: "tag",
type: "string"
}
],
index: [
{
label: "Serial number",
parameter: "DeviceID.SerialNumber"
},
{
label: "Product class",
parameter: "DeviceID.ProductClass"
},
{
label: "Software version",
parameter: "InternetGatewayDevice.DeviceInfo.SoftwareVersion"
},
{
label: "MAC",
parameter:
"InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress"
}
],
overview: {
online: {
label: "Online devices",
charts: {
all: {
label: "All devices",
slices: {
onlineNow: {
label: "Online now",
color: "#31a354",
filter: "Events.Inform > NOW() - 5 * 60 * 1000"
},
past24: {
label: "Past 24 hours",
color: "#addd8e",
filter:
"Events.Inform > (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000) AND Events.Inform < (NOW() - 5 * 60 * 1000)"
},
others: {
label: "Others",
color: "#f7fcb9",
filter: "Events.Inform < (NOW() - 5 * 60 * 1000) - (24 * 60 * 60 * 1000)"
}
}
import * as Filter from "../common/filter";
const configFile = window.clientConfig;
const cache = {};
function parse(name, value) {
if (/^ui\.overview\.charts\.[^.]+\.slices\.[^.]+\.filter$/.test(name))
return new Filter(value);
return value;
}
function get(name) {
let v = cache[name];
if (v === undefined) {
v = configFile[name];
if (v != null) {
v = parse(name, v);
} else {
const prefix = `${name}.`;
for (let k of Object.keys(configFile))
if (k.startsWith(prefix)) {
v = v || {};
let ar = k.slice(prefix.length).split(".");
let ref = v;
for (let a of ar.slice(0, -1)) ref = ref[a] = ref[a] || {};
ref[ar[ar.length - 1]] = parse(k, configFile[k]);
}
}
}
if (v) cache[name] = v;
else cache[name] = null;
}
};
return v;
}
export { get };
+36
View File
@@ -244,6 +244,42 @@ button {
}
}
table.parameter-list {
text-align: left;
& th {
padding-right: 1em;
}
}
table.parameter-table {
border-spacing: 0;
text-align: left;
& th {
color: var(--color4);
border-bottom: 2px solid var(--color4);
margin: 0;
padding: 0.2em 0.5em;
}
& td {
margin: 0;
padding: 0.2em 0.5em;
}
}
.overview-dot {
& > svg {
margin-bottom: -0.125em;
margin-left: 0.2em;
margin-right: 0.2em;
& > circle {
stroke: var(--color5);
stroke-width: 0.08em;
}
}
}
.pie-chart {
margin: 20px;
text-align: center;
+41
View File
@@ -0,0 +1,41 @@
"use strict";
import m from "mithril";
import * as config from "./config";
import Filter from "../common/filter";
import * as store from "./store";
import * as components from "./components";
function init(args) {
return new Promise(resolve => {
resolve({
deviceId: args.id,
deviceFilter: new Filter(["=", "DeviceID.ID", args.id])
});
});
}
const component = {
view: vnode => {
document.title = `${vnode.attrs.deviceId} - Devices - GenieACS`;
let dev = store.fetch("devices", vnode.attrs.deviceFilter).value;
if (!dev.length) return "Loading";
const conf = config.get("ui.device");
const cmps = [];
for (let c of Object.values(conf))
cmps.push(
m(components.get(c["type"]), Object.assign({ device: dev[0] }, c))
);
return [
m("h1", vnode.attrs.deviceId),
m("span", dev[0]["InternetGatewayDevice.DeviceInfo.SerialNumber"]),
cmps
];
}
};
export { init, component };
+42 -73
View File
@@ -2,78 +2,50 @@
import m from "mithril";
import config from "./config";
import * as config from "./config";
import filterComponent from "./filter-component";
import * as filterParser from "../common/filter-parser";
function load(filter, limit) {
return new Promise((resolve, reject) => {
m
.request({
url: "/api/devices/",
data: { filter: filter, limit: limit }
})
.then(res => {
resolve(res);
})
.catch(err => {
err.message = err.message || "Unknown error";
reject(err);
});
});
}
function count(filter, limit) {
return new Promise((resolve, reject) => {
function extract(xhr) {
return +xhr.getResponseHeader("x-total-count");
}
m
.request({
method: "HEAD",
url:
"/api/devices/?" +
m.buildQueryString({ filter: filter, limit: limit }),
extract: extract
})
.then(res => {
resolve(res);
})
.catch(err => {
err.message = err.message || "Unknown error";
reject(err);
});
});
}
import Filter from "../common/filter";
import * as store from "./store";
import * as components from "./components";
function init(args) {
return new Promise((resolve, reject) => {
let filter;
if (args.filter) filter = filterParser.parse(args.filter);
Promise.all([count(args.filter), load(args.filter, config.pageSize)])
.then(res => {
resolve({ filter: filter, total: res[0], devices: res[1] });
})
.catch(reject);
return new Promise(resolve => {
let filter = new Filter(args.filter);
resolve({ filter: filter });
});
}
function renderTable(devices, parameters, total, showMoreCallback) {
let labels = [];
for (let param of parameters) labels.push(m("th", param.label));
labels.push(m("th"));
let rows = [];
for (let device of devices)
rows.push(
m(
"tr",
parameters.map(p => {
if (!device[p.parameter] || !device[p.parameter].value)
return m("td.na");
if (!device[p.parameter].value[0] === "") return m("td.blank");
return m("td", device[p.parameter].value[0]);
})
parameters
.map(p => {
const attrs = Object.assign({ device: device }, p);
const comp = m(components.get("parameter"), attrs);
return m("td", comp);
})
.concat(
m(
"td.table-row-links",
m(
"a",
{
href: `#!/devices/${encodeURIComponent(
device["DeviceID.ID"].value[0]
)}`
},
"Show"
)
)
)
)
);
@@ -84,7 +56,7 @@ function renderTable(devices, parameters, total, showMoreCallback) {
let tfoot = m(
"tfoot",
m("tr", m("td", { colspan: labels.length }, footerElements))
m("tr", m("td", { colspan: labels.length + 1 }, footerElements))
);
return m("table.table", [
@@ -99,18 +71,8 @@ const component = {
document.title = "Devices - GenieACS";
function showMore() {
const lastDevice = vnode.attrs.devices[vnode.attrs.devices.length - 1];
let f = filterParser.and(vnode.attrs.filter, [
">",
"DeviceID.ID",
lastDevice["DeviceID.ID"].value[0]
]);
load(filterParser.stringify(f), config.pageSize).then(res => {
vnode.attrs.devices = vnode.attrs.devices.concat(res);
if (res.length < config.pageSize)
vnode.attrs.total = vnode.attrs.devices.length;
});
vnode.state.showCount = (vnode.state.showCount || 10) + 10;
m.redraw();
}
function onFilterChanged(filter) {
@@ -119,16 +81,23 @@ const component = {
m.route.set("/devices", params);
}
let devs = store.fetch(
"devices",
vnode.attrs.filter,
vnode.state.showCount || 10
);
let count = store.count("devices", vnode.attrs.filter);
return [
m("h1", "Listing devices"),
m(filterComponent, {
filter: vnode.attrs.filter,
filter: vnode.attrs.filter ? vnode.attrs.filter.ast : null,
onChange: onFilterChanged
}),
renderTable(
vnode.attrs.devices,
config.index,
vnode.attrs.total,
devs.value,
Object.values(config.get("ui.index")),
count.value,
showMore
)
];
+11 -6
View File
@@ -2,18 +2,21 @@
import m from "mithril";
import config from "./config";
import * as config from "./config";
import * as filterParser from "../common/filter-parser";
const component = {
view: vnode => {
let a = m(
"datalist#filters",
config.filter.map(f => m("option", { value: `${f.parameter} = ` }))
Object.values(config.get("ui.filters")).map(f =>
m("option", { value: `${f.parameter} = ` })
)
);
if (!vnode.state.filterList) {
if (vnode.attrs.filter !== vnode.state.filter) {
vnode.state.filterList = [];
vnode.state.filter = vnode.attrs.filter;
if (vnode.attrs.filter)
if (vnode.attrs.filter[0] === "AND")
for (let i = 1; i < vnode.attrs.filter.length; ++i)
@@ -32,16 +35,18 @@ const component = {
vnode.state.filterList = vnode.state.filterList.filter(
f => f && f.trim()
);
let filter = vnode.state.filterList.map((f, idx) => {
let filterAst = vnode.state.filterList.map((f, idx) => {
try {
return filterParser.parse(f);
} catch (err) {
vnode.state.filterInvalid |= 1 << idx;
}
});
if (filterAst.length) filterAst = ["AND"].concat(filterAst);
else filterAst = null;
vnode.state.filterList.push("");
if (!vnode.state.filterInvalid)
vnode.attrs.onChange(["AND"].concat(filter));
if (!vnode.state.filterInvalid) vnode.attrs.onChange(filterAst);
}
return m(
+21 -70
View File
@@ -1,93 +1,44 @@
"use strict";
import m from "mithril";
import config from "./config";
import * as config from "./config";
import * as store from "./store";
import pieChartComponent from "./pie-chart-component";
import * as filterParser from "../common/filter-parser";
function count(filter, limit) {
return new Promise((resolve, reject) => {
function extract(xhr) {
return +xhr.getResponseHeader("x-total-count");
}
m
.request({
method: "HEAD",
url:
"/api/devices/?" +
m.buildQueryString({ filter: filter, limit: limit }),
extract: extract
})
.then(res => {
resolve(res);
})
.catch(err => {
err.message = err.message || "Unknown error";
reject(err);
});
});
}
const GROUPS = config.get("ui.overview.groups");
const CHARTS = {};
for (let group of Object.values(GROUPS))
for (let chartName of Object.values(group["charts"]))
CHARTS[chartName] = config.get("ui.overview.charts")[chartName];
function queryCharts(groups) {
const now = Date.now();
function evalNow(exp) {
if (exp[0] == "FUNC" && exp[1] === "NOW" && exp.length == 2) return now;
}
let calls = [];
groups = Object.assign({}, groups);
for (let [groupName, group] of Object.entries(groups)) {
groups[groupName] = group = Object.assign({}, group);
for (let [chartName, chart] of Object.entries(group.charts)) {
group.charts[chartName] = chart = Object.assign({}, chart);
for (let [sliceName, slice] of Object.entries(chart.slices)) {
chart.slices[sliceName] = slice = Object.assign({}, slice);
calls.push(
new Promise((resolve, reject) => {
let f = filterParser.map(filterParser.parse(slice.filter), evalNow);
count(filterParser.stringify(f))
.then(c => {
slice.count = c;
resolve();
})
.catch(reject);
})
);
}
function queryCharts(charts) {
charts = Object.assign({}, charts);
for (let [chartName, chart] of Object.entries(charts)) {
charts[chartName] = chart = Object.assign({}, chart);
for (let [sliceName, slice] of Object.entries(chart.slices)) {
chart.slices[sliceName] = slice = Object.assign({}, slice);
slice.count = store.count("devices", slice.filter);
}
}
return new Promise((resolve, reject) => {
Promise.all(calls)
.then(() => {
resolve(groups);
})
.catch(reject);
});
return charts;
}
const init = function() {
return new Promise((resolve, reject) => {
queryCharts(config.overview)
.then(charts => {
resolve({ charts: charts });
})
.catch(err => {
err.message = err.message || "Unknown error";
reject(err);
});
return new Promise(resolve => {
resolve({ charts: queryCharts(CHARTS) });
});
};
const component = {
view: vnode => {
view: () => {
document.title = "Overview - GenieACS";
let children = [];
for (let group of Object.values(vnode.attrs.charts)) {
for (let group of Object.values(GROUPS)) {
if (group.label) children.push(m("h1", group.label));
let groupChildren = [];
for (let chart of Object.values(group.charts)) {
for (let chartName of Object.values(group.charts)) {
const chart = CHARTS[chartName];
let chartChildren = [];
if (chart.label) chartChildren.push(m("h2", chart.label));
+5 -5
View File
@@ -5,7 +5,7 @@ import m from "mithril";
const drawChart = function(chartData) {
let slices = chartData.slices;
let total = Array.from(Object.values(chartData.slices)).reduce(
(a, s) => a + s.count,
(a, s) => a + (s.count.value || 0),
0
);
let legend = [];
@@ -17,7 +17,7 @@ const drawChart = function(chartData) {
let endX, endY;
for (let slice of Object.values(slices)) {
let percent = total > 0 ? slice.count / total : 0;
let percent = total > 0 ? (slice.count.value || 0) / total : 0;
legend.push(
m(".legend-line", [
m("span.color", {
@@ -28,10 +28,10 @@ const drawChart = function(chartData) {
"a",
{
href: `/#!/devices/?${m.buildQueryString({
filter: slice.filter
filter: slice.filter.toString()
})}`
},
slice.count
slice.count.value || 0
),
` (${(percent * 100).toFixed(2)}%)`
])
@@ -68,7 +68,7 @@ const drawChart = function(chartData) {
"a",
{
href: `/#!/devices/?${m.buildQueryString({
filter: slice.filter
filter: slice.filter.toString()
})}`
},
[
+307
View File
@@ -0,0 +1,307 @@
"use strict";
import m from "mithril";
import Filter from "../common/filter";
import * as config from "./config";
let fulfillTimestamp = 0;
let unpackedFilters = new WeakMap();
const queries = {
filter: new WeakMap(),
last: new WeakMap(),
limit: new WeakMap(),
fulfilled: new WeakMap(),
fulfilling: new WeakSet(),
mapper: new WeakMap(),
accessed: new WeakMap(),
value: new WeakMap()
};
const resources = {};
for (let r of ["devices"])
resources[r] = {
objects: new Map(),
count: new Map(),
fetch: new Map(),
combinedFilter: null
};
class QueryResponse {
get fulfilled() {
queries.accessed.set(this, Date.now());
return !!queries.fulfilled.get(this);
}
get value() {
queries.accessed.set(this, Date.now());
const mapper = queries.mapper.get(this);
const value = queries.value.get(this);
if (mapper) return mapper(queries.value.get(this));
else return value;
}
}
function unpackFilter(filter) {
let f = unpackedFilters.get(filter);
if (!f) {
f = f.evaluateExpressions(fulfillTimestamp);
unpackedFilters.set(filter, f);
}
return f;
}
function count(resourceType, filter) {
let queryResponse = resources[resourceType].count.get(filter.toString());
if (queryResponse) return queryResponse;
queryResponse = new QueryResponse();
resources[resourceType].count.set(filter.toString(), queryResponse);
queries.filter.set(queryResponse, filter);
return queryResponse;
}
function limitFilter(resourceType, filter, last) {
if (resourceType === "devices")
return filter.and(new Filter(["<=", "DeviceID.ID", last]));
else return filter.and(new Filter(["<=", "_id", last]));
}
function findMatches(resourceType, filter, limit) {
let value = [];
for (let [id, obj] of resources[resourceType].objects.entries())
if (filter.test(obj)) value.push(id);
value = value.sort();
if (limit) value = value.slice(0, limit);
return value;
}
function inferQuery(resourceType, queryResponse) {
const limit = queries.limit.get(queryResponse);
let filter = queries.filter.get(queryResponse);
filter = unpackFilter(filter);
let last = queries.last.get(queryResponse);
if (last || !limit) {
if (last) filter = limitFilter(resourceType, filter, last);
if (
resources[resourceType].combinedFilter &&
filter.subset(resources[resourceType].combinedFilter)
)
queries.fulfilled.set(queryResponse, fulfillTimestamp);
}
queries.value.set(queryResponse, findMatches(resourceType, filter, limit));
}
function fetch(resourceType, filter, limit = 0) {
const key = `${limit}:${filter.toString()}`;
let queryResponse = resources[resourceType].fetch.get(key);
if (queryResponse) return queryResponse;
queryResponse = new QueryResponse();
resources[resourceType].fetch.set(key, queryResponse);
queries.filter.set(queryResponse, filter);
queries.limit.set(queryResponse, limit);
queries.mapper.set(queryResponse, list =>
list.map(x => resources[resourceType].objects.get(x))
);
inferQuery(resourceType, queryResponse);
return queryResponse;
}
function fulfill(accessTimestamp, _fulfillTimestamp) {
let updated = false;
if (_fulfillTimestamp > fulfillTimestamp) {
unpackedFilters = new WeakMap();
for (let resource of Object.values(resources))
resource.combinedFilter = null;
fulfillTimestamp = _fulfillTimestamp;
}
const allPromises = [];
for (let [resourceType, resource] of Object.entries(resources))
for (let queryResponse of resource.count.values()) {
if (queries.fulfilling.has(queryResponse)) continue;
if (
queries.accessed.get(queryResponse) >= accessTimestamp &&
!(fulfillTimestamp <= queries.fulfilled.get(queryResponse))
) {
queries.fulfilling.add(queryResponse);
allPromises.push(
new Promise((resolve, reject) => {
updated = true;
let filter = queries.filter.get(queryResponse);
filter = unpackFilter(filter);
m
.request({
method: "HEAD",
url:
`/api/${resourceType}/?` +
m.buildQueryString({
filter: filter.toString()
}),
extract: xhr => +xhr.getResponseHeader("x-total-count"),
background: true
})
.then(c => {
queries.value.set(queryResponse, c);
queries.fulfilled.set(queryResponse, fulfillTimestamp);
queries.fulfilling.delete(queryResponse);
resolve();
})
.catch(err => reject(err));
})
);
}
}
let toFetchAll = {};
for (let [resourceType, resource] of Object.entries(resources))
for (let queryResponse of resource.fetch.values()) {
if (queries.fulfilling.has(queryResponse)) continue;
if (
queries.accessed.get(queryResponse) >= accessTimestamp &&
!(fulfillTimestamp <= queries.fulfilled.get(queryResponse))
) {
queries.fulfilling.add(queryResponse);
toFetchAll[resourceType] = toFetchAll[resourceType] || [];
toFetchAll[resourceType].push(queryResponse);
const limit = queries.limit.get(queryResponse);
if (limit)
allPromises.push(
new Promise((resolve, reject) => {
updated = true;
let filter = queries.filter.get(queryResponse);
filter = unpackFilter(filter);
m
.request({
method: "GET",
url:
`/api/${resourceType}/?` +
m.buildQueryString({
filter: filter.toString(),
limit: 1,
skip: limit - 1,
projection: "_id"
})
})
.then(res => {
if (res.length)
if (resourceType === "devices")
queries.last.set(
queryResponse,
res[0]["DeviceID.ID"].value[0]
);
else queries.last.set(queryResponse, res[0]["_id"]);
else queries.last.delete(queryResponse);
resolve();
})
.catch(reject);
})
);
}
}
return new Promise((resolve, reject) => {
Promise.all(allPromises)
.then(() => {
const allPromises2 = [];
for (let [resourceType, toFetch] of Object.entries(toFetchAll)) {
let combinedFilter = null;
toFetch = toFetch.filter(queryResponse => {
let filter = queries.filter.get(queryResponse);
filter = unpackFilter(filter);
let last = queries.last.get(queryResponse);
if (last) filter = limitFilter(resourceType, filter, last);
if (
resources[resourceType].combinedFilter &&
filter.subset(resources[resourceType].combinedFilter)
) {
queries.value.set(
queryResponse,
findMatches(resourceType, filter)
);
queries.fulfilled.set(queryResponse, fulfillTimestamp);
queries.fulfilling.delete(queryResponse);
return false;
}
combinedFilter = filter.or(combinedFilter);
return true;
});
if (!combinedFilter) return resolve(updated);
updated = true;
let combinedFilterDiff = combinedFilter;
if (resources[resourceType].combinedFilter)
combinedFilterDiff = combinedFilterDiff.and(
resources[resourceType].combinedFilter.not()
);
resources[resourceType].combinedFilter = combinedFilter.or(
resources[resourceType].combinedFilter
);
allPromises2.push(
new Promise((resolve2, reject2) => {
m
.request({
method: "GET",
url:
`/api/${resourceType}/?` +
m.buildQueryString({
filter: combinedFilterDiff.toString()
})
})
.then(res => {
for (let r of res)
if (resourceType === "devices")
resources.devices.objects.set(
r["DeviceID.ID"].value[0],
r
);
else resources[resourceType].objects.set(r["_id"], r);
for (let queryResponse of toFetch) {
let filter = queries.filter.get(queryResponse);
filter = unpackFilter(filter);
let last = queries.last.get(queryResponse);
if (last) filter = limitFilter(resourceType, filter, last);
queries.value.set(
queryResponse,
findMatches(resourceType, filter)
);
queries.fulfilled.set(queryResponse, fulfillTimestamp);
queries.fulfilling.delete(queryResponse);
}
resolve2();
})
.catch(reject2);
})
);
}
Promise.all(allPromises2)
.then(() => resolve(updated))
.catch(reject);
})
.catch(reject);
});
}
function getTimestamp() {
return fulfillTimestamp;
}
export { count, fetch, fulfill, unpackFilter, getTimestamp };