From c0203f13366606fb85afab739dfc77158c34b36c Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 15 Jun 2018 21:49:25 +0200 Subject: [PATCH] Implemented loading of short URLs --- package.json | 4 +- src/App.js | 2 +- src/api/ShlinkApiClient.js | 70 +++++++++++++++++++ src/common/AsideMenu.js | 8 +++ src/common/AsideMenu.scss | 15 ++++ src/common/MainHeader.js | 4 +- src/common/MenuLayout.js | 21 ++++-- src/index.js | 5 +- src/reducers/index.js | 6 +- src/reducers/types.js | 8 +++ src/servers/ServersDropdown.js | 12 ++-- src/servers/reducers/selectedServer.js | 7 +- src/servers/reducers/server.js | 8 +-- src/short-urls/ShortUrlsList.js | 33 +++++++++ src/short-urls/reducers/shortUrlsList.js | 21 ++++++ .../servers/ServersDropdown.test.js | 0 16 files changed, 191 insertions(+), 33 deletions(-) create mode 100644 src/api/ShlinkApiClient.js create mode 100644 src/common/AsideMenu.js create mode 100644 src/common/AsideMenu.scss create mode 100644 src/reducers/types.js create mode 100644 src/short-urls/ShortUrlsList.js create mode 100644 src/short-urls/reducers/shortUrlsList.js rename {tests => test}/servers/ServersDropdown.test.js (100%) diff --git a/package.json b/package.json index 85dd3c04..91bdad98 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@fortawesome/fontawesome-free-solid": "^5.0.13", "@fortawesome/react-fontawesome": "0.0.19", "autoprefixer": "7.1.6", + "axios": "^0.18.0", "babel-core": "6.26.0", "babel-eslint": "7.2.3", "babel-jest": "20.0.3", @@ -46,6 +47,7 @@ "react-router-dom": "^4.2.2", "reactstrap": "^6.0.1", "redux": "^4.0.0", + "redux-thunk": "^2.3.0", "resolve": "1.6.0", "style-loader": "0.19.0", "sw-precache-webpack-plugin": "0.11.4", @@ -74,7 +76,7 @@ "/config/setupEnzyme.js" ], "testMatch": [ - "/tests/**/*.test.{js,jsx,mjs}" + "/test/**/*.test.{js,jsx,mjs}" ], "testEnvironment": "node", "testURL": "http://localhost", diff --git a/src/App.js b/src/App.js index 3e5a7cd3..58c5d3b9 100644 --- a/src/App.js +++ b/src/App.js @@ -9,7 +9,7 @@ import CreateServer from './servers/CreateServer'; export default class App extends React.Component { render() { return ( -
+
diff --git a/src/api/ShlinkApiClient.js b/src/api/ShlinkApiClient.js new file mode 100644 index 00000000..b435db1a --- /dev/null +++ b/src/api/ShlinkApiClient.js @@ -0,0 +1,70 @@ +import axios from 'axios'; +import { isEmpty } from 'ramda'; + +export class ShlinkApiClient { + constructor(axios) { + this.axios = axios; + this._baseUrl = ''; + this._apiKey = ''; + this._token = ''; + } + + /** + * Sets the base URL to be used on any request + * @param {String} baseUrl + * @param {String} apiKey + */ + setConfig = ({ url, apiKey }) => { + this._baseUrl = url; + this._apiKey = apiKey; + }; + + /** + * Returns the list of short URLs + * @param params + * @returns {Promise} + */ + listShortUrls = (params = {}) => { + const { page = 1 } = params; + return this._performRequest(`/rest/short-codes?page=${page}`) + .then(resp => resp.data.shortUrls.data) + .catch(e => { + // If auth failed, reset token to force it to be regenerated, and perform a new request + if (e.response.status === 401) { + this._token = ''; + return this.listShortUrls(params); + } + + // Otherwise, let caller handle the rejection + return Promise.reject(e); + }); + }; + + _performRequest = async (url, method = 'GET') => { + if (isEmpty(this._token)) { + this._token = await this._handleAuth(); + } + + return await this.axios({ + method, + url: `${this._baseUrl}${url}`, + headers: { 'Authorization': `Bearer ${this._token}` } + }).then(resp => { + // Save new token + const { authorization = '' } = resp.headers; + this._token = authorization.substr('Bearer '.length); + return resp; + }); + }; + + _handleAuth = async () => { + const resp = await this.axios({ + method: 'POST', + url: `${this._baseUrl}/rest/authenticate`, + data: { apiKey: this._apiKey } + }); + return resp.data.token; + }; +} + +export default new ShlinkApiClient(axios); diff --git a/src/common/AsideMenu.js b/src/common/AsideMenu.js new file mode 100644 index 00000000..3f9fb51d --- /dev/null +++ b/src/common/AsideMenu.js @@ -0,0 +1,8 @@ +import React from 'react'; +import './AsideMenu.scss'; + +export default function AsideMenu() { + return ( + + ); +} diff --git a/src/common/AsideMenu.scss b/src/common/AsideMenu.scss new file mode 100644 index 00000000..77691222 --- /dev/null +++ b/src/common/AsideMenu.scss @@ -0,0 +1,15 @@ +@import "../utils/base"; + +.aside-menu { + position: fixed !important; + top: $headerHeight; + bottom: 0; + left: 0; + z-index: 1000; + display: block; + padding: 20px; + overflow-x: hidden; + overflow-y: auto; + background-color: #f7f7f7; + border-right: 1px solid #eee; +} diff --git a/src/common/MainHeader.js b/src/common/MainHeader.js index d8e8287b..8b511ac7 100644 --- a/src/common/MainHeader.js +++ b/src/common/MainHeader.js @@ -25,14 +25,14 @@ export default class MainHeader extends React.Component { render() { return ( - + Shlink Shlink this.toggle()}/>