From 55c3ac246a97a4a57ac9fd679b30791248cd42fd Mon Sep 17 00:00:00 2001 From: veeramarni Date: Fri, 6 Jan 2017 13:57:30 -0500 Subject: [PATCH] Squashed 'app/packages/meteor-integration/' content from commit d5bee6f git-subtree-dir: app/packages/meteor-integration git-subtree-split: d5bee6f274d01f72f4c6a88d866e78a00e2f965e --- .versions | 71 +++++++++++++++++++++++++++ CHANGELOG.md | 50 +++++++++++++++++++ README.md | 26 ++++++++++ check-npm.js | 19 ++++++++ main-client.js | 98 ++++++++++++++++++++++++++++++++++++++ main-server.js | 124 ++++++++++++++++++++++++++++++++++++++++++++++++ package.js | 27 +++++++++++ tests/client.js | 12 +++++ tests/server.js | 12 +++++ 9 files changed, 439 insertions(+) create mode 100644 .versions create mode 100644 CHANGELOG.md create mode 100644 README.md create mode 100644 check-npm.js create mode 100644 main-client.js create mode 100644 main-server.js create mode 100644 package.js create mode 100644 tests/client.js create mode 100644 tests/server.js diff --git a/.versions b/.versions new file mode 100644 index 0000000..aa143c5 --- /dev/null +++ b/.versions @@ -0,0 +1,71 @@ +accounts-base@1.2.9 +allow-deny@1.0.5 +apollo@0.2.0 +autoupdate@1.2.11 +babel-compiler@6.9.0 +babel-runtime@0.1.10 +base64@1.0.9 +binary-heap@1.0.9 +blaze@2.1.8 +blaze-tools@1.0.9 +boilerplate-generator@1.0.9 +caching-compiler@1.0.6 +caching-html-compiler@1.0.6 +callback-hook@1.0.9 +check@1.2.3 +coffeescript@1.1.3 +ddp@1.2.5 +ddp-client@1.2.9 +ddp-common@1.2.6 +ddp-rate-limiter@1.0.5 +ddp-server@1.2.9 +deps@1.0.12 +diff-sequence@1.0.6 +ecmascript@0.5.7 +ecmascript-runtime@0.3.12 +ejson@1.0.12 +geojson-utils@1.0.9 +html-tools@1.0.10 +htmljs@1.0.10 +http@1.1.8 +id-map@1.0.8 +jquery@1.11.9 +local-test:apollo@0.2.0 +localstorage@1.0.11 +logging@1.1.14 +meteor@1.2.16 +minifier-js@1.1.13 +minimongo@1.0.17 +modules@0.7.5 +modules-runtime@0.7.5 +mongo@1.1.10 +mongo-id@1.0.5 +npm-mongo@1.5.45 +observe-sequence@1.0.12 +ordered-dict@1.0.8 +practicalmeteor:chai@2.1.0_1 +practicalmeteor:loglevel@1.2.0_2 +practicalmeteor:mocha@2.1.0_7 +practicalmeteor:mocha-core@0.1.4 +practicalmeteor:sinon@1.14.1_2 +promise@0.8.3 +random@1.0.10 +rate-limit@1.0.5 +reactive-dict@1.1.8 +reactive-var@1.0.10 +reload@1.1.10 +retry@1.0.8 +routepolicy@1.0.11 +service-configuration@1.0.10 +session@1.1.6 +spacebars@1.0.12 +spacebars-compiler@1.0.12 +templating@1.1.13 +templating-tools@1.0.4 +tmeasday:check-npm-versions@0.3.1 +tracker@1.1.0 +ui@1.0.11 +underscore@1.0.9 +url@1.0.10 +webapp@1.3.10 +webapp-hashing@1.0.9 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..30da16d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,50 @@ +# Change Log +All notable changes to this project will be documented in this file. [*File syntax*](http://keepachangelog.com/). +This project adheres to [Semantic Versioning](http://semver.org/). + +## vNEXT + +## [0.2.1] - 2016-12-23 +### Added + +- Support for `v0.8.x` of `graphql` [#54](https://github.com/apollostack/meteor-integration/pull/54) +- When user is not logged in, provide `{}` as context [#55](https://github.com/apollostack/meteor-integration/pull/55) + +## [0.2.0] - 2016-11-04 +### Updated + +- `apollo-client` [v0.5.x](https://github.com/apollostack/apollo-client/blob/master/CHANGELOG.md#v050) +- Updated createNetworkInterface call to match new signature ([@jasonphillips](https://github.com/jasonphillips) in [#43](https://github.com/apollostack/meteor-integration/pull/43)). +- `graphql-server` [v0.4.2](https://github.com/apollostack/graphql-server/blob/master/CHANGELOG.md#v042) + +### Added + +- Added the logged-in user's doc to `context.user` + +## [0.1.2] - 2016-10-04 +### Added + +- Pass a function to configure the express server in createApolloServer ([@nicolaslopezj](https://github.com/nicolaslopezj) in [#32](https://github.com/apollostack/meteor-integration/pull/32)). +- Automatically pass Meteor authentication in GraphiQL ([@nicolaslopezj](https://github.com/nicolaslopezj) in [#35](https://github.com/apollostack/meteor-integration/pull/35)). + +## [0.1.1] - 2016-09-21 +### Fixed + +- Fix userId persisting in options.context (reported in [#37](https://github.com/apollostack/meteor-integration/pull/37)) + +## [0.1.0] - 2016-09-09 +### Updated + +- `apollo-server` [v0.2.x](https://github.com/apollostack/apollo-server/blob/cc15ebfb1c9637989e09976c8416b4fd5c2b6728/CHANGELOG.md) + - Updated interface to reflect `apollo-server` refactor. +- `apollo-client` [v0.4.x](https://github.com/apollostack/apollo-client/blob/master/CHANGELOG.md#v040) + +## [0.0.4] - 2016-08-24 +### Fixed + +- Fixed global auth issue + +## [0.0.2] - 2016-06-17 +### Fixed + +- Fix dependencies #17 diff --git a/README.md b/README.md new file mode 100644 index 0000000..dcee18f --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +Use the [Apollo Stack](http://dev.apollodata.com/) in your [Meteor](https://www.meteor.com/) app. + +```sh +meteor add apollo +``` + +# Docs + +**[The docs](http://dev.apollodata.com/core/meteor.html)** + +# Package dev + +## Tests + +TODO broken, see #3 + +```bash +git clone git@github.com:apollostack/meteor-integration.git +cd meteor-integration +meteor test-packages ./ --driver-package practicalmeteor:mocha +open localhost:3000 +``` + +## Credits + +[Contributors](https://github.com/apollostack/meteor-integration/graphs/contributors) diff --git a/check-npm.js b/check-npm.js new file mode 100644 index 0000000..985f7c0 --- /dev/null +++ b/check-npm.js @@ -0,0 +1,19 @@ +import { Meteor } from 'meteor/meteor'; +import { checkNpmVersions } from 'meteor/tmeasday:check-npm-versions'; + +if (Meteor.isClient) { + checkNpmVersions({ + 'apollo-client': '^0.5.0', + }, 'apollo'); +} else { + checkNpmVersions({ + 'graphql-server-express': '^0.4.3', + "body-parser": "^1.15.2", + "express": "^4.14.0", + "graphql": "^0.7.0 || ^0.8.0", + "graphql-tools": "^0.8.0", + "subscriptions-transport-ws": "^0.3.0", + "graphql-tag": "^1.1.0", + "graphql-subscriptions": "^0.2.0" + }, 'apollo'); +} diff --git a/main-client.js b/main-client.js new file mode 100644 index 0000000..07b6e28 --- /dev/null +++ b/main-client.js @@ -0,0 +1,98 @@ +import './check-npm.js'; + +import { createNetworkInterface } from 'apollo-client'; +import { Accounts } from 'meteor/accounts-base'; +import { _ } from 'meteor/underscore'; +import { print } from 'graphql-tag/printer'; +import { Client } from 'subscriptions-transport-ws'; + +const defaultNetworkInterfaceConfig = { + path: '/graphql', + options: {}, + useMeteorAccounts: true, + useSubscription: true, +}; + +const getDefaultWsClient = () => new Client('ws://localhost:8080'); + +export const createMeteorNetworkInterface = (givenConfig) => { + const config = _.extend(defaultNetworkInterfaceConfig, givenConfig); + const wsClient = givenConfig && givenConfig.wsClient ? givenConfig.wsClient : getDefaultWsClient(); + // absoluteUrl adds a '/', so let's remove it first + let path = config.path; + if (path[0] === '/') { + path = path.slice(1); + } + + // For SSR + const uri = Meteor.absoluteUrl(path); + const networkInterface = createNetworkInterface({ uri }); + + if (config.useMeteorAccounts) { + networkInterface.use([{ + applyMiddleware(request, next) { + const currentUserToken = Accounts._storedLoginToken() ? Accounts._storedLoginToken() : null; + + if (!currentUserToken) { + next(); + return; + } + + if (!request.options.headers) { + request.options.headers = new Headers(); + } + + request.options.headers.Authorization = currentUserToken; + + next(); + }, + }]); + } + + if (config.useSubscription) { + return _.extend(networkInterface, { + subscribe: (request, handler) => wsClient.subscribe({ + query: print(request.query), + variables: request.variables, + }, handler), + unsubscribe: (id) => { + wsClient.unsubscribe(id); + }, + }); + } + return networkInterface; +}; +// const configureSubscription = (wsClientProvided) => { +// const wsClient = wsClientProvided ? wsClientProvided : getDefaultWsClient(); +// return { +// subscribe: (request, handler) => wsClient.subscribe({ +// query: print(request.query), +// variables: request.variables, +// }, handler), +// unsubscribe: (id) => { +// wsClient.unsubscribe(id); +// }, +// }; +// } +export const meteorClientConfig = (networkInterfaceConfig) => { + const networkInterface = createMeteorNetworkInterface(networkInterfaceConfig); + let { initialState } = networkInterface; + if(initialState){ + // Temporary workaround for bug in AC@0.5.0: https://github.com/apollostack/apollo-client/issues/845 + delete initialState.apollo.queries; + delete initialState.apollo.mutations; + } + + return { + networkInterface, + initialState, + // Default to using Mongo _id, must use _id for queries. + dataIdFromObject: (result) => { + if (result._id && result.__typename) { + const dataId = result.__typename + result._id; + return dataId; + } + return null; + }, + }; +}; diff --git a/main-server.js b/main-server.js new file mode 100644 index 0000000..ede3f7e --- /dev/null +++ b/main-server.js @@ -0,0 +1,124 @@ +import './check-npm.js'; + +import { graphqlExpress, graphiqlExpress } from 'graphql-server-express'; +import bodyParser from 'body-parser'; +import express from 'express'; +import { createServer } from 'http'; +import { Meteor } from 'meteor/meteor'; +import { WebApp } from 'meteor/webapp'; +import { check } from 'meteor/check'; +import { Accounts } from 'meteor/accounts-base'; +import { _ } from 'meteor/underscore'; +import { SubscriptionServer } from 'subscriptions-transport-ws'; + +export { meteorClientConfig, createMeteorNetworkInterface } from './main-client'; + +const defaultConfig = { + path: '/graphql', + maxAccountsCacheSizeInMB: 1, + graphiql : Meteor.isDevelopment, + graphiqlPath : '/graphiql', + graphiqlOptions : { + passHeader : "'Authorization': localStorage['Meteor.loginToken']" + }, + useSubscription: true, + subscriptionPort: 8080, + configServer: (graphQLServer) => {}, +}; + +const defaultOptions = { + formatError: e => ({ + message: e.message, + locations: e.locations, + path: e.path + }), +}; + +if (Meteor.isDevelopment) { + defaultOptions.debug = true; +} + +export const createApolloServer = (givenOptions = {}, givenConfig = {}) => { + const { subscriptionManager, ...restOfConfig } = givenConfig; + let graphiqlOptions = Object.assign({}, defaultConfig.graphiqlOptions, restOfConfig.graphiqlOptions); + let config = Object.assign({}, defaultConfig, restOfConfig); + config.graphiqlOptions = graphiqlOptions; + + const graphQLServer = express(); + + config.configServer(graphQLServer) + + // GraphQL endpoint + graphQLServer.use(config.path, bodyParser.json(), graphqlExpress(async (req) => { + let options, + user = null; + + if (_.isFunction(givenOptions)) + options = givenOptions(req); + else + options = givenOptions; + + // Merge in the defaults + options = Object.assign({}, defaultOptions, options); + if (options.context) { + // don't mutate the context provided in options + options.context = Object.assign({}, options.context); + } else { + options.context = {}; + } + + // Get the token from the header + if (req.headers.authorization) { + const token = req.headers.authorization; + check(token, String); + const hashedToken = Accounts._hashLoginToken(token); + + // Get the user from the database + user = await Meteor.users.findOne( + {"services.resume.loginTokens.hashedToken": hashedToken} + ); + + if (user) { + const loginToken = _.findWhere(user.services.resume.loginTokens, { hashedToken }); + const expiresAt = Accounts._tokenExpiration(loginToken.when); + const isExpired = expiresAt < new Date(); + + if (!isExpired) { + options.context.userId = user._id; + options.context.user = user; + } + } + } + + return options; + + })); + + // Start GraphiQL if enabled + if (config.graphiql) { + graphQLServer.use(config.graphiqlPath, graphiqlExpress(_.extend(config.graphiqlOptions, {endpointURL : config.path}))); + } + + // create http server for subscription + const server = createServer(graphQLServer); + + // This binds the specified paths to the Express server running Apollo + GraphiQL + WebApp.connectHandlers.use(Meteor.bindEnvironment(graphQLServer)); + + // Add subscriptionManager here + if (config.useSubscription) { + if (!subscriptionManager) { + throw new Meteor.Error('SubscriptionManager which is mandatory missing.'); + } + new SubscriptionServer({ + subscriptionManager, + }, server); + try { + server.listen(config.subscriptionPort, () => { + console.log('Subscription manager running ' + config.subscriptionPort); + }); + } catch (e) { + console.log(e); + } + } +}; diff --git a/package.js b/package.js new file mode 100644 index 0000000..6be302c --- /dev/null +++ b/package.js @@ -0,0 +1,27 @@ +Package.describe({ + name: 'apollo', + version: '0.2.1', + summary: ' 🚀 Add Apollo to your Meteor app', + git: 'https://github.com/apollostack/meteor-integration' +}); + +Package.onUse(function(api) { + api.versionsFrom('1.4.0.1'); + api.use(['ecmascript', + 'underscore', + 'accounts-base', + 'tmeasday:check-npm-versions@0.3.1']); + + api.mainModule('main-client.js', 'client'); + api.mainModule('main-server.js', 'server'); +}); + +Package.onTest(function(api) { + api.use(['ecmascript', + 'practicalmeteor:mocha', + 'practicalmeteor:chai', + 'apollo']); + + api.mainModule('tests/client.js', 'client'); + api.mainModule('tests/server.js', 'server'); +}); diff --git a/tests/client.js b/tests/client.js new file mode 100644 index 0000000..24fd71a --- /dev/null +++ b/tests/client.js @@ -0,0 +1,12 @@ +import { assert } from 'meteor/practicalmeteor:chai'; + +import { createMeteorNetworkInterface } from 'meteor/apollo'; + +describe('client', function() { + + it('works', function() { + assert.ok(createMeteorNetworkInterface()); + }); + +}); + diff --git a/tests/server.js b/tests/server.js new file mode 100644 index 0000000..1efc010 --- /dev/null +++ b/tests/server.js @@ -0,0 +1,12 @@ +import { assert } from 'meteor/practicalmeteor:chai'; + +import { createApolloServer } from 'meteor/apollo'; + +describe('server', function() { + + it('works', function() { + assert.ok(createApolloServer()); + }); + +}); +