diff --git a/src/App.jsx b/src/App.jsx
index 854ab61733..624242384d 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -38,8 +38,17 @@ class App extends Component {
config: {},
error: false,
};
+
+ this.onLogout = this.onLogout.bind(this);
};
+ async onLogout () {
+ const { api } = this.props;
+
+ await api.logout();
+ window.location.replace('/#/login')
+ }
+
onNavToggle = () => {
this.setState(({ isNavOpen }) => ({ isNavOpen: !isNavOpen }));
};
diff --git a/src/api.js b/src/api.js
index 9e07c3bac5..007fa37f5c 100644
--- a/src/api.js
+++ b/src/api.js
@@ -1,33 +1,35 @@
import axios from 'axios';
-import {
- API_CONFIG,
- API_LOGIN,
- API_ROOT,
-} from './endpoints';
+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_ORGANIZATIONS = `${API_V2}organizations/`;
const CSRF_COOKIE_NAME = 'csrftoken';
const CSRF_HEADER_NAME = 'X-CSRFToken';
-
const LOGIN_CONTENT_TYPE = 'application/x-www-form-urlencoded';
-class APIClient {
- constructor () {
- this.http = axios.create({
- xsrfCookieName: CSRF_COOKIE_NAME,
- xsrfHeaderName: CSRF_HEADER_NAME,
- });
- }
+const defaultHttpAdapter = axios.create({
+ xsrfCookieName: CSRF_COOKIE_NAME,
+ xsrfHeaderName: CSRF_HEADER_NAME,
+});
- /* eslint class-methods-use-this: ["error", { "exceptMethods": ["getCookie"] }] */
- getCookie () {
+class APIClient {
+ static getCookie () {
return document.cookie;
}
- isAuthenticated () {
- let authenticated = false;
+ constructor (httpAdapter = defaultHttpAdapter) {
+ this.http = httpAdapter;
+ }
- const parsed = (`; ${this.getCookie()}`).split('; userLoggedIn=');
+ isAuthenticated () {
+ const cookie = this.constructor.getCookie();
+ const parsed = (`; ${cookie}`).split('; userLoggedIn=');
+
+ let authenticated = false;
if (parsed.length === 2) {
authenticated = parsed.pop().split(';').shift() === 'true';
@@ -36,10 +38,6 @@ class APIClient {
return authenticated;
}
- getRoot () {
- return this.http.get(API_ROOT);
- }
-
async login (username, password, redirect = API_CONFIG) {
const un = encodeURIComponent(username);
const pw = encodeURIComponent(password);
@@ -49,12 +47,36 @@ class APIClient {
const headers = { 'Content-Type': LOGIN_CONTENT_TYPE };
await this.http.get(API_LOGIN, { headers });
- await this.http.post(API_LOGIN, data, { headers });
+ const response = await this.http.post(API_LOGIN, data, { headers });
+
+ return response;
}
- get = (endpoint, params = {}) => this.http.get(endpoint, { params });
+ logout () {
+ return this.http.get(API_LOGOUT);
+ }
- post = (endpoint, data) => this.http.post(endpoint, data);
+ getRoot () {
+ return this.http.get(API_ROOT);
+ }
+
+ getConfig () {
+ return this.http.get(API_CONFIG);
+ }
+
+ getOrganizations (params = {}) {
+ return this.http.get(API_ORGANIZATIONS, { params });
+ }
+
+ createOrganization (data) {
+ return this.http.post(API_ORGANIZATIONS, data);
+ }
+
+ getOrganizationDetails (id) {
+ const endpoint = `${API_ORGANIZATIONS}${id}/`;
+
+ return this.http.get(endpoint);
+ }
}
-export default new APIClient();
+export default APIClient;
diff --git a/src/index.jsx b/src/index.jsx
index 04cb57b6c8..5c63f5163f 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -18,7 +18,8 @@ import './app.scss';
import './components/Pagination/styles.scss';
import './components/DataListToolbar/styles.scss';
-import api from './api';
+import APIClient from './api';
+
import App from './App';
import Background from './components/Background';
import Applications from './pages/Applications';
@@ -55,7 +56,7 @@ const language = (navigator.languages && navigator.languages[0])
|| navigator.userLanguage;
const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0];
-export async function main () {
+export async function main (api) {
const el = document.getElementById('app');
// fetch additional config from server
const { data } = await api.getRoot();
@@ -67,6 +68,7 @@ export async function main () {
path="/login"
render={() => (
@@ -92,6 +94,7 @@ export async function main () {
(
(
)}
@@ -262,4 +266,4 @@ export async function main () {
, el);
};
-export default main();
+export default main(new APIClient());
diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx
index 20690940b2..207aa1491d 100644
--- a/src/pages/Login.jsx
+++ b/src/pages/Login.jsx
@@ -8,7 +8,6 @@ import {
} from '@patternfly/react-core';
import towerLogo from '../../images/tower-logo-header.svg';
-import api from '../api';
class AtLogin extends Component {
constructor (props) {
@@ -34,6 +33,7 @@ class AtLogin extends Component {
handleSubmit = async event => {
const { username, password, loading } = this.state;
+ const { api } = this.props;
event.preventDefault();
@@ -54,7 +54,7 @@ class AtLogin extends Component {
render () {
const { username, password, isValidPassword } = this.state;
- const { logo, alt } = this.props;
+ const { api, alt, logo } = this.props;
const logoSrc = logo ? `data:image/jpeg;${logo}` : towerLogo;
if (api.isAuthenticated()) {
diff --git a/src/pages/Organizations/index.jsx b/src/pages/Organizations/index.jsx
index 09299873df..b3a13a0161 100644
--- a/src/pages/Organizations/index.jsx
+++ b/src/pages/Organizations/index.jsx
@@ -5,12 +5,31 @@ import OrganizationAdd from './views/Organization.add';
import OrganizationView from './views/Organization.view';
import OrganizationsList from './views/Organizations.list';
-const Organizations = ({ match }) => (
+export default ({ api, match }) => (
-
-
-
+ (
+
+ )}
+ />
+ (
+
+ )}
+ />
+ (
+
+ )}
+ />
);
-
-export default Organizations;
diff --git a/src/pages/Organizations/views/Organization.view.jsx b/src/pages/Organizations/views/Organization.view.jsx
index dbfb42c4b4..18c7066afa 100644
--- a/src/pages/Organizations/views/Organization.view.jsx
+++ b/src/pages/Organizations/views/Organization.view.jsx
@@ -2,16 +2,13 @@ import React, { Component, Fragment } from 'react';
import { i18nMark } from '@lingui/react';
import {
Switch,
- Route
+ Route,
+ withRouter,
} from 'react-router-dom';
-
import OrganizationBreadcrumb from '../components/OrganizationBreadcrumb';
import OrganizationDetail from '../components/OrganizationDetail';
import OrganizationEdit from '../components/OrganizationEdit';
-import api from '../../../api';
-import { API_ORGANIZATIONS } from '../../../endpoints';
-
class OrganizationView extends Component {
constructor (props) {
super(props);
@@ -47,13 +44,15 @@ class OrganizationView extends Component {
async fetchOrganization () {
const { mounted } = this.state;
+ const { api } = this.props;
+
if (mounted) {
this.setState({ error: false, loading: true });
const { match } = this.props;
const { parentBreadcrumbObj, organization } = this.state;
try {
- const { data } = await api.get(`${API_ORGANIZATIONS}${match.params.id}/`);
+ const { data } = await api.getOrganizationDetails(match.params.id);
if (organization === 'loading') {
this.setState({ organization: data });
}
@@ -118,4 +117,4 @@ class OrganizationView extends Component {
}
}
-export default OrganizationView;
+export default withRouter(OrganizationView);
diff --git a/src/pages/Organizations/views/Organizations.list.jsx b/src/pages/Organizations/views/Organizations.list.jsx
index 5ca2809911..0f14bf21ff 100644
--- a/src/pages/Organizations/views/Organizations.list.jsx
+++ b/src/pages/Organizations/views/Organizations.list.jsx
@@ -17,9 +17,6 @@ import DataListToolbar from '../../../components/DataListToolbar';
import OrganizationListItem from '../components/OrganizationListItem';
import Pagination from '../../../components/Pagination';
-import api from '../../../api';
-import { API_ORGANIZATIONS } from '../../../endpoints';
-
import {
encodeQueryString,
parseQueryString,
@@ -132,6 +129,7 @@ class Organizations extends Component {
}
async fetchOrganizations (queryParams) {
+ const { api } = this.props;
const { page, page_size, order_by } = queryParams;
let sortOrder = 'ascending';
@@ -145,7 +143,7 @@ class Organizations extends Component {
this.setState({ error: false, loading: true });
try {
- const { data } = await api.get(API_ORGANIZATIONS, queryParams);
+ const { data } = await api.getOrganizations(queryParams);
const { count, results } = data;
const pageCount = Math.ceil(count / page_size);