From d2b263a6c13d7650a237a44ccaaf41fa6a6daf26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Tue, 28 Aug 2018 22:19:32 -0400 Subject: [PATCH] withAuth -> withUser, fixes #33 --- src/components/App.js | 23 +++------------ src/components/CurrentUser.js | 24 ++++++++-------- src/components/Profile.js | 13 +++++---- src/components/Reviews.js | 8 ++++-- src/index.js | 54 ++--------------------------------- src/lib/apollo.js | 51 +++++++++++++++++++++++++++++++++ src/lib/auth.js | 48 +++++++++++++++++++++++++++++++ src/lib/withUser.js | 25 ++++++++++++++++ 8 files changed, 155 insertions(+), 91 deletions(-) create mode 100644 src/lib/apollo.js create mode 100644 src/lib/auth.js create mode 100644 src/lib/withUser.js diff --git a/src/components/App.js b/src/components/App.js index f330c41..4fa2b31 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -1,7 +1,6 @@ import React, { Component } from 'react' import { Switch, Route, Redirect } from 'react-router' import { Link } from 'react-router-dom' -import PropTypes from 'prop-types' import logo from '../logo.svg' import StarCount from './StarCount' @@ -9,7 +8,6 @@ import TableOfContents from './TableOfContents' import Section from './Section' import CurrentUser from './CurrentUser' import Profile from './Profile' -import withAuth from '../lib/withAuth' import Reviews from './Reviews' const Book = ({ user }) => ( @@ -24,8 +22,6 @@ const Book = ({ user }) => ( class App extends Component { render() { - const { logout, ...authProps } = this.props - return (
@@ -34,27 +30,16 @@ class App extends Component { logo

The GraphQL Guide

- +
} /> - } - /> - } /> + +
) } } -App.propTypes = { - user: PropTypes.object, - login: PropTypes.func.isRequired, - logout: PropTypes.func.isRequired, - loading: PropTypes.bool.isRequired -} - -export default withAuth(App) +export default App diff --git a/src/components/CurrentUser.js b/src/components/CurrentUser.js index 94ebfdf..dfb69f0 100644 --- a/src/components/CurrentUser.js +++ b/src/components/CurrentUser.js @@ -2,19 +2,20 @@ import React from 'react' import PropTypes from 'prop-types' import { Link } from 'react-router-dom' -const CurrentUser = ({ user, login, loading }) => { - const User = () => ( - - {user.firstName} - {user.firstName} - - ) +import { withUser } from '../lib/withUser' +import { login } from '../lib/auth' +const CurrentUser = ({ user, loggingIn }) => { let content if (user) { - content = - } else if (loading) { + content = ( + + {user.firstName} + {user.firstName} + + ) + } else if (loggingIn) { content =
} else { content = @@ -28,8 +29,7 @@ CurrentUser.propTypes = { firstName: PropTypes.string.isRequired, photo: PropTypes.string.isRequired }), - login: PropTypes.func.isRequired, - loading: PropTypes.bool.isRequired + loggingIn: PropTypes.bool.isRequired } -export default CurrentUser +export default withUser(CurrentUser) diff --git a/src/components/Profile.js b/src/components/Profile.js index 4cbe616..8db29d8 100644 --- a/src/components/Profile.js +++ b/src/components/Profile.js @@ -1,8 +1,11 @@ import React from 'react' import PropTypes from 'prop-types' -const Profile = ({ user, login, logout, loading }) => { - if (loading) { +import { withUser } from '../lib/withUser' +import { login, logout } from '../lib/auth' + +const Profile = ({ user, loggingIn }) => { + if (loggingIn) { return (
@@ -63,9 +66,7 @@ Profile.propTypes = { email: PropTypes.string.isRequired, hasPurchased: PropTypes.string }), - login: PropTypes.func.isRequired, - logout: PropTypes.func.isRequired, - loading: PropTypes.bool.isRequired + loggingIn: PropTypes.bool.isRequired } -export default Profile +export default withUser(Profile) diff --git a/src/components/Reviews.js b/src/components/Reviews.js index b4f3abc..38af18c 100644 --- a/src/components/Reviews.js +++ b/src/components/Reviews.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import { graphql } from 'react-apollo' +import { graphql, compose } from 'react-apollo' import get from 'lodash/get' import FavoriteIcon from 'material-ui-icons/Favorite' import Button from 'material-ui/Button' @@ -9,6 +9,7 @@ import Modal from 'material-ui/Modal' import { propType } from 'graphql-anywhere' import Review from './Review' +import { withUser } from '../lib/withUser' import AddReview from './AddReview' import { REVIEWS_QUERY, REVIEW_ENTRY } from '../graphql/Review' @@ -91,4 +92,7 @@ const withReviews = graphql(REVIEWS_QUERY, { props: ({ data: { reviews, loading } }) => ({ reviews, loading }) }) -export default withReviews(Reviews) +export default compose( + withReviews, + withUser +)(Reviews) diff --git a/src/index.js b/src/index.js index 186804a..5e89560 100644 --- a/src/index.js +++ b/src/index.js @@ -1,63 +1,13 @@ import React from 'react' import ReactDOM from 'react-dom' -import { ApolloClient } from 'apollo-client' import { ApolloProvider } from 'react-apollo' -import { InMemoryCache } from 'apollo-cache-inmemory' -import { split } from 'apollo-link' -import { WebSocketLink } from 'apollo-link-ws' -import { createHttpLink } from 'apollo-link-http' -import { getMainDefinition } from 'apollo-utilities' import { BrowserRouter } from 'react-router-dom' -import { setContext } from 'apollo-link-context' -import { getAuthToken } from 'auth0-helpers' import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles' import './index.css' import registerServiceWorker from './registerServiceWorker' import App from './components/App' - -const httpLink = createHttpLink({ - uri: 'https://api.graphql.guide/graphql' -}) - -const authLink = setContext(async (_, { headers }) => { - const token = await getAuthToken({ - doLoginIfTokenExpired: true - }) - - if (token) { - return { - headers: { - ...headers, - authorization: `Bearer ${token}` - } - } - } else { - return { headers } - } -}) - -const authedHttpLink = authLink.concat(httpLink) - -const wsLink = new WebSocketLink({ - uri: `wss://api.graphql.guide/subscriptions`, - options: { - reconnect: true - } -}) - -const link = split( - ({ query }) => { - const { kind, operation } = getMainDefinition(query) - return kind === 'OperationDefinition' && operation === 'subscription' - }, - wsLink, - authedHttpLink -) - -const cache = new InMemoryCache() - -const client = new ApolloClient({ link, cache }) +import { apollo } from './lib/apollo' const GRAPHQL_PINK = '#e10098' @@ -67,7 +17,7 @@ const theme = createMuiTheme({ ReactDOM.render( - + diff --git a/src/lib/apollo.js b/src/lib/apollo.js new file mode 100644 index 0000000..5b73c55 --- /dev/null +++ b/src/lib/apollo.js @@ -0,0 +1,51 @@ +import { ApolloClient } from 'apollo-client' +import { InMemoryCache } from 'apollo-cache-inmemory' +import { split } from 'apollo-link' +import { WebSocketLink } from 'apollo-link-ws' +import { createHttpLink } from 'apollo-link-http' +import { getMainDefinition } from 'apollo-utilities' +import { setContext } from 'apollo-link-context' +import { getAuthToken } from 'auth0-helpers' + +const httpLink = createHttpLink({ + uri: 'https://api.graphql.guide/graphql' +}) + +const authLink = setContext(async (_, { headers }) => { + const token = await getAuthToken({ + doLoginIfTokenExpired: true + }) + + if (token) { + return { + headers: { + ...headers, + authorization: `Bearer ${token}` + } + } + } else { + return { headers } + } +}) + +const authedHttpLink = authLink.concat(httpLink) + +const wsLink = new WebSocketLink({ + uri: `wss://api.graphql.guide/subscriptions`, + options: { + reconnect: true + } +}) + +const link = split( + ({ query }) => { + const { kind, operation } = getMainDefinition(query) + return kind === 'OperationDefinition' && operation === 'subscription' + }, + wsLink, + authedHttpLink +) + +const cache = new InMemoryCache() + +export const apollo = new ApolloClient({ link, cache }) diff --git a/src/lib/auth.js b/src/lib/auth.js new file mode 100644 index 0000000..2ac23f4 --- /dev/null +++ b/src/lib/auth.js @@ -0,0 +1,48 @@ +import auth0 from 'auth0-js' +import { + initAuthHelpers, + login as auth0Login, + logout as auth0Logout +} from 'auth0-helpers' + +import { apollo } from './apollo' + +const client = new auth0.WebAuth({ + domain: 'graphql.auth0.com', + clientID: '8fErnZoF3hbzQ2AbMYu5xcS0aVNzQ0PC', + responseType: 'token', + audience: 'https://api.graphql.guide', + scope: 'openid profile guide' +}) + +initAuthHelpers({ + client, + usePopup: true, + authOptions: { + connection: 'github', + owp: true, + popupOptions: { height: 623 } // make tall enough for content + }, + checkSessionOptions: { + redirect_uri: window.location.origin + }, + onError: e => console.error(e) +}) + +export const login = () => { + auth0Login({ + onCompleted: e => { + if (e) { + console.error(e) + return + } + + apollo.reFetchObservableQueries() + } + }) +} + +export const logout = () => { + auth0Logout() + apollo.resetStore() +} diff --git a/src/lib/withUser.js b/src/lib/withUser.js new file mode 100644 index 0000000..0bf5587 --- /dev/null +++ b/src/lib/withUser.js @@ -0,0 +1,25 @@ +import { graphql } from 'react-apollo' +import gql from 'graphql-tag' + +export const USER_QUERY = gql` + query UserQuery { + currentUser { + firstName + name + username + email + photo + hasPurchased + favoriteReviews { + id + } + } + } +` + +export const withUser = graphql(USER_QUERY, { + props: ({ data: { currentUser, loading } }) => ({ + user: currentUser, + loggingIn: loading + }) +})