Skip to content

Commit 7e88120

Browse files
committed
Move ticket creation wiring in the JS api to a single class
1 parent 574a2db commit 7e88120

10 files changed

+191
-111
lines changed

web/client-api/src/main/java/io/deephaven/web/client/api/ClientConfiguration.java

-51
This file was deleted.

web/client-api/src/main/java/io/deephaven/web/client/api/JsTable.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,7 @@ public Promise<JsTreeTable> rollup(@TsTypeRef(JsRollupConfig.class) Object confi
10441044
config = new JsRollupConfig(Js.cast(configObject));
10451045
}
10461046

1047-
Ticket rollupTicket = workerConnection.getConfig().newTicket();
1047+
Ticket rollupTicket = workerConnection.getTickets().newExportTicket();
10481048

10491049
Promise<Object> rollupPromise = Callbacks.grpcUnaryPromise(c -> {
10501050
RollupRequest request = config.buildRequest(getColumns());
@@ -1080,7 +1080,7 @@ public Promise<JsTreeTable> treeTable(@TsTypeRef(JsTreeTableConfig.class) Object
10801080
config = new JsTreeTableConfig(Js.cast(configObject));
10811081
}
10821082

1083-
Ticket treeTicket = workerConnection.getConfig().newTicket();
1083+
Ticket treeTicket = workerConnection.getTickets().newExportTicket();
10841084

10851085
Promise<Object> treePromise = Callbacks.grpcUnaryPromise(c -> {
10861086
TreeRequest requestMessage = new TreeRequest();
@@ -1297,7 +1297,7 @@ public Promise<JsPartitionedTable> partitionBy(Object keys, @JsOptional Boolean
12971297

12981298
// Start the partitionBy on the server - we want to get the error from here, but we'll race the fetch against
12991299
// this to avoid an extra round-trip
1300-
Ticket partitionedTableTicket = workerConnection.getConfig().newTicket();
1300+
Ticket partitionedTableTicket = workerConnection.getTickets().newExportTicket();
13011301
Promise<PartitionByResponse> partitionByPromise = Callbacks.<PartitionByResponse, Object>grpcUnaryPromise(c -> {
13021302
PartitionByRequest partitionBy = new PartitionByRequest();
13031303
partitionBy.setTableId(state().getHandle().makeTicket());

web/client-api/src/main/java/io/deephaven/web/client/api/QueryConnectable.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,8 @@ public JsRunnable onLogMessage(JsConsumer<LogItem> callback) {
146146
public CancellablePromise<IdeSession> startSession(String type) {
147147
JsLog.debug("Starting", type, "console session");
148148
LazyPromise<Ticket> promise = new LazyPromise<>();
149-
final ClientConfiguration config = connection.get().getConfig();
150-
final Ticket ticket = new Ticket();
151-
ticket.setTicket(config.newTicketRaw());
149+
final Tickets config = connection.get().getTickets();
150+
final Ticket ticket = config.newExportTicket();
152151

153152
final JsRunnable closer = () -> {
154153
boolean run = !cancelled.has(ticket);

web/client-api/src/main/java/io/deephaven/web/client/api/TableTicket.java

+2-14
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,12 @@
77
import io.deephaven.javascript.proto.dhinternal.arrow.flight.protocol.flight_pb.FlightDescriptor;
88
import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.ticket_pb.Ticket;
99
import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.table_pb.TableReference;
10-
import io.deephaven.web.client.api.console.JsVariableDefinition;
1110

1211
/**
13-
* Replacement for TableHandle, wraps up Ticket plus current export state. We only consider the lower bytes for hashing
14-
* (since until we've got millions of tickets it won't matter).
12+
* Replacement for TableHandle, wraps up export tickets plus current export state. We only consider the lower bytes for
13+
* hashing (since until we've got millions of tickets it won't matter).
1514
*/
1615
public class TableTicket {
17-
public static Ticket createTicket(JsVariableDefinition varDef) {
18-
Ticket ticket = new Ticket();
19-
ticket.setTicket(varDef.getId());
20-
return ticket;
21-
}
22-
23-
public static TableReference createTableRef(JsVariableDefinition varDef) {
24-
TableReference tableRef = new TableReference();
25-
tableRef.setTicket(createTicket(varDef));
26-
return tableRef;
27-
}
2816

2917
/**
3018
* UNKNOWN: 0, PENDING: 1, PUBLISHING: 2, QUEUED: 3, RUNNING: 4, EXPORTED: 5, RELEASED: 6, CANCELLED: 7, FAILED: 8,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
//
2+
// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending
3+
//
4+
package io.deephaven.web.client.api;
5+
6+
import elemental2.core.TypedArray;
7+
import elemental2.core.Uint8Array;
8+
import elemental2.dom.DomGlobal;
9+
import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.table_pb.TableReference;
10+
import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.ticket_pb.Ticket;
11+
import io.deephaven.web.client.api.console.JsVariableDefinition;
12+
import jsinterop.annotations.JsMethod;
13+
import jsinterop.annotations.JsPackage;
14+
import jsinterop.base.Js;
15+
16+
/**
17+
* Single factory for known ticket types. By definition, this cannot be exhaustive, since flight tickets have no
18+
* inherent structure - Deephaven Core only specifies that the first byte will indicate the type of ticket, and later
19+
* bytes will be handled by handlers for that type. Deephaven Core requires only that export tickets be support, but
20+
* also offers application tickets, scope tickets, and shared tickets. Downstream projects may define new ticket types,
21+
* which won't necessarily be understood by this client.
22+
*
23+
* @see io.deephaven.server.session.ExportTicketResolver
24+
* @see io.deephaven.server.appmode.ApplicationTicketResolver
25+
* @see io.deephaven.server.console.ScopeTicketResolver
26+
* @see io.deephaven.server.session.SharedTicketResolver
27+
*/
28+
public class Tickets {
29+
// Prefix for all export tickets
30+
private static final byte EXPORT_PREFIX = 'e';
31+
// Prefix for all application tickets
32+
private static final byte APPLICATION_PREFIX = 'a';
33+
// Prefix for all scope tickets
34+
private static final byte SCOPE_PREFIX = 's';
35+
// Prefix for all shared tickets
36+
private static final byte SHARED_PREFIX = 'h';
37+
38+
// Some ticket types use a slash as a delimeter between fields
39+
private static final char TICKET_DELIMITER = '/';
40+
41+
/**
42+
* The next number to use when making an export ticket. These values must always be positive, as zero is an invalid
43+
* value, and negative values represent server-created tickets.
44+
*/
45+
private int nextExport = 1;
46+
47+
public Tickets() {}
48+
49+
/**
50+
* Utility method to create a ticket from a known-valid base64 encoding of a ticket.
51+
* <p>
52+
* Use caution with non-export tickets, the definition may change between calls to the server - they should be
53+
* exported before use.
54+
*
55+
* @param varDef the variable definition to create a ticket from
56+
* @return a ticket with the variable's id as the ticket bytes
57+
*/
58+
public static Ticket createTicket(JsVariableDefinition varDef) {
59+
Ticket ticket = new Ticket();
60+
ticket.setTicket(varDef.getId());
61+
return ticket;
62+
}
63+
64+
/**
65+
* Utility method to create a ticket wrapped in a TableReference from a known-valid base64 encoding of a ticket.
66+
* <p>
67+
* Use caution with non-export tickets, the definition may change between calls to the server - they should be
68+
* exported before use.
69+
*
70+
* @param varDef the variable definition to create a ticket from
71+
* @return a table reference with the variable's id as the ticket bytes
72+
*/
73+
74+
public static TableReference createTableRef(JsVariableDefinition varDef) {
75+
TableReference tableRef = new TableReference();
76+
tableRef.setTicket(createTicket(varDef));
77+
return tableRef;
78+
}
79+
80+
public static void validateScopeOrApplicationTicketBase64(String base64Bytes) {
81+
String bytes = DomGlobal.atob(base64Bytes);
82+
if (bytes.length() > 2) {
83+
String prefix = bytes.substring(0, 2);
84+
if ((prefix.charAt(0) == SCOPE_PREFIX || prefix.charAt(0) == APPLICATION_PREFIX)
85+
&& prefix.charAt(1) == TICKET_DELIMITER) {
86+
return;
87+
}
88+
}
89+
throw new IllegalArgumentException("Cannot create a VariableDefinition from a non-scope ticket");
90+
}
91+
92+
/**
93+
* Provides the next export id for the current session as a ticket.
94+
*
95+
* @return a new ticket with an export id that hasn't previously been used for this session
96+
*/
97+
public Ticket newExportTicket() {
98+
Ticket ticket = new Ticket();
99+
ticket.setTicket(newExportTicketRaw());
100+
return ticket;
101+
}
102+
103+
/**
104+
* Provides the next export id for the current session.
105+
*
106+
* @return the next export id
107+
*/
108+
public int newTicketInt() {
109+
if (nextExport == Integer.MAX_VALUE) {
110+
throw new IllegalStateException("Ran out of tickets!");
111+
}
112+
113+
return nextExport++;
114+
}
115+
116+
private Uint8Array newExportTicketRaw() {
117+
final int exportId = newTicketInt();
118+
final double[] dest = new double[5];
119+
dest[0] = EXPORT_PREFIX;
120+
dest[1] = (byte) exportId;
121+
dest[2] = (byte) (exportId >>> 8);
122+
dest[3] = (byte) (exportId >>> 16);
123+
dest[4] = (byte) (exportId >>> 24);
124+
125+
final Uint8Array bytes = new Uint8Array(5);
126+
bytes.set(dest);
127+
return bytes;
128+
}
129+
130+
/**
131+
* Provides the next export id for the current session as a table ticket.
132+
*
133+
* @return a new table ticket with an export id that hasn't previously been used for this session
134+
*/
135+
public TableTicket newTableTicket() {
136+
return new TableTicket(newExportTicketRaw());
137+
}
138+
139+
/**
140+
* Creates a shared ticket from the provided array of bytes.
141+
* <p>
142+
* Use caution with non-export tickets, the definition may change between calls to the server - they should be
143+
* exported before use.
144+
*
145+
* @param array array of bytes to populate the ticket with
146+
* @return a new shared ticket
147+
*/
148+
public Ticket sharedTicket(TypedArray.SetArrayUnionType array) {
149+
int length = Js.asArrayLike(array).getLength();
150+
Uint8Array bytesWithPrefix = new Uint8Array(length + 2);
151+
// Add the shared ticket prefix at the start of the provided value
152+
bytesWithPrefix.setAt(0, (double) SHARED_PREFIX);
153+
bytesWithPrefix.setAt(1, (double) TICKET_DELIMITER);
154+
bytesWithPrefix.set(array, 2);
155+
156+
Ticket ticket = new Ticket();
157+
ticket.setTicket(bytesWithPrefix);
158+
return ticket;
159+
}
160+
}

0 commit comments

Comments
 (0)