Merge branch 'master' into fix-my-profile-title-link

This commit is contained in:
Wyatt Johnson
2018-05-11 14:13:29 -06:00
committed by GitHub
33 changed files with 287 additions and 130 deletions
+11
View File
@@ -0,0 +1,11 @@
:global {
html, body, #root, #root > div {
min-height: 100%;
}
body {
margin: 0;
background-color: #FAFAFA;
font-family: 'Roboto', sans-serif;
}
}
+1
View File
@@ -1,5 +1,6 @@
import React from 'react';
import ToastContainer from './ToastContainer';
import './App.css';
import 'material-design-lite';
import AppRouter from '../AppRouter';
+11 -5
View File
@@ -37,7 +37,8 @@ export default class Popup extends Component {
this.onBlur();
};
// Use `onunload` instead of `onbeforeunload` which is not supported in IOS Safari.
// Use `onunload` instead of `onbeforeunload` which is not supported in iOS
// Safari.
this.ref.onunload = () => {
this.onUnload();
@@ -46,10 +47,15 @@ export default class Popup extends Component {
}
this.resetCallbackInterval = setInterval(() => {
if (this.ref && this.ref.onload === null) {
clearInterval(this.resetCallbackInterval);
this.resetCallbackInterval = null;
this.setCallbacks();
try {
if (this.ref && this.ref.onload === null) {
clearInterval(this.resetCallbackInterval);
this.resetCallbackInterval = null;
this.setCallbacks();
}
} catch (err) {
// We could be getting a security exception here if the login page
// gets redirected to another domain to authenticate.
}
}, 50);
+6 -2
View File
@@ -94,9 +94,13 @@ const createResolveFactory = (() => {
module.exports = async (req, res, next) => {
try {
// Attach the custom css url.
const { customCssUrl } = await SettingsService.select('customCssUrl');
// Attach the custom css url and organization name.
const { customCssUrl, organizationName } = await SettingsService.select(
'customCssUrl',
'organizationName'
);
res.locals.customCssUrl = customCssUrl;
res.locals.organizationName = organizationName;
} catch (err) {
console.warn(err);
}
+1
View File
@@ -219,6 +219,7 @@
"babel-plugin-dynamic-import-node": "^1.1.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"browserstack-local": "^1.3.0",
"casual": "^1.5.19",
"chai": "^3.5.0",
"chai-as-promised": "^6.0.0",
"chai-datetime": "^1.5.0",
@@ -0,0 +1,9 @@
import * as actions from './constants';
export const startAttach = () => ({
type: actions.START_ATTACH,
});
export const finishAttach = () => ({
type: actions.FINISH_ATTACH,
});
@@ -45,6 +45,10 @@ class AddEmailAddressDialog extends React.Component {
),
};
componentDidMount() {
this.props.startAttach();
}
onChange = e => {
const { name, value } = e.target;
this.setState(
@@ -99,7 +103,13 @@ class AddEmailAddressDialog extends React.Component {
});
};
confirmChanges = async () => {
finish = () => {
this.props.finishAttach();
};
confirmChanges = async e => {
e.preventDefault();
if (!this.validate()) {
this.showErrors();
return;
@@ -113,7 +123,11 @@ class AddEmailAddressDialog extends React.Component {
email: emailAddress,
password: confirmPassword,
});
this.props.notify('success', 'Email Added!');
this.props.notify(
'success',
t('talk-plugin-local-auth.add_email.added.alert')
);
this.goToNextStep();
} catch (err) {
this.props.notify('error', getErrorMessages(err));
@@ -143,13 +157,13 @@ class AddEmailAddressDialog extends React.Component {
)}
{step === 1 &&
!settings.requireEmailConfirmation && (
<EmailAddressAdded done={() => {}} />
<EmailAddressAdded done={this.finish} />
)}
{step === 1 &&
settings.requireEmailConfirmation && (
<VerifyEmailAddress
emailAddress={formData.emailAddress}
done={() => {}}
done={this.finish}
/>
)}
</Dialog>
@@ -161,6 +175,8 @@ AddEmailAddressDialog.propTypes = {
attachLocalAuth: PropTypes.func.isRequired,
notify: PropTypes.func.isRequired,
root: PropTypes.object.isRequired,
startAttach: PropTypes.func.isRequired,
finishAttach: PropTypes.func.isRequired,
};
export default AddEmailAddressDialog;
@@ -41,7 +41,7 @@ const AddEmailContent = ({
</li>
</ul>
<form autoComplete="off">
<form autoComplete="off" onSubmit={confirmChanges}>
<InputField
id="emailAddress"
label={t('talk-plugin-local-auth.add_email.enter_email_address')}
@@ -82,12 +82,9 @@ const AddEmailContent = ({
showSuccess={false}
/>
<div className={styles.actions}>
<a
className={cn(styles.button, styles.proceed)}
onClick={confirmChanges}
>
<button className={cn(styles.button, styles.proceed)}>
{t('talk-plugin-local-auth.add_email.add_email_address')}
</a>
</button>
</div>
</form>
</div>
@@ -0,0 +1,4 @@
const prefix = 'TALK_LOCAL_AUTH';
export const START_ATTACH = `${prefix}_START_ATTACH`;
export const FINISH_ATTACH = `${prefix}_FINISH_ATTACH`;
@@ -3,10 +3,16 @@ import { bindActionCreators } from 'redux';
import { connect, withFragments, excludeIf } from 'plugin-api/beta/client/hocs';
import AddEmailAddressDialog from '../components/AddEmailAddressDialog';
import { notify } from 'coral-framework/actions/notification';
import { withAttachLocalAuth } from '../hocs';
import { startAttach, finishAttach } from '../actions';
import get from 'lodash/get';
const mapDispatchToProps = dispatch => bindActionCreators({ notify }, dispatch);
const mapStateToProps = ({ talkPluginLocalAuth: state }) => ({
inProgress: state.inProgress,
});
const mapDispatchToProps = dispatch =>
bindActionCreators({ notify, startAttach, finishAttach }, dispatch);
const withData = withFragments({
root: gql`
@@ -14,6 +20,13 @@ const withData = withFragments({
me {
id
email
state {
status {
username {
status
}
}
}
}
settings {
requireEmailConfirmation
@@ -23,8 +36,13 @@ const withData = withFragments({
});
export default compose(
connect(null, mapDispatchToProps),
connect(mapStateToProps, mapDispatchToProps),
withAttachLocalAuth,
withData,
excludeIf(({ root: { me } }) => !me || me.email)
excludeIf(
({ root: { me }, inProgress }) =>
!me ||
get(me, 'state.status.username.status') === 'UNSET' ||
(me.email && !inProgress)
)
)(AddEmailAddressDialog);
@@ -1,10 +1,12 @@
import ChangePassword from './containers/ChangePassword';
import AddEmailAddressDialog from './containers/AddEmailAddressDialog';
import Profile from './containers/Profile';
import translations from './translations.yml';
import translations from '../translations.yml';
import graphql from './graphql';
import reducer from './reducer';
export default {
reducer,
translations,
slots: {
profileHeader: [Profile],
@@ -0,0 +1,20 @@
import * as actions from './constants';
const initialState = {
inProgress: false,
};
export default function reducer(state = initialState, action) {
switch (action.type) {
case actions.START_ATTACH:
return {
inProgress: true,
};
case actions.FINISH_ATTACH:
return {
inProgress: false,
};
default:
return state;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ const mutators = require('./server/mutators');
const path = require('path');
module.exports = {
translations: path.join(__dirname, 'server', 'translations.yml'),
translations: path.join(__dirname, 'translations.yml'),
typeDefs,
mutators,
resolvers,
@@ -1,9 +0,0 @@
en:
email:
email_change_original:
subject: Email change
body: Your email address has been changed from {0} to {1}. If you did not initiate this change, please contact {2}. # TODO: update translation
error:
NO_LOCAL_PROFILE: No existing email address is associated with this account.
LOCAL_PROFILE: An email address is already associated with this account.
INCORRECT_PASSWORD: Provided password was incorrect.
@@ -1,4 +1,12 @@
en:
email:
email_change_original:
subject: Email change
body: Your email address has been changed from {0} to {1}. If you did not request this change, please contact {2}.
error:
NO_LOCAL_PROFILE: No existing email address is associated with this account.
LOCAL_PROFILE: An email address is already associated with this account.
INCORRECT_PASSWORD: Provided password was incorrect.
talk-plugin-local-auth:
change_password:
change_password: "Change Password"
@@ -43,7 +51,7 @@ en:
email_does_not_match: "Email Address does not match"
insert_password: "Insert Password:"
required_field: "This field is required"
done: "done"
done: "Done"
content:
title: "Add an Email Address"
description: "For your added security, we require users to add an email address to their accounts. Your email address will be used to:"
@@ -59,6 +67,7 @@ en:
subtitle: "Need to change your email address?"
description_2: "You can change your account settings by visiting"
path: "My Profile > Settings"
alert: "Email Added!"
es:
talk-plugin-local-auth:
change_password:
@@ -1,5 +1,12 @@
@custom-media --narrow-viewport (max-width: 420px);
.commentContent {
composes: content from "./CommentContent.css";
/* Prevent zoom on narrow viewports */
@media (--narrow-viewport) {
font-size: 16px;
}
}
.placeholder {
@@ -8,6 +8,7 @@ import RTE from './rte/RTE';
import { Icon } from 'plugin-api/beta/client/components/ui';
import { Bold, Italic, Blockquote } from './rte/features';
import { t } from 'plugin-api/beta/client/services';
import bowser from 'bowser';
class Editor extends React.Component {
ref = null;
@@ -40,7 +41,9 @@ class Editor extends React.Component {
}
});
}
if (this.props.isReply) {
// Skip IOS due to a bug, see https://www.pivotaltracker.com/story/show/157434928
if (this.props.isReply && !bowser.ios) {
this.ref.focus();
}
}
+9
View File
@@ -0,0 +1,9 @@
.container {
width: auto;
max-width: 680px;
padding: 0 15px;
}
.graphiql em {
font-family: georgia;
}
+27 -18
View File
@@ -1,49 +1,58 @@
const express = require('express');
const router = express.Router();
const errors = require('../../errors');
const Assets = require('../../services/assets');
const body =
'Lorem ipsum dolor sponge amet, consectetur adipiscing clam. Ut lobortis sollicitudin pillar a ornare. Curabitur dignissim vestibulum cay non rhoncus. Cras laoreet ante vel nunc hendrerit, shelf imperdiet neque egestas. Suspendisse aliquet iaculis fermentum. Talk volutpat, tellus posuere laoreet consequat, mi lacus laoreet massa, sed vehicula mauris velit non lectus. Integer non trust nec neque congue faucibus porttitor sit amet elkhorn.';
const casual = require('casual');
const { ErrNotFound } = require('../../errors');
const Asset = require('../../models/asset');
router.get('/id/:asset_id', async (req, res, next) => {
try {
const asset = await Assets.findById(req.params.asset_id);
const asset = await Asset.findOne({ id: req.params.asset_id });
if (asset === null) {
return next(errors.ErrNotFound);
throw new ErrNotFound();
}
res.render('dev/article', {
title: asset.title,
asset_id: asset.id,
asset_url: asset.url,
body: '',
basePath: '/client/embed/stream',
});
} catch (err) {
return next(err);
}
});
router.get('/random', (req, res) => {
const title = casual.title;
res.redirect(`./title/${title.replace(/ /g, '-')}`);
});
router.get('/title/:asset_title', (req, res) => {
return res.render('dev/article', {
res.render('dev/article', {
title: req.params.asset_title.split('-').join(' '),
asset_url: '',
asset_id: null,
body: body,
basePath: '/client/embed/stream',
});
});
router.get('/', async (req, res, next) => {
let skip = req.query.skip ? parseInt(req.query.skip) : 0;
let limit = req.query.limit ? parseInt(req.query.limit) : 25;
try {
const assets = await Assets.all(skip, limit);
const skip = req.query.skip ? parseInt(req.query.skip) : 0;
const limit = req.query.limit ? parseInt(req.query.limit) : 6;
const [assets, count] = await Promise.all([
Asset.find({})
.sort({ created_at: 1 })
.limit(limit)
.skip(skip),
Asset.count(),
]);
res.render('dev/articles', {
assets: assets,
skip,
limit,
count,
assets,
});
} catch (err) {
return next(err);
-2
View File
@@ -16,8 +16,6 @@ router.get('/', staticTemplate, async (req, res) => {
title: 'Coral Talk',
asset_url: '',
asset_id: '',
body: '',
basePath: '/static/embed/stream',
});
}
});
-6
View File
@@ -232,11 +232,5 @@ module.exports = class AssetsService {
await AssetModel.remove({
id: srcAssetID,
});
// That's it!
}
static all(limit = undefined) {
return AssetModel.find({}).limit(limit);
}
};
+2 -7
View File
@@ -3,16 +3,11 @@
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
<title>Talk - Coral Admin</title>
<style media="screen">
html, body, #root, #root > div { min-height: 100%; }
body { margin: 0; background-color: #FAFAFA; font-family: 'Roboto', sans-serif; }
</style>
<%- include partials/head %>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
<link rel="stylesheet" href="https://code.getmdl.io/1.2.1/material.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<%- include partials/head %>
<link rel="stylesheet" type="text/css" href="<%= resolve('coral-admin/bundle.css') %>">
<%- include partials/custom-css %>
</head>
<body class="admin-page">
<div id="root"></div>
+9 -3
View File
@@ -5,13 +5,17 @@
<meta charset="utf-8" />
<title>GraphiQL</title>
<meta name="robots" content="noindex" />
<%- include ../partials/dev %>
<style>
html, body {
html, body, #graphiql {
height: 100%;
margin: 0;
overflow: hidden;
width: 100%;
}
*, ::after, ::before {
box-sizing: inherit;
}
</style>
<link href="//cdn.jsdelivr.net/graphiql/0.9.1/graphiql.css" rel="stylesheet" />
<script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script>
@@ -20,6 +24,8 @@
<script src="//cdn.jsdelivr.net/graphiql/0.9.1/graphiql.min.js"></script>
</head>
<body>
<%- include ../partials/dev-nav %>
<main id="graphiql"></main>
<script>
// Collect the URL parameters
var parameters = {};
@@ -112,8 +118,8 @@
variables: null,
operationName: null,
}),
document.body
document.querySelector("#graphiql")
);
</script>
</body>
</html>
</html>
+36 -44
View File
@@ -8,53 +8,45 @@
<meta property="article:published" itemprop="datePublished" content="2016-11-16T11:46:06-05:00" />
<meta property="article:modified" itemprop="dateModified" content="2016-11-16T12:09:44-05:00" />
<meta property="article:section" itemprop="articleSection" content="The Section!" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
main {
margin-left:auto;
margin-right:auto;
max-width:500px;
display: block;
}
</style>
<title><%= title %></title>
<%- include ../partials/dev %>
</head>
<body>
<main>
<h1><%= title %></h1>
<p><%= body %></p>
<p><a href="<%= BASE_PATH %>admin">Admin</a> - <a href="<%= BASE_PATH %>dev/assets">All Assets</a></p>
<div id='coralStreamEmbed'></div>
<script src="<%= resolve('embed.js') %>" async onload="
window.TalkEmbed = Coral.Talk.render(document.getElementById('coralStreamEmbed'), {
talk: '<%= BASE_URL %>',
asset_url: '<%= asset_url ? asset_url : '' %>',
asset_id: '<%= asset_id ? asset_id : '' %>',
auth_token: '',
/**
* You can listen to events using the example below.
* The argument passed is the event emitter from
* https://github.com/asyncly/EventEmitter2
*
* events: function(events) {
* events.onAny(function(eventName, data) {
* console.log(eventName, data);
* });
* },
*/
plugins_config: {
<%- include ../partials/dev-nav %>
<div class="container">
<h1 class="mt-3"><%= title %></h1>
<div id='coralStreamEmbed'></div>
<script src="<%= resolve('embed.js') %>"></script>
<script>
window.TalkEmbed = Coral.Talk.render(document.getElementById('coralStreamEmbed'), {
talk: '<%= BASE_URL %>',
asset_url: '<%= asset_url ? asset_url : '' %>',
asset_id: '<%= asset_id ? asset_id : '' %>',
auth_token: '',
/**
* You can disable rendering slot components of a plugin by doing:
*
* 'talk-plugin-love': {
* disable_components: true,
* },
*/
test: 'data',
debug: false
}
})
"></script>
</main>
* You can listen to events using the example below.
* The argument passed is the event emitter from
* https://github.com/asyncly/EventEmitter2
*
* events: function(events) {
* events.onAny(function(eventName, data) {
* console.log(eventName, data);
* });
* },
*/
plugins_config: {
/**
* You can disable rendering slot components of a plugin by doing:
*
* 'talk-plugin-love': {
* disable_components: true,
* },
*/
test: 'data',
debug: false
}
})
</script>
</div>
</body>
</html>
+38 -12
View File
@@ -1,14 +1,40 @@
<html>
<body>
<h1>
Asset list
</h1>
<% assets.forEach(function (asset) { %>
<a href="<%= BASE_PATH %>dev/assets/id/<%= asset.id %>"><%= asset.url %></a><br />
<% }) %>
<p>
(For dev use only. FYI, you can: ?skip=100&limit=25)
</p>
</body>
<head>
<title>All Assets</title>
<%- include ../partials/dev %>
</head>
<body>
<%- include ../partials/dev-nav %>
<div class="container">
<div class="d-flex w-100 justify-content-between mt-3 mb-2">
<h1 class="mb-0">All Assets</h1>
<span class="text-muted"><%= skip + 1 %> - <%= skip + assets.length %> of <%= count %> Assets</span>
</div>
<div class="list-group">
<% if (skip === 0) { %><a href="<%= BASE_PATH %>dev/assets/random" class="list-group-item list-group-item-action list-group-item-primary"><i class="fa fa-plus" aria-hidden="true"></i> Create a random article</a><% } %>
<% assets.forEach(function (asset) { %>
<a href="<%= BASE_PATH %>dev/assets/id/<%= asset.id %>" class="list-group-item list-group-item-action flex-column align-items-start">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1"><%= asset.title %></h5>
<small>Created <%= asset.created_at.toLocaleString('en-US') %></small>
</div>
<small><%= asset.url %></small>
</a>
<% }) %>
</div>
<% if (count !== assets.length) { %>
<nav aria-label="Page navigation example" class="mt-2">
<ul class="pagination justify-content-center">
<% let page = 1; for (let i = 0; i < count; i += limit) { %>
<% if (i === skip) { %>
<li class="page-item disabled"><a class="page-link" href="#"><%= page %></a></li>
<% } else { %>
<li class="page-item"><a class="page-link" href="?skip=<%= i %>&amp;limit=<%= limit %>"><%= page %></a></li>
<% } %>
<% page++; } %>
</ul>
</nav>
<% } %>
</div>
</body>
</html>
+1
View File
@@ -5,6 +5,7 @@
<%- include ../partials/head %>
<link rel="stylesheet" type="text/css" href="<%= resolve('embed/stream/default.css') %>">
<link rel="stylesheet" type="text/css" href="<%= resolve('embed/stream/bundle.css') %>">
<%- include ../partials/custom-css %>
</head>
<body class="embed-stream-page">
<div id="talk-embed-stream-container"></div>
+1
View File
@@ -6,6 +6,7 @@
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<%- include partials/head %>
<link rel="stylesheet" type="text/css" href="<%= resolve('coral-login/bundle.css') %>">
<%- include partials/custom-css %>
</head>
<body>
<div id="talk-login-container"></div>
+2 -1
View File
@@ -1,3 +1,4 @@
<%- include ./head %>
<%- include head %>
<link rel="stylesheet" href="https://code.getmdl.io/1.2.1/material.indigo-pink.min.css">
<link rel="stylesheet" href="<%= BASE_PATH %>public/css/admin.css">
<%- include custom-css %>
+1
View File
@@ -0,0 +1 @@
<% if (locals.customCssUrl) { %><link href="<%= customCssUrl %>" rel="stylesheet" type="text/css"><% } %>
+8
View File
@@ -0,0 +1,8 @@
<nav class="navbar navbar-dark bg-dark">
<a class="navbar-brand" href="<%= BASE_PATH %>dev"><%= organizationName %> <span class="text-muted">Organization</span></a>
<form class="form-inline mb-0">
<a href="<%= BASE_PATH %>admin" class="btn btn-outline-primary mr-2"><i class="fa fa-lock" aria-hidden="true"></i> Admin</a>
<a href="<%= BASE_PATH %>api/v1/graph/iql" class="btn btn-outline-secondary mr-2 graphiql"><i class="fa fa-terminal" aria-hidden="true"></i> Graph<em>i</em>QL</a>
<a href="<%= BASE_PATH %>dev/assets" class="btn btn-outline-secondary"><i class="fa fa-list" aria-hidden="true"></i> All Assets</a>
</form>
</nav>
+5
View File
@@ -0,0 +1,5 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600" rel="stylesheet">
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous" rel="stylesheet">
<link href="<%= BASE_PATH %>public/css/dev.css" rel="stylesheet" >
-3
View File
@@ -18,9 +18,6 @@
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600" rel="stylesheet">
<%_ if (locals.customCssUrl) { _%>
<link href="<%= customCssUrl %>" rel="stylesheet" type="text/css">
<%_ } _%>
<%- include data %>
<base href="<%= BASE_URL %>"/>
+15
View File
@@ -1828,6 +1828,13 @@ caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
casual@^1.5.19:
version "1.5.19"
resolved "https://registry.yarnpkg.com/casual/-/casual-1.5.19.tgz#66fac46f7ae463f468f5913eb139f9c41c58bbf2"
dependencies:
mersenne-twister "^1.0.1"
moment "^2.15.2"
center-align@^0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
@@ -7154,6 +7161,10 @@ merge@^1.1.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da"
mersenne-twister@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mersenne-twister/-/mersenne-twister-1.1.0.tgz#f916618ee43d7179efcf641bec4531eb9670978a"
metascraper-author@^3.9.2:
version "3.9.2"
resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-3.9.2.tgz#ff2020ac428f59a875d655df3b0d4bea171fde19"
@@ -7436,6 +7447,10 @@ moment@^2.10.3:
version "2.19.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.1.tgz#56da1a2d1cbf01d38b7e1afc31c10bcfa1929167"
moment@^2.15.2:
version "2.22.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.1.tgz#529a2e9bf973f259c9643d237fda84de3a26e8ad"
mongodb-core@2.1.17:
version "2.1.17"
resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.17.tgz#a418b337a14a14990fb510b923dee6a813173df8"