Skip to content

Commit ca37d8f

Browse files
run UDAs
1 parent fb01082 commit ca37d8f

File tree

9 files changed

+302
-10
lines changed

9 files changed

+302
-10
lines changed

src/classes/insightsConnection.ts

Lines changed: 157 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { MetaInfoType, MetaObject, MetaObjectPayload } from "../models/meta";
1919
import {
2020
getCurrentToken,
2121
getHttpsAgent,
22+
IToken,
2223
} from "../services/kdbInsights/codeFlowLogin";
2324
import { InsightsNode } from "../services/kdbTreeProvider";
2425
import { GetDataObjectPayload } from "../models/data";
@@ -38,6 +39,7 @@ import { InsightsConfig, InsightsEndpoints } from "../models/config";
3839
import { convertTimeToTimestamp } from "../utils/dataSource";
3940
import { ScratchpadRequestBody } from "../models/scratchpad";
4041
import { StructuredTextResults } from "../models/queryResult";
42+
import { UDARequestBody } from "../models/uda";
4143

4244
export class InsightsConnection {
4345
public connected: boolean;
@@ -160,13 +162,15 @@ export class InsightsConnection {
160162
import: "servicebroker/scratchpad/import/data",
161163
importSql: "servicebroker/scratchpad/import/sql",
162164
importQsql: "servicebroker/scratchpad/import/qsql",
165+
importUDA: "servicebroker/scratchpad/import/uda",
163166
reset: "servicebroker/scratchpad/reset",
164167
},
165168
serviceGateway: {
166169
meta: "servicegateway/meta",
167170
data: "servicegateway/data",
168171
sql: "servicegateway/qe/sql",
169172
qsql: "servicegateway/qe/qsql",
173+
udaBase: "servicegateway/",
170174
},
171175
};
172176
// uncomment this WHEN the insights version is available
@@ -395,6 +399,157 @@ export class InsightsConnection {
395399
}
396400
}
397401

402+
public async generateToken(): Promise<IToken | undefined> {
403+
const token = await getCurrentToken(
404+
this.node.details.server,
405+
this.node.details.alias,
406+
this.node.details.realm || "insights",
407+
!!this.node.details.insecure,
408+
);
409+
if (token === undefined) {
410+
tokenUndefinedError(this.connLabel);
411+
}
412+
return token;
413+
}
414+
415+
public async getUDAQuery(
416+
udaReqBody: UDARequestBody,
417+
): Promise<any | undefined> {
418+
if (this.connected && this.connEndpoints) {
419+
const udaEndpoint =
420+
this.connEndpoints.serviceGateway.udaBase +
421+
udaReqBody.name.split(".").slice(1).join("/");
422+
const udaURL = new url.URL(udaEndpoint, this.node.details.server);
423+
const token = await this.generateToken();
424+
if (token === undefined) {
425+
return undefined;
426+
}
427+
428+
const headers = {
429+
Authorization: `Bearer ${token.accessToken}`,
430+
Accept: "application/octet-stream",
431+
"Content-Type": "application/json",
432+
};
433+
434+
const body = udaReqBody.params;
435+
436+
const options: AxiosRequestConfig = {
437+
method: "post",
438+
url: udaURL.toString(),
439+
data: body,
440+
headers: headers,
441+
responseType: "arraybuffer",
442+
httpsAgent: getHttpsAgent(this.node.details.insecure),
443+
};
444+
const results = await window.withProgress(
445+
{
446+
location: ProgressLocation.Notification,
447+
cancellable: false,
448+
},
449+
async (progress, token) => {
450+
token.onCancellationRequested(() => {
451+
kdbOutputLog(`User cancelled the Datasource Run.`, "WARNING");
452+
});
453+
454+
progress.report({ message: "Query executing..." });
455+
456+
return await axios(options)
457+
.then((response: any) => {
458+
kdbOutputLog(
459+
`[Datasource RUN] Status: ${response.status}.`,
460+
"INFO",
461+
);
462+
if (isCompressed(response.data)) {
463+
response.data = uncompress(response.data);
464+
}
465+
return {
466+
error: "",
467+
arrayBuffer: response.data.buffer
468+
? response.data.buffer
469+
: response.data,
470+
};
471+
})
472+
.catch((error: any) => {
473+
kdbOutputLog(
474+
`[Datasource RUN] Status: ${error.response.status}.`,
475+
"INFO",
476+
);
477+
return {
478+
error: { buffer: error.response.data },
479+
arrayBuffer: undefined,
480+
};
481+
});
482+
},
483+
);
484+
return results;
485+
}
486+
}
487+
488+
public async getUDAScratchpadQuery(
489+
udaReqBody: UDARequestBody,
490+
): Promise<any | undefined> {
491+
if (this.connected && this.connEndpoints) {
492+
const isTableView = udaReqBody.returnFormat === "structuredText";
493+
const udaURL = new url.URL(
494+
this.connEndpoints.scratchpad.importUDA,
495+
this.node.details.server,
496+
);
497+
const token = await this.generateToken();
498+
if (token === undefined) {
499+
return undefined;
500+
}
501+
const username = jwtDecode<JwtUser>(token.accessToken);
502+
if (username === undefined || username.preferred_username === "") {
503+
invalidUsernameJWT(this.connLabel);
504+
}
505+
506+
const headers = {
507+
Authorization: `Bearer ${token.accessToken}`,
508+
Username: username.preferred_username,
509+
"Content-Type": "application/json",
510+
};
511+
512+
const udaResponse = await window.withProgress(
513+
{
514+
location: ProgressLocation.Notification,
515+
cancellable: false,
516+
},
517+
async (_progress, token) => {
518+
token.onCancellationRequested(() => {
519+
kdbOutputLog(`User cancelled the UDA execution.`, "WARNING");
520+
});
521+
522+
const udaRes = await axios
523+
.post(udaURL.toString(), udaReqBody, {
524+
headers,
525+
httpsAgent: getHttpsAgent(this.node.details.insecure),
526+
})
527+
.then((response: any) => {
528+
if (response.data.error) {
529+
return response.data;
530+
} else {
531+
kdbOutputLog(`[UDA] Status: ${response.status}`, "INFO");
532+
if (!response.data.error) {
533+
if (isTableView) {
534+
response.data = JSON.parse(
535+
response.data.data,
536+
) as StructuredTextResults;
537+
}
538+
return response.data;
539+
}
540+
return response.data;
541+
}
542+
});
543+
return udaRes;
544+
},
545+
);
546+
return udaResponse;
547+
} else {
548+
this.noConnectionOrEndpoints();
549+
}
550+
return undefined;
551+
}
552+
398553
public async getScratchpadQuery(
399554
query: string,
400555
context?: string,
@@ -444,7 +599,7 @@ export class InsightsConnection {
444599
"Content-Type": "application/json",
445600
};
446601

447-
const spReponse = await window.withProgress(
602+
const spResponse = await window.withProgress(
448603
{
449604
location: ProgressLocation.Notification,
450605
cancellable: false,
@@ -503,7 +658,7 @@ export class InsightsConnection {
503658
return spRes;
504659
},
505660
);
506-
return spReponse;
661+
return spResponse;
507662
} else {
508663
this.noConnectionOrEndpoints();
509664
}

src/commands/dataSourceCommand.ts

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ import {
5050
offerConnectAction,
5151
} from "../utils/core";
5252
import { ServerType } from "../models/connectionsModels";
53+
import { retrieveDataTypeByString } from "../utils/uda";
54+
import { UDARequestBody } from "../models/uda";
5355

5456
export async function addDataSource(): Promise<void> {
5557
const kdbDataSourcesFolderPath = createKdbDataSourcesFolder();
@@ -161,6 +163,9 @@ export async function runDataSource(
161163
case "QSQL":
162164
res = await runQsqlDataSource(fileContent, selectedConnection);
163165
break;
166+
case "UDA":
167+
res = await runUDADataSource(fileContent, selectedConnection);
168+
break;
164169
case "SQL":
165170
default:
166171
res = await runSqlDataSource(fileContent, selectedConnection);
@@ -179,9 +184,12 @@ export async function runDataSource(
179184
if (success) {
180185
const resultCount = typeof res === "string" ? "0" : res.rows.length;
181186
kdbOutputLog(`[DATASOURCE] Results: ${resultCount} rows`, "INFO");
187+
} else if (!success) {
188+
res = res.errorMsg ? res.errorMsg : res.error;
182189
}
190+
const connVersion = selectedConnection.insightsVersion ?? 0;
183191
await writeQueryResultsToView(
184-
success ? res : res.error,
192+
res,
185193
query,
186194
connLabel,
187195
executorName,
@@ -194,9 +202,13 @@ export async function runDataSource(
194202
`[DATASOURCE] Results is a string with length: ${res.length}`,
195203
"INFO",
196204
);
205+
} else {
206+
if (res.errorMsg) {
207+
res = res.errorMsg;
208+
}
197209
}
198210
await writeQueryResultsToConsole(
199-
success ? res : res.error,
211+
res,
200212
query,
201213
connLabel,
202214
executorName,
@@ -243,6 +255,8 @@ export function getSelectedType(fileContent: DataSourceFiles): string {
243255
return "QSQL";
244256
case DataSourceTypes.SQL:
245257
return "SQL";
258+
case DataSourceTypes.UDA:
259+
return "UDA";
246260
default:
247261
throw new Error(`Invalid selectedType: ${selectedType}`);
248262
}
@@ -411,6 +425,58 @@ export async function runSqlDataSource(
411425
}
412426
}
413427

428+
export async function runUDADataSource(
429+
fileContent: DataSourceFiles,
430+
selectedConn: InsightsConnection,
431+
): Promise<any> {
432+
const UDA = fileContent.dataSource.uda;
433+
const returnFormat = ext.isResultsTabVisible ? "structuredText" : "text";
434+
if (!UDA) {
435+
return { error: "UDA not found" };
436+
}
437+
if (UDA.name === "") {
438+
return { error: "UDA name not found" };
439+
}
440+
441+
const params: { [key: string]: any } = {};
442+
const parameterTypes: { [key: string]: any } = {};
443+
444+
if (UDA.params && UDA.params.length > 0) {
445+
UDA.params.forEach((param) => {
446+
if (param.isVisible) {
447+
params[param.name] = param.value;
448+
parameterTypes[param.name] = param.selectedMultiTypeString
449+
? retrieveDataTypeByString(param.selectedMultiTypeString)
450+
: param.type;
451+
}
452+
});
453+
}
454+
// if (Object.keys(params).length === 0) {
455+
// params["table"] = "nsansUdaTable";
456+
// parameterTypes["table"] = -10;
457+
// }
458+
const udaReqBody: UDARequestBody = {
459+
language: "q",
460+
name: UDA.name,
461+
parameterTypes,
462+
params,
463+
returnFormat,
464+
sampleFn: "first",
465+
sampleSize: 10000,
466+
};
467+
468+
const udaCall = await selectedConn.getUDAQuery(udaReqBody);
469+
470+
if (udaCall?.error) {
471+
return parseError(udaCall.error);
472+
} else if (udaCall?.arrayBuffer) {
473+
const results = handleWSResults(udaCall.arrayBuffer);
474+
return handleScratchpadTableRes(results);
475+
} else {
476+
return { error: "UDA call failed" };
477+
}
478+
}
479+
414480
export function getQuery(
415481
fileContent: DataSourceFiles,
416482
selectedType: string,

src/extensionVariables.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,52 @@ export namespace ext {
290290
"101": "Unary",
291291
}),
292292
),
293+
reverseDataTypes: new Map(
294+
Object.entries({
295+
Boolean: -1,
296+
GUID: -2,
297+
Byte: -4,
298+
Short: -5,
299+
Int: -6,
300+
Long: -7,
301+
Float: -8,
302+
Double: -9,
303+
Char: -10,
304+
Symbol: -11,
305+
Timestamp: -12,
306+
Month: -13,
307+
Date: -14,
308+
DateTime: -15,
309+
Timespan: -16,
310+
Minute: -17,
311+
Second: -18,
312+
Time: -19,
313+
List: 0,
314+
"Boolean List": 1,
315+
"GUID List": 2,
316+
"Byte List": 4,
317+
"Short List": 5,
318+
"Int List": 6,
319+
"Long List": 7,
320+
"Float List": 8,
321+
"Double List": 9,
322+
String: 10,
323+
"Symbol List": 11,
324+
"Timestamp List": 12,
325+
"Month List": 13,
326+
"Date List": 14,
327+
"DateTime List": 15,
328+
"Timespan List": 16,
329+
"Minute List": 17,
330+
"Second List": 18,
331+
"Time List": 19,
332+
"Any Map": 77,
333+
Table: 98,
334+
Dictionary: 99,
335+
Lambda: 100,
336+
Unary: 101,
337+
}),
338+
),
293339
listSeparator: [
294340
";",
295341
"",

src/models/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ export type InsightsEndpoints = {
2626
import: string;
2727
importSql: string;
2828
importQsql: string;
29+
importUDA: string;
2930
reset: string;
3031
};
3132
serviceGateway: {
3233
meta: string;
3334
data: string;
3435
sql: string;
3536
qsql: string;
37+
udaBase: string;
3638
};
3739
};

0 commit comments

Comments
 (0)