Skip to content
This repository has been archived by the owner on Oct 11, 2022. It is now read-only.

Commit

Permalink
Merge pull request #4015 from withspectrum/2.4.42
Browse files Browse the repository at this point in the history
2.4.42
  • Loading branch information
brianlovin authored Oct 4, 2018
2 parents 09fc604 + 81a5cff commit 0e5c074
Show file tree
Hide file tree
Showing 82 changed files with 1,981 additions and 1,134 deletions.
10 changes: 5 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,21 @@ jobs:
at: ~/spectrum
- run: yarn run db:migrate
- run: yarn run db:seed
- run:
name: Run Unit Tests
command: yarn run test:ci
- run: *setup-and-build-web
- run: *build-api
- run: *start-api
- run: *start-web
# Wait for the API and webserver to start
- run: ./node_modules/.bin/wait-on http://localhost:3000 http://localhost:3001
- run:
name: Run Unit Tests
command: yarn run test:ci
name: Run E2E Tests
command: test $CYPRESS_RECORD_KEY && yarn run test:e2e -- --record || yarn run test:e2e
- run:
name: Build desktop apps
command: yarn run build:desktop
- run:
name: Run E2E Tests
command: test $CYPRESS_RECORD_KEY && yarn run test:e2e -- --record || yarn run test:e2e

deploy_alpha:
<<: *js_defaults
Expand Down
1 change: 0 additions & 1 deletion admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"apollo-link-http": "^1.3.2",
"apollo-link-ws": "^1.0.4",
"apollo-upload-client": "6.x",
"apollo-upload-server": "^2.0.4",
"apollo-utilities": "^1.0.4",
"d3-array": "^1.2.0",
"graphql": "^0.12.3",
Expand Down
119 changes: 119 additions & 0 deletions api/apollo-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// @flow
import { ApolloServer } from 'apollo-server-express';
import depthLimit from 'graphql-depth-limit';
import costAnalysis from 'graphql-cost-analysis';
import createLoaders from './loaders';
import createErrorFormatter from './utils/create-graphql-error-formatter';
import schema from './schema';
import { getUser, setUserOnline } from './models/user';
import { getUserIdFromReq } from './utils/session-store';
import UserError from './utils/UserError';
import type { DBUser } from 'shared/types';

// NOTE(@mxstbr): Evil hack to make graphql-cost-analysis work with Apollo Server v2
// @see pa-bru/graphql-cost-analysis#12
// @author @arianon
class ProtectedApolloServer extends ApolloServer {
async createGraphQLServerOptions(
req: express$Request,
res: express$Response
): Promise<*> {
const options = await super.createGraphQLServerOptions(req, res);

return {
...options,
validationRules: [
...options.validationRules,
costAnalysis({
maximumCost: 750,
defaultCost: 1,
variables: req.body.variables,
createError: (max, actual) => {
const err = new UserError(
`GraphQL query exceeds maximum complexity, please remove some nesting or fields and try again. (max: ${max}, actual: ${actual})`
);
return err;
},
}),
],
};
}
}

const server = new ProtectedApolloServer({
schema,
formatError: createErrorFormatter(),
// For subscriptions, this gets passed "connection", for everything else "req" and "res"
context: ({ req, res, connection, ...rest }, ...other) => {
if (connection) {
return {
...(connection.context || {}),
};
}

const loaders = createLoaders();
let currentUser = req.user && !req.user.bannedAt ? req.user : null;

return {
loaders,
updateCookieUserData: (data: DBUser) =>
new Promise((res, rej) =>
req.login(data, err => (err ? rej(err) : res()))
),
user: currentUser,
};
},
subscriptions: {
path: '/websocket',
onOperation: (_: any, params: Object) => {
const errorFormatter = createErrorFormatter();
params.formatError = errorFormatter;
return params;
},
onDisconnect: rawSocket => {
return getUserIdFromReq(rawSocket.upgradeReq)
.then(id => id && setUserOnline(id, false))
.catch(err => {
console.error(err);
});
},
onConnect: (connectionParams, rawSocket) =>
getUserIdFromReq(rawSocket.upgradeReq)
.then(id => (id ? setUserOnline(id, true) : null))
.then(user => {
return {
user: user || null,
loaders: createLoaders({ cache: false }),
};
})
.catch(err => {
console.error(err);
return {
loaders: createLoaders({ cache: false }),
};
}),
},
playground: {
settings: {
'editor.theme': 'light',
},
tabs: [
{
endpoint: 'http://localhost:3001/api',
query: `{
user(username: "mxstbr") {
id
username
}
}`,
},
],
},
maxFileSize: 25 * 1024 * 1024, // 25MB
engine: false,
tracing: true,
cacheControl: true,
validationRules: [depthLimit(10)],
});

export default server;
75 changes: 28 additions & 47 deletions api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,63 +14,51 @@ import toobusy from 'shared/middlewares/toobusy';
import addSecurityMiddleware from 'shared/middlewares/security';
import csrf from 'shared/middlewares/csrf';
import { init as initPassport } from './authentication.js';
import apolloServer from './apollo-server';
import { corsOptions } from 'shared/middlewares/cors';
import errorHandler from 'shared/middlewares/error-handler';
import middlewares from './routes/middlewares';
import authRoutes from './routes/auth';
import apiRoutes from './routes/api';
import type { DBUser } from 'shared/types';
import type { Loader } from './loaders/types';

export type GraphQLContext = {
user: DBUser,
updateCookieUserData: (data: DBUser) => Promise<void>,
loaders: {
[key: string]: Loader,
},
};

const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 3001;

// Initialize authentication
initPassport();

// API server
const app = express();

// Trust the now proxy
app.set('trust proxy', true);

// Return the request if the server is too busy
app.use(toobusy);

// Security middleware.
addSecurityMiddleware(app);

if (process.env.NODE_ENV === 'production' && !process.env.FORCE_DEV) {
app.use(csrf);
}

// Send all responses as gzip
// All other middlewares
app.use(compression());

import middlewares from './routes/middlewares';
app.use(middlewares);

import authRoutes from './routes/auth';
// Routes
app.use('/auth', authRoutes);

import apiRoutes from './routes/api';
app.use('/api', apiRoutes);

// $FlowIssue
app.use(
(
err: Error,
req: express$Request,
res: express$Response,
next: express$NextFunction
) => {
if (err) {
console.error(err);
res
.status(500)
.send(
'Oops, something went wrong! Our engineers have been alerted and will fix this asap.'
);
Raven.captureException(err);
} else {
return next();
}
}
);
// GraphQL middleware
apolloServer.applyMiddleware({ app, path: '/api', cors: corsOptions });

// Redirect a request to the root path to the main app
app.use('/', (req: express$Request, res: express$Response) => {
res.redirect(
process.env.NODE_ENV === 'production' && !process.env.FORCE_DEV
Expand All @@ -79,20 +67,12 @@ app.use('/', (req: express$Request, res: express$Response) => {
);
});

import type { Loader } from './loaders/types';
export type GraphQLContext = {
user: DBUser,
updateCookieUserData: (data: DBUser) => Promise<void>,
loaders: {
[key: string]: Loader,
},
};

const server = createServer(app);
// $FlowIssue
app.use(errorHandler);

// Create subscriptions server at /websocket
import createSubscriptionsServer from './routes/create-subscription-server';
const subscriptionsServer = createSubscriptionsServer(server, '/websocket');
// We need to create a separate HTTP server to handle GraphQL subscriptions via websockets
const httpServer = createServer(app);
apolloServer.installSubscriptionHandlers(httpServer);

// Start API wrapped in Apollo Engine
const engine = new ApolloEngine({
Expand All @@ -119,10 +99,11 @@ const engine = new ApolloEngine({

engine.listen({
port: PORT,
httpServer: server,
httpServer: httpServer,
graphqlPaths: ['/api'],
});
debug(`GraphQL server running at http://localhost:${PORT}/api`);

debug(`GraphQL API running at http://localhost:${PORT}/api`);

process.on('unhandledRejection', async err => {
console.error('Unhandled rejection', err);
Expand Down
8 changes: 4 additions & 4 deletions api/loaders/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ export const __createChannelThreadCountLoader = createLoader(
'group'
);

export const __createChannelMemberCountLoader = createLoader(
channels => getChannelsMemberCounts(channels),
export const __createChannelPendingMembersLoader = createLoader(
channels => getPendingUsersInChannels(channels),
'group'
);

export const __createChannelPendingMembersLoader = createLoader(
channels => getPendingUsersInChannels(channels),
export const __createChannelMemberCountLoader = createLoader(
channels => getChannelsMemberCounts(channels),
'group'
);

Expand Down
2 changes: 1 addition & 1 deletion api/loaders/community.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import {
getCommunities,
getCommunitiesBySlug,
getCommunitiesMemberCounts,
getCommunitiesChannelCounts,
getCommunitiesOnlineMemberCounts,
getCommunitiesMemberCounts,
} from '../models/community';
import { getCommunitiesSettings } from '../models/communitySettings';
import createLoader from './create-loader';
Expand Down
4 changes: 2 additions & 2 deletions api/loaders/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ import {
import { __createNotificationLoader } from './notification';
import {
__createChannelLoader,
__createChannelMemberCountLoader,
__createChannelThreadCountLoader,
__createChannelMemberCountLoader,
__createChannelPendingMembersLoader,
__createChannelSettingsLoader,
} from './channel';
import {
__createCommunityLoader,
__createCommunityBySlugLoader,
__createCommunityMemberCountLoader,
__createCommunityChannelCountLoader,
__createCommunitySettingsLoader,
__createCommunityMemberCountLoader,
__createCommunityOnlineMemberCountLoader,
} from './community';
import {
Expand Down
2 changes: 1 addition & 1 deletion api/loaders/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

export type Loader = {
load: (key: string | Array<string>) => Promise<any>,
loadMany: (keys: Array<string>) => Promise<any>,
loadMany: (keys: Array<*>) => Promise<any>,
clear: (key: string | Array<string>) => void,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
exports.up = function(r, conn) {
return Promise.all([
r
.table('communities')
.update(
{
memberCount: r
.table('usersCommunities')
.getAll(r.row('id'), { index: 'communityId' })
.filter(row => row('isMember').eq(true))
.count()
.default(1),
},
{
nonAtomic: true,
}
)
.run(conn),
r
.table('channels')
.update(
{
memberCount: r
.table('usersChannels')
.getAll(r.row('id'), { index: 'channelId' })
.filter(row => row('isMember').eq(true))
.count()
.default(1),
},
{
nonAtomic: true,
}
)
.run(conn),
]).catch(err => console.error(err));
};
exports.down = function(r, conn) {
return Promise.all([
r
.table('communities')
.update({
memberCount: r.literal(),
})
.run(conn),
r
.table('channels')
.update({
memberCount: r.literal(),
})
.run(conn),
]);
};
Loading

0 comments on commit 0e5c074

Please sign in to comment.