diff --git a/packages/controller/controller.js b/packages/controller/controller.js index 86f7cedd..dc9c1365 100755 --- a/packages/controller/controller.js +++ b/packages/controller/controller.js @@ -28,6 +28,7 @@ const jwt = require("jsonwebtoken"); // homebrew modules const libErrors = require("@clusterio/lib/errors"); const libFileOps = require("@clusterio/lib/file_ops"); +const libLink = require("@clusterio/lib/link"); const libPluginLoader = require("@clusterio/lib/plugin_loader"); const libPrometheus = require("@clusterio/lib/prometheus"); const libConfig = require("@clusterio/lib/config"); @@ -290,6 +291,7 @@ async function initialize() { logger.info("Loading Plugin info"); parameters.pluginInfos = await libPluginLoader.loadPluginInfos(pluginList); + libLink.registerPluginMessages(parameters.pluginInfos); libConfig.registerPluginConfigGroups(parameters.pluginInfos); libConfig.finalizeConfigs(); diff --git a/packages/controller/src/BaseConnection.js b/packages/controller/src/BaseConnection.js index fa0e46ba..40fddbba 100644 --- a/packages/controller/src/BaseConnection.js +++ b/packages/controller/src/BaseConnection.js @@ -19,13 +19,13 @@ class BaseConnection extends libLink.Link { super(connector); this.router = new ControllerRouter(controller); this._controller = controller; - for (let [Request, handler] of controller._registeredRequests) { this.register(Request, handler); } + for (let [Request, handler] of controller._registeredRequests) { this.handle(Request, handler); } for (let [Request, handler] of controller._fallbackedRequests) { this.fallbackRequest(Request, handler); } - for (let [Event, handler] of controller._registeredEvents) { this.register(Event, handler); } + for (let [Event, handler] of controller._registeredEvents) { this.handle(Event, handler); } for (let [Event, handler] of controller._snoopedEvents) { this.snoopEvent(Event, handler); } - this.register(libData.ModPackGetRequest, this.handleModPackGetRequest.bind(this)); - this.register(libData.ModPackGetDefaultRequest, this.handleModPackGetDefaultRequest.bind(this)); + this.handle(libData.ModPackGetRequest, this.handleModPackGetRequest.bind(this)); + this.handle(libData.ModPackGetDefaultRequest, this.handleModPackGetDefaultRequest.bind(this)); } async disconnect(code, reason) { diff --git a/packages/controller/src/ControlConnection.js b/packages/controller/src/ControlConnection.js index 776ae636..d65350c5 100644 --- a/packages/controller/src/ControlConnection.js +++ b/packages/controller/src/ControlConnection.js @@ -104,69 +104,69 @@ class ControlConnection extends BaseConnection { }); } - this.register(libData.ControllerConfigGetRequest, this.handleControllerConfigGetRequest.bind(this)); - this.register(libData.ControllerConfigSetFieldRequest, this.handleControllerConfigSetFieldRequest.bind(this)); - this.register(libData.ControllerConfigSetPropRequest, this.handleControllerConfigSetPropRequest.bind(this)); - this.register(libData.HostListRequest, this.handleHostListRequest.bind(this)); - this.register(libData.HostSetSubscriptionsRequest, this.handleHostSetSubscriptionsRequest.bind(this)); - this.register(libData.HostGenerateTokenRequest, this.handleHostGenerateTokenRequest.bind(this)); - this.register(libData.HostConfigCreateRequest, this.handleHostConfigCreateRequest.bind(this)); - this.register(libData.InstanceDetailsGetRequest, this.handleInstanceDetailsGetRequest.bind(this)); - this.register(libData.InstanceDetailsListRequest, this.handleInstanceDetailsListRequest.bind(this)); - this.register( + this.handle(libData.ControllerConfigGetRequest, this.handleControllerConfigGetRequest.bind(this)); + this.handle(libData.ControllerConfigSetFieldRequest, this.handleControllerConfigSetFieldRequest.bind(this)); + this.handle(libData.ControllerConfigSetPropRequest, this.handleControllerConfigSetPropRequest.bind(this)); + this.handle(libData.HostListRequest, this.handleHostListRequest.bind(this)); + this.handle(libData.HostSetSubscriptionsRequest, this.handleHostSetSubscriptionsRequest.bind(this)); + this.handle(libData.HostGenerateTokenRequest, this.handleHostGenerateTokenRequest.bind(this)); + this.handle(libData.HostConfigCreateRequest, this.handleHostConfigCreateRequest.bind(this)); + this.handle(libData.InstanceDetailsGetRequest, this.handleInstanceDetailsGetRequest.bind(this)); + this.handle(libData.InstanceDetailsListRequest, this.handleInstanceDetailsListRequest.bind(this)); + this.handle( libData.InstanceDetailsSetSubscriptionsRequest, this.handleInstanceDetailsSetSubscriptionsRequest.bind(this) ); - this.register(libData.InstanceCreateRequest, this.handleInstanceCreateRequest.bind(this)); - this.register(libData.InstanceDeleteRequest, this.handleInstanceDeleteRequest.bind(this)); - this.register(libData.InstanceConfigGetRequest, this.handleInstanceConfigGetRequest.bind(this)); - this.register(libData.InstanceConfigSetFieldRequest, this.handleInstanceConfigSetFieldRequest.bind(this)); - this.register(libData.InstanceConfigSetPropRequest, this.handleInstanceConfigSetPropRequest.bind(this)); - this.register(libData.InstanceAssignRequest, this.handleInstanceAssignRequest.bind(this)); - this.register( + this.handle(libData.InstanceCreateRequest, this.handleInstanceCreateRequest.bind(this)); + this.handle(libData.InstanceDeleteRequest, this.handleInstanceDeleteRequest.bind(this)); + this.handle(libData.InstanceConfigGetRequest, this.handleInstanceConfigGetRequest.bind(this)); + this.handle(libData.InstanceConfigSetFieldRequest, this.handleInstanceConfigSetFieldRequest.bind(this)); + this.handle(libData.InstanceConfigSetPropRequest, this.handleInstanceConfigSetPropRequest.bind(this)); + this.handle(libData.InstanceAssignRequest, this.handleInstanceAssignRequest.bind(this)); + this.handle( libData.InstanceSetSaveListSubscriptionsRequest, this.handleInstanceSetSaveListSubscriptionsRequest.bind(this) ); - this.register( + this.handle( libData.InstanceRenameSaveRequest, this._controller.sendToHostByInstanceId.bind(this._controller) ); - this.register(libData.InstanceCopySaveRequest, this._controller.sendToHostByInstanceId.bind(this._controller)); - this.register( + this.handle(libData.InstanceCopySaveRequest, this._controller.sendToHostByInstanceId.bind(this._controller)); + this.handle( libData.InstanceDeleteSaveRequest, this._controller.sendToHostByInstanceId.bind(this._controller) ); - this.register(libData.InstanceDownloadSaveRequest, this.handleInstanceDownloadSaveRequest.bind(this)); - this.register(libData.InstanceTransferSaveRequest, this.handleInstanceTransferSaveRequest.bind(this)); - this.register(libData.ModPackListRequest, this.handleModPackListRequest.bind(this)); - this.register(libData.ModPackSetSubscriptionsRequest, this.handleModPackSetSubscriptionsRequest.bind(this)); - this.register(libData.ModPackCreateRequest, this.handleModPackCreateRequest.bind(this)); - this.register(libData.ModPackUpdateRequest, this.handleModPackUpdateRequest.bind(this)); - this.register(libData.ModPackDeleteRequest, this.handleModPackDeleteRequest.bind(this)); - this.register(libData.ModGetRequest, this.handleModGetRequest.bind(this)); - this.register(libData.ModListRequest, this.handleModListRequest.bind(this)); - this.register(libData.ModSearchRequest, this.handleModSearchRequest.bind(this)); - this.register(libData.ModSetSubscriptionsRequest, this.handleModSetSubscriptionsRequest.bind(this)); - this.register(libData.ModDownloadRequest, this.handleModDownloadRequest.bind(this)); - this.register(libData.ModDeleteRequest, this.handleModDeleteRequest.bind(this)); - this.register(libData.LogSetSubscriptionsRequest, this.handleLogSetSubscriptionsRequest.bind(this)); - this.register(libData.LogQueryRequest, this.handleLogQueryRequest.bind(this)); - this.register(libData.PermissionListRequest, this.handlePermissionListRequest.bind(this)); - this.register(libData.RoleListRequest, this.handleRoleListRequest.bind(this)); - this.register(libData.RoleCreateRequest, this.handleRoleCreateRequest.bind(this)); - this.register(libData.RoleUpdateRequest, this.handleRoleUpdateRequest.bind(this)); - this.register( + this.handle(libData.InstanceDownloadSaveRequest, this.handleInstanceDownloadSaveRequest.bind(this)); + this.handle(libData.InstanceTransferSaveRequest, this.handleInstanceTransferSaveRequest.bind(this)); + this.handle(libData.ModPackListRequest, this.handleModPackListRequest.bind(this)); + this.handle(libData.ModPackSetSubscriptionsRequest, this.handleModPackSetSubscriptionsRequest.bind(this)); + this.handle(libData.ModPackCreateRequest, this.handleModPackCreateRequest.bind(this)); + this.handle(libData.ModPackUpdateRequest, this.handleModPackUpdateRequest.bind(this)); + this.handle(libData.ModPackDeleteRequest, this.handleModPackDeleteRequest.bind(this)); + this.handle(libData.ModGetRequest, this.handleModGetRequest.bind(this)); + this.handle(libData.ModListRequest, this.handleModListRequest.bind(this)); + this.handle(libData.ModSearchRequest, this.handleModSearchRequest.bind(this)); + this.handle(libData.ModSetSubscriptionsRequest, this.handleModSetSubscriptionsRequest.bind(this)); + this.handle(libData.ModDownloadRequest, this.handleModDownloadRequest.bind(this)); + this.handle(libData.ModDeleteRequest, this.handleModDeleteRequest.bind(this)); + this.handle(libData.LogSetSubscriptionsRequest, this.handleLogSetSubscriptionsRequest.bind(this)); + this.handle(libData.LogQueryRequest, this.handleLogQueryRequest.bind(this)); + this.handle(libData.PermissionListRequest, this.handlePermissionListRequest.bind(this)); + this.handle(libData.RoleListRequest, this.handleRoleListRequest.bind(this)); + this.handle(libData.RoleCreateRequest, this.handleRoleCreateRequest.bind(this)); + this.handle(libData.RoleUpdateRequest, this.handleRoleUpdateRequest.bind(this)); + this.handle( libData.RoleGrantDefaultPermissionsRequest, this.handleRoleGrantDefaultPermissionsRequest.bind(this) ); - this.register(libData.RoleDeleteRequest, this.handleRoleDeleteRequest.bind(this)); - this.register(libData.UserGetRequest, this.handleUserGetRequest.bind(this)); - this.register(libData.UserListRequest, this.handleUserListRequest.bind(this)); - this.register(libData.UserSetSubscriptionsRequest, this.handleUserSetSubscriptionsRequest.bind(this)); - this.register(libData.UserCreateRequest, this.handleUserCreateRequest.bind(this)); - this.register(libData.UserRevokeTokenRequest, this.handleUserRevokeTokenRequest.bind(this)); - this.register(libData.UserUpdateRolesRequest, this.handleUserUpdateRolesRequest.bind(this)); - this.register(libData.UserSetAdminRequest, this.handleUserSetAdminRequest.bind(this)); - this.register(libData.UserSetBannedRequest, this.handleUserSetBannedRequest.bind(this)); - this.register(libData.UserSetWhitelistedRequest, this.handleUserSetWhitelistedRequest.bind(this)); - this.register(libData.UserDeleteRequest, this.handleUserDeleteRequest.bind(this)); - this.register(libData.DebugDumpWsRequest, this.handleDebugDumpWsRequest.bind(this)); + this.handle(libData.RoleDeleteRequest, this.handleRoleDeleteRequest.bind(this)); + this.handle(libData.UserGetRequest, this.handleUserGetRequest.bind(this)); + this.handle(libData.UserListRequest, this.handleUserListRequest.bind(this)); + this.handle(libData.UserSetSubscriptionsRequest, this.handleUserSetSubscriptionsRequest.bind(this)); + this.handle(libData.UserCreateRequest, this.handleUserCreateRequest.bind(this)); + this.handle(libData.UserRevokeTokenRequest, this.handleUserRevokeTokenRequest.bind(this)); + this.handle(libData.UserUpdateRolesRequest, this.handleUserUpdateRolesRequest.bind(this)); + this.handle(libData.UserSetAdminRequest, this.handleUserSetAdminRequest.bind(this)); + this.handle(libData.UserSetBannedRequest, this.handleUserSetBannedRequest.bind(this)); + this.handle(libData.UserSetWhitelistedRequest, this.handleUserSetWhitelistedRequest.bind(this)); + this.handle(libData.UserDeleteRequest, this.handleUserDeleteRequest.bind(this)); + this.handle(libData.DebugDumpWsRequest, this.handleDebugDumpWsRequest.bind(this)); } async handleControllerConfigGetRequest() { diff --git a/packages/controller/src/Controller.js b/packages/controller/src/Controller.js index 53636cf1..26db2fa4 100644 --- a/packages/controller/src/Controller.js +++ b/packages/controller/src/Controller.js @@ -13,6 +13,7 @@ const libConfig = require("@clusterio/lib/config"); const libData = require("@clusterio/lib/data"); const libErrors = require("@clusterio/lib/errors"); const libFileOps = require("@clusterio/lib/file_ops"); +const libLink = require("@clusterio/lib/link"); const libLoggingUtils = require("@clusterio/lib/logging_utils"); const libPlugin = require("@clusterio/lib/plugin"); const libPluginLoader = require("@clusterio/lib/plugin_loader"); @@ -970,17 +971,20 @@ class Controller { }; } - register(Class, handler) { + handle(Class, handler) { if (Class.type === "request") { - this.registerRequest(Class, handler); + this.handleRequest(Class, handler); } else if (Class.type === "event") { - this.registerEvent(Class, handler); + this.handleEvent(Class, handler); } else { throw new Error(`Class ${Class.name} has unrecognized type ${Class.type}`); } } - registerRequest(Request, handler) { + handleRequest(Request, handler) { + if (!libLink.Link._requestsByClass.has(Request)) { + throw new Error(`Unregistered Request class ${Request.name}`); + } if (this._registeredRequests.has(Request)) { throw new Error(`Request ${Request.name} is already registered`); } @@ -988,13 +992,19 @@ class Controller { } fallbackRequest(Request, handler) { + if (!libLink.Link._requestsByClass.has(Request)) { + throw new Error(`Unregistered Request class ${Request.name}`); + } if (this._fallbackedRequests.has(Request)) { throw new Error(`Request ${Request.name} is already fallbacked`); } this._fallbackedRequests.set(Request, handler); } - registerEvent(Event, handler) { + handleEvent(Event, handler) { + if (!libLink.Link._eventsByClass.has(Event)) { + throw new Error(`Unregistered Event class ${Event.name}`); + } if (this._registeredEvents.has(Event)) { throw new Error(`Event ${Event.name} is already registered`); } @@ -1002,6 +1012,9 @@ class Controller { } snoopEvent(Event, handler) { + if (!libLink.Link._eventsByClass.has(Event)) { + throw new Error(`Unregistered Event class ${Event.name}`); + } if (this._snoopedEvents.has(Event)) { throw new Error(`Event ${Event.name} is already snooped`); } diff --git a/packages/controller/src/HostConnection.js b/packages/controller/src/HostConnection.js index aa14e366..b5c626f3 100644 --- a/packages/controller/src/HostConnection.js +++ b/packages/controller/src/HostConnection.js @@ -58,11 +58,11 @@ class HostConnection extends BaseConnection { } }); - this.register(libData.InstanceStatusChangedEvent, this.handleInstanceStatusChangedEvent.bind(this)); - this.register(libData.InstancesUpdateRequest, this.handleInstancesUpdateRequest.bind(this)); - this.register(libData.InstanceSaveListUpdateEvent, this.handleInstanceSaveListUpdateEvent.bind(this)); - this.register(libData.LogMessageEvent, this.handleLogMessageEvent.bind(this)); - this.register(libData.InstancePlayerUpdateEvent, this.handleInstancePlayerUpdateEvent.bind(this)); + this.handle(libData.InstanceStatusChangedEvent, this.handleInstanceStatusChangedEvent.bind(this)); + this.handle(libData.InstancesUpdateRequest, this.handleInstancesUpdateRequest.bind(this)); + this.handle(libData.InstanceSaveListUpdateEvent, this.handleInstanceSaveListUpdateEvent.bind(this)); + this.handle(libData.LogMessageEvent, this.handleLogMessageEvent.bind(this)); + this.handle(libData.InstancePlayerUpdateEvent, this.handleInstancePlayerUpdateEvent.bind(this)); } _checkPluginVersions() { diff --git a/packages/ctl/ctl.js b/packages/ctl/ctl.js index b1ee3472..e02dd2a5 100755 --- a/packages/ctl/ctl.js +++ b/packages/ctl/ctl.js @@ -76,8 +76,8 @@ class Control extends libLink.Link { */ this.keepOpen = false; - this.register(libData.LogMessageEvent, this.handleLogMessageEvent.bind(this)); - this.register(libData.DebugWsMessageEvent, this.handleDebugWsMessageEvent.bind(this)); + this.handle(libData.LogMessageEvent, this.handleLogMessageEvent.bind(this)); + this.handle(libData.DebugWsMessageEvent, this.handleDebugWsMessageEvent.bind(this)); } async setLogSubscriptions({ @@ -114,6 +114,7 @@ class Control extends libLink.Link { async function loadPlugins(pluginList) { let pluginInfos = await libPluginLoader.loadPluginInfos(pluginList); + libLink.registerPluginMessages(pluginInfos); libConfig.registerPluginConfigGroups(pluginInfos); libConfig.finalizeConfigs(); diff --git a/packages/host/host.js b/packages/host/host.js index cee1f688..159e1aac 100755 --- a/packages/host/host.js +++ b/packages/host/host.js @@ -141,6 +141,7 @@ async function startHost() { logger.info("Loading Plugin info"); let pluginInfos = await libPluginLoader.loadPluginInfos(pluginList); + libLink.registerPluginMessages(pluginInfos); libConfig.registerPluginConfigGroups(pluginInfos); libConfig.finalizeConfigs(); diff --git a/packages/host/src/Host.js b/packages/host/src/Host.js index da87711a..3b7eea31 100644 --- a/packages/host/src/Host.js +++ b/packages/host/src/Host.js @@ -307,21 +307,21 @@ class Host extends libLink.Link { }); } - this.register(libData.SyncUserListsEvent, this.handleSyncUserListsEvent.bind(this)); + this.handle(libData.SyncUserListsEvent, this.handleSyncUserListsEvent.bind(this)); this.snoopEvent(libData.InstanceAdminlistUpdateEvent, this.handleAdminlistUpdateEvent.bind(this)); this.snoopEvent(libData.InstanceBanlistUpdateEvent, this.handleBanlistUpdateEvent.bind(this)); this.snoopEvent(libData.InstanceWhitelistUpdateEvent, this.handleWhitelistUpdateEvent.bind(this)); - this.register(libData.InstanceAssignInternalRequest, this.handleInstanceAssignInternalRequest.bind(this)); - this.register(libData.InstanceUnassignInternalRequest, this.handleInstanceUnassignInternalRequest.bind(this)); - this.register(libData.HostMetricsRequest, this.handleHostMetricsRequest.bind(this)); + this.handle(libData.InstanceAssignInternalRequest, this.handleInstanceAssignInternalRequest.bind(this)); + this.handle(libData.InstanceUnassignInternalRequest, this.handleInstanceUnassignInternalRequest.bind(this)); + this.handle(libData.HostMetricsRequest, this.handleHostMetricsRequest.bind(this)); this.fallbackRequest(libData.InstanceListSavesRequest, this.fallbackInstanceListSavesRequest.bind(this)); - this.register(libData.InstanceRenameSaveRequest, this.handleInstanceRenameSaveRequest.bind(this)); - this.register(libData.InstanceCopySaveRequest, this.handleInstanceCopySaveRequest.bind(this)); - this.register(libData.InstanceTransferSaveRequest, this.handleInstanceTransferSaveRequest.bind(this)); - this.register(libData.InstanceDeleteSaveRequest, this.handleInstanceDeleteSaveRequest.bind(this)); - this.register(libData.InstancePullSaveRequest, this.handleInstancePullSaveRequest.bind(this)); - this.register(libData.InstancePushSaveRequest, this.handleInstancePushSaveRequest.bind(this)); - this.register(libData.InstanceDeleteInternalRequest, this.handleInstanceDeleteInternalRequest.bind(this)); + this.handle(libData.InstanceRenameSaveRequest, this.handleInstanceRenameSaveRequest.bind(this)); + this.handle(libData.InstanceCopySaveRequest, this.handleInstanceCopySaveRequest.bind(this)); + this.handle(libData.InstanceTransferSaveRequest, this.handleInstanceTransferSaveRequest.bind(this)); + this.handle(libData.InstanceDeleteSaveRequest, this.handleInstanceDeleteSaveRequest.bind(this)); + this.handle(libData.InstancePullSaveRequest, this.handleInstancePullSaveRequest.bind(this)); + this.handle(libData.InstancePushSaveRequest, this.handleInstancePushSaveRequest.bind(this)); + this.handle(libData.InstanceDeleteInternalRequest, this.handleInstanceDeleteInternalRequest.bind(this)); } async _createNewInstanceDir(name) { diff --git a/packages/host/src/Instance.js b/packages/host/src/Instance.js index f442565f..b9d9c2ff 100644 --- a/packages/host/src/Instance.js +++ b/packages/host/src/Instance.js @@ -211,23 +211,23 @@ class Instance extends libLink.Link { this._hadPlayersOnline = false; this._playerAutosaveSlot = 1; - this.register(libData.InstanceExtractPlayersRequest, this.handleInstanceExtractPlayersRequest.bind(this)); - this.register(libData.InstanceAdminlistUpdateEvent, this.handleInstanceAdminlistUpdateEvent.bind(this)); - this.register(libData.InstanceBanlistUpdateEvent, this.handleInstanceBanlistUpdateEvent.bind(this)); - this.register(libData.InstanceWhitelistUpdateEvent, this.handleInstanceWhitelistUpdateEvent.bind(this)); - this.register(libData.ControllerConnectionEvent, this.handleControllerConnectionEvent.bind(this)); - this.register( + this.handle(libData.InstanceExtractPlayersRequest, this.handleInstanceExtractPlayersRequest.bind(this)); + this.handle(libData.InstanceAdminlistUpdateEvent, this.handleInstanceAdminlistUpdateEvent.bind(this)); + this.handle(libData.InstanceBanlistUpdateEvent, this.handleInstanceBanlistUpdateEvent.bind(this)); + this.handle(libData.InstanceWhitelistUpdateEvent, this.handleInstanceWhitelistUpdateEvent.bind(this)); + this.handle(libData.ControllerConnectionEvent, this.handleControllerConnectionEvent.bind(this)); + this.handle( libData.PrepareControllerDisconnectRequest, this.handlePrepareControllerDisconnectRequest.bind(this) ); - this.register(libData.InstanceMetricsRequest, this.handleInstanceMetricsRequest.bind(this)); - this.register(libData.InstanceStartRequest, this.handleInstanceStartRequest.bind(this)); - this.register(libData.InstanceLoadScenarioRequest, this.handleInstanceLoadScenarioRequest.bind(this)); - this.register(libData.InstanceListSavesRequest, this.handleInstanceListSavesRequest.bind(this)); - this.register(libData.InstanceCreateSaveRequest, this.handleInstanceCreateSaveRequest.bind(this)); - this.register(libData.InstanceExportDataRequest, this.handleInstanceExportDataRequest.bind(this)); - this.register(libData.InstanceStopRequest, this.handleInstanceStopRequest.bind(this)); - this.register(libData.InstanceKillRequest, this.handleInstanceKillRequest.bind(this)); - this.register(libData.InstanceSendRconRequest, this.handleInstanceSendRconRequest.bind(this)); + this.handle(libData.InstanceMetricsRequest, this.handleInstanceMetricsRequest.bind(this)); + this.handle(libData.InstanceStartRequest, this.handleInstanceStartRequest.bind(this)); + this.handle(libData.InstanceLoadScenarioRequest, this.handleInstanceLoadScenarioRequest.bind(this)); + this.handle(libData.InstanceListSavesRequest, this.handleInstanceListSavesRequest.bind(this)); + this.handle(libData.InstanceCreateSaveRequest, this.handleInstanceCreateSaveRequest.bind(this)); + this.handle(libData.InstanceExportDataRequest, this.handleInstanceExportDataRequest.bind(this)); + this.handle(libData.InstanceStopRequest, this.handleInstanceStopRequest.bind(this)); + this.handle(libData.InstanceKillRequest, this.handleInstanceKillRequest.bind(this)); + this.handle(libData.InstanceSendRconRequest, this.handleInstanceSendRconRequest.bind(this)); } _watchPlayerJoinsByChat() { diff --git a/packages/host/src/InstanceConnection.js b/packages/host/src/InstanceConnection.js index 51e82d72..223863fa 100644 --- a/packages/host/src/InstanceConnection.js +++ b/packages/host/src/InstanceConnection.js @@ -14,7 +14,7 @@ class InstanceConnection extends libLink.Link { this.plugins = new Map(); this.status = "stopped"; - this.register(libData.InstanceInitialisedEvent, this.handleInstanceInitialisedEvent.bind(this)); + this.handle(libData.InstanceInitialisedEvent, this.handleInstanceInitialisedEvent.bind(this)); this.snoopEvent(libData.InstanceStatusChangedEvent, this.snoopInstanceStatusChangedEvent.bind(this)); } diff --git a/packages/lib/data/messages_controller.js b/packages/lib/data/messages_controller.js index f35b6640..0280320f 100644 --- a/packages/lib/data/messages_controller.js +++ b/packages/lib/data/messages_controller.js @@ -301,8 +301,8 @@ LogQueryRequest.Response = class Response { /** @memberof module:lib/data */ class LogMessageEvent { static type = "event"; - static src = "host"; - static dst = "controller"; + static src = ["host", "controller"]; + static dst = ["controller", "control"]; /** @type {object} */ info; diff --git a/packages/lib/data/messages_core.js b/packages/lib/data/messages_core.js index 3028ad43..037e4c88 100644 --- a/packages/lib/data/messages_core.js +++ b/packages/lib/data/messages_core.js @@ -848,7 +848,7 @@ Message.validate = libSchema.compile(Message.jsonSchema); class PingRequest { static type = "request"; static src = ["controller", "host", "control"]; - static dest = ["controller", "host", "control"]; + static dst = ["controller", "host", "control"]; static permission = null; } diff --git a/packages/lib/data/messages_instance.js b/packages/lib/data/messages_instance.js index f7ace486..c288edbc 100644 --- a/packages/lib/data/messages_instance.js +++ b/packages/lib/data/messages_instance.js @@ -461,7 +461,7 @@ class InstanceSetSaveListSubscriptionsRequest { /** @memberof module:lib/data */ class InstanceSaveListUpdateEvent { static type = "event"; - static src = ["instance", "host"]; + static src = ["instance", "host", "controller"]; static dst = ["controller", "control"]; /** @type {number} */ @@ -533,8 +533,8 @@ class InstanceCreateSaveRequest { /** @memberof module:lib/data */ class InstanceRenameSaveRequest { static type = "request"; - static src = "control"; - static dst = "host"; + static src = ["control", "controller"]; + static dst = ["controller", "host"]; static permission = "core.instance.save.rename"; /** @type {number} */ @@ -568,8 +568,8 @@ class InstanceRenameSaveRequest { /** @memberof module:lib/data */ class InstanceCopySaveRequest { static type = "request"; - static src = "control"; - static dst = "host"; + static src = ["control", "controller"]; + static dst = ["controller", "host"]; static permission = "core.instance.save.copy"; /** @type {number} */ @@ -603,8 +603,8 @@ class InstanceCopySaveRequest { /** @memberof module:lib/data */ class InstanceDeleteSaveRequest { static type = "request"; - static src = "control"; - static dst = "host"; + static src = ["control", "controller"]; + static dst = ["controller", "host"]; static permission = "core.instance.save.delete"; /** @type {number} */ @@ -667,7 +667,7 @@ class InstanceDownloadSaveRequest { /** @memberof module:lib/data */ class InstanceTransferSaveRequest { static type = "request"; - static src = "control"; + static src = ["control", "controller"]; static dst = ["controller", "host"]; static permission = "core.instance.save.transfer"; @@ -1081,7 +1081,7 @@ class InstanceInitialisedEvent { /** @memberof module:lib/data */ class InstanceStatusChangedEvent { static type = "event"; - static src = "instance"; + static src = ["instance", "host"]; static dst = "controller"; /** @type {integer} */ diff --git a/packages/lib/data/messages_user.js b/packages/lib/data/messages_user.js index c657a698..e45e1b4d 100644 --- a/packages/lib/data/messages_user.js +++ b/packages/lib/data/messages_user.js @@ -266,11 +266,11 @@ class RawUser { // TODO refactor into lib/user.User properties: { name: { type: "string" }, roles: { type: "array", items: { type: "integer" } }, + instances: { type: "array", items: { type: "integer" } }, isAdmin: { type: "boolean" }, isBanned: { type: "boolean" }, isWhitelisted: { type: "boolean" }, banReason: { type: "string" }, - instances: { type: "array", items: { type: "integer" } }, isDeleted: { type: "boolean" }, playerStats: PlayerStats.jsonSchema, instanceStats: { @@ -299,6 +299,24 @@ class RawUser { // TODO refactor into lib/user.User json.isDeleted, playerStats, instanceStats ); } + + toJSON() { + const json = { + name: this.name, + roles: this.roles, + instances: this.instances, + isAdmin: this.isAdmin, + isBanned: this.isBanned, + isWhitelisted: this.isWhitelisted, + banReason: this.banReason, + isDeleted: this.isDeleted, + playerStats: this.playerStats, + }; + if (this.instanceStats) { + json.instanceStats = [...this.instanceStats]; + } + return json; + } } /* @memberof module:lib/data */ diff --git a/packages/lib/link/connectors.js b/packages/lib/link/connectors.js index 45159a2f..39a7ed69 100644 --- a/packages/lib/link/connectors.js +++ b/packages/lib/link/connectors.js @@ -36,8 +36,9 @@ class BaseConnector extends events.EventEmitter { let seq = this._seq; this._seq += 1; let src = new libData.Address(this.src.type, this.src.id, requestId); - let name = request.constructor.name; - let hasData = Boolean(request.constructor.jsonSchema); + let Request = request.constructor; + let name = Request.plugin ? `${Request.plugin}:${Request.name}` : Request.name; + let hasData = Boolean(Request.jsonSchema); const message = new libData.MessageRequest(seq, src, dst, name, hasData ? request : undefined); this.send(message); return seq; @@ -62,8 +63,9 @@ class BaseConnector extends events.EventEmitter { sendEvent(event, dst) { let seq = this._seq; this._seq += 1; - let name = event.constructor.name; - let hasData = Boolean(event.constructor.jsonSchema); + let Event = event.constructor; + let name = Event.plugin ? `${Event.plugin}:${Event.name}` : Event.name; + let hasData = Boolean(Event.jsonSchema); const message = new libData.MessageEvent(seq, this.src, dst, name, hasData ? event : undefined); this.send(message); return seq; diff --git a/packages/lib/link/index.js b/packages/lib/link/index.js index dcc14b7f..132e6150 100644 --- a/packages/lib/link/index.js +++ b/packages/lib/link/index.js @@ -10,6 +10,8 @@ const connectors = require("./connectors"); module.exports = { ...link, ...connectors, + + // migrate: Allow info for plugins from before link refactor to load. Event: class Event {}, Request: class Request {}, }; diff --git a/packages/lib/link/link.js b/packages/lib/link/link.js index f49cf253..5895074c 100644 --- a/packages/lib/link/link.js +++ b/packages/lib/link/link.js @@ -5,6 +5,7 @@ const libData = require("../data"); const libErrors = require("../errors"); const { logger } = require("../logging"); const libSchema = require("../schema"); +const { dataClasses } = require("./messages"); // Some definitions for the terminology used here: // link: Either side of a controller - client connection @@ -12,8 +13,6 @@ const libSchema = require("../schema"); // connection: the controller side of a link // client: the side that is not the controller of a link // message: the complete object sent using the 'message' event -// data: the data property of a message, essentially the payload. -// type: the type property of a message, identifies what to expect in data. /** * Common interface for server and client connections @@ -26,15 +25,14 @@ class Link { this.router = undefined; this.validateSent = true; - this._registeredRequests = new Map(); - this._fallbackedRequests = new Map(); - this._registeredEvents = new Map(); - this._snoopedEvents = new Map(); + this._requestHandlers = new Map(); + this._requestFallbacks = new Map(); this._eventHandlers = new Map(); + this._eventSnoopers = new Map(); this._pendingRequests = new Map(); this._nextRequestId = 1; - this.register(libData.PingRequest, () => {}); + this.handle(libData.PingRequest, () => {}); // Process messages received by the connector connector.on("message", payload => { @@ -97,28 +95,22 @@ class Link { throw new libErrors.InvalidMessage(`Unhandled message type ${message.type}`); } - if (message.type === "event" && this._snoopedEvents.has(message.name)) { - let event = this._snoopedEvents.get(message.name); - event.handler(event.eventFromJSON(message.data), message.src, message.dst).catch(err => { + let entry = this._validateMessage(message); + + if (message.type === "event" && this._eventSnoopers.has(entry.Event)) { + let handler = this._eventSnoopers.get(entry.Event); + handler(entry.eventFromJSON(message.data), message.src, message.dst).catch(err => { logger.error(`Unexpected error snooping ${message.name}:\n${err.stack}`); }); } if (!message.dst.addressedTo(this.connector.src)) { - this._routeMessage(message); + this._routeMessage(message, entry); return; } if (message.type === "request") { - let request = this._registeredRequests.get(message.name); - if (!request) { - this.connector.sendResponseError( - new libData.ResponseError(`Unrecognized request ${message.name}`), - message.src, - ); - return; - } - this._processRequest(message, request); + this._processRequest(message, entry, this._requestHandlers.get(entry.Request)); } else if (message.type === "response") { this._processResponse(message); @@ -127,11 +119,61 @@ class Link { this._processResponseError(message); } else if (message.type === "event") { - this._processEvent(message); + this._processEvent(message, entry); } } - _routeMessage(message) { + _validateMessage(message) { + if (message.type === "request") { + let entry = this.constructor._requestsByName.get(message.name); + if (!entry) { + let err = new libErrors.InvalidMessage(`Unrecognized request ${message.name}`); + this.connector.sendResponseError(new libData.ResponseError(err.message, err.code), message.src); + throw err; + } + if (!entry.allowedSrcTypes.has(message.src.type)) { + let err = new libErrors.InvalidMessage(`Source ${message.src} is not allowed for ${message.name}`); + this.connector.sendResponseError(new libData.ResponseError(err.message, err.code), message.src); + throw err; + } + if (!entry.allowedDstTypes.has(message.dst.type)) { + let err = new libErrors.InvalidMessage(`Destination ${message.dst} is not allowed for ${message.name}`); + this.connector.sendResponseError(new libData.ResponseError(err.message, err.code), message.src); + throw err; + } + return entry; + + } else if (message.type === "response") { + return undefined; + + } else if (message.type === "responseError") { + return undefined; + + } else if (message.type === "event") { + let entry = this.constructor._eventsByName.get(message.name); + if (!entry) { + throw new libErrors.InvalidMessage(`Unrecognized event ${message.name}`); + } + if (!entry.allowedSrcTypes.has(message.src.type)) { + throw new libErrors.InvalidMessage(`Source ${message.src} is not allowed for ${message.name}`); + } + if (message.dst.type === libData.Address.broadcast) { + if (!entry.allowedDstTypes.has(message.dst.id)) { + throw new libErrors.InvalidMessage(`Destination ${message.dst} is not allowed for ${message.name}`); + } + } else { + // eslint-disable-next-line no-lonely-if + if (!entry.allowedDstTypes.has(message.dst.type)) { + throw new libErrors.InvalidMessage(`Destination ${message.dst} is not allowed for ${message.name}`); + } + } + return entry; + } + + throw new Error("Should be unreachable"); + } + + _routeMessage(message, entry) { if (!this.router) { let err = new libErrors.InvalidMessage( `Received message addressed to ${message.dst} but this link does not route messages` @@ -144,7 +186,7 @@ class Link { throw err; } - let fallback = this._fallbackedRequests.get(message.name); + let fallback = message.type === "request" ? this._requestFallbacks.get(entry.Request) : undefined; if (this.router.forwardMessage(this, message, Boolean(fallback))) { return; } @@ -154,20 +196,20 @@ class Link { if (message.type !== "request") { throw new Error(`Router requested fallback handling of unsupported message type ${message.type}`); } - this._processRequest(message, fallback); + this._processRequest(message, entry, fallback); } - _processRequest(message, request) { - if (!request.handler) { + _processRequest(message, entry, handler) { + if (!handler) { this.connector.sendResponseError( - new libData.ResponseError(`No handler registered for ${request.Request.name}`), message.src + new libData.ResponseError(`No handler for ${entry.Request.name}`), message.src ); return; } let response; try { - response = request.handler(request.requestFromJSON(message.data), message.src, message.dst); + response = handler(entry.requestFromJSON(message.data), message.src, message.dst); } catch (err) { if (err.errors) { logger.error(JSON.stringify(err.errors, null, 4)); @@ -185,17 +227,22 @@ class Link { response.then( result => { if (this.validateSent) { - if (request.Response) { - request.responseFromJSON(JSON.parse(JSON.stringify(result))); + if (entry.Response) { + entry.responseFromJSON(JSON.parse(JSON.stringify(result))); } else if (result !== undefined) { - throw new Error(`Expected empty response from ${request.Request.name} handler`); + throw new Error(`Expected empty response from ${entry.Request.name} handler`); } } this.connector.sendResponse(result, message.src); } ).catch( err => { - if (!(err instanceof libErrors.RequestError)) { + if (err instanceof libErrors.InvalidMessage) { + logger.error(err.message); + if (err.errors) { + logger.error(JSON.stringify(err.errors, null, 4)); + } + } else if (!(err instanceof libErrors.RequestError)) { logger.error(`Unexpected error responding to ${message.name}:\n${err.stack}`); } this.connector.sendResponseError( @@ -232,16 +279,13 @@ class Link { pending.reject(new libErrors.RequestError(message.data.message, message.data.code, message.data.stack)); } - _processEvent(message) { - let event = this._registeredEvents.get(message.name); - if (!event) { - throw new libErrors.InvalidMessage(`Unrecognized event ${message.name}`); + _processEvent(message, entry) { + let handler = this._eventHandlers.get(entry.Event); + if (!handler) { + throw new libErrors.InvalidMessage(`Unhandled event ${message.name}`); } - if (!event.handler) { - return; - } - event.handler(event.eventFromJSON(message.data), message.src, message.dst).catch(err => { + handler(entry.eventFromJSON(message.data), message.src, message.dst).catch(err => { logger.error(`Unexpected error handling ${message.name}:\n${err.stack}`); }); } @@ -296,18 +340,16 @@ class Link { } sendRequest(request, dst) { - let name = request.constructor.name; - if (!this._registeredRequests.has(name)) { - // XXX should this be allowed? - this.registerRequest(request.constructor); + let entry = this.constructor._requestsByClass.get(request.constructor); + if (!entry) { + throw new Error(`Attempt to send unregistered Request ${request.constructor.name}`); } - let registeredRequest = this._registeredRequests.get(name); if (this.validateSent) { - registeredRequest.requestFromJSON(JSON.parse(JSON.stringify(request))); + entry.requestFromJSON(JSON.parse(JSON.stringify(request))); } let pending = { - request: registeredRequest, + request: entry, }; pending.promise = new Promise((resolve, reject) => { pending.resolve = resolve; @@ -321,141 +363,231 @@ class Link { } sendEvent(event, dst) { + let entry = this.constructor._eventsByClass.get(event.constructor); + if (!entry) { + throw new Error(`Attempt to send unregistered Event ${event.constructor.name}`); + } if (this.validateSent) { - let name = event.constructor.name; - if (!this._registeredEvents.has(name)) { - // XXX should this be allowed? - this.registerEvent(event.constructor); - } - this._registeredEvents.get(name).eventFromJSON(JSON.parse(JSON.stringify(event))); + entry.eventFromJSON(JSON.parse(JSON.stringify(event))); } this.connector.sendEvent(event, dst); } - register(Class, handler) { + handle(Class, handler) { if (Class.type === "request") { - this.registerRequest(Class, handler); + this.handleRequest(Class, handler); } else if (Class.type === "event") { - this.registerEvent(Class, handler); + this.handleEvent(Class, handler); } else { throw new Error(`Class ${Class.name} has unrecognized type ${Class.type}`); } } - _requestEntry(Request, handler) { - let name = Request.name; - let entry = { - Request, - handler, - }; + handleRequest(Request, handler) { + let entry = this.constructor._requestsByClass.get(Request); + if (!entry) { + throw new Error(`Unregistered Request class ${Request.name}`); + } + if (this._requestHandlers.has(Request)) { + throw new Error(`Request ${entry.name} is already registered`); + } + this._requestHandlers.set(Request, handler); + } + + fallbackRequest(Request, handler) { + let entry = this.constructor._requestsByClass.get(Request); + if (!entry) { + throw new Error(`Unregistered Request class ${Request.name}`); + } + if (this._requestFallbacks.has(Request)) { + throw new Error(`Request ${entry.name} is already fallbacked`); + } + this._requestFallbacks.set(Request, handler); + } + + handleEvent(Event, handler) { + let entry = this.constructor._eventsByClass.get(Event); + if (!entry) { + throw new Error(`Unregistered Event class ${Event.name}`); + } + if (this._eventHandlers.has(Event)) { + throw new Error(`Event ${entry.name} is already registered`); + } + this._eventHandlers.set(Event, handler); + } + + snoopEvent(Event, handler) { + let entry = this.constructor._eventsByClass.get(Event); + if (!entry) { + throw new Error(`Unregistered Event class ${Event.name}`); + } + if (this._eventSnoopers.has(Event)) { + throw new Error(`Event ${entry.name} is already snooped`); + } + this._eventSnoopers.set(Event, handler); + } + + static register(Class) { + if (Class.type === "request") { + this.registerRequest(Class); + } else if (Class.type === "event") { + this.registerEvent(Class); + } else { + throw new Error(`Data class ${Class.name} has unknown type ${Class.type}`); + } + } + + static requestFromJSON(Request, name) { if (Request.fromJSON) { if (!Request.jsonSchema) { throw new Error(`Request ${name} has static fromJSON but is missing static jsonSchema`); } let validate = libSchema.compile(Request.jsonSchema); - entry.requestFromJSON = (json) => { + return (json) => { if (!validate(json)) { throw new libErrors.InvalidMessage(`Request ${name} failed validation`, validate.errors); } return Request.fromJSON(json); }; - } else if (Request.jsonSchema) { - throw new Error(`Request ${name} has static jsonSchema but is missing static fromJSON`); - } else { - entry.requestFromJSON = () => new Request(); } + if (Request.jsonSchema) { + throw new Error(`Request ${name} has static jsonSchema but is missing static fromJSON`); + } + return () => new Request(); + } + + static responseFromJSON(Response, name) { + if (!Response.jsonSchema) { + throw new Error(`Response for Request ${name} is missing static jsonSchema`); + } + if (!Response.fromJSON) { + throw new Error(`Response for Request ${name} is missing static fromJSON`); + } + + let validate = libSchema.compile(Response.jsonSchema); + return (json) => { + if (!validate(json)) { + throw new libErrors.InvalidMessage( + `Response for request ${name} failed validation`, validate.errors + ); + } + return Response.fromJSON(json); + }; + } + + static allowedTypes(types, name, side) { + if (types === undefined) { + throw new Error(`Missing ${side} specification in ${name}`); + } + + if (!(types instanceof Array)) { + types = [types]; + } + + let allowed = new Set(); + for (let type of types) { + let id = libData.Address[type]; + if (typeof id !== "number") { + throw new Error(`Invalid type ${type} in ${side} specification of ${name}`); + } + if (id === libData.Address.broadcast) { + throw new Error(`${side} specification may not use the broadcast type in ${name}`); + } + allowed.add(id); + } + return allowed; + } + + static _requestsByName = new Map(); + static _requestsByClass = new Map(); + + static registerRequest(Request) { + const name = Request.plugin ? `${Request.plugin}:${Request.name}` : Request.name; + if (this._requestsByName.has(name)) { + throw new Error(`Request ${name} is already registered`); + } + + let entry = { + Request, + name, + requestFromJSON: this.requestFromJSON(Request, name), + allowedSrcTypes: this.allowedTypes(Request.src, name, "src"), + allowedDstTypes: this.allowedTypes(Request.dst, name, "dst"), + }; let Response = Request.Response; if (Response) { - if (!Response.jsonSchema) { - throw new Error(`Response for Request ${name} is missing static jsonSchema`); - } - if (!Response.fromJSON) { - throw new Error(`Response for Request ${name} is missing static fromJSON`); - } - - let validate = libSchema.compile(Response.jsonSchema); - entry.responseFromJSON = (json) => { - if (!validate(json)) { - throw new libErrors.InvalidMessage( - `Response for request ${name} failed validation`, validate.errors - ); - } - return Response.fromJSON(json); - }; - entry.Response = Response; + entry.responseFromJSON = this.responseFromJSON(Response, name); } else { entry.responseFromJSON = () => undefined; } - return entry; + + this._requestsByName.set(name, entry); + this._requestsByClass.set(Request, entry); } - registerRequest(Request, handler) { - let name = Request.name; - if (this._registeredRequests.has(name)) { - throw new Error(`Request ${name} is already registered`); - } - let request = this._requestEntry(Request, handler); - this._registeredRequests.set(name, request); - } - - fallbackRequest(Request, handler) { - let name = Request.name; - if (this._fallbackedRequests.has(name)) { - throw new Error(`Request ${name} is already fallbacked`); - } - let request = this._requestEntry(Request, handler); - this._fallbackedRequests.set(name, request); - } - - _createEventFromJSON(Event) { + static eventFromJSON(Event, name) { if (Event.fromJSON) { if (!Event.jsonSchema) { - throw new Error(`Event ${Event.name} has static fromJSON but is missing static jsonSchema`); + throw new Error(`Event ${name} has static fromJSON but is missing static jsonSchema`); } let validate = libSchema.compile(Event.jsonSchema); return (json) => { if (!validate(json)) { - throw new libErrors.InvalidMessage(`Event ${Event.name} failed validation`, validate.errors); + throw new libErrors.InvalidMessage(`Event ${name} failed validation`, validate.errors); } return Event.fromJSON(json); }; } if (Event.jsonSchema) { - throw new Error(`Event ${Event.name} has static jsonSchema but is missing static fromJSON`); + throw new Error(`Event ${name} has static jsonSchema but is missing static fromJSON`); } return () => new Event(); } - registerEvent(Event, handler) { - if (this._registeredEvents.has(Event.name)) { - throw new Error(`Event ${Event.name} is already registered`); - } - let event = { - Event, - handler, - eventFromJSON: this._createEventFromJSON(Event), - }; - this._registeredEvents.set(Event.name, event); - } + static _eventsByName = new Map(); + static _eventsByClass = new Map(); - snoopEvent(Event, handler) { - let name = Event.name; - if (this._snoopedEvents.has(name)) { - throw new Error(`Event ${name} is already snooped`); + static registerEvent(Event) { + const name = Event.plugin ? `${Event.plugin}:${Event.name}` : Event.name; + if (this._eventsByName.has(name)) { + throw new Error(`Event ${name} is already registered`); } - let event = { + + let entry = { Event, - handler, - eventFromJSON: this._createEventFromJSON(Event), + name, + eventFromJSON: this.eventFromJSON(Event, name), + allowedSrcTypes: this.allowedTypes(Event.src, name, "src"), + allowedDstTypes: this.allowedTypes(Event.dst, name, "dst"), }; - this._snoopedEvents.set(Event.name, event); + this._eventsByName.set(name, entry); + this._eventsByClass.set(Event, entry); + } +} + +for (let Class of dataClasses) { + Link.register(Class); +} + +function registerPluginMessages(pluginInfos) { + for (let pluginInfo of pluginInfos) { + for (let Class of pluginInfo.messages || []) { + if (Class.plugin !== pluginInfo.name) { + throw new Error( + `Expected message ${Class.name} from ${pluginInfo.name} to have a static ` + + `plugin property set to "${pluginInfo.name}"` + ); + } + Link.register(Class); + } } } module.exports = { Link, + registerPluginMessages, }; diff --git a/packages/lib/link/messages.js b/packages/lib/link/messages.js new file mode 100644 index 00000000..6a6acf5c --- /dev/null +++ b/packages/lib/link/messages.js @@ -0,0 +1,111 @@ +"use strict"; +const core = require("../data/messages_core"); +const controller = require("../data/messages_controller"); +const host = require("../data/messages_host"); +const instance = require("../data/messages_instance"); +const mod = require("../data/messages_mod"); +const user = require("../data/messages_user"); + +// Event and Request payloads registered with the Link class. Used to decode +// the data payload of incomming requsts and event messages into instances of +// their proper classes. +const dataClasses = [ + core.PingRequest, + + controller.ControllerConfigGetRequest, + controller.ControllerConfigSetFieldRequest, + controller.ControllerConfigSetPropRequest, + controller.HostGenerateTokenRequest, + controller.HostConfigCreateRequest, + controller.LogSetSubscriptionsRequest, + controller.LogQueryRequest, + controller.LogMessageEvent, + controller.DebugDumpWsRequest, + controller.DebugWsMessageEvent, + + host.HostListRequest, + host.HostSetSubscriptionsRequest, + host.HostUpdateEvent, + host.HostMetricsRequest, + host.ControllerConnectionEvent, + host.PrepareControllerDisconnectRequest, + host.SyncUserListsEvent, + + instance.InstanceDetailsGetRequest, + instance.InstanceDetailsListRequest, + instance.InstanceDetailsSetSubscriptionsRequest, + instance.InstanceDetailsUpdateEvent, + instance.InstanceCreateRequest, + instance.InstanceConfigGetRequest, + instance.InstanceConfigSetFieldRequest, + instance.InstanceConfigSetPropRequest, + instance.InstanceAssignRequest, + instance.InstanceMetricsRequest, + instance.InstanceStartRequest, + instance.InstanceListSavesRequest, + instance.InstanceSetSaveListSubscriptionsRequest, + instance.InstanceSaveListUpdateEvent, + instance.InstanceCreateSaveRequest, + instance.InstanceRenameSaveRequest, + instance.InstanceCopySaveRequest, + instance.InstanceDeleteSaveRequest, + instance.InstanceDownloadSaveRequest, + instance.InstanceTransferSaveRequest, + instance.InstancePullSaveRequest, + instance.InstancePushSaveRequest, + instance.InstanceLoadScenarioRequest, + instance.InstanceExportDataRequest, + instance.InstanceExtractPlayersRequest, + instance.InstanceStopRequest, + instance.InstanceKillRequest, + instance.InstanceDeleteRequest, + instance.InstanceDeleteInternalRequest, + instance.InstanceSendRconRequest, + instance.InstancesUpdateRequest, + instance.InstanceAssignInternalRequest, + instance.InstanceUnassignInternalRequest, + instance.InstanceInitialisedEvent, + instance.InstanceStatusChangedEvent, + instance.InstanceDetailsChangedEvent, + instance.InstanceBanlistUpdateEvent, + instance.InstanceAdminlistUpdateEvent, + instance.InstanceWhitelistUpdateEvent, + instance.InstancePlayerUpdateEvent, + + mod.ModPackGetRequest, + mod.ModPackGetDefaultRequest, + mod.ModPackListRequest, + mod.ModPackCreateRequest, + mod.ModPackUpdateRequest, + mod.ModPackDeleteRequest, + mod.ModPackSetSubscriptionsRequest, + mod.ModGetRequest, + mod.ModListRequest, + mod.ModSearchRequest, + mod.ModSetSubscriptionsRequest, + mod.ModDownloadRequest, + mod.ModDeleteRequest, + mod.ModPackUpdateEvent, + mod.ModUpdateEvent, + + user.PermissionListRequest, + user.RoleListRequest, + user.RoleCreateRequest, + user.RoleUpdateRequest, + user.RoleGrantDefaultPermissionsRequest, + user.RoleDeleteRequest, + user.UserGetRequest, + user.UserListRequest, + user.UserSetSubscriptionsRequest, + user.UserCreateRequest, + user.UserRevokeTokenRequest, + user.UserUpdateRolesRequest, + user.UserSetAdminRequest, + user.UserSetWhitelistedRequest, + user.UserSetBannedRequest, + user.UserDeleteRequest, + user.AccountUpdateEvent, + user.UserUpdateEvent, +]; + +module.exports = { dataClasses }; diff --git a/packages/lib/plugin_loader.js b/packages/lib/plugin_loader.js index e1f03ca0..e831e5db 100644 --- a/packages/lib/plugin_loader.js +++ b/packages/lib/plugin_loader.js @@ -7,6 +7,7 @@ const libErrors = require("./errors"); const libPlugin = require("./plugin"); +const { logger } = require("./logging"); const path = require("path"); @@ -42,6 +43,12 @@ async function loadPluginInfos(pluginList) { ); } + // migrate: ignore incompatible old plugins + if (pluginInfo.messages && !(pluginInfo.messages instanceof Array)) { + logger.warn(`Ignoring incompatible pre alpha.14 plugin ${pluginName}`); + continue; + } + pluginInfo.requirePath = pluginPath; pluginInfo.version = pluginPackage.version; plugins.push(pluginInfo); diff --git a/packages/web_ui/src/bootstrap.jsx b/packages/web_ui/src/bootstrap.jsx index e6f3bbb0..d07d1ed3 100644 --- a/packages/web_ui/src/bootstrap.jsx +++ b/packages/web_ui/src/bootstrap.jsx @@ -3,7 +3,7 @@ import "./index.css"; import React from "react"; import ReactDOM from "react-dom"; -import { libConfig, libLogging, libPlugin } from "@clusterio/lib"; +import { libConfig, libLink, libLogging, libPlugin } from "@clusterio/lib"; const { ConsoleTransport, WebConsoleFormat, logger } = libLogging; import App from "./components/App"; @@ -102,6 +102,7 @@ export default async function bootstrap() { format: new WebConsoleFormat(), })); let pluginInfos = await loadPluginInfos(); + libLink.registerPluginMessages(pluginInfos); libConfig.registerPluginConfigGroups(pluginInfos); libConfig.finalizeConfigs(); let plugins = await loadPlugins(pluginInfos); diff --git a/packages/web_ui/src/util/websocket.jsx b/packages/web_ui/src/util/websocket.jsx index 2d1b43c8..314745d0 100644 --- a/packages/web_ui/src/util/websocket.jsx +++ b/packages/web_ui/src/util/websocket.jsx @@ -114,15 +114,15 @@ export class Control extends libLink.Link { }); } - this.register(libData.AccountUpdateEvent, this.handleAccountUpdateEvent.bind(this)); - this.register(libData.HostUpdateEvent, this.handleHostUpdateEvent.bind(this)); - this.register(libData.InstanceDetailsUpdateEvent, this.handleInstanceDetailsUpdateEvent.bind(this)); - this.register(libData.InstanceSaveListUpdateEvent, this.handleInstanceSaveListUpdateEvent.bind(this)); - this.register(libData.ModPackUpdateEvent, this.handleModPackUpdateEvent.bind(this)); - this.register(libData.ModUpdateEvent, this.handleModUpdateEvent.bind(this)); - this.register(libData.UserUpdateEvent, this.handleUserUpdateEvent.bind(this)); - this.register(libData.LogMessageEvent, this.handleLogMessageEvent.bind(this)); - this.register(libData.DebugWsMessageEvent, this.handleDebugWsMessageEvent.bind(this)); + this.handle(libData.AccountUpdateEvent, this.handleAccountUpdateEvent.bind(this)); + this.handle(libData.HostUpdateEvent, this.handleHostUpdateEvent.bind(this)); + this.handle(libData.InstanceDetailsUpdateEvent, this.handleInstanceDetailsUpdateEvent.bind(this)); + this.handle(libData.InstanceSaveListUpdateEvent, this.handleInstanceSaveListUpdateEvent.bind(this)); + this.handle(libData.ModPackUpdateEvent, this.handleModPackUpdateEvent.bind(this)); + this.handle(libData.ModUpdateEvent, this.handleModUpdateEvent.bind(this)); + this.handle(libData.UserUpdateEvent, this.handleUserUpdateEvent.bind(this)); + this.handle(libData.LogMessageEvent, this.handleLogMessageEvent.bind(this)); + this.handle(libData.DebugWsMessageEvent, this.handleDebugWsMessageEvent.bind(this)); } async handleAccountUpdateEvent(event) { diff --git a/plugins/global_chat/info.js b/plugins/global_chat/info.js index fee0683f..875dec62 100644 --- a/plugins/global_chat/info.js +++ b/plugins/global_chat/info.js @@ -1,4 +1,5 @@ "use strict"; +const messages = require("./messages"); module.exports = { name: "global_chat", @@ -6,4 +7,8 @@ module.exports = { description: "Forwards chat between instances.", instanceEntrypoint: "instance", controlEntrypoint: "control", + + messages: [ + messages.ChatEvent, + ], }; diff --git a/plugins/global_chat/instance.js b/plugins/global_chat/instance.js index 01ba87a6..a334b230 100644 --- a/plugins/global_chat/instance.js +++ b/plugins/global_chat/instance.js @@ -20,7 +20,7 @@ function removeTags(content) { class InstancePlugin extends libPlugin.BaseInstancePlugin { async init() { this.messageQueue = []; - this.instance.register(ChatEvent, this.handleChatEvent.bind(this)); + this.instance.handle(ChatEvent, this.handleChatEvent.bind(this)); } onControllerConnectionEvent(event) { diff --git a/plugins/global_chat/test/plugin.js b/plugins/global_chat/test/plugin.js index 4029d659..4b147d92 100644 --- a/plugins/global_chat/test/plugin.js +++ b/plugins/global_chat/test/plugin.js @@ -1,5 +1,6 @@ "use strict"; const assert = require("assert").strict; +const libLink = require("@clusterio/lib/link"); const mock = require("../../../test/mock"); const lines = require("../../../test/lib/factorio/lines"); @@ -25,6 +26,7 @@ describe("global_chat plugin", function() { let instancePlugin; before(async function() { + libLink.Link.register(ChatEvent); instancePlugin = new instance.InstancePlugin(info, new mock.MockInstance(), new mock.MockHost()); await instancePlugin.init(); }); diff --git a/plugins/inventory_sync/controller.js b/plugins/inventory_sync/controller.js index e78d659e..34cc2bb0 100644 --- a/plugins/inventory_sync/controller.js +++ b/plugins/inventory_sync/controller.js @@ -48,11 +48,11 @@ class ControllerPlugin extends libPlugin.BaseControllerPlugin { }); }, this.controller.config.get("inventory_sync.autosave_interval") * 1000); - this.controller.register(AcquireRequest, this.handleAcquireRequest.bind(this)); - this.controller.register(ReleaseRequest, this.handleReleaseRequest.bind(this)); - this.controller.register(UploadRequest, this.handleUploadRequest.bind(this)); - this.controller.register(DownloadRequest, this.handleDownloadRequest.bind(this)); - this.controller.register(DatabaseStatsRequest, this.handleDatabaseStatsRequest.bind(this)); + this.controller.handle(AcquireRequest, this.handleAcquireRequest.bind(this)); + this.controller.handle(ReleaseRequest, this.handleReleaseRequest.bind(this)); + this.controller.handle(UploadRequest, this.handleUploadRequest.bind(this)); + this.controller.handle(DownloadRequest, this.handleDownloadRequest.bind(this)); + this.controller.handle(DatabaseStatsRequest, this.handleDatabaseStatsRequest.bind(this)); } async onInstanceStatusChanged(instance) { diff --git a/plugins/inventory_sync/info.js b/plugins/inventory_sync/info.js index 72f69ccb..9b341fcd 100644 --- a/plugins/inventory_sync/info.js +++ b/plugins/inventory_sync/info.js @@ -1,5 +1,6 @@ "use strict"; const { libConfig, libUsers } = require("@clusterio/lib"); +let messages = require("./messages"); class ControllerConfigGroup extends libConfig.PluginConfigGroup { } ControllerConfigGroup.defaultAccess = ["controller", "host", "control"]; @@ -51,6 +52,13 @@ module.exports = { controllerEntrypoint: "controller", ControllerConfigGroup, + messages: [ + messages.AcquireRequest, + messages.ReleaseRequest, + messages.UploadRequest, + messages.DownloadRequest, + messages.DatabaseStatsRequest, + ], webEntrypoint: "./web", routes: ["/inventory"], }; diff --git a/plugins/player_auth/controller.js b/plugins/player_auth/controller.js index 96afe5c8..aab1ad2c 100644 --- a/plugins/player_auth/controller.js +++ b/plugins/player_auth/controller.js @@ -63,8 +63,8 @@ class ControllerPlugin extends libPlugin.BaseControllerPlugin { this.handleVerify(req, res).catch(next); }); - this.controller.register(FetchPlayerCodeRequest, this.handleFetchPlayerCodeRequest.bind(this)); - this.controller.register(SetVerifyCodeRequest, this.handleSetVerifyCodeRequest.bind(this)); + this.controller.handle(FetchPlayerCodeRequest, this.handleFetchPlayerCodeRequest.bind(this)); + this.controller.handle(SetVerifyCodeRequest, this.handleSetVerifyCodeRequest.bind(this)); } async handlePlayerCode(req, res) { diff --git a/plugins/player_auth/info.js b/plugins/player_auth/info.js index 2f937b37..ebe16f55 100644 --- a/plugins/player_auth/info.js +++ b/plugins/player_auth/info.js @@ -1,5 +1,6 @@ "use strict"; let { libConfig } = require("@clusterio/lib"); +let messages = require("./messages"); class ControllerConfigGroup extends libConfig.PluginConfigGroup {} @@ -29,4 +30,9 @@ module.exports = { instanceEntrypoint: "instance", webEntrypoint: "./web", ControllerConfigGroup, + + messages: [ + messages.FetchPlayerCodeRequest, + messages.SetVerifyCodeRequest, + ], }; diff --git a/plugins/research_sync/controller.js b/plugins/research_sync/controller.js index e182badb..3375306e 100644 --- a/plugins/research_sync/controller.js +++ b/plugins/research_sync/controller.js @@ -49,9 +49,9 @@ class ControllerPlugin extends libPlugin.BaseControllerPlugin { this.progressBroadcastId = null; this.progressToBroadcast = new Set(); - this.controller.register(ContributionEvent, this.handleContributionEvent.bind(this)); - this.controller.register(FinishedEvent, this.handleFinishedEvent.bind(this)); - this.controller.register(SyncTechnologiesRequest, this.handleSyncTechnologiesRequest.bind(this)); + this.controller.handle(ContributionEvent, this.handleContributionEvent.bind(this)); + this.controller.handle(FinishedEvent, this.handleFinishedEvent.bind(this)); + this.controller.handle(SyncTechnologiesRequest, this.handleSyncTechnologiesRequest.bind(this)); } async onShutdown() { diff --git a/plugins/research_sync/info.js b/plugins/research_sync/info.js index 2b895a22..c84a2053 100644 --- a/plugins/research_sync/info.js +++ b/plugins/research_sync/info.js @@ -1,4 +1,5 @@ "use strict"; +const messages = require("./messages"); module.exports = { name: "research_sync", @@ -6,4 +7,11 @@ module.exports = { description: "Synchronises technology research progress between instances.", instanceEntrypoint: "instance", controllerEntrypoint: "controller", + + messages: [ + messages.ContributionEvent, + messages.ProgressEvent, + messages.FinishedEvent, + messages.SyncTechnologiesRequest, + ], }; diff --git a/plugins/research_sync/instance.js b/plugins/research_sync/instance.js index b37892c4..d106b508 100644 --- a/plugins/research_sync/instance.js +++ b/plugins/research_sync/instance.js @@ -28,8 +28,8 @@ class InstancePlugin extends libPlugin.BaseInstancePlugin { }); this.syncStarted = false; - this.instance.register(ProgressEvent, this.handleProgressEvent.bind(this)); - this.instance.register(FinishedEvent, this.handleFinishedEvent.bind(this)); + this.instance.handle(ProgressEvent, this.handleProgressEvent.bind(this)); + this.instance.handle(FinishedEvent, this.handleFinishedEvent.bind(this)); } async researchContribution(tech) { diff --git a/plugins/research_sync/messages.js b/plugins/research_sync/messages.js index 85670154..8d5e839b 100644 --- a/plugins/research_sync/messages.js +++ b/plugins/research_sync/messages.js @@ -5,7 +5,7 @@ class ContributionEvent { static type = "event"; static src = "instance"; static dst = "controller"; - static json = "research_sync"; + static plugin = "research_sync"; /** @type {string} */ name; @@ -39,7 +39,7 @@ class ProgressEvent { static type = "event"; static src = "controller"; static dst = "instance"; - static json = "research_sync"; + static plugin = "research_sync"; /** @type {Array<>} */ technologies; @@ -74,9 +74,9 @@ class ProgressEvent { class FinishedEvent { static type = "event"; - static src = "instance"; + static src = ["instance", "controller"]; static dst = "instance"; - static json = "research_sync"; + static plugin = "research_sync"; /** @type {string} */ name; @@ -144,7 +144,7 @@ class SyncTechnologiesRequest { static type = "request"; static src = "instance"; static dst = "controller"; - static json = "research_sync"; + static plugin = "research_sync"; /** @type {Array} */ technologies; diff --git a/plugins/subspace_storage/controller.js b/plugins/subspace_storage/controller.js index aaedf1c9..f062a28d 100644 --- a/plugins/subspace_storage/controller.js +++ b/plugins/subspace_storage/controller.js @@ -97,10 +97,10 @@ class ControllerPlugin extends libPlugin.BaseControllerPlugin { routes.addApiRoutes(this.controller.app, this.items); - this.controller.register(GetStorageRequest, this.handleGetStorageRequest.bind(this)); - this.controller.register(PlaceEvent, this.handlePlaceEvent.bind(this)); - this.controller.register(RemoveRequest, this.handleRemoveRequest.bind(this)); - this.controller.register(SetStorageSubscriptionRequest, this.handleSetStorageSubscriptionRequest.bind(this)); + this.controller.handle(GetStorageRequest, this.handleGetStorageRequest.bind(this)); + this.controller.handle(PlaceEvent, this.handlePlaceEvent.bind(this)); + this.controller.handle(RemoveRequest, this.handleRemoveRequest.bind(this)); + this.controller.handle(SetStorageSubscriptionRequest, this.handleSetStorageSubscriptionRequest.bind(this)); } updateStorage() { diff --git a/plugins/subspace_storage/info.js b/plugins/subspace_storage/info.js index bcc421b5..394bb4e9 100644 --- a/plugins/subspace_storage/info.js +++ b/plugins/subspace_storage/info.js @@ -1,5 +1,6 @@ "use strict"; const { libConfig, libUsers } = require("@clusterio/lib"); +const messages = require("./messages"); class ControllerConfigGroup extends libConfig.PluginConfigGroup {} ControllerConfigGroup.defaultAccess = ["controller", "host", "control"]; @@ -57,6 +58,13 @@ module.exports = { controllerEntrypoint: "controller", ControllerConfigGroup, + messages: [ + messages.PlaceEvent, + messages.RemoveRequest, + messages.GetStorageRequest, + messages.UpdateStorageEvent, + messages.SetStorageSubscriptionRequest, + ], webEntrypoint: "./web", routes: ["/storage"], }; diff --git a/plugins/subspace_storage/instance.js b/plugins/subspace_storage/instance.js index a2ac7b6b..cd0a5fb3 100644 --- a/plugins/subspace_storage/instance.js +++ b/plugins/subspace_storage/instance.js @@ -31,7 +31,7 @@ class InstancePlugin extends libPlugin.BaseInstancePlugin { task.finally(() => { this.pendingTasks.delete(task); }); }); - this.instance.register(UpdateStorageEvent, this.handleUpdateStorageEvent.bind(this)); + this.instance.handle(UpdateStorageEvent, this.handleUpdateStorageEvent.bind(this)); } async onStart() { diff --git a/test/integration/index.js b/test/integration/index.js index ae759f8f..3f6c4eeb 100644 --- a/test/integration/index.js +++ b/test/integration/index.js @@ -53,13 +53,13 @@ class TestControl extends libLink.Link { ).catch(err => logger.error(`Error setting user subscriptions:\n${err.stack}`)); }); - this.register(libData.AccountUpdateEvent); - this.register(libData.HostUpdateEvent, this.handleHostUpdateEvent.bind(this)); - this.register(libData.InstanceDetailsUpdateEvent, this.handleInstanceDetailsUpdateEvent.bind(this)); - this.register(libData.InstanceSaveListUpdateEvent, this.handleInstanceSaveListUpdateEvent.bind(this)); - this.register(libData.ModUpdateEvent, this.handleModUpdateEvent.bind(this)); - this.register(libData.ModPackUpdateEvent, this.handleModPackUpdateEvent.bind(this)); - this.register(libData.UserUpdateEvent, this.handleUserUpdateEvent.bind(this)); + this.handle(libData.AccountUpdateEvent); + this.handle(libData.HostUpdateEvent, this.handleHostUpdateEvent.bind(this)); + this.handle(libData.InstanceDetailsUpdateEvent, this.handleInstanceDetailsUpdateEvent.bind(this)); + this.handle(libData.InstanceSaveListUpdateEvent, this.handleInstanceSaveListUpdateEvent.bind(this)); + this.handle(libData.ModUpdateEvent, this.handleModUpdateEvent.bind(this)); + this.handle(libData.ModPackUpdateEvent, this.handleModPackUpdateEvent.bind(this)); + this.handle(libData.UserUpdateEvent, this.handleUserUpdateEvent.bind(this)); } async handleHostUpdateEvent(event) { diff --git a/test/lib/command.js b/test/lib/command.js index 2d879e83..38d6f635 100644 --- a/test/lib/command.js +++ b/test/lib/command.js @@ -13,15 +13,18 @@ const addr = libData.Address.fromShorthand; describe("lib/command", function() { let testRole = libData.RawRole.fromJSON({ id: 28, name: "Test Role", description: "Test", permissions: [] }); - let loopbackConnector = new libLink.VirtualConnector(addr("controller"), addr("controller")); - let testControl = new mock.MockControl(loopbackConnector); - testControl.register( + let [controlConnector, controllerConnector] = libLink.VirtualConnector.makePair( + addr({ controlId: 1}), addr("controller") + ); + let testControl = new mock.MockControl(controlConnector); + let testController = new libLink.Link(controllerConnector); + testController.handle( libData.HostListRequest, () => [new libData.HostDetails("test", "0.1", "Test Host", 11, false)] ); - testControl.register( + testController.handle( libData.InstanceDetailsListRequest, () => [new libData.InstanceDetails("Test Instance", 57, 4, null, "stopped")] ); - testControl.register(libData.RoleListRequest, () => [testRole]); + testController.handle(libData.RoleListRequest, () => [testRole]); describe("resolveHost", function() { it("should pass an integer like string back", async function() { diff --git a/test/lib/link/link.js b/test/lib/link/link.js index c67e98f9..484029bf 100644 --- a/test/lib/link/link.js +++ b/test/lib/link/link.js @@ -27,6 +27,7 @@ describe("lib/link/link", function() { static src = ["controller", "control"]; static dst = ["controller", "control"]; } + libLink.Link.register(SimpleRequest); class NumberRequest { static type = "request"; static src = ["controller", "control"]; @@ -42,11 +43,13 @@ describe("lib/link/link", function() { toJSON() { return this.value; } static fromJSON(json) { return new this(json); } }; + libLink.Link.register(NumberRequest); class SimpleEvent { static type = "event"; static src = ["controller", "control"]; static dst = ["controller", "control"]; } + libLink.Link.register(SimpleEvent); class NumberEvent { static type = "event"; static src = ["controller", "control"]; @@ -56,6 +59,7 @@ describe("lib/link/link", function() { toJSON() { return this.value; } static fromJSON(json) { return new this(json); } } + libLink.Link.register(NumberEvent); beforeEach(function() { testConnector = new mock.MockConnector(src, dst); @@ -82,14 +86,14 @@ describe("lib/link/link", function() { static src = "controller"; static dst = "control"; } - testLink.register(UnhandledRequest); + libLink.Link.register(UnhandledRequest); testConnector.emit("message", new libData.MessageRequest(1, dst, src, "UnhandledRequest")); assert.deepEqual(testConnector.sentMessages, [ new libData.MessageResponseError( testConnector._seq - 1, src, dst, - new libData.ResponseError("No handler registered for UnhandledRequest") + new libData.ResponseError("No handler for UnhandledRequest") ), ]); }); @@ -190,18 +194,18 @@ describe("lib/link/link", function() { ); }); }); - describe(".register()", function() { - it("should register a request", function() { + describe(".handle()", function() { + it("should register a request handler", function() { let handled = false; - testLink.register(SimpleRequest, async () => { handled = true; }); - assert(testLink._registeredRequests.has("SimpleRequest"), "request was not registered"); + testLink.handle(SimpleRequest, async () => { handled = true; }); + assert(testLink._requestHandlers.has(SimpleRequest), "request handler was not registered"); testConnector.emit("message", new libData.MessageRequest(1, dst, src, "SimpleRequest")); assert(handled, "request was not handled"); }); it("should send response error from request handler throwing", async function() { let message = events.once(testConnector, "send"); message.catch(() => {}); - testLink.register(SimpleRequest, async () => { throwSimple("Error"); }); + testLink.handle(SimpleRequest, async () => { throwSimple("Error"); }); testConnector.emit("message", new libData.MessageRequest(1, dst, src, "SimpleRequest")); assert.deepEqual( await message, @@ -213,7 +217,7 @@ describe("lib/link/link", function() { it("should send response error on request validation failing", async function() { let message = events.once(testConnector, "send"); message.catch(() => {}); - testLink.register(NumberRequest, async () => 1); + testLink.handle(NumberRequest, async () => 1); testConnector.emit("message", new libData.MessageRequest(1, dst, src, "NumberRequest", "not a number")); let response = (await message)[0]; assert.deepEqual( @@ -228,7 +232,7 @@ describe("lib/link/link", function() { it("should send response error on response validation failing", async function() { let message = events.once(testConnector, "send"); message.catch(() => {}); - testLink.register(NumberRequest, async () => "not a number"); + testLink.handle(NumberRequest, async () => "not a number"); testConnector.emit("message", new libData.MessageRequest(1, dst, src, "NumberRequest", 1)); let response = (await message)[0]; assert.deepEqual( @@ -243,7 +247,7 @@ describe("lib/link/link", function() { it("should send value returned from request handler", async function() { let message = events.once(testConnector, "send"); message.catch(() => {}); - testLink.register(NumberRequest, async (request) => request.value + 4); + testLink.handle(NumberRequest, async (request) => request.value + 4); testConnector.emit("message", new libData.MessageRequest(1, dst, src, "NumberRequest", 1)); let response = (await message)[0]; assert.deepEqual( @@ -251,41 +255,43 @@ describe("lib/link/link", function() { new libData.MessageResponse(1, src, dst, 5) ); }); - it("should register an event", function() { + it("should register an event handler", function() { let handled = false; - testLink.register(SimpleEvent, async () => { handled = true; }); - assert(testLink._registeredEvents.has("SimpleEvent"), "event was not registered"); + testLink.handle(SimpleEvent, async () => { handled = true; }); + assert(testLink._eventHandlers.has(SimpleEvent), "event handler was not registered"); testConnector.emit("message", new libData.MessageEvent(1, dst, src, "SimpleEvent")); assert(handled, "event was not handled"); }); it("should pass value to event handler", async function() { let value; - testLink.register(NumberEvent, async (event) => { value = event.value; }); + testLink.handle(NumberEvent, async (event) => { value = event.value; }); testConnector.emit("message", new libData.MessageEvent(1, dst, src, "NumberEvent", 9)); assert.deepEqual(value, 9); }); it("should log errors from event handler", function() { - testLink.register(SimpleEvent, async () => { throwSimple("Error"); }); + testLink.handle(SimpleEvent, async () => { throwSimple("Error"); }); testConnector.emit("message", new libData.MessageEvent(1, dst, src, "SimpleEvent")); }); it("should throw on unknown type", function() { assert.throws( - () => testLink.register({ name: "Bad", type: "bad" }), + () => testLink.handle({ name: "Bad", type: "bad" }), { message: "Class Bad has unrecognized type bad" } ); }); it("should throw on double registration", function() { - testLink.register(SimpleRequest); + testLink.handle(SimpleRequest); assert.throws( - () => testLink.register(SimpleRequest), + () => testLink.handle(SimpleRequest), new Error("Request SimpleRequest is already registered") ); - testLink.register(SimpleEvent); + testLink.handle(SimpleEvent); assert.throws( - () => testLink.register(SimpleEvent), + () => testLink.handle(SimpleEvent), new Error("Event SimpleEvent is already registered") ); }); + }); + describe("static .register()", function() { it("should throw if Request has only one of jsonSchema and fromJSON", function() { class BadRequest1 { static type = "request"; @@ -294,7 +300,7 @@ describe("lib/link/link", function() { static jsonSchema = {}; } assert.throws( - () => testLink.register(BadRequest1), + () => libLink.Link.register(BadRequest1), new Error("Request BadRequest1 has static jsonSchema but is missing static fromJSON") ); class BadRequest2 { @@ -304,7 +310,7 @@ describe("lib/link/link", function() { static fromJSON() {}; } assert.throws( - () => testLink.register(BadRequest2), + () => libLink.Link.register(BadRequest2), new Error("Request BadRequest2 has static fromJSON but is missing static jsonSchema") ); }); @@ -316,7 +322,7 @@ describe("lib/link/link", function() { static jsonSchema = {}; } assert.throws( - () => testLink.register(BadEvent1), + () => libLink.Link.register(BadEvent1), new Error("Event BadEvent1 has static jsonSchema but is missing static fromJSON") ); class BadEvent2 { @@ -326,7 +332,7 @@ describe("lib/link/link", function() { static fromJSON() {}; } assert.throws( - () => testLink.register(BadEvent2), + () => libLink.Link.register(BadEvent2), new Error("Event BadEvent2 has static fromJSON but is missing static jsonSchema") ); }); @@ -348,7 +354,8 @@ describe("lib/link/link", function() { static jsonSchema = { type: "string" }; static fromJSON(json) { return new this(json); }; } - testLink.register(StringEvent, () => {}); + libLink.Link.register(StringEvent); + testLink.handle(StringEvent, () => {}); assert.throws( () => testLink._processMessage(new libData.MessageEvent(1, dst, src, "StringEvent", 99)), { message: "Event StringEvent failed validation" } @@ -360,7 +367,7 @@ describe("lib/link/link", function() { it("should snoop an event", function() { let handled = false; testLink.snoopEvent(SimpleEvent, async () => { handled = true; }); - assert(testLink._snoopedEvents.has("SimpleEvent"), "event was not snooped"); + assert(testLink._eventSnoopers.has(SimpleEvent), "event was not snooped"); testConnector.emit("message", new libData.MessageEvent(1, dst, src, "SimpleEvent")); assert(handled, "event was not handled"); }); @@ -375,28 +382,6 @@ describe("lib/link/link", function() { new Error("Event SimpleEvent is already snooped") ); }); - it("should throw if Event has only one of jsonSchema and fromJSON", function() { - class BadEvent1 { - static type = "event"; - static src = "controller"; - static dst = "control"; - static jsonSchema = {}; - } - assert.throws( - () => testLink.snoopEvent(BadEvent1), - new Error("Event BadEvent1 has static jsonSchema but is missing static fromJSON") - ); - class BadEvent2 { - static type = "event"; - static src = "controller"; - static dst = "control"; - static fromJSON() {}; - } - assert.throws( - () => testLink.snoopEvent(BadEvent2), - new Error("Event BadEvent2 has static fromJSON but is missing static jsonSchema") - ); - }); }); }); }); diff --git a/test/mock.js b/test/mock.js index d6226245..2006e360 100644 --- a/test/mock.js +++ b/test/mock.js @@ -185,7 +185,7 @@ class MockController { this.hosts = new Map(); } - register() { + handle() { } getControllerUrl() { @@ -210,6 +210,7 @@ class MockController { } async function createControllerPlugin(ControllerPluginClass, info) { + libLink.registerPluginMessages([info]); let controller = new MockController(); let metrics = {}; let logger = new MockLogger();