better utilize caching for rpc
This commit is contained in:
parent
adee96f697
commit
6ab0eb8351
5 changed files with 52 additions and 28 deletions
|
@ -1,25 +1,26 @@
|
||||||
import gateway from "./gateway";
|
import gateway from "./gateway";
|
||||||
import { apiRoute, getItem } from "./storage";
|
import { apiRoute, getItem } from "./storage";
|
||||||
// TODO: circular dependency
|
|
||||||
import { overlayStore, OverlayType } from "./stores";
|
const method = (methodId, requiresAuthentication) => ({methodId, requiresAuthentication});
|
||||||
|
const withCacheable = (method) => ({ ...method, cacheable: true })
|
||||||
|
|
||||||
export const methods = {
|
export const methods = {
|
||||||
// methodName: [ methodId, requiresAuthentication ]
|
// methodName: [ methodId, requiresAuthentication ]
|
||||||
createUser: [ 0, false ],
|
createUser: method(0, false),
|
||||||
loginUser: [ 1, false ],
|
loginUser: method(1, false),
|
||||||
getUserSelf: [ 2, true ],
|
getUserSelf: withCacheable(method(2, true)),
|
||||||
promoteUserSelf: [ 3, true ],
|
promoteUserSelf: method(3, true),
|
||||||
createChannel: [ 4, true ],
|
createChannel: method(4, true),
|
||||||
updateChannelName: [ 5, true ],
|
updateChannelName: method(5, true),
|
||||||
deleteChannel: [ 6, true ],
|
deleteChannel: method(6, true),
|
||||||
getChannel: [ 7, true ],
|
getChannel: withCacheable(method(7, true)),
|
||||||
getChannels: [ 8, true ],
|
getChannels: withCacheable(method(8, true)),
|
||||||
createChannelMessage: [ 9, true ],
|
createChannelMessage: method(9, true),
|
||||||
getChannelMessages: [ 10, true ],
|
getChannelMessages: withCacheable(method(10, true)),
|
||||||
putChannelTyping: [ 11, true ],
|
putChannelTyping: method(11, true),
|
||||||
deleteMessage: [ 12, true ],
|
deleteMessage: method(12, true),
|
||||||
updateMessageContent: [ 13, true ],
|
updateMessageContent: method(13, true),
|
||||||
getMessage: [ 14, true ]
|
getMessage: withCacheable(method(14, true))
|
||||||
};
|
};
|
||||||
|
|
||||||
export function compatibleFetch(endpoint, options) {
|
export function compatibleFetch(endpoint, options) {
|
||||||
|
@ -93,9 +94,9 @@ export default function doRequest(method, endpoint, auth=true, body=null) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remoteCall([methodId, requiresAuthentication], ...args) {
|
export async function remoteCall({methodId, requiresAuthentication, cacheable}, ...args) {
|
||||||
const calls = [[methodId, ...args]];
|
const calls = [[methodId, ...args]];
|
||||||
if (requiresAuthentication && gateway.authenticated) {
|
if (requiresAuthentication && gateway.authenticated && !cacheable) {
|
||||||
const replies = await gateway.sendRPCRequest(calls);
|
const replies = await gateway.sendRPCRequest(calls);
|
||||||
const ok = Array.isArray(replies) && replies[0] && !replies[0].code;
|
const ok = Array.isArray(replies) && replies[0] && !replies[0].code;
|
||||||
return {
|
return {
|
||||||
|
@ -104,7 +105,12 @@ export async function remoteCall([methodId, requiresAuthentication], ...args) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await doRequest("POST", apiRoute("rpc"), requiresAuthentication, calls);
|
let response;
|
||||||
|
if (cacheable) {
|
||||||
|
response = await doRequest("GET", apiRoute(`rpc?calls=${encodeURI(JSON.stringify(calls))}`), requiresAuthentication);
|
||||||
|
} else {
|
||||||
|
response = await doRequest("POST", apiRoute("rpc"), requiresAuthentication, calls);
|
||||||
|
}
|
||||||
response.ok = response.ok && Array.isArray(response.json) && response.json[0] && !response.json[0].code;
|
response.ok = response.ok && Array.isArray(response.json) && response.json[0] && !response.json[0].code;
|
||||||
response.json = response.ok ? response.json[0] : null;
|
response.json = response.ok ? response.json[0] : null;
|
||||||
return response;
|
return response;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export const errors = {
|
export const errors = {
|
||||||
INVALID_RPC_CALL: { code: 6000, message: "Invalid RPC call. Please see 'detail' property." },
|
BAD_REQUEST: { code: 6000, message: "Bad request, see 'detail' field for more information" },
|
||||||
INVALID_DATA: { code: 6001, message: "Invalid data" },
|
RPC_VALIDATION_ERROR: { code: 6001, message: "RPC validation error, see 'errors' field for more information" },
|
||||||
BAD_LOGIN_CREDENTIALS: { code: 6002, message: "Bad login credentials provided" },
|
BAD_LOGIN_CREDENTIALS: { code: 6002, message: "Bad login credentials provided" },
|
||||||
BAD_AUTH: { code: 6003, message: "Bad authentication" },
|
BAD_AUTH: { code: 6003, message: "Bad authentication" },
|
||||||
NOT_FOUND: { code: 6004, message: "Not found" },
|
NOT_FOUND: { code: 6004, message: "Not found" },
|
||||||
|
|
|
@ -13,4 +13,22 @@ router.post(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/",
|
||||||
|
authenticateRoute(false),
|
||||||
|
async (req, res) => {
|
||||||
|
const call = req.query.calls;
|
||||||
|
if (typeof call !== "string" || !call.length || call.length > 4500) {
|
||||||
|
return res.json({ ...errors.BAD_REQUEST, detail: "Bad 'call': expected string between 1 and 4500 characters" });
|
||||||
|
}
|
||||||
|
let callJson;
|
||||||
|
try {
|
||||||
|
callJson = JSON.parse(call);
|
||||||
|
} catch(O_o) {
|
||||||
|
return res.json({ ...errors.BAD_REQUEST, detail: "Bad 'call': failed to parse as JSON" });
|
||||||
|
}
|
||||||
|
res.json(await processMethodBatch(req.authenticated ? req.user : null, callJson));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
@ -17,7 +17,7 @@ methodButWarningDoesNotAuthenticate(
|
||||||
const existingUser = await query("SELECT * FROM users WHERE username = $1", [username]);
|
const existingUser = await query("SELECT * FROM users WHERE username = $1", [username]);
|
||||||
if (existingUser && existingUser.rowCount > 0) {
|
if (existingUser && existingUser.rowCount > 0) {
|
||||||
return {
|
return {
|
||||||
...errors.INVALID_DATA,
|
...errors.RPC_VALIDATION_ERROR,
|
||||||
errors: [ { index: 0, msg: "Username already exists" } ]
|
errors: [ { index: 0, msg: "Username already exists" } ]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,13 +56,13 @@ export const methodButWarningDoesNotAuthenticate = (name: string, args: RPCArgum
|
||||||
export const userInvokeMethod = async (user: User | null, methodId: number, args: any[]) => {
|
export const userInvokeMethod = async (user: User | null, methodId: number, args: any[]) => {
|
||||||
const methodData = methods.get(methodId);
|
const methodData = methods.get(methodId);
|
||||||
if (!methodData) return {
|
if (!methodData) return {
|
||||||
...errors.INVALID_RPC_CALL,
|
...errors.BAD_REQUEST,
|
||||||
detail: "The method was not found."
|
detail: "The method was not found."
|
||||||
};
|
};
|
||||||
|
|
||||||
const argSchema = methodData.args;
|
const argSchema = methodData.args;
|
||||||
if (argSchema.length !== args.length) return {
|
if (argSchema.length !== args.length) return {
|
||||||
...errors.INVALID_RPC_CALL,
|
...errors.BAD_REQUEST,
|
||||||
detail: "Invalid number of arguments provided to method."
|
detail: "Invalid number of arguments provided to method."
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ export const userInvokeMethod = async (user: User | null, methodId: number, args
|
||||||
|
|
||||||
if (validationErrors.length !== 0) {
|
if (validationErrors.length !== 0) {
|
||||||
return {
|
return {
|
||||||
...errors.INVALID_DATA,
|
...errors.RPC_VALIDATION_ERROR,
|
||||||
errors: validationErrors
|
errors: validationErrors
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ export const userInvokeMethod = async (user: User | null, methodId: number, args
|
||||||
export const processMethodBatch = async (user: User | null, calls: any) => {
|
export const processMethodBatch = async (user: User | null, calls: any) => {
|
||||||
if (!Array.isArray(calls) || !calls.length || calls.length > 5) {
|
if (!Array.isArray(calls) || !calls.length || calls.length > 5) {
|
||||||
return {
|
return {
|
||||||
...errors.INVALID_RPC_CALL,
|
...errors.BAD_REQUEST,
|
||||||
detail: "Expected RPC batch: an array of arrays with at least a single element and at most 5 elements, where each inner array represents a method call."
|
detail: "Expected RPC batch: an array of arrays with at least a single element and at most 5 elements, where each inner array represents a method call."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ export const processMethodBatch = async (user: User | null, calls: any) => {
|
||||||
calls.forEach((call, index) => {
|
calls.forEach((call, index) => {
|
||||||
if (!Array.isArray(call) || !call.length || call.length > 8) {
|
if (!Array.isArray(call) || !call.length || call.length > 8) {
|
||||||
responses[index] = {
|
responses[index] = {
|
||||||
...errors.INVALID_RPC_CALL,
|
...errors.BAD_REQUEST,
|
||||||
detail: "Invalid method call. Expected inner array with at least one element and at most 8 elements."
|
detail: "Invalid method call. Expected inner array with at least one element and at most 8 elements."
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in a new issue