Add lint rule for unhandled promise rejections

This commit is contained in:
Zaid Abdulla committed 2020-09-08 17:33:48 -07:00
1 parent ad682d0b69
commit 0b60406b43
14 files changed
+177 -117

No files matched your search

+1
View File
@@ -21,6 +21,7 @@
"@typescript-eslint/no-explicit-any": 0, "@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-use-before-define": ["error", { "functions": false }], "@typescript-eslint/no-use-before-define": ["error", { "functions": false }],
"@typescript-eslint/explicit-function-return-type": ["error", { "allowExpressions": true }], "@typescript-eslint/explicit-function-return-type": ["error", { "allowExpressions": true }],
"@typescript-eslint/no-floating-promises": "error",
"require-atomic-updates": 0, "require-atomic-updates": 0,
"no-prototype-builtins": 0 "no-prototype-builtins": 0
} }
+1 -1
View File
@@ -43,7 +43,7 @@ function exitWorkerGracefully(): void {
} }
function exitWorkerUngracefully(): void { function exitWorkerUngracefully(): void {
extensions.killAll().then(() => { extensions.killAll().finally(() => {
process.exit(1); process.exit(1);
}); });
} }
+1 -1
View File
@@ -43,7 +43,7 @@ function exitWorkerGracefully(): void {
} }
function exitWorkerUngracefully(): void { function exitWorkerUngracefully(): void {
extensions.killAll().then(() => { extensions.killAll().finally(() => {
process.exit(1); process.exit(1);
}); });
} }
+4 -2
View File
@@ -45,7 +45,7 @@ function exitWorkerGracefully(): void {
} }
function exitWorkerUngracefully(): void { function exitWorkerUngracefully(): void {
extensions.killAll().then(() => { extensions.killAll().finally(() => {
process.exit(1); process.exit(1);
}); });
} }
@@ -99,7 +99,9 @@ if (!cluster.worker) {
const _listener = (req, res): void => { const _listener = (req, res): void => {
if (stopping) res.setHeader("Connection", "close"); if (stopping) res.setHeader("Connection", "close");
listener(req, res); listener(req, res).catch((err) => {
throw err;
});
}; };
const initPromise = Promise.all([db2.connect(), cache.connect()]) const initPromise = Promise.all([db2.connect(), cache.connect()])
+1
View File
@@ -58,6 +58,7 @@ export function run(args: string[]): Promise<{ fault: Fault; value: any }> {
}); });
} }
// eslint-disable-next-line @typescript-eslint/no-floating-promises
kill(processes[scriptName]); kill(processes[scriptName]);
delete processes[scriptName]; delete processes[scriptName];
} }
+3 -3
View File
@@ -299,7 +299,7 @@ export async function transferComplete(
sessionContext.operationsTouched[commandKey] = 1; sessionContext.operationsTouched[commandKey] = 1;
if (rpcReq.faultStruct && rpcReq.faultStruct.faultCode !== "0") { if (rpcReq.faultStruct && rpcReq.faultStruct.faultCode !== "0") {
await revertDownloadParameters(sessionContext, operation.args.instance); revertDownloadParameters(sessionContext, operation.args.instance);
const fault: Fault = { const fault: Fault = {
code: `cwmp.${rpcReq.faultStruct.faultCode}`, code: `cwmp.${rpcReq.faultStruct.faultCode}`,
@@ -398,10 +398,10 @@ export async function transferComplete(
}; };
} }
async function revertDownloadParameters( function revertDownloadParameters(
sessionContext: SessionContext, sessionContext: SessionContext,
instance instance
): Promise<void> { ): void {
const timestamp = sessionContext.timestamp + sessionContext.iteration + 1; const timestamp = sessionContext.timestamp + sessionContext.iteration + 1;
let p; let p;
+3 -2
View File
@@ -110,7 +110,7 @@ export async function postTasks(
try { try {
await connectionRequest(deviceId, device); await connectionRequest(deviceId, device);
} catch (err) { } catch (err) {
for (const t of statuses) db.deleteTask(new ObjectID(t._id)); await Promise.all(statuses.map((t) => db.deleteTask(new ObjectID(t._id))));
return { return {
connectionRequest: err.message, connectionRequest: err.message,
tasks: statuses, tasks: statuses,
@@ -138,9 +138,10 @@ export async function postTasks(
r.status = "fault"; r.status = "fault";
r["fault"] = res[i * 2 + 1][0]; r["fault"] = res[i * 2 + 1][0];
} }
db.deleteTask(new ObjectID(r._id));
} }
await Promise.all(statuses.map((t) => db.deleteTask(new ObjectID(t._id))));
return { connectionRequest: "OK", tasks: statuses }; return { connectionRequest: "OK", tasks: statuses };
} }
+74 -70
View File
@@ -128,78 +128,80 @@ export function query(
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getClient().then((client) => { getClient()
const collection = client .then((client) => {
.db() const collection = client
.collection(RESOURCE_COLLECTION[resource] || resource); .db()
const cursor = collection.find(q); .collection(RESOURCE_COLLECTION[resource] || resource);
if (options.projection) { const cursor = collection.find(q);
let projection = options.projection; if (options.projection) {
if (resource === "devices") { let projection = options.projection;
projection = mongodbFunctions.processDeviceProjection( if (resource === "devices") {
options.projection projection = mongodbFunctions.processDeviceProjection(
); options.projection
);
}
if (resource === "presets") projection.configurations = 1;
cursor.project(projection);
} }
if (resource === "presets") projection.configurations = 1; if (resource === "users") cursor.project({ password: 0, salt: 0 });
cursor.project(projection);
}
if (resource === "users") cursor.project({ password: 0, salt: 0 }); if (options.skip) cursor.skip(options.skip);
if (options.limit) cursor.limit(options.limit);
if (options.skip) cursor.skip(options.skip); if (options.sort) {
if (options.limit) cursor.limit(options.limit); let s = Object.entries(options.sort)
.sort((a, b) => Math.abs(b[1]) - Math.abs(a[1]))
.reduce(
(obj, [k, v]) =>
Object.assign(obj, { [k]: Math.min(Math.max(v, -1), 1) }),
{}
);
if (options.sort) { if (resource === "devices") s = mongodbFunctions.processDeviceSort(s);
let s = Object.entries(options.sort) cursor.sort(s);
.sort((a, b) => Math.abs(b[1]) - Math.abs(a[1])) }
.reduce(
(obj, [k, v]) =>
Object.assign(obj, { [k]: Math.min(Math.max(v, -1), 1) }),
{}
);
if (resource === "devices") s = mongodbFunctions.processDeviceSort(s); if (!callback) {
cursor.sort(s); cursor.toArray((err, docs) => {
} if (err) return reject(err);
if (!callback) {
cursor.toArray((err, docs) => {
if (err) return reject(err);
if (resource === "devices")
docs = docs.map((d) => mongodbFunctions.flattenDevice(d));
else if (resource === "faults")
docs = docs.map((d) => mongodbFunctions.flattenFault(d));
else if (resource === "tasks")
docs = docs.map((d) => mongodbFunctions.flattenTask(d));
else if (resource === "presets")
docs = docs.map((d) => mongodbFunctions.flattenPreset(d));
else if (resource === "files")
docs = docs.map((d) => mongodbFunctions.flattenFile(d));
return resolve(docs);
});
} else {
cursor.forEach(
(doc) => {
if (resource === "devices") if (resource === "devices")
doc = mongodbFunctions.flattenDevice(doc); docs = docs.map((d) => mongodbFunctions.flattenDevice(d));
else if (resource === "faults") else if (resource === "faults")
doc = mongodbFunctions.flattenFault(doc); docs = docs.map((d) => mongodbFunctions.flattenFault(d));
else if (resource === "tasks") else if (resource === "tasks")
doc = mongodbFunctions.flattenTask(doc); docs = docs.map((d) => mongodbFunctions.flattenTask(d));
else if (resource === "presets") else if (resource === "presets")
doc = mongodbFunctions.flattenPreset(doc); docs = docs.map((d) => mongodbFunctions.flattenPreset(d));
else if (resource === "files") else if (resource === "files")
doc = mongodbFunctions.flattenFile(doc); docs = docs.map((d) => mongodbFunctions.flattenFile(d));
callback(doc); return resolve(docs);
}, });
(err) => { } else {
if (err) reject(err); cursor.forEach(
else resolve(); (doc) => {
} if (resource === "devices")
); doc = mongodbFunctions.flattenDevice(doc);
} else if (resource === "faults")
}); doc = mongodbFunctions.flattenFault(doc);
else if (resource === "tasks")
doc = mongodbFunctions.flattenTask(doc);
else if (resource === "presets")
doc = mongodbFunctions.flattenPreset(doc);
else if (resource === "files")
doc = mongodbFunctions.flattenFile(doc);
callback(doc);
},
(err) => {
if (err) reject(err);
else resolve();
}
);
}
})
.catch(reject);
}); });
} }
@@ -221,15 +223,17 @@ export function count(resource: string, filter: Expression): Promise<number> {
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getClient().then((client) => { getClient()
const collection = client .then((client) => {
.db() const collection = client
.collection(RESOURCE_COLLECTION[resource] || resource); .db()
collection.find(q).count((err, c) => { .collection(RESOURCE_COLLECTION[resource] || resource);
if (err) reject(err); collection.find(q).count((err, c) => {
else resolve(c); if (err) reject(err);
}); else resolve(c);
}); });
})
.catch(reject);
}); });
} }
+1 -1
View File
@@ -71,7 +71,7 @@ let fulfillTimeout = null;
function fulfill(): void { function fulfill(): void {
clearTimeout(fulfillTimeout); clearTimeout(fulfillTimeout);
fulfillTimeout = setTimeout(() => { fulfillTimeout = setTimeout(() => {
store.fulfill(lastRenderTimestamp, pageVisitTimestamp).then((updated) => { store.fulfill(lastRenderTimestamp, pageVisitTimestamp, (updated) => {
if (updated) m.redraw(); if (updated) m.redraw();
}); });
}, 100); }, 100);
+20 -15
View File
@@ -27,23 +27,28 @@ const component: ClosureComponent = (vn): Component => {
let interval: ReturnType<typeof setInterval>; let interval: ReturnType<typeof setInterval>;
let host: string; let host: string;
const refresh = async (): Promise<void> => { const refresh = (): void => {
let status = ""; if (!host) {
if (host) { const dom = (vn as VnodeDOM).dom;
try { if (dom) dom.innerHTML = "";
const res = await store.ping(host); return;
if (res["avg"] != null) status = `${Math.trunc(res["avg"])} ms`;
else status = "Unreachable";
} catch (err) {
setTimeout(() => {
throw err;
}, 0);
}
} }
const dom = (vn as VnodeDOM).dom; let status = "";
store
if (dom) dom.innerHTML = `Pinging ${host}: ${status}`; .ping(host)
.then((res) => {
if (res["avg"] != null) status = `${Math.trunc(res["avg"])} ms`;
else status = "Unreachable";
})
.catch(() => {
status = "Error!";
clearInterval(interval);
})
.finally(() => {
const dom = (vn as VnodeDOM).dom;
if (dom) dom.innerHTML = `Pinging ${host}: ${status}`;
});
}; };
return { return {
+3
View File
@@ -79,6 +79,9 @@ const component: ClosureComponent = (): Component => {
.then(() => { .then(() => {
e.target.disabled = false; e.target.disabled = false;
store.fulfill(0, Date.now()); store.fulfill(0, Date.now());
})
.catch((err) => {
throw err;
}); });
}, },
}, },
+3
View File
@@ -441,6 +441,9 @@ const component: ClosureComponent = (): Component => {
) )
.then(() => { .then(() => {
store.fulfill(0, Date.now()); store.fulfill(0, Date.now());
})
.catch((err) => {
throw err;
}); });
}, },
}, },
+45 -13
View File
@@ -152,6 +152,8 @@ function checkConnection(): void {
} }
} }
}, },
}).catch((err) => {
notifications.push("error", err.message);
}); });
} }
@@ -169,11 +171,25 @@ export async function xhrRequest(
): any => { ): any => {
if (typeof extract === "function") return extract(xhr, _options); if (typeof extract === "function") return extract(xhr, _options);
// https://mithril.js.org/request.html#error-handling
if (xhr.status !== 304 && Math.floor(xhr.status / 100) !== 2) {
const err = new Error();
err["message"] =
xhr.status === 0
? "Server is unrachable"
: `Unexpected response status code ${xhr.status}`;
err["code"] = xhr.status;
err["response"] = xhr.responseText;
throw err;
}
let response: any; let response: any;
if (typeof deserialize === "function") { if (typeof deserialize === "function") {
response = deserialize(xhr.responseText); response = deserialize(xhr.responseText);
} else if ( } else if (
xhr.getResponseHeader("content-type").startsWith("application/json") (xhr.getResponseHeader("content-type") || "").startsWith(
"application/json"
)
) { ) {
try { try {
response = xhr.responseText ? JSON.parse(xhr.responseText) : null; response = xhr.responseText ? JSON.parse(xhr.responseText) : null;
@@ -184,15 +200,6 @@ export async function xhrRequest(
response = xhr.responseText; response = xhr.responseText;
} }
// https://mithril.js.org/request.html#error-handling
if (xhr.status !== 304 && Math.floor(xhr.status / 100) !== 2) {
const err = new Error();
err["message"] = xhr.responseText;
err["code"] = xhr.status;
err["response"] = response;
throw err;
}
return response; return response;
}; };
@@ -340,7 +347,7 @@ export function fetch(
return queryResponse; return queryResponse;
} }
export function fulfill( function _fulfill(
accessTimestamp: number, accessTimestamp: number,
_fulfillTimestamp: number _fulfillTimestamp: number
): Promise<boolean> { ): Promise<boolean> {
@@ -377,7 +384,16 @@ export function fulfill(
m.buildQueryString({ m.buildQueryString({
filter: memoizedStringify(filter), filter: memoizedStringify(filter),
}), }),
extract: (xhr) => +xhr.getResponseHeader("x-total-count"), extract: (xhr) => {
if (!xhr.status) {
throw new Error("Server is unrachable");
} else if (xhr.status !== 200) {
throw new Error(
`Unexpected response status code ${xhr.status}`
);
}
return +xhr.getResponseHeader("x-total-count");
},
background: true, background: true,
}) })
.then((c) => { .then((c) => {
@@ -560,6 +576,17 @@ export function fulfill(
}); });
} }
export function fulfill(
accessTimestamp: number,
_fulfillTimestamp: number,
callback?: (updated: boolean) => void
): void {
_fulfill(accessTimestamp, _fulfillTimestamp).then(callback, (err) => {
notifications.push("error", err.message);
if (callback) callback(false);
});
}
export function getTimestamp(): number { export function getTimestamp(): number {
return fulfillTimestamp; return fulfillTimestamp;
} }
@@ -647,7 +674,12 @@ export function resourceExists(resource: string, id: string): Promise<number> {
m.buildQueryString({ m.buildQueryString({
filter: memoizedStringify(filter), filter: memoizedStringify(filter),
}), }),
extract: (xhr) => +xhr.getResponseHeader("x-total-count"), extract: (xhr) => {
if (!xhr.status) throw new Error("Server is unrachable");
else if (xhr.status !== 200)
throw new Error(`Unexpected response status code ${xhr.status}`);
return +xhr.getResponseHeader("x-total-count");
},
background: true, background: true,
}); });
} }
+17 -9
View File
@@ -87,16 +87,24 @@ export const component: ClosureComponent = (vnode): Component => {
}) })
.then(() => { .then(() => {
setTimeout(() => { setTimeout(() => {
m.request({ url: "init" }).then((o) => { m.request({ url: "init" })
e.target.disabled = false; .then((o) => {
options = o; e.target.disabled = false;
notifications.push("success", "Initialization complete", { options = o;
"Open Sesame!": () => { notifications.push(
m.route.set("/login"); "success",
window.location.reload(); "Initialization complete",
}, {
"Open Sesame!": () => {
m.route.set("/login");
window.location.reload();
},
}
);
})
.catch((err) => {
notifications.push("error", err.message);
}); });
});
}, 3000); }, 3000);
if (opts["users"]) { if (opts["users"]) {
alert( alert(