Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cashay #64

Merged
merged 5 commits into from Jun 16, 2016
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 13 additions & 14 deletions package.json
Expand Up @@ -19,8 +19,8 @@
"build": "rimraf build && concurrently \"npm run build:client\" \"npm run build:server\"",
"bs": "rimraf build && concurrently \"npm run build:client\" \"npm run build:server\" \"npm run start\"",
"quickstart": "rimraf build && concurrently \"npm run db:migrate\" \"npm run build:client\" \"npm run build:server\" \"npm run start\"",
"build:client": "NODE_ENV=production webpack -p --config ./webpack/prod.babel.js",
"build:server": "npm run build:theme && NODE_ENV=production webpack -p --config ./webpack/server.babel.js",
"build:client": "NODE_ENV=production webpack --config ./webpack/prod.babel.js",
"build:server": "npm run build:theme && NODE_ENV=production webpack --config ./webpack/server.babel.js",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these slow me down! ditching uglify makes dev much more fun. maybe we should write a separate deploy script?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use npm run dev and leave build:client and build:server alone?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i use npm run bs when working on the server. that way, i don't need to rebuild the client for every change i make to the server.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see! Seems like adding build:deploy might be the way to go then.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I can do that later)

"build:theme": "NODE_ENV=development mkdir -p build && babel-node ./src/universal/utils/buildThemeJSON.js > ./build/theme.json",
"db:migrate": "./src/server/database/migrate.sh up-all",
"test": "NODE_ENV=testing ava ./src/**/__tests__/**/*-tests.js --verbose",
Expand All @@ -34,8 +34,8 @@
"pre-commit": [],
"devDependencies": {
"assets-webpack-plugin": "3.4.0",
"ava": "0.15.1",
"babel-cli": "6.9.0",
"ava": "0.15.2",
"babel-cli": "6.10.1",
"babel-core": "6.9.1",
"babel-eslint": "^6.0.4",
"babel-loader": "6.2.4",
Expand All @@ -48,9 +48,9 @@
"babel-preset-stage-0": "6.5.0",
"babel-register": "6.9.0",
"concurrently": "2.1.0",
"eslint": "^2.11.1",
"eslint": "^2.12.0",
"eslint-config-airbnb": "^9.0.1",
"eslint-config-xo-react": "0.7.0",
"eslint-config-xo-react": "0.8.0",
"eslint-plugin-import": "^1.8.1",
"eslint-plugin-react": "5.1.1",
"file-loader": "0.8.5",
Expand All @@ -72,12 +72,12 @@
"webpack-hot-middleware": "^2.10.0"
},
"dependencies": {
"auth0": "^2.1.0",
"auth0": "^2.3.0",
"auth0-lock": "^9.2.1",
"babel-polyfill": "6.9.1",
"babel-runtime": "6.9.2",
"body-parser": "1.15.1",
"cashay": "^0.5.11",
"cashay": "^0.8.1",
"compression": "1.6.2",
"cors": "2.7.1",
"dotenv": "^2.0.0",
Expand All @@ -87,16 +87,16 @@
"express": "4.13.4",
"express-jwt": "3.4.0",
"font-awesome": "^4.6.3",
"graphiql": "0.7.1",
"graphiql": "0.7.2",
"graphql": "0.6.0",
"immutable": "3.8.1",
"isomorphic-fetch": "2.2.1",
"jsonwebtoken": "7.0.0",
"jsonwebtoken": "7.0.1",
"jwt-decode": "^2.0.1",
"mailgun-js": "^0.7.11",
"node-env-flag": "0.1.0",
"node-uuid": "1.4.7",
"piping": "1.0.0-rc.1",
"piping": "1.0.0-rc.2",
"react": "15.1.0",
"react-copy-to-clipboard": "^4.1.0",
"react-css-modules": "^3.7.6",
Expand All @@ -108,16 +108,15 @@
"react-notification-system": "^0.2.7",
"react-redux": "4.4.5",
"react-router": "2.4.1",
"react-router-redux": "4.0.4",
"react-router-redux": "4.0.5",
"redux": "3.5.2",
"redux-immutablejs": "0.0.8",
"redux-logger": "2.6.1",
"redux-optimistic-ui": "0.4.0",
"redux-promise": "0.5.3",
"redux-socket-cluster": "0.5.1",
"redux-thunk": "2.1.0",
"rethink-migrate": "^1.3.1",
"rethinkdbdash": "2.3.6",
"rethinkdbdash": "2.3.8",
"serve-favicon": "^2.3.0",
"socketcluster": "^4.6.2",
"socketcluster-client": "^4.3.18",
Expand Down
15 changes: 15 additions & 0 deletions src/client/ActionHTTPTransport.js
@@ -0,0 +1,15 @@
import {HTTPTransport} from 'cashay';
import {getGraphQLUri} from 'universal/utils/graphQLConfig';

export default class ActionHTTPTransport extends HTTPTransport {
constructor(authToken) {
super();
this.uri = getGraphQLUri();
this.init = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${authToken}`
}
};
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

3 changes: 1 addition & 2 deletions src/client/Root.js
Expand Up @@ -4,7 +4,6 @@ import {Router, browserHistory} from 'react-router';
import {Provider} from 'react-redux';
import routes from '../universal/routes/index';
import {syncHistoryWithStore} from 'react-router-redux';
import {ensureState} from 'redux-optimistic-ui';

const lookConfig = Presets['react-dom'];
lookConfig.styleElementId = '_look';
Expand All @@ -16,7 +15,7 @@ if (!__PRODUCTION__) {
export default function Root({store}) {
const history = syncHistoryWithStore(
browserHistory, store,
{selectLocationState: state => ensureState(state).get('routing')}
{selectLocationState: state => state.get('routing')}
);

return (
Expand Down
36 changes: 16 additions & 20 deletions src/client/client.js
@@ -1,11 +1,11 @@
import {render} from 'react-dom';
import React from 'react';
import { AppContainer } from 'react-hot-loader';
import {AppContainer} from 'react-hot-loader';
import {Map as iMap, fromJS} from 'immutable';
import {Cashay, HTTPTransport} from 'cashay';
import {cashay} from 'cashay';
import ActionHTTPTransport from './ActionHTTPTransport';
import makeStore from './makeStore';
import Root from './Root';
import {getGraphQLUri} from 'universal/utils/graphQLConfig';
import {localStorageVars} from 'universal/utils/clientOptions';

const {auth, routing, form} = window.__INITIAL_STATE__; // eslint-disable-line no-underscore-dangle
Expand All @@ -17,35 +17,31 @@ const initialState = iMap([
['form', form]
]);

// const authToken = localStorage.getItem(authTokenName);

if (authToken) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can clean up here or in next PR (likely tomorrow)


}
// Create the store:
const store = makeStore(initialState);

// Create the Cashay singleton:
const cashaySchema = require('cashay!../server/utils/getCashaySchema.js');
const authToken = localStorage.getItem(localStorageVars.authTokenName);
const cashayHttpTransport = new HTTPTransport(
getGraphQLUri(),
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${authToken}`
}
}
);
const cashayParams = {

const cashayHttpTransport = new ActionHTTPTransport(authToken);

cashay.create({
store,
schema: cashaySchema,
getToState: reduxStore => reduxStore.getState().get('cashay'),
transport: cashayHttpTransport
};

// export the Cashay singleton:
export const cashay = new Cashay(cashayParams);
});


render(
<AppContainer>
<Root store={store} />
<Root store={store}/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style note: I like the space before /. For me, makes it clearer to see. You opposed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Webstorm doesn't have that rule specific to self-closing tags, so the space would apply to everything eg <AppContainer >. Is that OK?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I promise you, imma going to give Webstorm a try one of these days. We'll use your style (no space) and I'll rely on the linter more heavily than my 👀

</AppContainer>,
document.getElementById('root')
);
Expand All @@ -57,10 +53,10 @@ if (module.hot) {
const Root = require('./Root');
render(
<AppContainer>
<Root store={store} />
<Root store={store}/>
</AppContainer>,
document.getElementById('root')
);
/* eslint-enable global-require */
/* eslint-enable global-require */
});
}
2 changes: 0 additions & 2 deletions src/client/makeStore.js
@@ -1,6 +1,5 @@
import {createStore, applyMiddleware, compose} from 'redux';
import thunkMiddleware from 'redux-thunk';
import optimisticMiddleware from '../universal/redux/middleware/optimisticMiddleware';
import {routerMiddleware} from 'react-router-redux';
import {browserHistory} from 'react-router';
import makeReducer from '../universal/redux/makeReducer';
Expand All @@ -11,7 +10,6 @@ export default initialState => {
const reduxRouterMiddleware = routerMiddleware(browserHistory);
const middlewares = [
reduxRouterMiddleware,
optimisticMiddleware,
thunkMiddleware
];

Expand Down
36 changes: 20 additions & 16 deletions src/server/graphql/models/CachedUser/cachedUserMutation.js
@@ -1,10 +1,10 @@
import r from '../../../database/rethinkDriver';
import { GraphQLString } from 'graphql';
import { CachedUser } from './cachedUserSchema';
import { AuthenticationClient } from 'auth0';
import { auth0 } from '../../../../universal/utils/clientOptions';
import { triggerNewUserEmail } from './helpers';
import { createUserProfile } from '../UserProfile/helpers';
import {GraphQLString} from 'graphql';
import {CachedUserAndToken} from './cachedUserSchema';
import {AuthenticationClient} from 'auth0';
import {auth0} from '../../../../universal/utils/clientOptions';
import {triggerNewUserEmail} from './helpers';
import {createUserProfile} from '../UserProfile/helpers';

// TODO this stuff is no good, we need the good server stuff so we don't 401
const auth0Client = new AuthenticationClient({
Expand All @@ -13,16 +13,16 @@ const auth0Client = new AuthenticationClient({
});

export default {
updateUserWithIdToken: {
type: CachedUser,
updateUserWithAuthToken: {
type: CachedUserAndToken,
args: {
idToken: {
authToken: {
type: GraphQLString,
description: 'The ID Token from auth0, a base64 JWT'
}
},
async resolve(source, {idToken}) {
const userInfo = await auth0Client.tokens.getInfo(idToken);
async resolve(source, {authToken}) {
const userInfo = await auth0Client.tokens.getInfo(authToken);
// TODO add the userId to the JWT to eliminate call to DB?
// JWT.sub is the userId, not id, maybe it'll do
// TODO loginsCount and blockedFor are not a part of this API response
Expand All @@ -41,28 +41,32 @@ export default {
picture: userInfo.picture,
name: userInfo.name,
nickname: userInfo.nickname,
identities: userInfo.identities,
identities: userInfo.identities || [],
loginsCount: userInfo.logins_count,
blockedFor: userInfo.blocked_for
blockedFor: userInfo.blocked_for || []
};
const newUserAndToken = {
user: newUserObj,
authToken
};
const changes = await r.table('CachedUser').insert(newUserObj, {
conflict: 'update',
returnChanges: true
});
// Did we update an existing cached profile?
if (changes.replaced > 0) {
return newUserObj;
return newUserAndToken;
}
// Let's make a new user profile object and link it to the CachedUser:
const userProfileId = await createUserProfile();
await r.table('CachedUser')
.get(changes.generated_keys[0])
.update({ userProfileId });
.update({userProfileId});
newUserObj.userProfileId = userProfileId;

await triggerNewUserEmail(newUserObj);

return newUserObj;
return newUserAndToken;
}
}
};
23 changes: 21 additions & 2 deletions src/server/graphql/models/CachedUser/cachedUserQuery.js
@@ -1,5 +1,5 @@
import {GraphQLNonNull, GraphQLID} from 'graphql';
import {CachedUser} from './cachedUserSchema';
import {GraphQLNonNull, GraphQLID, GraphQLString} from 'graphql';
import {CachedUser, CachedUserAndToken} from './cachedUserSchema';
import {getUserByUserId} from './helpers';
import {errorObj} from '../utils';

Expand All @@ -19,5 +19,24 @@ export default {
}
return user;
}
},
getUserWithAuthToken: {
type: CachedUserAndToken,
args: {
authToken: {
type: new GraphQLNonNull(GraphQLString),
description: 'The ID Token from auth0, a base64 JWT'
}
},
async resolve(source, args, {authToken}) {
const user = await getUserByUserId(authToken.sub);
if (!user) {
throw errorObj({_error: 'User ID not found'});
}
return {
user,
authToken: args.authToken
};
}
}
};
17 changes: 16 additions & 1 deletion src/server/graphql/models/CachedUser/cachedUserSchema.js
Expand Up @@ -21,7 +21,7 @@ const IdentityType = new GraphQLObjectType({
},
userId: {
type: GraphQLID,
description: 'The unique identifier for the user for the identity.',
description: 'The unique identifier for the user for the identity.'
},
provider: {
type: GraphQLString,
Expand Down Expand Up @@ -113,6 +113,21 @@ export const CachedUser = new GraphQLObjectType({
userProfile: {
type: new GraphQLNonNull(UserProfile),
description: 'The associated user profile, stored locally in our database.'
}
})
});

export const CachedUserAndToken = new GraphQLObjectType({
name: 'CachedUserAndToken',
description: 'The user account profile + JWT',
fields: () => ({
user: {
type: CachedUser,
description: 'The user account profile'
},
authToken: {
type: GraphQLString,
description: 'The JWT that comes from auth0'
}
})
});
2 changes: 1 addition & 1 deletion src/server/graphql/models/utils.js
Expand Up @@ -24,7 +24,7 @@ export const prepareClientError = res => {
}
const error = errors[0].message;
if (error && error.indexOf('{"_error"') === -1) {
console.log('DEBUG GraphQL Error:', error);
console.log('DEBUG PARSING GraphQL Error:', error);
return {data, error: JSON.stringify({_error: 'Server error while querying data'})};
}
return {data, error};
Expand Down
2 changes: 2 additions & 0 deletions src/server/utils/getCashaySchema.js
Expand Up @@ -3,4 +3,6 @@ require('babel-polyfill');
const {transformSchema} = require('cashay');
const graphql = require('graphql').graphql;
const rootSchema = require('../graphql/rootSchema');
const r = require('../database/rethinkDriver');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran into trouble here when using npm run dev. Each time the piping watch ran, it would run cashay-loader, evaluate this getCashaySchema.js and then drain the rethink pool. Made for much sadness. We'll probably have to introduce a webpack flag/contant. 😩

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can comment out the const r... like we do the line below it. perhaps we should think of a way to use cashay-loader inside the webpack config itself?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll think about it post merge…

Sent with Mixmax

On Wed, Jun 15, 2016 6:55 PM, Matt Krick notifications@github.com wrote:
In src/server/utils/getCashaySchema.js :





we can comment out the const r... like we do the line below it. perhaps we should think of a way to use
cashay-loader inside the webpack config itself?


You are receiving this because you commented.
Reply to this email directly, view it on GitHub , or mute the thread .

Jordan @jrhusney / 612.227.5673

// r.getPoolMaster().drain();
module.exports = transformSchema(rootSchema, graphql);
4 changes: 0 additions & 4 deletions src/server/utils/rethinkExit.js

This file was deleted.

Expand Up @@ -7,11 +7,9 @@ import Notifications
from 'universal/modules/notifications/containers/notifications/Notifications';
import './addFontAwesome';

let styles = {};

@look
// eslint-disable-next-line react/prefer-stateless-function
export default class App extends Component {
export default class Action extends Component {
static propTypes = {
children: PropTypes.any
};
Expand All @@ -26,7 +24,7 @@ export default class App extends Component {
}
}

styles = StyleSheet.create({
const styles = StyleSheet.create({
app: {
height: '100vh',
margin: 0,
Expand Down