Skip to content

Commit

Permalink
Adds support for toggling approval notifications on orgs and wfjts
Browse files Browse the repository at this point in the history
  • Loading branch information
mabashian committed Aug 20, 2020
1 parent a659b9d commit 681b765
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 25 deletions.
14 changes: 14 additions & 0 deletions awx/ui_next/src/api/mixins/Notifications.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ const NotificationsMixin = parent =>
notificationId,
notificationType
) {
if (notificationType === 'approvals') {
return this.associateNotificationTemplatesApprovals(
resourceId,
notificationId
);
}

if (notificationType === 'started') {
return this.associateNotificationTemplatesStarted(
resourceId,
Expand Down Expand Up @@ -126,6 +133,13 @@ const NotificationsMixin = parent =>
notificationId,
notificationType
) {
if (notificationType === 'approvals') {
return this.disassociateNotificationTemplatesApprovals(
resourceId,
notificationId
);
}

if (notificationType === 'started') {
return this.disassociateNotificationTemplatesStarted(
resourceId,
Expand Down
21 changes: 21 additions & 0 deletions awx/ui_next/src/api/models/Organizations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
21 changes: 21 additions & 0 deletions awx/ui_next/src/api/models/WorkflowJobTemplates.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
37 changes: 34 additions & 3 deletions awx/ui_next/src/components/NotificationList/NotificationList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -27,6 +33,7 @@ function NotificationList({ apiModel, canToggleNotifications, id, i18n }) {
result: {
notifications,
itemCount,
approvalsTemplateIds,
startedTemplateIds,
successTemplateIds,
errorTemplateIds,
Expand Down Expand Up @@ -71,18 +78,35 @@ function NotificationList({ apiModel, canToggleNotifications, id, i18n }) {
apiModel.readNotificationTemplatesError(id, idMatchParams),
]);

return {
const rtnObj = {
notifications: notificationsResults,
itemCount: notificationsCount,
startedTemplateIds: startedTemplates.results.map(st => st.id),
successTemplateIds: successTemplates.results.map(su => su.id),
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: [],
Expand Down Expand Up @@ -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}
/>
)}
/>
Expand All @@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -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}`}
Expand Down Expand Up @@ -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`)}
Expand Down Expand Up @@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -58,26 +73,64 @@ 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}
startedTurnedOn
approvalsTurnedOn
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
typeLabels={typeLabels}
showApprovalsToggle
/>
);
wrapper
.find('Switch')
.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}
startedTurnedOn
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
typeLabels={typeLabels}
/>
);
wrapper
.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}
Expand All @@ -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}
Expand All @@ -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}
Expand Down
Loading

0 comments on commit 681b765

Please sign in to comment.