mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Azure Monitor : Adding json formatting of error messages in Panel Header Corner and Inspect Error Tab (#44877)
* Trying out json formatting of azure graph err from frontend * Added some tests wip * Wrap text in popper tooltip * fix conflict * Wrap text in tooltip * Complete tests * Added invalid json test * Backend changes and tests * removed comments * Added split of message / json and edge cases tests * Addressed comments * moved catch to parseErrorMessage
This commit is contained in:
parent
dd9b52fd41
commit
530913dd37
@ -69,6 +69,7 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
transition: opacity 0.3s;
|
||||
z-index: ${theme.zIndex.tooltip};
|
||||
max-width: 400px;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
&[data-popper-interactive='false'] {
|
||||
pointer-events: none;
|
||||
|
@ -235,7 +235,7 @@ func (e *AzureResourceGraphDatasource) unmarshalResponse(res *http.Response) (Az
|
||||
|
||||
if res.StatusCode/100 != 2 {
|
||||
azlog.Debug("Request failed", "status", res.Status, "body", string(body))
|
||||
return AzureResourceGraphResponse{}, fmt.Errorf("request failed, status: %s, body: %s", res.Status, string(body))
|
||||
return AzureResourceGraphResponse{}, fmt.Errorf("%s. Azure Resource Graph error: %s", res.Status, string(body))
|
||||
}
|
||||
|
||||
var data AzureResourceGraphResponse
|
||||
|
@ -2,7 +2,9 @@ package azuremonitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -150,3 +152,46 @@ func TestGetAzurePortalUrl(t *testing.T) {
|
||||
assert.Equal(t, expectedAzurePortalUrl[cloud], azurePortalUrl)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalResponse400(t *testing.T) {
|
||||
datasource := &AzureResourceGraphDatasource{}
|
||||
res, err := datasource.unmarshalResponse(&http.Response{
|
||||
StatusCode: 400,
|
||||
Status: "400 Bad Request",
|
||||
Body: io.NopCloser(strings.NewReader(("Azure Error Message"))),
|
||||
})
|
||||
|
||||
expectedErrMsg := "400 Bad Request. Azure Resource Graph error: Azure Error Message"
|
||||
|
||||
assert.Equal(t, expectedErrMsg, err.Error())
|
||||
assert.Empty(t, res)
|
||||
}
|
||||
|
||||
func TestUnmarshalResponse200Invalid(t *testing.T) {
|
||||
datasource := &AzureResourceGraphDatasource{}
|
||||
res, err := datasource.unmarshalResponse(&http.Response{
|
||||
StatusCode: 200,
|
||||
Status: "OK",
|
||||
Body: io.NopCloser(strings.NewReader(("Azure Data"))),
|
||||
})
|
||||
|
||||
expectedRes := AzureResourceGraphResponse{}
|
||||
expectedErr := "invalid character 'A' looking for beginning of value"
|
||||
|
||||
assert.Equal(t, expectedErr, err.Error())
|
||||
assert.Equal(t, expectedRes, res)
|
||||
}
|
||||
|
||||
func TestUnmarshalResponse200(t *testing.T) {
|
||||
datasource := &AzureResourceGraphDatasource{}
|
||||
res, err2 := datasource.unmarshalResponse(&http.Response{
|
||||
StatusCode: 200,
|
||||
Status: "OK",
|
||||
Body: io.NopCloser(strings.NewReader("{}")),
|
||||
})
|
||||
|
||||
expectedRes := AzureResourceGraphResponse{}
|
||||
|
||||
assert.NoError(t, err2)
|
||||
assert.Equal(t, expectedRes, res)
|
||||
}
|
||||
|
72
public/app/features/inspector/InspectErrorTab.test.tsx
Normal file
72
public/app/features/inspector/InspectErrorTab.test.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { InspectErrorTab } from './InspectErrorTab';
|
||||
|
||||
describe('InspectErrorTab', () => {
|
||||
it('should return null when error does not exist', () => {
|
||||
const { container } = render(<InspectErrorTab />);
|
||||
expect(container.childElementCount).toEqual(0);
|
||||
});
|
||||
|
||||
it('should return a jsonFormatter object of error.data if it exists', () => {
|
||||
const error = {
|
||||
data: {
|
||||
message: 'This is an error',
|
||||
error: 'my error',
|
||||
},
|
||||
};
|
||||
render(<InspectErrorTab error={error} />);
|
||||
expect(screen.getByText('This is an error')).toBeInTheDocument();
|
||||
expect(screen.getByText('error:')).toBeInTheDocument();
|
||||
expect(screen.getByText('"my error"')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should return a jsonFormatter object of error.message if it exists and data does not exist', () => {
|
||||
const error = {
|
||||
message:
|
||||
'{ "error": { "code": "BadRequest", "message": "Please provide below info when asking for support.", "details": [] } }',
|
||||
};
|
||||
const { container } = render(<InspectErrorTab error={error} />);
|
||||
expect(container.childElementCount).toEqual(1);
|
||||
expect(screen.getByText('code:')).toBeInTheDocument();
|
||||
expect(screen.getByText('"BadRequest"')).toBeInTheDocument();
|
||||
expect(screen.getByText('"Please provide below info when asking for support."')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should return an h3 and jsonFormatter object of error.message if it exists and data does not exist', () => {
|
||||
const error = {
|
||||
message:
|
||||
'400 BadRequest, Error from Azure: { "error": { "code": "BadRequest", "message": "Please provide below info when asking for support.", "details": [] } }',
|
||||
};
|
||||
const { container } = render(<InspectErrorTab error={error} />);
|
||||
expect(container.childElementCount).toEqual(2);
|
||||
expect(screen.getByRole('heading', { name: '400 BadRequest, Error from Azure:' })).toBeInTheDocument();
|
||||
expect(screen.getByText('code:')).toBeInTheDocument();
|
||||
expect(screen.getByText('"BadRequest"')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
[
|
||||
'{ invalidJSON{',
|
||||
"hello, I am an error that's just text, no json at all, altoough I do mention template variables {{test}}",
|
||||
'and I am a simple string',
|
||||
].forEach((errMsg) => {
|
||||
it(`should return error.message error.data does not exist nd error.message cannot be parsed - ${errMsg} `, () => {
|
||||
const error = {
|
||||
message: errMsg,
|
||||
};
|
||||
render(<InspectErrorTab error={error} />);
|
||||
expect(screen.queryByRole('heading')).toBeNull();
|
||||
expect(screen.getByText(errMsg)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a jsonFormatter object of error if it has no .data and no .message', () => {
|
||||
const error = {
|
||||
status: '400',
|
||||
};
|
||||
const { container } = render(<InspectErrorTab error={error} />);
|
||||
expect(container.childElementCount).toEqual(1);
|
||||
expect(screen.getByText('status:')).toBeInTheDocument();
|
||||
expect(screen.getByText('"400"')).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -6,6 +6,19 @@ interface InspectErrorTabProps {
|
||||
error?: DataQueryError;
|
||||
}
|
||||
|
||||
const parseErrorMessage = (message: string): { msg: string; json?: any } => {
|
||||
try {
|
||||
const [msg, json] = message.split(/(\{.+)/);
|
||||
const jsonError = JSON.parse(json);
|
||||
return {
|
||||
msg,
|
||||
json: jsonError,
|
||||
};
|
||||
} catch {
|
||||
return { msg: message };
|
||||
}
|
||||
};
|
||||
|
||||
export const InspectErrorTab: React.FC<InspectErrorTabProps> = ({ error }) => {
|
||||
if (!error) {
|
||||
return null;
|
||||
@ -18,5 +31,18 @@ export const InspectErrorTab: React.FC<InspectErrorTabProps> = ({ error }) => {
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <div>{error.message}</div>;
|
||||
if (error.message) {
|
||||
const { msg, json } = parseErrorMessage(error.message);
|
||||
if (!json) {
|
||||
return <div>{msg}</div>;
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{msg !== '' && <h3>{msg}</h3>}
|
||||
<JSONFormatter json={json} open={5} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <JSONFormatter json={error} open={2} />;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user