mirror of
https://github.com/ansible/awx.git
synced 2026-03-06 11:11:07 -03:30
api.js refactor using classes (#250)
Refactor api.js into an api module where endpoint specific models can be imported and used in components.
This commit is contained in:
@@ -12,6 +12,7 @@ Have questions about this document or anything not covered here? Feel free to re
|
|||||||
* [Node and npm](#node-and-npm)
|
* [Node and npm](#node-and-npm)
|
||||||
* [Build the user interface](#build-the-user-interface)
|
* [Build the user interface](#build-the-user-interface)
|
||||||
* [Accessing the AWX web interface](#accessing-the-awx-web-interface)
|
* [Accessing the AWX web interface](#accessing-the-awx-web-interface)
|
||||||
|
* [AWX REST API Interaction](#awx-rest-api-interaction)
|
||||||
* [Working with React](#working-with-react)
|
* [Working with React](#working-with-react)
|
||||||
* [App structure](#app-structure)
|
* [App structure](#app-structure)
|
||||||
* [Naming files](#naming-files)
|
* [Naming files](#naming-files)
|
||||||
@@ -58,6 +59,57 @@ Run the following to build the AWX UI:
|
|||||||
|
|
||||||
You can now log into the AWX web interface at [https://127.0.0.1:3001](https://127.0.0.1:3001).
|
You can now log into the AWX web interface at [https://127.0.0.1:3001](https://127.0.0.1:3001).
|
||||||
|
|
||||||
|
## AWX REST API Interaction
|
||||||
|
|
||||||
|
This interface is built on top of the AWX REST API. If a component needs to interact with the API then the model that corresponds to that base endpoint will need to be imported from the api module.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
`import { OrganizationsAPI, UsersAPI } from '../../../api';`
|
||||||
|
|
||||||
|
All models extend a `Base` class which provides an interface to the standard HTTP methods (GET, POST, PUT etc). Methods that are specific to that endpoint should be added directly to model's class.
|
||||||
|
|
||||||
|
**Mixins** - For related endpoints that apply to several different models a mixin should be used. Mixins are classes with a number of methods and can be used to avoid adding the same methods to a number of different models. A good example of this is the Notifications mixin. This mixin provides generic methods for reading notification templates and toggling them on and off.
|
||||||
|
Note that mixins can be chained. See the example below.
|
||||||
|
|
||||||
|
Example of a model using multiple mixins:
|
||||||
|
|
||||||
|
```
|
||||||
|
import NotificationsMixin from '../mixins/Notifications.mixin';
|
||||||
|
import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin';
|
||||||
|
|
||||||
|
class Organizations extends InstanceGroupsMixin(NotificationsMixin(Base)) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Organizations;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Testing** - The easiest way to mock the api module in tests is to use jest's [automatic mock](https://jestjs.io/docs/en/es6-class-mocks#automatic-mock). This syntax will replace the class with a mock constructor and mock out all methods to return undefined by default. If necessary, you can still override these mocks for specific tests. See the example below.
|
||||||
|
|
||||||
|
Example of mocking a specific method for every test in a suite:
|
||||||
|
|
||||||
|
```
|
||||||
|
import { OrganizationsAPI } from '../../../../src/api';
|
||||||
|
|
||||||
|
// Mocks out all available methods. Comparable to:
|
||||||
|
// OrganizationsAPI.readAccessList = jest.fn();
|
||||||
|
// but for every available method
|
||||||
|
jest.mock('../../../../src/api');
|
||||||
|
|
||||||
|
// Return a specific mock value for the readAccessList method
|
||||||
|
beforeEach(() => {
|
||||||
|
OrganizationsAPI.readAccessList.mockReturnValue({ foo: 'bar' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset mocks
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
## Working with React
|
## Working with React
|
||||||
|
|
||||||
### App structure
|
### App structure
|
||||||
@@ -191,20 +243,19 @@ this.state = {
|
|||||||
|
|
||||||
We have several React contexts that wrap much of the app, including those from react-router, lingui, and some of our own. When testing a component that depends on one or more of these, you can use the `mountWithContexts()` helper function found in `__tests__/enzymeHelpers.jsx`. This can be used just like Enzyme's `mount()` function, except it will wrap the component tree with the necessary context providers and basic stub data.
|
We have several React contexts that wrap much of the app, including those from react-router, lingui, and some of our own. When testing a component that depends on one or more of these, you can use the `mountWithContexts()` helper function found in `__tests__/enzymeHelpers.jsx`. This can be used just like Enzyme's `mount()` function, except it will wrap the component tree with the necessary context providers and basic stub data.
|
||||||
|
|
||||||
If you want to stub the value of a context, or assert actions taken on it, you can customize a contexts value by passing a second parameter to `mountWithContexts`. For example, this provides a custom value for the `Network` context:
|
If you want to stub the value of a context, or assert actions taken on it, you can customize a contexts value by passing a second parameter to `mountWithContexts`. For example, this provides a custom value for the `Config` context:
|
||||||
|
|
||||||
```
|
```
|
||||||
const network = {
|
const config = {
|
||||||
api: {
|
custom_virtualenvs: ['foo', 'bar'],
|
||||||
getOrganizationInstanceGroups: jest.fn(),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
mountWithContexts(<OrganizationForm />, {
|
mountWithContexts(<OrganizationForm />, {
|
||||||
context: { network },
|
context: { config },
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
In this test, when the `OrganizationForm` calls `api.getOrganizationInstanceGroups` from the network context, it will invoke the provided stub. You can assert that this stub is invoked when you expect or to provide stubbed data.
|
Now that these custom virtual environments are available in this `OrganizationForm` test we can assert that the component that displays
|
||||||
|
them is rendering properly.
|
||||||
|
|
||||||
The object containing context values looks for five known contexts, identified by the keys `linguiPublisher`, `router`, `config`, `network`, and `dialog` — the latter three each referring to the contexts defined in `src/contexts`. You can pass `false` for any of these values, and the corresponding context will be omitted from your test. For example, this will mount your component without the dialog context:
|
The object containing context values looks for five known contexts, identified by the keys `linguiPublisher`, `router`, `config`, `network`, and `dialog` — the latter three each referring to the contexts defined in `src/contexts`. You can pass `false` for any of these values, and the corresponding context will be omitted from your test. For example, this will mount your component without the dialog context:
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import { asyncFlush } from '../jest.setup';
|
|||||||
|
|
||||||
import App from '../src/App';
|
import App from '../src/App';
|
||||||
|
|
||||||
|
import { RootAPI } from '../src/api';
|
||||||
|
|
||||||
|
jest.mock('../src/api');
|
||||||
|
|
||||||
describe('<App />', () => {
|
describe('<App />', () => {
|
||||||
test('expected content is rendered', () => {
|
test('expected content is rendered', () => {
|
||||||
const appWrapper = mountWithContexts(
|
const appWrapper = mountWithContexts(
|
||||||
@@ -89,15 +93,13 @@ describe('<App />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('onLogout makes expected call to api client', async (done) => {
|
test('onLogout makes expected call to api client', async (done) => {
|
||||||
const logout = jest.fn(() => Promise.resolve());
|
|
||||||
|
|
||||||
const appWrapper = mountWithContexts(<App />, {
|
const appWrapper = mountWithContexts(<App />, {
|
||||||
context: { network: { api: { logout }, handleHttpError: () => {} } }
|
context: { network: { handleHttpError: () => {} } }
|
||||||
}).find('App');
|
}).find('App');
|
||||||
|
|
||||||
appWrapper.instance().onLogout();
|
appWrapper.instance().onLogout();
|
||||||
await asyncFlush();
|
await asyncFlush();
|
||||||
expect(logout).toHaveBeenCalledTimes(1);
|
expect(RootAPI.logout).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -46,17 +46,6 @@ exports[`mountWithContexts injected I18nProvider should mount and render deeply
|
|||||||
</Parent>
|
</Parent>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`mountWithContexts injected Network should mount and render 1`] = `
|
|
||||||
<Foo
|
|
||||||
api={"/api/"}
|
|
||||||
handleHttpError={[Function]}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
test
|
|
||||||
</div>
|
|
||||||
</Foo>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`mountWithContexts injected Router should mount and render 1`] = `
|
exports[`mountWithContexts injected Router should mount and render 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -1,173 +0,0 @@
|
|||||||
import APIClient from '../src/api';
|
|
||||||
|
|
||||||
const invalidCookie = 'invalid';
|
|
||||||
const validLoggedOutCookie = 'current_user=%7B%22id%22%3A1%2C%22type%22%3A%22user%22%2C%22url%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2F%22%2C%22related%22%3A%7B%22admin_of_organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fadmin_of_organizations%2F%22%2C%22authorized_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fauthorized_tokens%2F%22%2C%22roles%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Froles%2F%22%2C%22organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Forganizations%2F%22%2C%22access_list%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Faccess_list%2F%22%2C%22teams%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fteams%2F%22%2C%22tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Ftokens%2F%22%2C%22personal_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fpersonal_tokens%2F%22%2C%22credentials%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fcredentials%2F%22%2C%22activity_stream%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Factivity_stream%2F%22%2C%22projects%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fprojects%2F%22%7D%2C%22summary_fields%22%3A%7B%7D%2C%22created%22%3A%222018-10-19T16%3A30%3A59.141963Z%22%2C%22username%22%3A%22admin%22%2C%22first_name%22%3A%22%22%2C%22last_name%22%3A%22%22%2C%22email%22%3A%22%22%2C%22is_superuser%22%3Atrue%2C%22is_system_auditor%22%3Afalse%2C%22ldap_dn%22%3A%22%22%2C%22external_account%22%3Anull%2C%22auth%22%3A%5B%5D%7D; userLoggedIn=false; csrftoken=lhOHpLQUFHlIVqx8CCZmEpdEZAz79GIRBIT3asBzTbPE7HS7wizt7WBsgJClz8Ge';
|
|
||||||
const validLoggedInCookie = 'current_user=%7B%22id%22%3A1%2C%22type%22%3A%22user%22%2C%22url%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2F%22%2C%22related%22%3A%7B%22admin_of_organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fadmin_of_organizations%2F%22%2C%22authorized_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fauthorized_tokens%2F%22%2C%22roles%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Froles%2F%22%2C%22organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Forganizations%2F%22%2C%22access_list%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Faccess_list%2F%22%2C%22teams%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fteams%2F%22%2C%22tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Ftokens%2F%22%2C%22personal_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fpersonal_tokens%2F%22%2C%22credentials%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fcredentials%2F%22%2C%22activity_stream%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Factivity_stream%2F%22%2C%22projects%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fprojects%2F%22%7D%2C%22summary_fields%22%3A%7B%7D%2C%22created%22%3A%222018-10-19T16%3A30%3A59.141963Z%22%2C%22username%22%3A%22admin%22%2C%22first_name%22%3A%22%22%2C%22last_name%22%3A%22%22%2C%22email%22%3A%22%22%2C%22is_superuser%22%3Atrue%2C%22is_system_auditor%22%3Afalse%2C%22ldap_dn%22%3A%22%22%2C%22external_account%22%3Anull%2C%22auth%22%3A%5B%5D%7D; userLoggedIn=true; csrftoken=lhOHpLQUFHlIVqx8CCZmEpdEZAz79GIRBIT3asBzTbPE7HS7wizt7WBsgJClz8Ge';
|
|
||||||
|
|
||||||
describe('APIClient (api.js)', () => {
|
|
||||||
test('isAuthenticated returns false when cookie is invalid', () => {
|
|
||||||
APIClient.getCookie = jest.fn(() => invalidCookie);
|
|
||||||
|
|
||||||
const api = new APIClient();
|
|
||||||
expect(api.isAuthenticated()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('isAuthenticated returns false when cookie is unauthenticated', () => {
|
|
||||||
APIClient.getCookie = jest.fn(() => validLoggedOutCookie);
|
|
||||||
|
|
||||||
const api = new APIClient();
|
|
||||||
expect(api.isAuthenticated()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('isAuthenticated returns true when cookie is valid and authenticated', () => {
|
|
||||||
APIClient.getCookie = jest.fn(() => validLoggedInCookie);
|
|
||||||
|
|
||||||
const api = new APIClient();
|
|
||||||
expect(api.isAuthenticated()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('login calls get and post with expected content headers', async (done) => {
|
|
||||||
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
|
||||||
|
|
||||||
const createPromise = () => Promise.resolve();
|
|
||||||
const mockHttp = ({ get: jest.fn(createPromise), post: jest.fn(createPromise) });
|
|
||||||
|
|
||||||
const api = new APIClient(mockHttp);
|
|
||||||
await api.login('username', 'password');
|
|
||||||
|
|
||||||
expect(mockHttp.get).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockHttp.get.mock.calls[0]).toContainEqual({ headers });
|
|
||||||
|
|
||||||
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockHttp.post.mock.calls[0]).toContainEqual({ headers });
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('login sends expected data', async (done) => {
|
|
||||||
const createPromise = () => Promise.resolve();
|
|
||||||
const mockHttp = ({ get: jest.fn(createPromise), post: jest.fn(createPromise) });
|
|
||||||
|
|
||||||
const api = new APIClient(mockHttp);
|
|
||||||
await api.login('foo', 'bar');
|
|
||||||
await api.login('foo', 'bar', 'baz');
|
|
||||||
|
|
||||||
expect(mockHttp.post).toHaveBeenCalledTimes(2);
|
|
||||||
expect(mockHttp.post.mock.calls[0]).toContainEqual('username=foo&password=bar&next=%2Fapi%2Fv2%2Fconfig%2F');
|
|
||||||
expect(mockHttp.post.mock.calls[1]).toContainEqual('username=foo&password=bar&next=baz');
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('logout calls expected http method', async (done) => {
|
|
||||||
const createPromise = () => Promise.resolve();
|
|
||||||
const mockHttp = ({ get: jest.fn(createPromise) });
|
|
||||||
|
|
||||||
const api = new APIClient(mockHttp);
|
|
||||||
await api.logout();
|
|
||||||
|
|
||||||
expect(mockHttp.get).toHaveBeenCalledTimes(1);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('getConfig calls expected http method', async (done) => {
|
|
||||||
const createPromise = () => Promise.resolve();
|
|
||||||
const mockHttp = ({ get: jest.fn(createPromise) });
|
|
||||||
|
|
||||||
const api = new APIClient(mockHttp);
|
|
||||||
await api.getConfig();
|
|
||||||
|
|
||||||
expect(mockHttp.get).toHaveBeenCalledTimes(1);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('getOrganizations calls http method with expected data', async (done) => {
|
|
||||||
const createPromise = () => Promise.resolve();
|
|
||||||
const mockHttp = ({ get: jest.fn(createPromise) });
|
|
||||||
const api = new APIClient(mockHttp);
|
|
||||||
|
|
||||||
const defaultParams = {};
|
|
||||||
const testParams = { foo: 'bar' };
|
|
||||||
|
|
||||||
await api.getOrganizations(testParams);
|
|
||||||
await api.getOrganizations();
|
|
||||||
|
|
||||||
expect(mockHttp.get).toHaveBeenCalledTimes(2);
|
|
||||||
expect(mockHttp.get.mock.calls[0][1]).toEqual({ params: testParams });
|
|
||||||
expect(mockHttp.get.mock.calls[1][1]).toEqual({ params: defaultParams });
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('createOrganization calls http method with expected data', async (done) => {
|
|
||||||
const createPromise = () => Promise.resolve();
|
|
||||||
const mockHttp = ({ post: jest.fn(createPromise) });
|
|
||||||
|
|
||||||
const api = new APIClient(mockHttp);
|
|
||||||
const data = { name: 'test ' };
|
|
||||||
await api.createOrganization(data);
|
|
||||||
|
|
||||||
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockHttp.post.mock.calls[0][1]).toEqual(data);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('getOrganizationDetails calls http method with expected data', async (done) => {
|
|
||||||
const createPromise = () => Promise.resolve();
|
|
||||||
const mockHttp = ({ get: jest.fn(createPromise) });
|
|
||||||
|
|
||||||
const api = new APIClient(mockHttp);
|
|
||||||
await api.getOrganizationDetails(99);
|
|
||||||
|
|
||||||
expect(mockHttp.get).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockHttp.get.mock.calls[0][0]).toContain('99');
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('getInstanceGroups calls expected http method', async (done) => {
|
|
||||||
const createPromise = () => Promise.resolve();
|
|
||||||
const mockHttp = ({ get: jest.fn(createPromise) });
|
|
||||||
|
|
||||||
const api = new APIClient(mockHttp);
|
|
||||||
await api.getInstanceGroups();
|
|
||||||
|
|
||||||
expect(mockHttp.get).toHaveBeenCalledTimes(1);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('associateInstanceGroup calls expected http method with expected data', async (done) => {
|
|
||||||
const createPromise = () => Promise.resolve();
|
|
||||||
const mockHttp = ({ post: jest.fn(createPromise) });
|
|
||||||
|
|
||||||
const api = new APIClient(mockHttp);
|
|
||||||
const url = 'foo/bar/';
|
|
||||||
const id = 1;
|
|
||||||
await api.associateInstanceGroup(url, id);
|
|
||||||
|
|
||||||
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockHttp.post.mock.calls[0][0]).toEqual(url);
|
|
||||||
expect(mockHttp.post.mock.calls[0][1]).toEqual({ id });
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('disassociate calls expected http method with expected data', async (done) => {
|
|
||||||
const createPromise = () => Promise.resolve();
|
|
||||||
const mockHttp = ({ post: jest.fn(createPromise) });
|
|
||||||
|
|
||||||
const api = new APIClient(mockHttp);
|
|
||||||
const url = 'foo/bar/';
|
|
||||||
const id = 1;
|
|
||||||
await api.disassociate(url, id);
|
|
||||||
|
|
||||||
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockHttp.post.mock.calls[0][0]).toEqual(url);
|
|
||||||
expect(mockHttp.post.mock.calls[0][1]).toEqual({ id, disassociate: true });
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
97
__tests__/api/base.test.jsx
Normal file
97
__tests__/api/base.test.jsx
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import Base from '../../src/api/Base';
|
||||||
|
|
||||||
|
describe('Base', () => {
|
||||||
|
const createPromise = () => Promise.resolve();
|
||||||
|
const mockBaseURL = '/api/v2/organizations/';
|
||||||
|
const mockHttp = ({
|
||||||
|
delete: jest.fn(createPromise),
|
||||||
|
get: jest.fn(createPromise),
|
||||||
|
options: jest.fn(createPromise),
|
||||||
|
patch: jest.fn(createPromise),
|
||||||
|
post: jest.fn(createPromise),
|
||||||
|
put: jest.fn(createPromise)
|
||||||
|
});
|
||||||
|
|
||||||
|
const BaseAPI = new Base(mockHttp, mockBaseURL);
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create calls http method with expected data', async (done) => {
|
||||||
|
const data = { name: 'test ' };
|
||||||
|
await BaseAPI.create(data);
|
||||||
|
|
||||||
|
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.post.mock.calls[0][1]).toEqual(data);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('destroy calls http method with expected data', async (done) => {
|
||||||
|
const resourceId = 1;
|
||||||
|
await BaseAPI.destroy(resourceId);
|
||||||
|
|
||||||
|
expect(mockHttp.delete).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.delete.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('read calls http method with expected data', async (done) => {
|
||||||
|
const defaultParams = {};
|
||||||
|
const testParams = { foo: 'bar' };
|
||||||
|
|
||||||
|
await BaseAPI.read(testParams);
|
||||||
|
await BaseAPI.read();
|
||||||
|
|
||||||
|
expect(mockHttp.get).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockHttp.get.mock.calls[0][1]).toEqual({ params: testParams });
|
||||||
|
expect(mockHttp.get.mock.calls[1][1]).toEqual({ params: defaultParams });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('readDetail calls http method with expected data', async (done) => {
|
||||||
|
const resourceId = 1;
|
||||||
|
|
||||||
|
await BaseAPI.readDetail(resourceId);
|
||||||
|
|
||||||
|
expect(mockHttp.get).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.get.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('readOptions calls http method with expected data', async (done) => {
|
||||||
|
await BaseAPI.readOptions();
|
||||||
|
|
||||||
|
expect(mockHttp.options).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.options.mock.calls[0][0]).toEqual(`${mockBaseURL}`);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('replace calls http method with expected data', async (done) => {
|
||||||
|
const resourceId = 1;
|
||||||
|
const data = { name: 'test ' };
|
||||||
|
|
||||||
|
await BaseAPI.replace(resourceId, data);
|
||||||
|
|
||||||
|
expect(mockHttp.put).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.put.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`);
|
||||||
|
expect(mockHttp.put.mock.calls[0][1]).toEqual(data);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('update calls http method with expected data', async (done) => {
|
||||||
|
const resourceId = 1;
|
||||||
|
const data = { name: 'test ' };
|
||||||
|
|
||||||
|
await BaseAPI.update(resourceId, data);
|
||||||
|
|
||||||
|
expect(mockHttp.patch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.patch.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`);
|
||||||
|
expect(mockHttp.patch.mock.calls[0][1]).toEqual(data);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
36
__tests__/api/organizations.test.jsx
Normal file
36
__tests__/api/organizations.test.jsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import Organizations from '../../src/api/models/Organizations';
|
||||||
|
|
||||||
|
describe('OrganizationsAPI', () => {
|
||||||
|
const orgId = 1;
|
||||||
|
const searchParams = { foo: 'bar' };
|
||||||
|
const createPromise = () => Promise.resolve();
|
||||||
|
const mockHttp = ({ get: jest.fn(createPromise) });
|
||||||
|
|
||||||
|
const OrganizationsAPI = new Organizations(mockHttp);
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('read access list calls get with expected params', async (done) => {
|
||||||
|
await OrganizationsAPI.readAccessList(orgId);
|
||||||
|
await OrganizationsAPI.readAccessList(orgId, searchParams);
|
||||||
|
|
||||||
|
expect(mockHttp.get).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockHttp.get.mock.calls[0]).toContainEqual(`/api/v2/organizations/${orgId}/access_list/`, { params: {} });
|
||||||
|
expect(mockHttp.get.mock.calls[1]).toContainEqual(`/api/v2/organizations/${orgId}/access_list/`, { params: searchParams });
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('read teams calls get with expected params', async (done) => {
|
||||||
|
await OrganizationsAPI.readTeams(orgId);
|
||||||
|
await OrganizationsAPI.readTeams(orgId, searchParams);
|
||||||
|
|
||||||
|
expect(mockHttp.get).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockHttp.get.mock.calls[0]).toContainEqual(`/api/v2/organizations/${orgId}/teams/`, { params: {} });
|
||||||
|
expect(mockHttp.get.mock.calls[1]).toContainEqual(`/api/v2/organizations/${orgId}/teams/`, { params: searchParams });
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
45
__tests__/api/root.test.jsx
Normal file
45
__tests__/api/root.test.jsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import Root from '../../src/api/models/Root';
|
||||||
|
|
||||||
|
describe('RootAPI', () => {
|
||||||
|
const createPromise = () => Promise.resolve();
|
||||||
|
const mockHttp = ({ get: jest.fn(createPromise), post: jest.fn(createPromise) });
|
||||||
|
|
||||||
|
const RootAPI = new Root(mockHttp);
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('login calls get and post with expected content headers', async (done) => {
|
||||||
|
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||||
|
|
||||||
|
await RootAPI.login('username', 'password');
|
||||||
|
|
||||||
|
expect(mockHttp.get).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.get.mock.calls[0]).toContainEqual({ headers });
|
||||||
|
|
||||||
|
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.post.mock.calls[0]).toContainEqual({ headers });
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('login sends expected data', async (done) => {
|
||||||
|
await RootAPI.login('foo', 'bar');
|
||||||
|
await RootAPI.login('foo', 'bar', 'baz');
|
||||||
|
|
||||||
|
expect(mockHttp.post).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockHttp.post.mock.calls[0]).toContainEqual('username=foo&password=bar&next=%2Fapi%2Fv2%2Fconfig%2F');
|
||||||
|
expect(mockHttp.post.mock.calls[1]).toContainEqual('username=foo&password=bar&next=baz');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('logout calls expected http method', async (done) => {
|
||||||
|
await RootAPI.logout();
|
||||||
|
|
||||||
|
expect(mockHttp.get).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
35
__tests__/api/teams.test.jsx
Normal file
35
__tests__/api/teams.test.jsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import Teams from '../../src/api/models/Teams';
|
||||||
|
|
||||||
|
describe('TeamsAPI', () => {
|
||||||
|
const teamId = 1;
|
||||||
|
const roleId = 7;
|
||||||
|
const createPromise = () => Promise.resolve();
|
||||||
|
const mockHttp = ({ post: jest.fn(createPromise) });
|
||||||
|
|
||||||
|
const TeamsAPI = new Teams(mockHttp);
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('associate role calls post with expected params', async (done) => {
|
||||||
|
await TeamsAPI.associateRole(teamId, roleId);
|
||||||
|
|
||||||
|
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/teams/${teamId}/roles/`, { id: roleId });
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('read teams calls post with expected params', async (done) => {
|
||||||
|
await TeamsAPI.disassociateRole(teamId, roleId);
|
||||||
|
|
||||||
|
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/teams/${teamId}/roles/`, {
|
||||||
|
id: roleId,
|
||||||
|
disassociate: true
|
||||||
|
});
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
35
__tests__/api/users.test.jsx
Normal file
35
__tests__/api/users.test.jsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import Users from '../../src/api/models/Users';
|
||||||
|
|
||||||
|
describe('UsersAPI', () => {
|
||||||
|
const userId = 1;
|
||||||
|
const roleId = 7;
|
||||||
|
const createPromise = () => Promise.resolve();
|
||||||
|
const mockHttp = ({ post: jest.fn(createPromise) });
|
||||||
|
|
||||||
|
const UsersAPI = new Users(mockHttp);
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('associate role calls post with expected params', async (done) => {
|
||||||
|
await UsersAPI.associateRole(userId, roleId);
|
||||||
|
|
||||||
|
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/users/${userId}/roles/`, { id: roleId });
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('read users calls post with expected params', async (done) => {
|
||||||
|
await UsersAPI.disassociateRole(userId, roleId);
|
||||||
|
|
||||||
|
expect(mockHttp.post).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/users/${userId}/roles/`, {
|
||||||
|
id: roleId,
|
||||||
|
disassociate: true
|
||||||
|
});
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,9 +2,12 @@ import React from 'react';
|
|||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import { mountWithContexts } from '../enzymeHelpers';
|
import { mountWithContexts } from '../enzymeHelpers';
|
||||||
import AddResourceRole, { _AddResourceRole } from '../../src/components/AddRole/AddResourceRole';
|
import AddResourceRole, { _AddResourceRole } from '../../src/components/AddRole/AddResourceRole';
|
||||||
|
import { TeamsAPI, UsersAPI } from '../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../src/api');
|
||||||
|
|
||||||
describe('<_AddResourceRole />', () => {
|
describe('<_AddResourceRole />', () => {
|
||||||
const readUsers = jest.fn().mockResolvedValue({
|
UsersAPI.read.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
count: 2,
|
count: 2,
|
||||||
results: [
|
results: [
|
||||||
@@ -13,10 +16,6 @@ describe('<_AddResourceRole />', () => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const readTeams = jest.fn();
|
|
||||||
const createUserRole = jest.fn();
|
|
||||||
const createTeamRole = jest.fn();
|
|
||||||
const api = { readUsers, readTeams, createUserRole, createTeamRole };
|
|
||||||
const roles = {
|
const roles = {
|
||||||
admin_role: {
|
admin_role: {
|
||||||
description: 'Can manage all aspects of the organization',
|
description: 'Can manage all aspects of the organization',
|
||||||
@@ -32,7 +31,6 @@ describe('<_AddResourceRole />', () => {
|
|||||||
test('initially renders without crashing', () => {
|
test('initially renders without crashing', () => {
|
||||||
shallow(
|
shallow(
|
||||||
<_AddResourceRole
|
<_AddResourceRole
|
||||||
api={api}
|
|
||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
onSave={() => {}}
|
onSave={() => {}}
|
||||||
roles={roles}
|
roles={roles}
|
||||||
@@ -43,7 +41,6 @@ describe('<_AddResourceRole />', () => {
|
|||||||
test('handleRoleCheckboxClick properly updates state', () => {
|
test('handleRoleCheckboxClick properly updates state', () => {
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(
|
||||||
<_AddResourceRole
|
<_AddResourceRole
|
||||||
api={api}
|
|
||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
onSave={() => {}}
|
onSave={() => {}}
|
||||||
roles={roles}
|
roles={roles}
|
||||||
@@ -79,7 +76,6 @@ describe('<_AddResourceRole />', () => {
|
|||||||
test('handleResourceCheckboxClick properly updates state', () => {
|
test('handleResourceCheckboxClick properly updates state', () => {
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(
|
||||||
<_AddResourceRole
|
<_AddResourceRole
|
||||||
api={api}
|
|
||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
onSave={() => {}}
|
onSave={() => {}}
|
||||||
roles={roles}
|
roles={roles}
|
||||||
@@ -115,7 +111,7 @@ describe('<_AddResourceRole />', () => {
|
|||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
onSave={() => {}}
|
onSave={() => {}}
|
||||||
roles={roles}
|
roles={roles}
|
||||||
/>, { context: { network: { api, handleHttpError: () => {} } } }
|
/>, { context: { network: { handleHttpError: () => {} } } }
|
||||||
).find('AddResourceRole');
|
).find('AddResourceRole');
|
||||||
const selectableCardWrapper = wrapper.find('SelectableCard');
|
const selectableCardWrapper = wrapper.find('SelectableCard');
|
||||||
expect(selectableCardWrapper.length).toBe(2);
|
expect(selectableCardWrapper.length).toBe(2);
|
||||||
@@ -126,35 +122,9 @@ describe('<_AddResourceRole />', () => {
|
|||||||
expect(spy).toHaveBeenCalledWith('teams');
|
expect(spy).toHaveBeenCalledWith('teams');
|
||||||
expect(wrapper.state('selectedResource')).toBe('teams');
|
expect(wrapper.state('selectedResource')).toBe('teams');
|
||||||
});
|
});
|
||||||
test('readUsers and readTeams call out to corresponding api functions', () => {
|
|
||||||
const wrapper = shallow(
|
|
||||||
<_AddResourceRole
|
|
||||||
api={api}
|
|
||||||
onClose={() => {}}
|
|
||||||
onSave={() => {}}
|
|
||||||
roles={roles}
|
|
||||||
i18n={{ _: val => val.toString() }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
wrapper.instance().readUsers({
|
|
||||||
foo: 'bar'
|
|
||||||
});
|
|
||||||
expect(readUsers).toHaveBeenCalledWith({
|
|
||||||
foo: 'bar',
|
|
||||||
is_superuser: false
|
|
||||||
});
|
|
||||||
wrapper.instance().readTeams({
|
|
||||||
foo: 'bar'
|
|
||||||
});
|
|
||||||
expect(readTeams).toHaveBeenCalledWith({
|
|
||||||
foo: 'bar'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('handleResourceSelect clears out selected lists and sets selectedResource', () => {
|
test('handleResourceSelect clears out selected lists and sets selectedResource', () => {
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(
|
||||||
<_AddResourceRole
|
<_AddResourceRole
|
||||||
api={api}
|
|
||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
onSave={() => {}}
|
onSave={() => {}}
|
||||||
roles={roles}
|
roles={roles}
|
||||||
@@ -192,7 +162,6 @@ describe('<_AddResourceRole />', () => {
|
|||||||
currentStepId: 1
|
currentStepId: 1
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handleWizardSave makes correct api calls, calls onSave when done', async () => {
|
test('handleWizardSave makes correct api calls, calls onSave when done', async () => {
|
||||||
const handleSave = jest.fn();
|
const handleSave = jest.fn();
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
@@ -200,7 +169,7 @@ describe('<_AddResourceRole />', () => {
|
|||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
roles={roles}
|
roles={roles}
|
||||||
/>, { context: { network: { api, handleHttpError: () => {} } } }
|
/>, { context: { network: { handleHttpError: () => {} } } }
|
||||||
).find('AddResourceRole');
|
).find('AddResourceRole');
|
||||||
wrapper.setState({
|
wrapper.setState({
|
||||||
selectedResource: 'users',
|
selectedResource: 'users',
|
||||||
@@ -224,7 +193,7 @@ describe('<_AddResourceRole />', () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
await wrapper.instance().handleWizardSave();
|
await wrapper.instance().handleWizardSave();
|
||||||
expect(createUserRole).toHaveBeenCalledTimes(2);
|
expect(UsersAPI.associateRole).toHaveBeenCalledTimes(2);
|
||||||
expect(handleSave).toHaveBeenCalled();
|
expect(handleSave).toHaveBeenCalled();
|
||||||
wrapper.setState({
|
wrapper.setState({
|
||||||
selectedResource: 'teams',
|
selectedResource: 'teams',
|
||||||
@@ -248,7 +217,7 @@ describe('<_AddResourceRole />', () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
await wrapper.instance().handleWizardSave();
|
await wrapper.instance().handleWizardSave();
|
||||||
expect(createTeamRole).toHaveBeenCalledTimes(2);
|
expect(TeamsAPI.associateRole).toHaveBeenCalledTimes(2);
|
||||||
expect(handleSave).toHaveBeenCalled();
|
expect(handleSave).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -70,10 +70,6 @@ const defaultContexts = {
|
|||||||
toJSON: () => '/router/',
|
toJSON: () => '/router/',
|
||||||
},
|
},
|
||||||
network: {
|
network: {
|
||||||
api: {
|
|
||||||
getConfig: () => {},
|
|
||||||
toJSON: () => '/api/',
|
|
||||||
},
|
|
||||||
handleHttpError: () => {},
|
handleHttpError: () => {},
|
||||||
},
|
},
|
||||||
dialog: {}
|
dialog: {}
|
||||||
@@ -146,7 +142,6 @@ export function mountWithContexts (node, options = {}) {
|
|||||||
history: shape({}).isRequired,
|
history: shape({}).isRequired,
|
||||||
}),
|
}),
|
||||||
network: shape({
|
network: shape({
|
||||||
api: shape({}).isRequired,
|
|
||||||
handleHttpError: func.isRequired,
|
handleHttpError: func.isRequired,
|
||||||
}),
|
}),
|
||||||
dialog: shape({
|
dialog: shape({
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { withI18n } from '@lingui/react';
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { mountWithContexts, waitForElement } from './enzymeHelpers';
|
import { mountWithContexts, waitForElement } from './enzymeHelpers';
|
||||||
import { Config } from '../src/contexts/Config';
|
import { Config } from '../src/contexts/Config';
|
||||||
import { withNetwork } from '../src/contexts/Network';
|
|
||||||
import { withRootDialog } from '../src/contexts/RootDialog';
|
import { withRootDialog } from '../src/contexts/RootDialog';
|
||||||
|
|
||||||
describe('mountWithContexts', () => {
|
describe('mountWithContexts', () => {
|
||||||
@@ -111,32 +110,6 @@ describe('mountWithContexts', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('injected Network', () => {
|
|
||||||
it('should mount and render', () => {
|
|
||||||
const Foo = () => (
|
|
||||||
<div>test</div>
|
|
||||||
);
|
|
||||||
const Bar = withNetwork(Foo);
|
|
||||||
const wrapper = mountWithContexts(<Bar />);
|
|
||||||
expect(wrapper.find('Foo')).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should mount and render with stubbed api', () => {
|
|
||||||
const network = {
|
|
||||||
api: {
|
|
||||||
getFoo: jest.fn().mockReturnValue('foo value'),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const Foo = ({ api }) => (
|
|
||||||
<div>{api.getFoo()}</div>
|
|
||||||
);
|
|
||||||
const Bar = withNetwork(Foo);
|
|
||||||
const wrapper = mountWithContexts(<Bar />, { context: { network } });
|
|
||||||
expect(network.api.getFoo).toHaveBeenCalledTimes(1);
|
|
||||||
expect(wrapper.find('div').text()).toEqual('foo value');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('injected root dialog', () => {
|
describe('injected root dialog', () => {
|
||||||
it('should mount and render', () => {
|
it('should mount and render', () => {
|
||||||
const Foo = ({ title, setRootDialogMessage }) => (
|
const Foo = ({ title, setRootDialogMessage }) => (
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import React from 'react';
|
|||||||
import { mountWithContexts } from '../enzymeHelpers';
|
import { mountWithContexts } from '../enzymeHelpers';
|
||||||
import { asyncFlush } from '../../jest.setup';
|
import { asyncFlush } from '../../jest.setup';
|
||||||
import AWXLogin from '../../src/pages/Login';
|
import AWXLogin from '../../src/pages/Login';
|
||||||
import APIClient from '../../src/api';
|
import { RootAPI } from '../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../src/api');
|
||||||
|
|
||||||
describe('<Login />', () => {
|
describe('<Login />', () => {
|
||||||
let loginWrapper;
|
let loginWrapper;
|
||||||
@@ -14,12 +16,8 @@ describe('<Login />', () => {
|
|||||||
let submitButton;
|
let submitButton;
|
||||||
let loginHeaderLogo;
|
let loginHeaderLogo;
|
||||||
|
|
||||||
const api = new APIClient({});
|
|
||||||
|
|
||||||
const mountLogin = () => {
|
const mountLogin = () => {
|
||||||
loginWrapper = mountWithContexts(<AWXLogin />, { context: { network: {
|
loginWrapper = mountWithContexts(<AWXLogin />, { context: { network: {} } });
|
||||||
api, handleHttpError: () => {}
|
|
||||||
} } });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const findChildren = () => {
|
const findChildren = () => {
|
||||||
@@ -33,6 +31,7 @@ describe('<Login />', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
loginWrapper.unmount();
|
loginWrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -98,32 +97,31 @@ describe('<Login />', () => {
|
|||||||
expect(loginWrapper.find('.pf-c-form__helper-text.pf-m-error').length).toBe(0);
|
expect(loginWrapper.find('.pf-c-form__helper-text.pf-m-error').length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('api.login not called when loading', () => {
|
test('login API not called when loading', () => {
|
||||||
api.login = jest.fn().mockImplementation(() => Promise.resolve({}));
|
|
||||||
mountLogin();
|
mountLogin();
|
||||||
findChildren();
|
findChildren();
|
||||||
expect(awxLogin.state().isLoading).toBe(false);
|
expect(awxLogin.state().isLoading).toBe(false);
|
||||||
awxLogin.setState({ isLoading: true });
|
awxLogin.setState({ isLoading: true });
|
||||||
submitButton.simulate('click');
|
submitButton.simulate('click');
|
||||||
expect(api.login).toHaveBeenCalledTimes(0);
|
expect(RootAPI.login).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('submit calls api.login successfully', async () => {
|
test('submit calls login API successfully', async () => {
|
||||||
api.login = jest.fn().mockImplementation(() => Promise.resolve({}));
|
RootAPI.login = jest.fn().mockImplementation(() => Promise.resolve({}));
|
||||||
mountLogin();
|
mountLogin();
|
||||||
findChildren();
|
findChildren();
|
||||||
expect(awxLogin.state().isLoading).toBe(false);
|
expect(awxLogin.state().isLoading).toBe(false);
|
||||||
awxLogin.setState({ username: 'unamee', password: 'pwordd' });
|
awxLogin.setState({ username: 'unamee', password: 'pwordd' });
|
||||||
submitButton.simulate('click');
|
submitButton.simulate('click');
|
||||||
expect(api.login).toHaveBeenCalledTimes(1);
|
expect(RootAPI.login).toHaveBeenCalledTimes(1);
|
||||||
expect(api.login).toHaveBeenCalledWith('unamee', 'pwordd');
|
expect(RootAPI.login).toHaveBeenCalledWith('unamee', 'pwordd');
|
||||||
expect(awxLogin.state().isLoading).toBe(true);
|
expect(awxLogin.state().isLoading).toBe(true);
|
||||||
await asyncFlush();
|
await asyncFlush();
|
||||||
expect(awxLogin.state().isLoading).toBe(false);
|
expect(awxLogin.state().isLoading).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('submit calls api.login handles 401 error', async () => {
|
test('submit calls login API and handles 401 error', async () => {
|
||||||
api.login = jest.fn().mockImplementation(() => {
|
RootAPI.login = jest.fn().mockImplementation(() => {
|
||||||
const err = new Error('401 error');
|
const err = new Error('401 error');
|
||||||
err.response = { status: 401, message: 'problem' };
|
err.response = { status: 401, message: 'problem' };
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
@@ -134,16 +132,16 @@ describe('<Login />', () => {
|
|||||||
expect(awxLogin.state().isInputValid).toBe(true);
|
expect(awxLogin.state().isInputValid).toBe(true);
|
||||||
awxLogin.setState({ username: 'unamee', password: 'pwordd' });
|
awxLogin.setState({ username: 'unamee', password: 'pwordd' });
|
||||||
submitButton.simulate('click');
|
submitButton.simulate('click');
|
||||||
expect(api.login).toHaveBeenCalledTimes(1);
|
expect(RootAPI.login).toHaveBeenCalledTimes(1);
|
||||||
expect(api.login).toHaveBeenCalledWith('unamee', 'pwordd');
|
expect(RootAPI.login).toHaveBeenCalledWith('unamee', 'pwordd');
|
||||||
expect(awxLogin.state().isLoading).toBe(true);
|
expect(awxLogin.state().isLoading).toBe(true);
|
||||||
await asyncFlush();
|
await asyncFlush();
|
||||||
expect(awxLogin.state().isInputValid).toBe(false);
|
expect(awxLogin.state().isInputValid).toBe(false);
|
||||||
expect(awxLogin.state().isLoading).toBe(false);
|
expect(awxLogin.state().isLoading).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('submit calls api.login handles non-401 error', async () => {
|
test('submit calls login API and handles non-401 error', async () => {
|
||||||
api.login = jest.fn().mockImplementation(() => {
|
RootAPI.login = jest.fn().mockImplementation(() => {
|
||||||
const err = new Error('500 error');
|
const err = new Error('500 error');
|
||||||
err.response = { status: 500, message: 'problem' };
|
err.response = { status: 500, message: 'problem' };
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
@@ -153,8 +151,8 @@ describe('<Login />', () => {
|
|||||||
expect(awxLogin.state().isLoading).toBe(false);
|
expect(awxLogin.state().isLoading).toBe(false);
|
||||||
awxLogin.setState({ username: 'unamee', password: 'pwordd' });
|
awxLogin.setState({ username: 'unamee', password: 'pwordd' });
|
||||||
submitButton.simulate('click');
|
submitButton.simulate('click');
|
||||||
expect(api.login).toHaveBeenCalledTimes(1);
|
expect(RootAPI.login).toHaveBeenCalledTimes(1);
|
||||||
expect(api.login).toHaveBeenCalledWith('unamee', 'pwordd');
|
expect(RootAPI.login).toHaveBeenCalledWith('unamee', 'pwordd');
|
||||||
expect(awxLogin.state().isLoading).toBe(true);
|
expect(awxLogin.state().isLoading).toBe(true);
|
||||||
await asyncFlush();
|
await asyncFlush();
|
||||||
expect(awxLogin.state().isLoading).toBe(false);
|
expect(awxLogin.state().isLoading).toBe(false);
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ import React from 'react';
|
|||||||
import { mountWithContexts } from '../../../enzymeHelpers';
|
import { mountWithContexts } from '../../../enzymeHelpers';
|
||||||
import { sleep } from '../../../testUtils';
|
import { sleep } from '../../../testUtils';
|
||||||
import OrganizationForm from '../../../../src/pages/Organizations/components/OrganizationForm';
|
import OrganizationForm from '../../../../src/pages/Organizations/components/OrganizationForm';
|
||||||
|
import { OrganizationsAPI } from '../../../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../../../src/api');
|
||||||
|
|
||||||
describe('<OrganizationForm />', () => {
|
describe('<OrganizationForm />', () => {
|
||||||
let network;
|
const network = {};
|
||||||
|
|
||||||
const mockData = {
|
const mockData = {
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -16,24 +19,11 @@ describe('<OrganizationForm />', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
network = {};
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should request related instance groups from api', () => {
|
test('should request related instance groups from api', () => {
|
||||||
const mockInstanceGroups = [
|
|
||||||
{ name: 'One', id: 1 },
|
|
||||||
{ name: 'Two', id: 2 }
|
|
||||||
];
|
|
||||||
network.api = {
|
|
||||||
getOrganizationInstanceGroups: jest.fn(() => (
|
|
||||||
Promise.resolve({ data: { results: mockInstanceGroups } })
|
|
||||||
))
|
|
||||||
};
|
|
||||||
mountWithContexts(
|
mountWithContexts(
|
||||||
(
|
(
|
||||||
<OrganizationForm
|
<OrganizationForm
|
||||||
@@ -46,7 +36,7 @@ describe('<OrganizationForm />', () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(network.api.getOrganizationInstanceGroups).toHaveBeenCalledTimes(1);
|
expect(OrganizationsAPI.readInstanceGroups).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('componentDidMount should set instanceGroups to state', async () => {
|
test('componentDidMount should set instanceGroups to state', async () => {
|
||||||
@@ -54,11 +44,11 @@ describe('<OrganizationForm />', () => {
|
|||||||
{ name: 'One', id: 1 },
|
{ name: 'One', id: 1 },
|
||||||
{ name: 'Two', id: 2 }
|
{ name: 'Two', id: 2 }
|
||||||
];
|
];
|
||||||
network.api = {
|
OrganizationsAPI.readInstanceGroups.mockReturnValue({
|
||||||
getOrganizationInstanceGroups: jest.fn(() => (
|
data: {
|
||||||
Promise.resolve({ data: { results: mockInstanceGroups } })
|
results: mockInstanceGroups
|
||||||
))
|
}
|
||||||
};
|
});
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
(
|
(
|
||||||
<OrganizationForm
|
<OrganizationForm
|
||||||
@@ -72,7 +62,7 @@ describe('<OrganizationForm />', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
expect(network.api.getOrganizationInstanceGroups).toHaveBeenCalled();
|
expect(OrganizationsAPI.readInstanceGroups).toHaveBeenCalled();
|
||||||
expect(wrapper.find('OrganizationForm').state().instanceGroups).toEqual(mockInstanceGroups);
|
expect(wrapper.find('OrganizationForm').state().instanceGroups).toEqual(mockInstanceGroups);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -165,20 +155,20 @@ describe('<OrganizationForm />', () => {
|
|||||||
{ name: 'One', id: 1 },
|
{ name: 'One', id: 1 },
|
||||||
{ name: 'Two', id: 2 }
|
{ name: 'Two', id: 2 }
|
||||||
];
|
];
|
||||||
network.api = {
|
OrganizationsAPI.readInstanceGroups.mockReturnValue({
|
||||||
getOrganizationInstanceGroups: jest.fn(() => (
|
data: {
|
||||||
Promise.resolve({ data: { results: mockInstanceGroups } })
|
results: mockInstanceGroups
|
||||||
))
|
}
|
||||||
};
|
});
|
||||||
const mockDataForm = {
|
const mockDataForm = {
|
||||||
name: 'Foo',
|
name: 'Foo',
|
||||||
description: 'Bar',
|
description: 'Bar',
|
||||||
custom_virtualenv: 'Fizz',
|
custom_virtualenv: 'Fizz',
|
||||||
};
|
};
|
||||||
const handleSubmit = jest.fn();
|
const handleSubmit = jest.fn();
|
||||||
network.api.updateOrganizationDetails = jest.fn().mockResolvedValue(1, mockDataForm);
|
OrganizationsAPI.update.mockResolvedValue(1, mockDataForm);
|
||||||
network.api.associateInstanceGroup = jest.fn().mockResolvedValue('done');
|
OrganizationsAPI.associateInstanceGroup.mockResolvedValue('done');
|
||||||
network.api.disassociate = jest.fn().mockResolvedValue('done');
|
OrganizationsAPI.disassociateInstanceGroup.mockResolvedValue('done');
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
(
|
(
|
||||||
<OrganizationForm
|
<OrganizationForm
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import { mountWithContexts } from '../../../../enzymeHelpers';
|
import { mountWithContexts } from '../../../../enzymeHelpers';
|
||||||
|
import { sleep } from '../../../../testUtils';
|
||||||
import Organization from '../../../../../src/pages/Organizations/screens/Organization/Organization';
|
import Organization from '../../../../../src/pages/Organizations/screens/Organization/Organization';
|
||||||
|
import { OrganizationsAPI } from '../../../../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../../../../src/api');
|
||||||
|
|
||||||
describe.only('<Organization />', () => {
|
describe.only('<Organization />', () => {
|
||||||
const me = {
|
const me = {
|
||||||
@@ -12,8 +17,41 @@ describe.only('<Organization />', () => {
|
|||||||
mountWithContexts(<Organization me={me} />);
|
mountWithContexts(<Organization me={me} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('notifications tab shown/hidden based on permissions', () => {
|
test('notifications tab shown/hidden based on permissions', async () => {
|
||||||
const wrapper = mountWithContexts(<Organization me={me} />);
|
OrganizationsAPI.readDetail.mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
name: 'foo'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
OrganizationsAPI.read.mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
results: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const history = createMemoryHistory({
|
||||||
|
initialEntries: ['/organizations/1/details'],
|
||||||
|
});
|
||||||
|
const match = { path: '/organizations/:id', url: '/organizations/1' };
|
||||||
|
const wrapper = mountWithContexts(
|
||||||
|
<Organization
|
||||||
|
me={me}
|
||||||
|
setBreadcrumb={() => {}}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
router: {
|
||||||
|
history,
|
||||||
|
route: {
|
||||||
|
location: history.location,
|
||||||
|
match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await sleep(0);
|
||||||
|
wrapper.update();
|
||||||
expect(wrapper.find('.pf-c-tabs__item').length).toBe(3);
|
expect(wrapper.find('.pf-c-tabs__item').length).toBe(3);
|
||||||
expect(wrapper.find('.pf-c-tabs__button[children="Notifications"]').length).toBe(0);
|
expect(wrapper.find('.pf-c-tabs__button[children="Notifications"]').length).toBe(0);
|
||||||
wrapper.find('Organization').setState({
|
wrapper.find('Organization').setState({
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ import { mountWithContexts } from '../../../../enzymeHelpers';
|
|||||||
import OrganizationAccess from '../../../../../src/pages/Organizations/screens/Organization/OrganizationAccess';
|
import OrganizationAccess from '../../../../../src/pages/Organizations/screens/Organization/OrganizationAccess';
|
||||||
import { sleep } from '../../../../testUtils';
|
import { sleep } from '../../../../testUtils';
|
||||||
|
|
||||||
|
import { OrganizationsAPI, TeamsAPI, UsersAPI } from '../../../../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../../../../src/api');
|
||||||
|
|
||||||
describe('<OrganizationAccess />', () => {
|
describe('<OrganizationAccess />', () => {
|
||||||
let network;
|
const network = {};
|
||||||
const organization = {
|
const organization = {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Default',
|
name: 'Default',
|
||||||
@@ -60,15 +64,11 @@ describe('<OrganizationAccess />', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
network = {
|
OrganizationsAPI.readAccessList.mockReturnValue({ data });
|
||||||
api: {
|
});
|
||||||
getOrganizationAccessList: jest.fn()
|
|
||||||
.mockReturnValue(Promise.resolve({ data })),
|
afterEach(() => {
|
||||||
disassociateTeamRole: jest.fn(),
|
jest.clearAllMocks();
|
||||||
disassociateUserRole: jest.fn(),
|
|
||||||
toJSON: () => '/api/',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('initially renders succesfully', () => {
|
test('initially renders succesfully', () => {
|
||||||
@@ -86,7 +86,7 @@ describe('<OrganizationAccess />', () => {
|
|||||||
);
|
);
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
expect(network.api.getOrganizationAccessList).toHaveBeenCalled();
|
expect(OrganizationsAPI.readAccessList).toHaveBeenCalled();
|
||||||
expect(wrapper.find('OrganizationAccess').state('isInitialized')).toBe(true);
|
expect(wrapper.find('OrganizationAccess').state('isInitialized')).toBe(true);
|
||||||
expect(wrapper.find('PaginatedDataList').prop('items')).toEqual(data.results);
|
expect(wrapper.find('PaginatedDataList').prop('items')).toEqual(data.results);
|
||||||
expect(wrapper.find('OrganizationAccessItem')).toHaveLength(2);
|
expect(wrapper.find('OrganizationAccessItem')).toHaveLength(2);
|
||||||
@@ -127,8 +127,8 @@ describe('<OrganizationAccess />', () => {
|
|||||||
const component = wrapper.find('OrganizationAccess');
|
const component = wrapper.find('OrganizationAccess');
|
||||||
expect(component.state('roleToDelete')).toBeNull();
|
expect(component.state('roleToDelete')).toBeNull();
|
||||||
expect(component.state('roleToDeleteAccessRecord')).toBeNull();
|
expect(component.state('roleToDeleteAccessRecord')).toBeNull();
|
||||||
expect(network.api.disassociateTeamRole).not.toHaveBeenCalled();
|
expect(TeamsAPI.disassociateRole).not.toHaveBeenCalled();
|
||||||
expect(network.api.disassociateUserRole).not.toHaveBeenCalled();
|
expect(UsersAPI.disassociateRole).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete user role', async () => {
|
it('should delete user role', async () => {
|
||||||
@@ -149,9 +149,9 @@ describe('<OrganizationAccess />', () => {
|
|||||||
const component = wrapper.find('OrganizationAccess');
|
const component = wrapper.find('OrganizationAccess');
|
||||||
expect(component.state('roleToDelete')).toBeNull();
|
expect(component.state('roleToDelete')).toBeNull();
|
||||||
expect(component.state('roleToDeleteAccessRecord')).toBeNull();
|
expect(component.state('roleToDeleteAccessRecord')).toBeNull();
|
||||||
expect(network.api.disassociateTeamRole).not.toHaveBeenCalled();
|
expect(TeamsAPI.disassociateRole).not.toHaveBeenCalled();
|
||||||
expect(network.api.disassociateUserRole).toHaveBeenCalledWith(1, 1);
|
expect(UsersAPI.disassociateRole).toHaveBeenCalledWith(1, 1);
|
||||||
expect(network.api.getOrganizationAccessList).toHaveBeenCalledTimes(2);
|
expect(OrganizationsAPI.readAccessList).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete team role', async () => {
|
it('should delete team role', async () => {
|
||||||
@@ -172,8 +172,8 @@ describe('<OrganizationAccess />', () => {
|
|||||||
const component = wrapper.find('OrganizationAccess');
|
const component = wrapper.find('OrganizationAccess');
|
||||||
expect(component.state('roleToDelete')).toBeNull();
|
expect(component.state('roleToDelete')).toBeNull();
|
||||||
expect(component.state('roleToDeleteAccessRecord')).toBeNull();
|
expect(component.state('roleToDeleteAccessRecord')).toBeNull();
|
||||||
expect(network.api.disassociateTeamRole).toHaveBeenCalledWith(5, 3);
|
expect(TeamsAPI.disassociateRole).toHaveBeenCalledWith(5, 3);
|
||||||
expect(network.api.disassociateUserRole).not.toHaveBeenCalled();
|
expect(UsersAPI.disassociateRole).not.toHaveBeenCalled();
|
||||||
expect(network.api.getOrganizationAccessList).toHaveBeenCalledTimes(2);
|
expect(OrganizationsAPI.readAccessList).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mountWithContexts } from '../../../../enzymeHelpers';
|
import { mountWithContexts } from '../../../../enzymeHelpers';
|
||||||
import OrganizationDetail from '../../../../../src/pages/Organizations/screens/Organization/OrganizationDetail';
|
import OrganizationDetail from '../../../../../src/pages/Organizations/screens/Organization/OrganizationDetail';
|
||||||
|
import { OrganizationsAPI } from '../../../../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../../../../src/api');
|
||||||
|
|
||||||
describe('<OrganizationDetail />', () => {
|
describe('<OrganizationDetail />', () => {
|
||||||
const mockDetails = {
|
const mockDetails = {
|
||||||
@@ -16,6 +19,10 @@ describe('<OrganizationDetail />', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
test('initially renders succesfully', () => {
|
test('initially renders succesfully', () => {
|
||||||
mountWithContexts(
|
mountWithContexts(
|
||||||
<OrganizationDetail
|
<OrganizationDetail
|
||||||
@@ -25,16 +32,15 @@ describe('<OrganizationDetail />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should request instance groups from api', () => {
|
test('should request instance groups from api', () => {
|
||||||
const getOrganizationInstanceGroups = jest.fn();
|
|
||||||
mountWithContexts(
|
mountWithContexts(
|
||||||
<OrganizationDetail
|
<OrganizationDetail
|
||||||
organization={mockDetails}
|
organization={mockDetails}
|
||||||
/>, { context: {
|
/>, { context: {
|
||||||
network: { api: { getOrganizationInstanceGroups }, handleHttpError: () => {} }
|
network: { handleHttpError: () => {} }
|
||||||
} }
|
} }
|
||||||
).find('OrganizationDetail');
|
).find('OrganizationDetail');
|
||||||
|
|
||||||
expect(getOrganizationInstanceGroups).toHaveBeenCalledTimes(1);
|
expect(OrganizationsAPI.readInstanceGroups).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should handle setting instance groups to state', async () => {
|
test('should handle setting instance groups to state', async () => {
|
||||||
@@ -42,18 +48,18 @@ describe('<OrganizationDetail />', () => {
|
|||||||
{ name: 'One', id: 1 },
|
{ name: 'One', id: 1 },
|
||||||
{ name: 'Two', id: 2 }
|
{ name: 'Two', id: 2 }
|
||||||
];
|
];
|
||||||
const getOrganizationInstanceGroups = jest.fn(() => (
|
OrganizationsAPI.readInstanceGroups.mockResolvedValue({
|
||||||
Promise.resolve({ data: { results: mockInstanceGroups } })
|
data: { results: mockInstanceGroups }
|
||||||
));
|
});
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<OrganizationDetail
|
<OrganizationDetail
|
||||||
organization={mockDetails}
|
organization={mockDetails}
|
||||||
/>, { context: {
|
/>, { context: {
|
||||||
network: { api: { getOrganizationInstanceGroups }, handleHttpError: () => {} }
|
network: { handleHttpError: () => {} }
|
||||||
} }
|
} }
|
||||||
).find('OrganizationDetail');
|
).find('OrganizationDetail');
|
||||||
|
|
||||||
await getOrganizationInstanceGroups();
|
await OrganizationsAPI.readInstanceGroups();
|
||||||
expect(wrapper.state().instanceGroups).toEqual(mockInstanceGroups);
|
expect(wrapper.state().instanceGroups).toEqual(mockInstanceGroups);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import { mountWithContexts } from '../../../../enzymeHelpers';
|
|||||||
|
|
||||||
import OrganizationEdit from '../../../../../src/pages/Organizations/screens/Organization/OrganizationEdit';
|
import OrganizationEdit from '../../../../../src/pages/Organizations/screens/Organization/OrganizationEdit';
|
||||||
|
|
||||||
|
import { OrganizationsAPI } from '../../../../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../../../../src/api');
|
||||||
|
|
||||||
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
describe('<OrganizationEdit />', () => {
|
describe('<OrganizationEdit />', () => {
|
||||||
@@ -44,10 +48,7 @@ describe('<OrganizationEdit />', () => {
|
|||||||
};
|
};
|
||||||
wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [], []);
|
wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [], []);
|
||||||
|
|
||||||
expect(api.updateOrganizationDetails).toHaveBeenCalledWith(
|
expect(OrganizationsAPI.update).toHaveBeenCalledWith(1, updatedOrgData);
|
||||||
1,
|
|
||||||
updatedOrgData
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handleSubmit associates and disassociates instance groups', async () => {
|
test('handleSubmit associates and disassociates instance groups', async () => {
|
||||||
@@ -68,18 +69,9 @@ describe('<OrganizationEdit />', () => {
|
|||||||
wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [3, 4], [2]);
|
wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [3, 4], [2]);
|
||||||
await sleep(1);
|
await sleep(1);
|
||||||
|
|
||||||
expect(api.associateInstanceGroup).toHaveBeenCalledWith(
|
expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(1, 3);
|
||||||
'/api/v2/organizations/1/instance_groups',
|
expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(1, 4);
|
||||||
3
|
expect(OrganizationsAPI.disassociateInstanceGroup).toHaveBeenCalledWith(1, 2);
|
||||||
);
|
|
||||||
expect(api.associateInstanceGroup).toHaveBeenCalledWith(
|
|
||||||
'/api/v2/organizations/1/instance_groups',
|
|
||||||
4
|
|
||||||
);
|
|
||||||
expect(api.disassociate).toHaveBeenCalledWith(
|
|
||||||
'/api/v2/organizations/1/instance_groups',
|
|
||||||
2
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should navigate to organization detail when cancel is clicked', () => {
|
test('should navigate to organization detail when cancel is clicked', () => {
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ import React from 'react';
|
|||||||
import { mountWithContexts } from '../../../../enzymeHelpers';
|
import { mountWithContexts } from '../../../../enzymeHelpers';
|
||||||
import OrganizationNotifications from '../../../../../src/pages/Organizations/screens/Organization/OrganizationNotifications';
|
import OrganizationNotifications from '../../../../../src/pages/Organizations/screens/Organization/OrganizationNotifications';
|
||||||
import { sleep } from '../../../../testUtils';
|
import { sleep } from '../../../../testUtils';
|
||||||
|
import { OrganizationsAPI } from '../../../../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../../../../src/api');
|
||||||
|
|
||||||
describe('<OrganizationNotifications />', () => {
|
describe('<OrganizationNotifications />', () => {
|
||||||
let data;
|
let data;
|
||||||
let network;
|
const network = {};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
data = {
|
data = {
|
||||||
@@ -22,23 +25,13 @@ describe('<OrganizationNotifications />', () => {
|
|||||||
notification_type: 'email',
|
notification_type: 'email',
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
network = {
|
OrganizationsAPI.readNotificationTemplates.mockReturnValue({ data });
|
||||||
api: {
|
OrganizationsAPI.readNotificationTemplatesSuccess.mockReturnValue({
|
||||||
getOrganizationNotifications: jest.fn()
|
data: { results: [{ id: 1 }] },
|
||||||
.mockReturnValue(Promise.resolve({ data })),
|
});
|
||||||
getOrganizationNotificationSuccess: jest.fn()
|
OrganizationsAPI.readNotificationTemplatesError.mockReturnValue({
|
||||||
.mockReturnValue(Promise.resolve({
|
data: { results: [{ id: 2 }] },
|
||||||
data: { results: [{ id: 1 }] },
|
});
|
||||||
})),
|
|
||||||
getOrganizationNotificationError: jest.fn()
|
|
||||||
.mockReturnValue(Promise.resolve({
|
|
||||||
data: { results: [{ id: 2 }] },
|
|
||||||
})),
|
|
||||||
createOrganizationNotificationSuccess: jest.fn(),
|
|
||||||
createOrganizationNotificationError: jest.fn(),
|
|
||||||
toJSON: () => '/api/',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -65,7 +58,7 @@ describe('<OrganizationNotifications />', () => {
|
|||||||
await sleep(0);
|
await sleep(0);
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
expect(network.api.getOrganizationNotifications).toHaveBeenCalled();
|
expect(OrganizationsAPI.readNotificationTemplates).toHaveBeenCalled();
|
||||||
expect(wrapper.find('OrganizationNotifications').state('notifications'))
|
expect(wrapper.find('OrganizationNotifications').state('notifications'))
|
||||||
.toEqual(data.results);
|
.toEqual(data.results);
|
||||||
const items = wrapper.find('NotificationListItem');
|
const items = wrapper.find('NotificationListItem');
|
||||||
@@ -91,7 +84,7 @@ describe('<OrganizationNotifications />', () => {
|
|||||||
).toEqual([1]);
|
).toEqual([1]);
|
||||||
const items = wrapper.find('NotificationListItem');
|
const items = wrapper.find('NotificationListItem');
|
||||||
items.at(1).find('Switch').at(0).prop('onChange')();
|
items.at(1).find('Switch').at(0).prop('onChange')();
|
||||||
expect(network.api.createOrganizationNotificationSuccess).toHaveBeenCalled();
|
expect(OrganizationsAPI.associateNotificationTemplatesSuccess).toHaveBeenCalled();
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
expect(
|
expect(
|
||||||
@@ -114,7 +107,7 @@ describe('<OrganizationNotifications />', () => {
|
|||||||
).toEqual([2]);
|
).toEqual([2]);
|
||||||
const items = wrapper.find('NotificationListItem');
|
const items = wrapper.find('NotificationListItem');
|
||||||
items.at(0).find('Switch').at(1).prop('onChange')();
|
items.at(0).find('Switch').at(1).prop('onChange')();
|
||||||
expect(network.api.createOrganizationNotificationError).toHaveBeenCalled();
|
expect(OrganizationsAPI.associateNotificationTemplatesError).toHaveBeenCalled();
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
expect(
|
expect(
|
||||||
@@ -137,7 +130,7 @@ describe('<OrganizationNotifications />', () => {
|
|||||||
).toEqual([1]);
|
).toEqual([1]);
|
||||||
const items = wrapper.find('NotificationListItem');
|
const items = wrapper.find('NotificationListItem');
|
||||||
items.at(0).find('Switch').at(0).prop('onChange')();
|
items.at(0).find('Switch').at(0).prop('onChange')();
|
||||||
expect(network.api.createOrganizationNotificationSuccess).toHaveBeenCalled();
|
expect(OrganizationsAPI.disassociateNotificationTemplatesSuccess).toHaveBeenCalled();
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
expect(
|
expect(
|
||||||
@@ -160,7 +153,7 @@ describe('<OrganizationNotifications />', () => {
|
|||||||
).toEqual([2]);
|
).toEqual([2]);
|
||||||
const items = wrapper.find('NotificationListItem');
|
const items = wrapper.find('NotificationListItem');
|
||||||
items.at(1).find('Switch').at(1).prop('onChange')();
|
items.at(1).find('Switch').at(1).prop('onChange')();
|
||||||
expect(network.api.createOrganizationNotificationError).toHaveBeenCalled();
|
expect(OrganizationsAPI.disassociateNotificationTemplatesError).toHaveBeenCalled();
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import { shallow } from 'enzyme';
|
|||||||
import { mountWithContexts } from '../../../../enzymeHelpers';
|
import { mountWithContexts } from '../../../../enzymeHelpers';
|
||||||
import { sleep } from '../../../../testUtils';
|
import { sleep } from '../../../../testUtils';
|
||||||
import OrganizationTeams, { _OrganizationTeams } from '../../../../../src/pages/Organizations/screens/Organization/OrganizationTeams';
|
import OrganizationTeams, { _OrganizationTeams } from '../../../../../src/pages/Organizations/screens/Organization/OrganizationTeams';
|
||||||
|
import { OrganizationsAPI } from '../../../../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../../../../src/api');
|
||||||
|
|
||||||
const listData = {
|
const listData = {
|
||||||
data: {
|
data: {
|
||||||
@@ -17,6 +20,14 @@ const listData = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
OrganizationsAPI.readTeams.mockResolvedValue(listData);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
describe('<OrganizationTeams />', () => {
|
describe('<OrganizationTeams />', () => {
|
||||||
test('renders succesfully', () => {
|
test('renders succesfully', () => {
|
||||||
shallow(
|
shallow(
|
||||||
@@ -24,25 +35,21 @@ describe('<OrganizationTeams />', () => {
|
|||||||
id={1}
|
id={1}
|
||||||
searchString=""
|
searchString=""
|
||||||
location={{ search: '', pathname: '/organizations/1/teams' }}
|
location={{ search: '', pathname: '/organizations/1/teams' }}
|
||||||
api={{
|
|
||||||
readOrganizationTeamsList: jest.fn(),
|
|
||||||
}}
|
|
||||||
handleHttpError={() => {}}
|
handleHttpError={() => {}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should load teams on mount', () => {
|
test('should load teams on mount', () => {
|
||||||
const readOrganizationTeamsList = jest.fn(() => Promise.resolve(listData));
|
|
||||||
mountWithContexts(
|
mountWithContexts(
|
||||||
<OrganizationTeams
|
<OrganizationTeams
|
||||||
id={1}
|
id={1}
|
||||||
searchString=""
|
searchString=""
|
||||||
/>, { context: {
|
/>, { context: {
|
||||||
network: { api: { readOrganizationTeamsList }, handleHttpError: () => {} } }
|
network: {} }
|
||||||
}
|
}
|
||||||
).find('OrganizationTeams');
|
).find('OrganizationTeams');
|
||||||
expect(readOrganizationTeamsList).toHaveBeenCalledWith(1, {
|
expect(OrganizationsAPI.readTeams).toHaveBeenCalledWith(1, {
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 5,
|
page_size: 5,
|
||||||
order_by: 'name',
|
order_by: 'name',
|
||||||
@@ -50,13 +57,12 @@ describe('<OrganizationTeams />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should pass fetched teams to PaginatedDatalist', async () => {
|
test('should pass fetched teams to PaginatedDatalist', async () => {
|
||||||
const readOrganizationTeamsList = jest.fn(() => Promise.resolve(listData));
|
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<OrganizationTeams
|
<OrganizationTeams
|
||||||
id={1}
|
id={1}
|
||||||
searchString=""
|
searchString=""
|
||||||
/>, { context: {
|
/>, { context: {
|
||||||
network: { api: { readOrganizationTeamsList }, handleHttpError: () => {} } }
|
network: { handleHttpError: () => {} } }
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
exports[`<OrganizationAccess /> initially renders succesfully 1`] = `
|
exports[`<OrganizationAccess /> initially renders succesfully 1`] = `
|
||||||
<OrganizationAccess
|
<OrganizationAccess
|
||||||
api={"/api/"}
|
|
||||||
handleHttpError={[Function]}
|
handleHttpError={[Function]}
|
||||||
history={"/history/"}
|
history={"/history/"}
|
||||||
i18n={"/i18n/"}
|
i18n={"/i18n/"}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
|
|||||||
<Provider
|
<Provider
|
||||||
value={
|
value={
|
||||||
Object {
|
Object {
|
||||||
"api": "/api/",
|
|
||||||
"handleHttpError": [Function],
|
"handleHttpError": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,7 +17,6 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
|
|||||||
value={"/config/"}
|
value={"/config/"}
|
||||||
>
|
>
|
||||||
<Provider
|
<Provider
|
||||||
api={"/api/"}
|
|
||||||
handleHttpError={[Function]}
|
handleHttpError={[Function]}
|
||||||
i18n={"/i18n/"}
|
i18n={"/i18n/"}
|
||||||
value={"/config/"}
|
value={"/config/"}
|
||||||
@@ -28,14 +26,12 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
|
|||||||
id={1}
|
id={1}
|
||||||
>
|
>
|
||||||
<withRouter(OrganizationNotifications)
|
<withRouter(OrganizationNotifications)
|
||||||
api={"/api/"}
|
|
||||||
canToggleNotifications={true}
|
canToggleNotifications={true}
|
||||||
handleHttpError={[Function]}
|
handleHttpError={[Function]}
|
||||||
id={1}
|
id={1}
|
||||||
>
|
>
|
||||||
<Route>
|
<Route>
|
||||||
<OrganizationNotifications
|
<OrganizationNotifications
|
||||||
api={"/api/"}
|
|
||||||
canToggleNotifications={true}
|
canToggleNotifications={true}
|
||||||
handleHttpError={[Function]}
|
handleHttpError={[Function]}
|
||||||
history={"/history/"}
|
history={"/history/"}
|
||||||
|
|||||||
@@ -3,23 +3,17 @@ import React from 'react';
|
|||||||
import { mountWithContexts } from '../../../enzymeHelpers';
|
import { mountWithContexts } from '../../../enzymeHelpers';
|
||||||
|
|
||||||
import OrganizationAdd from '../../../../src/pages/Organizations/screens/OrganizationAdd';
|
import OrganizationAdd from '../../../../src/pages/Organizations/screens/OrganizationAdd';
|
||||||
|
import { OrganizationsAPI } from '../../../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../../../src/api');
|
||||||
|
|
||||||
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
describe('<OrganizationAdd />', () => {
|
describe('<OrganizationAdd />', () => {
|
||||||
let api;
|
|
||||||
let networkProviderValue;
|
let networkProviderValue;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
api = {
|
|
||||||
getInstanceGroups: jest.fn(),
|
|
||||||
createOrganization: jest.fn(),
|
|
||||||
associateInstanceGroup: jest.fn(),
|
|
||||||
disassociate: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
networkProviderValue = {
|
networkProviderValue = {
|
||||||
api,
|
|
||||||
handleHttpError: () => {}
|
handleHttpError: () => {}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -34,7 +28,7 @@ describe('<OrganizationAdd />', () => {
|
|||||||
custom_virtualenv: 'Buzz',
|
custom_virtualenv: 'Buzz',
|
||||||
};
|
};
|
||||||
wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [], []);
|
wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [], []);
|
||||||
expect(api.createOrganization).toHaveBeenCalledWith(updatedOrgData);
|
expect(OrganizationsAPI.create).toHaveBeenCalledWith(updatedOrgData);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should navigate to organizations list when cancel is clicked', () => {
|
test('should navigate to organizations list when cancel is clicked', () => {
|
||||||
@@ -70,7 +64,7 @@ describe('<OrganizationAdd />', () => {
|
|||||||
description: 'new description',
|
description: 'new description',
|
||||||
custom_virtualenv: 'Buzz',
|
custom_virtualenv: 'Buzz',
|
||||||
};
|
};
|
||||||
api.createOrganization.mockReturnValueOnce({
|
OrganizationsAPI.create.mockReturnValueOnce({
|
||||||
data: {
|
data: {
|
||||||
id: 5,
|
id: 5,
|
||||||
related: {
|
related: {
|
||||||
@@ -96,7 +90,7 @@ describe('<OrganizationAdd />', () => {
|
|||||||
description: 'new description',
|
description: 'new description',
|
||||||
custom_virtualenv: 'Buzz',
|
custom_virtualenv: 'Buzz',
|
||||||
};
|
};
|
||||||
api.createOrganization.mockReturnValueOnce({
|
OrganizationsAPI.create.mockReturnValueOnce({
|
||||||
data: {
|
data: {
|
||||||
id: 5,
|
id: 5,
|
||||||
related: {
|
related: {
|
||||||
@@ -107,8 +101,8 @@ describe('<OrganizationAdd />', () => {
|
|||||||
});
|
});
|
||||||
wrapper.find('OrganizationForm').prop('handleSubmit')(orgData, [3], []);
|
wrapper.find('OrganizationForm').prop('handleSubmit')(orgData, [3], []);
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
expect(api.associateInstanceGroup)
|
expect(OrganizationsAPI.associateInstanceGroup)
|
||||||
.toHaveBeenCalledWith('/api/v2/organizations/5/instance_groups', 3);
|
.toHaveBeenCalledWith(5, 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('AnsibleSelect component renders if there are virtual environments', () => {
|
test('AnsibleSelect component renders if there are virtual environments', () => {
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import React from 'react';
|
|||||||
import { createMemoryHistory } from 'history';
|
import { createMemoryHistory } from 'history';
|
||||||
import { mountWithContexts } from '../../../enzymeHelpers';
|
import { mountWithContexts } from '../../../enzymeHelpers';
|
||||||
import OrganizationsList, { _OrganizationsList } from '../../../../src/pages/Organizations/screens/OrganizationsList';
|
import OrganizationsList, { _OrganizationsList } from '../../../../src/pages/Organizations/screens/OrganizationsList';
|
||||||
|
import { OrganizationsAPI } from '../../../../src/api';
|
||||||
|
|
||||||
|
jest.mock('../../../../src/api');
|
||||||
|
|
||||||
const mockAPIOrgsList = {
|
const mockAPIOrgsList = {
|
||||||
data: {
|
data: {
|
||||||
@@ -124,7 +127,7 @@ describe('<OrganizationsList />', () => {
|
|||||||
selected: mockAPIOrgsList.data.results
|
selected: mockAPIOrgsList.data.results
|
||||||
});
|
});
|
||||||
wrapper.find('ToolbarDeleteButton').prop('onDelete')();
|
wrapper.find('ToolbarDeleteButton').prop('onDelete')();
|
||||||
expect(api.destroyOrganization).toHaveBeenCalledTimes(component.state('selected').length);
|
expect(OrganizationsAPI.destroy).toHaveBeenCalledTimes(component.state('selected').length);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('call fetchOrganizations after org(s) have been deleted', () => {
|
test('call fetchOrganizations after org(s) have been deleted', () => {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import styled from 'styled-components';
|
|||||||
import { RootDialog } from './contexts/RootDialog';
|
import { RootDialog } from './contexts/RootDialog';
|
||||||
import { withNetwork } from './contexts/Network';
|
import { withNetwork } from './contexts/Network';
|
||||||
import { Config } from './contexts/Config';
|
import { Config } from './contexts/Config';
|
||||||
|
import { RootAPI } from './api';
|
||||||
|
|
||||||
import AlertModal from './components/AlertModal';
|
import AlertModal from './components/AlertModal';
|
||||||
import About from './components/About';
|
import About from './components/About';
|
||||||
@@ -56,9 +57,9 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onLogout () {
|
async onLogout () {
|
||||||
const { api, handleHttpError } = this.props;
|
const { handleHttpError } = this.props;
|
||||||
try {
|
try {
|
||||||
await api.logout();
|
await RootAPI.logout();
|
||||||
window.location.replace('/#/login');
|
window.location.replace('/#/login');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleHttpError(err);
|
handleHttpError(err);
|
||||||
|
|||||||
182
src/api.js
182
src/api.js
@@ -1,182 +0,0 @@
|
|||||||
const API_ROOT = '/api/';
|
|
||||||
const API_LOGIN = `${API_ROOT}login/`;
|
|
||||||
const API_LOGOUT = `${API_ROOT}logout/`;
|
|
||||||
const API_V2 = `${API_ROOT}v2/`;
|
|
||||||
const API_CONFIG = `${API_V2}config/`;
|
|
||||||
const API_ME = `${API_V2}me/`;
|
|
||||||
const API_ORGANIZATIONS = `${API_V2}organizations/`;
|
|
||||||
const API_INSTANCE_GROUPS = `${API_V2}instance_groups/`;
|
|
||||||
const API_USERS = `${API_V2}users/`;
|
|
||||||
const API_TEAMS = `${API_V2}teams/`;
|
|
||||||
|
|
||||||
const LOGIN_CONTENT_TYPE = 'application/x-www-form-urlencoded';
|
|
||||||
|
|
||||||
class APIClient {
|
|
||||||
static getCookie () {
|
|
||||||
return document.cookie;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor (httpAdapter) {
|
|
||||||
this.http = httpAdapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
isAuthenticated () {
|
|
||||||
const cookie = this.constructor.getCookie();
|
|
||||||
const parsed = (`; ${cookie}`).split('; userLoggedIn=');
|
|
||||||
|
|
||||||
let authenticated = false;
|
|
||||||
|
|
||||||
if (parsed.length === 2) {
|
|
||||||
authenticated = parsed.pop().split(';').shift() === 'true';
|
|
||||||
}
|
|
||||||
|
|
||||||
return authenticated;
|
|
||||||
}
|
|
||||||
|
|
||||||
async login (username, password, redirect = API_CONFIG) {
|
|
||||||
const un = encodeURIComponent(username);
|
|
||||||
const pw = encodeURIComponent(password);
|
|
||||||
const next = encodeURIComponent(redirect);
|
|
||||||
|
|
||||||
const data = `username=${un}&password=${pw}&next=${next}`;
|
|
||||||
const headers = { 'Content-Type': LOGIN_CONTENT_TYPE };
|
|
||||||
|
|
||||||
await this.http.get(API_LOGIN, { headers });
|
|
||||||
const response = await this.http.post(API_LOGIN, data, { headers });
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
logout () {
|
|
||||||
return this.http.get(API_LOGOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
getRoot () {
|
|
||||||
return this.http.get(API_ROOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
getConfig () {
|
|
||||||
return this.http.get(API_CONFIG);
|
|
||||||
}
|
|
||||||
|
|
||||||
getMe () {
|
|
||||||
return this.http.get(API_ME);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyOrganization (id) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/`;
|
|
||||||
return (this.http.delete(endpoint));
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrganizations (params = {}) {
|
|
||||||
return this.http.get(API_ORGANIZATIONS, { params });
|
|
||||||
}
|
|
||||||
|
|
||||||
createOrganization (data) {
|
|
||||||
return this.http.post(API_ORGANIZATIONS, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
optionsOrganizations () {
|
|
||||||
return this.http.options(API_ORGANIZATIONS);
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrganizationAccessList (id, params = {}) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/access_list/`;
|
|
||||||
|
|
||||||
return this.http.get(endpoint, { params });
|
|
||||||
}
|
|
||||||
|
|
||||||
readOrganizationTeamsList (id, params = {}) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/teams/`;
|
|
||||||
|
|
||||||
return this.http.get(endpoint, { params });
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrganizationDetails (id) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/`;
|
|
||||||
|
|
||||||
return this.http.get(endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateOrganizationDetails (id, data) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/`;
|
|
||||||
|
|
||||||
return this.http.patch(endpoint, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrganizationInstanceGroups (id, params = {}) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/instance_groups/`;
|
|
||||||
|
|
||||||
return this.http.get(endpoint, { params });
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrganizationNotifications (id, params = {}) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates/`;
|
|
||||||
|
|
||||||
return this.http.get(endpoint, { params });
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrganizationNotificationSuccess (id, params = {}) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates_success/`;
|
|
||||||
|
|
||||||
return this.http.get(endpoint, { params });
|
|
||||||
}
|
|
||||||
|
|
||||||
getOrganizationNotificationError (id, params = {}) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates_error/`;
|
|
||||||
|
|
||||||
return this.http.get(endpoint, { params });
|
|
||||||
}
|
|
||||||
|
|
||||||
createOrganizationNotificationSuccess (id, data) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates_success/`;
|
|
||||||
|
|
||||||
return this.http.post(endpoint, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
createOrganizationNotificationError (id, data) {
|
|
||||||
const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates_error/`;
|
|
||||||
|
|
||||||
return this.http.post(endpoint, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
getInstanceGroups (params) {
|
|
||||||
return this.http.get(API_INSTANCE_GROUPS, { params });
|
|
||||||
}
|
|
||||||
|
|
||||||
associateInstanceGroup (url, id) {
|
|
||||||
return this.http.post(url, { id });
|
|
||||||
}
|
|
||||||
|
|
||||||
disassociateTeamRole (teamId, roleId) {
|
|
||||||
const url = `/api/v2/teams/${teamId}/roles/`;
|
|
||||||
return this.disassociate(url, roleId);
|
|
||||||
}
|
|
||||||
|
|
||||||
disassociateUserRole (accessRecordId, roleId) {
|
|
||||||
const url = `/api/v2/users/${accessRecordId}/roles/`;
|
|
||||||
return this.disassociate(url, roleId);
|
|
||||||
}
|
|
||||||
|
|
||||||
disassociate (url, id) {
|
|
||||||
return this.http.post(url, { id, disassociate: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
readUsers (params) {
|
|
||||||
return this.http.get(API_USERS, { params });
|
|
||||||
}
|
|
||||||
|
|
||||||
readTeams (params) {
|
|
||||||
return this.http.get(API_TEAMS, { params });
|
|
||||||
}
|
|
||||||
|
|
||||||
createUserRole (userId, roleId) {
|
|
||||||
return this.http.post(`${API_USERS}${userId}/roles/`, { id: roleId });
|
|
||||||
}
|
|
||||||
|
|
||||||
createTeamRole (teamId, roleId) {
|
|
||||||
return this.http.post(`${API_TEAMS}${teamId}/roles/`, { id: roleId });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default APIClient;
|
|
||||||
43
src/api/Base.js
Normal file
43
src/api/Base.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const defaultHttp = axios.create({
|
||||||
|
xsrfCookieName: 'csrftoken',
|
||||||
|
xsrfHeaderName: 'X-CSRFToken'
|
||||||
|
});
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
constructor (http = defaultHttp, baseURL) {
|
||||||
|
this.http = http;
|
||||||
|
this.baseUrl = baseURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
create (data) {
|
||||||
|
return this.http.post(this.baseUrl, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy (id) {
|
||||||
|
return this.http.delete(`${this.baseUrl}${id}/`);
|
||||||
|
}
|
||||||
|
|
||||||
|
read (params = {}) {
|
||||||
|
return this.http.get(this.baseUrl, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
readDetail (id) {
|
||||||
|
return this.http.get(`${this.baseUrl}${id}/`);
|
||||||
|
}
|
||||||
|
|
||||||
|
readOptions () {
|
||||||
|
return this.http.options(this.baseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
replace (id, data) {
|
||||||
|
return this.http.put(`${this.baseUrl}${id}/`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
update (id, data) {
|
||||||
|
return this.http.patch(`${this.baseUrl}${id}/`, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Base;
|
||||||
25
src/api/index.js
Normal file
25
src/api/index.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import Config from './models/Config';
|
||||||
|
import InstanceGroups from './models/InstanceGroups';
|
||||||
|
import Me from './models/Me';
|
||||||
|
import Organizations from './models/Organizations';
|
||||||
|
import Root from './models/Root';
|
||||||
|
import Teams from './models/Teams';
|
||||||
|
import Users from './models/Users';
|
||||||
|
|
||||||
|
const ConfigAPI = new Config();
|
||||||
|
const InstanceGroupsAPI = new InstanceGroups();
|
||||||
|
const MeAPI = new Me();
|
||||||
|
const OrganizationsAPI = new Organizations();
|
||||||
|
const RootAPI = new Root();
|
||||||
|
const TeamsAPI = new Teams();
|
||||||
|
const UsersAPI = new Users();
|
||||||
|
|
||||||
|
export {
|
||||||
|
ConfigAPI,
|
||||||
|
InstanceGroupsAPI,
|
||||||
|
MeAPI,
|
||||||
|
OrganizationsAPI,
|
||||||
|
RootAPI,
|
||||||
|
TeamsAPI,
|
||||||
|
UsersAPI
|
||||||
|
};
|
||||||
15
src/api/mixins/InstanceGroups.mixin.js
Normal file
15
src/api/mixins/InstanceGroups.mixin.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
const InstanceGroupsMixin = (parent) => class extends parent {
|
||||||
|
readInstanceGroups (resourceId, params = {}) {
|
||||||
|
return this.http.get(`${this.baseUrl}${resourceId}/instance_groups/`, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
associateInstanceGroup (resourceId, instanceGroupId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${resourceId}/instance_groups/`, { id: instanceGroupId });
|
||||||
|
}
|
||||||
|
|
||||||
|
disassociateInstanceGroup (resourceId, instanceGroupId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${resourceId}/instance_groups/`, { id: instanceGroupId, disassociate: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InstanceGroupsMixin;
|
||||||
31
src/api/mixins/Notifications.mixin.js
Normal file
31
src/api/mixins/Notifications.mixin.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const NotificationsMixin = (parent) => class extends parent {
|
||||||
|
readNotificationTemplates (id, params = {}) {
|
||||||
|
return this.http.get(`${this.baseUrl}${id}/notification_templates/`, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
readNotificationTemplatesSuccess (id, params = {}) {
|
||||||
|
return this.http.get(`${this.baseUrl}${id}/notification_templates_success/`, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
readNotificationTemplatesError (id, params = {}) {
|
||||||
|
return this.http.get(`${this.baseUrl}${id}/notification_templates_error/`, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
associateNotificationTemplatesSuccess (resourceId, notificationId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${resourceId}/notification_templates_success/`, { id: notificationId });
|
||||||
|
}
|
||||||
|
|
||||||
|
disassociateNotificationTemplatesSuccess (resourceId, notificationId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${resourceId}/notification_templates_success/`, { id: notificationId, disassociate: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
associateNotificationTemplatesError (resourceId, notificationId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${resourceId}/notification_templates_error/`, { id: notificationId });
|
||||||
|
}
|
||||||
|
|
||||||
|
disassociateNotificationTemplatesError (resourceId, notificationId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${resourceId}/notification_templates_error/`, { id: notificationId, disassociate: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotificationsMixin;
|
||||||
10
src/api/models/Config.js
Normal file
10
src/api/models/Config.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class Config extends Base {
|
||||||
|
constructor (http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/config/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Config;
|
||||||
10
src/api/models/InstanceGroups.js
Normal file
10
src/api/models/InstanceGroups.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class InstanceGroups extends Base {
|
||||||
|
constructor (http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/instance_groups/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InstanceGroups;
|
||||||
10
src/api/models/Me.js
Normal file
10
src/api/models/Me.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class Me extends Base {
|
||||||
|
constructor (http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/me/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Me;
|
||||||
20
src/api/models/Organizations.js
Normal file
20
src/api/models/Organizations.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
import NotificationsMixin from '../mixins/Notifications.mixin';
|
||||||
|
import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin';
|
||||||
|
|
||||||
|
class Organizations extends InstanceGroupsMixin(NotificationsMixin(Base)) {
|
||||||
|
constructor (http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/organizations/';
|
||||||
|
}
|
||||||
|
|
||||||
|
readAccessList (id, params = {}) {
|
||||||
|
return this.http.get(`${this.baseUrl}${id}/access_list/`, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
readTeams (id, params = {}) {
|
||||||
|
return this.http.get(`${this.baseUrl}${id}/teams/`, { params });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Organizations;
|
||||||
30
src/api/models/Root.js
Normal file
30
src/api/models/Root.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class Root extends Base {
|
||||||
|
constructor (http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/';
|
||||||
|
this.redirectURL = '/api/v2/config/';
|
||||||
|
}
|
||||||
|
|
||||||
|
async login (username, password, redirect = this.redirectURL) {
|
||||||
|
const loginUrl = `${this.baseUrl}login/`;
|
||||||
|
const un = encodeURIComponent(username);
|
||||||
|
const pw = encodeURIComponent(password);
|
||||||
|
const next = encodeURIComponent(redirect);
|
||||||
|
|
||||||
|
const data = `username=${un}&password=${pw}&next=${next}`;
|
||||||
|
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||||
|
|
||||||
|
await this.http.get(loginUrl, { headers });
|
||||||
|
const response = await this.http.post(loginUrl, data, { headers });
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
logout () {
|
||||||
|
return this.http.get(`${this.baseUrl}logout/`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Root;
|
||||||
18
src/api/models/Teams.js
Normal file
18
src/api/models/Teams.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class Teams extends Base {
|
||||||
|
constructor (http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/teams/';
|
||||||
|
}
|
||||||
|
|
||||||
|
associateRole (teamId, roleId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${teamId}/roles/`, { id: roleId });
|
||||||
|
}
|
||||||
|
|
||||||
|
disassociateRole (teamId, roleId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${teamId}/roles/`, { id: roleId, disassociate: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Teams;
|
||||||
18
src/api/models/Users.js
Normal file
18
src/api/models/Users.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class Users extends Base {
|
||||||
|
constructor (http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/users/';
|
||||||
|
}
|
||||||
|
|
||||||
|
associateRole (userId, roleId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${userId}/roles/`, { id: roleId });
|
||||||
|
}
|
||||||
|
|
||||||
|
disassociateRole (userId, roleId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${userId}/roles/`, { id: roleId, disassociate: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Users;
|
||||||
@@ -7,6 +7,13 @@ import { withNetwork } from '../../contexts/Network';
|
|||||||
import SelectResourceStep from './SelectResourceStep';
|
import SelectResourceStep from './SelectResourceStep';
|
||||||
import SelectRoleStep from './SelectRoleStep';
|
import SelectRoleStep from './SelectRoleStep';
|
||||||
import SelectableCard from './SelectableCard';
|
import SelectableCard from './SelectableCard';
|
||||||
|
import { TeamsAPI, UsersAPI } from '../../api';
|
||||||
|
|
||||||
|
const readUsers = async (queryParams) => UsersAPI.read(
|
||||||
|
Object.assign(queryParams, { is_superuser: false })
|
||||||
|
);
|
||||||
|
|
||||||
|
const readTeams = async (queryParams) => TeamsAPI.read(queryParams);
|
||||||
|
|
||||||
class AddResourceRole extends React.Component {
|
class AddResourceRole extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -24,8 +31,6 @@ class AddResourceRole extends React.Component {
|
|||||||
this.handleRoleCheckboxClick = this.handleRoleCheckboxClick.bind(this);
|
this.handleRoleCheckboxClick = this.handleRoleCheckboxClick.bind(this);
|
||||||
this.handleWizardNext = this.handleWizardNext.bind(this);
|
this.handleWizardNext = this.handleWizardNext.bind(this);
|
||||||
this.handleWizardSave = this.handleWizardSave.bind(this);
|
this.handleWizardSave = this.handleWizardSave.bind(this);
|
||||||
this.readTeams = this.readTeams.bind(this);
|
|
||||||
this.readUsers = this.readUsers.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResourceCheckboxClick (user) {
|
handleResourceCheckboxClick (user) {
|
||||||
@@ -76,8 +81,7 @@ class AddResourceRole extends React.Component {
|
|||||||
|
|
||||||
async handleWizardSave () {
|
async handleWizardSave () {
|
||||||
const {
|
const {
|
||||||
onSave,
|
onSave
|
||||||
api
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {
|
const {
|
||||||
selectedResourceRows,
|
selectedResourceRows,
|
||||||
@@ -92,11 +96,11 @@ class AddResourceRole extends React.Component {
|
|||||||
for (let j = 0; j < selectedRoleRows.length; j++) {
|
for (let j = 0; j < selectedRoleRows.length; j++) {
|
||||||
if (selectedResource === 'users') {
|
if (selectedResource === 'users') {
|
||||||
roleRequests.push(
|
roleRequests.push(
|
||||||
api.createUserRole(selectedResourceRows[i].id, selectedRoleRows[j].id)
|
UsersAPI.associateRole(selectedResourceRows[i].id, selectedRoleRows[j].id)
|
||||||
);
|
);
|
||||||
} else if (selectedResource === 'teams') {
|
} else if (selectedResource === 'teams') {
|
||||||
roleRequests.push(
|
roleRequests.push(
|
||||||
api.createTeamRole(selectedResourceRows[i].id, selectedRoleRows[j].id)
|
TeamsAPI.associateRole(selectedResourceRows[i].id, selectedRoleRows[j].id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,16 +113,6 @@ class AddResourceRole extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async readUsers (queryParams) {
|
|
||||||
const { api } = this.props;
|
|
||||||
return api.readUsers(Object.assign(queryParams, { is_superuser: false }));
|
|
||||||
}
|
|
||||||
|
|
||||||
async readTeams (queryParams) {
|
|
||||||
const { api } = this.props;
|
|
||||||
return api.readTeams(queryParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
selectedResource,
|
selectedResource,
|
||||||
@@ -183,7 +177,7 @@ class AddResourceRole extends React.Component {
|
|||||||
columns={userColumns}
|
columns={userColumns}
|
||||||
displayKey="username"
|
displayKey="username"
|
||||||
onRowClick={this.handleResourceCheckboxClick}
|
onRowClick={this.handleResourceCheckboxClick}
|
||||||
onSearch={this.readUsers}
|
onSearch={readUsers}
|
||||||
selectedLabel={i18n._(t`Selected`)}
|
selectedLabel={i18n._(t`Selected`)}
|
||||||
selectedResourceRows={selectedResourceRows}
|
selectedResourceRows={selectedResourceRows}
|
||||||
sortedColumnKey="username"
|
sortedColumnKey="username"
|
||||||
@@ -194,7 +188,7 @@ class AddResourceRole extends React.Component {
|
|||||||
<SelectResourceStep
|
<SelectResourceStep
|
||||||
columns={teamColumns}
|
columns={teamColumns}
|
||||||
onRowClick={this.handleResourceCheckboxClick}
|
onRowClick={this.handleResourceCheckboxClick}
|
||||||
onSearch={this.readTeams}
|
onSearch={readTeams}
|
||||||
selectedLabel={i18n._(t`Selected`)}
|
selectedLabel={i18n._(t`Selected`)}
|
||||||
selectedResourceRows={selectedResourceRows}
|
selectedResourceRows={selectedResourceRows}
|
||||||
itemName="team"
|
itemName="team"
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React, { Component } from 'react';
|
|||||||
|
|
||||||
import { withNetwork } from './Network';
|
import { withNetwork } from './Network';
|
||||||
|
|
||||||
|
import { ConfigAPI, MeAPI, RootAPI } from '../api';
|
||||||
|
|
||||||
const ConfigContext = React.createContext({});
|
const ConfigContext = React.createContext({});
|
||||||
|
|
||||||
class Provider extends Component {
|
class Provider extends Component {
|
||||||
@@ -46,13 +48,13 @@ class Provider extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async fetchMe () {
|
async fetchMe () {
|
||||||
const { api, handleHttpError } = this.props;
|
const { handleHttpError } = this.props;
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
results: [me]
|
results: [me]
|
||||||
}
|
}
|
||||||
} = await api.getMe();
|
} = await MeAPI.read();
|
||||||
this.setState(prevState => ({
|
this.setState(prevState => ({
|
||||||
value: {
|
value: {
|
||||||
...prevState.value,
|
...prevState.value,
|
||||||
@@ -75,13 +77,13 @@ class Provider extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetchConfig () {
|
async fetchConfig () {
|
||||||
const { api, handleHttpError } = this.props;
|
const { handleHttpError } = this.props;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [configRes, rootRes, meRes] = await Promise.all([
|
const [configRes, rootRes, meRes] = await Promise.all([
|
||||||
api.getConfig(),
|
ConfigAPI.read(),
|
||||||
api.getRoot(),
|
RootAPI.read(),
|
||||||
api.getMe()
|
MeAPI.read()
|
||||||
]);
|
]);
|
||||||
this.setState({
|
this.setState({
|
||||||
value: {
|
value: {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
|
|
||||||
import axios from 'axios';
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
@@ -9,8 +8,6 @@ import { t } from '@lingui/macro';
|
|||||||
|
|
||||||
import { withRootDialog } from './RootDialog';
|
import { withRootDialog } from './RootDialog';
|
||||||
|
|
||||||
import APIClient from '../api';
|
|
||||||
|
|
||||||
const NetworkContext = React.createContext({});
|
const NetworkContext = React.createContext({});
|
||||||
|
|
||||||
class Provider extends Component {
|
class Provider extends Component {
|
||||||
@@ -19,7 +16,6 @@ class Provider extends Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
value: {
|
value: {
|
||||||
api: new APIClient(axios.create({ xsrfCookieName: 'csrftoken', xsrfHeaderName: 'X-CSRFToken' })),
|
|
||||||
handleHttpError: err => {
|
handleHttpError: err => {
|
||||||
if (err.response.status === 401) {
|
if (err.response.status === 401) {
|
||||||
this.handle401();
|
this.handle401();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
|
|
||||||
import { withRootDialog } from '../contexts/RootDialog';
|
import { withRootDialog } from '../contexts/RootDialog';
|
||||||
import { withNetwork } from '../contexts/Network';
|
import { withNetwork } from '../contexts/Network';
|
||||||
|
import { RootAPI } from '../api';
|
||||||
|
|
||||||
import towerLogo from '../../images/tower-logo-header.svg';
|
import towerLogo from '../../images/tower-logo-header.svg';
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ class AWXLogin extends Component {
|
|||||||
|
|
||||||
async onLoginButtonClick (event) {
|
async onLoginButtonClick (event) {
|
||||||
const { username, password, isLoading } = this.state;
|
const { username, password, isLoading } = this.state;
|
||||||
const { api, handleHttpError, clearRootDialogMessage, fetchMe, updateConfig } = this.props;
|
const { handleHttpError, clearRootDialogMessage, fetchMe, updateConfig } = this.props;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ class AWXLogin extends Component {
|
|||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await api.login(username, password);
|
const { data } = await RootAPI.login(username, password);
|
||||||
updateConfig(data);
|
updateConfig(data);
|
||||||
await fetchMe();
|
await fetchMe();
|
||||||
this.setState({ isAuthenticated: true, isLoading: false });
|
this.setState({ isAuthenticated: true, isLoading: false });
|
||||||
|
|||||||
@@ -9,19 +9,11 @@ import Lookup from '../../../components/Lookup';
|
|||||||
|
|
||||||
import { withNetwork } from '../../../contexts/Network';
|
import { withNetwork } from '../../../contexts/Network';
|
||||||
|
|
||||||
|
import { InstanceGroupsAPI } from '../../../api';
|
||||||
|
|
||||||
|
const getInstanceGroups = async (params) => InstanceGroupsAPI.read(params);
|
||||||
|
|
||||||
class InstanceGroupsLookup extends React.Component {
|
class InstanceGroupsLookup extends React.Component {
|
||||||
constructor (props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.getInstanceGroups = this.getInstanceGroups.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getInstanceGroups (params) {
|
|
||||||
const { api } = this.props;
|
|
||||||
const data = await api.getInstanceGroups(params);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { value, tooltip, onChange, i18n } = this.props;
|
const { value, tooltip, onChange, i18n } = this.props;
|
||||||
|
|
||||||
@@ -51,7 +43,7 @@ class InstanceGroupsLookup extends React.Component {
|
|||||||
name="instanceGroups"
|
name="instanceGroups"
|
||||||
value={value}
|
value={value}
|
||||||
onLookupSave={onChange}
|
onLookupSave={onChange}
|
||||||
getItems={this.getInstanceGroups}
|
getItems={getInstanceGroups}
|
||||||
columns={[
|
columns={[
|
||||||
{ name: i18n._(t`Name`), key: 'name', isSortable: true },
|
{ name: i18n._(t`Name`), key: 'name', isSortable: true },
|
||||||
{ name: i18n._(t`Modified`), key: 'modified', isSortable: false, isNumeric: true },
|
{ name: i18n._(t`Modified`), key: 'modified', isSortable: false, isNumeric: true },
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup
|
|||||||
import AnsibleSelect from '../../../components/AnsibleSelect';
|
import AnsibleSelect from '../../../components/AnsibleSelect';
|
||||||
import InstanceGroupsLookup from './InstanceGroupsLookup';
|
import InstanceGroupsLookup from './InstanceGroupsLookup';
|
||||||
import { required } from '../../../util/validators';
|
import { required } from '../../../util/validators';
|
||||||
|
import { OrganizationsAPI } from '../../../api';
|
||||||
|
|
||||||
class OrganizationForm extends Component {
|
class OrganizationForm extends Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -52,10 +53,9 @@ class OrganizationForm extends Component {
|
|||||||
|
|
||||||
async getRelatedInstanceGroups () {
|
async getRelatedInstanceGroups () {
|
||||||
const {
|
const {
|
||||||
api,
|
|
||||||
organization: { id }
|
organization: { id }
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { data } = await api.getOrganizationInstanceGroups(id);
|
const { data } = await OrganizationsAPI.readInstanceGroups(id);
|
||||||
return data.results;
|
return data.results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import OrganizationEdit from './OrganizationEdit';
|
|||||||
import OrganizationNotifications from './OrganizationNotifications';
|
import OrganizationNotifications from './OrganizationNotifications';
|
||||||
import OrganizationTeams from './OrganizationTeams';
|
import OrganizationTeams from './OrganizationTeams';
|
||||||
import RoutedTabs from '../../../../components/Tabs/RoutedTabs';
|
import RoutedTabs from '../../../../components/Tabs/RoutedTabs';
|
||||||
|
import { OrganizationsAPI } from '../../../../api';
|
||||||
|
|
||||||
class Organization extends Component {
|
class Organization extends Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -45,22 +46,21 @@ class Organization extends Component {
|
|||||||
const {
|
const {
|
||||||
match,
|
match,
|
||||||
setBreadcrumb,
|
setBreadcrumb,
|
||||||
api,
|
|
||||||
handleHttpError
|
handleHttpError
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [{ data }, notifAdminRest, auditorRes, adminRes] = await Promise.all([
|
const [{ data }, notifAdminRest, auditorRes, adminRes] = await Promise.all([
|
||||||
api.getOrganizationDetails(parseInt(match.params.id, 10)),
|
OrganizationsAPI.readDetail(parseInt(match.params.id, 10)),
|
||||||
api.getOrganizations({
|
OrganizationsAPI.read({
|
||||||
role_level: 'notification_admin_role',
|
role_level: 'notification_admin_role',
|
||||||
page_size: 1
|
page_size: 1
|
||||||
}),
|
}),
|
||||||
api.getOrganizations({
|
OrganizationsAPI.read({
|
||||||
role_level: 'auditor_role',
|
role_level: 'auditor_role',
|
||||||
id: parseInt(match.params.id, 10)
|
id: parseInt(match.params.id, 10)
|
||||||
}),
|
}),
|
||||||
api.getOrganizations({
|
OrganizationsAPI.read({
|
||||||
role_level: 'admin_role',
|
role_level: 'admin_role',
|
||||||
id: parseInt(match.params.id, 10)
|
id: parseInt(match.params.id, 10)
|
||||||
})
|
})
|
||||||
@@ -82,12 +82,11 @@ class Organization extends Component {
|
|||||||
const {
|
const {
|
||||||
match,
|
match,
|
||||||
setBreadcrumb,
|
setBreadcrumb,
|
||||||
api,
|
|
||||||
handleHttpError
|
handleHttpError
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await api.getOrganizationDetails(parseInt(match.params.id, 10));
|
const { data } = await OrganizationsAPI.readDetail(parseInt(match.params.id, 10));
|
||||||
setBreadcrumb(data);
|
setBreadcrumb(data);
|
||||||
this.setState({ organization: data, loading: false });
|
this.setState({ organization: data, loading: false });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import AddResourceRole from '../../../../components/AddRole/AddResourceRole';
|
|||||||
import { withNetwork } from '../../../../contexts/Network';
|
import { withNetwork } from '../../../../contexts/Network';
|
||||||
import { getQSConfig, parseNamespacedQueryString } from '../../../../util/qs';
|
import { getQSConfig, parseNamespacedQueryString } from '../../../../util/qs';
|
||||||
import { Organization } from '../../../../types';
|
import { Organization } from '../../../../types';
|
||||||
|
import { OrganizationsAPI, TeamsAPI, UsersAPI } from '../../../../api';
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('access', {
|
const QS_CONFIG = getQSConfig('access', {
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -56,10 +57,10 @@ class OrganizationAccess extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async readOrgAccessList () {
|
async readOrgAccessList () {
|
||||||
const { organization, api, handleHttpError, location } = this.props;
|
const { organization, handleHttpError, location } = this.props;
|
||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
try {
|
try {
|
||||||
const { data } = await api.getOrganizationAccessList(
|
const { data } = await OrganizationsAPI.readAccessList(
|
||||||
organization.id,
|
organization.id,
|
||||||
parseNamespacedQueryString(QS_CONFIG, location.search)
|
parseNamespacedQueryString(QS_CONFIG, location.search)
|
||||||
);
|
);
|
||||||
@@ -92,7 +93,7 @@ class OrganizationAccess extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeRole () {
|
async removeRole () {
|
||||||
const { api, handleHttpError } = this.props;
|
const { handleHttpError } = this.props;
|
||||||
const { roleToDelete: role, roleToDeleteAccessRecord: accessRecord } = this.state;
|
const { roleToDelete: role, roleToDeleteAccessRecord: accessRecord } = this.state;
|
||||||
if (!role || !accessRecord) {
|
if (!role || !accessRecord) {
|
||||||
return;
|
return;
|
||||||
@@ -101,9 +102,9 @@ class OrganizationAccess extends React.Component {
|
|||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
try {
|
try {
|
||||||
if (type === 'teams') {
|
if (type === 'teams') {
|
||||||
await api.disassociateTeamRole(role.team_id, role.id);
|
await TeamsAPI.disassociateRole(role.team_id, role.id);
|
||||||
} else {
|
} else {
|
||||||
await api.disassociateUserRole(accessRecord.id, role.id);
|
await UsersAPI.disassociateRole(accessRecord.id, role.id);
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import styled from 'styled-components';
|
|||||||
import { DetailList, Detail } from '../../../../components/DetailList';
|
import { DetailList, Detail } from '../../../../components/DetailList';
|
||||||
import { withNetwork } from '../../../../contexts/Network';
|
import { withNetwork } from '../../../../contexts/Network';
|
||||||
import { ChipGroup, Chip } from '../../../../components/Chip';
|
import { ChipGroup, Chip } from '../../../../components/Chip';
|
||||||
|
import { OrganizationsAPI } from '../../../../api';
|
||||||
|
|
||||||
const CardBody = styled(PFCardBody)`
|
const CardBody = styled(PFCardBody)`
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
@@ -29,14 +30,13 @@ class OrganizationDetail extends Component {
|
|||||||
|
|
||||||
async loadInstanceGroups () {
|
async loadInstanceGroups () {
|
||||||
const {
|
const {
|
||||||
api,
|
|
||||||
handleHttpError,
|
handleHttpError,
|
||||||
match
|
match
|
||||||
} = this.props;
|
} = this.props;
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data
|
data
|
||||||
} = await api.getOrganizationInstanceGroups(match.params.id);
|
} = await OrganizationsAPI.readInstanceGroups(match.params.id);
|
||||||
this.setState({
|
this.setState({
|
||||||
instanceGroups: [...data.results]
|
instanceGroups: [...data.results]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { withRouter } from 'react-router-dom';
|
|||||||
import { CardBody } from '@patternfly/react-core';
|
import { CardBody } from '@patternfly/react-core';
|
||||||
import OrganizationForm from '../../components/OrganizationForm';
|
import OrganizationForm from '../../components/OrganizationForm';
|
||||||
import { withNetwork } from '../../../../contexts/Network';
|
import { withNetwork } from '../../../../contexts/Network';
|
||||||
|
import { OrganizationsAPI } from '../../../../api';
|
||||||
|
|
||||||
class OrganizationEdit extends Component {
|
class OrganizationEdit extends Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -20,9 +21,9 @@ class OrganizationEdit extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleSubmit (values, groupsToAssociate, groupsToDisassociate) {
|
async handleSubmit (values, groupsToAssociate, groupsToDisassociate) {
|
||||||
const { api, organization, handleHttpError } = this.props;
|
const { organization, handleHttpError } = this.props;
|
||||||
try {
|
try {
|
||||||
await api.updateOrganizationDetails(organization.id, values);
|
await OrganizationsAPI.update(organization.id, values);
|
||||||
await this.submitInstanceGroups(groupsToAssociate, groupsToDisassociate);
|
await this.submitInstanceGroups(groupsToAssociate, groupsToDisassociate);
|
||||||
this.handleSuccess();
|
this.handleSuccess();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -41,12 +42,17 @@ class OrganizationEdit extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async submitInstanceGroups (groupsToAssociate, groupsToDisassociate) {
|
async submitInstanceGroups (groupsToAssociate, groupsToDisassociate) {
|
||||||
const { api, organization, handleHttpError } = this.props;
|
const { organization, handleHttpError } = this.props;
|
||||||
const url = organization.related.instance_groups;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.all(groupsToAssociate.map(id => api.associateInstanceGroup(url, id)));
|
await Promise.all(
|
||||||
await Promise.all(groupsToDisassociate.map(id => api.disassociate(url, id)));
|
groupsToAssociate.map(id => OrganizationsAPI.associateInstanceGroup(organization.id, id))
|
||||||
|
);
|
||||||
|
await Promise.all(
|
||||||
|
groupsToDisassociate.map(
|
||||||
|
id => OrganizationsAPI.disassociateInstanceGroup(organization.id, id)
|
||||||
|
)
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleHttpError(err) || this.setState({ error: err });
|
handleHttpError(err) || this.setState({ error: err });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { withNetwork } from '../../../../contexts/Network';
|
|||||||
import PaginatedDataList from '../../../../components/PaginatedDataList';
|
import PaginatedDataList from '../../../../components/PaginatedDataList';
|
||||||
import NotificationListItem from '../../../../components/NotificationsList/NotificationListItem';
|
import NotificationListItem from '../../../../components/NotificationsList/NotificationListItem';
|
||||||
import { getQSConfig, parseNamespacedQueryString } from '../../../../util/qs';
|
import { getQSConfig, parseNamespacedQueryString } from '../../../../util/qs';
|
||||||
|
import { OrganizationsAPI } from '../../../../api';
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('notification', {
|
const QS_CONFIG = getQSConfig('notification', {
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -49,11 +50,11 @@ class OrganizationNotifications extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async readNotifications () {
|
async readNotifications () {
|
||||||
const { id, api, handleHttpError, location } = this.props;
|
const { id, handleHttpError, location } = this.props;
|
||||||
const params = parseNamespacedQueryString(QS_CONFIG, location.search);
|
const params = parseNamespacedQueryString(QS_CONFIG, location.search);
|
||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
try {
|
try {
|
||||||
const { data } = await api.getOrganizationNotifications(id, params);
|
const { data } = await OrganizationsAPI.readNotificationTemplates(id, params);
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
itemCount: data.count || 0,
|
itemCount: data.count || 0,
|
||||||
@@ -72,21 +73,22 @@ class OrganizationNotifications extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async readSuccessesAndErrors () {
|
async readSuccessesAndErrors () {
|
||||||
const { api, handleHttpError, id } = this.props;
|
const { handleHttpError, id } = this.props;
|
||||||
const { notifications } = this.state;
|
const { notifications } = this.state;
|
||||||
if (!notifications.length) {
|
if (!notifications.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ids = notifications.map(n => n.id).join(',');
|
const ids = notifications.map(n => n.id).join(',');
|
||||||
try {
|
try {
|
||||||
const successTemplatesPromise = api.getOrganizationNotificationSuccess(
|
const successTemplatesPromise = OrganizationsAPI.readNotificationTemplatesSuccess(
|
||||||
id,
|
id,
|
||||||
{ id__in: ids }
|
{ id__in: ids }
|
||||||
);
|
);
|
||||||
const errorTemplatesPromise = api.getOrganizationNotificationError(
|
const errorTemplatesPromise = OrganizationsAPI.readNotificationTemplatesError(
|
||||||
id,
|
id,
|
||||||
{ id__in: ids }
|
{ id__in: ids }
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: successTemplates } = await successTemplatesPromise;
|
const { data: successTemplates } = await successTemplatesPromise;
|
||||||
const { data: errorTemplates } = await errorTemplatesPromise;
|
const { data: errorTemplates } = await errorTemplatesPromise;
|
||||||
|
|
||||||
@@ -104,53 +106,65 @@ class OrganizationNotifications extends Component {
|
|||||||
|
|
||||||
toggleNotification = (notificationId, isCurrentlyOn, status) => {
|
toggleNotification = (notificationId, isCurrentlyOn, status) => {
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.createSuccess(notificationId, isCurrentlyOn);
|
if (isCurrentlyOn) {
|
||||||
|
this.disassociateSuccess(notificationId);
|
||||||
|
} else {
|
||||||
|
this.associateSuccess(notificationId);
|
||||||
|
}
|
||||||
} else if (status === 'error') {
|
} else if (status === 'error') {
|
||||||
this.createError(notificationId, isCurrentlyOn);
|
if (isCurrentlyOn) {
|
||||||
|
this.disassociateError(notificationId);
|
||||||
|
} else {
|
||||||
|
this.associateError(notificationId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async createSuccess (notificationId, isCurrentlyOn) {
|
async associateSuccess (notificationId) {
|
||||||
const { id, api, handleHttpError } = this.props;
|
const { id, handleHttpError } = this.props;
|
||||||
const postParams = { id: notificationId };
|
|
||||||
if (isCurrentlyOn) {
|
|
||||||
postParams.disassociate = true;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await api.createOrganizationNotificationSuccess(id, postParams);
|
await OrganizationsAPI.associateNotificationTemplatesSuccess(id, notificationId);
|
||||||
if (isCurrentlyOn) {
|
this.setState(prevState => ({
|
||||||
this.setState((prevState) => ({
|
successTemplateIds: [...prevState.successTemplateIds, notificationId]
|
||||||
successTemplateIds: prevState.successTemplateIds
|
}));
|
||||||
.filter((templateId) => templateId !== notificationId)
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this.setState(prevState => ({
|
|
||||||
successTemplateIds: [...prevState.successTemplateIds, notificationId]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleHttpError(err) || this.setState({ error: true });
|
handleHttpError(err) || this.setState({ error: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createError (notificationId, isCurrentlyOn) {
|
async disassociateSuccess (notificationId) {
|
||||||
const { id, api, handleHttpError } = this.props;
|
const { id, handleHttpError } = this.props;
|
||||||
const postParams = { id: notificationId };
|
|
||||||
if (isCurrentlyOn) {
|
|
||||||
postParams.disassociate = true;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await api.createOrganizationNotificationError(id, postParams);
|
await OrganizationsAPI.disassociateNotificationTemplatesSuccess(id, notificationId);
|
||||||
if (isCurrentlyOn) {
|
this.setState((prevState) => ({
|
||||||
this.setState((prevState) => ({
|
successTemplateIds: prevState.successTemplateIds
|
||||||
errorTemplateIds: prevState.errorTemplateIds
|
.filter((templateId) => templateId !== notificationId)
|
||||||
.filter((templateId) => templateId !== notificationId)
|
}));
|
||||||
}));
|
} catch (err) {
|
||||||
} else {
|
handleHttpError(err) || this.setState({ error: true });
|
||||||
this.setState(prevState => ({
|
}
|
||||||
errorTemplateIds: [...prevState.errorTemplateIds, notificationId]
|
}
|
||||||
}));
|
|
||||||
}
|
async associateError (notificationId) {
|
||||||
|
const { id, handleHttpError } = this.props;
|
||||||
|
try {
|
||||||
|
await OrganizationsAPI.associateNotificationTemplatesError(id, notificationId);
|
||||||
|
this.setState(prevState => ({
|
||||||
|
errorTemplateIds: [...prevState.errorTemplateIds, notificationId]
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
handleHttpError(err) || this.setState({ error: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async disassociateError (notificationId) {
|
||||||
|
const { id, handleHttpError } = this.props;
|
||||||
|
try {
|
||||||
|
await OrganizationsAPI.disassociateNotificationTemplatesError(id, notificationId);
|
||||||
|
this.setState((prevState) => ({
|
||||||
|
errorTemplateIds: prevState.errorTemplateIds
|
||||||
|
.filter((templateId) => templateId !== notificationId)
|
||||||
|
}));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleHttpError(err) || this.setState({ error: true });
|
handleHttpError(err) || this.setState({ error: true });
|
||||||
}
|
}
|
||||||
@@ -205,13 +219,6 @@ OrganizationNotifications.propTypes = {
|
|||||||
id: number.isRequired,
|
id: number.isRequired,
|
||||||
canToggleNotifications: bool.isRequired,
|
canToggleNotifications: bool.isRequired,
|
||||||
handleHttpError: func.isRequired,
|
handleHttpError: func.isRequired,
|
||||||
api: shape({
|
|
||||||
getOrganizationNotifications: func.isRequired,
|
|
||||||
getOrganizationNotificationSuccess: func.isRequired,
|
|
||||||
getOrganizationNotificationError: func.isRequired,
|
|
||||||
createOrganizationNotificationSuccess: func.isRequired,
|
|
||||||
createOrganizationNotificationError: func.isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
location: shape({
|
location: shape({
|
||||||
search: string.isRequired,
|
search: string.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { withRouter } from 'react-router-dom';
|
|||||||
import PaginatedDataList from '../../../../components/PaginatedDataList';
|
import PaginatedDataList from '../../../../components/PaginatedDataList';
|
||||||
import { getQSConfig, parseNamespacedQueryString } from '../../../../util/qs';
|
import { getQSConfig, parseNamespacedQueryString } from '../../../../util/qs';
|
||||||
import { withNetwork } from '../../../../contexts/Network';
|
import { withNetwork } from '../../../../contexts/Network';
|
||||||
|
import { OrganizationsAPI } from '../../../../api';
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('team', {
|
const QS_CONFIG = getQSConfig('team', {
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -38,13 +39,13 @@ class OrganizationTeams extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async readOrganizationTeamsList () {
|
async readOrganizationTeamsList () {
|
||||||
const { id, api, handleHttpError, location } = this.props;
|
const { id, handleHttpError, location } = this.props;
|
||||||
const params = parseNamespacedQueryString(QS_CONFIG, location.search);
|
const params = parseNamespacedQueryString(QS_CONFIG, location.search);
|
||||||
this.setState({ isLoading: true, error: null });
|
this.setState({ isLoading: true, error: null });
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data: { count = 0, results = [] },
|
data: { count = 0, results = [] },
|
||||||
} = await api.readOrganizationTeamsList(id, params);
|
} = await OrganizationsAPI.readTeams(id, params);
|
||||||
this.setState({
|
this.setState({
|
||||||
itemCount: count,
|
itemCount: count,
|
||||||
teams: results,
|
teams: results,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
import { withNetwork } from '../../../contexts/Network';
|
import { withNetwork } from '../../../contexts/Network';
|
||||||
import CardCloseButton from '../../../components/CardCloseButton';
|
import CardCloseButton from '../../../components/CardCloseButton';
|
||||||
import OrganizationForm from '../components/OrganizationForm';
|
import OrganizationForm from '../components/OrganizationForm';
|
||||||
|
import { OrganizationsAPI } from '../../../api';
|
||||||
|
|
||||||
class OrganizationAdd extends React.Component {
|
class OrganizationAdd extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -29,13 +30,12 @@ class OrganizationAdd extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleSubmit (values, groupsToAssociate) {
|
async handleSubmit (values, groupsToAssociate) {
|
||||||
const { api, handleHttpError } = this.props;
|
const { handleHttpError } = this.props;
|
||||||
try {
|
try {
|
||||||
const { data: response } = await api.createOrganization(values);
|
const { data: response } = await OrganizationsAPI.create(values);
|
||||||
const instanceGroupsUrl = response.related.instance_groups;
|
|
||||||
try {
|
try {
|
||||||
await Promise.all(groupsToAssociate.map(id => api
|
await Promise.all(groupsToAssociate.map(id => OrganizationsAPI
|
||||||
.associateInstanceGroup(instanceGroupsUrl, id)));
|
.associateInstanceGroup(response.id, id)));
|
||||||
this.handleSuccess(response.id);
|
this.handleSuccess(response.id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleHttpError(err) || this.setState({ error: err });
|
handleHttpError(err) || this.setState({ error: err });
|
||||||
@@ -83,10 +83,6 @@ class OrganizationAdd extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OrganizationAdd.propTypes = {
|
|
||||||
api: PropTypes.shape().isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
OrganizationAdd.contextTypes = {
|
OrganizationAdd.contextTypes = {
|
||||||
custom_virtualenvs: PropTypes.arrayOf(PropTypes.string)
|
custom_virtualenvs: PropTypes.arrayOf(PropTypes.string)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import PaginatedDataList, {
|
|||||||
import DataListToolbar from '../../../components/DataListToolbar';
|
import DataListToolbar from '../../../components/DataListToolbar';
|
||||||
import OrganizationListItem from '../components/OrganizationListItem';
|
import OrganizationListItem from '../components/OrganizationListItem';
|
||||||
import { getQSConfig, parseNamespacedQueryString } from '../../../util/qs';
|
import { getQSConfig, parseNamespacedQueryString } from '../../../util/qs';
|
||||||
|
import { OrganizationsAPI } from '../../../api';
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('organization', {
|
const QS_CONFIG = getQSConfig('organization', {
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -73,11 +74,11 @@ class OrganizationsList extends Component {
|
|||||||
|
|
||||||
async handleOrgDelete () {
|
async handleOrgDelete () {
|
||||||
const { selected } = this.state;
|
const { selected } = this.state;
|
||||||
const { api, handleHttpError } = this.props;
|
const { handleHttpError } = this.props;
|
||||||
let errorHandled;
|
let errorHandled;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.all(selected.map((org) => api.destroyOrganization(org.id)));
|
await Promise.all(selected.map((org) => OrganizationsAPI.destroy(org.id)));
|
||||||
this.setState({
|
this.setState({
|
||||||
selected: []
|
selected: []
|
||||||
});
|
});
|
||||||
@@ -91,13 +92,13 @@ class OrganizationsList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetchOrganizations () {
|
async fetchOrganizations () {
|
||||||
const { api, handleHttpError, location } = this.props;
|
const { handleHttpError, location } = this.props;
|
||||||
const params = parseNamespacedQueryString(QS_CONFIG, location.search);
|
const params = parseNamespacedQueryString(QS_CONFIG, location.search);
|
||||||
|
|
||||||
this.setState({ error: false, isLoading: true });
|
this.setState({ error: false, isLoading: true });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await api.getOrganizations(params);
|
const { data } = await OrganizationsAPI.read(params);
|
||||||
const { count, results } = data;
|
const { count, results } = data;
|
||||||
|
|
||||||
const stateToUpdate = {
|
const stateToUpdate = {
|
||||||
@@ -115,10 +116,8 @@ class OrganizationsList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetchOptionsOrganizations () {
|
async fetchOptionsOrganizations () {
|
||||||
const { api } = this.props;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await api.optionsOrganizations();
|
const { data } = await OrganizationsAPI.readOptions();
|
||||||
const { actions } = data;
|
const { actions } = data;
|
||||||
|
|
||||||
const stateToUpdate = {
|
const stateToUpdate = {
|
||||||
|
|||||||
Reference in New Issue
Block a user