wip: Construct API
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { IPC, PROTO } from "./MCBE-IPC/ipc";
|
||||
import { APICallerError } from "./Errors/APICallerError";
|
||||
import { APIErrorEnum } from "./Errors/APIErrorEnum";
|
||||
import { APIServerError } from "./Errors/APIServerError";
|
||||
import { APIVersionMismatchError } from "./Errors/APIVersionMismatchError";
|
||||
import { ReturnModelShell } from "./APIModels";
|
||||
|
||||
export class AddonAPI {
|
||||
#name;
|
||||
#version;
|
||||
|
||||
constructor(name, version) {
|
||||
this.#name = name;
|
||||
this.#version = version;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.#name;
|
||||
}
|
||||
|
||||
get version() {
|
||||
return this.#version;
|
||||
}
|
||||
|
||||
get endpointBase() {
|
||||
return this.#name + ':';
|
||||
}
|
||||
|
||||
setupController(apiController) {
|
||||
for (const [endpoint, features] of Object.entries(apiController.endpoints)) {
|
||||
const {callback, parameterModel, returnModel} = features;
|
||||
const boundCallback = callback.bind(apiController);
|
||||
this.#setupEndpoint(endpoint, boundCallback, parameterModel, returnModel);
|
||||
}
|
||||
}
|
||||
|
||||
#setupEndpoint(endpoint, callback, parameterModel, returnDataModel) {
|
||||
const returnPacketModel = this.#resolveReturnModel(returnDataModel);
|
||||
const endpointPath = this.endpointBase + endpoint;
|
||||
IPC.handle(endpointPath, parameterModel, returnPacketModel, (callPacket) => {
|
||||
const apiVersion = callPacket.apiVersion;
|
||||
const parameters = Object.values(callPacket.parameterMap);
|
||||
return this.#handleCallback(apiVersion, callback, parameters);
|
||||
});
|
||||
}
|
||||
|
||||
#handleCallback(apiVersion, callback, parameters) {
|
||||
try {
|
||||
this.#assertVersionsMatch(apiVersion);
|
||||
const returnValue = callback(...parameters);
|
||||
return this.#bundleReturnPacket({ code: APIErrorEnum.Success }, returnValue);
|
||||
} catch(error) {
|
||||
if (error instanceof APICallerError)
|
||||
const errorPacket = this.#resolveErrorPacket(error);
|
||||
return this.#bundleReturnPacket(errorPacket);
|
||||
console.error(error);
|
||||
const apiError = new APIServerError(error);
|
||||
const errorPacket = this.#resolveErrorPacket(apiError);
|
||||
return this.#bundleReturnPacket(errorPacket);
|
||||
}
|
||||
}
|
||||
|
||||
#assertVersionsMatch(versionToCheck) {
|
||||
if (versionToCheck !== this.version) {
|
||||
const apiVersionMismatchError = new APIVersionMismatchError(this.version, versionToCheck);
|
||||
throw new APICallerError(apiVersionMismatchError);
|
||||
}
|
||||
}
|
||||
|
||||
#resolveReturnModel(returnDataModel) {
|
||||
let returnModel = { ...ReturnModelShell };
|
||||
returnModel.data = returnDataModel;
|
||||
returnModel = PROTO.Object(returnModel);
|
||||
return returnModel;
|
||||
}
|
||||
|
||||
#bundleReturnPacket(errorPacket, returnValue = void 0) {
|
||||
return {
|
||||
apiVersion: this.version,
|
||||
data: returnValue,
|
||||
error: errorPacket
|
||||
};
|
||||
}
|
||||
|
||||
#resolveErrorPacket(error) {
|
||||
return {
|
||||
code: error.errorCode,
|
||||
name: error.thrownError.name,
|
||||
message: error.thrownError.message
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export class APIController {
|
||||
#endpoints;
|
||||
|
||||
constructor(endpoints) {
|
||||
if (this.constructor === APIController)
|
||||
throw new Error("Cannot instantiate abstract class 'APIController'");
|
||||
this.#endpoints = endpoints;
|
||||
}
|
||||
|
||||
get endpoints() {
|
||||
return this.#endpoints;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { PROTO } from "./MCBE-IPC/ipc";
|
||||
|
||||
export const VoidModel = PROTO.Void;
|
||||
|
||||
export const ErrorModel = PROTO.Optional(PROTO.Object({
|
||||
code: PROTO.Int8,
|
||||
name: PROTO.Optional(PROTO.String),
|
||||
message: PROTO.Optional(PROTO.String)
|
||||
}));
|
||||
|
||||
export const ReturnModelShell = {
|
||||
apiVersion: PROTO.String,
|
||||
data: void 0,
|
||||
error: ErrorModel
|
||||
};
|
||||
|
||||
export const CallModelShell = {
|
||||
apiVersion: PROTO.String,
|
||||
parameterMap: void 0
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { AddonAPI } from "./API";
|
||||
import { APIController } from "./APIController";
|
||||
import { VoidModel } from "./APIModels";
|
||||
import { APICallerError } from "./Errors/APICallerError";
|
||||
import { PROTO } from "./MCBE-IPC/ipc";
|
||||
|
||||
export { AddonAPI, APIController, VoidModel, APICallerError, PROTO };
|
||||
@@ -0,0 +1,11 @@
|
||||
import { APIErrorEnum } from "./APIErrorEnum";
|
||||
|
||||
export class APICallerError extends Error {
|
||||
constructor(error) {
|
||||
super(error.message);
|
||||
this.errorName = error.name;
|
||||
this.errorMessage = error.message;
|
||||
this.errorCode = APIErrorEnum.Caller;
|
||||
this.name = "APICallerError";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export const APIErrorEnum = Object.freeze({
|
||||
Unknown: 0,
|
||||
Success: 1,
|
||||
Caller: 2,
|
||||
Server: 3
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
import { APIErrorEnum } from "./APIErrorEnum";
|
||||
|
||||
export class APIServerError extends Error {
|
||||
constructor(error) {
|
||||
super(error.message);
|
||||
this.thrownError = error;
|
||||
this.errorCode = APIErrorEnum.Server;
|
||||
this.name = "APIServerError";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export class APIVersionMismatchError extends Error {
|
||||
constructor(serverApiVersion, callerApiVersion) {
|
||||
super(`API version numbers do not match (${callerApiVersion} != ${serverApiVersion}). Please use API version ${serverApiVersion}.`);
|
||||
this.name = 'APIVersionMismatchError';
|
||||
}
|
||||
}
|
||||
Vendored
Reference in New Issue
Block a user