Skip to content

Commit

Permalink
fix: JS API shouldn't require reference to window object (#6193)
Browse files Browse the repository at this point in the history
Also introduces a dh.Event type to use instead of the browser Event and
CustomEvent types.

Fixes #5537
Fixes #6190
  • Loading branch information
niloc132 authored Oct 21, 2024
1 parent 8f02178 commit 680047b
Show file tree
Hide file tree
Showing 39 changed files with 335 additions and 430 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <i>- base 64 encoded auth token. String serviceId -</i> 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<String> 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}.
* <p>
* 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() {

}
Expand All @@ -24,6 +52,18 @@ public ConnectOptions() {
public ConnectOptions(Object connectOptions) {
this();
JsPropertyMap<Object> 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();
// }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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",
Expand All @@ -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 <R> Promise<String[][]> getConfigs(Consumer<JsBiConsumer<Object, R>> rpcCall,
Expand Down Expand Up @@ -82,20 +79,20 @@ public String getServerUrl() {
}

public Promise<String[][]> 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<String> 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<Void> login(@TsTypeRef(LoginCredentials.class) JsPropertyMap<Object> credentials) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -112,10 +110,8 @@ public Promise<JsPartitionedTable> refetch() {
fireEvent(EVENT_RECONNECT);
return null;
}, failure -> {
CustomEventInit<Object> init = CustomEventInit.create();
init.setDetail(failure);
unsuppressEvents();
fireEvent(EVENT_RECONNECTFAILED, init);
fireEvent(EVENT_RECONNECTFAILED, failure);
suppressEvents();
return null;
});
Expand All @@ -141,20 +137,16 @@ private Promise<JsPartitionedTable> subscribeToBaseTable() {
return promise.asPromise();
}

private void handleKeys(Event update) {
// noinspection unchecked
CustomEvent<SubscriptionTableData> event = (CustomEvent<SubscriptionTableData>) update;
private void handleKeys(Event<SubscriptionTableData> 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<Object> key = eventData.getColumns().map((c, p1) -> eventData.getData(index, c));
knownKeys.add(key.asList());
CustomEventInit<JsArray<Object>> init = CustomEventInit.create();
init.setDetail(key);
fireEvent(EVENT_KEYADDED, init);
fireEvent(EVENT_KEYADDED, key);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -223,7 +224,7 @@ public <T> boolean removeEventListener(String name, EventFn<T> callback) {
}

@JsMethod
public <T> Promise<CustomEvent<T>> nextEvent(String eventName, Double timeoutInMillis) {
public <T> Promise<Event<T>> nextEvent(String eventName, Double timeoutInMillis) {
return wrappedTable.nextEvent(eventName, timeoutInMillis);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -51,9 +54,9 @@ public QueryConnectable() {
this.connection = JsLazy.of(() -> new WorkerConnection(this));
}

public abstract Promise<ConnectToken> getConnectToken();
public abstract ConnectToken getToken();

public abstract Promise<ConnectOptions> getConnectOptions();
public abstract ConnectOptions getOptions();

@Deprecated
public void notifyConnectionError(ResponseStreamWrapper.Status status) {
Expand All @@ -62,12 +65,10 @@ public void notifyConnectionError(ResponseStreamWrapper.Status status) {
}
notifiedConnectionError = true;

CustomEventInit<JsPropertyMap<Object>> 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");
}
Expand Down Expand Up @@ -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> T createClient(BiFunction<String, Object, T> 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;
}
}
Loading

0 comments on commit 680047b

Please sign in to comment.