mirror of
https://github.com/ansible/awx.git
synced 2026-01-23 15:38:06 -03:30
Adds support for toggling approval notifications on orgs and wfjts
This commit is contained in:
parent
a659b9d994
commit
681b765b9a
@ -87,6 +87,13 @@ const NotificationsMixin = parent =>
|
||||
notificationId,
|
||||
notificationType
|
||||
) {
|
||||
if (notificationType === 'approvals') {
|
||||
return this.associateNotificationTemplatesApprovals(
|
||||
resourceId,
|
||||
notificationId
|
||||
);
|
||||
}
|
||||
|
||||
if (notificationType === 'started') {
|
||||
return this.associateNotificationTemplatesStarted(
|
||||
resourceId,
|
||||
@ -126,6 +133,13 @@ const NotificationsMixin = parent =>
|
||||
notificationId,
|
||||
notificationType
|
||||
) {
|
||||
if (notificationType === 'approvals') {
|
||||
return this.disassociateNotificationTemplatesApprovals(
|
||||
resourceId,
|
||||
notificationId
|
||||
);
|
||||
}
|
||||
|
||||
if (notificationType === 'started') {
|
||||
return this.disassociateNotificationTemplatesStarted(
|
||||
resourceId,
|
||||
|
||||
@ -19,6 +19,27 @@ class Organizations extends InstanceGroupsMixin(NotificationsMixin(Base)) {
|
||||
createUser(id, data) {
|
||||
return this.http.post(`${this.baseUrl}${id}/users/`, data);
|
||||
}
|
||||
|
||||
readNotificationTemplatesApprovals(id, params) {
|
||||
return this.http.get(
|
||||
`${this.baseUrl}${id}/notification_templates_approvals/`,
|
||||
{ params }
|
||||
);
|
||||
}
|
||||
|
||||
associateNotificationTemplatesApprovals(resourceId, notificationId) {
|
||||
return this.http.post(
|
||||
`${this.baseUrl}${resourceId}/notification_templates_approvals/`,
|
||||
{ id: notificationId }
|
||||
);
|
||||
}
|
||||
|
||||
disassociateNotificationTemplatesApprovals(resourceId, notificationId) {
|
||||
return this.http.post(
|
||||
`${this.baseUrl}${resourceId}/notification_templates_approvals/`,
|
||||
{ id: notificationId, disassociate: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Organizations;
|
||||
|
||||
@ -65,6 +65,27 @@ class WorkflowJobTemplates extends SchedulesMixin(NotificationsMixin(Base)) {
|
||||
destroySurvey(id) {
|
||||
return this.http.delete(`${this.baseUrl}${id}/survey_spec/`);
|
||||
}
|
||||
|
||||
readNotificationTemplatesApprovals(id, params) {
|
||||
return this.http.get(
|
||||
`${this.baseUrl}${id}/notification_templates_approvals/`,
|
||||
{ params }
|
||||
);
|
||||
}
|
||||
|
||||
associateNotificationTemplatesApprovals(resourceId, notificationId) {
|
||||
return this.http.post(
|
||||
`${this.baseUrl}${resourceId}/notification_templates_approvals/`,
|
||||
{ id: notificationId }
|
||||
);
|
||||
}
|
||||
|
||||
disassociateNotificationTemplatesApprovals(resourceId, notificationId) {
|
||||
return this.http.post(
|
||||
`${this.baseUrl}${resourceId}/notification_templates_approvals/`,
|
||||
{ id: notificationId, disassociate: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WorkflowJobTemplates;
|
||||
|
||||
@ -17,7 +17,13 @@ const QS_CONFIG = getQSConfig('notification', {
|
||||
order_by: 'name',
|
||||
});
|
||||
|
||||
function NotificationList({ apiModel, canToggleNotifications, id, i18n }) {
|
||||
function NotificationList({
|
||||
apiModel,
|
||||
canToggleNotifications,
|
||||
id,
|
||||
i18n,
|
||||
showApprovalsToggle,
|
||||
}) {
|
||||
const location = useLocation();
|
||||
const [isToggleLoading, setIsToggleLoading] = useState(false);
|
||||
const [toggleError, setToggleError] = useState(null);
|
||||
@ -27,6 +33,7 @@ function NotificationList({ apiModel, canToggleNotifications, id, i18n }) {
|
||||
result: {
|
||||
notifications,
|
||||
itemCount,
|
||||
approvalsTemplateIds,
|
||||
startedTemplateIds,
|
||||
successTemplateIds,
|
||||
errorTemplateIds,
|
||||
@ -71,7 +78,7 @@ function NotificationList({ apiModel, canToggleNotifications, id, i18n }) {
|
||||
apiModel.readNotificationTemplatesError(id, idMatchParams),
|
||||
]);
|
||||
|
||||
return {
|
||||
const rtnObj = {
|
||||
notifications: notificationsResults,
|
||||
itemCount: notificationsCount,
|
||||
startedTemplateIds: startedTemplates.results.map(st => st.id),
|
||||
@ -79,10 +86,27 @@ function NotificationList({ apiModel, canToggleNotifications, id, i18n }) {
|
||||
errorTemplateIds: errorTemplates.results.map(e => e.id),
|
||||
typeLabels: labels,
|
||||
};
|
||||
}, [apiModel, id, location]),
|
||||
|
||||
if (showApprovalsToggle) {
|
||||
const {
|
||||
data: approvalsTemplates,
|
||||
} = await apiModel.readNotificationTemplatesApprovals(
|
||||
id,
|
||||
idMatchParams
|
||||
);
|
||||
rtnObj.approvalsTemplateIds = approvalsTemplates.results.map(
|
||||
st => st.id
|
||||
);
|
||||
} else {
|
||||
rtnObj.approvalsTemplateIds = [];
|
||||
}
|
||||
|
||||
return rtnObj;
|
||||
}, [apiModel, id, location, showApprovalsToggle]),
|
||||
{
|
||||
notifications: [],
|
||||
itemCount: 0,
|
||||
approvalsTemplateIds: [],
|
||||
startedTemplateIds: [],
|
||||
successTemplateIds: [],
|
||||
errorTemplateIds: [],
|
||||
@ -186,10 +210,12 @@ function NotificationList({ apiModel, canToggleNotifications, id, i18n }) {
|
||||
detailUrl={`/notifications/${notification.id}`}
|
||||
canToggleNotifications={canToggleNotifications && !isToggleLoading}
|
||||
toggleNotification={handleNotificationToggle}
|
||||
approvalsTurnedOn={approvalsTemplateIds.includes(notification.id)}
|
||||
errorTurnedOn={errorTemplateIds.includes(notification.id)}
|
||||
startedTurnedOn={startedTemplateIds.includes(notification.id)}
|
||||
successTurnedOn={successTemplateIds.includes(notification.id)}
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle={showApprovalsToggle}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -212,6 +238,11 @@ NotificationList.propTypes = {
|
||||
apiModel: shape({}).isRequired,
|
||||
id: number.isRequired,
|
||||
canToggleNotifications: bool.isRequired,
|
||||
showApprovalsToggle: bool,
|
||||
};
|
||||
|
||||
NotificationList.defaultProps = {
|
||||
showApprovalsToggle: false,
|
||||
};
|
||||
|
||||
export default withI18n()(NotificationList);
|
||||
|
||||
@ -17,25 +17,25 @@ const DataListAction = styled(_DataListAction)`
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: repeat(3, max-content);
|
||||
grid-template-columns: ${props => `repeat(${props.columns}, max-content)`};
|
||||
`;
|
||||
const Label = styled.b`
|
||||
margin-right: 20px;
|
||||
`;
|
||||
|
||||
function NotificationListItem(props) {
|
||||
const {
|
||||
canToggleNotifications,
|
||||
notification,
|
||||
detailUrl,
|
||||
startedTurnedOn,
|
||||
successTurnedOn,
|
||||
errorTurnedOn,
|
||||
toggleNotification,
|
||||
i18n,
|
||||
typeLabels,
|
||||
} = props;
|
||||
|
||||
function NotificationListItem({
|
||||
canToggleNotifications,
|
||||
notification,
|
||||
detailUrl,
|
||||
approvalsTurnedOn,
|
||||
startedTurnedOn,
|
||||
successTurnedOn,
|
||||
errorTurnedOn,
|
||||
toggleNotification,
|
||||
i18n,
|
||||
typeLabels,
|
||||
showApprovalsToggle,
|
||||
}) {
|
||||
return (
|
||||
<DataListItem
|
||||
aria-labelledby={`items-list-item-${notification.id}`}
|
||||
@ -66,7 +66,25 @@ function NotificationListItem(props) {
|
||||
aria-label="actions"
|
||||
aria-labelledby={`items-list-item-${notification.id}`}
|
||||
id={`items-list-item-${notification.id}`}
|
||||
columns={showApprovalsToggle ? 4 : 3}
|
||||
>
|
||||
{showApprovalsToggle && (
|
||||
<Switch
|
||||
id={`notification-${notification.id}-approvals-toggle`}
|
||||
label={i18n._(t`Approval`)}
|
||||
labelOff={i18n._(t`Approval`)}
|
||||
isChecked={approvalsTurnedOn}
|
||||
isDisabled={!canToggleNotifications}
|
||||
onChange={() =>
|
||||
toggleNotification(
|
||||
notification.id,
|
||||
approvalsTurnedOn,
|
||||
'approvals'
|
||||
)
|
||||
}
|
||||
aria-label={i18n._(t`Toggle notification approvals`)}
|
||||
/>
|
||||
)}
|
||||
<Switch
|
||||
id={`notification-${notification.id}-started-toggle`}
|
||||
label={i18n._(t`Start`)}
|
||||
@ -114,17 +132,21 @@ NotificationListItem.propTypes = {
|
||||
}).isRequired,
|
||||
canToggleNotifications: bool.isRequired,
|
||||
detailUrl: string.isRequired,
|
||||
approvalsTurnedOn: bool,
|
||||
errorTurnedOn: bool,
|
||||
startedTurnedOn: bool,
|
||||
successTurnedOn: bool,
|
||||
toggleNotification: func.isRequired,
|
||||
typeLabels: shape().isRequired,
|
||||
showApprovalsToggle: bool,
|
||||
};
|
||||
|
||||
NotificationListItem.defaultProps = {
|
||||
approvalsTurnedOn: false,
|
||||
errorTurnedOn: false,
|
||||
startedTurnedOn: false,
|
||||
successTurnedOn: false,
|
||||
showApprovalsToggle: false,
|
||||
};
|
||||
|
||||
export default withI18n()(NotificationListItem);
|
||||
|
||||
@ -39,6 +39,21 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find('NotificationListItem')).toMatchSnapshot();
|
||||
expect(wrapper.find('Switch').length).toBe(3);
|
||||
});
|
||||
|
||||
test('shows approvals toggle when configured', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find('Switch').length).toBe(4);
|
||||
});
|
||||
|
||||
test('displays correct label in correct column', () => {
|
||||
@ -58,7 +73,46 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
expect(typeCell.text()).toContain('Slack');
|
||||
});
|
||||
|
||||
test('handles start click when toggle is on', () => {
|
||||
test('handles approvals click when toggle is on', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
approvalsTurnedOn
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch[aria-label="Toggle notification approvals"]')
|
||||
.first()
|
||||
.find('input')
|
||||
.simulate('change');
|
||||
expect(toggleNotification).toHaveBeenCalledWith(9000, true, 'approvals');
|
||||
});
|
||||
|
||||
test('handles approvals click when toggle is off', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
approvalsTurnedOn={false}
|
||||
toggleNotification={toggleNotification}
|
||||
detailUrl="/foo"
|
||||
canToggleNotifications
|
||||
typeLabels={typeLabels}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch[aria-label="Toggle notification approvals"]')
|
||||
.find('input')
|
||||
.simulate('change');
|
||||
expect(toggleNotification).toHaveBeenCalledWith(9000, false, 'approvals');
|
||||
});
|
||||
|
||||
test('handles started click when toggle is on', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
@ -70,14 +124,13 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
/>
|
||||
);
|
||||
wrapper
|
||||
.find('Switch')
|
||||
.first()
|
||||
.find('Switch[aria-label="Toggle notification start"]')
|
||||
.find('input')
|
||||
.simulate('change');
|
||||
expect(toggleNotification).toHaveBeenCalledWith(9000, true, 'started');
|
||||
});
|
||||
|
||||
test('handles start click when toggle is off', () => {
|
||||
test('handles started click when toggle is off', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
@ -95,7 +148,7 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
expect(toggleNotification).toHaveBeenCalledWith(9000, false, 'started');
|
||||
});
|
||||
|
||||
test('handles error click when toggle is on', () => {
|
||||
test('handles success click when toggle is on', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
@ -113,7 +166,7 @@ describe('<NotificationListItem canToggleNotifications />', () => {
|
||||
expect(toggleNotification).toHaveBeenCalledWith(9000, true, 'success');
|
||||
});
|
||||
|
||||
test('handles error click when toggle is off', () => {
|
||||
test('handles success click when toggle is off', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<NotificationListItem
|
||||
notification={mockNotif}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
exports[`<NotificationListItem canToggleNotifications /> initially renders succesfully and displays correct label 1`] = `
|
||||
<NotificationListItem
|
||||
approvalsTurnedOn={false}
|
||||
canToggleNotifications={true}
|
||||
detailUrl="/foo"
|
||||
errorTurnedOn={false}
|
||||
@ -13,6 +14,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"notification_type": "slack",
|
||||
}
|
||||
}
|
||||
showApprovalsToggle={false}
|
||||
startedTurnedOn={false}
|
||||
successTurnedOn={false}
|
||||
toggleNotification={[MockFunction]}
|
||||
@ -215,6 +217,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
<Styled(DataListAction)
|
||||
aria-label="actions"
|
||||
aria-labelledby="items-list-item-9000"
|
||||
columns={3}
|
||||
id="items-list-item-9000"
|
||||
key=".1"
|
||||
rowid="items-list-item-9000"
|
||||
@ -222,6 +225,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
<StyledComponent
|
||||
aria-label="actions"
|
||||
aria-labelledby="items-list-item-9000"
|
||||
columns={3}
|
||||
forwardedComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
@ -235,7 +239,9 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: repeat(3, max-content);
|
||||
grid-template-columns: ",
|
||||
[Function],
|
||||
";
|
||||
",
|
||||
],
|
||||
},
|
||||
@ -257,11 +263,13 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
aria-label="actions"
|
||||
aria-labelledby="items-list-item-9000"
|
||||
className="sc-bwzfXH llKtln"
|
||||
columns={3}
|
||||
id="items-list-item-9000"
|
||||
rowid="items-list-item-9000"
|
||||
>
|
||||
<div
|
||||
className="pf-c-data-list__item-action sc-bwzfXH llKtln"
|
||||
columns={3}
|
||||
rowid="items-list-item-9000"
|
||||
>
|
||||
<Switch
|
||||
|
||||
@ -268,7 +268,10 @@ describe('<CredentialEdit />', () => {
|
||||
|
||||
test('handleCancel returns the user to credential detail', async () => {
|
||||
await waitForElement(wrapper, 'isLoading', el => el.length === 0);
|
||||
wrapper.find('Button[aria-label="Cancel"]').simulate('click');
|
||||
await act(async () => {
|
||||
wrapper.find('Button[aria-label="Cancel"]').simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
expect(history.location.pathname).toEqual('/credentials/3/details');
|
||||
});
|
||||
|
||||
|
||||
@ -200,6 +200,7 @@ class Organization extends Component {
|
||||
id={Number(match.params.id)}
|
||||
canToggleNotifications={canToggleNotifications}
|
||||
apiModel={OrganizationsAPI}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
</Route>
|
||||
)}
|
||||
|
||||
@ -233,6 +233,7 @@ class WorkflowJobTemplate extends Component {
|
||||
id={Number(match.params.id)}
|
||||
canToggleNotifications={canToggleNotifications}
|
||||
apiModel={WorkflowJobTemplatesAPI}
|
||||
showApprovalsToggle
|
||||
/>
|
||||
</Route>
|
||||
)}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user