From 680047bcb1c32caf8af138ef5e66f26e091d0aec Mon Sep 17 00:00:00 2001 From: Colin Alworth Date: Mon, 21 Oct 2024 10:45:35 -0500 Subject: [PATCH] fix: JS API shouldn't require reference to window object (#6193) Also introduces a dh.Event type to use instead of the browser Event and CustomEvent types. Fixes #5537 Fixes #6190 --- .../web/DeephavenJsApiLinkerTemplate.js | 7 +- .../deephaven/web/client/api/Callbacks.java | 1 + .../web/client/api/ConnectOptions.java | 46 +++++++++++- .../deephaven/web/client/api/CoreClient.java | 35 +++++----- .../io/deephaven/web/client/api/EventFn.java | 15 ---- .../web/client/api/JsPartitionedTable.java | 18 ++--- .../io/deephaven/web/client/api/JsTable.java | 9 +-- .../web/client/api/JsTotalsTable.java | 5 +- .../web/client/api/QueryConnectable.java | 39 +++++++++-- .../web/client/api/WorkerConnection.java | 70 ++++++++----------- .../web/client/api/barrage/WebGrpcUtils.java | 38 ---------- .../client/api/barrage/stream/BiDiStream.java | 8 +-- .../web/client/api/batch/RequestBatcher.java | 9 +-- .../deephaven/web/client/api/event/Event.java | 38 ++++++++++ .../web/client/api/event/EventFn.java | 15 ++++ .../api/{ => event}/HasEventHandling.java | 46 +++++------- .../web/client/api/grpc/UnaryWithHeaders.java | 22 +++--- .../client/api/lifecycle/HasLifecycle.java | 12 ++-- .../AbstractTableSubscription.java | 7 +- .../TableViewportSubscription.java | 38 ++++------ .../web/client/api/tree/JsTreeTable.java | 15 ++-- .../web/client/api/widget/JsWidget.java | 7 +- .../api/widget/plot/FigureSubscription.java | 31 +++----- .../web/client/api/widget/plot/JsChart.java | 2 +- .../web/client/api/widget/plot/JsFigure.java | 19 ++--- .../api/widget/plot/JsFigureFactory.java | 9 +-- .../client/api/widget/plot/JsMultiSeries.java | 12 +--- .../web/client/api/widget/plot/OneClick.java | 6 +- .../deephaven/web/client/ide/IdeClient.java | 28 -------- .../web/client/ide/IdeConnection.java | 46 +++--------- .../deephaven/web/client/ide/IdeSession.java | 22 ++---- .../web/client/state/ClientTableState.java | 3 +- .../web/client/state/HasTableBinding.java | 3 +- .../web/client/state/TableReviver.java | 7 +- .../client/api/AbstractAsyncGwtTestCase.java | 11 +-- .../client/api/HierarchicalTableTestGwt.java | 28 ++++---- .../client/api/PartitionedTableTestGwt.java | 6 +- .../web/client/api/TotalsTableTestGwt.java | 16 ++--- .../api/subscription/ViewportTestGwt.java | 16 ++--- 39 files changed, 335 insertions(+), 430 deletions(-) delete mode 100644 web/client-api/src/main/java/io/deephaven/web/client/api/EventFn.java delete mode 100644 web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebGrpcUtils.java create mode 100644 web/client-api/src/main/java/io/deephaven/web/client/api/event/Event.java create mode 100644 web/client-api/src/main/java/io/deephaven/web/client/api/event/EventFn.java rename web/client-api/src/main/java/io/deephaven/web/client/api/{ => event}/HasEventHandling.java (81%) delete mode 100644 web/client-api/src/main/java/io/deephaven/web/client/ide/IdeClient.java diff --git a/web/client-api/src/main/java/io/deephaven/web/DeephavenJsApiLinkerTemplate.js b/web/client-api/src/main/java/io/deephaven/web/DeephavenJsApiLinkerTemplate.js index 6a7ed749806..708227464ac 100644 --- a/web/client-api/src/main/java/io/deephaven/web/DeephavenJsApiLinkerTemplate.js +++ b/web/client-api/src/main/java/io/deephaven/web/DeephavenJsApiLinkerTemplate.js @@ -15,11 +15,12 @@ function bindTo(target, source) { var Scope = function () { }; -Scope.prototype = self; +Scope.prototype = globalThis; var $doc, $entry, $moduleName, $moduleBase; var $wnd = new Scope(); -bindTo($wnd, self); -var dh = {} +bindTo($wnd, globalThis); +var window = $wnd; +var dh = {}; $wnd.dh = dh; import {dhinternal} from './dh-internal.js'; $wnd.dhinternal = dhinternal; diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/Callbacks.java b/web/client-api/src/main/java/io/deephaven/web/client/api/Callbacks.java index 7a32670c3e8..c3568e8fac3 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/Callbacks.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/Callbacks.java @@ -6,6 +6,7 @@ import elemental2.dom.DomGlobal; import elemental2.promise.Promise; import elemental2.promise.Promise.PromiseExecutorCallbackFn.RejectCallbackFn; +import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.shared.fu.JsBiConsumer; import javax.annotation.Nullable; diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/ConnectOptions.java b/web/client-api/src/main/java/io/deephaven/web/client/api/ConnectOptions.java index 82bf5d8bfc4..8bf5c5d10bc 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/ConnectOptions.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/ConnectOptions.java @@ -3,19 +3,47 @@ // package io.deephaven.web.client.api; +import elemental2.core.Function; import jsinterop.annotations.JsIgnore; +import jsinterop.annotations.JsNullable; import jsinterop.annotations.JsType; import jsinterop.base.Js; import jsinterop.base.JsPropertyMap; /** - * Presently optional and not used by the server, this allows the client to specify some authentication details. String - * authToken - base 64 encoded auth token. String serviceId - The service ID to use for the connection. + * Addition optional configuration that can be passed to the {@link CoreClient} constructor. */ @JsType(namespace = "dh") public class ConnectOptions { + /** + * Optional map of http header names and values to send to the server with each request. + */ + @JsNullable public JsPropertyMap headers = Js.uncheckedCast(JsPropertyMap.of()); + /** + * True to enable debug logging. At this time, only enables logging for gRPC calls. + */ + @JsNullable + public boolean debug = false; + + /** + * Set this to true to force the use of websockets when connecting to the deephaven instance, false to force the use + * of {@code fetch}. + *

+ * Defaults to null, indicating that the server URL should be checked to see if we connect with fetch or websockets. + */ + @JsNullable + public Boolean useWebsockets; + + // TODO (deephaven-core#6214) provide our own grpc-web library that can replace fetch + // /** + // * Optional fetch implementation to use instead of the global {@code fetch()} call, allowing callers to provide a + // * polyfill rather than add a new global. + // */ + // @JsNullable + // public Function fetch; + public ConnectOptions() { } @@ -24,6 +52,18 @@ public ConnectOptions() { public ConnectOptions(Object connectOptions) { this(); JsPropertyMap map = Js.asPropertyMap(connectOptions); - headers = Js.uncheckedCast(map.getAsAny("headers").asPropertyMap()); + if (map.has("headers")) { + headers = Js.uncheckedCast(map.getAsAny("headers").asPropertyMap()); + } + if (map.has("debug")) { + debug = map.getAsAny("debug").asBoolean(); + } + if (map.has("useWebsockets")) { + useWebsockets = map.getAsAny("useWebsockets").asBoolean(); + } + // TODO (deephaven-core#6214) provide our own grpc-web library that can replace fetch + // if (map.has("fetch")) { + // fetch = map.getAsAny("fetch").uncheckedCast(); + // } } } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/CoreClient.java b/web/client-api/src/main/java/io/deephaven/web/client/api/CoreClient.java index 6c6c2d2d556..7c95690175b 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/CoreClient.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/CoreClient.java @@ -14,6 +14,7 @@ import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.config_pb.ConfigurationConstantsResponse; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.config_pb_service.ConfigServiceClient; import io.deephaven.javascript.proto.dhinternal.jspb.Map; +import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.client.api.storage.JsStorageService; import io.deephaven.web.client.fu.JsLog; import io.deephaven.web.client.fu.LazyPromise; @@ -29,8 +30,6 @@ import java.util.Objects; import java.util.function.Consumer; -import static io.deephaven.web.client.api.barrage.WebGrpcUtils.CLIENT_OPTIONS; - @JsType(namespace = "dh") public class CoreClient extends HasEventHandling { public static final String EVENT_CONNECT = "connect", @@ -48,13 +47,11 @@ public class CoreClient extends HasEventHandling { private final IdeConnection ideConnection; public CoreClient(String serverUrl, @TsTypeRef(ConnectOptions.class) @JsOptional Object connectOptions) { - ideConnection = new IdeConnection(serverUrl, connectOptions, true); + ideConnection = new IdeConnection(serverUrl, connectOptions); // For now the only real connection is the IdeConnection, so we re-fire the auth token refresh // event here for the UI to listen to - ideConnection.addEventListener(EVENT_REFRESH_TOKEN_UPDATED, event -> { - fireEvent(EVENT_REFRESH_TOKEN_UPDATED, event); - }); + ideConnection.addEventListener(EVENT_REFRESH_TOKEN_UPDATED, this::fireEvent); } private Promise getConfigs(Consumer> rpcCall, @@ -82,20 +79,20 @@ public String getServerUrl() { } public Promise getAuthConfigValues() { - return ideConnection.getConnectOptions().then(options -> { - BrowserHeaders metadata = new BrowserHeaders(); - JsObject.keys(options.headers).forEach((key, index) -> { - metadata.set(key, options.headers.get(key)); - return null; - }); - return getConfigs( - // Explicitly creating a new client, and not passing auth details, so this works pre-connection - c -> new ConfigServiceClient(getServerUrl(), CLIENT_OPTIONS).getAuthenticationConstants( - new AuthenticationConstantsRequest(), - metadata, - c::apply), - AuthenticationConstantsResponse::getConfigValuesMap); + BrowserHeaders metadata = new BrowserHeaders(); + JsPropertyMap headers = ideConnection.getOptions().headers; + JsObject.keys(headers).forEach((key, index) -> { + metadata.set(key, headers.get(key)); + return null; }); + ConfigServiceClient configService = ideConnection.createClient(ConfigServiceClient::new); + return getConfigs( + // Explicitly creating a new client, and not passing auth details, so this works pre-connection + c -> configService.getAuthenticationConstants( + new AuthenticationConstantsRequest(), + metadata, + c::apply), + AuthenticationConstantsResponse::getConfigValuesMap); } public Promise login(@TsTypeRef(LoginCredentials.class) JsPropertyMap credentials) { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/EventFn.java b/web/client-api/src/main/java/io/deephaven/web/client/api/EventFn.java deleted file mode 100644 index a220cc4e7df..00000000000 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/EventFn.java +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending -// -package io.deephaven.web.client.api; - -import elemental2.dom.CustomEvent; -import elemental2.dom.Event; -import jsinterop.annotations.JsFunction; - -/** - */ -@JsFunction -public interface EventFn { - void onEvent(CustomEvent e); -} diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/JsPartitionedTable.java b/web/client-api/src/main/java/io/deephaven/web/client/api/JsPartitionedTable.java index a56f42f08f6..a46a8152802 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/JsPartitionedTable.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/JsPartitionedTable.java @@ -6,9 +6,6 @@ import elemental2.core.JsArray; import elemental2.core.JsObject; import elemental2.core.JsSet; -import elemental2.dom.CustomEvent; -import elemental2.dom.CustomEventInit; -import elemental2.dom.Event; import elemental2.promise.Promise; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.partitionedtable_pb.GetTableRequest; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.partitionedtable_pb.MergeRequest; @@ -18,6 +15,7 @@ import io.deephaven.web.client.api.barrage.WebBarrageUtils; import io.deephaven.web.client.api.barrage.def.ColumnDefinition; import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; +import io.deephaven.web.client.api.event.Event; import io.deephaven.web.client.api.lifecycle.HasLifecycle; import io.deephaven.web.client.api.subscription.SubscriptionTableData; import io.deephaven.web.client.api.subscription.TableSubscription; @@ -112,10 +110,8 @@ public Promise refetch() { fireEvent(EVENT_RECONNECT); return null; }, failure -> { - CustomEventInit init = CustomEventInit.create(); - init.setDetail(failure); unsuppressEvents(); - fireEvent(EVENT_RECONNECTFAILED, init); + fireEvent(EVENT_RECONNECTFAILED, failure); suppressEvents(); return null; }); @@ -141,20 +137,16 @@ private Promise subscribeToBaseTable() { return promise.asPromise(); } - private void handleKeys(Event update) { - // noinspection unchecked - CustomEvent event = (CustomEvent) update; + private void handleKeys(Event update) { // We're only interested in added rows, send an event indicating the new keys that are available - SubscriptionTableData eventData = event.detail; + SubscriptionTableData eventData = update.getDetail(); RangeSet added = eventData.getAdded().getRange(); added.indexIterator().forEachRemaining((long index) -> { // extract the key to use JsArray key = eventData.getColumns().map((c, p1) -> eventData.getData(index, c)); knownKeys.add(key.asList()); - CustomEventInit> init = CustomEventInit.create(); - init.setDetail(key); - fireEvent(EVENT_KEYADDED, init); + fireEvent(EVENT_KEYADDED, key); }); } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/JsTable.java b/web/client-api/src/main/java/io/deephaven/web/client/api/JsTable.java index 0e1828e74b6..aa0721bd546 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/JsTable.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/JsTable.java @@ -8,7 +8,6 @@ import com.vertispan.tsdefs.annotations.TsUnion; import com.vertispan.tsdefs.annotations.TsUnionMember; import elemental2.core.JsArray; -import elemental2.dom.CustomEventInit; import elemental2.promise.IThenable.ThenOnFulfilledCallbackFn; import elemental2.promise.Promise; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.hierarchicaltable_pb.RollupRequest; @@ -1677,9 +1676,7 @@ public void setState(final ClientTableState state) { }); }); } - final CustomEventInit init = CustomEventInit.create(); - init.setDetail(state); - fireEvent(INTERNAL_EVENT_STATECHANGED, init); + fireEvent(INTERNAL_EVENT_STATECHANGED, state); } } @@ -1748,9 +1745,7 @@ public void setSize(double s) { // If the size changed, and we have no subscription active, fire. Otherwise, we want to let the // subscription itself manage this, so that the size changes are synchronized with data changes, // and consumers won't be confused by the table size not matching data. - CustomEventInit event = CustomEventInit.create(); - event.setDetail(s); - fireEvent(JsTable.EVENT_SIZECHANGED, event); + fireEvent(JsTable.EVENT_SIZECHANGED, s); } fireEvent(JsTable.INTERNAL_EVENT_SIZELISTENER); } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/JsTotalsTable.java b/web/client-api/src/main/java/io/deephaven/web/client/api/JsTotalsTable.java index 0bab10ea1d5..6801739b1fe 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/JsTotalsTable.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/JsTotalsTable.java @@ -8,10 +8,11 @@ import com.vertispan.tsdefs.annotations.TsTypeRef; import elemental2.core.JsArray; import elemental2.core.JsString; -import elemental2.dom.CustomEvent; import elemental2.promise.Promise; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.ticket_pb.TypedTicket; import io.deephaven.web.client.api.console.JsVariableType; +import io.deephaven.web.client.api.event.Event; +import io.deephaven.web.client.api.event.EventFn; import io.deephaven.web.client.api.filter.FilterCondition; import io.deephaven.web.client.api.subscription.AbstractTableSubscription; import io.deephaven.web.client.api.subscription.ViewportData; @@ -223,7 +224,7 @@ public boolean removeEventListener(String name, EventFn callback) { } @JsMethod - public Promise> nextEvent(String eventName, Double timeoutInMillis) { + public Promise> nextEvent(String eventName, Double timeoutInMillis) { return wrappedTable.nextEvent(eventName, timeoutInMillis); } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/QueryConnectable.java b/web/client-api/src/main/java/io/deephaven/web/client/api/QueryConnectable.java index 1f4fc572f91..1394069e0b7 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/QueryConnectable.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/QueryConnectable.java @@ -6,10 +6,12 @@ import com.vertispan.tsdefs.annotations.TsIgnore; import elemental2.core.JsArray; import elemental2.core.JsSet; -import elemental2.dom.CustomEventInit; -import elemental2.dom.DomGlobal; import elemental2.promise.Promise; +import io.deephaven.javascript.proto.dhinternal.grpcweb.Grpc; +import io.deephaven.javascript.proto.dhinternal.grpcweb.client.RpcOptions; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.session_pb.TerminationNotificationResponse; +import io.deephaven.web.client.api.event.HasEventHandling; +import io.deephaven.web.client.api.grpc.MultiplexedWebsocketTransport; import io.deephaven.web.client.ide.IdeSession; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.console_pb.*; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.ticket_pb.Ticket; @@ -26,6 +28,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.BiFunction; import static io.deephaven.web.client.ide.IdeConnection.HACK_CONNECTION_FAILURE; import static io.deephaven.web.shared.fu.PromiseLike.CANCELLATION_MESSAGE; @@ -51,9 +54,9 @@ public QueryConnectable() { this.connection = JsLazy.of(() -> new WorkerConnection(this)); } - public abstract Promise getConnectToken(); + public abstract ConnectToken getToken(); - public abstract Promise getConnectOptions(); + public abstract ConnectOptions getOptions(); @Deprecated public void notifyConnectionError(ResponseStreamWrapper.Status status) { @@ -62,12 +65,10 @@ public void notifyConnectionError(ResponseStreamWrapper.Status status) { } notifiedConnectionError = true; - CustomEventInit> event = CustomEventInit.create(); - event.setDetail(JsPropertyMap.of( + fireEvent(HACK_CONNECTION_FAILURE, JsPropertyMap.of( "status", status.getCode(), "details", status.getDetails(), "metadata", status.getMetadata())); - fireEvent(HACK_CONNECTION_FAILURE, event); JsLog.warn( "The event dh.IdeConnection.HACK_CONNECTION_FAILURE is deprecated and will be removed in a later release"); } @@ -244,4 +245,28 @@ public void disconnected() { } public abstract void notifyServerShutdown(TerminationNotificationResponse success); + + public boolean useWebsockets() { + Boolean useWebsockets = getOptions().useWebsockets; + if (useWebsockets == null) { + useWebsockets = getServerUrl().startsWith("http:"); + } + return useWebsockets; + } + + public T createClient(BiFunction constructor) { + return constructor.apply(getServerUrl(), makeRpcOptions()); + } + + public RpcOptions makeRpcOptions() { + RpcOptions options = RpcOptions.create(); + options.setDebug(getOptions().debug); + if (useWebsockets()) { + // Replace with our custom websocket impl, with fallback to the built-in one + options.setTransport(o -> new MultiplexedWebsocketTransport(o, () -> { + Grpc.setDefaultTransport.onInvoke(Grpc.WebsocketTransport.onInvoke()); + })); + } + return options; + } } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/WorkerConnection.java b/web/client-api/src/main/java/io/deephaven/web/client/api/WorkerConnection.java index abe4c914bb0..281d3173fb9 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/WorkerConnection.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/WorkerConnection.java @@ -10,13 +10,13 @@ import elemental2.core.JsSet; import elemental2.core.JsWeakMap; import elemental2.core.Uint8Array; -import elemental2.dom.CustomEventInit; import elemental2.dom.DomGlobal; import elemental2.promise.Promise; import io.deephaven.javascript.proto.dhinternal.arrow.flight.protocol.browserflight_pb_service.BrowserFlightServiceClient; import io.deephaven.javascript.proto.dhinternal.arrow.flight.protocol.flight_pb.FlightData; import io.deephaven.javascript.proto.dhinternal.arrow.flight.protocol.flight_pb_service.FlightServiceClient; import io.deephaven.javascript.proto.dhinternal.browserheaders.BrowserHeaders; +import io.deephaven.javascript.proto.dhinternal.grpcweb.client.ClientRpcOptions; import io.deephaven.javascript.proto.dhinternal.grpcweb.grpc.Code; import io.deephaven.javascript.proto.dhinternal.grpcweb.grpc.UnaryOutput; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.application_pb.FieldInfo; @@ -64,6 +64,7 @@ import io.deephaven.web.client.api.console.JsVariableChanges; import io.deephaven.web.client.api.console.JsVariableDefinition; import io.deephaven.web.client.api.console.JsVariableType; +import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.client.api.grpc.UnaryWithHeaders; import io.deephaven.web.client.api.i18n.JsTimeZone; import io.deephaven.web.client.api.impl.TicketAndPromise; @@ -110,7 +111,6 @@ import java.util.stream.Collectors; import static io.deephaven.web.client.api.CoreClient.EVENT_REFRESH_TOKEN_UPDATED; -import static io.deephaven.web.client.api.barrage.WebGrpcUtils.CLIENT_OPTIONS; /** * Non-exported class, manages the connection to a given worker server. Exported types like QueryInfo and Table will @@ -159,6 +159,7 @@ private enum State { } private final QueryConnectable info; + private final ClientRpcOptions options = ClientRpcOptions.create(); private final ClientConfiguration config; private final ReconnectState newSessionReconnect; private final TableReviver reviver; @@ -204,28 +205,22 @@ private enum State { public WorkerConnection(QueryConnectable info) { this.info = info; + this.config = new ClientConfiguration(); state = State.Connecting; this.reviver = new TableReviver(this); - sessionServiceClient = new SessionServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - tableServiceClient = new TableServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - consoleServiceClient = new ConsoleServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - flightServiceClient = new FlightServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - applicationServiceClient = - new ApplicationServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - browserFlightServiceClient = - new BrowserFlightServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - inputTableServiceClient = - new InputTableServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - objectServiceClient = new ObjectServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - partitionedTableServiceClient = - new PartitionedTableServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - storageServiceClient = new StorageServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - configServiceClient = new ConfigServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - hierarchicalTableServiceClient = - new HierarchicalTableServiceClient(info.getServerUrl(), CLIENT_OPTIONS); - - // builder.setConnectionErrorHandler(msg -> info.failureHandled(String.valueOf(msg))); + sessionServiceClient = info.createClient(SessionServiceClient::new); + tableServiceClient = info.createClient(TableServiceClient::new); + consoleServiceClient = info.createClient(ConsoleServiceClient::new); + flightServiceClient = info.createClient(FlightServiceClient::new); + applicationServiceClient = info.createClient(ApplicationServiceClient::new); + browserFlightServiceClient = info.createClient(BrowserFlightServiceClient::new); + inputTableServiceClient = info.createClient(InputTableServiceClient::new); + objectServiceClient = info.createClient(ObjectServiceClient::new); + partitionedTableServiceClient = info.createClient(PartitionedTableServiceClient::new); + storageServiceClient = info.createClient(StorageServiceClient::new); + configServiceClient = info.createClient(ConfigServiceClient::new); + hierarchicalTableServiceClient = info.createClient(HierarchicalTableServiceClient::new); newSessionReconnect = new ReconnectState(this::connectToWorker); @@ -253,21 +248,13 @@ private void connectToWorker() { if (metadata().has(FLIGHT_AUTH_HEADER_NAME)) { return authUpdate().then(ignore -> Promise.resolve(Boolean.FALSE)); } - return Promise.all( - info.getConnectToken().then(authToken -> { - // set the proposed initial token and make the first call - metadata.set(FLIGHT_AUTH_HEADER_NAME, - (authToken.getType() + " " + authToken.getValue()).trim()); - return Promise.resolve(authToken); - }), - info.getConnectOptions().then(options -> { - // set other specified headers, if any - JsObject.keys(options.headers).forEach((key, index) -> { - metadata.set(key, options.headers.get(key)); - return null; - }); - return Promise.resolve(options); - })).then(ignore -> authUpdate()).then(ignore -> Promise.resolve(Boolean.TRUE)); + metadata.set(FLIGHT_AUTH_HEADER_NAME, + (info.getToken().getType() + " " + info.getToken().getValue()).trim()); + JsObject.keys(info.getOptions().headers).forEach((key, index) -> { + metadata.set(key, info.getOptions().headers.get(key)); + return null; + }); + return authUpdate().then(ignore -> Promise.resolve(Boolean.TRUE)); }).then(newSession -> { // subscribe to fatal errors subscribeToTerminationNotification(); @@ -463,7 +450,9 @@ private Promise authUpdate() { scheduledAuthUpdate = null; } return UnaryWithHeaders.call( - this, ConfigService.GetConfigurationConstants, new ConfigurationConstantsRequest()) + info.getServerUrl(), + metadata(), info.makeRpcOptions(), ConfigService.GetConfigurationConstants, + new ConfigurationConstantsRequest()) .then(result -> { BrowserHeaders headers = result.getHeaders(); // unchecked cast is required here due to "aliasing" in ts/webpack resulting in BrowserHeaders != @@ -475,9 +464,8 @@ this, ConfigService.GetConfigurationConstants, new ConfigurationConstantsRequest if (!existing.getAt(0).equals(authorization.getAt(0))) { // use this new token metadata().set(FLIGHT_AUTH_HEADER_NAME, authorization); - CustomEventInit init = CustomEventInit.create(); - init.setDetail(new JsRefreshToken(authorization.getAt(0), sessionTimeoutMs)); - info.fireEvent(EVENT_REFRESH_TOKEN_UPDATED, init); + info.fireEvent(EVENT_REFRESH_TOKEN_UPDATED, + new JsRefreshToken(authorization.getAt(0), sessionTimeoutMs)); } } @@ -1002,7 +990,7 @@ public BrowserHeaders metadata() { } public BiDiStream.Factory streamFactory() { - return new BiDiStream.Factory<>(this::metadata, config::newTicketInt); + return new BiDiStream.Factory<>(info.useWebsockets(), this::metadata, config::newTicketInt); } public Promise newTable(String[] columnNames, String[] types, Object[][] data, String userTimeZone, diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebGrpcUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebGrpcUtils.java deleted file mode 100644 index 5d6ea33527e..00000000000 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebGrpcUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending -// -package io.deephaven.web.client.api.barrage; - -import elemental2.dom.DomGlobal; -import io.deephaven.javascript.proto.dhinternal.grpcweb.Grpc; -import io.deephaven.web.client.api.grpc.MultiplexedWebsocketTransport; -import jsinterop.base.JsPropertyMap; - -/** - * Holds some static details, must be initialized before any calls can be made. When our gRPC-web client is redone to - * support interceptors/etc, this will likely no longer need to exist - */ -public class WebGrpcUtils { - /** - * One place to change all debug flags in the app. - */ - public static final Object CLIENT_OPTIONS = JsPropertyMap.of( - "debug", false); - - /** - * True if https isn't available (as a proxy for h2). - */ - public static final boolean USE_WEBSOCKETS; - - static { - // TODO configurable, let us support this even when ssl? - if (DomGlobal.window.location.protocol.equals("http:")) { - USE_WEBSOCKETS = true; - Grpc.setDefaultTransport.onInvoke(options -> new MultiplexedWebsocketTransport(options, () -> { - Grpc.setDefaultTransport.onInvoke(Grpc.WebsocketTransport.onInvoke()); - })); - } else { - USE_WEBSOCKETS = false; - } - } -} diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/stream/BiDiStream.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/stream/BiDiStream.java index 758fe62f20c..5febd0a9fa4 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/stream/BiDiStream.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/stream/BiDiStream.java @@ -16,8 +16,6 @@ import java.util.function.IntSupplier; import java.util.function.Supplier; -import static io.deephaven.web.client.api.barrage.WebGrpcUtils.USE_WEBSOCKETS; - public abstract class BiDiStream { public interface BiDiStreamFactory { /** @@ -38,10 +36,12 @@ public interface NextStreamMessageFactory { void nextStreamMessage(Req nextPayload, BrowserHeaders headers, JsBiConsumer callback); } public static class Factory { + private final boolean useWebsockets; private final Supplier headers; private final IntSupplier nextIntTicket; - public Factory(Supplier headers, IntSupplier nextIntTicket) { + public Factory(boolean useWebsockets, Supplier headers, IntSupplier nextIntTicket) { + this.useWebsockets = useWebsockets; this.headers = headers; this.nextIntTicket = nextIntTicket; } @@ -51,7 +51,7 @@ public BiDiStream create( OpenStreamFactory openEmulatedStream, NextStreamMessageFactory nextEmulatedStream, ReqT emptyReq) { - if (USE_WEBSOCKETS) { + if (useWebsockets) { return websocket(bidirectionalStream.openBiDiStream(headers.get())); } else { return new EmulatedBiDiStream<>( diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/batch/RequestBatcher.java b/web/client-api/src/main/java/io/deephaven/web/client/api/batch/RequestBatcher.java index 9d10b006c81..10631549a4c 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/batch/RequestBatcher.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/batch/RequestBatcher.java @@ -3,7 +3,6 @@ // package io.deephaven.web.client.api.batch; -import elemental2.dom.CustomEventInit; import elemental2.promise.Promise; import elemental2.promise.Promise.PromiseExecutorCallbackFn.RejectCallbackFn; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.ticket_pb.Ticket; @@ -341,7 +340,6 @@ public Promise sendRequest() { } private void failTable(JsTable t, String failureMessage) { - final CustomEventInit event = CustomEventInit.create(); ClientTableState best = t.state(); for (ClientTableState state : best.reversed()) { if (allStates().anyMatch(state::equals)) { @@ -350,9 +348,6 @@ private void failTable(JsTable t, String failureMessage) { } } - event.setDetail(JsPropertyMap.of( - "errorMessage", failureMessage, - "configuration", best.toJs())); try { t.rollback(); } catch (Exception e) { @@ -360,7 +355,9 @@ private void failTable(JsTable t, String failureMessage) { "An exception occurred trying to rollback the table. This means that there will be no ticking data until the table configuration is applied again in a way that makes sense. See IDS-5199 for more detail.", e); } - t.fireEvent(CoreClient.EVENT_REQUEST_FAILED, event); + t.fireEvent(CoreClient.EVENT_REQUEST_FAILED, JsPropertyMap.of( + "errorMessage", failureMessage, + "configuration", best.toJs())); } private void failed(RejectCallbackFn reject, String fail) { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/event/Event.java b/web/client-api/src/main/java/io/deephaven/web/client/api/event/Event.java new file mode 100644 index 00000000000..8749a68c755 --- /dev/null +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/event/Event.java @@ -0,0 +1,38 @@ +// +// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending +// +package io.deephaven.web.client.api.event; + +import com.vertispan.tsdefs.annotations.TsInterface; +import com.vertispan.tsdefs.annotations.TsName; +import jsinterop.annotations.JsNullable; +import jsinterop.annotations.JsProperty; + +/** + * Similar to the browser {@code CustomEvent} type, this class holds only the type of the event, and optionally some + * details about the event. + * + * @param the type of the event detail + */ +@TsInterface +@TsName(namespace = "dh") +public class Event { + private final String type; + private final T detail; + + public Event(String type, T detail) { + this.type = type; + this.detail = detail; + } + + @JsProperty + public String getType() { + return type; + } + + @JsProperty + @JsNullable + public T getDetail() { + return detail; + } +} diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/event/EventFn.java b/web/client-api/src/main/java/io/deephaven/web/client/api/event/EventFn.java new file mode 100644 index 00000000000..314c1d26795 --- /dev/null +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/event/EventFn.java @@ -0,0 +1,15 @@ +// +// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending +// +package io.deephaven.web.client.api.event; + +import jsinterop.annotations.JsFunction; + +/** + * Event handler function. In JS/TS, this will only show up as a function type when used, but these docs will never be + * visible. + */ +@JsFunction +public interface EventFn { + void onEvent(Event e); +} diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/HasEventHandling.java b/web/client-api/src/main/java/io/deephaven/web/client/api/event/HasEventHandling.java similarity index 81% rename from web/client-api/src/main/java/io/deephaven/web/client/api/HasEventHandling.java rename to web/client-api/src/main/java/io/deephaven/web/client/api/event/HasEventHandling.java index 16cb9bfe549..c4fc78234d9 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/HasEventHandling.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/event/HasEventHandling.java @@ -1,16 +1,15 @@ // // Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending // -package io.deephaven.web.client.api; +package io.deephaven.web.client.api.event; import com.vertispan.tsdefs.annotations.TsInterface; import com.vertispan.tsdefs.annotations.TsName; import elemental2.core.JsArray; import elemental2.core.JsObject; -import elemental2.dom.CustomEvent; -import elemental2.dom.CustomEventInit; import elemental2.dom.DomGlobal; import elemental2.promise.Promise; +import io.deephaven.web.client.api.CoreClient; import io.deephaven.web.client.fu.JsLog; import io.deephaven.web.client.fu.LazyPromise; import io.deephaven.web.shared.fu.RemoverFn; @@ -70,7 +69,7 @@ final class WrappedCallback implements EventFn { private EventFn self; @Override - public void onEvent(CustomEvent e) { + public void onEvent(Event e) { removeEventListener(name, self); callback.onEvent(e); } @@ -100,14 +99,14 @@ public void addEventListenerOneShot(EventPair... pairs) { return; } seen[0] = true; - pair.callback.onEvent((CustomEvent) e); + pair.callback.onEvent((Event) e); }); } } @JsMethod - public Promise> nextEvent(String eventName, @JsOptional Double timeoutInMillis) { - LazyPromise> promise = new LazyPromise<>(); + public Promise> nextEvent(String eventName, @JsOptional Double timeoutInMillis) { + LazyPromise> promise = new LazyPromise<>(); addEventListenerOneShot(eventName, promise::succeed); @@ -158,31 +157,26 @@ public boolean removeEventListener(String name, EventFn callback) { } public void fireEvent(String type) { - fireEvent(type, CustomEventInit.create()); + fireEvent(new Event<>(type, null)); } - public void fireEventWithDetail(String type, @DoNotAutobox T detail) { - final CustomEventInit evt = CustomEventInit.create(); - evt.setDetail(detail); - fireEvent(type, evt); + public void fireEvent(String type, @DoNotAutobox T detail) { + fireEvent(new Event<>(type, detail)); } - public void fireEvent(String type, CustomEventInit init) { - fireEvent(type, new CustomEvent<>(type, init)); - } - - public void fireEvent(String type, CustomEvent e) { + public void fireEvent(Event e) { if (suppress) { - JsLog.debug("Event suppressed", type, e); + JsLog.debug("Event suppressed", e.getType(), e); return; } - if (map.has(e.type)) { - final JsArray> callbacks = Js.cast(JsArray.from((JsArrayLike>) map.get(e.type))); + if (map.has(e.getType())) { + final JsArray> callbacks = Js.cast(JsArray.from((JsArrayLike>) map.get(e.getType()))); callbacks.forEach((item, ind) -> { try { item.onEvent(e); } catch (Throwable t) { - DomGlobal.console.error(logPrefix() + "User callback (", item, ") of type ", type, " failed: ", t); + DomGlobal.console.error(logPrefix() + "User callback (", item, ") of type ", e.getType(), + " failed: ", t); t.printStackTrace(); } return true; @@ -198,11 +192,11 @@ public void fireCriticalEvent(String type) { } } - public void fireCriticalEvent(String type, CustomEventInit init) { + public void fireCriticalEvent(String type, T detail) { if (hasListeners(type)) { - fireEvent(type, init); + fireEvent(type, detail); } else { - DomGlobal.console.error(logPrefix(), init.getDetail(), + DomGlobal.console.error(logPrefix(), detail, "(to prevent this log message, handle the " + type + " event)"); } } @@ -211,9 +205,7 @@ public void failureHandled(String failure) { if (failure == null) { return; } - final CustomEventInit event = CustomEventInit.create(); - event.setDetail(failure); - fireCriticalEvent(CoreClient.EVENT_REQUEST_FAILED, event); + fireCriticalEvent(CoreClient.EVENT_REQUEST_FAILED, failure); } public void suppressEvents() { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/grpc/UnaryWithHeaders.java b/web/client-api/src/main/java/io/deephaven/web/client/api/grpc/UnaryWithHeaders.java index 2b82a40d2b8..4b6316631b5 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/grpc/UnaryWithHeaders.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/grpc/UnaryWithHeaders.java @@ -5,31 +5,36 @@ import elemental2.promise.IThenable; import elemental2.promise.Promise; +import io.deephaven.javascript.proto.dhinternal.browserheaders.BrowserHeaders; import io.deephaven.javascript.proto.dhinternal.grpcweb.Grpc; +import io.deephaven.javascript.proto.dhinternal.grpcweb.client.RpcOptions; import io.deephaven.javascript.proto.dhinternal.grpcweb.grpc.Code; import io.deephaven.javascript.proto.dhinternal.grpcweb.unary.UnaryOutput; import io.deephaven.javascript.proto.dhinternal.grpcweb.unary.UnaryRpcOptions; -import io.deephaven.web.client.api.WorkerConnection; public class UnaryWithHeaders { /** * Improbable-eng's grpc-web implementation doesn't pass headers to api callers - this changes the contract a bit so * that we can get a typed UnaryOutput with the headers/trailers intact. - * - * @param connection provides access to the metadata and the server url + * + * @param host the server to connect to + * @param metadata gRPC metadata to set on the request + * @param options options to use when sending the request * @param methodDescriptor the service method to invoke * @return a promise that will resolve to the response plus headers/trailers, or reject with the headers/trailers * @param type of the message object */ - public static Promise> call(WorkerConnection connection, Object methodDescriptor, + public static Promise> call(String host, BrowserHeaders metadata, RpcOptions options, + Object methodDescriptor, Req request) { return new Promise<>((resolve, reject) -> { UnaryRpcOptions props = UnaryRpcOptions.create(); - props.setHost(connection.configServiceClient().serviceHost); - props.setMetadata(connection.metadata()); - props.setTransport(null);// ts doesn't expose these two, stick with defaults for now - props.setDebug(false); + props.setRequest(request); + props.setHost(host); + props.setMetadata(metadata); + props.setTransport(options.getTransport()); + props.setDebug(options.isDebug()); props.setOnEnd(response -> { if (response.getStatus() != Code.OK) { reject.onInvoke(response); @@ -37,7 +42,6 @@ public static Promise> call(WorkerConnection connect resolve.onInvoke((IThenable>) response); } }); - props.setRequest(request); Grpc.unary.onInvoke(methodDescriptor, props); }); } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/lifecycle/HasLifecycle.java b/web/client-api/src/main/java/io/deephaven/web/client/api/lifecycle/HasLifecycle.java index 3fd9f2c4923..84e54e63bca 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/lifecycle/HasLifecycle.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/lifecycle/HasLifecycle.java @@ -3,11 +3,9 @@ // package io.deephaven.web.client.api.lifecycle; -import elemental2.dom.CustomEvent; -import elemental2.dom.CustomEventInit; import elemental2.promise.Promise; -import io.deephaven.web.client.api.HasEventHandling; import io.deephaven.web.client.api.JsTable; +import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.client.fu.JsLog; import static io.deephaven.web.client.api.JsTable.EVENT_RECONNECTFAILED; @@ -28,10 +26,8 @@ public abstract class HasLifecycle extends HasEventHandling { */ public void die(Object error) { JsLog.debug("Die!", this, error); - final CustomEventInit init = CustomEventInit.create(); - init.setDetail(error); unsuppressEvents(); - fireEvent(EVENT_RECONNECTFAILED, init); + fireEvent(EVENT_RECONNECTFAILED, error); suppressEvents(); } @@ -78,9 +74,9 @@ public Promise nextReconnect() { addEventListenerOneShot( HasEventHandling.EventPair.of(JsTable.EVENT_RECONNECT, event -> resolve.onInvoke((Void) null)), HasEventHandling.EventPair.of(JsTable.EVENT_DISCONNECT, - event -> reject.onInvoke(((CustomEvent) event).detail)), + event -> reject.onInvoke(event.getDetail())), HasEventHandling.EventPair.of(EVENT_RECONNECTFAILED, - event -> reject.onInvoke(((CustomEvent) event).detail))); + event -> reject.onInvoke(event.getDetail()))); }); } } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/subscription/AbstractTableSubscription.java b/web/client-api/src/main/java/io/deephaven/web/client/api/subscription/AbstractTableSubscription.java index 55ea471c752..f690d3df66f 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/subscription/AbstractTableSubscription.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/subscription/AbstractTableSubscription.java @@ -6,7 +6,6 @@ import com.google.flatbuffers.FlatBufferBuilder; import com.vertispan.tsdefs.annotations.TsIgnore; import elemental2.core.JsArray; -import elemental2.dom.CustomEventInit; import io.deephaven.barrage.flatbuf.BarrageMessageType; import io.deephaven.barrage.flatbuf.BarrageSubscriptionRequest; import io.deephaven.extensions.barrage.BarrageSubscriptionOptions; @@ -14,7 +13,6 @@ import io.deephaven.javascript.proto.dhinternal.arrow.flight.protocol.flight_pb.FlightData; import io.deephaven.web.client.api.Column; import io.deephaven.web.client.api.Format; -import io.deephaven.web.client.api.HasEventHandling; import io.deephaven.web.client.api.JsRangeSet; import io.deephaven.web.client.api.LongWrapper; import io.deephaven.web.client.api.TableData; @@ -26,6 +24,7 @@ import io.deephaven.web.client.api.barrage.data.WebBarrageSubscription; import io.deephaven.web.client.api.barrage.stream.BiDiStream; import io.deephaven.web.client.api.barrage.stream.ResponseStreamWrapper; +import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.client.fu.JsSettings; import io.deephaven.web.client.state.ClientTableState; import io.deephaven.web.shared.data.RangeSet; @@ -243,9 +242,7 @@ protected void notifyUpdate(RangeSet rowsAdded, RangeSet rowsRemoved, RangeSet t rowsRemoved, totalMods, shifted); - CustomEventInit event = CustomEventInit.create(); - event.setDetail(detail); - fireEvent(TableSubscription.EVENT_UPDATED, event); + fireEvent(TableSubscription.EVENT_UPDATED, detail); } public static class SubscriptionRow implements TableData.Row { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/subscription/TableViewportSubscription.java b/web/client-api/src/main/java/io/deephaven/web/client/api/subscription/TableViewportSubscription.java index d33c47f51c7..e745899cb5a 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/subscription/TableViewportSubscription.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/subscription/TableViewportSubscription.java @@ -7,8 +7,6 @@ import com.vertispan.tsdefs.annotations.TsName; import com.vertispan.tsdefs.annotations.TsTypeRef; import elemental2.core.JsArray; -import elemental2.dom.CustomEvent; -import elemental2.dom.CustomEventInit; import elemental2.promise.Promise; import io.deephaven.barrage.flatbuf.BarrageMessageType; import io.deephaven.barrage.flatbuf.BarrageSnapshotRequest; @@ -28,6 +26,7 @@ import io.deephaven.web.client.api.barrage.WebBarrageUtils; import io.deephaven.web.client.api.barrage.data.WebBarrageSubscription; import io.deephaven.web.client.api.barrage.stream.BiDiStream; +import io.deephaven.web.client.api.event.Event; import io.deephaven.web.client.fu.JsLog; import io.deephaven.web.client.fu.LazyPromise; import io.deephaven.web.client.state.ClientTableState; @@ -137,16 +136,14 @@ protected void sendFirstSubscriptionRequest() { protected void notifyUpdate(RangeSet rowsAdded, RangeSet rowsRemoved, RangeSet totalMods, ShiftedRange[] shifted) { // viewport subscriptions are sometimes required to notify of size change events if (rowsAdded.size() != rowsRemoved.size() && originalActive) { - fireEventWithDetail(JsTable.EVENT_SIZECHANGED, size()); + fireEvent(JsTable.EVENT_SIZECHANGED, size()); } UpdateEventData detail = new ViewportEventData(barrageSubscription, rowStyleColumn, getColumns(), rowsAdded, rowsRemoved, totalMods, shifted); detail.setOffset(this.viewportRowSet.getFirstRow()); this.viewportData = detail; - CustomEventInit event = CustomEventInit.create(); - event.setDetail(detail); - refire(new CustomEvent<>(EVENT_UPDATED, event)); + refire(new Event<>(EVENT_UPDATED, detail)); if (hasListeners(EVENT_ROWADDED) || hasListeners(EVENT_ROWREMOVED) || hasListeners(EVENT_ROWUPDATED)) { RangeSet modifiedCopy = totalMods.copy(); @@ -175,9 +172,8 @@ protected void notifyUpdate(RangeSet rowsAdded, RangeSet rowsRemoved, RangeSet t private void fireLegacyEventOnRowsetEntries(String eventName, UpdateEventData updateEventData, RangeSet rowset) { if (hasListeners(eventName)) { rowset.indexIterator().forEachRemaining((long row) -> { - CustomEventInit> addedEvent = CustomEventInit.create(); - addedEvent.setDetail(wrap((SubscriptionRow) updateEventData.getRows().getAt((int) row), (int) row)); - fireEvent(eventName, addedEvent); + JsPropertyMap detail = wrap((SubscriptionRow) updateEventData.getRows().getAt((int) row), (int) row); + fireEvent(eventName, detail); }); } } @@ -188,26 +184,16 @@ private static JsPropertyMap wrap(SubscriptionRow rowObj, int row) { @Override public void fireEvent(String type) { - refire(new CustomEvent<>(type)); + refire(new Event<>(type, null)); } @Override - public void fireEventWithDetail(String type, T detail) { - CustomEventInit init = CustomEventInit.create(); - init.setDetail(detail); - refire(new CustomEvent(type, init)); + public void fireEvent(String type, T detail) { + refire(new Event(type, detail)); } @Override - public void fireEvent(String type, CustomEventInit init) { - refire(new CustomEvent(type, init)); - } - - @Override - public void fireEvent(String type, CustomEvent e) { - if (!type.equals(e.type)) { - throw new IllegalArgumentException(type + " != " + e.type); - } + public void fireEvent(Event e) { refire(e); } @@ -228,16 +214,16 @@ public boolean hasListeners(String name) { * @param e the event to fire * @param the type of the custom event data */ - private void refire(CustomEvent e) { + private void refire(Event e) { // explicitly calling super.fireEvent to avoid calling ourselves recursively - super.fireEvent(e.type, e); + super.fireEvent(e); if (originalActive && initialState == original.state()) { // When these fail to match, it probably means that the original's state was paused, but we're still // holding on to it. Since we haven't been internalClose()d yet, that means we're still waiting for // the new state to resolve or fail, so we can be restored, or stopped. In theory, we should put this // assert back, and make the pause code also tell us to pause. // assert initialState == original.state() : "Table owning this viewport subscription forgot to release it"; - original.fireEvent(e.type, e); + original.fireEvent(e); } } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsTreeTable.java b/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsTreeTable.java index f2825e8fac7..483d9465fc4 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsTreeTable.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/tree/JsTreeTable.java @@ -10,8 +10,6 @@ import elemental2.core.JsArray; import elemental2.core.JsObject; import elemental2.core.Uint8Array; -import elemental2.dom.CustomEvent; -import elemental2.dom.CustomEventInit; import elemental2.dom.DomGlobal; import elemental2.promise.IThenable; import elemental2.promise.Promise; @@ -31,6 +29,7 @@ import io.deephaven.web.client.api.barrage.def.ColumnDefinition; import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; import io.deephaven.web.client.api.barrage.stream.ResponseStreamWrapper; +import io.deephaven.web.client.api.event.Event; import io.deephaven.web.client.api.filter.FilterCondition; import io.deephaven.web.client.api.impl.TicketAndPromise; import io.deephaven.web.client.api.lifecycle.HasLifecycle; @@ -574,9 +573,7 @@ protected void notifyUpdate(RangeSet rowsAdded, RangeSet rowsRemoved, RangeSet t new TreeViewportDataImpl(barrageSubscription, rowStyleColumn, getColumns(), rowsAdded, rowsRemoved, totalMods, shifted); detail.setOffset(this.serverViewport.getFirstRow()); - CustomEventInit event = CustomEventInit.create(); - event.setDetail(detail); - fireEvent(EVENT_UPDATED, event); + fireEvent(EVENT_UPDATED, detail); } } @@ -660,9 +657,9 @@ private void replaceSubscription(RebuildStep step) { TreeSubscription subscription = new TreeSubscription(state, connection); subscription.addEventListener(TreeSubscription.EVENT_UPDATED, - (CustomEvent data) -> { + (Event data) -> { TreeSubscription.TreeViewportDataImpl detail = - (TreeSubscription.TreeViewportDataImpl) data.detail; + (TreeSubscription.TreeViewportDataImpl) data.getDetail(); handleUpdate(nextSort, nextFilters, detail, alwaysFireEvent); }); @@ -705,9 +702,7 @@ private void handleUpdate(List nextSort, List nextFilters this.filters = nextFilters; if (fireEvent) { - CustomEventInit updatedEvent = CustomEventInit.create(); - updatedEvent.setDetail(viewportData); - fireEvent(EVENT_UPDATED, updatedEvent); + fireEvent(EVENT_UPDATED, viewportData); } } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/JsWidget.java b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/JsWidget.java index e0a35c53791..68d55d86c34 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/JsWidget.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/JsWidget.java @@ -10,15 +10,14 @@ import elemental2.core.ArrayBufferView; import elemental2.core.JsArray; import elemental2.core.Uint8Array; -import elemental2.dom.CustomEventInit; import elemental2.promise.Promise; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.object_pb.*; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.ticket_pb.Ticket; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.ticket_pb.TypedTicket; -import io.deephaven.web.client.api.HasEventHandling; import io.deephaven.web.client.api.ServerObject; import io.deephaven.web.client.api.WorkerConnection; import io.deephaven.web.client.api.barrage.stream.BiDiStream; +import io.deephaven.web.client.api.event.HasEventHandling; import jsinterop.annotations.JsMethod; import jsinterop.annotations.JsOptional; import jsinterop.annotations.JsOverlay; @@ -153,9 +152,7 @@ public Promise refetch() { hasFetched = true; resolve.onInvoke(this); } else { - CustomEventInit messageEvent = CustomEventInit.create(); - messageEvent.setDetail(new EventDetails(res.getData(), responseObjects)); - fireEvent(EVENT_MESSAGE, messageEvent); + fireEvent(EVENT_MESSAGE, new EventDetails(res.getData(), responseObjects)); } }); messageStream.onStatus(status -> { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/FigureSubscription.java b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/FigureSubscription.java index cd156e01a98..166d133aecf 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/FigureSubscription.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/FigureSubscription.java @@ -3,7 +3,6 @@ // package io.deephaven.web.client.api.widget.plot; -import elemental2.dom.CustomEventInit; import elemental2.promise.Promise; import io.deephaven.web.client.api.DateWrapper; import io.deephaven.web.client.api.JsTable; @@ -82,25 +81,20 @@ public Set replaceSeries(final Set replacements) { // For each of the series in copy, if this subscription is downsampled we need to notify of this fact. if (downsampleAxisRange != null) { - CustomEventInit init = CustomEventInit.create(); - init.setDetail(replacements.toArray()); - figure.fireEvent(JsFigure.EVENT_DOWNSAMPLESTARTED, init); + figure.fireEvent(JsFigure.EVENT_DOWNSAMPLESTARTED, replacements.toArray()); } if (firstEventFired) { // Next, if any data has loaded, regardless of downsample state, we need to fire an update event for those // series - CustomEventInit event = CustomEventInit.create(); - event.setDetail(new DataUpdateEvent(replacements.toArray(new JsSeries[0]), currentData, null)); - figure.fireEvent(JsFigure.EVENT_UPDATED, event); + figure.fireEvent(JsFigure.EVENT_UPDATED, + new DataUpdateEvent(replacements.toArray(new JsSeries[0]), currentData, null)); // Finally, if data was loaded and also the subscription is downsampled, we need to notify that the // downsample // is complete if (downsampleAxisRange != null) { - CustomEventInit successInit = CustomEventInit.create(); - successInit.setDetail(replacements.toArray()); - figure.fireEvent(JsFigure.EVENT_DOWNSAMPLEFINISHED, successInit); + figure.fireEvent(JsFigure.EVENT_DOWNSAMPLEFINISHED, replacements.toArray()); } } @@ -211,9 +205,7 @@ public void subscribe() { } else { zoomRange = null; } - CustomEventInit init = CustomEventInit.create(); - init.setDetail(includedSeries.toArray()); - figure.fireEvent(JsFigure.EVENT_DOWNSAMPLESTARTED, init); + figure.fireEvent(JsFigure.EVENT_DOWNSAMPLESTARTED, includedSeries.toArray()); Promise downsampled = tablePromise.then(t -> t .downsample(zoomRange, downsampleParams.getPixelCount(), downsampleAxisRange.getxCol(), @@ -259,21 +251,18 @@ private Promise subscribe(final Promise tablePromise sub.addEventListener(TableSubscription.EVENT_UPDATED, e -> { // refire with specifics for the columns that we're watching here, after updating data arrays AbstractTableSubscription.SubscriptionEventData subscriptionUpdateData = - (AbstractTableSubscription.SubscriptionEventData) e.detail; + (AbstractTableSubscription.SubscriptionEventData) e.getDetail(); currentData.update(subscriptionUpdateData); - CustomEventInit event = CustomEventInit.create(); - event.setDetail(new DataUpdateEvent(includedSeries.toArray(new JsSeries[0]), currentData, - subscriptionUpdateData)); - figure.fireEvent(JsFigure.EVENT_UPDATED, event); + figure.fireEvent(JsFigure.EVENT_UPDATED, + new DataUpdateEvent(includedSeries.toArray(new JsSeries[0]), currentData, + subscriptionUpdateData)); if (!firstEventFired) { firstEventFired = true; if (downsampleAxisRange != null) { - CustomEventInit successInit = CustomEventInit.create(); - successInit.setDetail(includedSeries.toArray()); - figure.fireEvent(JsFigure.EVENT_DOWNSAMPLEFINISHED, successInit); + figure.fireEvent(JsFigure.EVENT_DOWNSAMPLEFINISHED, includedSeries.toArray()); } } }); diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsChart.java b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsChart.java index 989ecd8982d..51649f4d39b 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsChart.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsChart.java @@ -7,7 +7,7 @@ import elemental2.core.JsArray; import elemental2.core.JsObject; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.console_pb.figuredescriptor.ChartDescriptor; -import io.deephaven.web.client.api.HasEventHandling; +import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.client.api.widget.plot.enums.JsChartType; import jsinterop.annotations.JsIgnore; import jsinterop.annotations.JsNullable; diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsFigure.java b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsFigure.java index f16ea87f6fd..c3cd507f37c 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsFigure.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsFigure.java @@ -5,7 +5,6 @@ import elemental2.core.JsArray; import elemental2.core.JsObject; -import elemental2.dom.CustomEventInit; import elemental2.promise.Promise; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.console_pb.FigureDescriptor; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.console_pb.figuredescriptor.AxisDescriptor; @@ -236,10 +235,8 @@ public Promise refetch() { final FigureFetchError fetchError = new FigureFetchError(LazyPromise.ofObject(err), this.descriptor != null ? this.descriptor.getErrorsList() : new JsArray<>()); // noinspection unchecked - final CustomEventInit init = CustomEventInit.create(); - init.setDetail(fetchError); unsuppressEvents(); - fireEvent(EVENT_RECONNECTFAILED, init); + fireEvent(EVENT_RECONNECTFAILED, fetchError); suppressEvents(); // noinspection unchecked,rawtypes @@ -269,10 +266,8 @@ public void reconnect() { enqueueSubscriptionCheck(); return null; }, failure -> { - CustomEventInit init = CustomEventInit.create(); - init.setDetail(failure); unsuppressEvents(); - fireEvent(EVENT_RECONNECTFAILED, init); + fireEvent(EVENT_RECONNECTFAILED, failure); suppressEvents(); return null; }); @@ -362,16 +357,14 @@ public void unsubscribe() { @JsIgnore public void downsampleNeeded(String message, Set series, double tableSize) { - CustomEventInit failInit = CustomEventInit.create(); - failInit.setDetail(JsPropertyMap.of("series", series, "message", message, "size", tableSize)); - fireEvent(EVENT_DOWNSAMPLENEEDED, failInit); + fireEvent(EVENT_DOWNSAMPLENEEDED, + JsPropertyMap.of("series", series, "message", message, "size", tableSize)); } @JsIgnore public void downsampleFailed(String message, Set series, double tableSize) { - CustomEventInit failInit = CustomEventInit.create(); - failInit.setDetail(JsPropertyMap.of("series", series, "message", message, "size", tableSize)); - fireEvent(EVENT_DOWNSAMPLEFAILED, failInit); + fireEvent(EVENT_DOWNSAMPLEFAILED, + JsPropertyMap.of("series", series, "message", message, "size", tableSize)); } private void updateSubscriptions() { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsFigureFactory.java b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsFigureFactory.java index 0b2dd4109ca..83360061f78 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsFigureFactory.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsFigureFactory.java @@ -5,7 +5,6 @@ import com.vertispan.tsdefs.annotations.TsTypeRef; import elemental2.core.JsArray; -import elemental2.dom.CustomEventInit; import elemental2.promise.Promise; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.console_pb.FigureDescriptor; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.console_pb.figuredescriptor.*; @@ -82,9 +81,7 @@ private static Promise create(JsFigureDescriptor descriptor) { figure.fireEvent(JsFigure.EVENT_RECONNECT); figure.enqueueSubscriptionCheck(); } catch (JsFigure.FigureSourceException e) { - final CustomEventInit init = CustomEventInit.create(); - init.setDetail(e); - figure.fireEvent(JsFigure.EVENT_RECONNECTFAILED, init); + figure.fireEvent(JsFigure.EVENT_RECONNECTFAILED, e); } })); removerFns @@ -94,9 +91,7 @@ private static Promise create(JsFigureDescriptor descriptor) { } figure.unsubscribe(); - final CustomEventInit init = CustomEventInit.create(); - init.setDetail(err); - figure.fireEvent(JsFigure.EVENT_RECONNECTFAILED, init); + figure.fireEvent(JsFigure.EVENT_RECONNECTFAILED, err); })); } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsMultiSeries.java b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsMultiSeries.java index 482bb9e281f..f377dc99e0c 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsMultiSeries.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/JsMultiSeries.java @@ -6,12 +6,9 @@ import com.vertispan.tsdefs.annotations.TsInterface; import com.vertispan.tsdefs.annotations.TsName; import com.vertispan.tsdefs.annotations.TsTypeRef; -import elemental2.dom.CustomEvent; -import elemental2.dom.CustomEventInit; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.console_pb.figuredescriptor.*; import io.deephaven.web.client.api.JsPartitionedTable; import io.deephaven.web.client.api.widget.plot.enums.JsSeriesPlotStyle; -import jsinterop.annotations.JsIgnore; import jsinterop.annotations.JsProperty; import java.util.Collections; @@ -47,7 +44,7 @@ public void initSources(Map plotHandlesToPartitione return null; }); partitionedTable.addEventListener(JsPartitionedTable.EVENT_KEYADDED, event -> { - requestTable(partitionedTable, ((CustomEvent) event).detail); + requestTable(partitionedTable, event.getDetail()); }); }); @@ -99,13 +96,10 @@ private void requestTable(JsPartitionedTable partitionedTable, Object key) { series.setMultiSeries(this); series.initSources(Collections.singletonMap(tableId, table), Collections.emptyMap()); - CustomEventInit init = CustomEventInit.create(); - init.setDetail(series); - parent.addSeriesFromMultiSeries(series); - figure.fireEvent(JsFigure.EVENT_SERIES_ADDED, init); - parent.fireEvent(JsChart.EVENT_SERIES_ADDED, init); + figure.fireEvent(JsFigure.EVENT_SERIES_ADDED, series); + parent.fireEvent(JsChart.EVENT_SERIES_ADDED, series); return null; }); } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/OneClick.java b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/OneClick.java index d95fa11614b..d1845fe88ce 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/OneClick.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/widget/plot/OneClick.java @@ -5,10 +5,8 @@ import com.vertispan.tsdefs.annotations.TsInterface; import com.vertispan.tsdefs.annotations.TsName; -import com.vertispan.tsdefs.annotations.TsTypeRef; import elemental2.core.JsArray; import elemental2.core.JsMap; -import elemental2.dom.CustomEventInit; import elemental2.promise.Promise; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.console_pb.figuredescriptor.OneClickDescriptor; import io.deephaven.web.client.api.Column; @@ -193,9 +191,7 @@ private void fetchTable() { if (table == null) { // No table, no need to change the figure subscription, just trigger a // synthetic event indicating no items - CustomEventInit event = CustomEventInit.create(); - event.setDetail(DataUpdateEvent.empty(jsSeries)); - jsFigure.fireEvent(JsFigure.EVENT_UPDATED, event); + jsFigure.fireEvent(JsFigure.EVENT_UPDATED, DataUpdateEvent.empty(jsSeries)); } else { // Subscribe to this key and wait for it... currentTable = table; diff --git a/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeClient.java b/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeClient.java deleted file mode 100644 index ce6a4f785f5..00000000000 --- a/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeClient.java +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending -// -package io.deephaven.web.client.ide; - -import elemental2.promise.Promise; -import io.deephaven.web.client.fu.CancellablePromise; -import jsinterop.annotations.JsMethod; -import jsinterop.annotations.JsType; - -/** - */ -@JsType(name = "Ide", namespace = "dh") -public class IdeClient { - @Deprecated - @JsMethod(name = "getExistingSession") - public Promise getExistingSession_old(String websocketUrl, String authToken, String serviceId, - String language) { - return IdeClient.getExistingSession(websocketUrl, authToken, serviceId, language); - } - - @Deprecated - public static CancellablePromise getExistingSession(String websocketUrl, String authToken, - String serviceId, String language) { - IdeConnection ideConnection = new IdeConnection(websocketUrl, null, true); - return ideConnection.startSession(language); - } -} diff --git a/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeConnection.java b/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeConnection.java index 7ea7d8a8ed1..5851fa1dbab 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeConnection.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeConnection.java @@ -5,7 +5,6 @@ import com.vertispan.tsdefs.annotations.TsTypeRef; import elemental2.core.JsArray; -import elemental2.dom.CustomEventInit; import elemental2.promise.Promise; import io.deephaven.javascript.proto.dhinternal.browserheaders.BrowserHeaders; import io.deephaven.javascript.proto.dhinternal.grpcweb.grpc.Code; @@ -17,13 +16,10 @@ import io.deephaven.web.client.api.barrage.stream.ResponseStreamWrapper; import io.deephaven.web.client.api.console.JsVariableChanges; import io.deephaven.web.client.api.console.JsVariableDescriptor; -import io.deephaven.web.client.fu.JsLog; import io.deephaven.web.shared.data.ConnectToken; import io.deephaven.web.shared.fu.JsConsumer; import io.deephaven.web.shared.fu.JsRunnable; -import jsinterop.annotations.JsConstructor; import jsinterop.annotations.JsIgnore; -import jsinterop.annotations.JsOptional; import jsinterop.annotations.JsType; import jsinterop.base.JsPropertyMap; @@ -40,39 +36,26 @@ public class IdeConnection extends QueryConnectable { public static final String EVENT_SHUTDOWN = "shutdown"; - private final JsRunnable deathListenerCleanup; private final String serverUrl; private final ConnectToken token = new ConnectToken(); private final ConnectOptions options; /** - * creates a new instance, from which console sessions can be made. options are optional. + * Creates a new instance, from which console sessions can be made. * * @param serverUrl The url used when connecting to the server. Read-only. * @param connectOptions Optional Object - * @param fromJava Optional boolean */ - @Deprecated - @JsConstructor - public IdeConnection(String serverUrl, @TsTypeRef(ConnectOptions.class) @JsOptional Object connectOptions, - @JsOptional Boolean fromJava) { + public IdeConnection(String serverUrl, Object connectOptions) { + // Remove trailing slashes from the url this.serverUrl = serverUrl.replaceAll("/+$", ""); - deathListenerCleanup = JsRunnable.doNothing(); if (connectOptions != null) { options = new ConnectOptions(connectOptions); } else { options = new ConnectOptions(); } - - if (fromJava != Boolean.TRUE) { - JsLog.warn( - "dh.IdeConnection constructor is deprecated, please create a dh.CoreClient, call login(), then call getAsIdeConnection()"); - token.setType("Anonymous"); - token.setValue(""); - connection.get().whenServerReady("login").then(ignore -> Promise.resolve((Void) null)); - } } @Override @@ -80,33 +63,24 @@ protected String logPrefix() { return "IdeConnection on " + getServerUrl() + ": "; } - /** - * Temporary method to split logic between IdeConnection and CoreClient - */ @JsIgnore + @Override public ConnectToken getToken() { return token; } @JsIgnore @Override - public Promise getConnectToken() { - return Promise.resolve(token); - } - - @JsIgnore - @Override - public Promise getConnectOptions() { - return Promise.resolve(options); + public ConnectOptions getOptions() { + return options; } /** - * closes the current connection, releasing any resources on the server or client. + * Closes the current connection, releasing any resources on the server or client. */ + // Made public to expose to JS public void close() { super.close(); - - deathListenerCleanup.run(); } /** @@ -182,9 +156,7 @@ public void notifyServerShutdown(TerminationNotificationResponse success) { } // fire shutdown advice event - CustomEventInit eventDetails = CustomEventInit.create(); - eventDetails.setDetail(details); - fireEvent(EVENT_SHUTDOWN, eventDetails); + fireEvent(EVENT_SHUTDOWN, details); // fire deprecated event notifyConnectionError(new ResponseStreamWrapper.Status() { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeSession.java b/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeSession.java index 438c5c857e4..2b7bf6b3a65 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeSession.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/ide/IdeSession.java @@ -7,7 +7,6 @@ import com.vertispan.tsdefs.annotations.TsTypeRef; import elemental2.core.JsArray; import elemental2.core.JsSet; -import elemental2.dom.CustomEventInit; import elemental2.promise.Promise; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.ticket_pb.Ticket; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.console_pb.*; @@ -18,6 +17,7 @@ import io.deephaven.web.client.api.console.JsVariableChanges; import io.deephaven.web.client.api.console.JsVariableDescriptor; import io.deephaven.web.client.api.console.JsVariableType; +import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.client.api.tree.JsTreeTable; import io.deephaven.web.client.api.widget.plot.JsFigure; import io.deephaven.web.client.fu.CancellablePromise; @@ -104,9 +104,7 @@ public IdeSession( public Promise getTable(String name, @JsOptional Boolean applyPreviewColumns) { return connection.getVariableDefinition(name, JsVariableType.TABLE).then(varDef -> { final Promise table = connection.getTable(varDef, applyPreviewColumns); - final CustomEventInit event = CustomEventInit.create(); - event.setDetail(table); - fireEvent(EVENT_TABLE_OPENED, event); + fireEvent(EVENT_TABLE_OPENED, table); return table; }); } @@ -149,9 +147,7 @@ public Promise getObject(@TsTypeRef(JsVariableDescriptor.class) JsPropertyMap public Promise newTable(String[] columnNames, String[] types, String[][] data, String userTimeZone) { return connection.newTable(columnNames, types, data, userTimeZone, this).then(table -> { - final CustomEventInit event = CustomEventInit.create(); - event.setDetail(table); - fireEvent(EVENT_TABLE_OPENED, event); + fireEvent(EVENT_TABLE_OPENED, table); return Promise.resolve(table); }); @@ -165,9 +161,7 @@ public Promise newTable(String[] columnNames, String[] types, String[][ */ public Promise mergeTables(JsTable[] tables) { return connection.mergeTables(tables, this).then(table -> { - final CustomEventInit event = CustomEventInit.create(); - event.setDetail(table); - fireEvent(EVENT_TABLE_OPENED, event); + fireEvent(EVENT_TABLE_OPENED, table); return Promise.resolve(table); }); @@ -223,9 +217,7 @@ public CancellablePromise runCode(String code) { }); CommandInfo commandInfo = new CommandInfo(code, result); - final CustomEventInit event = CustomEventInit.create(); - event.setDetail(commandInfo); - fireEvent(IdeSession.EVENT_COMMANDSTARTED, event); + fireEvent(IdeSession.EVENT_COMMANDSTARTED, commandInfo); return result; } @@ -251,9 +243,7 @@ private BiDiStream ensureStream() { }); currentStream.onStatus(status -> { if (!status.isOk()) { - CustomEventInit init = CustomEventInit.create(); - init.setDetail(status.getDetails()); - fireEvent(EVENT_REQUEST_FAILED, init); + fireEvent(EVENT_REQUEST_FAILED, status.getDetails()); pendingAutocompleteCalls.values().forEach(p -> { p.fail("Connection error" + status.getDetails()); }); diff --git a/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java b/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java index 311a956e111..9fd784043f3 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java @@ -16,6 +16,7 @@ import io.deephaven.web.client.api.barrage.def.ColumnDefinition; import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; import io.deephaven.web.client.api.batch.TableConfig; +import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.client.api.filter.FilterCondition; import io.deephaven.web.client.api.lifecycle.HasLifecycle; import io.deephaven.web.client.api.state.HasTableState; @@ -918,7 +919,7 @@ public void cleanup() { // notify any retainers who have events that we've been released. for (Object retainer : JsItr.iterate(retainers.values())) { if (retainer instanceof HasEventHandling) { - ((HasEventHandling) retainer).fireEventWithDetail(HasEventHandling.INTERNAL_EVENT_RELEASED, this); + ((HasEventHandling) retainer).fireEvent(HasEventHandling.INTERNAL_EVENT_RELEASED, this); } } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/state/HasTableBinding.java b/web/client-api/src/main/java/io/deephaven/web/client/state/HasTableBinding.java index ba897fc54e9..0cc0a4e142c 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/state/HasTableBinding.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/state/HasTableBinding.java @@ -3,7 +3,6 @@ // package io.deephaven.web.client.state; -import elemental2.dom.CustomEventInit; import io.deephaven.web.client.api.TableTicket; /** @@ -26,7 +25,7 @@ public interface HasTableBinding { void fireEvent(String name); - void fireEvent(String name, CustomEventInit eventInit); + void fireEvent(String name, T detail); void setState(ClientTableState appendTo); diff --git a/web/client-api/src/main/java/io/deephaven/web/client/state/TableReviver.java b/web/client-api/src/main/java/io/deephaven/web/client/state/TableReviver.java index 2f3220b476a..c6429ffff6c 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/state/TableReviver.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/state/TableReviver.java @@ -5,7 +5,6 @@ import elemental2.core.JsArray; import elemental2.core.JsMap; -import elemental2.dom.CustomEventInit; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.ticket_pb.Ticket; import io.deephaven.javascript.proto.dhinternal.browserheaders.BrowserHeaders; import io.deephaven.javascript.proto.dhinternal.io.deephaven.proto.table_pb.BatchTableRequest; @@ -175,15 +174,15 @@ public void fireEvent(String name) { } @Override - public void fireEvent(String name, CustomEventInit e) { + public void fireEvent(String name, T detail) { switch (name) { case JsTable.EVENT_REQUEST_FAILED: // log this failure - JsLog.debug("Revivification failed", e.getDetail()); + JsLog.debug("Revivification failed", detail); // return; default: - JsLog.debug("The table reviver does not accept event", name, e); + JsLog.debug("The table reviver does not accept event", name, detail); } } diff --git a/web/client-api/src/test/java/io/deephaven/web/client/api/AbstractAsyncGwtTestCase.java b/web/client-api/src/test/java/io/deephaven/web/client/api/AbstractAsyncGwtTestCase.java index 557f0ec2952..c674de47a4f 100644 --- a/web/client-api/src/test/java/io/deephaven/web/client/api/AbstractAsyncGwtTestCase.java +++ b/web/client-api/src/test/java/io/deephaven/web/client/api/AbstractAsyncGwtTestCase.java @@ -8,10 +8,11 @@ import elemental2.core.JsArray; import elemental2.core.JsError; import elemental2.core.JsString; -import elemental2.dom.CustomEvent; import elemental2.dom.DomGlobal; import elemental2.promise.IThenable; import elemental2.promise.Promise; +import io.deephaven.web.client.api.event.Event; +import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.client.api.subscription.ViewportData; import io.deephaven.web.client.api.tree.JsTreeTable; import io.deephaven.web.client.fu.CancellablePromise; @@ -106,7 +107,7 @@ JsRunnable assertEventNotCalled( final String ev = events[i]; undos[i] = handling.addEventListener(ev, e -> { log("Did not expect", ev, "but fired event", e); - report("Expected " + ev + " to not be called; detail: " + (e.detail)); + report("Expected " + ev + " to not be called; detail: " + (e.getDetail())); }); } return () -> { @@ -242,7 +243,7 @@ protected Promise assertUpdateReceived(JsTable table, int count, int ti protected Promise assertUpdateReceived(JsTable table, Consumer check, int timeoutInMillis) { return Promise.race(this.waitForEvent(table, JsTable.EVENT_UPDATED, e -> { - ViewportData viewportData = e.detail; + ViewportData viewportData = e.getDetail(); check.accept(viewportData); }, timeoutInMillis), table.nextEvent(JsTable.EVENT_REQUEST_FAILED, (double) timeoutInMillis).then(Promise::reject)) @@ -274,7 +275,7 @@ protected IThenable.ThenOnFulfilledCallbackFn waitForTickTwice } protected Promise waitForEventWhere(V evented, String eventName, - Predicate> check, int timeout) { + Predicate> check, int timeout) { // note that this roughly reimplements the 'kill timer' so this can be run in parallel with itself or other // similar steps return new Promise<>((resolve, reject) -> { @@ -309,7 +310,7 @@ protected Promise waitForEventWhere(V evented } protected Promise waitForEvent(V evented, String eventName, - Consumer> check, int timeout) { + Consumer> check, int timeout) { return this.waitForEventWhere(evented, eventName, e -> { check.accept(e); return true; diff --git a/web/client-api/src/test/java/io/deephaven/web/client/api/HierarchicalTableTestGwt.java b/web/client-api/src/test/java/io/deephaven/web/client/api/HierarchicalTableTestGwt.java index 0016db6133c..244e1fb1c29 100644 --- a/web/client-api/src/test/java/io/deephaven/web/client/api/HierarchicalTableTestGwt.java +++ b/web/client-api/src/test/java/io/deephaven/web/client/api/HierarchicalTableTestGwt.java @@ -4,9 +4,9 @@ package io.deephaven.web.client.api; import elemental2.core.JsArray; -import elemental2.dom.CustomEvent; import elemental2.promise.Promise; import io.deephaven.web.client.api.tree.JsRollupConfig; +import io.deephaven.web.client.api.event.Event; import io.deephaven.web.client.api.tree.JsTreeTable; import io.deephaven.web.client.api.tree.TreeViewportData; import io.deephaven.web.client.api.tree.enums.JsAggregationOperation; @@ -60,8 +60,8 @@ public void testStaticTreeTable() { return treeTable.nextEvent( JsTreeTable.EVENT_UPDATED, 2001d); }).then(event -> { - assertEquals(10d, event.detail.getTreeSize()); - assertEquals(10, event.detail.getRows().length); + assertEquals(10d, event.getDetail().getTreeSize()); + assertEquals(10, event.getDetail().getRows().length); // move the viewport and try again @@ -69,8 +69,8 @@ public void testStaticTreeTable() { return treeTable.nextEvent( JsTreeTable.EVENT_UPDATED, 2002d); }).then(event -> { - assertEquals(10d, event.detail.getTreeSize()); - assertEquals(5, event.detail.getRows().length); + assertEquals(10d, event.getDetail().getTreeSize()); + assertEquals(5, event.getDetail().getRows().length); treeTable.close(); @@ -101,9 +101,9 @@ public void testRefreshingTreeTable() { // Wait for the table to tick such that the first row has children // Read values from the one returned row return waitForEventWhere(treeTable, JsTreeTable.EVENT_UPDATED, - (CustomEvent d) -> d.detail + (Event d) -> d.getDetail() .getTreeSize() == 1 - && d.detail.getRows().getAtAsAny(0).cast() + && d.getDetail().getRows().getAtAsAny(0).cast() .hasChildren(), 10001) .then(JsTreeTable::getViewportData) @@ -133,7 +133,7 @@ public void testRefreshingTreeTable() { // Wait for the expand to occur and table to show all 10 rows return waitForEventWhere(treeTable, JsTreeTable.EVENT_UPDATED, - (CustomEvent d) -> d.detail.getTreeSize() == 10, + (Event d) -> d.getDetail().getTreeSize() == 10, 14004); }) .then(JsTreeTable::getViewportData) @@ -160,8 +160,8 @@ public void testRefreshingTreeTable() { return treeTable.nextEvent( JsTreeTable.EVENT_UPDATED, 2002d); }).then(event -> { - assertEquals(10d, event.detail.getTreeSize()); - assertEquals(5, event.detail.getRows().length); + assertEquals(10d, event.getDetail().getTreeSize()); + assertEquals(5, event.getDetail().getRows().length); return Promise.resolve(treeTable); }) @@ -195,7 +195,7 @@ public void testTickingRollup() { // Wait for the table to tick such that we have at least 4 rows (root, three children) return waitForEventWhere(rollup, JsTreeTable.EVENT_UPDATED, - (CustomEvent d) -> d.detail.getTreeSize() == 4, + (Event d) -> d.getDetail().getTreeSize() == 4, 10002) .then(JsTreeTable::getViewportData) .then(data -> Promise.resolve((TreeViewportData) data)) @@ -240,7 +240,7 @@ public void testTickingRollup() { // Wait for the expand to occur and table to show all 10 rows return waitForEventWhere(rollup, JsTreeTable.EVENT_UPDATED, - (CustomEvent d) -> d.detail.getTreeSize() > 4, + (Event d) -> d.getDetail().getTreeSize() > 4, 14008); }) .then(JsTreeTable::getViewportData) @@ -267,7 +267,7 @@ public void testTickingRollup() { // Collapse row 2, wait until back to 4 rows rollup.collapse(JsTreeTable.RowReferenceUnion.of(1)); return waitForEventWhere(rollup, JsTreeTable.EVENT_UPDATED, - (CustomEvent d) -> d.detail.getTreeSize() == 4, + (Event d) -> d.getDetail().getTreeSize() == 4, 14009); }) .then(event -> { @@ -313,7 +313,7 @@ public void testCreateRollup() { delayTestFinish(15000 + step); return waitForEventWhere(r, JsTreeTable.EVENT_UPDATED, - (CustomEvent d) -> r.getSize() == 4, 13000 + step) + (Event d) -> r.getSize() == 4, 13000 + step) .then(event -> Promise.resolve(r)); })); } diff --git a/web/client-api/src/test/java/io/deephaven/web/client/api/PartitionedTableTestGwt.java b/web/client-api/src/test/java/io/deephaven/web/client/api/PartitionedTableTestGwt.java index d0b9c4fcc40..9bd0b9446a2 100644 --- a/web/client-api/src/test/java/io/deephaven/web/client/api/PartitionedTableTestGwt.java +++ b/web/client-api/src/test/java/io/deephaven/web/client/api/PartitionedTableTestGwt.java @@ -4,7 +4,7 @@ package io.deephaven.web.client.api; import elemental2.core.JsArray; -import elemental2.dom.CustomEvent; +import io.deephaven.web.client.api.event.Event; public class PartitionedTableTestGwt extends AbstractAsyncGwtTestCase { @Override @@ -112,7 +112,7 @@ public void testTickingPartitionedTable() { assertEquals("x", columns[2].getName()); return waitForEventWhere(partitionedTable, JsPartitionedTable.EVENT_KEYADDED, - (CustomEvent> e) -> e.detail.getAt(0).equals("2"), 14004) + (Event> e) -> e.getDetail().getAt(0).equals("2"), 14004) .then(event -> partitionedTable.getTable("2")).then(constituentTable -> { assertEquals(3, constituentTable.getColumns().length); assertTrue(constituentTable.getSize() >= 2); @@ -141,7 +141,7 @@ public void testTickingTransformedPartitionedTable() { assertEquals("x", columns[1].getName()); return waitForEventWhere(partitionedTable, JsPartitionedTable.EVENT_KEYADDED, - (CustomEvent> e) -> e.detail.getAt(0).equals("2"), 14005) + (Event> e) -> e.getDetail().getAt(0).equals("2"), 14005) .then(event -> partitionedTable.getTable("2")).then(constituentTable -> { assertEquals(2, constituentTable.getColumns().length); assertTrue(constituentTable.getSize() >= 1); diff --git a/web/client-api/src/test/java/io/deephaven/web/client/api/TotalsTableTestGwt.java b/web/client-api/src/test/java/io/deephaven/web/client/api/TotalsTableTestGwt.java index 857cad855c5..baeaf723cfb 100644 --- a/web/client-api/src/test/java/io/deephaven/web/client/api/TotalsTableTestGwt.java +++ b/web/client-api/src/test/java/io/deephaven/web/client/api/TotalsTableTestGwt.java @@ -5,9 +5,9 @@ import elemental2.core.JsArray; import elemental2.core.JsString; -import elemental2.dom.CustomEvent; import elemental2.promise.IThenable; import elemental2.promise.Promise; +import io.deephaven.web.client.api.event.Event; import io.deephaven.web.client.api.filter.FilterCondition; import io.deephaven.web.client.api.filter.FilterValue; import io.deephaven.web.client.api.subscription.ViewportData; @@ -331,7 +331,7 @@ public void testGroupedTotals() { // confirm the grand totals are unchanged return waitForEvent(totals, JsTable.EVENT_UPDATED, update -> { - ViewportData viewportData = (ViewportData) update.detail; + ViewportData viewportData = (ViewportData) update.getDetail(); // 2 rows (one for k=0, one for k=1) assertEquals(2, viewportData.getRows().length); @@ -389,7 +389,7 @@ static TotalsResults of(int k, long i, double avg, double min) { } } - private Consumer checkTotals( + private Consumer> checkTotals( JsTotalsTable totals, long i, double avg, @@ -397,7 +397,7 @@ private Consumer checkTotals( String messages) { String ext = messages; return update -> { - ViewportData viewportData = (ViewportData) update.detail; + ViewportData viewportData = update.getDetail(); assertEquals(1, viewportData.getRows().length); assertEquals(3, viewportData.getColumns().length); @@ -412,13 +412,13 @@ private Consumer checkTotals( }; } - private Consumer checkTotals( + private Consumer> checkTotals( JsTotalsTable totals, String messages, TotalsResults... expected) { String ext = messages; return update -> { - ViewportData viewportData = (ViewportData) update.detail; + ViewportData viewportData = update.getDetail(); assertEquals("Viewport data rows", expected.length, viewportData.getRows().length); assertEquals("Viewport columns", 4, viewportData.getColumns().length); @@ -442,9 +442,9 @@ private Consumer checkTotals( * Specialized waitForEvent since JsTotalsTable isn't a HasEventHandling subtype, and doesnt make sense to shoehorn * it in just for tests. */ - private Promise waitForEvent(JsTotalsTable table, String eventName, Consumer check, + private Promise waitForEvent(JsTotalsTable table, String eventName, Consumer> check, int timeout) { - return waitForEvent(table.getWrappedTable(), eventName, check::accept, timeout) + return waitForEvent(table.getWrappedTable(), eventName, check, timeout) .then(t -> Promise.resolve(table)); } diff --git a/web/client-api/src/test/java/io/deephaven/web/client/api/subscription/ViewportTestGwt.java b/web/client-api/src/test/java/io/deephaven/web/client/api/subscription/ViewportTestGwt.java index 4feb544df3d..0d89e2c7917 100644 --- a/web/client-api/src/test/java/io/deephaven/web/client/api/subscription/ViewportTestGwt.java +++ b/web/client-api/src/test/java/io/deephaven/web/client/api/subscription/ViewportTestGwt.java @@ -4,7 +4,6 @@ package io.deephaven.web.client.api.subscription; import elemental2.core.JsArray; -import elemental2.dom.CustomEvent; import elemental2.dom.DomGlobal; import elemental2.promise.IThenable; import elemental2.promise.Promise; @@ -12,10 +11,11 @@ import io.deephaven.web.client.api.Column; import io.deephaven.web.client.api.DateWrapper; import io.deephaven.web.client.api.Format; -import io.deephaven.web.client.api.HasEventHandling; import io.deephaven.web.client.api.JsRangeSet; import io.deephaven.web.client.api.JsTable; import io.deephaven.web.client.api.TableData; +import io.deephaven.web.client.api.event.Event; +import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.client.api.filter.FilterCondition; import io.deephaven.web.client.api.filter.FilterValue; import io.deephaven.web.shared.fu.RemoverFn; @@ -538,8 +538,8 @@ private IThenable helperForViewportWithNoInitialItems(JsTable t, Column .then(table -> { // wait for the next tick, where we get the "first" row added, confirm that the viewport // data is sane - return waitForEventWhere(table, "updated", (CustomEvent e) -> { - ViewportData viewport = e.detail; + return waitForEventWhere(table, "updated", (Event e) -> { + ViewportData viewport = e.getDetail(); if (viewport.getRows().length != 1) { return false; // wrong data, wait for another event } @@ -553,8 +553,8 @@ private IThenable helperForViewportWithNoInitialItems(JsTable t, Column }) .then(table -> { // again wait for the table to go back to zero items, make sure it makes sense - return waitForEventWhere(table, "updated", (CustomEvent e) -> { - ViewportData emptyViewport = (ViewportData) e.detail; + return waitForEventWhere(table, "updated", (Event e) -> { + ViewportData emptyViewport = e.getDetail(); if (emptyViewport.getRows().length != 0) { return false; // wrong data, wait for another event } @@ -564,8 +564,8 @@ private IThenable helperForViewportWithNoInitialItems(JsTable t, Column }) .then(table -> { // one more tick later, we'll see the item back again - return waitForEventWhere(table, "updated", (CustomEvent e) -> { - ViewportData viewport = (ViewportData) e.detail; + return waitForEventWhere(table, "updated", (Event e) -> { + ViewportData viewport = e.getDetail(); if (viewport.getRows().length != 1) { return false; // wrong data, wait for another event }