Fix reported errors by Faro (#4408)

# What this PR does

Fixes the following reported errors by Faro
- Cannot read properties of undefined (reading 'error_code') - found in
Page Error Handling wrapper
- Cannot read properties of undefined (reading 'slack_team_identity') -
Found in DefaultPageLayout helpers
- Cannot read properties of undefined (reading 'reduce') - undefined
passed labels in labels helpers
- Cannot read properties of undefined (reading 'controlled_fields') -
webhooks
- Cannot read properties of undefined (reading 'data')

The following needs to be further investigated to see if the response
misses whole body
- Cannot read properties of undefined (reading 'config') - happening in
Faro. I assume it's when an exception is thrown and the ex doesn't have
the `config` field

## Which issue(s) this PR closes

Closes #4403
This commit is contained in:
Rares Mardare 2024-05-28 17:11:34 +03:00 committed by GitHub
parent 7e3008ba0f
commit 80ecde6fbb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 28 additions and 50 deletions

View file

@ -8,7 +8,7 @@ export function getWrongTeamResponseInfo(response): Partial<PageErrorData> {
if (response) {
if (response.status === 404) {
return { isNotFoundError: true };
} else if (response.status === 403 && response.data.error_code === 'wrong_team') {
} else if (response.status === 403 && response.data?.error_code === 'wrong_team') {
let res = response.data;
if (res.owner_team) {
return { isWrongTeamError: true, switchToTeam: { name: res.owner_team.name, id: res.owner_team.id } };

View file

@ -1,6 +1,7 @@
import React from 'react';
import { PluginLink } from 'components/PluginLink/PluginLink';
import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally';
import { Organization } from 'models/organization/organization.types';
import { SlackError } from './DefaultPageLayout.types';
@ -10,12 +11,15 @@ export function getSlackMessage(slackError: SlackError, organization: Organizati
return (
<>
Couldn't connect Slack.
{Boolean(organization?.slack_team_identity) && (
<>
{' '}
Select <b>{organization.slack_team_identity.cached_name}</b> workspace when connecting please
</>
)}
<RenderConditionally
shouldRender={Boolean(organization?.slack_team_identity)}
render={() => (
<>
{' '}
Select <b>{organization.slack_team_identity.cached_name}</b> workspace when connecting please
</>
)}
/>
</>
);
}

View file

@ -74,7 +74,7 @@ function prepareDataForEdit(
function prepareForSave(rawData: Partial<ApiSchemas['Webhook']>, selectedPreset: OutgoingWebhookPreset) {
const data = { ...rawData };
selectedPreset.controlled_fields.forEach((field) => {
selectedPreset?.controlled_fields.forEach((field) => {
delete data[field];
});

View file

@ -354,7 +354,7 @@ export const OutgoingWebhookFormFields = ({
return (
<>
{React.Children.toArray(controls.props.children).filter(
(child) => !preset || !preset.controlled_fields.includes((child as React.ReactElement).props.name)
(child) => !preset?.controlled_fields.includes((child as React.ReactElement).props.name)
)}
</>
);

View file

@ -10,7 +10,7 @@ export const additionalWebhookPresetIcons: { [id: string]: () => React.ReactElem
};
export const getWebhookPresetIcons = (features: Record<string, boolean>) => {
if (features[AppFeature.MsTeams]) {
if (features?.[AppFeature.MsTeams]) {
return { ...commonWebhookPresetIconsConfig, ...additionalWebhookPresetIcons };
}

View file

@ -202,9 +202,11 @@ export const RotationForm = observer((props: RotationFormProps) => {
}
};
const onError = useCallback((error) => {
setErrors(error.response.data);
}, []);
const onError = (error) => {
if (error.response?.data) {
setErrors(error.response.data);
}
};
const handleChange = useDebouncedCallback(updatePreview, 200);

View file

@ -43,7 +43,7 @@ export const ScheduleUserDetails: FC<ScheduleUserDetailsProps> = observer((props
const { organizationStore } = store;
const slackWorkspaceName =
organizationStore.currentOrganization.slack_team_identity?.cached_name?.replace(/[^0-9a-z]/gi, '') || '';
organizationStore.currentOrganization?.slack_team_identity?.cached_name?.replace(/[^0-9a-z]/gi, '') || '';
return (
<div className={cx('root')} data-testid="schedule-user-details">

View file

@ -86,7 +86,7 @@ export class EscalationChainStore extends BaseStore {
try {
escalationChain = await this.getById(id, skipErrorHandling);
} catch (error) {
if (error.response.data.error_code === 'wrong_team') {
if (error.response.data?.error_code === 'wrong_team') {
escalationChain = {
id,
name: '🔒 Private escalation chain',

View file

@ -1,7 +1,7 @@
import { ApiSchemas } from 'network/oncall-api/api.types';
export const splitToGroups = (labels: Array<ApiSchemas['LabelKey']> | Array<ApiSchemas['LabelValue']>) => {
return labels.reduce(
return labels?.reduce(
(memo, option) => {
memo.find(({ name }) => name === (option.prescribed ? 'System' : 'User added')).options.push(option);

View file

@ -189,7 +189,7 @@ export class ScheduleStore extends BaseStore {
try {
schedule = await this.getById(id, true, fromOrganization);
} catch (error) {
if (error.response.data.error_code === 'wrong_team') {
if (error.response.data?.error_code === 'wrong_team') {
schedule = {
id,
name: '🔒 Private schedule',

View file

@ -118,7 +118,7 @@ class _SlackSettings extends Component<SlackProps, SlackState> {
<div className={cx('root')}>
<Legend>Slack App settings</Legend>
<InlineField label="Slack Workspace" grow disabled>
<Input value={currentOrganization.slack_team_identity?.cached_name} />
<Input value={currentOrganization?.slack_team_identity?.cached_name} />
</InlineField>
<InlineField
label="Default channel for Slack notifications"
@ -203,34 +203,6 @@ class _SlackSettings extends Component<SlackProps, SlackState> {
);
};
renderSlackWorkspace = () => {
const { store } = this.props;
return <Text>{store.organizationStore.currentOrganization.slack_team_identity?.cached_name}</Text>;
};
renderSlackChannels = () => {
const {
store: { organizationStore, slackChannelStore },
} = this.props;
return (
<WithPermissionControlTooltip userAction={UserActions.ChatOpsUpdateSettings}>
<GSelect<SlackChannel>
className={cx('select', 'control')}
items={slackChannelStore.items}
fetchItemsFn={slackChannelStore.updateItems}
fetchItemFn={slackChannelStore.updateItem}
getSearchResult={slackChannelStore.getSearchResult}
displayField="display_name"
valueField="id"
placeholder="Select Slack Channel"
value={organizationStore.currentOrganization?.slack_channel?.id}
onChange={this.handleSlackChannelChange}
nullItemName={PRIVATE_CHANNEL_NAME}
/>
</WithPermissionControlTooltip>
);
};
removeSlackIntegration = async () => {
const { store } = this.props;
try {

View file

@ -367,7 +367,7 @@ class Users extends React.Component<UsersProps, UsersState> {
warnings.push('Phone not verified');
}
if (organizationStore.currentOrganization.slack_team_identity && !user.slack_user_identity) {
if (organizationStore.currentOrganization?.slack_team_identity && !user.slack_user_identity) {
warnings.push('Slack profile is not connected');
}

View file

@ -99,17 +99,17 @@ class BaseFaroHelper {
pushAxiosNetworkResponseEvent = ({ name, res }: { name: string; res: AxiosResponse }) => {
this.faro?.api.pushEvent(name, {
url: res.config.url,
url: res.config?.url,
status: `${res.status}`,
statusText: `${res.statusText}`,
method: res.config.method.toUpperCase(),
method: res.config?.method.toUpperCase(),
});
};
pushAxiosNetworkError = (res: AxiosResponse) => {
this.faro?.api.pushError(new Error(`Network error: ${res.status}`), {
context: {
url: res.config.url,
url: res.config?.url,
type: 'network',
data: `${safeJSONStringify(res.data)}`,
status: `${res.status}`,