Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions src/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { KdbRequest } from "./model/kdb-request";
import { QueryParam } from "./model/query-param";
import { QueryDictionary } from "./model/queryDictionary";
import { ConflationParams } from "./model/conflationParams";
import { graphFunction } from './model/kdb-request-config';
import { graphFunction,graphFormatFunc, tableFormatFunc } from './model/kdb-request-config';
import { conflationDurationDefault, conflationUnitDefault } from './query_ctrl';
import { tabFunction,defaultTimeout,kdbEpoch,durationMap } from './model/kdb-request-config';
import { tabFunction,defaultTimeout,kdbEpoch,durationMap,defaultPostback } from './model/kdb-request-config';
export class KDBDatasource {
//This is declaring the types of each member
id: any;
Expand Down Expand Up @@ -69,6 +69,10 @@ export class KDBDatasource {
private variablesReplace(target:any, search: string, replace:any) {
//Format Options as array or scalar
// console.log('VARIABLESREPLACE TARGET: ', target)
// if target doesn't have queryType as a key, it is not initialised yet, so skip injection
if (!target.queryType) {
return
}
if (Array.isArray(replace)) {
target.kdbFunction = target.kdbFunction.replace(search, replace.join(','))
} else {
Expand Down Expand Up @@ -217,7 +221,9 @@ export class KDBDatasource {
queryParam.column = this.buildColumnParams(target);
queryParam.temporal_field = target.useTemporalField ? this.buildTemporalField(target) : [];
queryParam.temporal_range = this.buildTemporalRange(target.range);
queryParam.maxRowCount = target.rowCountLimit
queryParam.maxRowCount = target.rowCountLimit;
queryParam.postbackFunction = target.postbackFunction;

if (target.queryType == 'selectQuery') queryParam.where = this.buildWhereParams(target.where);
//conflation
if (target.useConflation) {
Expand Down Expand Up @@ -247,8 +253,14 @@ export class KDBDatasource {
kdbRequest.query = ''//query;
kdbRequest.queryParam = Object.assign({}, queryParam);
kdbRequest.format = target.format;
kdbRequest.formatFunc = target.format == 'time series' ? graphFormatFunc : tableFormatFunc;
kdbRequest.queryId = target.queryId;
kdbRequest.version = target.version;
kdbRequest.useAsyncFunction = (typeof target.useAsyncFunction === 'undefined') ? false : target.useAsyncFunction;
kdbRequest.useCustomPostback = (typeof target.useCustomPostback === 'undefined') ? false : target.useCustomPostback;
kdbRequest.asyncProcTypes = (typeof target.asyncProcTypes === 'undefined') ? '' : target.asyncProcTypes.split("`").splice(1).map(proc => "`"+proc);
//kdbRequest.asyncPostbackFunction = (typeof target.useCustomPostback === 'undefined') ? '' : target.useCustomPostback ? defaultPostback : target.postbackFunction;
kdbRequest.asyncPostbackFunction = kdbRequest.useCustomPostback ? target.postbackFunction : defaultPostback;

return [
((target.format == 'time series') ? graphFunction : tabFunction),
Expand Down Expand Up @@ -553,6 +565,8 @@ export class KDBDatasource {
//Response parser called here**********************
private getQueryResult = (request: any): Promise<Object> => {
let curRequest = request;
console.log('getQueryResult REQUEST:')
console.log(request)
let timeoutError = "Query sent at " + new Date() + " timed out.";
let malformedResError = "Malformed response. Check KDB+ WebSocket handler is correctly configured."
let response = new Promise(resolve => {
Expand All @@ -572,6 +586,7 @@ export class KDBDatasource {
}

connectWS() {
console.log('connectWS');
return new Promise (connected => {
this.ws = new WebSocket(this.wsUrl);
this.ws.binaryType = 'arraybuffer';
Expand All @@ -580,6 +595,7 @@ export class KDBDatasource {
};

this.ws.onopen = () => {
console.log('open WS')
connected(true);
}

Expand All @@ -603,9 +619,15 @@ export class KDBDatasource {
executeAsyncQuery(request: any) {
var requestResolve;
let _c = this.c;
console.log('executeAsyncQuery: REQUEST:')
console.log(request);
var requestPromise = new Promise(resolve => {
let refIDn = Math.round(10000000 * Math.random());
var wrappedRequest = {i:request, ID:refIDn};
let formatter: string = typeof(request) == 'string' ? '{[x;y] :x}' : request[1].formatFunc;
let asyncbool:boolean = typeof(request) == 'string' ? false : request[1].useAsyncFunction;
var wrappedRequest = {async: asyncbool, formatFunc:formatter, i:request, ID:refIDn};
console.log('WRAPPED REQUEST: ')
console.log(wrappedRequest)
this.ws.send(_c.serialize(wrappedRequest));
this.requestSentIDList.push(refIDn);
requestResolve = resolve;
Expand All @@ -619,11 +641,20 @@ export class KDBDatasource {

executeAsyncReceive(responseObj) {
let _c = this.c;
console.log('executeAsyncReceive: RESPONSE:')
console.log(responseObj);
let deserializedResult = _c.deserialize(responseObj.data);
if (Array.isArray(deserializedResult)) {
if (deserializedResult.length !== 3) {
return console.log('received malformed data (array)')
}
//deserializedResult = { ...deserializedResult[0], ...deserializedResult[2] }
deserializedResult = { ID: deserializedResult[0], o:deserializedResult[2].o}
};
if (!deserializedResult.ID) {
// return console.log('received malformed data')
return console.log('received malformed data')
} else if (this.requestSentIDList.indexOf(deserializedResult.ID) === -1) {
// return console.log('received unrequested data');
return console.log('received unrequested data');
} else {
var requestNum = this.requestSentIDList.indexOf(deserializedResult.ID);
this.requestSentList[requestNum].resolve(deserializedResult.o);
Expand Down Expand Up @@ -698,13 +729,15 @@ export class KDBDatasource {

//This is the function called by Grafana when it is testing a connection on the configuration page
testDatasource() {
console.log('testDatasource')
return this.connect()
.then((result) => {
return result;
});
};

connect(): Promise<Object> {
console.log('connect')
return new Promise<Object>((resolve, reject) => {
if ("WebSocket" in window) {
this.$q.when(this.setupWebSocket()).then(setTimeout(() => {
Expand All @@ -721,6 +754,7 @@ export class KDBDatasource {

//This checks the kdb+ connection state for the 'test connection' funciton
checkConnectionState(): Promise<Object> {
console.log('checkconnectionstate')
return new Promise(resolve => {
return this.connectWS().then(connectStatus => {
if (connectStatus === false) {
Expand All @@ -733,7 +767,9 @@ export class KDBDatasource {
}, this.timeoutLength)
});
let response = new Promise(resolve => {
console.log('running .z.ws query')
this.executeAsyncQuery('.z.ws').then(res => {
console.log('res: ',res)
if (typeof res !== 'string') {
resolve(this.buildResponse('fail', 'Malformed response. Check KDB+ WebSocket handler is correctly configured.', 'Fail'));
} else if (res.replace(' ', '').includes('ds:-9!x;')) {
Expand Down
58 changes: 36 additions & 22 deletions src/model/kdb-request-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,43 @@ export const durationMap = {
h: 3600 * Math.pow(10,9)
}

export const defaultPostback : string = '{[query;formatter;id] .dg.dev.r:.gw.asyncexecjpt[.dg.dev.query:@[query;0;value];.dg.dev.proctypes:query[1;`asyncProcTypes],();{[res;id;fmt] -8!`o`ID!(fmt[raze res;""];id)}[;id;formatter];.dg.dev.pb:();.dg.dev.timeout:0Wn]}'

export const callbackHandler: string = '.z.ws:{[x] \n ' +
' ds:-9!x; \n '+
' fmtter:@[value;ds[`formatFunc];{`$"\'",x}]; \n ' +
' r:$[ds`async; \n ' +
' @[value;ds[`i;1;`asyncPostbackFunction];{`$"\'",x}][ds[`i];fmtter;ds`ID]; \n ' +
' `o`ID!(fmtter[;ds`queryId] @[value;ds[`i];{`$"\'",x}];ds[`ID])]; \n ' +
' if[not ds`async;neg[.z.w] -8! r] \n '+
' }';

// .z.ws:{[x] ds:-9!x;fmtter:@[value;ds[`formatFunc];{`$"'",x}];r:$[ds`async;@[value;ds[`i;1;`asyncPostbackFunction];{`$"'",x}][ds[`i];fmtter;ds`ID];`o`ID!(fmtter[;ds`queryId] @[value;ds[`i];{`$"'",x}];ds[`ID])];if[not ds`async;neg[.z.w] -8! r]}

export const graphFormatFunc: string = '{ \n ' +
` .[x;(y;z);{'\"Function:format - Error:\",x}]}[{[x;gfid] \n ` +
' \n ' +
' if[not .Q.qt x;:x];\n ' +
' t:$[99h=type x; \n ' +
' key[x]!([]data:flip each value x); \n ' +
' ([id:1#`x]data:`#enlist x)]; \n ' +
' :`payload`id`error`success!(t;gfid;$[(99h=type t)and(0<count x);\"OK\";\"NOT OK - Final table fault\"];1b); \n ' +
' }]'

export const tableFormatFunc:string = '{ \n ' +
` .[x;(y;z);{'\"Function:format - Error Type:\",x}]}[{[x;gfid] \n ` +
' \n ' +
' if[not .Q.qt x;:x];\n ' +
' t:0!x; \n ' +
' rows:enlist{value x}each t; \n ' +
' columns:enlist ([]text:key flip t); \n ' +
' r:`columns`rows!(columns;rows); \n ' +
' :`payload`id`error`success!(r;gfid;$[0<count x;\"OK\";\"NOT OK - Final table fault\"];1b); \n ' +
' }]'

//Graph response type
export const graphFunction: string = '{@[x;y;{`payload`error`success!(();"Error! - ",x;0b)}]}{[dict] \n ' +
' \n ' +
' \n ' +
' conc:{ \n ' +
` .[x;(y;z);{'\"Function:conc - Error:\",x}]}[{[d;raw] \n ` +
' \n ' +
Expand Down Expand Up @@ -66,15 +99,6 @@ export const graphFunction: string = '{@[x;y;{`payload`error`success!(();"Error
' :$[99h=type g;$[end;g;{x!x}value g];{$[x~();0b;x!x]}distinct g]; \n ' +
' }]; \n ' +
' \n ' +
' format:{ \n ' +
` .[x;(y;z);{'\"Function:format - Error:\",x}]}[{[x;gfid] \n ` +
' \n ' +
' t:$[99h=type x; \n ' +
' key[x]!([]data:flip each value x); \n ' +
' ([id:1#`x]data:`#enlist x)]; \n ' +
' :`payload`id`error`success!(t;gfid;$[(99h=type t)and(0<count x);\"OK\";\"NOT OK - Final table fault\"];1b); \n ' +
' }]; \n ' +
' \n ' +
' end:0b; \n ' +
' control:`rowlimit`gfid! \n ' +
' (dict[`queryParam;`maxRowCount];dict[`queryId]); \n ' +
Expand Down Expand Up @@ -178,7 +202,7 @@ export const graphFunction: string = '{@[x;y;{`payload`error`success!(();"Error
' funcparts[`c]:{x!x}cols[raw]except d`grouping \n ' +
' ]; \n ' +
' final:?[raw;();funcparts[`b];funcparts[`c]]; \n ' +
' :format[final;control`gfid]; \n ' +
' :final; \n ' +
' }';

//Table response type
Expand Down Expand Up @@ -237,16 +261,6 @@ export const tabFunction: string = '{@[x;y;{`payload`error`success!(();"Error! -
' :$[99h=type g;$[end;g;{x!x}value g];{$[x~();0b;x!x]}distinct g]; \n ' +
' }]; \n ' +
' \n ' +
' format:{ \n ' +
` .[x;(y;z);{'\"Function:format - Error Type:\",x}]}[{[x;gfid] \n ` +
' \n ' +
' t:0!x; \n ' +
' rows:enlist{value x}each t; \n ' +
' columns:enlist ([]text:key flip t); \n ' +
' r:`columns`rows!(columns;rows); \n ' +
' :`payload`id`error`success!(r;gfid;$[0<count x;\"OK\";\"NOT OK - Final table fault\"];1b); \n ' +
' }]; \n ' +
' \n ' +
' end:0b; \n ' +
' control:`rowlimit`gfid! \n ' +
' (dict[`queryParam;`maxRowCount];dict[`queryId]); \n ' +
Expand Down Expand Up @@ -341,7 +355,7 @@ export const tabFunction: string = '{@[x;y;{`payload`error`success!(();"Error! -
' if[qt=`select;final:{[t]((cols t)[0],{`$x} each count[1_cols t]#.Q.a) xcol t}final]; \n ' +
' final \n ' +
' ; \n ' +
' :format[final;control`gfid]; \n ' +
' :final; \n ' +
' } \n ' +
'';

Expand Down
5 changes: 5 additions & 0 deletions src/model/kdb-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,10 @@ export class KdbRequest {
query: string;
queryParam: QueryParam;
format: string;
formatFunc: string;
version: string;
useAsyncFunction: boolean;
useCustomPostback: boolean;
asyncProcTypes: string[];
asyncPostbackFunction: string;
}
1 change: 1 addition & 0 deletions src/model/query-param.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export class QueryParam {
conflation: any;
query: QueryDictionary;
maxRowCount: number | string;
postbackFunction: string;
}
34 changes: 34 additions & 0 deletions src/partials/query.editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,40 @@
</div>
</div>
</div>

<div class="gf-form-inline" ng-if="ctrl.target.queryType=='functionQuery'">
<div class="gf-form">
<label class="gf-form-label width-13">
<span><input type="checkbox" class="width-2" ng-model="ctrl.target.useAsyncFunction" ng-change="ctrl.asyncFieldChanged()" value="useAsyncFunction"></span>
<span style="margin-right: 20px;">Use Async with Postback</span>
<info-popover mode="right-absolute">
This allows use of asynchronous functions provided they utilise a postback function. Enable 'Custom Postback' if using non-TorQ Gateway.
</info-popover>
</label>
</div>
<div ng-if="ctrl.target.useAsyncFunction" class="gf-form">
<span class="gf-form-label query-keyword width-10">Proc Types:</span>
<input type="text" class="gf-form-input width-10" ng-model='ctrl.target.asyncProcTypes' placeholder="Proc Types" ng-change="ctrl.asyncFieldChanged()"></input>
</div>
<div ng-if="ctrl.target.useAsyncFunction" class="gf-form">
<label class="gf-form-label width-13">
<span><input type="checkbox" class="width-2" ng-model="ctrl.target.useCustomPostback" ng-change="ctrl.asyncFieldChanged()" value="useCustomPostback"></span>
<span style="margin-right: 20px;">Custom Postback</span>
</label>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline" ng-if="ctrl.target.useAsyncFunction && ctrl.target.useCustomPostback">
<div style="height:111px; margin-left:210px ">
<textarea type="text" class="gf-form-textarea width-30" rows="5" style="background-color:#0b0c0e" ng-model='ctrl.target.postbackFunction' placeholder="Enter function" ng-change="ctrl.asyncFieldChanged()"></textarea>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow" style="height:111px"></div>
</div>
</div>

<div ng-if="ctrl.target.format!=='table'">
<div class="gf-form-inline">
<div class="gf-form">
Expand Down
5 changes: 5 additions & 0 deletions src/query_ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export class KDBQueryCtrl extends QueryCtrl {
{text: 'Hours', value: 'h'}];

this.target.version = this.datasource.meta.info.version;
this.target.postbackFunction = ''

//If queryError isn't present, build it
if(!this.target.queryError) {
Expand Down Expand Up @@ -772,4 +773,8 @@ export class KDBQueryCtrl extends QueryCtrl {
this.panelCtrl.refresh();
}

asyncFieldChanged() {
this.panelCtrl.refresh();
}

}