Skip to content

Commit

Permalink
Rest functionality (finos#1515)
Browse files Browse the repository at this point in the history
* Pagination in Table

* fix table sizing issue

* table cellblock selection support

* cell block selection, keyboard support I

* fix row selection block styling

* fix filter input when no suggestion provider

* cell block selection

* fixed websocket tests

* fic table cypress tecst

* fix type issues
  • Loading branch information
heswell authored Oct 7, 2024
1 parent f2fc2c5 commit f3d4838
Show file tree
Hide file tree
Showing 43 changed files with 1,634 additions and 417 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
import { RestDataSource, ConnectionManager } from "@finos/vuu-data-remote";
import { RestDataSource } from "@finos/vuu-data-remote";
import { ServerAPI } from "@finos/vuu-data-types";
import {
VuuCreateVisualLink,
VuuRemoveVisualLink,
VuuRpcMenuRequest,
VuuRpcServiceRequest,
VuuRpcViewportRequest,
VuuTable,
} from "@finos/vuu-protocol-types";
import { DataSourceProvider } from "@finos/vuu-utils";
import { ReactNode } from "react";

const getServerAPI = () => ConnectionManager.serverAPI;
const serverAPI: Pick<
ServerAPI,
"getTableList" | "getTableSchema" | "rpcCall"
> = {
getTableList: async () => {
console.log(`Rest data source does not yet support table list`);
return { tables: [] };
},
getTableSchema: async (vuuTable: VuuTable) => {
throw Error(
`Rest data source does not yet support table schema (${vuuTable.table})`,
);
},
rpcCall: async (
message:
| VuuRpcServiceRequest
| VuuRpcMenuRequest
| VuuRpcViewportRequest
| VuuCreateVisualLink
| VuuRemoveVisualLink,
) =>
Promise.reject(
Error(`Rest data source does not yet support RPC (${message.type})`),
),
};

const getServerAPI = async () => serverAPI;

export const RestDataSourceProvider = ({
children,
Expand All @@ -12,7 +47,7 @@ export const RestDataSourceProvider = ({
url: string;
}) => {
// url is a static property
RestDataSource.url = url;
RestDataSource.api = url;
return (
<DataSourceProvider
VuuDataSource={RestDataSource}
Expand Down
53 changes: 34 additions & 19 deletions vuu-ui/packages/vuu-data-react/src/hooks/useTypeaheadSuggestions.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ConnectionManager } from "@finos/vuu-data-remote";
import { SuggestionFetcher, TableSchemaTable } from "@finos/vuu-data-types";
import {
VuuRpcServiceRequest,
TypeaheadParams,
} from "@finos/vuu-protocol-types";
import { useDataSource } from "@finos/vuu-utils";
import { useCallback } from "react";

export const getTypeaheadParams = (
Expand All @@ -18,21 +18,36 @@ export const getTypeaheadParams = (
return [table, column];
};

export const useTypeaheadSuggestions = () =>
useCallback<SuggestionFetcher>(async (params: TypeaheadParams) => {
const rpcMessage: VuuRpcServiceRequest =
params.length === 2
? {
type: "RPC_CALL",
service: "TypeAheadRpcHandler",
method: "getUniqueFieldValues",
params,
}
: {
type: "RPC_CALL",
service: "TypeAheadRpcHandler",
method: "getUniqueFieldValuesStartingWith",
params,
};
return ConnectionManager.makeRpcCall<string[]>(rpcMessage);
}, []);
export const useTypeaheadSuggestions = () => {
const { getServerAPI } = useDataSource();
return useCallback<SuggestionFetcher>(
async (params: TypeaheadParams) => {
const rpcMessage: VuuRpcServiceRequest =
params.length === 2
? {
type: "RPC_CALL",
service: "TypeAheadRpcHandler",
method: "getUniqueFieldValues",
params,
}
: {
type: "RPC_CALL",
service: "TypeAheadRpcHandler",
method: "getUniqueFieldValuesStartingWith",
params,
};

try {
const serverAPI = await getServerAPI();
// We don't just return serverAPI.rpcCall . In the case of an
// error we will be returning the rejected promise, bypassing
// the catch block below.
const response = await serverAPI.rpcCall<string[]>(rpcMessage);
return response;
} catch (err) {
return false;
}
},
[getServerAPI],
);
};
8 changes: 0 additions & 8 deletions vuu-ui/packages/vuu-data-remote/src/ConnectionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,14 +230,6 @@ class ConnectionManager extends EventEmitter<ConnectionEvents> {
destroy() {
this.#worker.terminate();
}

async makeRpcCall<T = unknown>(rpcRequest: VuuRpcServiceRequest) {
try {
return this.asyncRequest<T>(rpcRequest);
} catch (err) {
throw Error("Error accessing server api");
}
}
}

export default ConnectionManager.instance;
28 changes: 21 additions & 7 deletions vuu-ui/packages/vuu-data-remote/src/WebSocketConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,36 @@ import { VuuClientMessage, VuuServerMessage } from "@finos/vuu-protocol-types";
import { DeferredPromise, EventEmitter } from "@finos/vuu-utils";

export type ConnectingStatus = "connecting" | "reconnecting";
export type RetryStatus = ConnectingStatus | "disconnected";
export type ConnectedStatus = "connected" | "reconnected";
export type ConnectionStatus =
| RetryStatus
| ConnectedStatus
| "closed"
| "connection-open-awaiting-session"
| "disconnected"
| "failed"
| "inactive";

type InternalConnectionStatus = ConnectionStatus | ConnectingStatus;

type ReconnectAttempts = {
retryAttemptsTotal: number;
retryAttemptsRemaining: number;
secondsToNextRetry: number;
};

export interface WebSocketConnectionState extends ReconnectAttempts {
export interface WebSocketConnectionState<
T extends InternalConnectionStatus = ConnectionStatus,
> extends ReconnectAttempts {
connectionPhase: ConnectingStatus;
connectionStatus: ConnectionStatus;
connectionStatus: T;
}

const isNotConnecting = (
connectionState: WebSocketConnectionState<InternalConnectionStatus>,
): connectionState is WebSocketConnectionState<ConnectionStatus> =>
connectionState.connectionStatus !== "connecting" &&
connectionState.connectionStatus !== "reconnecting";

export const isWebSocketConnectionMessage = (
msg: object | WebSocketConnectionState,
): msg is WebSocketConnectionState => {
Expand Down Expand Up @@ -96,7 +105,7 @@ export class WebSocketConnection extends EventEmitter<WebSocketConnectionEvents>
a proxy.
*/
#confirmedOpen = false;
#connectionState: WebSocketConnectionState;
#connectionState: WebSocketConnectionState<InternalConnectionStatus>;
#connectionTimeout;
#deferredConnection?: DeferredPromise;
#protocols;
Expand Down Expand Up @@ -166,12 +175,16 @@ export class WebSocketConnection extends EventEmitter<WebSocketConnectionEvents>
return this.#connectionState.connectionStatus;
}

private set status(connectionStatus: ConnectionStatus) {
private set status(connectionStatus: InternalConnectionStatus) {
this.#connectionState = {
...this.#connectionState,
connectionStatus,
};
this.emit("connection-status", this.#connectionState);
// we don't publish the connecting states. They have little meaning for clients
// and are will generally be very short-lived.
if (isNotConnecting(this.#connectionState)) {
this.emit("connection-status", this.#connectionState);
}
}

get connectionState() {
Expand Down Expand Up @@ -253,6 +266,7 @@ export class WebSocketConnection extends EventEmitter<WebSocketConnectionEvents>
} else {
this.emit("reconnected");
}
console.log("connected");
};
ws.onerror = () => {
clearTimeout(timer);
Expand Down
69 changes: 53 additions & 16 deletions vuu-ui/packages/vuu-data-remote/src/rest-data/rest-data-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@ import {
import { NDJsonReader, jsonToDataSourceRow } from "./rest-utils";
import { MovingWindow } from "./moving-window";

export type RestMetaData = {
recordCount: number;
};

export class RestDataSource
extends EventEmitter<DataSourceEvents>
implements DataSource
{
private static _url = "/api";
private static _api = "/api";

private clientCallback: SubscribeCallback | undefined;
#columnMap: ColumnMap = buildColumnMap([
Expand Down Expand Up @@ -70,8 +74,7 @@ export class RestDataSource
}) {
super();

if (!table)
throw Error("RemoteDataSource constructor called without table");
if (!table) throw Error("RestDataSource constructor called without table");

this.table = table;
this.viewport = viewport;
Expand All @@ -91,11 +94,29 @@ export class RestDataSource
this.#title = title;
}

static get url() {
return this._url;
private get pageSize() {
return this.#range.to - this.#range.from;
}
static set url(url: string) {
this._url = url;

static get api() {
return this._api;
}

static set api(url: string) {
this._api = url;
}

get url() {
return `${RestDataSource.api}/${this.table.table}`;
}

get dataUrl() {
const { from, to } = this.#range;
return `${this.url}?origin=${from}&limit=${to - from}`;
}

get metaDataUrl() {
return `${this.url}/summary`;
}

get title() {
Expand Down Expand Up @@ -141,11 +162,16 @@ export class RestDataSource
if (range.from !== this.#range.from || range.to !== this.#range.to) {
this.#range = range;
this.#dataWindow.setRange(range);
this.sendRowsToClient();
this.fetchData();
}
}

private fetchData() {
private fetchData = async () => {
const { recordCount } = await this.fetchMetaData();

const pageCount = Math.ceil(recordCount / this.pageSize);
this.emit("page-count", pageCount);

const start = performance.now();
const allDone = () => {
const end = performance.now();
Expand All @@ -155,18 +181,16 @@ export class RestDataSource
this.clientCallback?.({
clientViewportId: this.viewport,
mode: "update",
range: this.#range,
rows: this.#dataWindow.data,
size: 200000,
size: recordCount,
type: "viewport-update",
});
};

console.log(`base ${RestDataSource.url}`);

const url = `${RestDataSource.url}/${this.table.table}`;
// const summaryUrl = `${url}/summary`;
console.log(`base ${RestDataSource.api}`);

fetch(url, {
fetch(this.dataUrl, {
mode: "cors",
}).then(
NDJsonReader(
Expand All @@ -178,7 +202,20 @@ export class RestDataSource
allDone,
),
);
}
};

private fetchMetaData = async () =>
new Promise<RestMetaData>((resolve, reject) => {
fetch(this.metaDataUrl, {
mode: "cors",
}).then((response) => {
if (response.ok) {
resolve(response.json());
} else {
reject(response.status);
}
});
});

private sendRowsToClient() {
console.log(`send rows to client`);
Expand Down
Loading

0 comments on commit f3d4838

Please sign in to comment.