mirror of
https://github.com/ansible/awx.git
synced 2026-01-22 15:08:03 -03:30
Refactor notifications list to be more generic. Hook notifictions tab up on JT details.
This commit is contained in:
parent
cf27ac295a
commit
2457926f0a
@ -8,6 +8,7 @@ import JobTemplates from './models/JobTemplates';
|
||||
import Jobs from './models/Jobs';
|
||||
import Labels from './models/Labels';
|
||||
import Me from './models/Me';
|
||||
import NotificationTemplates from './models/NotificationTemplates';
|
||||
import Organizations from './models/Organizations';
|
||||
import Projects from './models/Projects';
|
||||
import ProjectUpdates from './models/ProjectUpdates';
|
||||
@ -30,6 +31,7 @@ const JobTemplatesAPI = new JobTemplates();
|
||||
const JobsAPI = new Jobs();
|
||||
const LabelsAPI = new Labels();
|
||||
const MeAPI = new Me();
|
||||
const NotificationTemplatesAPI = new NotificationTemplates();
|
||||
const OrganizationsAPI = new Organizations();
|
||||
const ProjectsAPI = new Projects();
|
||||
const ProjectUpdatesAPI = new ProjectUpdates();
|
||||
@ -53,6 +55,7 @@ export {
|
||||
JobsAPI,
|
||||
LabelsAPI,
|
||||
MeAPI,
|
||||
NotificationTemplatesAPI,
|
||||
OrganizationsAPI,
|
||||
ProjectsAPI,
|
||||
ProjectUpdatesAPI,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import Base from '../Base';
|
||||
import NotificationsMixin from '../mixins/Notifications.mixin';
|
||||
import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin';
|
||||
|
||||
class JobTemplates extends InstanceGroupsMixin(Base) {
|
||||
class JobTemplates extends InstanceGroupsMixin(NotificationsMixin(Base)) {
|
||||
constructor(http) {
|
||||
super(http);
|
||||
this.baseUrl = '/api/v2/job_templates/';
|
||||
|
||||
10
awx/ui_next/src/api/models/NotificationTemplates.js
Normal file
10
awx/ui_next/src/api/models/NotificationTemplates.js
Normal file
@ -0,0 +1,10 @@
|
||||
import Base from '../Base';
|
||||
|
||||
class NotificationTemplates extends Base {
|
||||
constructor(http) {
|
||||
super(http);
|
||||
this.baseUrl = '/api/v2/notification_templates/';
|
||||
}
|
||||
}
|
||||
|
||||
export default NotificationTemplates;
|
||||
@ -4,13 +4,14 @@ import { withRouter } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
|
||||
import { OrganizationsAPI } from '@api';
|
||||
import AlertModal from '@components/AlertModal';
|
||||
import ErrorDetail from '@components/ErrorDetail';
|
||||
import NotificationListItem from '@components/NotificationsList/NotificationListItem';
|
||||
import NotificationListItem from '@components/NotificationList/NotificationListItem';
|
||||
import PaginatedDataList from '@components/PaginatedDataList';
|
||||
import { getQSConfig, parseQueryString } from '@util/qs';
|
||||
|
||||
import { NotificationTemplatesAPI } from '@api';
|
||||
|
||||
const QS_CONFIG = getQSConfig('notification', {
|
||||
page: 1,
|
||||
page_size: 5,
|
||||
@ -23,7 +24,7 @@ const COLUMNS = [
|
||||
{ key: 'created', name: 'Created', isSortable: true, isNumeric: true },
|
||||
];
|
||||
|
||||
class OrganizationNotifications extends Component {
|
||||
class NotificationList extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@ -57,26 +58,23 @@ class OrganizationNotifications extends Component {
|
||||
}
|
||||
|
||||
async loadNotifications() {
|
||||
const { id, location } = this.props;
|
||||
const { id, location, apiModel } = this.props;
|
||||
const { typeLabels } = this.state;
|
||||
const params = parseQueryString(QS_CONFIG, location.search);
|
||||
|
||||
const promises = [OrganizationsAPI.readNotificationTemplates(id, params)];
|
||||
const promises = [NotificationTemplatesAPI.read(params)];
|
||||
|
||||
if (!typeLabels) {
|
||||
promises.push(OrganizationsAPI.readOptionsNotificationTemplates(id));
|
||||
promises.push(NotificationTemplatesAPI.readOptions());
|
||||
}
|
||||
|
||||
this.setState({ contentError: null, hasContentLoading: true });
|
||||
try {
|
||||
const {
|
||||
data: { count: itemCount = 0, results: notifications = [] },
|
||||
} = await OrganizationsAPI.readNotificationTemplates(id, params);
|
||||
} = await NotificationTemplatesAPI.read(params);
|
||||
|
||||
const optionsResponse = await OrganizationsAPI.readOptionsNotificationTemplates(
|
||||
id,
|
||||
params
|
||||
);
|
||||
const optionsResponse = await NotificationTemplatesAPI.readOptions();
|
||||
|
||||
let idMatchParams;
|
||||
if (notifications.length > 0) {
|
||||
@ -90,9 +88,9 @@ class OrganizationNotifications extends Component {
|
||||
{ data: successTemplates },
|
||||
{ data: errorTemplates },
|
||||
] = await Promise.all([
|
||||
OrganizationsAPI.readNotificationTemplatesStarted(id, idMatchParams),
|
||||
OrganizationsAPI.readNotificationTemplatesSuccess(id, idMatchParams),
|
||||
OrganizationsAPI.readNotificationTemplatesError(id, idMatchParams),
|
||||
apiModel.readNotificationTemplatesStarted(id, idMatchParams),
|
||||
apiModel.readNotificationTemplatesSuccess(id, idMatchParams),
|
||||
apiModel.readNotificationTemplatesError(id, idMatchParams),
|
||||
]);
|
||||
|
||||
const stateToUpdate = {
|
||||
@ -129,7 +127,7 @@ class OrganizationNotifications extends Component {
|
||||
}
|
||||
|
||||
async handleNotificationToggle(notificationId, isCurrentlyOn, status) {
|
||||
const { id } = this.props;
|
||||
const { id, apiModel } = this.props;
|
||||
|
||||
let stateArrayName;
|
||||
if (status === 'success') {
|
||||
@ -158,13 +156,13 @@ class OrganizationNotifications extends Component {
|
||||
this.setState({ toggleLoading: true });
|
||||
try {
|
||||
if (isCurrentlyOn) {
|
||||
await OrganizationsAPI.disassociateNotificationTemplate(
|
||||
await apiModel.disassociateNotificationTemplate(
|
||||
id,
|
||||
notificationId,
|
||||
status
|
||||
);
|
||||
} else {
|
||||
await OrganizationsAPI.associateNotificationTemplate(
|
||||
await apiModel.associateNotificationTemplate(
|
||||
id,
|
||||
notificationId,
|
||||
status
|
||||
@ -204,7 +202,7 @@ class OrganizationNotifications extends Component {
|
||||
hasContentLoading={hasContentLoading}
|
||||
items={notifications}
|
||||
itemCount={itemCount}
|
||||
pluralizedItemName="Notifications"
|
||||
pluralizedItemName={i18n._(t`Notifications`)}
|
||||
qsConfig={QS_CONFIG}
|
||||
toolbarColumns={COLUMNS}
|
||||
renderItem={notification => (
|
||||
@ -235,7 +233,7 @@ class OrganizationNotifications extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
OrganizationNotifications.propTypes = {
|
||||
NotificationList.propTypes = {
|
||||
id: number.isRequired,
|
||||
canToggleNotifications: bool.isRequired,
|
||||
location: shape({
|
||||
@ -243,5 +241,5 @@ OrganizationNotifications.propTypes = {
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export { OrganizationNotifications as _OrganizationNotifications };
|
||||
export default withI18n()(withRouter(OrganizationNotifications));
|
||||
export { NotificationList as _NotificationList };
|
||||
export default withI18n()(withRouter(NotificationList));
|
||||
@ -1,13 +1,15 @@
|
||||
import React from 'react';
|
||||
import { OrganizationsAPI } from '@api';
|
||||
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import { sleep } from '@testUtils/testUtils';
|
||||
|
||||
import OrganizationNotifications from './OrganizationNotifications';
|
||||
import { NotificationTemplatesAPI } from '@api';
|
||||
|
||||
import NotificationList from './NotificationList';
|
||||
|
||||
jest.mock('@api');
|
||||
|
||||
describe('<OrganizationNotifications />', () => {
|
||||
describe('<NotificationList />', () => {
|
||||
const data = {
|
||||
count: 2,
|
||||
results: [
|
||||
@ -32,7 +34,19 @@ describe('<OrganizationNotifications />', () => {
|
||||
],
|
||||
};
|
||||
|
||||
OrganizationsAPI.readOptionsNotificationTemplates.mockReturnValue({
|
||||
const MockModel = jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
readNotificationTemplatesSuccess: jest.fn(),
|
||||
readNotificationTemplatesError: jest.fn(),
|
||||
readNotificationTemplatesStarted: jest.fn(),
|
||||
associateNotificationTemplate: jest.fn(),
|
||||
disassociateNotificationTemplate: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const MockModelAPI = new MockModel();
|
||||
|
||||
NotificationTemplatesAPI.readOptions.mockReturnValue({
|
||||
data: {
|
||||
actions: {
|
||||
GET: {
|
||||
@ -45,14 +59,14 @@ describe('<OrganizationNotifications />', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
OrganizationsAPI.readNotificationTemplates.mockReturnValue({ data });
|
||||
OrganizationsAPI.readNotificationTemplatesSuccess.mockReturnValue({
|
||||
NotificationTemplatesAPI.read.mockReturnValue({ data });
|
||||
MockModelAPI.readNotificationTemplatesSuccess.mockReturnValue({
|
||||
data: { results: [{ id: 1 }] },
|
||||
});
|
||||
OrganizationsAPI.readNotificationTemplatesError.mockReturnValue({
|
||||
MockModelAPI.readNotificationTemplatesError.mockReturnValue({
|
||||
data: { results: [{ id: 2 }] },
|
||||
});
|
||||
OrganizationsAPI.readNotificationTemplatesStarted.mockReturnValue({
|
||||
MockModelAPI.readNotificationTemplatesStarted.mockReturnValue({
|
||||
data: { results: [{ id: 3 }] },
|
||||
});
|
||||
});
|
||||
@ -63,7 +77,7 @@ describe('<OrganizationNotifications />', () => {
|
||||
|
||||
test('initially renders succesfully', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<OrganizationNotifications id={1} canToggleNotifications />
|
||||
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
@ -72,15 +86,15 @@ describe('<OrganizationNotifications />', () => {
|
||||
|
||||
test('should render list fetched of items', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<OrganizationNotifications id={1} canToggleNotifications />
|
||||
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
|
||||
expect(OrganizationsAPI.readNotificationTemplates).toHaveBeenCalled();
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('notifications')
|
||||
).toEqual(data.results);
|
||||
expect(NotificationTemplatesAPI.read).toHaveBeenCalled();
|
||||
expect(wrapper.find('NotificationList').state('notifications')).toEqual(
|
||||
data.results
|
||||
);
|
||||
const items = wrapper.find('NotificationListItem');
|
||||
expect(items).toHaveLength(3);
|
||||
expect(items.at(0).prop('successTurnedOn')).toEqual(true);
|
||||
@ -96,13 +110,13 @@ describe('<OrganizationNotifications />', () => {
|
||||
|
||||
test('should enable success notification', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<OrganizationNotifications id={1} canToggleNotifications />
|
||||
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('successTemplateIds')
|
||||
wrapper.find('NotificationList').state('successTemplateIds')
|
||||
).toEqual([1]);
|
||||
const items = wrapper.find('NotificationListItem');
|
||||
items
|
||||
@ -110,7 +124,7 @@ describe('<OrganizationNotifications />', () => {
|
||||
.find('Switch')
|
||||
.at(1)
|
||||
.prop('onChange')();
|
||||
expect(OrganizationsAPI.associateNotificationTemplate).toHaveBeenCalledWith(
|
||||
expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith(
|
||||
1,
|
||||
2,
|
||||
'success'
|
||||
@ -118,47 +132,48 @@ describe('<OrganizationNotifications />', () => {
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('successTemplateIds')
|
||||
wrapper.find('NotificationList').state('successTemplateIds')
|
||||
).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
test('should enable error notification', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<OrganizationNotifications id={1} canToggleNotifications />
|
||||
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('errorTemplateIds')
|
||||
).toEqual([2]);
|
||||
expect(wrapper.find('NotificationList').state('errorTemplateIds')).toEqual([
|
||||
2,
|
||||
]);
|
||||
const items = wrapper.find('NotificationListItem');
|
||||
items
|
||||
.at(0)
|
||||
.find('Switch')
|
||||
.at(2)
|
||||
.prop('onChange')();
|
||||
expect(OrganizationsAPI.associateNotificationTemplate).toHaveBeenCalledWith(
|
||||
expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith(
|
||||
1,
|
||||
1,
|
||||
'error'
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('errorTemplateIds')
|
||||
).toEqual([2, 1]);
|
||||
expect(wrapper.find('NotificationList').state('errorTemplateIds')).toEqual([
|
||||
2,
|
||||
1,
|
||||
]);
|
||||
});
|
||||
|
||||
test('should enable start notification', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<OrganizationNotifications id={1} canToggleNotifications />
|
||||
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('startedTemplateIds')
|
||||
wrapper.find('NotificationList').state('startedTemplateIds')
|
||||
).toEqual([3]);
|
||||
const items = wrapper.find('NotificationListItem');
|
||||
items
|
||||
@ -166,7 +181,7 @@ describe('<OrganizationNotifications />', () => {
|
||||
.find('Switch')
|
||||
.at(0)
|
||||
.prop('onChange')();
|
||||
expect(OrganizationsAPI.associateNotificationTemplate).toHaveBeenCalledWith(
|
||||
expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith(
|
||||
1,
|
||||
1,
|
||||
'started'
|
||||
@ -174,19 +189,19 @@ describe('<OrganizationNotifications />', () => {
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('startedTemplateIds')
|
||||
wrapper.find('NotificationList').state('startedTemplateIds')
|
||||
).toEqual([3, 1]);
|
||||
});
|
||||
|
||||
test('should disable success notification', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<OrganizationNotifications id={1} canToggleNotifications />
|
||||
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('successTemplateIds')
|
||||
wrapper.find('NotificationList').state('successTemplateIds')
|
||||
).toEqual([1]);
|
||||
const items = wrapper.find('NotificationListItem');
|
||||
items
|
||||
@ -194,51 +209,55 @@ describe('<OrganizationNotifications />', () => {
|
||||
.find('Switch')
|
||||
.at(1)
|
||||
.prop('onChange')();
|
||||
expect(
|
||||
OrganizationsAPI.disassociateNotificationTemplate
|
||||
).toHaveBeenCalledWith(1, 1, 'success');
|
||||
expect(MockModelAPI.disassociateNotificationTemplate).toHaveBeenCalledWith(
|
||||
1,
|
||||
1,
|
||||
'success'
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('successTemplateIds')
|
||||
wrapper.find('NotificationList').state('successTemplateIds')
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('should disable error notification', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<OrganizationNotifications id={1} canToggleNotifications />
|
||||
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('errorTemplateIds')
|
||||
).toEqual([2]);
|
||||
expect(wrapper.find('NotificationList').state('errorTemplateIds')).toEqual([
|
||||
2,
|
||||
]);
|
||||
const items = wrapper.find('NotificationListItem');
|
||||
items
|
||||
.at(1)
|
||||
.find('Switch')
|
||||
.at(2)
|
||||
.prop('onChange')();
|
||||
expect(
|
||||
OrganizationsAPI.disassociateNotificationTemplate
|
||||
).toHaveBeenCalledWith(1, 2, 'error');
|
||||
expect(MockModelAPI.disassociateNotificationTemplate).toHaveBeenCalledWith(
|
||||
1,
|
||||
2,
|
||||
'error'
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('errorTemplateIds')
|
||||
).toEqual([]);
|
||||
expect(wrapper.find('NotificationList').state('errorTemplateIds')).toEqual(
|
||||
[]
|
||||
);
|
||||
});
|
||||
|
||||
test('should disable start notification', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<OrganizationNotifications id={1} canToggleNotifications />
|
||||
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('startedTemplateIds')
|
||||
wrapper.find('NotificationList').state('startedTemplateIds')
|
||||
).toEqual([3]);
|
||||
const items = wrapper.find('NotificationListItem');
|
||||
items
|
||||
@ -246,13 +265,15 @@ describe('<OrganizationNotifications />', () => {
|
||||
.find('Switch')
|
||||
.at(0)
|
||||
.prop('onChange')();
|
||||
expect(
|
||||
OrganizationsAPI.disassociateNotificationTemplate
|
||||
).toHaveBeenCalledWith(1, 3, 'started');
|
||||
expect(MockModelAPI.disassociateNotificationTemplate).toHaveBeenCalledWith(
|
||||
1,
|
||||
3,
|
||||
'started'
|
||||
);
|
||||
await sleep(0);
|
||||
wrapper.update();
|
||||
expect(
|
||||
wrapper.find('OrganizationNotifications').state('startedTemplateIds')
|
||||
wrapper.find('NotificationList').state('startedTemplateIds')
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@ -106,9 +106,9 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "NotificationListItem__DataListCell-j7c411-0",
|
||||
"componentId": "NotificationListItem__DataListCell-w674ng-0",
|
||||
"isStatic": false,
|
||||
"lastClassName": "hoXOpW",
|
||||
"lastClassName": "dXsFLF",
|
||||
"rules": Array [
|
||||
"display:flex;justify-content:",
|
||||
[Function],
|
||||
@ -122,7 +122,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"displayName": "NotificationListItem__DataListCell",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
|
||||
"styledComponentId": "NotificationListItem__DataListCell-w674ng-0",
|
||||
"target": [Function],
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
@ -132,10 +132,10 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
forwardedRef={null}
|
||||
>
|
||||
<DataListCell
|
||||
className="NotificationListItem__DataListCell-j7c411-0 kIdLtz"
|
||||
className="NotificationListItem__DataListCell-w674ng-0 faYgxF"
|
||||
>
|
||||
<div
|
||||
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 kIdLtz"
|
||||
className="pf-c-data-list__cell NotificationListItem__DataListCell-w674ng-0 faYgxF"
|
||||
>
|
||||
<Styled(Link)
|
||||
to={
|
||||
@ -209,9 +209,9 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "NotificationListItem__DataListCell-j7c411-0",
|
||||
"componentId": "NotificationListItem__DataListCell-w674ng-0",
|
||||
"isStatic": false,
|
||||
"lastClassName": "hoXOpW",
|
||||
"lastClassName": "dXsFLF",
|
||||
"rules": Array [
|
||||
"display:flex;justify-content:",
|
||||
[Function],
|
||||
@ -225,7 +225,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"displayName": "NotificationListItem__DataListCell",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
|
||||
"styledComponentId": "NotificationListItem__DataListCell-w674ng-0",
|
||||
"target": [Function],
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
@ -235,10 +235,10 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
forwardedRef={null}
|
||||
>
|
||||
<DataListCell
|
||||
className="NotificationListItem__DataListCell-j7c411-0 kIdLtz"
|
||||
className="NotificationListItem__DataListCell-w674ng-0 faYgxF"
|
||||
>
|
||||
<div
|
||||
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 kIdLtz"
|
||||
className="pf-c-data-list__cell NotificationListItem__DataListCell-w674ng-0 faYgxF"
|
||||
>
|
||||
Slack
|
||||
</div>
|
||||
@ -255,9 +255,9 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "NotificationListItem__DataListCell-j7c411-0",
|
||||
"componentId": "NotificationListItem__DataListCell-w674ng-0",
|
||||
"isStatic": false,
|
||||
"lastClassName": "hoXOpW",
|
||||
"lastClassName": "dXsFLF",
|
||||
"rules": Array [
|
||||
"display:flex;justify-content:",
|
||||
[Function],
|
||||
@ -271,7 +271,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"displayName": "NotificationListItem__DataListCell",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
|
||||
"styledComponentId": "NotificationListItem__DataListCell-w674ng-0",
|
||||
"target": [Function],
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
@ -282,11 +282,11 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
righthalf="true"
|
||||
>
|
||||
<DataListCell
|
||||
className="NotificationListItem__DataListCell-j7c411-0 hoXOpW"
|
||||
className="NotificationListItem__DataListCell-w674ng-0 dXsFLF"
|
||||
righthalf="true"
|
||||
>
|
||||
<div
|
||||
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 hoXOpW"
|
||||
className="pf-c-data-list__cell NotificationListItem__DataListCell-w674ng-0 dXsFLF"
|
||||
righthalf="true"
|
||||
>
|
||||
<NotificationListItem__Switch
|
||||
@ -305,9 +305,9 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "NotificationListItem__Switch-j7c411-1",
|
||||
"componentId": "NotificationListItem__Switch-w674ng-1",
|
||||
"isStatic": true,
|
||||
"lastClassName": "ceuHGn",
|
||||
"lastClassName": "hbNxaH",
|
||||
"rules": Array [
|
||||
"display:flex;flex-wrap:no-wrap;",
|
||||
],
|
||||
@ -315,7 +315,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"displayName": "NotificationListItem__Switch",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
|
||||
"styledComponentId": "NotificationListItem__Switch-w674ng-1",
|
||||
"target": [Function],
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
@ -332,7 +332,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
>
|
||||
<Component
|
||||
aria-label="Toggle notification start"
|
||||
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
className="NotificationListItem__Switch-w674ng-1 hbNxaH"
|
||||
id="notification-9000-started-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
@ -345,7 +345,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
componentProps={
|
||||
Object {
|
||||
"aria-label": "Toggle notification start",
|
||||
"className": "NotificationListItem__Switch-j7c411-1 ceuHGn",
|
||||
"className": "NotificationListItem__Switch-w674ng-1 hbNxaH",
|
||||
"id": "notification-9000-started-toggle",
|
||||
"isChecked": false,
|
||||
"isDisabled": false,
|
||||
@ -358,7 +358,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
>
|
||||
<Switch
|
||||
aria-label="Toggle notification start"
|
||||
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
className="NotificationListItem__Switch-w674ng-1 hbNxaH"
|
||||
id="notification-9000-started-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
@ -373,7 +373,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
}
|
||||
>
|
||||
<label
|
||||
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
className="pf-c-switch NotificationListItem__Switch-w674ng-1 hbNxaH"
|
||||
htmlFor="notification-9000-started-toggle"
|
||||
>
|
||||
<input
|
||||
@ -425,9 +425,9 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "NotificationListItem__Switch-j7c411-1",
|
||||
"componentId": "NotificationListItem__Switch-w674ng-1",
|
||||
"isStatic": true,
|
||||
"lastClassName": "ceuHGn",
|
||||
"lastClassName": "hbNxaH",
|
||||
"rules": Array [
|
||||
"display:flex;flex-wrap:no-wrap;",
|
||||
],
|
||||
@ -435,7 +435,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"displayName": "NotificationListItem__Switch",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
|
||||
"styledComponentId": "NotificationListItem__Switch-w674ng-1",
|
||||
"target": [Function],
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
@ -452,7 +452,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
>
|
||||
<Component
|
||||
aria-label="Toggle notification success"
|
||||
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
className="NotificationListItem__Switch-w674ng-1 hbNxaH"
|
||||
id="notification-9000-success-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
@ -465,7 +465,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
componentProps={
|
||||
Object {
|
||||
"aria-label": "Toggle notification success",
|
||||
"className": "NotificationListItem__Switch-j7c411-1 ceuHGn",
|
||||
"className": "NotificationListItem__Switch-w674ng-1 hbNxaH",
|
||||
"id": "notification-9000-success-toggle",
|
||||
"isChecked": false,
|
||||
"isDisabled": false,
|
||||
@ -478,7 +478,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
>
|
||||
<Switch
|
||||
aria-label="Toggle notification success"
|
||||
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
className="NotificationListItem__Switch-w674ng-1 hbNxaH"
|
||||
id="notification-9000-success-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
@ -493,7 +493,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
}
|
||||
>
|
||||
<label
|
||||
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
className="pf-c-switch NotificationListItem__Switch-w674ng-1 hbNxaH"
|
||||
htmlFor="notification-9000-success-toggle"
|
||||
>
|
||||
<input
|
||||
@ -545,9 +545,9 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "NotificationListItem__Switch-j7c411-1",
|
||||
"componentId": "NotificationListItem__Switch-w674ng-1",
|
||||
"isStatic": true,
|
||||
"lastClassName": "ceuHGn",
|
||||
"lastClassName": "hbNxaH",
|
||||
"rules": Array [
|
||||
"display:flex;flex-wrap:no-wrap;",
|
||||
],
|
||||
@ -555,7 +555,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
"displayName": "NotificationListItem__Switch",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
|
||||
"styledComponentId": "NotificationListItem__Switch-w674ng-1",
|
||||
"target": [Function],
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
@ -572,7 +572,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
>
|
||||
<Component
|
||||
aria-label="Toggle notification failure"
|
||||
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
className="NotificationListItem__Switch-w674ng-1 hbNxaH"
|
||||
id="notification-9000-error-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
@ -585,7 +585,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
componentProps={
|
||||
Object {
|
||||
"aria-label": "Toggle notification failure",
|
||||
"className": "NotificationListItem__Switch-j7c411-1 ceuHGn",
|
||||
"className": "NotificationListItem__Switch-w674ng-1 hbNxaH",
|
||||
"id": "notification-9000-error-toggle",
|
||||
"isChecked": false,
|
||||
"isDisabled": false,
|
||||
@ -598,7 +598,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
>
|
||||
<Switch
|
||||
aria-label="Toggle notification failure"
|
||||
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
className="NotificationListItem__Switch-w674ng-1 hbNxaH"
|
||||
id="notification-9000-error-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
@ -613,7 +613,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
}
|
||||
>
|
||||
<label
|
||||
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
className="pf-c-switch NotificationListItem__Switch-w674ng-1 hbNxaH"
|
||||
htmlFor="notification-9000-error-toggle"
|
||||
>
|
||||
<input
|
||||
2
awx/ui_next/src/components/NotificationList/index.js
Normal file
2
awx/ui_next/src/components/NotificationList/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export { default } from './NotificationList';
|
||||
export { default as NotificationListItem } from './NotificationListItem';
|
||||
@ -1 +0,0 @@
|
||||
export { default } from './NotificationListItem';
|
||||
@ -11,10 +11,10 @@ import styled from 'styled-components';
|
||||
import CardCloseButton from '@components/CardCloseButton';
|
||||
import RoutedTabs from '@components/RoutedTabs';
|
||||
import ContentError from '@components/ContentError';
|
||||
import NotificationList from '@components/NotificationList/NotificationList';
|
||||
import { OrganizationAccess } from './OrganizationAccess';
|
||||
import OrganizationDetail from './OrganizationDetail';
|
||||
import OrganizationEdit from './OrganizationEdit';
|
||||
import OrganizationNotifications from './OrganizationNotifications';
|
||||
import OrganizationTeams from './OrganizationTeams';
|
||||
import { OrganizationsAPI } from '@api';
|
||||
|
||||
@ -228,9 +228,10 @@ class Organization extends Component {
|
||||
<Route
|
||||
path="/organizations/:id/notifications"
|
||||
render={() => (
|
||||
<OrganizationNotifications
|
||||
<NotificationList
|
||||
id={Number(match.params.id)}
|
||||
canToggleNotifications={canToggleNotifications}
|
||||
apiModel={OrganizationsAPI}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export { default } from './OrganizationNotifications';
|
||||
@ -5,9 +5,10 @@ import { Card, CardHeader, PageSection } from '@patternfly/react-core';
|
||||
import { Switch, Route, Redirect, withRouter, Link } from 'react-router-dom';
|
||||
import CardCloseButton from '@components/CardCloseButton';
|
||||
import ContentError from '@components/ContentError';
|
||||
import NotificationList from '@components/NotificationList';
|
||||
import RoutedTabs from '@components/RoutedTabs';
|
||||
import JobTemplateDetail from './JobTemplateDetail';
|
||||
import { JobTemplatesAPI } from '@api';
|
||||
import { JobTemplatesAPI, OrganizationsAPI } from '@api';
|
||||
import JobTemplateEdit from './JobTemplateEdit';
|
||||
|
||||
class Template extends Component {
|
||||
@ -18,12 +19,14 @@ class Template extends Component {
|
||||
contentError: null,
|
||||
hasContentLoading: true,
|
||||
template: null,
|
||||
isNotifAdmin: false,
|
||||
};
|
||||
this.loadTemplate = this.loadTemplate.bind(this);
|
||||
this.loadTemplateAndRoles = this.loadTemplateAndRoles.bind(this);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
await this.loadTemplate();
|
||||
await this.loadTemplateAndRoles();
|
||||
}
|
||||
|
||||
async componentDidUpdate(prevProps) {
|
||||
@ -33,6 +36,31 @@ class Template extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
async loadTemplateAndRoles() {
|
||||
const { match, setBreadcrumb } = this.props;
|
||||
const id = parseInt(match.params.id, 10);
|
||||
|
||||
this.setState({ contentError: null, hasContentLoading: true });
|
||||
try {
|
||||
const [{ data }, notifAdminRes] = await Promise.all([
|
||||
JobTemplatesAPI.readDetail(id),
|
||||
OrganizationsAPI.read({
|
||||
page_size: 1,
|
||||
role_level: 'notification_admin_role',
|
||||
}),
|
||||
]);
|
||||
setBreadcrumb(data);
|
||||
this.setState({
|
||||
template: data,
|
||||
isNotifAdmin: notifAdminRes.data.results.length > 0,
|
||||
});
|
||||
} catch (err) {
|
||||
this.setState({ contentError: err });
|
||||
} finally {
|
||||
this.setState({ hasContentLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
async loadTemplate() {
|
||||
const { setBreadcrumb, match } = this.props;
|
||||
const { id } = match.params;
|
||||
@ -50,18 +78,47 @@ class Template extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { history, i18n, location, match } = this.props;
|
||||
const { contentError, hasContentLoading, template } = this.state;
|
||||
const { history, i18n, location, match, me } = this.props;
|
||||
const {
|
||||
contentError,
|
||||
hasContentLoading,
|
||||
isNotifAdmin,
|
||||
template,
|
||||
} = this.state;
|
||||
|
||||
const canSeeNotificationsTab = me.is_system_auditor || isNotifAdmin;
|
||||
|
||||
const tabsArray = [
|
||||
{ name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 },
|
||||
{ name: i18n._(t`Access`), link: '/home', id: 1 },
|
||||
{ name: i18n._(t`Notifications`), link: '/home', id: 2 },
|
||||
{ name: i18n._(t`Schedules`), link: '/home', id: 3 },
|
||||
{ name: i18n._(t`Completed Jobs`), link: '/home', id: 4 },
|
||||
{ name: i18n._(t`Survey`), link: '/home', id: 5 },
|
||||
];
|
||||
|
||||
if (canSeeNotificationsTab) {
|
||||
tabsArray.push({
|
||||
name: i18n._(t`Notifications`),
|
||||
link: `${match.url}/notifications`,
|
||||
id: 2,
|
||||
});
|
||||
}
|
||||
|
||||
tabsArray.push(
|
||||
{
|
||||
name: i18n._(t`Schedules`),
|
||||
link: '/home',
|
||||
id: canSeeNotificationsTab ? 3 : 2,
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Completed Jobs`),
|
||||
link: '/home',
|
||||
id: canSeeNotificationsTab ? 4 : 3,
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Survey`),
|
||||
link: '/home',
|
||||
id: canSeeNotificationsTab ? 5 : 4,
|
||||
}
|
||||
);
|
||||
|
||||
let cardHeader = hasContentLoading ? null : (
|
||||
<CardHeader style={{ padding: 0 }}>
|
||||
<RoutedTabs history={history} tabsArray={tabsArray} />
|
||||
@ -89,6 +146,7 @@ class Template extends Component {
|
||||
</PageSection>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PageSection>
|
||||
<Card className="awx-c-card">
|
||||
@ -99,7 +157,7 @@ class Template extends Component {
|
||||
to="/templates/:templateType/:id/details"
|
||||
exact
|
||||
/>
|
||||
{template && [
|
||||
{template && (
|
||||
<Route
|
||||
key="details"
|
||||
path="/templates/:templateType/:id/details"
|
||||
@ -110,30 +168,44 @@ class Template extends Component {
|
||||
template={template}
|
||||
/>
|
||||
)}
|
||||
/>,
|
||||
/>
|
||||
)}
|
||||
{template && (
|
||||
<Route
|
||||
key="edit"
|
||||
path="/templates/:templateType/:id/edit"
|
||||
render={() => <JobTemplateEdit template={template} />}
|
||||
/>,
|
||||
/>
|
||||
)}
|
||||
{canSeeNotificationsTab && (
|
||||
<Route
|
||||
key="not-found"
|
||||
path="*"
|
||||
render={() =>
|
||||
!hasContentLoading && (
|
||||
<ContentError isNotFound>
|
||||
{match.params.id && (
|
||||
<Link
|
||||
to={`/templates/${match.params.templateType}/${match.params.id}/details`}
|
||||
>
|
||||
{i18n._(`View Template Details`)}
|
||||
</Link>
|
||||
)}
|
||||
</ContentError>
|
||||
)
|
||||
}
|
||||
/>,
|
||||
]}
|
||||
path="/templates/:templateType/:id/notifications"
|
||||
render={() => (
|
||||
<NotificationList
|
||||
id={Number(match.params.id)}
|
||||
canToggleNotifications={isNotifAdmin}
|
||||
apiModel={JobTemplatesAPI}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Route
|
||||
key="not-found"
|
||||
path="*"
|
||||
render={() =>
|
||||
!hasContentLoading && (
|
||||
<ContentError isNotFound>
|
||||
{match.params.id && (
|
||||
<Link
|
||||
to={`/templates/${match.params.templateType}/${match.params.id}/details`}
|
||||
>
|
||||
{i18n._(`View Template Details`)}
|
||||
</Link>
|
||||
)}
|
||||
</ContentError>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Switch>
|
||||
</Card>
|
||||
</PageSection>
|
||||
|
||||
@ -1,20 +1,52 @@
|
||||
import React from 'react';
|
||||
import { JobTemplatesAPI, OrganizationsAPI } from '@api';
|
||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||
import Template, { _Template } from './Template';
|
||||
|
||||
import mockJobTemplateData from './shared/data.job_template.json';
|
||||
|
||||
jest.mock('@api');
|
||||
|
||||
JobTemplatesAPI.readDetail.mockResolvedValue({
|
||||
data: mockJobTemplateData,
|
||||
});
|
||||
|
||||
OrganizationsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
count: 1,
|
||||
next: null,
|
||||
previous: null,
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const mockMe = {
|
||||
is_super_user: true,
|
||||
is_system_auditor: false,
|
||||
};
|
||||
|
||||
describe('<Template />', () => {
|
||||
test('initially renders succesfully', () => {
|
||||
mountWithContexts(<Template />);
|
||||
mountWithContexts(<Template setBreadcrumb={() => {}} me={mockMe} />);
|
||||
});
|
||||
test('When component mounts API is called and the response is put in state', async done => {
|
||||
const loadTemplate = jest.spyOn(_Template.prototype, 'loadTemplate');
|
||||
const wrapper = mountWithContexts(<Template />);
|
||||
const loadTemplateAndRoles = jest.spyOn(
|
||||
_Template.prototype,
|
||||
'loadTemplateAndRoles'
|
||||
);
|
||||
const wrapper = mountWithContexts(
|
||||
<Template setBreadcrumb={() => {}} me={mockMe} />
|
||||
);
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'Template',
|
||||
el => el.state('hasContentLoading') === true
|
||||
);
|
||||
expect(loadTemplate).toHaveBeenCalled();
|
||||
expect(loadTemplateAndRoles).toHaveBeenCalled();
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'Template',
|
||||
@ -22,4 +54,37 @@ describe('<Template />', () => {
|
||||
);
|
||||
done();
|
||||
});
|
||||
test('notifications tab shown for admins', async done => {
|
||||
const wrapper = mountWithContexts(
|
||||
<Template setBreadcrumb={() => {}} me={mockMe} />
|
||||
);
|
||||
const tabs = await waitForElement(
|
||||
wrapper,
|
||||
'.pf-c-tabs__item',
|
||||
el => el.length === 6
|
||||
);
|
||||
expect(tabs.at(2).text()).toEqual('Notifications');
|
||||
done();
|
||||
});
|
||||
test('notifications tab hidden with reduced permissions', async done => {
|
||||
OrganizationsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
count: 0,
|
||||
next: null,
|
||||
previous: null,
|
||||
results: [],
|
||||
},
|
||||
});
|
||||
|
||||
const wrapper = mountWithContexts(
|
||||
<Template setBreadcrumb={() => {}} me={mockMe} />
|
||||
);
|
||||
const tabs = await waitForElement(
|
||||
wrapper,
|
||||
'.pf-c-tabs__item',
|
||||
el => el.length === 5
|
||||
);
|
||||
tabs.forEach(tab => expect(tab.text()).not.toEqual('Notifications'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
162
awx/ui_next/src/screens/Template/shared/data.job_template.json
Normal file
162
awx/ui_next/src/screens/Template/shared/data.job_template.json
Normal file
@ -0,0 +1,162 @@
|
||||
{
|
||||
"id": 7,
|
||||
"type": "job_template",
|
||||
"url": "/api/v2/job_templates/7/",
|
||||
"related": {
|
||||
"named_url": "/api/v2/job_templates/Mike's JT/",
|
||||
"created_by": "/api/v2/users/1/",
|
||||
"modified_by": "/api/v2/users/1/",
|
||||
"labels": "/api/v2/job_templates/7/labels/",
|
||||
"inventory": "/api/v2/inventories/1/",
|
||||
"project": "/api/v2/projects/6/",
|
||||
"extra_credentials": "/api/v2/job_templates/7/extra_credentials/",
|
||||
"credentials": "/api/v2/job_templates/7/credentials/",
|
||||
"last_job": "/api/v2/jobs/12/",
|
||||
"jobs": "/api/v2/job_templates/7/jobs/",
|
||||
"schedules": "/api/v2/job_templates/7/schedules/",
|
||||
"activity_stream": "/api/v2/job_templates/7/activity_stream/",
|
||||
"launch": "/api/v2/job_templates/7/launch/",
|
||||
"notification_templates_started": "/api/v2/job_templates/7/notification_templates_started/",
|
||||
"notification_templates_success": "/api/v2/job_templates/7/notification_templates_success/",
|
||||
"notification_templates_error": "/api/v2/job_templates/7/notification_templates_error/",
|
||||
"access_list": "/api/v2/job_templates/7/access_list/",
|
||||
"survey_spec": "/api/v2/job_templates/7/survey_spec/",
|
||||
"object_roles": "/api/v2/job_templates/7/object_roles/",
|
||||
"instance_groups": "/api/v2/job_templates/7/instance_groups/",
|
||||
"slice_workflow_jobs": "/api/v2/job_templates/7/slice_workflow_jobs/",
|
||||
"copy": "/api/v2/job_templates/7/copy/"
|
||||
},
|
||||
"summary_fields": {
|
||||
"inventory": {
|
||||
"id": 1,
|
||||
"name": "Mike's Inventory",
|
||||
"description": "",
|
||||
"has_active_failures": false,
|
||||
"total_hosts": 1,
|
||||
"hosts_with_active_failures": 0,
|
||||
"total_groups": 0,
|
||||
"groups_with_active_failures": 0,
|
||||
"has_inventory_sources": false,
|
||||
"total_inventory_sources": 0,
|
||||
"inventory_sources_with_failures": 0,
|
||||
"organization_id": 1,
|
||||
"kind": ""
|
||||
},
|
||||
"project": {
|
||||
"id": 6,
|
||||
"name": "Mike's Project",
|
||||
"description": "",
|
||||
"status": "successful",
|
||||
"scm_type": "git"
|
||||
},
|
||||
"last_job": {
|
||||
"id": 12,
|
||||
"name": "Mike's JT",
|
||||
"description": "",
|
||||
"finished": "2019-10-01T14:34:35.142483Z",
|
||||
"status": "successful",
|
||||
"failed": false
|
||||
},
|
||||
"last_update": {
|
||||
"id": 12,
|
||||
"name": "Mike's JT",
|
||||
"description": "",
|
||||
"status": "successful",
|
||||
"failed": false
|
||||
},
|
||||
"created_by": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"first_name": "",
|
||||
"last_name": ""
|
||||
},
|
||||
"modified_by": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"first_name": "",
|
||||
"last_name": ""
|
||||
},
|
||||
"object_roles": {
|
||||
"admin_role": {
|
||||
"description": "Can manage all aspects of the job template",
|
||||
"name": "Admin",
|
||||
"id": 24
|
||||
},
|
||||
"execute_role": {
|
||||
"description": "May run the job template",
|
||||
"name": "Execute",
|
||||
"id": 25
|
||||
},
|
||||
"read_role": {
|
||||
"description": "May view settings for the job template",
|
||||
"name": "Read",
|
||||
"id": 26
|
||||
}
|
||||
},
|
||||
"user_capabilities": {
|
||||
"edit": true,
|
||||
"delete": true,
|
||||
"start": true,
|
||||
"schedule": true,
|
||||
"copy": true
|
||||
},
|
||||
"labels": {
|
||||
"count": 0,
|
||||
"results": []
|
||||
},
|
||||
"survey": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
},
|
||||
"recent_jobs": [
|
||||
{
|
||||
"id": 12,
|
||||
"status": "successful",
|
||||
"finished": "2019-10-01T14:34:35.142483Z",
|
||||
"type": "job"
|
||||
}
|
||||
],
|
||||
"extra_credentials": [],
|
||||
"credentials": []
|
||||
},
|
||||
"created": "2019-09-30T16:18:34.564820Z",
|
||||
"modified": "2019-10-01T14:47:31.818431Z",
|
||||
"name": "Mike's JT",
|
||||
"description": "",
|
||||
"job_type": "run",
|
||||
"inventory": 1,
|
||||
"project": 6,
|
||||
"playbook": "ping.yml",
|
||||
"scm_branch": "",
|
||||
"forks": 0,
|
||||
"limit": "",
|
||||
"verbosity": 0,
|
||||
"extra_vars": "",
|
||||
"job_tags": "",
|
||||
"force_handlers": false,
|
||||
"skip_tags": "",
|
||||
"start_at_task": "",
|
||||
"timeout": 0,
|
||||
"use_fact_cache": false,
|
||||
"last_job_run": "2019-10-01T14:34:35.142483Z",
|
||||
"last_job_failed": false,
|
||||
"next_job_run": null,
|
||||
"status": "successful",
|
||||
"host_config_key": "",
|
||||
"ask_scm_branch_on_launch": false,
|
||||
"ask_diff_mode_on_launch": false,
|
||||
"ask_variables_on_launch": false,
|
||||
"ask_limit_on_launch": false,
|
||||
"ask_tags_on_launch": false,
|
||||
"ask_skip_tags_on_launch": false,
|
||||
"ask_job_type_on_launch": false,
|
||||
"ask_verbosity_on_launch": false,
|
||||
"ask_inventory_on_launch": false,
|
||||
"ask_credential_on_launch": false,
|
||||
"survey_enabled": true,
|
||||
"become_enabled": false,
|
||||
"diff_mode": false,
|
||||
"allow_simultaneous": false,
|
||||
"custom_virtualenv": null,
|
||||
"job_slice_count": 1
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user