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:
Yaelle Chaudy 2022-02-18 12:09:24 +01:00 committed by GitHub
parent dd9b52fd41
commit 530913dd37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 146 additions and 2 deletions

View File

@ -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;

View File

@ -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

View File

@ -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)
}

View 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();
});
});

View File

@ -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} />;
};