Skip to content

Commit

Permalink
Merge pull request #39 from oracle/template_vars
Browse files Browse the repository at this point in the history
Support hardcoded strings in template variables
  • Loading branch information
yuliashmeleva authored Jan 23, 2020
2 parents 667f559 + 617f8b8 commit 8216bc8
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 81 deletions.
1 change: 1 addition & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ GOOS=$GOOS go build -o ./dist/oci-plugin$POST
# For debugger
# GOOS=$GOOS go build -o ./dist/oci-plugin$POST -gcflags="all=-N -l"

# For release
# GOOS=linux go build -o ./dist/oci-plugin_linux_amd64
# GOOS=windows GOARCH=amd64 go build -o ./dist/oci-plugin_windows_amd64.exe
# tar cvf plugin.tar ./dist
Expand Down
4 changes: 2 additions & 2 deletions plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
{"name": "GitHub", "url": "https://github.com/oracle/oci-grafana-plugin"},
{"name": "MIT License", "url": "https://github.com/oracle/oci-grafana-plugin"}
],
"version": "1.0.5",
"updated": "2019-09-24"
"version": "1.0.6",
"updated": "2020-01-23"
},

"dependencies": {
Expand Down
25 changes: 19 additions & 6 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,22 @@ export const windows = ['1m', '5m', '1h']
export const environments = ['local', 'OCI Instance']


export const compartmentsQueryRegex = /^compartments\(\)/;
export const regionsQueryRegex = /^regions\(\)/;
export const namespacesQueryRegex = /namespaces\((\$?\w+)(,\s*\$\w+)*\)/;
export const metricsQueryRegex = /metrics\((\s*\$?\w+)(\s*,\s*\$\w+)(\s*,\s*\$\w+\s*)*\)/;
export const dimensionKeysQueryRegex = /dimensions\((\s*\$?\w+)(\s*,\s*\$\w+)(\s*,\s*\$\w+)(\s*,\s*\$\w+\s*)*\)/;
export const dimensionValuesQueryRegex = /dimensionOptions\((\s*\$?\w+)(\s*,\s*\$\w+)(\s*,\s*\$\w+)(\s*,\s*\$\w+)(\s*,\s*\$\w+\s*)*\)/;
export const compartmentsQueryRegex = /^compartments\(\)\s*/;
export const regionsQueryRegex = /^regions\(\)\s*/;
export const namespacesQueryRegex = /^namespaces\(\s*(\".+\"|\'.+\'|\$\w+)\s*,\s*(\".+\"|\'.+\'|\$\w+)\s*\)/;
export const metricsQueryRegex = /^metrics\(\s*(\".+\"|\'.+\'|\$\w+)\s*,\s*(\".+\"|\'.+\'|\$\w+)\s*,\s*(\".+\"|\'.+\'|\$\w+)\s*\)/;
export const dimensionKeysQueryRegex = /^dimensions\(\s*(\".+\"|\'.+\'|\$\w+)\s*,\s*(\".+\"|\'.+\'|\$\w+)\s*,\s*(\".+\"|\'.+\'|\$\w+)\s*,\s*(\".+\"|\'.+\'|\$\w+)\s*\)/;
export const dimensionValuesQueryRegex = /^dimensionOptions\(\s*(\".+\"|\'.+\'|\$\w+)\s*,\s*(\".+\"|\'.+\'|\$\w+)\s*,\s*(\".+\"|\'.+\'|\$\w+)\s*,\s*(\".+\"|\'.+\'|\$\w+)\s*,\s*(\".+\"|\'.+\'|\$\w+)\s*\)/;

export const removeQuotes = str => {
if (!str) return str;

let res = str;
if (str.startsWith("'") || str.startsWith('"')) {
res = res.slice(1);
}
if (str.endsWith("'") || str.endsWith('"')) {
res = res.slice(0, res.length - 1);
}
return res;
}
114 changes: 75 additions & 39 deletions src/datasource.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
*/
import _ from 'lodash'
import { aggregations, dimensionKeysQueryRegex, namespacesQueryRegex, metricsQueryRegex, regionsQueryRegex, compartmentsQueryRegex, dimensionValuesQueryRegex, adsQueryRegex } from './constants'
import { aggregations, dimensionKeysQueryRegex, namespacesQueryRegex, metricsQueryRegex, regionsQueryRegex, compartmentsQueryRegex, dimensionValuesQueryRegex, removeQuotes } from './constants'
import retryOrThrow from './util/retry'
import { SELECT_PLACEHOLDERS } from './query_ctrl'

Expand All @@ -20,6 +20,12 @@ export default class OCIDatasource {
this.backendSrv = backendSrv
this.templateSrv = templateSrv
this.timeSrv = timeSrv

this.compartmentsCache = [];
this.regionsCache = [];

this.getRegions();
this.getCompartments();
}

/**
Expand All @@ -35,8 +41,8 @@ export default class OCIDatasource {
* Required method
* Used by panels to get data
*/
query(options) {
var query = this.buildQueryParameters(options);
async query(options) {
var query = await this.buildQueryParameters(options);

if (query.targets.length <= 0) {
return this.q.when({ data: [] });
Expand Down Expand Up @@ -88,7 +94,7 @@ export default class OCIDatasource {
* Required method
* Used by query editor to get metric suggestions
*/
metricFindQuery(target) {
async metricFindQuery(target) {
if (typeof (target) === 'string') {
// used in template editor for creating variables
return this.templateMetricQuery(target);
Expand All @@ -102,14 +108,15 @@ export default class OCIDatasource {
return this.q.when([]);
}

const compartmentId = await this.getCompartmentId(compartment);
return this.doRequest({
targets: [{
environment: this.environment,
datasourceId: this.id,
tenancyOCID: this.tenancyOCID,
queryType: 'search',
region: _.isEmpty(region) ? this.defaultRegion : region,
compartment: compartment,
compartment: compartmentId,
namespace: namespace
}],
range: this.timeSrv.timeRange()
Expand All @@ -121,7 +128,7 @@ export default class OCIDatasource {
/**
* Build and validate query parameters.
*/
buildQueryParameters(options) {
async buildQueryParameters(options) {
let queries = options.targets
.filter(t => !t.hide)
.filter(t => !_.isEmpty(this.getVariableValue(t.compartment, options.scopedVars)) && t.compartment !== SELECT_PLACEHOLDERS.COMPARTMENT)
Expand All @@ -137,7 +144,8 @@ export default class OCIDatasource {
// we support multiselect for dimension values, so we need to parse 1 query into multiple queries
queries = this.splitMultiValueDimensionsIntoQuieries(queries, options);

queries = queries.map(t => {
const results = [];
for (let t of queries) {
const region = t.region === SELECT_PLACEHOLDERS.REGION ? '' : this.getVariableValue(t.region, options.scopedVars);
let query = this.getVariableValue(t.target, options.scopedVars);

Expand All @@ -154,7 +162,8 @@ export default class OCIDatasource {
query = `${this.getVariableValue(t.metric, options.scopedVars)}[${t.window}]${dimension}.${t.aggregation}`;
}

return {
const compartmentId = await this.getCompartmentId(this.getVariableValue(t.compartment, options.scopedVars));
const result = {
environment: this.environment,
datasourceId: this.id,
tenancyOCID: this.tenancyOCID,
Expand All @@ -164,13 +173,14 @@ export default class OCIDatasource {
hide: t.hide,
type: t.type || 'timeserie',
region: _.isEmpty(region) ? this.defaultRegion : region,
compartment: this.getVariableValue(t.compartment, options.scopedVars),
compartment: compartmentId,
namespace: this.getVariableValue(t.namespace, options.scopedVars),
query: query
}
});
results.push(result);
};

options.targets = queries;
options.targets = results;

return options;
}
Expand Down Expand Up @@ -276,55 +286,61 @@ export default class OCIDatasource {

let compartmentQuery = varString.match(compartmentsQueryRegex)
if (compartmentQuery) {
return this.getCompartments().catch(err => { throw new Error('Unable to get compartments: ' + err) })
return this.getCompartments().then(compartments => {
return compartments.map(c => ({ text: c.text, value: c.text }));
}).catch(err => { throw new Error('Unable to get compartments: ' + err) })
}

let namespaceQuery = varString.match(namespacesQueryRegex)
if (namespaceQuery) {
let target = {
region: this.getVariableValue(namespaceQuery[1]),
compartment: this.getVariableValue(namespaceQuery[2]).replace(',', '').trim()
region: removeQuotes(this.getVariableValue(namespaceQuery[1])),
compartment: removeQuotes(this.getVariableValue(namespaceQuery[2]))
}
return this.getNamespaces(target).catch(err => { throw new Error('Unable to get namespaces: ' + err) })
}

let metricQuery = varString.match(metricsQueryRegex)
if (metricQuery) {
let target = {
region: this.getVariableValue(metricQuery[1].trim()),
compartment: this.getVariableValue(metricQuery[2].replace(',', '').trim()),
namespace: this.getVariableValue(metricQuery[3].replace(',', '').trim())
region: removeQuotes(this.getVariableValue(metricQuery[1])),
compartment: removeQuotes(this.getVariableValue(metricQuery[2])),
namespace: removeQuotes(this.getVariableValue(metricQuery[3]))
}
return this.metricFindQuery(target).catch(err => { throw new Error('Unable to get metrics: ' + err) })
}

let dimensionsQuery = varString.match(dimensionKeysQueryRegex)
if (dimensionsQuery) {
let target = {
region: this.getVariableValue(dimensionsQuery[1].trim()),
compartment: this.getVariableValue(dimensionsQuery[2].replace(',', '').trim()),
namespace: this.getVariableValue(dimensionsQuery[3].replace(',', '').trim()),
metric: this.getVariableValue(dimensionsQuery[4].replace(',', '').trim()),
region: removeQuotes(this.getVariableValue(dimensionsQuery[1])),
compartment: removeQuotes(this.getVariableValue(dimensionsQuery[2])),
namespace: removeQuotes(this.getVariableValue(dimensionsQuery[3])),
metric: removeQuotes(this.getVariableValue(dimensionsQuery[4])),
}
return this.getDimensionKeys(target).catch(err => { throw new Error('Unable to get dimensions: ' + err) })
}

let dimensionOptionsQuery = varString.match(dimensionValuesQueryRegex)
if (dimensionOptionsQuery) {
let target = {
region: this.getVariableValue(dimensionOptionsQuery[1].trim()),
compartment: this.getVariableValue(dimensionOptionsQuery[2].replace(',', '').trim()),
namespace: this.getVariableValue(dimensionOptionsQuery[3].replace(',', '').trim()),
metric: this.getVariableValue(dimensionOptionsQuery[4].replace(',', '').trim())
region: removeQuotes(this.getVariableValue(dimensionOptionsQuery[1])),
compartment: removeQuotes(this.getVariableValue(dimensionOptionsQuery[2])),
namespace: removeQuotes(this.getVariableValue(dimensionOptionsQuery[3])),
metric: removeQuotes(this.getVariableValue(dimensionOptionsQuery[4]))
}
const dimensionKey = this.getVariableValue(dimensionOptionsQuery[5].replace(',', '').trim());
const dimensionKey = removeQuotes(this.getVariableValue(dimensionOptionsQuery[5]));
return this.getDimensionValues(target, dimensionKey).catch(err => { throw new Error('Unable to get dimension options: ' + err) })
}

throw new Error('Unable to parse templating string');
}

getRegions() {
if (this.regionsCache && this.regionsCache.length > 0) {
return this.q.when(this.regionsCache);
}

return this.doRequest({
targets: [{
environment: this.environment,
Expand All @@ -334,11 +350,16 @@ export default class OCIDatasource {
}],
range: this.timeSrv.timeRange()
}).then((items) => {
return this.mapToTextValue(items, 'regions')
this.regionsCache = this.mapToTextValue(items, 'regions');
return this.regionsCache;
});
}

getCompartments() {
if (this.compartmentsCache && this.compartmentsCache.length > 0) {
return this.q.when(this.compartmentsCache);
}

return this.doRequest({
targets: [{
environment: this.environment,
Expand All @@ -349,25 +370,34 @@ export default class OCIDatasource {
}],
range: this.timeSrv.timeRange()
}).then((items) => {
return this.mapToTextValue(items, 'compartments')
this.compartmentsCache = this.mapToTextValue(items, 'compartments');
return this.compartmentsCache;
});
}

getNamespaces(target) {
getCompartmentId(compartment) {
return this.getCompartments().then(compartments => {
const compartmentFound = compartments.find(c => c.text === compartment || c.value === compartment);
return compartmentFound ? compartmentFound.value : compartment;
});
}

async getNamespaces(target) {
const region = target.region === SELECT_PLACEHOLDERS.REGION ? '' : this.getVariableValue(target.region);
const compartment = target.compartment === SELECT_PLACEHOLDERS.COMPARTMENT ? '' : this.getVariableValue(target.compartment);
if (_.isEmpty(compartment)) {
return this.q.when([]);
}

const compartmentId = await this.getCompartmentId(compartment);
return this.doRequest({
targets: [{
environment: this.environment,
datasourceId: this.id,
tenancyOCID: this.tenancyOCID,
queryType: 'namespaces',
region: _.isEmpty(region) ? this.defaultRegion : region,
compartment: compartment
compartment: compartmentId
}],
range: this.timeSrv.timeRange()
}).then((items) => {
Expand All @@ -393,14 +423,16 @@ export default class OCIDatasource {
continue;
}
dimensionsMap[m] = null;

const compartmentId = await this.getCompartmentId(compartment);
await this.doRequest({
targets: [{
environment: this.environment,
datasourceId: this.id,
tenancyOCID: this.tenancyOCID,
queryType: 'dimensions',
region: _.isEmpty(region) ? this.defaultRegion : region,
compartment: compartment,
compartment: compartmentId,
namespace: namespace,
metric: m
}],
Expand Down Expand Up @@ -523,23 +555,27 @@ export default class OCIDatasource {
/**
* Get all template variable descriptors
*/
getVariableDescriptors(regex, type) {
let vars = this.templateSrv.variables || [];
getVariableDescriptors(regex, includeCustom = true) {
const vars = this.templateSrv.variables || [];

if (regex) {
vars = vars.filter(item => item.query.match(regex) !== null);
}
if (type) {
vars = vars.filter(item => item.type === type)
let regexVars = vars.filter(item => item.query.match(regex) !== null);
if (includeCustom) {
const custom = vars.filter(item => item.type === 'custom' || item.type === 'constant');
regexVars = regexVars.concat(custom);
}
return regexVars;
}

return vars;
}

/**
* List all variable names optionally filtered by regex or/and type
* Returns list of names with '$' at the beginning. Example: ['$dimensionKey', '$dimensionValue']
*/
getVariables(regex, type) {
const varDescriptors = this.getVariableDescriptors(regex, type) || [];
getVariables(regex, includeCustom) {
const varDescriptors = this.getVariableDescriptors(regex, includeCustom) || [];
return varDescriptors.map(item => `$${item.name}`);
}

Expand Down
4 changes: 2 additions & 2 deletions src/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
{"name": "GitHub", "url": "https://github.com/oracle/oci-grafana-plugin"},
{"name": "UPL", "url": "https://oss.oracle.com/licenses/upl"}
],
"version": "1.0.5",
"updated": "2019-09-24"
"version": "1.0.6",
"updated": "2020-01-23"
},

"dependencies": {
Expand Down
Loading

0 comments on commit 8216bc8

Please sign in to comment.