Skip to content

Commit 51f1277

Browse files
hoophalabdavemarco
andauthored
feat(webui): Add support for submitting Presto SQL queries. (#1127)
Co-authored-by: davemarco <[email protected]>
1 parent 4a5b471 commit 51f1277

File tree

9 files changed

+279
-2
lines changed

9 files changed

+279
-2
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import axios, {AxiosResponse} from "axios";
2+
3+
4+
// eslint-disable-next-line no-warning-comments
5+
// TODO: Replace with shared type from the `@common` directory once refactoring is completed.
6+
// Currently, server schema types require typebox dependency so they cannot be moved to the
7+
// `@common` directory with current implementation.
8+
type PrestoQueryJobCreationSchema = {
9+
queryString: string;
10+
};
11+
12+
type PrestoQueryJobSchema = {
13+
searchJobId: string;
14+
};
15+
16+
17+
/**
18+
* Sends post request to server to submit presto query.
19+
*
20+
* @param payload
21+
* @return
22+
*/
23+
const submitQuery = async (
24+
payload: PrestoQueryJobCreationSchema
25+
): Promise<AxiosResponse<PrestoQueryJobSchema>> => {
26+
console.log("Submitting query:", JSON.stringify(payload));
27+
28+
return axios.post<PrestoQueryJobSchema>("/api/presto-search/query", payload);
29+
};
30+
31+
export type {
32+
PrestoQueryJobCreationSchema,
33+
PrestoQueryJobSchema,
34+
};
35+
36+
export {submitQuery};

components/webui/client/src/pages/SearchPage/SearchControls/Presto/RunButton/index.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import {useCallback} from "react";
2+
13
import {CaretRightOutlined} from "@ant-design/icons";
24
import {
35
Button,
46
Tooltip,
57
} from "antd";
68

79
import useSearchStore from "../../../SearchState/index";
10+
import {handlePrestoQuerySubmit} from "../presto-search-requests";
811

912

1013
/**
@@ -20,6 +23,10 @@ const RunButton = () => {
2023
"Enter SQL query to run" :
2124
"";
2225

26+
const handleClick = useCallback(() => {
27+
handlePrestoQuerySubmit({queryString});
28+
}, [queryString]);
29+
2330
return (
2431
<Tooltip title={tooltipTitle}>
2532
<Button
@@ -28,6 +35,7 @@ const RunButton = () => {
2835
icon={<CaretRightOutlined/>}
2936
size={"large"}
3037
variant={"solid"}
38+
onClick={handleClick}
3139
>
3240
Run
3341
</Button>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {
2+
type PrestoQueryJobCreationSchema,
3+
submitQuery,
4+
} from "../../../../api/presto-search";
5+
6+
7+
/**
8+
* Submits a new Presto query to server.
9+
*
10+
* @param payload
11+
*/
12+
const handlePrestoQuerySubmit = (payload: PrestoQueryJobCreationSchema) => {
13+
submitQuery(payload)
14+
.then((result) => {
15+
const {searchJobId} = result.data;
16+
console.debug(
17+
"Presto search job created - ",
18+
"Search job ID:",
19+
searchJobId
20+
);
21+
})
22+
.catch((err: unknown) => {
23+
console.error("Failed to submit query:", err);
24+
});
25+
};
26+
27+
export {handlePrestoQuerySubmit};

components/webui/server/package-lock.json

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/webui/server/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
"@aws-sdk/s3-request-presigner": "^3.758.0",
2323
"@fastify/autoload": "^6.3.0",
2424
"@fastify/env": "^5.0.2",
25+
"@fastify/http-proxy": "^11.3.0",
2526
"@fastify/mongodb": "^9.0.2",
2627
"@fastify/mysql": "^5.0.2",
2728
"@fastify/rate-limit": "^10.2.2",
28-
"@fastify/http-proxy": "^11.3.0",
2929
"@fastify/sensible": "^6.0.3",
3030
"@fastify/static": "^8.1.1",
3131
"@fastify/type-provider-typebox": "^5.1.0",
@@ -38,10 +38,12 @@
3838
"fastify-plugin": "^5.0.1",
3939
"http-status-codes": "^2.3.0",
4040
"pino-pretty": "^13.0.0",
41+
"presto-client": "^1.1.0",
4142
"socket.io": "^4.8.1",
4243
"typescript": "~5.7.3"
4344
},
4445
"devDependencies": {
46+
"@types/presto-client": "^1.0.2",
4547
"concurrently": "^9.1.2",
4648
"eslint-config-yscope": "latest",
4749
"fastify-cli": "^7.4.0",

components/webui/server/settings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,9 @@
1616
"StreamTargetUncompressedSize": 134217728,
1717
"StreamFilesS3Region": null,
1818
"StreamFilesS3PathPrefix": null,
19-
"StreamFilesS3Profile": null
19+
"StreamFilesS3Profile": null,
20+
21+
"ClpQueryEngine": "native",
22+
"PrestoHost": "localhost",
23+
"PrestoPort": 8889
2024
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import fp from "fastify-plugin";
2+
import {
3+
Client,
4+
ClientOptions,
5+
} from "presto-client";
6+
7+
import settings from "../../../settings.json" with {type: "json"};
8+
9+
10+
/**
11+
* Class to manage Presto client connections.
12+
*/
13+
class Presto {
14+
readonly client;
15+
16+
/**
17+
* @param clientOptions
18+
*/
19+
constructor (clientOptions: ClientOptions) {
20+
this.client = new Client(clientOptions);
21+
}
22+
}
23+
24+
declare module "fastify" {
25+
interface FastifyInstance {
26+
Presto?: Presto;
27+
}
28+
}
29+
30+
export default fp(
31+
(fastify) => {
32+
if ("presto" !== settings.ClpQueryEngine) {
33+
return;
34+
}
35+
36+
const clientOptions: ClientOptions = {
37+
host: settings.PrestoHost,
38+
port: settings.PrestoPort,
39+
};
40+
41+
fastify.log.info(
42+
clientOptions,
43+
"Initializing Presto"
44+
);
45+
fastify.decorate("Presto", new Presto(clientOptions));
46+
},
47+
);
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {FastifyPluginAsyncTypebox} from "@fastify/type-provider-typebox";
2+
import {StatusCodes} from "http-status-codes";
3+
4+
import {ErrorSchema} from "../../../schemas/error.js";
5+
import {
6+
PrestoQueryJobCreationSchema,
7+
PrestoQueryJobSchema,
8+
} from "../../../schemas/presto-search.js";
9+
10+
11+
/**
12+
* Presto search API routes.
13+
*
14+
* @param fastify
15+
*/
16+
const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
17+
const {Presto} = fastify;
18+
19+
if ("undefined" === typeof Presto) {
20+
// If Presto client is not available, skip the plugin registration.
21+
return;
22+
}
23+
24+
/**
25+
* Submits a search query.
26+
*/
27+
fastify.post(
28+
"/query",
29+
{
30+
schema: {
31+
body: PrestoQueryJobCreationSchema,
32+
response: {
33+
[StatusCodes.CREATED]: PrestoQueryJobSchema,
34+
[StatusCodes.INTERNAL_SERVER_ERROR]: ErrorSchema,
35+
},
36+
tags: ["Presto Search"],
37+
},
38+
},
39+
40+
async (request, reply) => {
41+
const {queryString} = request.body;
42+
43+
let searchJobId: string;
44+
45+
try {
46+
searchJobId = await new Promise<string>((resolve, reject) => {
47+
let isResolved = false;
48+
49+
Presto.client.execute({
50+
// eslint-disable-next-line no-warning-comments
51+
// TODO: Data, error, and success handlers are dummy implementations
52+
// and will be replaced with proper implementations.
53+
data: (_, data, columns) => {
54+
request.log.info({columns, data}, "Presto data");
55+
},
56+
error: (error) => {
57+
request.log.info(error, "Presto search failed");
58+
if (false === isResolved) {
59+
isResolved = true;
60+
reject(new Error("Presto search failed"));
61+
}
62+
},
63+
query: queryString,
64+
state: (_, queryId, stats) => {
65+
request.log.info({
66+
searchJobId: queryId,
67+
state: stats.state,
68+
}, "Presto search state updated");
69+
70+
if (false === isResolved) {
71+
isResolved = true;
72+
resolve(queryId);
73+
}
74+
},
75+
success: () => {
76+
request.log.info("Presto search succeeded");
77+
},
78+
});
79+
});
80+
} catch (error) {
81+
request.log.error(error, "Failed to submit Presto query");
82+
throw error;
83+
}
84+
85+
reply.code(StatusCodes.CREATED);
86+
87+
return {searchJobId};
88+
}
89+
);
90+
};
91+
92+
export default plugin;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {Type} from "@sinclair/typebox";
2+
3+
import {StringSchema} from "./common.js";
4+
5+
6+
/**
7+
* Schema for request to create a new Presto query job.
8+
*/
9+
const PrestoQueryJobCreationSchema = Type.Object({
10+
queryString: StringSchema,
11+
});
12+
13+
/**
14+
* Schema to identify a Presto query job.
15+
*/
16+
const PrestoQueryJobSchema = Type.Object({
17+
searchJobId: StringSchema,
18+
});
19+
20+
export {
21+
PrestoQueryJobCreationSchema,
22+
PrestoQueryJobSchema,
23+
};

0 commit comments

Comments
 (0)