@neurosity/sdk
Version:
Neurosity SDK
192 lines (191 loc) • 7.75 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDeviceStore = void 0;
const app_1 = __importDefault(require("firebase/app"));
const SERVER_TIMESTAMP = app_1.default.database.ServerValue.TIMESTAMP;
/**
* @hidden
*/
const createDeviceStore = (app, deviceId, subscriptionManager) => {
const deviceRef = app.database().ref(`devices/${deviceId}`);
const clientId = deviceRef.child("subscriptions").push().key;
const clientRef = deviceRef.child(`clients/${clientId}`);
let listenersToRemove = [];
const set = (namespace, payload) => {
return deviceRef.child(namespace).set(payload);
};
const push = (namespace, payload) => {
return deviceRef.child(namespace).push(payload);
};
const update = (namespace, payload) => {
return deviceRef.child(namespace).update(payload);
};
const on = (eventType = "value", namespace, callback) => {
const listener = deviceRef
.child(namespace)
.on(eventType, (snapshot) => {
callback(snapshot.val(), snapshot);
});
listenersToRemove.push(() => {
deviceRef.child(namespace).off(eventType, listener);
});
return listener;
};
const off = (namespace, eventType, listener) => {
if (listener) {
deviceRef.child(namespace).off(eventType, listener);
}
else {
deviceRef.child(namespace).off(eventType);
}
};
const once = (namespace, eventType = "value") => __awaiter(void 0, void 0, void 0, function* () {
const snapshot = yield deviceRef.child(namespace).once(eventType);
return snapshot.val();
});
const remove = (namespace) => {
deviceRef.child(namespace).remove();
};
const bindListener = (eventType, namespace, callback, overrideResponse) => {
on(eventType, namespace, (data) => {
if (data !== null) {
off(namespace, eventType);
const response = overrideResponse ? overrideResponse : data;
callback(response);
}
});
};
const lastOfChildValue = (namespace, key, value) => __awaiter(void 0, void 0, void 0, function* () {
const snapshot = yield deviceRef
.child(namespace)
.orderByChild(key)
.equalTo(value)
.limitToLast(1)
.once("value");
const results = snapshot.val();
const [match] = Object.values(results || {});
return match || null;
});
// Add client connections and subscriptions to db and remove them when offline
const connectedListener = app
.database()
.ref(".info/connected")
.on("value", (snapshot) => {
if (!snapshot.val()) {
return;
}
clientRef
.onDisconnect()
.remove()
.then(() => {
clientRef.set(SERVER_TIMESTAMP);
// NOTION-115: Re-subscribe when internet connection is lost and regained
update("subscriptions", subscriptionManager.get()).then(() => {
subscriptionManager.toList().forEach((subscription) => {
const childPath = `subscriptions/${subscription.id}`;
deviceRef.child(childPath).onDisconnect().remove();
});
});
});
});
listenersToRemove.push(() => {
app
.database()
.ref(".info/connected")
.off("value", connectedListener);
});
return {
set,
once,
update,
lastOfChildValue,
onNamespace: (namespace, callback) => {
return on("value", namespace, (data) => {
callback(data);
});
},
offNamespace: (namespace, listener) => {
off(namespace, "value", listener);
},
dispatchAction: (action) => __awaiter(void 0, void 0, void 0, function* () {
const snapshot = yield push("actions", action);
const actionId = snapshot.key;
const actionPath = `actions/${actionId}`;
snapshot.onDisconnect().remove();
if (action.responseRequired) {
const responseTimeout = action.responseTimeout || 600000; // defaults to 10 minutes
const timeout = new Promise((_, reject) => {
const id = setTimeout(() => {
clearTimeout(id);
snapshot.remove();
reject(`Action response timed out in ${responseTimeout}ms.`);
}, responseTimeout);
});
const response = new Promise((resolve) => {
bindListener("value", `${actionPath}/response`, resolve);
});
return Promise.race([response, timeout]);
}
return actionId;
}),
nextMetric: (metricName, metricValue) => __awaiter(void 0, void 0, void 0, function* () {
set(`metrics/${metricName}`, metricValue);
}),
onMetric: (subscription, callback) => {
const { atomic, metric, labels } = subscription;
const child = atomic
? `metrics/${metric}`
: `metrics/${metric}/${labels[0]}`;
return on("value", child, (data) => {
if (data !== null) {
callback(data);
}
});
},
subscribeToMetric: (subscription) => {
const id = deviceRef.child("subscriptions").push().key;
const childPath = `subscriptions/${id}`;
const subscriptionCreated = Object.assign({ id,
clientId }, subscription);
set(childPath, subscriptionCreated);
deviceRef.child(childPath).onDisconnect().remove();
return subscriptionCreated;
},
unsubscribeFromMetric: (subscription) => {
remove(`subscriptions/${subscription.id}`);
},
removeMetricListener(subscription, listener) {
const { atomic, metric, labels } = subscription;
const child = atomic
? `metrics/${metric}`
: `metrics/${metric}/${labels[0]}`;
off(child, "value", listener);
},
disconnect() {
clientRef.remove();
listenersToRemove.forEach((removeListener) => {
removeListener();
});
subscriptionManager
.toList()
.filter((subscription) => subscription.clientId === clientId)
.forEach((subscription) => {
const childPath = `subscriptions/${subscription.id}`;
deviceRef.child(childPath).remove();
});
}
};
};
exports.createDeviceStore = createDeviceStore;