diff --git a/package.json b/package.json index 049914c2..78f27f80 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "watchify": "^3.7.0" }, "dependencies": { + "api-js": "github:foxcomm/api-js#v0.2.1", "classnames": "^2.2.3", "core-decorators": "^0.11.0", "currency-symbol-map": "^2.2.0", diff --git a/src/components/auth/auth.jsx b/src/components/auth/auth.jsx index 9b92d1de..dbbd40c6 100644 --- a/src/components/auth/auth.jsx +++ b/src/components/auth/auth.jsx @@ -3,7 +3,7 @@ import React, { Component } from 'react'; import { Link } from 'react-router'; import { authBlockTypes } from 'paragons/auth'; -import { assoc } from 'sprout-data'; +import { assoc, dissoc } from 'sprout-data'; import { autobind } from 'core-decorators'; import styles from './auth.css'; @@ -43,8 +43,8 @@ class Auth extends Component { } @autobind - getPath(newType: string): Object { - return assoc(this.props.path, ['query', 'auth'], newType); + getPath(newType: ?string): Object { + return newType ? assoc(this.props.path, ['query', 'auth'], newType) : dissoc(this.props.path, ['query', 'auth']); } render(): HTMLElement { diff --git a/src/components/auth/login.jsx b/src/components/auth/login.jsx index f29b9c42..bc960e09 100644 --- a/src/components/auth/login.jsx +++ b/src/components/auth/login.jsx @@ -73,7 +73,7 @@ class Login extends Component { this.props.authenticate({email, password, kind}).then(() => { this.props.fetchCart(); browserHistory.push(this.props.getPath()); - }).catch(() => { + }, () => { this.setState({error: 'Email or password is invalid'}); }); } diff --git a/src/core/lib/api.js b/src/core/lib/api.js index 1e70513d..5abf2ea3 100644 --- a/src/core/lib/api.js +++ b/src/core/lib/api.js @@ -1,174 +1,8 @@ - /* @flow weak */ -import fetch from 'isomorphic-fetch'; -import type { Promise as PromiseType } from 'types/promise'; - +import Api from 'api-js'; const isServer: boolean = typeof self === 'undefined'; -export function appendQueryString(url: string, queryString: ?string): string { - if (!queryString) { - return url; - } - const joinWith = url.indexOf('?') != -1 ? '&' : '?'; - - return `${url}${joinWith}${queryString}`; -} - -type Params = { - [key: string]: string|Object|number|boolean; -}; - -function serialize(data: Params): string { - const params: string[] = []; - for (const param in data) { - if (data.hasOwnProperty(param)) { - const value = data[param]; - if (value != null) { - const asString: string = typeof value != 'string' ? JSON.stringify(value) : value; - params.push(`${encodeURIComponent(param)}'='${encodeURIComponent(asString)}`); - } - } - } - return params.join('&'); -} - -// Removing this will BREAK Demo Storefront because we use -// basic auth to protect access to the frontend since it is publically -// accessible -function demoAuthHeader(): string|void { - const demoToken = process.env.DEMO_AUTH_TOKEN; - return demoToken ? `Basic ${demoToken}` : void 0; -} - -type RequestOptions = { - method?: string; - headers?: {[key: string]: string|void}; - body?: string; -}; - -type ResponseError = { - response: any; - responseJson: any; -}; - -/* eslint-disable no-param-reassign */ - -export function request(method: string, uri: string, data: ?Params, options: ?RequestOptions): PromiseType { - const defaultHeaders = { - 'Content-Type': 'application/json;charset=UTF-8', - Authorization: demoAuthHeader(), - }; - - options = { - ...options || {}, - credentials: 'same-origin', - method: method.toUpperCase(), - headers: { - ...defaultHeaders, - ...(options && options.headers || {}), - }, - }; - - if (data) { - if (method.toUpperCase() === 'GET') { - const queryString = serialize(data); - if (queryString) { - uri = appendQueryString(uri, queryString); - } - } else { - options.body = JSON.stringify(data); - } - } - - // $FlowFixMe - let error: ?ResponseError = null; - - return fetch(uri, options) - .then(response => { - if (response.status < 200 || response.status >= 300) { - error = new Error(response.statusText); - // $FlowFixMe - error.response = response; - } - - return response; - }) - .then(response => response.text()) - .then(responseText => { - let json = null; - if (responseText) { - try { - json = JSON.parse(responseText); - } catch (ex) { - // invalid json - } - } - - if (error) { - error.responseJson = json; - throw error; - } - - return json; - }); -} - -/* eslint-enable no-param-reassign */ - - -class Api { - prefix: string; - headers: ?Object; - - constructor(prefix: ?string = '') { - // $FlowFixMe: flow why you don't count default values - this.prefix = prefix; - } - - addHeaders(headers) { - this.headers = headers; - - return this; - } - - addAuth(jwt) { - this.headers = { - ...this.headers, - JWT: jwt, - }; - - return this; - } - - request(method: string, uri: string, data: ?Params, options: RequestOptions = {}): PromiseType { - const finalUrl = `${this.prefix}${uri}`; - - if (this.headers) { - options.headers = { // eslint-disable-line no-param-reassign - ...this.headers, - ...(options.headers || {}), - }; - } - - return request(method, finalUrl, data, options); - } - - get(...args: Array): PromiseType { - return this.request('get', ...args); - } - - post(...args: Array): PromiseType { - return this.request('post', ...args); - } - - patch(...args: Array): PromiseType { - return this.request('patch', ...args); - } - - delete(...args: Array): PromiseType { - return this.request('delete', ...args); - } -} - -export const api = new Api(isServer ? `${process.env.API_URL}/api` : '/api'); +export const api = new Api({ + api_url: isServer ? `${process.env.API_URL}/api` : '/api', +}); diff --git a/src/core/modules/auth.js b/src/core/modules/auth.js index e23e5d02..d7cb7e41 100644 --- a/src/core/modules/auth.js +++ b/src/core/modules/auth.js @@ -2,7 +2,6 @@ import { createAction, createReducer } from 'redux-act'; import createAsyncActions from './async-utils'; -import fetch from 'isomorphic-fetch'; import { dissoc } from 'sprout-data'; import { api } from 'lib/api'; @@ -26,56 +25,36 @@ export const setUser = createAction('USER_SET'); export const removeUser = createAction('REMOVE_USER'); export const setJwt = createAction('AUTH_SET_JWT'); -const headers = {'Content-Type': 'application/json;charset=UTF-8'}; - export const signUp = createAsyncActions('auth-signup', function signUp(payload: SignUpPayload): Promise { - return api.post('/v1/public/registrations/new', payload); + const {email, name, password} = payload; + return api.auth.signup(email, name, password); }).perform; export const authenticate = createAsyncActions('auth-login', function authenticate(payload: LoginPayload): Promise { - return fetch('/api/v1/public/login', { - method: 'POST', - body: JSON.stringify(payload), - credentials: 'same-origin', - headers, - }) - .then(response => { - const jwt = response.headers.get('jwt'); - if (response.status == 200 && jwt) { - localStorage.setItem('jwt', jwt); - this.dispatch(setJwt(jwt)); - return response.json(); - } - throw new Error('Server error, try again later. Sorry for inconvenience :('); - }) - .then((token) => { - if (token.email && token.name) { - localStorage.setItem('user', JSON.stringify(token)); - this.dispatch(setUser(token)); - return; - } - throw new Error('Server error, try again later. Sorry for inconvenience :('); + const {email, password, kind} = payload; + return api.auth.login(email, password, kind) + .then(({jwt, user}) => { + this.dispatch(setJwt(jwt)); + this.dispatch(setUser(user)); }); }).perform; export function googleSignin(): asyncAction { return () => { - api.get('/v1/public/signin/google/customer').then(urlInfo => { + api.auth.googleSignin().then(urlInfo => { window.location.href = urlInfo.url; }); }; } export const logout = createAsyncActions('auth-logout', function logout(): Promise { - return api.post('/v1/public/logout') + return api.auth.logout() .then(() => { this.dispatch(removeUser()); - localStorage.removeItem('user'); }); }).perform; const initialState = { - inProgress: false, }; const reducer = createReducer({