mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Azure Monitor: Add template variables for namespaces and resource names (#52247)
This commit is contained in:
committed by
GitHub
parent
8fc51932f5
commit
10b9830cec
@@ -64,7 +64,7 @@ describe('AzureMonitorDatasource', () => {
|
||||
const expected =
|
||||
basePath +
|
||||
'/providers/microsoft.insights/components/resource1' +
|
||||
'/providers/microsoft.insights/metricNamespaces?api-version=2017-12-01-preview';
|
||||
'/providers/microsoft.insights/metricNamespaces?region=global&api-version=2017-12-01-preview';
|
||||
expect(path).toBe(expected);
|
||||
return Promise.resolve(response);
|
||||
});
|
||||
@@ -80,7 +80,7 @@ describe('AzureMonitorDatasource', () => {
|
||||
expect(results.length).toEqual(2);
|
||||
expect(results[0].text).toEqual('Azure.ApplicationInsights');
|
||||
expect(results[0].value).toEqual('Azure.ApplicationInsights');
|
||||
expect(results[1].text).toEqual('microsoft.insights-components');
|
||||
expect(results[1].text).toEqual('microsoft.insights/components');
|
||||
expect(results[1].value).toEqual('microsoft.insights/components');
|
||||
});
|
||||
});
|
||||
@@ -405,7 +405,7 @@ describe('AzureMonitorDatasource', () => {
|
||||
ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => {
|
||||
const basePath = `azuremonitor/subscriptions/${subscription}/resourceGroups`;
|
||||
expect(path).toBe(
|
||||
`${basePath}/${resourceGroup}/resources?$filter=resourceType eq '${metricDefinition}'&api-version=2021-04-01`
|
||||
`${basePath}/${resourceGroup}/resources?api-version=2021-04-01&$filter=resourceType eq '${metricDefinition}'`
|
||||
);
|
||||
return Promise.resolve(response);
|
||||
});
|
||||
@@ -456,7 +456,7 @@ describe('AzureMonitorDatasource', () => {
|
||||
const basePath = `azuremonitor/subscriptions/${subscription}/resourceGroups`;
|
||||
expect(path).toBe(
|
||||
basePath +
|
||||
`/${resourceGroup}/resources?$filter=resourceType eq '${validMetricDefinition}'&api-version=2021-04-01`
|
||||
`/${resourceGroup}/resources?api-version=2021-04-01&$filter=resourceType eq '${validMetricDefinition}'`
|
||||
);
|
||||
return Promise.resolve(response);
|
||||
});
|
||||
@@ -467,7 +467,7 @@ describe('AzureMonitorDatasource', () => {
|
||||
expect(results[0].text).toEqual('storagetest/default');
|
||||
expect(results[0].value).toEqual('storagetest/default');
|
||||
expect(ctx.ds.azureMonitorDatasource.getResource).toHaveBeenCalledWith(
|
||||
`azuremonitor/subscriptions/${subscription}/resourceGroups/${resourceGroup}/resources?$filter=resourceType eq '${validMetricDefinition}'&api-version=2021-04-01`
|
||||
`azuremonitor/subscriptions/${subscription}/resourceGroups/${resourceGroup}/resources?api-version=2021-04-01&$filter=resourceType eq '${validMetricDefinition}'`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -497,7 +497,7 @@ describe('AzureMonitorDatasource', () => {
|
||||
const fn = jest.fn();
|
||||
ctx.ds.azureMonitorDatasource.getResource = fn;
|
||||
const basePath = `azuremonitor/subscriptions/${subscription}/resourceGroups`;
|
||||
const expectedPath = `${basePath}/${resourceGroup}/resources?$filter=resourceType eq '${metricDefinition}'&api-version=2021-04-01`;
|
||||
const expectedPath = `${basePath}/${resourceGroup}/resources?api-version=2021-04-01&$filter=resourceType eq '${metricDefinition}'`;
|
||||
// first page
|
||||
fn.mockImplementationOnce((path: string) => {
|
||||
expect(path).toBe(expectedPath);
|
||||
@@ -520,6 +520,35 @@ describe('AzureMonitorDatasource', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('without a resource group or a metric definition', () => {
|
||||
const response = {
|
||||
value: [
|
||||
{
|
||||
name: 'Failure Anomalies - nodeapp',
|
||||
type: 'microsoft.insights/alertrules',
|
||||
},
|
||||
{
|
||||
name: resourceGroup,
|
||||
type: metricDefinition,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => {
|
||||
const basePath = `azuremonitor/subscriptions/${subscription}/resources?api-version=2021-04-01`;
|
||||
expect(path).toBe(basePath);
|
||||
return Promise.resolve(response);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return list of Resource Names', () => {
|
||||
return ctx.ds.getResourceNames(subscription).then((results: Array<{ text: string; value: string }>) => {
|
||||
expect(results.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('When performing getMetricNames', () => {
|
||||
|
||||
@@ -204,14 +204,18 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
});
|
||||
}
|
||||
|
||||
getResourceNames(subscriptionId: string, resourceGroup: string, metricDefinition: string, skipToken?: string) {
|
||||
getResourceNames(subscriptionId: string, resourceGroup?: string, metricDefinition?: string, skipToken?: string) {
|
||||
const validMetricDefinition = startsWith(metricDefinition, 'Microsoft.Storage/storageAccounts/')
|
||||
? 'Microsoft.Storage/storageAccounts'
|
||||
: metricDefinition;
|
||||
let url =
|
||||
`${this.resourcePath}/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/resources?` +
|
||||
`$filter=resourceType eq '${validMetricDefinition}'&` +
|
||||
`api-version=${this.listByResourceGroupApiVersion}`;
|
||||
let url = `${this.resourcePath}/subscriptions/${subscriptionId}`;
|
||||
if (resourceGroup) {
|
||||
url += `/resourceGroups/${resourceGroup}`;
|
||||
}
|
||||
url += `/resources?api-version=${this.listByResourceGroupApiVersion}`;
|
||||
if (validMetricDefinition) {
|
||||
url += `&$filter=resourceType eq '${validMetricDefinition}'`;
|
||||
}
|
||||
if (skipToken) {
|
||||
url += `&$skiptoken=${skipToken}`;
|
||||
}
|
||||
@@ -251,7 +255,11 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
);
|
||||
return this.getResource(url)
|
||||
.then((result: AzureMonitorMetricNamespacesResponse) => {
|
||||
return ResponseParser.parseResponseValues(result, 'name', 'properties.metricNamespaceName');
|
||||
return ResponseParser.parseResponseValues(
|
||||
result,
|
||||
'properties.metricNamespaceName',
|
||||
'properties.metricNamespaceName'
|
||||
);
|
||||
})
|
||||
.then((result) => {
|
||||
if (url.includes('Microsoft.Storage/storageAccounts')) {
|
||||
|
||||
@@ -33,7 +33,7 @@ export default class ResponseParser {
|
||||
return list;
|
||||
}
|
||||
|
||||
static parseResourceNames(result: any, metricDefinition: string): Array<{ text: string; value: string }> {
|
||||
static parseResourceNames(result: any, metricDefinition?: string): Array<{ text: string; value: string }> {
|
||||
const list: Array<{ text: string; value: string }> = [];
|
||||
|
||||
if (!result) {
|
||||
@@ -43,7 +43,7 @@ export default class ResponseParser {
|
||||
for (let i = 0; i < result.value.length; i++) {
|
||||
if (
|
||||
typeof result.value[i].type === 'string' &&
|
||||
result.value[i].type.toLocaleLowerCase() === metricDefinition.toLocaleLowerCase()
|
||||
(!metricDefinition || result.value[i].type.toLocaleLowerCase() === metricDefinition.toLocaleLowerCase())
|
||||
) {
|
||||
list.push({
|
||||
text: result.value[i].name,
|
||||
|
||||
@@ -92,7 +92,7 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview'
|
||||
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricNamespaces?region=global&api-version=2017-05-01-preview'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -130,7 +130,7 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.NetApp/netAppAccounts/rn1/capacityPools/rn2/volumes/rn3/' +
|
||||
'providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview'
|
||||
'providers/microsoft.insights/metricNamespaces?region=global&api-version=2017-05-01-preview'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -150,7 +150,7 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn1/databases/rn2/' +
|
||||
'providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview'
|
||||
'providers/microsoft.insights/metricNamespaces?region=global&api-version=2017-05-01-preview'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -170,7 +170,7 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn/' +
|
||||
'providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview'
|
||||
'providers/microsoft.insights/metricNamespaces?region=global&api-version=2017-05-01-preview'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,7 +57,7 @@ export default class UrlBuilder {
|
||||
);
|
||||
}
|
||||
|
||||
return `${baseUrl}${resourceUri}/providers/microsoft.insights/metricNamespaces?api-version=${apiVersion}`;
|
||||
return `${baseUrl}${resourceUri}/providers/microsoft.insights/metricNamespaces?region=global&api-version=${apiVersion}`;
|
||||
}
|
||||
|
||||
static buildAzureMonitorGetMetricNamesUrl(
|
||||
|
||||
@@ -29,7 +29,15 @@ const defaultProps = {
|
||||
subscription: 'id',
|
||||
},
|
||||
onChange: jest.fn(),
|
||||
datasource: createMockDatasource(),
|
||||
datasource: createMockDatasource({
|
||||
getSubscriptions: jest.fn().mockResolvedValue([{ text: 'Primary Subscription', value: 'sub' }]),
|
||||
getResourceGroups: jest.fn().mockResolvedValue([{ text: 'rg', value: 'rg' }]),
|
||||
getMetricNamespaces: jest.fn().mockResolvedValue([{ text: 'foo/bar', value: 'foo/bar' }]),
|
||||
getVariablesRaw: jest.fn().mockReturnValue([
|
||||
{ label: 'query0', name: 'sub0' },
|
||||
{ label: 'query1', name: 'rg', query: { queryType: AzureQueryType.ResourceGroupsQuery } },
|
||||
]),
|
||||
}),
|
||||
};
|
||||
|
||||
const originalConfigValue = grafanaRuntime.config.featureToggles.azTemplateVars;
|
||||
@@ -166,11 +174,8 @@ describe('VariableEditor:', () => {
|
||||
|
||||
it('should run the query if requesting resource groups', async () => {
|
||||
grafanaRuntime.config.featureToggles.azTemplateVars = true;
|
||||
const ds = createMockDatasource({
|
||||
getSubscriptions: jest.fn().mockResolvedValue([{ text: 'Primary Subscription', value: 'sub' }]),
|
||||
});
|
||||
const onChange = jest.fn();
|
||||
const { rerender } = render(<VariableEditor {...defaultProps} onChange={onChange} datasource={ds} />);
|
||||
const { rerender } = render(<VariableEditor {...defaultProps} onChange={onChange} />);
|
||||
// wait for initial load
|
||||
await waitFor(() => expect(screen.getByText('Logs')).toBeInTheDocument());
|
||||
// Select RGs variable
|
||||
@@ -195,14 +200,7 @@ describe('VariableEditor:', () => {
|
||||
it('should show template variables as options ', async () => {
|
||||
const onChange = jest.fn();
|
||||
grafanaRuntime.config.featureToggles.azTemplateVars = true;
|
||||
const ds = createMockDatasource({
|
||||
getSubscriptions: jest.fn().mockResolvedValue([{ text: 'Primary Subscription', value: 'sub' }]),
|
||||
getVariablesRaw: jest.fn().mockReturnValue([
|
||||
{ label: 'query0', name: 'sub0' },
|
||||
{ label: 'query1', name: 'rg', query: { queryType: AzureQueryType.ResourceGroupsQuery } },
|
||||
]),
|
||||
});
|
||||
const { rerender } = render(<VariableEditor {...defaultProps} datasource={ds} onChange={onChange} />);
|
||||
const { rerender } = render(<VariableEditor {...defaultProps} onChange={onChange} />);
|
||||
// wait for initial load
|
||||
await waitFor(() => expect(screen.getByText('Logs')).toBeInTheDocument());
|
||||
// Select RGs variable
|
||||
@@ -210,7 +208,7 @@ describe('VariableEditor:', () => {
|
||||
screen.getByText('Resource Groups').click();
|
||||
// Simulate onChange behavior
|
||||
const newQuery = onChange.mock.calls.at(-1)[0];
|
||||
rerender(<VariableEditor {...defaultProps} query={newQuery} onChange={onChange} datasource={ds} />);
|
||||
rerender(<VariableEditor {...defaultProps} query={newQuery} onChange={onChange} />);
|
||||
await waitFor(() => expect(screen.getByText('Select subscription')).toBeInTheDocument());
|
||||
// Select a subscription
|
||||
openMenu(screen.getByLabelText('select subscription'));
|
||||
@@ -218,10 +216,60 @@ describe('VariableEditor:', () => {
|
||||
screen.getByText('Template Variables').click();
|
||||
// Simulate onChange behavior
|
||||
const lastQuery = onChange.mock.calls.at(-1)[0];
|
||||
rerender(<VariableEditor {...defaultProps} query={lastQuery} onChange={onChange} datasource={ds} />);
|
||||
rerender(<VariableEditor {...defaultProps} query={lastQuery} onChange={onChange} />);
|
||||
await waitFor(() => expect(screen.getByText('query0')).toBeInTheDocument());
|
||||
// Template variables of the same type than the current one should not appear
|
||||
expect(screen.queryByText('query1')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should run the query if requesting namespaces', async () => {
|
||||
grafanaRuntime.config.featureToggles.azTemplateVars = true;
|
||||
const onChange = jest.fn();
|
||||
const { rerender } = render(<VariableEditor {...defaultProps} onChange={onChange} />);
|
||||
// wait for initial load
|
||||
await waitFor(() => expect(screen.getByText('Logs')).toBeInTheDocument());
|
||||
// Select RGs variable
|
||||
openMenu(screen.getByLabelText('select query type'));
|
||||
screen.getByText('Namespaces').click();
|
||||
// Simulate onChange behavior
|
||||
const newQuery = onChange.mock.calls.at(-1)[0];
|
||||
rerender(<VariableEditor {...defaultProps} query={newQuery} onChange={onChange} />);
|
||||
await waitFor(() => expect(screen.getByText('Select subscription')).toBeInTheDocument());
|
||||
// Select a subscription
|
||||
openMenu(screen.getByLabelText('select subscription'));
|
||||
screen.getByText('Primary Subscription').click();
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
queryType: AzureQueryType.NamespacesQuery,
|
||||
subscription: 'sub',
|
||||
refId: 'A',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should run the query if requesting resource names', async () => {
|
||||
grafanaRuntime.config.featureToggles.azTemplateVars = true;
|
||||
const onChange = jest.fn();
|
||||
const { rerender } = render(<VariableEditor {...defaultProps} onChange={onChange} />);
|
||||
// wait for initial load
|
||||
await waitFor(() => expect(screen.getByText('Logs')).toBeInTheDocument());
|
||||
// Select RGs variable
|
||||
openMenu(screen.getByLabelText('select query type'));
|
||||
screen.getByText('Resource Names').click();
|
||||
// Simulate onChange behavior
|
||||
const newQuery = onChange.mock.calls.at(-1)[0];
|
||||
rerender(<VariableEditor {...defaultProps} query={newQuery} onChange={onChange} />);
|
||||
await waitFor(() => expect(screen.getByText('Select subscription')).toBeInTheDocument());
|
||||
// Select a subscription
|
||||
openMenu(screen.getByLabelText('select subscription'));
|
||||
screen.getByText('Primary Subscription').click();
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
queryType: AzureQueryType.ResourceNamesQuery,
|
||||
subscription: 'sub',
|
||||
refId: 'A',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,8 @@ type Props = {
|
||||
datasource: DataSource;
|
||||
};
|
||||
|
||||
const removeOption: SelectableValue = { label: '-', value: '' };
|
||||
|
||||
const VariableEditor = (props: Props) => {
|
||||
const { query, onChange, datasource } = props;
|
||||
const AZURE_QUERY_VARIABLE_TYPE_OPTIONS = [
|
||||
@@ -30,13 +32,19 @@ const VariableEditor = (props: Props) => {
|
||||
if (config.featureToggles.azTemplateVars) {
|
||||
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Subscriptions', value: AzureQueryType.SubscriptionsQuery });
|
||||
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Resource Groups', value: AzureQueryType.ResourceGroupsQuery });
|
||||
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Namespaces', value: AzureQueryType.NamespacesQuery });
|
||||
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Resource Names', value: AzureQueryType.ResourceNamesQuery });
|
||||
}
|
||||
const [variableOptionGroup, setVariableOptionGroup] = useState<{ label: string; options: AzureMonitorOption[] }>({
|
||||
label: 'Template Variables',
|
||||
options: [],
|
||||
});
|
||||
const [requireSubscription, setRequireSubscription] = useState(false);
|
||||
const [hasResourceGroup, setHasResourceGroup] = useState(false);
|
||||
const [hasNamespace, setHasNamespace] = useState(false);
|
||||
const [subscriptions, setSubscriptions] = useState<SelectableValue[]>([]);
|
||||
const [resourceGroups, setResourceGroups] = useState<SelectableValue[]>([]);
|
||||
const [namespaces, setNamespaces] = useState<SelectableValue[]>([]);
|
||||
const [errorMessage, setError] = useLastError();
|
||||
const queryType = typeof query === 'string' ? '' : query.queryType;
|
||||
|
||||
@@ -47,12 +55,22 @@ const VariableEditor = (props: Props) => {
|
||||
}, [query, datasource, onChange]);
|
||||
|
||||
useEffect(() => {
|
||||
setRequireSubscription(false);
|
||||
setHasResourceGroup(false);
|
||||
setHasNamespace(false);
|
||||
switch (queryType) {
|
||||
case AzureQueryType.ResourceGroupsQuery:
|
||||
setRequireSubscription(true);
|
||||
break;
|
||||
default:
|
||||
setRequireSubscription(false);
|
||||
case AzureQueryType.NamespacesQuery:
|
||||
setRequireSubscription(true);
|
||||
setHasResourceGroup(true);
|
||||
break;
|
||||
case AzureQueryType.ResourceNamesQuery:
|
||||
setRequireSubscription(true);
|
||||
setHasResourceGroup(true);
|
||||
setHasNamespace(true);
|
||||
break;
|
||||
}
|
||||
}, [queryType]);
|
||||
|
||||
@@ -75,6 +93,24 @@ const VariableEditor = (props: Props) => {
|
||||
});
|
||||
});
|
||||
|
||||
const subscription = typeof query === 'object' && query.subscription;
|
||||
useEffect(() => {
|
||||
if (subscription) {
|
||||
datasource.getResourceGroups(subscription).then((rgs) => {
|
||||
setResourceGroups(rgs.map((s) => ({ label: s.text, value: s.value })));
|
||||
});
|
||||
}
|
||||
}, [datasource, subscription]);
|
||||
|
||||
const resourceGroup = (typeof query === 'object' && query.resourceGroup) || '';
|
||||
useEffect(() => {
|
||||
if (subscription) {
|
||||
datasource.getMetricNamespaces(subscription, resourceGroup).then((rgs) => {
|
||||
setNamespaces(rgs.map((s) => ({ label: s.text, value: s.value })));
|
||||
});
|
||||
}
|
||||
}, [datasource, subscription, resourceGroup]);
|
||||
|
||||
if (typeof query === 'string') {
|
||||
// still migrating the query
|
||||
return null;
|
||||
@@ -98,6 +134,20 @@ const VariableEditor = (props: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeResourceGroup = (selectableValue: SelectableValue) => {
|
||||
onChange({
|
||||
...query,
|
||||
resourceGroup: selectableValue.value,
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeNamespace = (selectableValue: SelectableValue) => {
|
||||
onChange({
|
||||
...query,
|
||||
namespace: selectableValue.value,
|
||||
});
|
||||
};
|
||||
|
||||
const onLogsQueryChange = (queryChange: AzureMonitorQuery) => {
|
||||
onChange(queryChange);
|
||||
};
|
||||
@@ -113,7 +163,7 @@ const VariableEditor = (props: Props) => {
|
||||
value={queryType}
|
||||
/>
|
||||
</InlineField>
|
||||
{typeof query === 'object' && query.queryType === AzureQueryType.LogAnalytics && (
|
||||
{query.queryType === AzureQueryType.LogAnalytics && (
|
||||
<>
|
||||
<LogsQueryEditor
|
||||
subscriptionId={query.subscription}
|
||||
@@ -134,10 +184,10 @@ const VariableEditor = (props: Props) => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{typeof query === 'object' && query.queryType === AzureQueryType.GrafanaTemplateVariableFn && (
|
||||
{query.queryType === AzureQueryType.GrafanaTemplateVariableFn && (
|
||||
<GrafanaTemplateVariableFnInput query={query} updateQuery={props.onChange} datasource={datasource} />
|
||||
)}
|
||||
{typeof query === 'object' && requireSubscription && (
|
||||
{requireSubscription && (
|
||||
<InlineField label="Select subscription" labelWidth={20}>
|
||||
<Select
|
||||
aria-label="select subscription"
|
||||
@@ -148,6 +198,30 @@ const VariableEditor = (props: Props) => {
|
||||
/>
|
||||
</InlineField>
|
||||
)}
|
||||
{hasResourceGroup && (
|
||||
<InlineField label="Select Resource Group" labelWidth={20}>
|
||||
<Select
|
||||
aria-label="select resource group"
|
||||
onChange={onChangeResourceGroup}
|
||||
options={resourceGroups.concat(variableOptionGroup, removeOption)}
|
||||
width={25}
|
||||
value={query.resourceGroup}
|
||||
placeholder="Optional"
|
||||
/>
|
||||
</InlineField>
|
||||
)}
|
||||
{hasNamespace && (
|
||||
<InlineField label="Select Namespace" labelWidth={20}>
|
||||
<Select
|
||||
aria-label="select namespace"
|
||||
onChange={onChangeNamespace}
|
||||
options={namespaces.concat(variableOptionGroup, removeOption)}
|
||||
width={25}
|
||||
value={query.namespace}
|
||||
placeholder="Optional"
|
||||
/>
|
||||
</InlineField>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -154,7 +154,15 @@ export default class Datasource extends DataSourceWithBackend<AzureMonitorQuery,
|
||||
);
|
||||
}
|
||||
|
||||
getResourceNames(subscriptionId: string, resourceGroup: string, metricDefinition: string) {
|
||||
getMetricNamespaces(subscriptionId: string, resourceGroup?: string) {
|
||||
let url = `/subscriptions/${subscriptionId}`;
|
||||
if (resourceGroup) {
|
||||
url += `/resourceGroups/${resourceGroup};`;
|
||||
}
|
||||
return this.azureMonitorDatasource.getMetricNamespaces({ resourceUri: url });
|
||||
}
|
||||
|
||||
getResourceNames(subscriptionId: string, resourceGroup?: string, metricDefinition?: string) {
|
||||
return this.azureMonitorDatasource.getResourceNames(
|
||||
this.templateSrv.replace(subscriptionId),
|
||||
this.templateSrv.replace(resourceGroup),
|
||||
|
||||
@@ -8,6 +8,8 @@ export enum AzureQueryType {
|
||||
AzureResourceGraph = 'Azure Resource Graph',
|
||||
SubscriptionsQuery = 'Azure Subscriptions',
|
||||
ResourceGroupsQuery = 'Azure Resource Groups',
|
||||
NamespacesQuery = 'Azure Namespaces',
|
||||
ResourceNamesQuery = 'Azure Resource Names',
|
||||
/** Deprecated */
|
||||
GrafanaTemplateVariableFn = 'Grafana Template Variable Function',
|
||||
}
|
||||
@@ -28,6 +30,10 @@ export interface AzureMonitorQuery extends DataQuery {
|
||||
azureLogAnalytics?: AzureLogsQuery;
|
||||
azureResourceGraph?: AzureResourceGraphQuery;
|
||||
grafanaTemplateVariableFn?: GrafanaTemplateVariableQuery;
|
||||
|
||||
/** Template variables params */
|
||||
resourceGroup?: string;
|
||||
namespace?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -559,5 +559,51 @@ describe('VariableSupport', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch namespaces', (done) => {
|
||||
const expectedResults = ['test'];
|
||||
const variableSupport = new VariableSupport(
|
||||
createMockDatasource({
|
||||
getMetricNamespaces: jest.fn().mockResolvedValueOnce(expectedResults),
|
||||
})
|
||||
);
|
||||
const mockRequest = {
|
||||
targets: [
|
||||
{
|
||||
refId: 'A',
|
||||
queryType: AzureQueryType.NamespacesQuery,
|
||||
subscription: 'sub',
|
||||
} as AzureMonitorQuery,
|
||||
],
|
||||
} as DataQueryRequest<AzureMonitorQuery>;
|
||||
const observables = variableSupport.query(mockRequest);
|
||||
observables.subscribe((result: DataQueryResponseData) => {
|
||||
expect(result.data[0].source).toEqual(expectedResults);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch resource names', (done) => {
|
||||
const expectedResults = ['test'];
|
||||
const variableSupport = new VariableSupport(
|
||||
createMockDatasource({
|
||||
getResourceNames: jest.fn().mockResolvedValueOnce(expectedResults),
|
||||
})
|
||||
);
|
||||
const mockRequest = {
|
||||
targets: [
|
||||
{
|
||||
refId: 'A',
|
||||
queryType: AzureQueryType.ResourceNamesQuery,
|
||||
subscription: 'sub',
|
||||
} as AzureMonitorQuery,
|
||||
],
|
||||
} as DataQueryRequest<AzureMonitorQuery>;
|
||||
const observables = variableSupport.query(mockRequest);
|
||||
observables.subscribe((result: DataQueryResponseData) => {
|
||||
expect(result.data[0].source).toEqual(expectedResults);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,6 +43,24 @@ export class VariableSupport extends CustomVariableSupport<DataSource, AzureMoni
|
||||
data: rgs?.length ? [toDataFrame(rgs)] : [],
|
||||
};
|
||||
}
|
||||
case AzureQueryType.NamespacesQuery:
|
||||
if (queryObj.subscription) {
|
||||
const rgs = await this.datasource.getMetricNamespaces(queryObj.subscription, queryObj.resourceGroup);
|
||||
return {
|
||||
data: rgs?.length ? [toDataFrame(rgs)] : [],
|
||||
};
|
||||
}
|
||||
case AzureQueryType.ResourceNamesQuery:
|
||||
if (queryObj.subscription) {
|
||||
const rgs = await this.datasource.getResourceNames(
|
||||
queryObj.subscription,
|
||||
queryObj.resourceGroup,
|
||||
queryObj.namespace
|
||||
);
|
||||
return {
|
||||
data: rgs?.length ? [toDataFrame(rgs)] : [],
|
||||
};
|
||||
}
|
||||
case AzureQueryType.GrafanaTemplateVariableFn:
|
||||
if (queryObj.grafanaTemplateVariableFn) {
|
||||
const templateVariablesResults = await this.callGrafanaTemplateVariableFn(
|
||||
@@ -57,7 +75,7 @@ export class VariableSupport extends CustomVariableSupport<DataSource, AzureMoni
|
||||
return lastValueFrom(this.datasource.query(request));
|
||||
}
|
||||
} catch (err) {
|
||||
return { data: [], error: { message: messageFromError(err) } };
|
||||
return { data: [], error: new Error(messageFromError(err)) };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user