diff --git a/.circleci/config.yml b/.circleci/config.yml index f8ed376fdb..2dad02d4d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,6 +21,38 @@ aliases: yarn cd ./mobile && yarn && yarn setup + - &install-rethinkdb + name: Install RethinkDB 2.3.5 + command: + | + echo "deb http://download.rethinkdb.com/apt jessie main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list + wget -qO- http://download.rethinkdb.com/apt/pubkey.gpg | sudo apt-key add - + sudo apt-get update + sudo apt-get install rethinkdb=2.3.5~0jessie + + - &start-rethinkdb + name: Start RethinkDB + command: rethinkdb --bind all + background: true + + - &setup-and-build-web + name: Setup and build + command: + | + node -e "const setup = require('./shared/testing/setup.js')().then(() => process.exit())" + yarn run build:web + yarn run build:iris + + - &start-iris + name: Start Iris in the background + command: TEST_DB=true yarn run dev:iris + background: true + + - &start-web + name: Start web client in the background + command: yarn run dev:web + background: true + defaults: &defaults working_directory: ~/spectrum @@ -51,6 +83,49 @@ jobs: root: . paths: . + # Start db and servers, then run e2e and unit tests + test_web: + <<: *defaults + docker: + - image: circleci/node:8-browsers + - image: redis:3.2.7 + - image: cypress/base:6 + environment: + TERM: xterm + steps: + - attach_workspace: + at: ~/spectrum + - run: *install-rethinkdb + - run: *start-rethinkdb + - run: sleep 10 + - run: *setup-and-build-web + - run: *start-iris + - run: *start-web + - run: sleep 60 + - run: + name: Run Unit Tests + command: yarn run test:ci + - run: + name: Run E2E Tests + command: yarn run test:e2e + - run: + name: Danger + when: always + command: yarn run danger ci + + # Run eslint, flow etc. + test_static_js: + <<: *js_defaults + steps: + - attach_workspace: + at: ~/spectrum + - run: + name: Run Flow + command: yarn run flow + - run: + name: Run ESLint + command: yarn run lint + # Tests js of the mobile app test_mobile_js: <<: *js_defaults @@ -89,8 +164,7 @@ jobs: workflows: version: 2 - # Tests mobile app - test_mobile: + test: jobs: - checkout_environment - test_mobile_js: @@ -100,3 +174,9 @@ workflows: # - test_mobile_native: # requires: # - checkout_environment + - test_web: + requires: + - checkout_environment + - test_static_js: + requires: + - checkout_environment diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..f4d797a995 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,15 @@ + + +**Description (type any text below)** + + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d52ae19134..3087f540d5 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,25 +1,22 @@ -### Deploy after merge (delete what needn't be deployed) -- iris -- hyperion + +**Status** +- [ ] WIP +- [ ] Ready for review +- [ ] Needs testing + +**Deploy after merge (delete what needn't be deployed)** +- iris (api) +- hyperion (frontend) - athena - vulcan - mercury - hermes - chronos +- mobile -### Run database migrations (delete if not) +**Run database migrations (delete if no migration was added)** YES -## Release notes +**Release notes for users (delete if codebase-only change)** - - diff --git a/cypress.json b/cypress.json new file mode 100644 index 0000000000..ac3ddcb2da --- /dev/null +++ b/cypress.json @@ -0,0 +1,4 @@ +{ + "baseUrl": "http://localhost:3000", + "viewportWidth": 1300 +} diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json new file mode 100644 index 0000000000..da18d9352a --- /dev/null +++ b/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/cypress/integration/channel_spec.js b/cypress/integration/channel_spec.js new file mode 100644 index 0000000000..2d610fc30c --- /dev/null +++ b/cypress/integration/channel_spec.js @@ -0,0 +1,24 @@ +import data from '../../shared/testing/data'; + +const channel = data.channels[0]; +const community = data.communities.find( + community => community.id === channel.communityId +); + +describe('Channel View', () => { + // Before every test suite set up a new browser and page + before(() => { + cy.visit(`/${community.slug}/${channel.slug}`); + }); + + it('should render', () => { + cy.get('[data-e2e-id="channel-view"]').should('be.visible'); + cy.contains(channel.description); + cy.contains(channel.name); + data.threads + .filter(thread => thread.channelId === channel.id) + .forEach(thread => { + cy.contains(thread.content.title); + }); + }); +}); diff --git a/cypress/integration/community_spec.js b/cypress/integration/community_spec.js new file mode 100644 index 0000000000..ae17a4b3aa --- /dev/null +++ b/cypress/integration/community_spec.js @@ -0,0 +1,33 @@ +import data from '../../shared/testing/data'; + +const community = data.communities[0]; + +describe('Community View', () => { + beforeEach(() => { + cy.visit(`/${community.slug}`); + }); + + it('should render all the communities data, and show a list of channels and threads', () => { + cy.get('[data-e2e-id="community-view"]').should('be.visible'); + cy.contains(community.description); + cy.contains(community.name); + cy.contains(community.website); + cy.get(`[src*="${community.profilePhoto}"]`).should('be.visible'); + // TODO: Actually use a Cypress API for this instead of this hacky shit + cy.document().then(document => { + expect(document.body.toString().indexOf(community.coverPhoto) > -1); + }); + + data.threads + .filter(thread => thread.communityId === community.id) + .forEach(thread => { + cy.contains(thread.content.title).should('be.visible'); + }); + + data.channels + .filter(channel => channel.communityId === community.id) + .forEach(channel => { + cy.contains(channel.name).should('be.visible'); + }); + }); +}); diff --git a/cypress/integration/inbox_spec.js b/cypress/integration/inbox_spec.js new file mode 100644 index 0000000000..f846e0487b --- /dev/null +++ b/cypress/integration/inbox_spec.js @@ -0,0 +1,32 @@ +import data from '../../shared/testing/data'; + +const user = data.users[0]; +const channelIds = data.usersChannels + .filter(({ userId }) => userId === user.id) + .map(({ channelId }) => channelId); +const dashboardThreads = data.threads.filter(({ channelId }) => + channelIds.includes(channelId) +); + +describe('Inbox View', () => { + before(() => { + cy.auth(user.id); + cy.visit('/'); + }); + + it('should render the inbox view', () => { + cy.get('[data-e2e-id="inbox-view"]').should('be.visible'); + dashboardThreads.forEach(thread => { + cy.contains(thread.content.title); + }); + const usersCommunities = data.usersCommunities + .filter(({ userId }) => user.id === userId) + .map(({ communityId }) => + data.communities.find(({ id }) => id === communityId) + ); + usersCommunities.forEach(community => { + cy.contains(community.name); + }); + cy.get('[data-e2e-id="thread-view"]').should('be.visible'); + }); +}); diff --git a/cypress/integration/login_spec.js b/cypress/integration/login_spec.js new file mode 100644 index 0000000000..b3757208fc --- /dev/null +++ b/cypress/integration/login_spec.js @@ -0,0 +1,15 @@ +describe('Login View', () => { + beforeEach(() => { + cy.visit('/login'); + }); + + it('should render', () => { + cy.get('[data-e2e-id="login-page"]').should('be.visible'); + cy.get('[href*="/auth/twitter"]').should('be.visible'); + cy.get('[href*="/auth/facebook"]').should('be.visible'); + cy.get('[href*="/auth/google"]').should('be.visible'); + cy + .get('[href*="github.com/withspectrum/code-of-conduct"]') + .should('be.visible'); + }); +}); diff --git a/cypress/integration/splash_spec.js b/cypress/integration/splash_spec.js new file mode 100644 index 0000000000..43d92a7db4 --- /dev/null +++ b/cypress/integration/splash_spec.js @@ -0,0 +1,11 @@ +describe('Splash View', () => { + before(() => { + cy.visit('/'); + }); + + it('should render the splash page', () => { + cy.get('[data-e2e-id="splash-page"]').should('be.visible'); + cy.get('[href*="/login"]').should('be.visible'); + cy.get('[href*="/new/community"]').should('be.visible'); + }); +}); diff --git a/cypress/integration/thread_spec.js b/cypress/integration/thread_spec.js new file mode 100644 index 0000000000..2f7e09de29 --- /dev/null +++ b/cypress/integration/thread_spec.js @@ -0,0 +1,36 @@ +import { toPlainText, toState } from '../../shared/draft-utils'; +import data from '../../shared/testing/data'; + +const thread = data.threads[0]; +const channel = data.channels.find(channel => channel.id === thread.channelId); +const community = data.communities.find( + community => community.id === thread.communityId +); +const author = data.users.find(user => user.id === thread.creatorId); +const messages = data.messages.filter( + message => message.threadId === thread.id +); + +describe('Thread View', () => { + // Before every test suite set up a new browser and page + before(() => { + cy.visit(`/thread/${thread.id}`); + }); + + it('should render', () => { + cy.get('[data-e2e-id="thread-view"]').should('be.visible'); + cy.contains(thread.content.title); + cy.contains( + toPlainText(toState(JSON.parse(thread.content.body))).split(' ')[0] + ); + cy.contains(author.name); + cy.contains(author.username); + cy.get(`[href*="/users/${author.username}"]`).should('be.visible'); + cy.get(`[href*="/${community.slug}"]`).should('be.visible'); + + cy.get('[data-e2e-id="message-group"]').should('be.visible'); + messages.forEach(message => { + cy.contains(toPlainText(toState(JSON.parse(message.content.body)))); + }); + }); +}); diff --git a/cypress/integration/user_spec.js b/cypress/integration/user_spec.js new file mode 100644 index 0000000000..78bc837b5b --- /dev/null +++ b/cypress/integration/user_spec.js @@ -0,0 +1,40 @@ +import data from '../../shared/testing/data'; + +const user = data.users[0]; + +describe('User View', () => { + before(() => { + cy.visit(`/users/${user.username}`); + }); + + it('should render', () => { + cy.get('[data-e2e-id="user-view"]').should('be.visible'); + cy.contains(user.username); + cy.contains(user.name); + cy.contains(user.description); + cy.contains(user.website); + cy.get('[data-e2e-id="thread-feed"]').should('be.visible'); + data.threads + .filter(thread => thread.creatorId === user.id) + .forEach(thread => { + cy.contains(thread.content.title); + }); + }); + + it('should list the communities a user is a member of, including their rep in that community', () => { + const usersCommunities = data.usersCommunities.filter( + ({ userId }) => userId === user.id + ); + const communityIds = usersCommunities.map(({ communityId }) => communityId); + const communities = data.communities.filter(({ id }) => + communityIds.includes(id) + ); + communities.forEach(community => { + cy.contains(community.name); + const userCommunity = usersCommunities.find( + ({ communityId }) => communityId === community.id + ); + cy.contains(userCommunity.reputation); + }); + }); +}); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 0000000000..dffed2532f --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,17 @@ +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +}; diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 0000000000..6e80f0d417 --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,21 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +import { encode } from '../../iris/utils/base64'; + +Cypress.Commands.add('auth', userId => { + cy.setCookie( + 'session', + encode(JSON.stringify({ passport: { user: userId } })), + { + httpOnly: true, + secure: false, + } + ); +}); diff --git a/cypress/support/index.js b/cypress/support/index.js new file mode 100644 index 0000000000..610304a94a --- /dev/null +++ b/cypress/support/index.js @@ -0,0 +1,23 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; + +before(() => { + cy.exec( + `node -e "const setup = require('./shared/testing/setup.js')().then(() => process.exit())"` + ); +}); diff --git a/dangerfile.js b/dangerfile.js index af2c15dd8d..030d02b4f2 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -80,6 +80,11 @@ schedule( created: 'fail', // Warn on modified untyped files modified: 'warn', - blacklist: ['flow-typed/**/*.js', 'public/**/*.js'], + blacklist: [ + 'flow-typed/**/*.js', + 'public/**/*.js', + 'iris/migrations/**/*.js', + 'cypress/**/*.js', + ], }) ); diff --git a/docs/testing.md b/docs/testing.md index 8d2b3f70f9..d5feacc015 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -1,18 +1,16 @@ # Testing -We use [`Jest`](https://github.com/facebook/jest) for all our testing needs. +We have a test suite consisting of a bunch of unit tests (mostly for the API) and integration tests to verify Spectrum keeps working as expected. The entire test suite is run in CI for every commit and PR, so if you introduce a breaking change the CI will fail and the PR will not be merge-able. -## Workflow +## Unit tests -To run Jest in watch mode locally so the tests run automatically while you're developing run: +We use [`Jest`](https://github.com/facebook/jest) for our unit testing needs. To run Jest in watch mode locally so the tests run automatically while you're developing run: ```sh yarn run test ``` -Before running the tests this will set up a RethinkDB database locally called `"testing"`. It will run the migrations over it and then insert some dummy data. - -This is important because we test our GraphQL API against the real database, we don't mock anything, to make sure everything is working 100%. +Before running the tests this will set up a RethinkDB database locally called `"testing"`. It will run the migrations over it and then insert some dummy data. This is important because we test our GraphQL API against the real database, we don't mock anything, to make sure everything is working 100%. When you exit the testing process with `CTRL+C` it will clean the database from all its data. It leaves the migrations in tact though, since there's no reason we should have to run them everytime we run the test suite. @@ -20,23 +18,49 @@ When you exit the testing process with `CTRL+C` it will clean the database from In edge cases it could happen that you end up with bad data locally and that tests fail/pass even though they shouldn't. To get rid of the stale data just stop the testing process and optionally run `yarn run posttest` to make extra sure there's nothing in the database anymore, before restarting the testing process to start from a clean slate. -### End-to-end tests +## End-to-end tests -To run e2e tests locally you have to have both iris and the client running. You also need Iris to be connected to the test database, which you do by setting `TEST_DB`: +We use [Cypress](https://cypress.io) to run our e2e tests, which gives you a nice GUI that you can use for your test runs. To run e2e tests you have to have both iris and the client running. You also need Iris to be connected to the test database, which you do by setting `TEST_DB`: ```sh +# In one tab TEST_DB=true yarn run dev:iris +# In another tab +yarn run dev:web ``` -Then, with both client and iris connected to the test database running, to run the full test suite including e2e tests: +Then open the Cypress GUI and you're good to start running the e2e tests: ```sh -E2E=true yarn run test +yarn run cypress:open +``` + +### Writing e2e tests + +**It is highly recommend to read the [Best Practices section of the Cypress docs](https://docs.cypress.io/guides/references/best-practices.html) before starting to write them!** + +All our integration tests live in `cypress/integration/`. This is what a normal integration test might look like: + +```JS +// cypress/integration/splash_spec.js +describe('Splash View', () => { + before(() => { + cy.visit('/'); + }); + + it('should render the splash page', () => { + cy.get('[data-e2e-id="splash-page"]').should('be.visible'); + cy.get('[href*="/login"]').should('be.visible'); + cy.get('[href*="/new/community"]').should('be.visible'); + }); +}); ``` -This automatically happens in CI, so you can't introduce a failing tests and expect builds to go through. +Note that while the Cypress API looks synchronous, it's actually totally asynchronous under the hood. They build up a queue of incoming assertions and wait for them to happen in order. While that's a bit confusing at the beginning, you get used to it very fast. + +Also note that Cypress uses Mocha under the hood, where our unit tests use Jest. This means rather than `expect().toEqual()` you'd have to write `expect().to.equal()` due to the syntax difference between the `expect` implementations. -#### End-to-end test ids +#### Test IDs To verify that certain elements are or aren't on the page we use custom `data-e2e-id` attributes. You render them from React like so: @@ -52,16 +76,12 @@ class SplashPage extends Component { } ``` -Then from puppeteer (which we use for e2e tests) you can make sure the splash page rendered correctly by waiting for that selector to appear on the page: +Then you can make sure the splash page rendered correctly by waiting for that selector to appear on the page: ```JS -it('should render', async () => { - await page.goto('http://localhost:3000/'); - await page.waitForSelector('[data-e2e-id="splash-page"]'); -}) +// cypress/integration/splash_spec.js +it('should render', () => { + cy.get('[data-e2e-id="splash-page"]').should('be.visible'); +}); ``` -## CI - -On CI we run `yarn run test:ci`, which forces Jest to exit after running the tests. You can also run this command locally in case you're in the mood to do a full test run rather than going into watch mode. - diff --git a/flow-typed/npm/aws-sdk_vx.x.x.js b/flow-typed/npm/aws-sdk_vx.x.x.js new file mode 100644 index 0000000000..e9fc4d3b99 --- /dev/null +++ b/flow-typed/npm/aws-sdk_vx.x.x.js @@ -0,0 +1,1809 @@ +// flow-typed signature: b073e824bc9714619fca4dd6caeb7e41 +// flow-typed version: <>/aws-sdk_v2.200.0/flow_v0.66.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'aws-sdk' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'aws-sdk' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'aws-sdk/browser' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/acm' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/alexaforbusiness' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/all' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/apigateway' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/applicationautoscaling' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/appstream' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/appsync' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/athena' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/autoscaling' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/autoscalingplans' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/batch' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/browser_default' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/budgets' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloud9' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/clouddirectory' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloudformation' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloudfront' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloudhsm' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloudhsmv2' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloudsearch' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloudsearchdomain' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloudtrail' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloudwatch' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloudwatchevents' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cloudwatchlogs' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/codebuild' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/codecommit' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/codedeploy' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/codepipeline' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/codestar' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cognitoidentity' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cognitoidentityserviceprovider' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cognitosync' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/comprehend' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/configservice' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/costexplorer' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/cur' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/datapipeline' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/dax' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/devicefarm' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/directconnect' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/directoryservice' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/discovery' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/dms' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/dynamodb' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/dynamodbstreams' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/ec2' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/ecr' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/ecs' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/efs' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/elasticache' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/elasticbeanstalk' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/elastictranscoder' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/elb' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/elbv2' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/emr' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/es' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/firehose' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/gamelift' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/glacier' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/glue' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/greengrass' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/guardduty' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/health' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/iam' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/importexport' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/inspector' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/iot' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/iotdata' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/iotjobsdataplane' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/kinesis' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/kinesisanalytics' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/kinesisvideo' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/kinesisvideoarchivedmedia' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/kinesisvideomedia' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/kms' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/lambda' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/lexmodelbuildingservice' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/lexruntime' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/lightsail' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/machinelearning' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/marketplacecommerceanalytics' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/marketplaceentitlementservice' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/marketplacemetering' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/mediaconvert' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/medialive' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/mediapackage' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/mediastore' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/mediastoredata' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/migrationhub' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/mobile' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/mobileanalytics' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/mq' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/mturk' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/opsworks' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/opsworkscm' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/organizations' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/pinpoint' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/polly' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/pricing' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/rds' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/redshift' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/rekognition' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/resourcegroups' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/resourcegroupstaggingapi' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/route53' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/route53domains' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/s3' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/sagemaker' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/sagemakerruntime' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/serverlessapplicationrepository' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/servicecatalog' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/servicediscovery' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/ses' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/shield' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/simpledb' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/sms' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/snowball' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/sns' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/sqs' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/ssm' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/stepfunctions' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/storagegateway' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/sts' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/support' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/swf' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/transcribeservice' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/translate' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/waf' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/wafregional' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/workdocs' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/workmail' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/workspaces' { + declare module.exports: any; +} + +declare module 'aws-sdk/clients/xray' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist-tools/browser-builder' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist-tools/client-creator' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist-tools/create-all-services' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist-tools/service-collector' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist-tools/transform' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist-tools/webpack.config.rn-core' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist-tools/webpack.config.rn-dep' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist-tools/webpack.config.rn' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist/aws-sdk-core-react-native' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist/aws-sdk-react-native' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist/aws-sdk' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist/aws-sdk.min' { + declare module.exports: any; +} + +declare module 'aws-sdk/dist/xml2js' { + declare module.exports: any; +} + +declare module 'aws-sdk/global' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/api_loader' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/aws' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/browser_loader' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/browser' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/browserCryptoLib' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/browserHashUtils' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/browserHmac' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/browserMd5' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/browserSha1' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/browserSha256' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/cloudfront/signer' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/config' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/core' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/cognito_identity_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/credential_provider_chain' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/ec2_metadata_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/ecs_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/environment_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/file_system_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/remote_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/saml_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/shared_ini_file_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/temporary_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/credentials/web_identity_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/dynamodb/converter' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/dynamodb/document_client' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/dynamodb/numberValue' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/dynamodb/set' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/dynamodb/translator' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/dynamodb/types' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/empty' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/event_listeners' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/http' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/http/node' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/http/xhr' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/json/builder' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/json/parser' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/metadata_service' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/model/api' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/model/collection' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/model/operation' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/model/paginator' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/model/resource_waiter' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/model/shape' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/node_loader' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/param_validator' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/polly/presigner' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/protocol/json' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/protocol/query' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/protocol/rest_json' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/protocol/rest_xml' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/protocol/rest' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/query/query_param_serializer' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/rds/signer' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/react-native-loader' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/react-native/add-content-type' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/region_config' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/request' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/resource_waiter' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/response' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/s3/managed_upload' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/sequential_executor' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/service' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/apigateway' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/cloudfront' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/cloudsearchdomain' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/cognitoidentity' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/dynamodb' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/ec2' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/glacier' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/iotdata' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/lambda' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/machinelearning' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/polly' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/rds' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/route53' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/s3' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/sqs' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/sts' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/services/swf' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/shared_ini' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/signers/presign' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/signers/request_signer' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/signers/s3' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/signers/v2' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/signers/v3' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/signers/v3https' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/signers/v4_credentials' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/signers/v4' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/state_machine' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/util' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/xml/browser_parser' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/xml/builder' { + declare module.exports: any; +} + +declare module 'aws-sdk/lib/xml/node_parser' { + declare module.exports: any; +} + +declare module 'aws-sdk/react-native' { + declare module.exports: any; +} + +declare module 'aws-sdk/scripts/changelog/add-change' { + declare module.exports: any; +} + +declare module 'aws-sdk/scripts/changelog/change-creator' { + declare module.exports: any; +} + +declare module 'aws-sdk/scripts/changelog/util' { + declare module.exports: any; +} + +declare module 'aws-sdk/scripts/lib/translator' { + declare module.exports: any; +} + +declare module 'aws-sdk/scripts/lib/ts-generator' { + declare module.exports: any; +} + +declare module 'aws-sdk/scripts/region-checker/index' { + declare module.exports: any; +} + +declare module 'aws-sdk/scripts/region-checker/whitelist' { + declare module.exports: any; +} + +declare module 'aws-sdk/scripts/services-table-generator' { + declare module.exports: any; +} + +declare module 'aws-sdk/scripts/typings-generator' { + declare module.exports: any; +} + +// Filename aliases +declare module 'aws-sdk/browser.js' { + declare module.exports: $Exports<'aws-sdk/browser'>; +} +declare module 'aws-sdk/clients/acm.js' { + declare module.exports: $Exports<'aws-sdk/clients/acm'>; +} +declare module 'aws-sdk/clients/alexaforbusiness.js' { + declare module.exports: $Exports<'aws-sdk/clients/alexaforbusiness'>; +} +declare module 'aws-sdk/clients/all.js' { + declare module.exports: $Exports<'aws-sdk/clients/all'>; +} +declare module 'aws-sdk/clients/apigateway.js' { + declare module.exports: $Exports<'aws-sdk/clients/apigateway'>; +} +declare module 'aws-sdk/clients/applicationautoscaling.js' { + declare module.exports: $Exports<'aws-sdk/clients/applicationautoscaling'>; +} +declare module 'aws-sdk/clients/appstream.js' { + declare module.exports: $Exports<'aws-sdk/clients/appstream'>; +} +declare module 'aws-sdk/clients/appsync.js' { + declare module.exports: $Exports<'aws-sdk/clients/appsync'>; +} +declare module 'aws-sdk/clients/athena.js' { + declare module.exports: $Exports<'aws-sdk/clients/athena'>; +} +declare module 'aws-sdk/clients/autoscaling.js' { + declare module.exports: $Exports<'aws-sdk/clients/autoscaling'>; +} +declare module 'aws-sdk/clients/autoscalingplans.js' { + declare module.exports: $Exports<'aws-sdk/clients/autoscalingplans'>; +} +declare module 'aws-sdk/clients/batch.js' { + declare module.exports: $Exports<'aws-sdk/clients/batch'>; +} +declare module 'aws-sdk/clients/browser_default.js' { + declare module.exports: $Exports<'aws-sdk/clients/browser_default'>; +} +declare module 'aws-sdk/clients/budgets.js' { + declare module.exports: $Exports<'aws-sdk/clients/budgets'>; +} +declare module 'aws-sdk/clients/cloud9.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloud9'>; +} +declare module 'aws-sdk/clients/clouddirectory.js' { + declare module.exports: $Exports<'aws-sdk/clients/clouddirectory'>; +} +declare module 'aws-sdk/clients/cloudformation.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloudformation'>; +} +declare module 'aws-sdk/clients/cloudfront.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloudfront'>; +} +declare module 'aws-sdk/clients/cloudhsm.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloudhsm'>; +} +declare module 'aws-sdk/clients/cloudhsmv2.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloudhsmv2'>; +} +declare module 'aws-sdk/clients/cloudsearch.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloudsearch'>; +} +declare module 'aws-sdk/clients/cloudsearchdomain.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloudsearchdomain'>; +} +declare module 'aws-sdk/clients/cloudtrail.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloudtrail'>; +} +declare module 'aws-sdk/clients/cloudwatch.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloudwatch'>; +} +declare module 'aws-sdk/clients/cloudwatchevents.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloudwatchevents'>; +} +declare module 'aws-sdk/clients/cloudwatchlogs.js' { + declare module.exports: $Exports<'aws-sdk/clients/cloudwatchlogs'>; +} +declare module 'aws-sdk/clients/codebuild.js' { + declare module.exports: $Exports<'aws-sdk/clients/codebuild'>; +} +declare module 'aws-sdk/clients/codecommit.js' { + declare module.exports: $Exports<'aws-sdk/clients/codecommit'>; +} +declare module 'aws-sdk/clients/codedeploy.js' { + declare module.exports: $Exports<'aws-sdk/clients/codedeploy'>; +} +declare module 'aws-sdk/clients/codepipeline.js' { + declare module.exports: $Exports<'aws-sdk/clients/codepipeline'>; +} +declare module 'aws-sdk/clients/codestar.js' { + declare module.exports: $Exports<'aws-sdk/clients/codestar'>; +} +declare module 'aws-sdk/clients/cognitoidentity.js' { + declare module.exports: $Exports<'aws-sdk/clients/cognitoidentity'>; +} +declare module 'aws-sdk/clients/cognitoidentityserviceprovider.js' { + declare module.exports: $Exports<'aws-sdk/clients/cognitoidentityserviceprovider'>; +} +declare module 'aws-sdk/clients/cognitosync.js' { + declare module.exports: $Exports<'aws-sdk/clients/cognitosync'>; +} +declare module 'aws-sdk/clients/comprehend.js' { + declare module.exports: $Exports<'aws-sdk/clients/comprehend'>; +} +declare module 'aws-sdk/clients/configservice.js' { + declare module.exports: $Exports<'aws-sdk/clients/configservice'>; +} +declare module 'aws-sdk/clients/costexplorer.js' { + declare module.exports: $Exports<'aws-sdk/clients/costexplorer'>; +} +declare module 'aws-sdk/clients/cur.js' { + declare module.exports: $Exports<'aws-sdk/clients/cur'>; +} +declare module 'aws-sdk/clients/datapipeline.js' { + declare module.exports: $Exports<'aws-sdk/clients/datapipeline'>; +} +declare module 'aws-sdk/clients/dax.js' { + declare module.exports: $Exports<'aws-sdk/clients/dax'>; +} +declare module 'aws-sdk/clients/devicefarm.js' { + declare module.exports: $Exports<'aws-sdk/clients/devicefarm'>; +} +declare module 'aws-sdk/clients/directconnect.js' { + declare module.exports: $Exports<'aws-sdk/clients/directconnect'>; +} +declare module 'aws-sdk/clients/directoryservice.js' { + declare module.exports: $Exports<'aws-sdk/clients/directoryservice'>; +} +declare module 'aws-sdk/clients/discovery.js' { + declare module.exports: $Exports<'aws-sdk/clients/discovery'>; +} +declare module 'aws-sdk/clients/dms.js' { + declare module.exports: $Exports<'aws-sdk/clients/dms'>; +} +declare module 'aws-sdk/clients/dynamodb.js' { + declare module.exports: $Exports<'aws-sdk/clients/dynamodb'>; +} +declare module 'aws-sdk/clients/dynamodbstreams.js' { + declare module.exports: $Exports<'aws-sdk/clients/dynamodbstreams'>; +} +declare module 'aws-sdk/clients/ec2.js' { + declare module.exports: $Exports<'aws-sdk/clients/ec2'>; +} +declare module 'aws-sdk/clients/ecr.js' { + declare module.exports: $Exports<'aws-sdk/clients/ecr'>; +} +declare module 'aws-sdk/clients/ecs.js' { + declare module.exports: $Exports<'aws-sdk/clients/ecs'>; +} +declare module 'aws-sdk/clients/efs.js' { + declare module.exports: $Exports<'aws-sdk/clients/efs'>; +} +declare module 'aws-sdk/clients/elasticache.js' { + declare module.exports: $Exports<'aws-sdk/clients/elasticache'>; +} +declare module 'aws-sdk/clients/elasticbeanstalk.js' { + declare module.exports: $Exports<'aws-sdk/clients/elasticbeanstalk'>; +} +declare module 'aws-sdk/clients/elastictranscoder.js' { + declare module.exports: $Exports<'aws-sdk/clients/elastictranscoder'>; +} +declare module 'aws-sdk/clients/elb.js' { + declare module.exports: $Exports<'aws-sdk/clients/elb'>; +} +declare module 'aws-sdk/clients/elbv2.js' { + declare module.exports: $Exports<'aws-sdk/clients/elbv2'>; +} +declare module 'aws-sdk/clients/emr.js' { + declare module.exports: $Exports<'aws-sdk/clients/emr'>; +} +declare module 'aws-sdk/clients/es.js' { + declare module.exports: $Exports<'aws-sdk/clients/es'>; +} +declare module 'aws-sdk/clients/firehose.js' { + declare module.exports: $Exports<'aws-sdk/clients/firehose'>; +} +declare module 'aws-sdk/clients/gamelift.js' { + declare module.exports: $Exports<'aws-sdk/clients/gamelift'>; +} +declare module 'aws-sdk/clients/glacier.js' { + declare module.exports: $Exports<'aws-sdk/clients/glacier'>; +} +declare module 'aws-sdk/clients/glue.js' { + declare module.exports: $Exports<'aws-sdk/clients/glue'>; +} +declare module 'aws-sdk/clients/greengrass.js' { + declare module.exports: $Exports<'aws-sdk/clients/greengrass'>; +} +declare module 'aws-sdk/clients/guardduty.js' { + declare module.exports: $Exports<'aws-sdk/clients/guardduty'>; +} +declare module 'aws-sdk/clients/health.js' { + declare module.exports: $Exports<'aws-sdk/clients/health'>; +} +declare module 'aws-sdk/clients/iam.js' { + declare module.exports: $Exports<'aws-sdk/clients/iam'>; +} +declare module 'aws-sdk/clients/importexport.js' { + declare module.exports: $Exports<'aws-sdk/clients/importexport'>; +} +declare module 'aws-sdk/clients/inspector.js' { + declare module.exports: $Exports<'aws-sdk/clients/inspector'>; +} +declare module 'aws-sdk/clients/iot.js' { + declare module.exports: $Exports<'aws-sdk/clients/iot'>; +} +declare module 'aws-sdk/clients/iotdata.js' { + declare module.exports: $Exports<'aws-sdk/clients/iotdata'>; +} +declare module 'aws-sdk/clients/iotjobsdataplane.js' { + declare module.exports: $Exports<'aws-sdk/clients/iotjobsdataplane'>; +} +declare module 'aws-sdk/clients/kinesis.js' { + declare module.exports: $Exports<'aws-sdk/clients/kinesis'>; +} +declare module 'aws-sdk/clients/kinesisanalytics.js' { + declare module.exports: $Exports<'aws-sdk/clients/kinesisanalytics'>; +} +declare module 'aws-sdk/clients/kinesisvideo.js' { + declare module.exports: $Exports<'aws-sdk/clients/kinesisvideo'>; +} +declare module 'aws-sdk/clients/kinesisvideoarchivedmedia.js' { + declare module.exports: $Exports<'aws-sdk/clients/kinesisvideoarchivedmedia'>; +} +declare module 'aws-sdk/clients/kinesisvideomedia.js' { + declare module.exports: $Exports<'aws-sdk/clients/kinesisvideomedia'>; +} +declare module 'aws-sdk/clients/kms.js' { + declare module.exports: $Exports<'aws-sdk/clients/kms'>; +} +declare module 'aws-sdk/clients/lambda.js' { + declare module.exports: $Exports<'aws-sdk/clients/lambda'>; +} +declare module 'aws-sdk/clients/lexmodelbuildingservice.js' { + declare module.exports: $Exports<'aws-sdk/clients/lexmodelbuildingservice'>; +} +declare module 'aws-sdk/clients/lexruntime.js' { + declare module.exports: $Exports<'aws-sdk/clients/lexruntime'>; +} +declare module 'aws-sdk/clients/lightsail.js' { + declare module.exports: $Exports<'aws-sdk/clients/lightsail'>; +} +declare module 'aws-sdk/clients/machinelearning.js' { + declare module.exports: $Exports<'aws-sdk/clients/machinelearning'>; +} +declare module 'aws-sdk/clients/marketplacecommerceanalytics.js' { + declare module.exports: $Exports<'aws-sdk/clients/marketplacecommerceanalytics'>; +} +declare module 'aws-sdk/clients/marketplaceentitlementservice.js' { + declare module.exports: $Exports<'aws-sdk/clients/marketplaceentitlementservice'>; +} +declare module 'aws-sdk/clients/marketplacemetering.js' { + declare module.exports: $Exports<'aws-sdk/clients/marketplacemetering'>; +} +declare module 'aws-sdk/clients/mediaconvert.js' { + declare module.exports: $Exports<'aws-sdk/clients/mediaconvert'>; +} +declare module 'aws-sdk/clients/medialive.js' { + declare module.exports: $Exports<'aws-sdk/clients/medialive'>; +} +declare module 'aws-sdk/clients/mediapackage.js' { + declare module.exports: $Exports<'aws-sdk/clients/mediapackage'>; +} +declare module 'aws-sdk/clients/mediastore.js' { + declare module.exports: $Exports<'aws-sdk/clients/mediastore'>; +} +declare module 'aws-sdk/clients/mediastoredata.js' { + declare module.exports: $Exports<'aws-sdk/clients/mediastoredata'>; +} +declare module 'aws-sdk/clients/migrationhub.js' { + declare module.exports: $Exports<'aws-sdk/clients/migrationhub'>; +} +declare module 'aws-sdk/clients/mobile.js' { + declare module.exports: $Exports<'aws-sdk/clients/mobile'>; +} +declare module 'aws-sdk/clients/mobileanalytics.js' { + declare module.exports: $Exports<'aws-sdk/clients/mobileanalytics'>; +} +declare module 'aws-sdk/clients/mq.js' { + declare module.exports: $Exports<'aws-sdk/clients/mq'>; +} +declare module 'aws-sdk/clients/mturk.js' { + declare module.exports: $Exports<'aws-sdk/clients/mturk'>; +} +declare module 'aws-sdk/clients/opsworks.js' { + declare module.exports: $Exports<'aws-sdk/clients/opsworks'>; +} +declare module 'aws-sdk/clients/opsworkscm.js' { + declare module.exports: $Exports<'aws-sdk/clients/opsworkscm'>; +} +declare module 'aws-sdk/clients/organizations.js' { + declare module.exports: $Exports<'aws-sdk/clients/organizations'>; +} +declare module 'aws-sdk/clients/pinpoint.js' { + declare module.exports: $Exports<'aws-sdk/clients/pinpoint'>; +} +declare module 'aws-sdk/clients/polly.js' { + declare module.exports: $Exports<'aws-sdk/clients/polly'>; +} +declare module 'aws-sdk/clients/pricing.js' { + declare module.exports: $Exports<'aws-sdk/clients/pricing'>; +} +declare module 'aws-sdk/clients/rds.js' { + declare module.exports: $Exports<'aws-sdk/clients/rds'>; +} +declare module 'aws-sdk/clients/redshift.js' { + declare module.exports: $Exports<'aws-sdk/clients/redshift'>; +} +declare module 'aws-sdk/clients/rekognition.js' { + declare module.exports: $Exports<'aws-sdk/clients/rekognition'>; +} +declare module 'aws-sdk/clients/resourcegroups.js' { + declare module.exports: $Exports<'aws-sdk/clients/resourcegroups'>; +} +declare module 'aws-sdk/clients/resourcegroupstaggingapi.js' { + declare module.exports: $Exports<'aws-sdk/clients/resourcegroupstaggingapi'>; +} +declare module 'aws-sdk/clients/route53.js' { + declare module.exports: $Exports<'aws-sdk/clients/route53'>; +} +declare module 'aws-sdk/clients/route53domains.js' { + declare module.exports: $Exports<'aws-sdk/clients/route53domains'>; +} +declare module 'aws-sdk/clients/s3.js' { + declare module.exports: $Exports<'aws-sdk/clients/s3'>; +} +declare module 'aws-sdk/clients/sagemaker.js' { + declare module.exports: $Exports<'aws-sdk/clients/sagemaker'>; +} +declare module 'aws-sdk/clients/sagemakerruntime.js' { + declare module.exports: $Exports<'aws-sdk/clients/sagemakerruntime'>; +} +declare module 'aws-sdk/clients/serverlessapplicationrepository.js' { + declare module.exports: $Exports<'aws-sdk/clients/serverlessapplicationrepository'>; +} +declare module 'aws-sdk/clients/servicecatalog.js' { + declare module.exports: $Exports<'aws-sdk/clients/servicecatalog'>; +} +declare module 'aws-sdk/clients/servicediscovery.js' { + declare module.exports: $Exports<'aws-sdk/clients/servicediscovery'>; +} +declare module 'aws-sdk/clients/ses.js' { + declare module.exports: $Exports<'aws-sdk/clients/ses'>; +} +declare module 'aws-sdk/clients/shield.js' { + declare module.exports: $Exports<'aws-sdk/clients/shield'>; +} +declare module 'aws-sdk/clients/simpledb.js' { + declare module.exports: $Exports<'aws-sdk/clients/simpledb'>; +} +declare module 'aws-sdk/clients/sms.js' { + declare module.exports: $Exports<'aws-sdk/clients/sms'>; +} +declare module 'aws-sdk/clients/snowball.js' { + declare module.exports: $Exports<'aws-sdk/clients/snowball'>; +} +declare module 'aws-sdk/clients/sns.js' { + declare module.exports: $Exports<'aws-sdk/clients/sns'>; +} +declare module 'aws-sdk/clients/sqs.js' { + declare module.exports: $Exports<'aws-sdk/clients/sqs'>; +} +declare module 'aws-sdk/clients/ssm.js' { + declare module.exports: $Exports<'aws-sdk/clients/ssm'>; +} +declare module 'aws-sdk/clients/stepfunctions.js' { + declare module.exports: $Exports<'aws-sdk/clients/stepfunctions'>; +} +declare module 'aws-sdk/clients/storagegateway.js' { + declare module.exports: $Exports<'aws-sdk/clients/storagegateway'>; +} +declare module 'aws-sdk/clients/sts.js' { + declare module.exports: $Exports<'aws-sdk/clients/sts'>; +} +declare module 'aws-sdk/clients/support.js' { + declare module.exports: $Exports<'aws-sdk/clients/support'>; +} +declare module 'aws-sdk/clients/swf.js' { + declare module.exports: $Exports<'aws-sdk/clients/swf'>; +} +declare module 'aws-sdk/clients/transcribeservice.js' { + declare module.exports: $Exports<'aws-sdk/clients/transcribeservice'>; +} +declare module 'aws-sdk/clients/translate.js' { + declare module.exports: $Exports<'aws-sdk/clients/translate'>; +} +declare module 'aws-sdk/clients/waf.js' { + declare module.exports: $Exports<'aws-sdk/clients/waf'>; +} +declare module 'aws-sdk/clients/wafregional.js' { + declare module.exports: $Exports<'aws-sdk/clients/wafregional'>; +} +declare module 'aws-sdk/clients/workdocs.js' { + declare module.exports: $Exports<'aws-sdk/clients/workdocs'>; +} +declare module 'aws-sdk/clients/workmail.js' { + declare module.exports: $Exports<'aws-sdk/clients/workmail'>; +} +declare module 'aws-sdk/clients/workspaces.js' { + declare module.exports: $Exports<'aws-sdk/clients/workspaces'>; +} +declare module 'aws-sdk/clients/xray.js' { + declare module.exports: $Exports<'aws-sdk/clients/xray'>; +} +declare module 'aws-sdk/dist-tools/browser-builder.js' { + declare module.exports: $Exports<'aws-sdk/dist-tools/browser-builder'>; +} +declare module 'aws-sdk/dist-tools/client-creator.js' { + declare module.exports: $Exports<'aws-sdk/dist-tools/client-creator'>; +} +declare module 'aws-sdk/dist-tools/create-all-services.js' { + declare module.exports: $Exports<'aws-sdk/dist-tools/create-all-services'>; +} +declare module 'aws-sdk/dist-tools/service-collector.js' { + declare module.exports: $Exports<'aws-sdk/dist-tools/service-collector'>; +} +declare module 'aws-sdk/dist-tools/transform.js' { + declare module.exports: $Exports<'aws-sdk/dist-tools/transform'>; +} +declare module 'aws-sdk/dist-tools/webpack.config.rn-core.js' { + declare module.exports: $Exports<'aws-sdk/dist-tools/webpack.config.rn-core'>; +} +declare module 'aws-sdk/dist-tools/webpack.config.rn-dep.js' { + declare module.exports: $Exports<'aws-sdk/dist-tools/webpack.config.rn-dep'>; +} +declare module 'aws-sdk/dist-tools/webpack.config.rn.js' { + declare module.exports: $Exports<'aws-sdk/dist-tools/webpack.config.rn'>; +} +declare module 'aws-sdk/dist/aws-sdk-core-react-native.js' { + declare module.exports: $Exports<'aws-sdk/dist/aws-sdk-core-react-native'>; +} +declare module 'aws-sdk/dist/aws-sdk-react-native.js' { + declare module.exports: $Exports<'aws-sdk/dist/aws-sdk-react-native'>; +} +declare module 'aws-sdk/dist/aws-sdk.js' { + declare module.exports: $Exports<'aws-sdk/dist/aws-sdk'>; +} +declare module 'aws-sdk/dist/aws-sdk.min.js' { + declare module.exports: $Exports<'aws-sdk/dist/aws-sdk.min'>; +} +declare module 'aws-sdk/dist/xml2js.js' { + declare module.exports: $Exports<'aws-sdk/dist/xml2js'>; +} +declare module 'aws-sdk/global.js' { + declare module.exports: $Exports<'aws-sdk/global'>; +} +declare module 'aws-sdk/index' { + declare module.exports: $Exports<'aws-sdk'>; +} +declare module 'aws-sdk/index.js' { + declare module.exports: $Exports<'aws-sdk'>; +} +declare module 'aws-sdk/lib/api_loader.js' { + declare module.exports: $Exports<'aws-sdk/lib/api_loader'>; +} +declare module 'aws-sdk/lib/aws.js' { + declare module.exports: $Exports<'aws-sdk/lib/aws'>; +} +declare module 'aws-sdk/lib/browser_loader.js' { + declare module.exports: $Exports<'aws-sdk/lib/browser_loader'>; +} +declare module 'aws-sdk/lib/browser.js' { + declare module.exports: $Exports<'aws-sdk/lib/browser'>; +} +declare module 'aws-sdk/lib/browserCryptoLib.js' { + declare module.exports: $Exports<'aws-sdk/lib/browserCryptoLib'>; +} +declare module 'aws-sdk/lib/browserHashUtils.js' { + declare module.exports: $Exports<'aws-sdk/lib/browserHashUtils'>; +} +declare module 'aws-sdk/lib/browserHmac.js' { + declare module.exports: $Exports<'aws-sdk/lib/browserHmac'>; +} +declare module 'aws-sdk/lib/browserMd5.js' { + declare module.exports: $Exports<'aws-sdk/lib/browserMd5'>; +} +declare module 'aws-sdk/lib/browserSha1.js' { + declare module.exports: $Exports<'aws-sdk/lib/browserSha1'>; +} +declare module 'aws-sdk/lib/browserSha256.js' { + declare module.exports: $Exports<'aws-sdk/lib/browserSha256'>; +} +declare module 'aws-sdk/lib/cloudfront/signer.js' { + declare module.exports: $Exports<'aws-sdk/lib/cloudfront/signer'>; +} +declare module 'aws-sdk/lib/config.js' { + declare module.exports: $Exports<'aws-sdk/lib/config'>; +} +declare module 'aws-sdk/lib/core.js' { + declare module.exports: $Exports<'aws-sdk/lib/core'>; +} +declare module 'aws-sdk/lib/credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials'>; +} +declare module 'aws-sdk/lib/credentials/cognito_identity_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/cognito_identity_credentials'>; +} +declare module 'aws-sdk/lib/credentials/credential_provider_chain.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/credential_provider_chain'>; +} +declare module 'aws-sdk/lib/credentials/ec2_metadata_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/ec2_metadata_credentials'>; +} +declare module 'aws-sdk/lib/credentials/ecs_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/ecs_credentials'>; +} +declare module 'aws-sdk/lib/credentials/environment_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/environment_credentials'>; +} +declare module 'aws-sdk/lib/credentials/file_system_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/file_system_credentials'>; +} +declare module 'aws-sdk/lib/credentials/remote_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/remote_credentials'>; +} +declare module 'aws-sdk/lib/credentials/saml_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/saml_credentials'>; +} +declare module 'aws-sdk/lib/credentials/shared_ini_file_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/shared_ini_file_credentials'>; +} +declare module 'aws-sdk/lib/credentials/temporary_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/temporary_credentials'>; +} +declare module 'aws-sdk/lib/credentials/web_identity_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/credentials/web_identity_credentials'>; +} +declare module 'aws-sdk/lib/dynamodb/converter.js' { + declare module.exports: $Exports<'aws-sdk/lib/dynamodb/converter'>; +} +declare module 'aws-sdk/lib/dynamodb/document_client.js' { + declare module.exports: $Exports<'aws-sdk/lib/dynamodb/document_client'>; +} +declare module 'aws-sdk/lib/dynamodb/numberValue.js' { + declare module.exports: $Exports<'aws-sdk/lib/dynamodb/numberValue'>; +} +declare module 'aws-sdk/lib/dynamodb/set.js' { + declare module.exports: $Exports<'aws-sdk/lib/dynamodb/set'>; +} +declare module 'aws-sdk/lib/dynamodb/translator.js' { + declare module.exports: $Exports<'aws-sdk/lib/dynamodb/translator'>; +} +declare module 'aws-sdk/lib/dynamodb/types.js' { + declare module.exports: $Exports<'aws-sdk/lib/dynamodb/types'>; +} +declare module 'aws-sdk/lib/empty.js' { + declare module.exports: $Exports<'aws-sdk/lib/empty'>; +} +declare module 'aws-sdk/lib/event_listeners.js' { + declare module.exports: $Exports<'aws-sdk/lib/event_listeners'>; +} +declare module 'aws-sdk/lib/http.js' { + declare module.exports: $Exports<'aws-sdk/lib/http'>; +} +declare module 'aws-sdk/lib/http/node.js' { + declare module.exports: $Exports<'aws-sdk/lib/http/node'>; +} +declare module 'aws-sdk/lib/http/xhr.js' { + declare module.exports: $Exports<'aws-sdk/lib/http/xhr'>; +} +declare module 'aws-sdk/lib/json/builder.js' { + declare module.exports: $Exports<'aws-sdk/lib/json/builder'>; +} +declare module 'aws-sdk/lib/json/parser.js' { + declare module.exports: $Exports<'aws-sdk/lib/json/parser'>; +} +declare module 'aws-sdk/lib/metadata_service.js' { + declare module.exports: $Exports<'aws-sdk/lib/metadata_service'>; +} +declare module 'aws-sdk/lib/model/api.js' { + declare module.exports: $Exports<'aws-sdk/lib/model/api'>; +} +declare module 'aws-sdk/lib/model/collection.js' { + declare module.exports: $Exports<'aws-sdk/lib/model/collection'>; +} +declare module 'aws-sdk/lib/model/operation.js' { + declare module.exports: $Exports<'aws-sdk/lib/model/operation'>; +} +declare module 'aws-sdk/lib/model/paginator.js' { + declare module.exports: $Exports<'aws-sdk/lib/model/paginator'>; +} +declare module 'aws-sdk/lib/model/resource_waiter.js' { + declare module.exports: $Exports<'aws-sdk/lib/model/resource_waiter'>; +} +declare module 'aws-sdk/lib/model/shape.js' { + declare module.exports: $Exports<'aws-sdk/lib/model/shape'>; +} +declare module 'aws-sdk/lib/node_loader.js' { + declare module.exports: $Exports<'aws-sdk/lib/node_loader'>; +} +declare module 'aws-sdk/lib/param_validator.js' { + declare module.exports: $Exports<'aws-sdk/lib/param_validator'>; +} +declare module 'aws-sdk/lib/polly/presigner.js' { + declare module.exports: $Exports<'aws-sdk/lib/polly/presigner'>; +} +declare module 'aws-sdk/lib/protocol/json.js' { + declare module.exports: $Exports<'aws-sdk/lib/protocol/json'>; +} +declare module 'aws-sdk/lib/protocol/query.js' { + declare module.exports: $Exports<'aws-sdk/lib/protocol/query'>; +} +declare module 'aws-sdk/lib/protocol/rest_json.js' { + declare module.exports: $Exports<'aws-sdk/lib/protocol/rest_json'>; +} +declare module 'aws-sdk/lib/protocol/rest_xml.js' { + declare module.exports: $Exports<'aws-sdk/lib/protocol/rest_xml'>; +} +declare module 'aws-sdk/lib/protocol/rest.js' { + declare module.exports: $Exports<'aws-sdk/lib/protocol/rest'>; +} +declare module 'aws-sdk/lib/query/query_param_serializer.js' { + declare module.exports: $Exports<'aws-sdk/lib/query/query_param_serializer'>; +} +declare module 'aws-sdk/lib/rds/signer.js' { + declare module.exports: $Exports<'aws-sdk/lib/rds/signer'>; +} +declare module 'aws-sdk/lib/react-native-loader.js' { + declare module.exports: $Exports<'aws-sdk/lib/react-native-loader'>; +} +declare module 'aws-sdk/lib/react-native/add-content-type.js' { + declare module.exports: $Exports<'aws-sdk/lib/react-native/add-content-type'>; +} +declare module 'aws-sdk/lib/region_config.js' { + declare module.exports: $Exports<'aws-sdk/lib/region_config'>; +} +declare module 'aws-sdk/lib/request.js' { + declare module.exports: $Exports<'aws-sdk/lib/request'>; +} +declare module 'aws-sdk/lib/resource_waiter.js' { + declare module.exports: $Exports<'aws-sdk/lib/resource_waiter'>; +} +declare module 'aws-sdk/lib/response.js' { + declare module.exports: $Exports<'aws-sdk/lib/response'>; +} +declare module 'aws-sdk/lib/s3/managed_upload.js' { + declare module.exports: $Exports<'aws-sdk/lib/s3/managed_upload'>; +} +declare module 'aws-sdk/lib/sequential_executor.js' { + declare module.exports: $Exports<'aws-sdk/lib/sequential_executor'>; +} +declare module 'aws-sdk/lib/service.js' { + declare module.exports: $Exports<'aws-sdk/lib/service'>; +} +declare module 'aws-sdk/lib/services/apigateway.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/apigateway'>; +} +declare module 'aws-sdk/lib/services/cloudfront.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/cloudfront'>; +} +declare module 'aws-sdk/lib/services/cloudsearchdomain.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/cloudsearchdomain'>; +} +declare module 'aws-sdk/lib/services/cognitoidentity.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/cognitoidentity'>; +} +declare module 'aws-sdk/lib/services/dynamodb.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/dynamodb'>; +} +declare module 'aws-sdk/lib/services/ec2.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/ec2'>; +} +declare module 'aws-sdk/lib/services/glacier.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/glacier'>; +} +declare module 'aws-sdk/lib/services/iotdata.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/iotdata'>; +} +declare module 'aws-sdk/lib/services/lambda.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/lambda'>; +} +declare module 'aws-sdk/lib/services/machinelearning.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/machinelearning'>; +} +declare module 'aws-sdk/lib/services/polly.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/polly'>; +} +declare module 'aws-sdk/lib/services/rds.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/rds'>; +} +declare module 'aws-sdk/lib/services/route53.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/route53'>; +} +declare module 'aws-sdk/lib/services/s3.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/s3'>; +} +declare module 'aws-sdk/lib/services/sqs.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/sqs'>; +} +declare module 'aws-sdk/lib/services/sts.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/sts'>; +} +declare module 'aws-sdk/lib/services/swf.js' { + declare module.exports: $Exports<'aws-sdk/lib/services/swf'>; +} +declare module 'aws-sdk/lib/shared_ini.js' { + declare module.exports: $Exports<'aws-sdk/lib/shared_ini'>; +} +declare module 'aws-sdk/lib/signers/presign.js' { + declare module.exports: $Exports<'aws-sdk/lib/signers/presign'>; +} +declare module 'aws-sdk/lib/signers/request_signer.js' { + declare module.exports: $Exports<'aws-sdk/lib/signers/request_signer'>; +} +declare module 'aws-sdk/lib/signers/s3.js' { + declare module.exports: $Exports<'aws-sdk/lib/signers/s3'>; +} +declare module 'aws-sdk/lib/signers/v2.js' { + declare module.exports: $Exports<'aws-sdk/lib/signers/v2'>; +} +declare module 'aws-sdk/lib/signers/v3.js' { + declare module.exports: $Exports<'aws-sdk/lib/signers/v3'>; +} +declare module 'aws-sdk/lib/signers/v3https.js' { + declare module.exports: $Exports<'aws-sdk/lib/signers/v3https'>; +} +declare module 'aws-sdk/lib/signers/v4_credentials.js' { + declare module.exports: $Exports<'aws-sdk/lib/signers/v4_credentials'>; +} +declare module 'aws-sdk/lib/signers/v4.js' { + declare module.exports: $Exports<'aws-sdk/lib/signers/v4'>; +} +declare module 'aws-sdk/lib/state_machine.js' { + declare module.exports: $Exports<'aws-sdk/lib/state_machine'>; +} +declare module 'aws-sdk/lib/util.js' { + declare module.exports: $Exports<'aws-sdk/lib/util'>; +} +declare module 'aws-sdk/lib/xml/browser_parser.js' { + declare module.exports: $Exports<'aws-sdk/lib/xml/browser_parser'>; +} +declare module 'aws-sdk/lib/xml/builder.js' { + declare module.exports: $Exports<'aws-sdk/lib/xml/builder'>; +} +declare module 'aws-sdk/lib/xml/node_parser.js' { + declare module.exports: $Exports<'aws-sdk/lib/xml/node_parser'>; +} +declare module 'aws-sdk/react-native.js' { + declare module.exports: $Exports<'aws-sdk/react-native'>; +} +declare module 'aws-sdk/scripts/changelog/add-change.js' { + declare module.exports: $Exports<'aws-sdk/scripts/changelog/add-change'>; +} +declare module 'aws-sdk/scripts/changelog/change-creator.js' { + declare module.exports: $Exports<'aws-sdk/scripts/changelog/change-creator'>; +} +declare module 'aws-sdk/scripts/changelog/util.js' { + declare module.exports: $Exports<'aws-sdk/scripts/changelog/util'>; +} +declare module 'aws-sdk/scripts/lib/translator.js' { + declare module.exports: $Exports<'aws-sdk/scripts/lib/translator'>; +} +declare module 'aws-sdk/scripts/lib/ts-generator.js' { + declare module.exports: $Exports<'aws-sdk/scripts/lib/ts-generator'>; +} +declare module 'aws-sdk/scripts/region-checker/index.js' { + declare module.exports: $Exports<'aws-sdk/scripts/region-checker/index'>; +} +declare module 'aws-sdk/scripts/region-checker/whitelist.js' { + declare module.exports: $Exports<'aws-sdk/scripts/region-checker/whitelist'>; +} +declare module 'aws-sdk/scripts/services-table-generator.js' { + declare module.exports: $Exports<'aws-sdk/scripts/services-table-generator'>; +} +declare module 'aws-sdk/scripts/typings-generator.js' { + declare module.exports: $Exports<'aws-sdk/scripts/typings-generator'>; +} diff --git a/flow-typed/npm/s3-image-uploader_vx.x.x.js b/flow-typed/npm/s3-image-uploader_vx.x.x.js deleted file mode 100644 index 9f7f70ea9d..0000000000 --- a/flow-typed/npm/s3-image-uploader_vx.x.x.js +++ /dev/null @@ -1,33 +0,0 @@ -// flow-typed signature: baee76376f183ef5fc657dfa65e21c66 -// flow-typed version: <>/s3-image-uploader_v^1.0.7/flow_v0.63.1 - -/** - * This is an autogenerated libdef stub for: - * - * 's3-image-uploader' - * - * Fill this stub out by replacing all the `any` types. - * - * Once filled out, we encourage you to share your work with the - * community by sending a pull request to: - * https://github.com/flowtype/flow-typed - */ - -declare module 's3-image-uploader' { - declare module.exports: any; -} - -/** - * We include stubs for each file inside this npm package in case you need to - * require those files directly. Feel free to delete any files that aren't - * needed. - */ - - -// Filename aliases -declare module 's3-image-uploader/index' { - declare module.exports: $Exports<'s3-image-uploader'>; -} -declare module 's3-image-uploader/index.js' { - declare module.exports: $Exports<'s3-image-uploader'>; -} diff --git a/flow-typed/npm/shortid_vx.x.x.js b/flow-typed/npm/shortid_vx.x.x.js new file mode 100644 index 0000000000..9a498046ca --- /dev/null +++ b/flow-typed/npm/shortid_vx.x.x.js @@ -0,0 +1,108 @@ +// flow-typed signature: 7b3061d921714787fed00295ae4e0433 +// flow-typed version: <>/shortid_v2.2.8/flow_v0.66.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'shortid' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'shortid' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'shortid/lib/alphabet' { + declare module.exports: any; +} + +declare module 'shortid/lib/build' { + declare module.exports: any; +} + +declare module 'shortid/lib/decode' { + declare module.exports: any; +} + +declare module 'shortid/lib/encode' { + declare module.exports: any; +} + +declare module 'shortid/lib/index' { + declare module.exports: any; +} + +declare module 'shortid/lib/is-valid' { + declare module.exports: any; +} + +declare module 'shortid/lib/random/random-byte-browser' { + declare module.exports: any; +} + +declare module 'shortid/lib/random/random-byte' { + declare module.exports: any; +} + +declare module 'shortid/lib/random/random-from-seed' { + declare module.exports: any; +} + +declare module 'shortid/lib/util/cluster-worker-id-browser' { + declare module.exports: any; +} + +declare module 'shortid/lib/util/cluster-worker-id' { + declare module.exports: any; +} + +// Filename aliases +declare module 'shortid/index' { + declare module.exports: $Exports<'shortid'>; +} +declare module 'shortid/index.js' { + declare module.exports: $Exports<'shortid'>; +} +declare module 'shortid/lib/alphabet.js' { + declare module.exports: $Exports<'shortid/lib/alphabet'>; +} +declare module 'shortid/lib/build.js' { + declare module.exports: $Exports<'shortid/lib/build'>; +} +declare module 'shortid/lib/decode.js' { + declare module.exports: $Exports<'shortid/lib/decode'>; +} +declare module 'shortid/lib/encode.js' { + declare module.exports: $Exports<'shortid/lib/encode'>; +} +declare module 'shortid/lib/index.js' { + declare module.exports: $Exports<'shortid/lib/index'>; +} +declare module 'shortid/lib/is-valid.js' { + declare module.exports: $Exports<'shortid/lib/is-valid'>; +} +declare module 'shortid/lib/random/random-byte-browser.js' { + declare module.exports: $Exports<'shortid/lib/random/random-byte-browser'>; +} +declare module 'shortid/lib/random/random-byte.js' { + declare module.exports: $Exports<'shortid/lib/random/random-byte'>; +} +declare module 'shortid/lib/random/random-from-seed.js' { + declare module.exports: $Exports<'shortid/lib/random/random-from-seed'>; +} +declare module 'shortid/lib/util/cluster-worker-id-browser.js' { + declare module.exports: $Exports<'shortid/lib/util/cluster-worker-id-browser'>; +} +declare module 'shortid/lib/util/cluster-worker-id.js' { + declare module.exports: $Exports<'shortid/lib/util/cluster-worker-id'>; +} diff --git a/iris/loaders/channel.js b/iris/loaders/channel.js index a8e362cbe7..84982dda4a 100644 --- a/iris/loaders/channel.js +++ b/iris/loaders/channel.js @@ -4,6 +4,7 @@ import { getChannelsThreadCounts, getChannelsMemberCounts, } from '../models/channel'; +import { getChannelsSettings } from '../models/channelSettings'; import createLoader from './create-loader'; import { getPendingUsersInChannels } from '../models/usersChannels'; import type { Loader } from './types'; @@ -27,6 +28,11 @@ export const __createChannelPendingMembersLoader = createLoader( 'group' ); +export const __createChannelSettingsLoader = createLoader( + channelIds => getChannelsSettings(channelIds), + 'group' +); + export default () => { throw new Error( '⚠️ Do not import loaders directly, get them from the GraphQL context instead! ⚠️' diff --git a/iris/loaders/index.js b/iris/loaders/index.js index ad82823aa2..f65cecda7f 100644 --- a/iris/loaders/index.js +++ b/iris/loaders/index.js @@ -20,6 +20,7 @@ import { __createChannelMemberCountLoader, __createChannelThreadCountLoader, __createChannelPendingMembersLoader, + __createChannelSettingsLoader, } from './channel'; import { __createCommunityLoader, @@ -57,6 +58,7 @@ const createLoaders = (options?: DataLoaderOptions) => ({ channelMemberCount: __createChannelMemberCountLoader(options), channelThreadCount: __createChannelThreadCountLoader(options), channelPendingUsers: __createChannelPendingMembersLoader(options), + channelSettings: __createChannelSettingsLoader(options), community: __createCommunityLoader(options), communityBySlug: __createCommunityBySlugLoader(options), communityRecurringPayments: __createCommunityRecurringPaymentsLoader(options), diff --git a/iris/migrations/20180316195507-create-channel-settings-table.js b/iris/migrations/20180316195507-create-channel-settings-table.js new file mode 100644 index 0000000000..0c60a9e477 --- /dev/null +++ b/iris/migrations/20180316195507-create-channel-settings-table.js @@ -0,0 +1,19 @@ +exports.up = function(r, conn) { + return r + .tableCreate('channelSettings') + .run(conn) + .then(() => + r + .table('channelSettings') + .indexCreate('channelId', r.row('channelId')) + .run(conn) + ) + .catch(err => { + console.error(err); + throw err; + }); +}; + +exports.down = function(r, conn) { + return Promise.all([r.tableDrop('channelSettings').run(conn)]); +}; diff --git a/iris/models/channelSettings.js b/iris/models/channelSettings.js new file mode 100644 index 0000000000..13b97f6ba9 --- /dev/null +++ b/iris/models/channelSettings.js @@ -0,0 +1,90 @@ +// @flow +const { db } = require('./db'); +import type { DBChannelSettings } from 'shared/types'; +import { getChannelById } from './channel'; +import shortid from 'shortid'; + +const defaultSettings = { + joinSettings: { + tokenJoinEnabled: false, + message: null, + }, +}; + +export const getChannelSettings = (id: string) => { + return db + .table('channelSettings') + .getAll(id, { index: 'channelId' }) + .run() + .then(data => { + if (!data || data.length === 0) { + return defaultSettings; + } + return data[0]; + }); +}; + +export const getChannelsSettings = ( + channelIds: Array +): Promise => { + return db + .table('channelSettings') + .getAll(...channelIds, { index: 'channelId' }) + .group('channelId') + .run(); +}; + +export const createChannelSettings = (id: string) => { + return db + .table('channelSettings') + .insert({ + channelId: id, + joinSettings: { + token: null, + tokenJoinEnabled: false, + }, + }) + .run() + .then(async () => await getChannelById(id)); +}; + +export const enableChannelTokenJoin = (id: string) => { + return db + .table('channelSettings') + .getAll(id, { index: 'channelId' }) + .update({ + joinSettings: { + tokenJoinEnabled: true, + token: shortid.generate(), + }, + }) + .run() + .then(async () => await getChannelById(id)); +}; + +export const disableChannelTokenJoin = (id: string) => { + return db + .table('channelSettings') + .getAll(id, { index: 'channelId' }) + .update({ + joinSettings: { + tokenJoinEnabled: false, + token: null, + }, + }) + .run() + .then(async () => await getChannelById(id)); +}; + +export const resetChannelJoinToken = (id: string) => { + return db + .table('channelSettings') + .getAll(id, { index: 'channelId' }) + .update({ + joinSettings: { + token: shortid.generate(), + }, + }) + .run() + .then(async () => await getChannelById(id)); +}; diff --git a/iris/models/thread.js b/iris/models/thread.js index 96c632c16c..b4faa9632b 100644 --- a/iris/models/thread.js +++ b/iris/models/thread.js @@ -10,7 +10,7 @@ const { NEW_DOCUMENTS, parseRange, createChangefeed } = require('./utils'); import { deleteMessagesInThread } from '../models/message'; import { turnOffAllThreadNotifications } from '../models/usersThreads'; import type { PaginationOptions } from '../utils/paginate-arrays'; -import type { DBThread } from 'shared/types'; +import type { DBThread, FileUpload } from 'shared/types'; import type { Timeframe } from './utils'; export const getThread = (threadId: string): Promise => { @@ -401,12 +401,7 @@ export const deleteThread = (threadId: string): Promise => { }); }; -type File = { - name: string, - type: string, - size: number, - path: string, -}; +type File = FileUpload; type Attachment = { attachmentType: string, diff --git a/iris/models/user.js b/iris/models/user.js index e0e8a6adda..af1254e273 100644 --- a/iris/models/user.js +++ b/iris/models/user.js @@ -4,7 +4,7 @@ import { uploadImage } from '../utils/s3'; import { createNewUsersSettings } from './usersSettings'; import { sendNewUserWelcomeEmailQueue } from 'shared/bull/queues'; import type { PaginationOptions } from '../utils/paginate-arrays'; -import type { DBUser } from 'shared/types'; +import type { DBUser, FileUpload } from 'shared/types'; type GetUserInput = { id?: string, @@ -184,7 +184,7 @@ const createOrFindUser = ( }) .catch(err => { if (user.id) { - console.log(err); + console.error(err); return new Error(`No user found for id ${user.id}.`); } return storeUser(user); @@ -246,11 +246,11 @@ const getUsersThreadCount = ( export type EditUserInput = { input: { - file?: any, + file?: FileUpload, name?: string, description?: string, website?: string, - coverFile?: string, + coverFile?: FileUpload, username?: string, timezone?: number, }, diff --git a/iris/models/usersChannels.js b/iris/models/usersChannels.js index 44bda35243..e148bde3be 100644 --- a/iris/models/usersChannels.js +++ b/iris/models/usersChannels.js @@ -269,7 +269,9 @@ const approvePendingUserInChannel = ( { returnChanges: true } ) .run() - .then(result => result.changes[0].new_val); + .then(() => { + return db.table('channels').get(channelId); + }); }; // toggles all pending users to make them a member in a channel. invoked by a diff --git a/iris/mutations/channel/disableChannelTokenJoin.js b/iris/mutations/channel/disableChannelTokenJoin.js new file mode 100644 index 0000000000..49d949d657 --- /dev/null +++ b/iris/mutations/channel/disableChannelTokenJoin.js @@ -0,0 +1,40 @@ +// @flow +import type { GraphQLContext } from '../../'; +import UserError from '../../utils/UserError'; +import { + createChannelSettings, + disableChannelTokenJoin, +} from '../../models/channelSettings'; + +type DisableChannelTokenJoinInput = { + input: { + id: string, + }, +}; + +export default async ( + _: any, + { input: { id: channelId } }: DisableChannelTokenJoinInput, + { user, loaders }: GraphQLContext +) => { + const currentUser = user; + if (!currentUser) { + return new UserError('You must be signed in to manage this channel.'); + } + + const [permissions, settings] = await Promise.all([ + loaders.userPermissionsInChannel.load([currentUser.id, channelId]), + loaders.channelSettings.load(channelId), + ]); + + if (!permissions.isOwner) { + return new UserError("You don't have permission to do this."); + } + + const hasSettings = settings && settings.reduction.length > 0; + if (hasSettings) { + return await disableChannelTokenJoin(channelId); + } else { + return await createChannelSettings(channelId); + } +}; diff --git a/iris/mutations/channel/enableChannelTokenJoin.js b/iris/mutations/channel/enableChannelTokenJoin.js new file mode 100644 index 0000000000..caa86cc079 --- /dev/null +++ b/iris/mutations/channel/enableChannelTokenJoin.js @@ -0,0 +1,42 @@ +// @flow +import type { GraphQLContext } from '../../'; +import UserError from '../../utils/UserError'; +import { + createChannelSettings, + enableChannelTokenJoin, +} from '../../models/channelSettings'; + +type EnableTokenJoinInput = { + input: { + id: string, + }, +}; + +export default async ( + _: any, + { input: { id: channelId } }: EnableTokenJoinInput, + { user, loaders }: GraphQLContext +) => { + const currentUser = user; + if (!currentUser) { + return new UserError('You must be signed in to manage this channel.'); + } + + const [permissions, settings] = await Promise.all([ + loaders.userPermissionsInChannel.load([currentUser.id, channelId]), + loaders.channelSettings.load(channelId), + ]); + + if (!permissions.isOwner) { + return new UserError("You don't have permission to do this."); + } + + const hasSettings = settings && settings.reduction.length > 0; + if (hasSettings) { + return await enableChannelTokenJoin(channelId); + } else { + return await createChannelSettings(channelId).then(() => + enableChannelTokenJoin(channelId) + ); + } +}; diff --git a/iris/mutations/channel/index.js b/iris/mutations/channel/index.js index d13bf7f48f..2460df9ac9 100644 --- a/iris/mutations/channel/index.js +++ b/iris/mutations/channel/index.js @@ -6,6 +6,10 @@ import toggleChannelSubscription from './toggleChannelSubscription'; import toggleChannelNotifications from './toggleChannelNotifications'; import togglePendingUser from './togglePendingUser'; import unblockUser from './unblockUser'; +import joinChannelWithToken from './joinChannelWithToken'; +import enableChannelTokenJoin from './enableChannelTokenJoin'; +import disableChannelTokenJoin from './disableChannelTokenJoin'; +import resetChannelJoinToken from './resetChannelJoinToken'; module.exports = { Mutation: { @@ -16,5 +20,9 @@ module.exports = { toggleChannelNotifications, togglePendingUser, unblockUser, + joinChannelWithToken, + enableChannelTokenJoin, + disableChannelTokenJoin, + resetChannelJoinToken, }, }; diff --git a/iris/mutations/channel/joinChannelWithToken.js b/iris/mutations/channel/joinChannelWithToken.js new file mode 100644 index 0000000000..fdde7f7541 --- /dev/null +++ b/iris/mutations/channel/joinChannelWithToken.js @@ -0,0 +1,104 @@ +// @flow +import type { GraphQLContext } from '../../'; +import UserError from '../../utils/UserError'; +import { + getUserPermissionsInChannel, + createMemberInChannel, + createMemberInDefaultChannels, + approvePendingUserInChannel, +} from '../../models/usersChannels'; +import { getChannelBySlug } from '../../models/channel'; +import { + getUserPermissionsInCommunity, + createMemberInCommunity, +} from '../../models/usersCommunities'; +import { getChannelSettings } from '../../models/channelSettings'; + +type JoinChannelWithTokenInput = { + input: { + communitySlug: string, + channelSlug: string, + token: string, + }, +}; + +export default async ( + _: any, + { input }: JoinChannelWithTokenInput, + { user }: GraphQLContext +) => { + const currentUser = user; + + if (!currentUser) { + return new UserError('You must be signed in to to join this channel.'); + } + + const { communitySlug, channelSlug, token } = input; + + const [communityPermissions, channelPermissions, channel] = await Promise.all( + [ + getUserPermissionsInCommunity(communitySlug, currentUser.id), + getUserPermissionsInChannel(channelSlug, currentUser.id), + getChannelBySlug(channelSlug, communitySlug), + ] + ); + + if (!channel) return new UserError('No channel found in this community'); + + const settings = await getChannelSettings(channel.id); + + if (!channel.isPrivate) { + return channel; + } + + if ( + channelPermissions.isOwner || + channelPermissions.isModerator || + channelPermissions.isMember + ) { + return channel; + } + + if (channelPermissions.isBlocked || communityPermissions.isBlocked) { + return new UserError("You don't have permission to view this channel"); + } + + if (!settings.joinSettings.tokenJoinEnabled) { + return new UserError( + "You can't join at this time, the token may have changed" + ); + } + if ( + settings.joinSettings.tokenJoinEnabled && + token !== settings.joinSettings.token + ) { + return new UserError( + "You can't join at this time, the token may have changed" + ); + } + + if (!communityPermissions.isMember) { + return await Promise.all([ + createMemberInCommunity(channel.communityId, currentUser.id), + createMemberInDefaultChannels(channel.communityId, currentUser.id), + ]) + .then(async () => { + if (channelPermissions.isPending) { + return await approvePendingUserInChannel(channel.id, currentUser.id); + } else { + return await createMemberInChannel(channel.id, currentUser.id); + } + }) + .then(joinedChannel => joinedChannel); + } + + if (channelPermissions.isPending) { + return await approvePendingUserInChannel(channel.id, currentUser.id); + } + + if (!channelPermissions.isMember) { + return await createMemberInChannel(channel.id, currentUser.id); + } + + return new UserError("Couldn't authenticate this request to join a channel"); +}; diff --git a/iris/mutations/channel/resetChannelJoinToken.js b/iris/mutations/channel/resetChannelJoinToken.js new file mode 100644 index 0000000000..d78973461e --- /dev/null +++ b/iris/mutations/channel/resetChannelJoinToken.js @@ -0,0 +1,43 @@ +// @flow +import type { GraphQLContext } from '../../'; +import UserError from '../../utils/UserError'; +import { + createChannelSettings, + enableChannelTokenJoin, + resetChannelJoinToken, +} from '../../models/channelSettings'; + +type ResetJoinTokenInput = { + input: { + id: string, + }, +}; + +export default async ( + _: any, + { input: { id: channelId } }: ResetJoinTokenInput, + { user, loaders }: GraphQLContext +) => { + const currentUser = user; + if (!currentUser) { + return new UserError('You must be signed in to manage this channel.'); + } + + const [permissions, settings] = await Promise.all([ + loaders.userPermissionsInChannel.load([currentUser.id, channelId]), + loaders.channelSettings.load(channelId), + ]); + + if (!permissions.isOwner) { + return new UserError("You don't have permission to do this."); + } + + const hasSettings = settings && settings.reduction.length > 0; + if (hasSettings) { + return await resetChannelJoinToken(channelId); + } else { + return await createChannelSettings(channelId).then( + async () => await enableChannelTokenJoin(channelId) + ); + } +}; diff --git a/iris/mutations/directMessageThread/createDirectMessageThread.js b/iris/mutations/directMessageThread/createDirectMessageThread.js index 27383995d9..0a62b79407 100644 --- a/iris/mutations/directMessageThread/createDirectMessageThread.js +++ b/iris/mutations/directMessageThread/createDirectMessageThread.js @@ -12,6 +12,7 @@ import { setUserLastSeenInDirectMessageThread, createMemberInDirectMessageThread, } from '../../models/usersDirectMessageThreads'; +import type { FileUpload } from 'shared/types'; type DMThreadInput = { input: { @@ -22,12 +23,7 @@ type DMThreadInput = { content: { body: string, }, - file?: { - name: string, - type: string, - size: number, - path: string, - }, + file?: FileUpload, }, }, }; @@ -84,7 +80,7 @@ export default async ( }; return await storeMessage(messageWithThread, currentUser.id); - } else if (message.messageType === 'media') { + } else if (message.messageType === 'media' && message.file) { const url = await uploadImage(message.file, 'threads', threadId); // build a new message object with a new file field with metadata @@ -95,9 +91,9 @@ export default async ( body: url, }, file: { - name: message.file && message.file.name, - size: message.file && message.file.size, - type: message.file && message.file.type, + name: message.file && message.file.filename, + size: null, + type: message.file && message.file.mimetype, }, }); diff --git a/iris/mutations/message/addMessage.js b/iris/mutations/message/addMessage.js index 2e5c0b1d34..17361e0d18 100644 --- a/iris/mutations/message/addMessage.js +++ b/iris/mutations/message/addMessage.js @@ -11,6 +11,7 @@ import { createParticipantWithoutNotificationsInThread, } from '../../models/usersThreads'; import addCommunityMember from '../communityMember/addCommunityMember'; +import type { FileUpload } from 'shared/types'; type AddMessageInput = { message: { @@ -20,12 +21,7 @@ type AddMessageInput = { content: { body: string, }, - file?: { - name: string, - type: string, - size: number, - path: string, - }, + file?: FileUpload, }, }; @@ -57,12 +53,21 @@ export default async ( // construct the shape of the object to be stored in the db let messageForDb = Object.assign({}, message); if (message.file && message.messageType === 'media') { + const { file } = message; + const fileMetaData = { - name: message.file.name, - size: message.file.size, - type: message.file.type, + name: file.filename, + size: null, + type: file.mimetype, }; - const url = await uploadImage(message.file, 'threads', message.threadId); + + const url = await uploadImage(file, 'threads', message.threadId); + + if (!url) + return new UserError( + "We weren't able to upload this image, please try again" + ); + messageForDb = Object.assign({}, messageForDb, { content: { body: url, @@ -170,7 +175,7 @@ export default async ( }; }) .catch(err => { - console.log('Error sending message', err); + console.error('Error sending message', err); return new UserError('Error sending message, please try again'); }); }; diff --git a/iris/mutations/thread/publishThread.js b/iris/mutations/thread/publishThread.js index b60e7fb258..57d09e8fb6 100644 --- a/iris/mutations/thread/publishThread.js +++ b/iris/mutations/thread/publishThread.js @@ -8,18 +8,14 @@ import { getCommunityRecurringPayments } from '../../models/recurringPayment'; import { getChannels } from '../../models/channel'; import { publishThread, editThread } from '../../models/thread'; import { createParticipantInThread } from '../../models/usersThreads'; +import type { FileUpload } from 'shared/types'; type Attachment = { attachmentType: string, data: string, }; -type File = { - name: string, - type: string, - size: number, - path: string, -}; +type File = FileUpload; type PublishThreadInput = { thread: { @@ -157,7 +153,9 @@ export default async ( // if the original mutation input contained files to upload const urls = await Promise.all( // upload each of the files to s3 - thread.filesToUpload.map(file => uploadImage(file, 'threads', dbThread.id)) + thread.filesToUpload.map( + file => file && uploadImage(file, 'threads', dbThread.id) + ) ); // Replace the local image srcs with the remote image src diff --git a/iris/package.json b/iris/package.json index 0aa08ec1bc..6275c9ab73 100644 --- a/iris/package.json +++ b/iris/package.json @@ -3,8 +3,9 @@ "algoliasearch": "^3.24.7", "apollo-engine": "1.x", "apollo-local-query": "^0.3.0", - "apollo-upload-client": "^5.1.0", - "apollo-upload-server": "^2.0.4", + "apollo-upload-client": "^8.0.0", + "apollo-upload-server": "^5.0.0", + "aws-sdk": "2.200.0", "axios": "^0.16.2", "babel-plugin-replace-dynamic-import-runtime": "^1.0.2", "babel-plugin-styled-components": "^1.1.7", @@ -105,9 +106,9 @@ "rethinkdb-inspector": "^0.3.3", "rethinkdb-migrate": "^1.1.0", "rethinkdbdash": "^2.3.29", - "s3-image-uploader": "^1.0.7", "serialize-javascript": "^1.4.0", "session-rethinkdb": "^2.0.0", + "shortid": "^2.2.8", "slate": "^0.20.1", "slate-markdown": "0.1.0", "slugg": "^1.1.0", diff --git a/iris/queries/channel/index.js b/iris/queries/channel/index.js index a2415f06da..7214416b7c 100644 --- a/iris/queries/channel/index.js +++ b/iris/queries/channel/index.js @@ -12,6 +12,7 @@ import pendingUsers from './pendingUsers'; import blockedUsers from './blockedUsers'; import moderators from './moderators'; import owners from './owners'; +import joinSettings from './joinSettings'; module.exports = { Query: { @@ -29,5 +30,6 @@ module.exports = { blockedUsers, moderators, owners, + joinSettings, }, }; diff --git a/iris/queries/channel/joinSettings.js b/iris/queries/channel/joinSettings.js new file mode 100644 index 0000000000..ca29a3cd68 --- /dev/null +++ b/iris/queries/channel/joinSettings.js @@ -0,0 +1,10 @@ +// @flow +import { getChannelSettings } from '../../models/channelSettings'; +import type { DBChannel } from 'shared/types'; +import type { GraphQLContext } from '../../'; + +export default async ({ id }: DBChannel, _: any) => { + return getChannelSettings(id).then(settings => { + return settings.joinSettings; + }); +}; diff --git a/iris/routes/middlewares/index.js b/iris/routes/middlewares/index.js index 23f6fdd7e3..a488c83c3d 100644 --- a/iris/routes/middlewares/index.js +++ b/iris/routes/middlewares/index.js @@ -37,7 +37,11 @@ import bodyParser from 'body-parser'; middlewares.use(bodyParser.json()); import { apolloUploadExpress } from 'apollo-upload-server'; -middlewares.use(apolloUploadExpress()); +middlewares.use( + apolloUploadExpress({ + maxFileSize: 52428800, // 50mb + }) +); import session from 'shared/middlewares/session'; middlewares.use(session); diff --git a/iris/test/channel/queries/__snapshots__/channelSettings.test.js.snap b/iris/test/channel/queries/__snapshots__/channelSettings.test.js.snap new file mode 100644 index 0000000000..2371e9abf4 --- /dev/null +++ b/iris/test/channel/queries/__snapshots__/channelSettings.test.js.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should fetch a private channels token join settings 1`] = ` +Object { + "data": Object { + "channel": Object { + "id": "ce2b4488-4c75-47e0-8ebc-2539c1e6a192", + "joinSettings": Object { + "token": null, + "tokenJoinEnabled": false, + }, + }, + }, +} +`; diff --git a/iris/test/channel/queries/channelSettings.test.js b/iris/test/channel/queries/channelSettings.test.js new file mode 100644 index 0000000000..95bcacec60 --- /dev/null +++ b/iris/test/channel/queries/channelSettings.test.js @@ -0,0 +1,24 @@ +//@flow +import { request } from '../../utils'; + +it('should fetch a private channels token join settings', async () => { + const query = /* GraphQL */ ` + { + channel(id: "ce2b4488-4c75-47e0-8ebc-2539c1e6a192") { + id + joinSettings { + tokenJoinEnabled + token + } + } + } + `; + + expect.assertions(3); + const result = await request(query); + const { data: { channel } } = result; + + expect(channel.joinSettings.tokenJoinEnabled).toEqual(false); + expect(channel.joinSettings.token).toEqual(null); + expect(result).toMatchSnapshot(); +}); diff --git a/iris/types/Channel.js b/iris/types/Channel.js index 828cae7dfd..68b79c4156 100644 --- a/iris/types/Channel.js +++ b/iris/types/Channel.js @@ -58,6 +58,11 @@ const Channel = /* GraphQL */ ` userId: ID! } + type JoinSettings { + tokenJoinEnabled: Boolean + token: String + } + type Channel { id: ID! createdAt: Date! @@ -78,6 +83,7 @@ const Channel = /* GraphQL */ ` blockedUsers: [User] @cost(complexity: 3) moderators: [User] @cost(complexity: 3) owners: [User] @cost(complexity: 3) + joinSettings: JoinSettings } @@ -85,15 +91,37 @@ const Channel = /* GraphQL */ ` channel(id: ID, channelSlug: String, communitySlug: String): Channel @cost(complexity: 1) } + input JoinChannelWithTokenInput { + communitySlug: String! + channelSlug: String! + token: String! + } + + input EnableChannelTokenJoinInput { + id: ID! + } + + input DisableChannelTokenJoinInput { + id: ID! + } + + input ResetChannelJoinTokenInput { + id: ID! + } + extend type Mutation { createChannel(input: CreateChannelInput!): Channel editChannel(input: EditChannelInput!): Channel deleteChannel(channelId: ID!): Boolean toggleChannelSubscription(channelId: ID!): Channel + joinChannelWithToken(input: JoinChannelWithTokenInput!): Channel toggleChannelNotifications(channelId: ID!): Channel togglePendingUser(input: TogglePendingUserInput!): Channel unblockUser(input: UnblockUserInput!): Channel sendChannelEmailInvites(input: EmailInvitesInput!): Boolean + enableChannelTokenJoin(input: EnableChannelTokenJoinInput!): Channel + disableChannelTokenJoin(input: DisableChannelTokenJoinInput!): Channel + resetChannelJoinToken(input: ResetChannelJoinTokenInput!): Channel } `; diff --git a/iris/types/Community.js b/iris/types/Community.js index c13df99533..e0c31eff02 100644 --- a/iris/types/Community.js +++ b/iris/types/Community.js @@ -124,16 +124,16 @@ const Community = /* GraphQL */ ` slug: String! description: String! website: String - file: File - coverFile: File + file: Upload + coverFile: Upload } input EditCommunityInput { name: String description: String website: String - file: File - coverFile: File + file: Upload + coverFile: Upload communityId: ID! } diff --git a/iris/types/DirectMessageThread.js b/iris/types/DirectMessageThread.js index ecd8d0b88e..bc830ff806 100644 --- a/iris/types/DirectMessageThread.js +++ b/iris/types/DirectMessageThread.js @@ -47,7 +47,7 @@ const DirectMessageThread = /* GraphQL */ ` messageType: MessageType! threadType: String! content: ContentInput! - file: File + file: Upload } input DirectMessageThreadInput { diff --git a/iris/types/Message.js b/iris/types/Message.js index 67e0655941..ff4c0e04a3 100644 --- a/iris/types/Message.js +++ b/iris/types/Message.js @@ -41,7 +41,7 @@ const Message = /* GraphQL */ ` threadType: ThreadTypes! messageType: MessageTypes! content: MessageContentInput! - file: File + file: Upload } extend type Query { diff --git a/iris/types/Thread.js b/iris/types/Thread.js index 8fb5680745..555b0830f1 100644 --- a/iris/types/Thread.js +++ b/iris/types/Thread.js @@ -84,7 +84,7 @@ const Thread = /* GraphQL */ ` threadId: ID! content: ThreadContentInput! attachments: [AttachmentInput] - filesToUpload: [File] + filesToUpload: [Upload] } input ThreadInput { @@ -93,7 +93,7 @@ const Thread = /* GraphQL */ ` type: ThreadType content: ThreadContentInput! attachments: [AttachmentInput] - filesToUpload: [File] + filesToUpload: [Upload] } extend type Mutation { diff --git a/iris/types/User.js b/iris/types/User.js index f68032db9f..fe511029ef 100644 --- a/iris/types/User.js +++ b/iris/types/User.js @@ -121,8 +121,8 @@ const User = /* GraphQL */ ` } input EditUserInput { - file: File - coverFile: File + file: Upload + coverFile: Upload name: String description: String website: String diff --git a/iris/types/scalars.js b/iris/types/scalars.js index 2d3cf92936..2781ade2a3 100644 --- a/iris/types/scalars.js +++ b/iris/types/scalars.js @@ -4,13 +4,16 @@ * both their type definitions and their resolvers */ const GraphQLDate = require('graphql-date'); +import { GraphQLUpload } from 'apollo-upload-server'; const typeDefs = /* GraphQL */ ` scalar Date + scalar Upload `; const resolvers = { Date: GraphQLDate, + Upload: GraphQLUpload, }; module.exports = { diff --git a/iris/utils/s3.js b/iris/utils/s3.js index d4e4b70001..64cd13932f 100644 --- a/iris/utils/s3.js +++ b/iris/utils/s3.js @@ -1,71 +1,74 @@ +// @flow require('now-env'); -const Uploader = require('s3-image-uploader'); +import AWS from 'aws-sdk'; +import shortid from 'shortid'; const IS_PROD = process.env.NODE_ENV === 'production'; +import type { FileUpload } from 'shared/types'; +type EntityTypes = 'communities' | 'channels' | 'users' | 'threads'; + let S3_TOKEN = process.env.S3_TOKEN; let S3_SECRET = process.env.S3_SECRET; if (!IS_PROD) { - // In development or testing default the tokens to some garbage - // so that the s3-image-uploader doesn't throw an error S3_TOKEN = S3_TOKEN || 'asdf123'; S3_SECRET = S3_SECRET || 'asdf123'; } -const uploader = new Uploader({ - aws: { - key: S3_TOKEN, - secret: S3_SECRET, +AWS.config.update({ + accessKeyId: S3_TOKEN, + secretAccessKey: S3_SECRET, + apiVersions: { + s3: 'latest', }, - websockets: false, }); +const s3 = new AWS.S3(); const generateImageUrl = path => { // remove the bucket name from the path - let newPath = path.replace('/spectrum-chat', ''); + const newPath = path.replace('spectrum-chat/', ''); // this is the default source for our imgix account, which starts // at the bucket root, thus we remove the bucket from the path - let imgixBase = 'https://spectrum.imgix.net'; + const imgixBase = 'https://spectrum.imgix.net'; // return a new url to update the user object - return imgixBase + newPath; + return imgixBase + '/' + newPath; }; -/* - Adds random number to filename to avoid conflicts -*/ -const generateFileName = (name: string) => { - const num = Math.random(); - const fileName = `${name}.${num}`; - return fileName; -}; - -type EntityTypes = 'communities' | 'channels' | 'users' | 'threads'; - -const uploadImage = (file: Object, entity: EntityTypes, id: string) => - new Promise((resolve, reject) => { - const fileName = generateFileName(file.name); - - return uploader.upload( +const upload = async ( + file: FileUpload, + entity: EntityTypes, + id: string +): Promise => { + const result = await file; + const { filename, stream } = result; + return new Promise(res => { + const path = `spectrum-chat/${entity}/${id}`; + const fileKey = `${shortid.generate()}-${filename}`; + return s3.upload( { - fileId: fileName, - bucket: `spectrum-chat/${entity}/${id}`, - source: file.path, - name: fileName, - }, - data => { - const url = generateImageUrl(data.path); - return resolve(encodeURI(url)); + Bucket: path, + Key: fileKey, + Body: stream, + ACL: 'public-read', }, - (errMsg, errObject) => { - // TODO: Figure out error handling in the backend if image upload fails - return new Error(errMsg); - console.error('unable to upload: ' + errMsg + ':', errObject); + (err, data) => { + if (err) throw new Error(err); + if (!data || !data.Key) throw new Error('Image upload failed.'); + const url = generateImageUrl(data.Key); + res(encodeURI(url)); } ); }); +}; -module.exports = { - uploadImage, +export const uploadImage = async ( + file: FileUpload, + entity: EntityTypes, + id: string +): Promise => { + return await upload(file, entity, id).catch(err => { + throw new Error(err); + }); }; diff --git a/iris/yarn.lock b/iris/yarn.lock index 2a97c87316..7a14cd526b 100644 --- a/iris/yarn.lock +++ b/iris/yarn.lock @@ -2,21 +2,21 @@ # yarn lockfile v1 -"@babel/runtime@^7.0.0-beta.37": - version "7.0.0-beta.37" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.37.tgz#a717c99db91d64e988788bcf9df68a7779bbc98d" +"@babel/runtime@^7.0.0-beta.38", "@babel/runtime@^7.0.0-beta.40": + version "7.0.0-beta.41" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.41.tgz#776ce13391b8154ccfdea71018a47b63e4d97e74" dependencies: - core-js "^2.4.0" + core-js "^2.5.3" regenerator-runtime "^0.11.1" -"@types/graphql@0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.10.2.tgz#d7c79acbaa17453b6681c80c34b38fcb10c4c08c" - "@types/graphql@^0.9.0", "@types/graphql@^0.9.1": version "0.9.4" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.4.tgz#cdeb6bcbef9b6c584374b81aa7f48ecf3da404fa" +"@types/node@^9.4.6": + version "9.4.7" + resolved "http://registry.npmjs.org/@types/node/-/node-9.4.7.tgz#57d81cd98719df2c9de118f2d5f3b1120dcd7275" + "@types/zen-observable@0.5.3": version "0.5.3" resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.5.3.tgz#91b728599544efbb7386d8b6633693a3c2e7ade5" @@ -166,20 +166,6 @@ apollo-cache-control@^0.0.x: dependencies: graphql-extensions "^0.0.x" -apollo-client@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-1.9.3.tgz#37000b3c801f4571b7b089739e696a158896aeab" - dependencies: - apollo-link-core "^0.5.0" - graphql "^0.10.0" - graphql-anywhere "^3.0.1" - graphql-tag "^2.0.0" - redux "^3.4.0" - symbol-observable "^1.0.2" - whatwg-fetch "^2.0.0" - optionalDependencies: - "@types/graphql" "0.10.2" - apollo-engine-binary-darwin@0.2018.2-111-gb13195fb0: version "0.2018.2-111-gb13195fb0" resolved "https://registry.yarnpkg.com/apollo-engine-binary-darwin/-/apollo-engine-binary-darwin-0.2018.2-111-gb13195fb0.tgz#5e5fcbde78a5c6ad818b07ea7c850c5024b70f97" @@ -200,13 +186,11 @@ apollo-engine@1.x: apollo-engine-binary-linux "0.2018.2-111-gb13195fb0" apollo-engine-binary-windows "0.2018.2-111-gb13195fb0" -apollo-link-core@^0.5.0: - version "0.5.4" - resolved "https://registry.yarnpkg.com/apollo-link-core/-/apollo-link-core-0.5.4.tgz#8efd4cd747959872a32f313f0ccfc2a76b396668" +apollo-link-http-common@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.3.tgz#82ae0d4ff0cdd7c5c8826411d9dd7f7d8049ca46" dependencies: - graphql "^0.10.3" - graphql-tag "^2.4.2" - zen-observable-ts "^0.4.4" + apollo-link "^1.2.1" apollo-link@^1.0.0: version "1.0.7" @@ -216,6 +200,14 @@ apollo-link@^1.0.0: apollo-utilities "^1.0.0" zen-observable "^0.6.0" +apollo-link@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.1.tgz#c120b16059f9bd93401b9f72b94d2f80f3f305d2" + dependencies: + "@types/node" "^9.4.6" + apollo-utilities "^1.0.0" + zen-observable-ts "^0.8.6" + apollo-local-query@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/apollo-local-query/-/apollo-local-query-0.3.1.tgz#e290375253879badd09ebe7ca410744aad7e5ec9" @@ -247,20 +239,20 @@ apollo-tracing@^0.1.0: dependencies: graphql-extensions "^0.0.x" -apollo-upload-client@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/apollo-upload-client/-/apollo-upload-client-5.1.1.tgz#11a22ecf7d29ac35e87c4fa666eaeec4cddc71b3" +apollo-upload-client@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/apollo-upload-client/-/apollo-upload-client-8.0.0.tgz#0067f3b426b3828f971964799bc31f8073bd0607" dependencies: - apollo-client "^1.9.1" - babel-runtime "^6.25.0" - extract-files "^2.0.1" + "@babel/runtime" "^7.0.0-beta.40" + apollo-link-http-common "^0.2.3" + extract-files "^3.1.0" -apollo-upload-server@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/apollo-upload-server/-/apollo-upload-server-2.0.4.tgz#5105081b6c061638ef7a04ef848758d30f3ba96b" +apollo-upload-server@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/apollo-upload-server/-/apollo-upload-server-5.0.0.tgz#c953b523608313966e0c8444637f4ae8ef77d5bc" dependencies: - formidable "^1.1.1" - mkdirp "^0.5.1" + "@babel/runtime" "^7.0.0-beta.40" + busboy "^0.2.14" object-path "^0.11.4" apollo-utilities@^1.0.0, apollo-utilities@^1.0.1: @@ -327,14 +319,6 @@ array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" -array-parallel@~0.1.0: - version "0.1.3" - resolved "https://registry.yarnpkg.com/array-parallel/-/array-parallel-0.1.3.tgz#8f785308926ed5aa478c47e64d1b334b6c0c947d" - -array-series@~0.1.0: - version "0.1.5" - resolved "https://registry.yarnpkg.com/array-series/-/array-series-0.1.5.tgz#df5d37bfc5c2ef0755e2aa4f92feae7d4b5a972f" - array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -444,12 +428,19 @@ autolinker@~0.15.0: version "0.15.3" resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.15.3.tgz#342417d8f2f3461b14cf09088d5edf8791dc9832" -aws-sdk@~2.0.17: - version "2.0.31" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.0.31.tgz#e72cf1fdc69015bd9fd2bdf3d3b88c16507d268e" +aws-sdk@2.200.0: + version "2.200.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.200.0.tgz#f460c96408725b0eb8c658fddea6e0bfe0ef5a44" dependencies: - xml2js "0.2.6" - xmlbuilder "0.4.2" + buffer "4.9.1" + events "^1.1.1" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.1.0" + xml2js "0.4.17" + xmlbuilder "4.2.1" aws-sign2@~0.6.0: version "0.6.0" @@ -1019,7 +1010,7 @@ babel-register@^6.26.0: mkdirp "^0.5.1" source-map-support "^0.4.15" -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.25.0, babel-runtime@^6.26.0: +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: @@ -1326,7 +1317,7 @@ buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" -buffer@^4.3.0: +buffer@4.9.1, buffer@^4.3.0: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" dependencies: @@ -1365,6 +1356,13 @@ bull@3.3.10: semver "^5.4.1" uuid "^3.1.0" +busboy@^0.2.14: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + bytebuffer@~5: version "5.0.1" resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd" @@ -1622,10 +1620,6 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" - commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" @@ -1762,7 +1756,7 @@ core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" -core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.1: +core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.1, core-js@^2.5.3: version "2.5.3" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" @@ -1944,10 +1938,6 @@ debounce@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.1.0.tgz#6a1a4ee2a9dc4b7c24bb012558dbcdb05b37f408" -debug@0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.0.tgz#f5be05ec0434c992d79940e50b2695cfb2e01b08" - debug@2.6.9, debug@^2.2.0, debug@^2.3.2, debug@^2.3.3, debug@^2.5.2, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2090,6 +2080,13 @@ detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" +dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + diff@^3.2.0: version "3.4.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c" @@ -2581,7 +2578,7 @@ eventemitter3@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba" -events@^1.0.0, events@^1.1.0: +events@^1.0.0, events@^1.1.0, events@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -2738,11 +2735,11 @@ extglob@^2.0.2: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-files@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-2.1.1.tgz#3e76eaeeccb5789fc369bfc22bdf9c0e6c5d8b1b" +extract-files@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-3.1.0.tgz#b70424c9d4a1a4208efe22069388f428e4ae00f1" dependencies: - "@babel/runtime" "^7.0.0-beta.37" + "@babel/runtime" "^7.0.0-beta.38" extsprintf@1.3.0: version "1.3.0" @@ -2790,12 +2787,6 @@ fbjs@^0.8.1, fbjs@^0.8.15, fbjs@^0.8.16, fbjs@^0.8.5, fbjs@^0.8.9: setimmediate "^1.0.5" ua-parser-js "^0.7.9" -fd-slicer@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-0.1.0.tgz#f597141dfe8a2841756fd54150a78fe0bec4bc03" - dependencies: - pend "~1.1.2" - filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -2867,10 +2858,6 @@ find-with-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/find-with-regex/-/find-with-regex-1.0.2.tgz#d3b36286539f14c527e31f194159c6d251651a45" -findit@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/findit/-/findit-2.0.0.tgz#6509f0126af4c178551cfa99394e032e13a4d56e" - flexbuffer@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/flexbuffer/-/flexbuffer-0.0.6.tgz#039fdf23f8823e440c38f3277e6fef1174215b30" @@ -2936,10 +2923,6 @@ form-data@~2.3.1: combined-stream "^1.0.5" mime-types "^2.1.12" -formidable@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9" - forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -3121,16 +3104,6 @@ globby@^5.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -gm@~1.16.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/gm/-/gm-1.16.0.tgz#2b3d37b25fb349286e829bf1ccf097f4de4fba88" - dependencies: - array-parallel "~0.1.0" - array-series "~0.1.0" - debug "0.7.0" - stream-to-buffer "~0.0.1" - through "~2.3.1" - good-listener@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" @@ -3202,10 +3175,6 @@ graceful-fs@~3.0.2: dependencies: natives "^1.1.0" -graphql-anywhere@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-3.1.0.tgz#3ea0d8e8646b5cee68035016a9a7557c15c21e96" - graphql-cost-analysis@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/graphql-cost-analysis/-/graphql-cost-analysis-0.1.1.tgz#e500cb2e896efe333f6912bb264ccb7948a6c06a" @@ -3259,10 +3228,6 @@ graphql-subscriptions@^0.5.6: es6-promise "^4.1.1" iterall "^1.1.3" -graphql-tag@^2.0.0, graphql-tag@^2.4.2: - version "2.6.1" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.6.1.tgz#4788d509f6e29607d947fc47a40c4e18f736d34a" - graphql-tools@1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-1.2.3.tgz#079bf4d157e46c0a0bae9fec117e0eea6e03ba2c" @@ -3288,12 +3253,6 @@ graphql@0.11.x: dependencies: iterall "1.1.3" -graphql@^0.10.0, graphql@^0.10.3: - version "0.10.5" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.10.5.tgz#c9be17ca2bdfdbd134077ffd9bbaa48b8becd298" - dependencies: - iterall "^1.1.0" - growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -3996,7 +3955,7 @@ items@2.x.x: version "2.1.1" resolved "https://registry.yarnpkg.com/items/-/items-2.1.1.tgz#8bd16d9c83b19529de5aea321acaada78364a198" -iterall@1.1.3, iterall@^1.1.0, iterall@^1.1.1, iterall@^1.1.3: +iterall@1.1.3, iterall@^1.1.1, iterall@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.1.3.tgz#1cbbff96204056dde6656e2ed2e2226d0e6d72c9" @@ -4229,6 +4188,10 @@ jest@^21.1.0, jest@^21.2.1: dependencies: jest-cli "^21.2.1" +jmespath@0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" + joi@^10.6.0: version "10.6.0" resolved "https://registry.yarnpkg.com/joi/-/joi-10.6.0.tgz#52587f02d52b8b75cdb0c74f0b164a191a0e1fc2" @@ -4655,6 +4618,10 @@ lodash.values@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" +lodash@^4.0.0: + version "4.17.5" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" + lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -4866,10 +4833,6 @@ mime@^1.2.11: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" -mime@~1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" - mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" @@ -4945,10 +4908,6 @@ nan@^2.3.0: version "2.8.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" -nan@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-1.0.0.tgz#ae24f8850818d662fcab5acf7f3b95bfaa2ccf38" - nanomatch@^1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.7.tgz#53cd4aa109ff68b7f869591fdc9d10daeeea3e79" @@ -5230,10 +5189,6 @@ optionator@^0.8.1: type-check "~0.3.2" wordwrap "~1.0.0" -options@>=0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" - os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" @@ -5495,10 +5450,6 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" -pend@~1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.1.3.tgz#ca68dd39e6dd7f8d3f8801dcdbcb44846c431845" - performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" @@ -5961,6 +5912,15 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" @@ -6029,7 +5989,7 @@ redux-thunk@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5" -redux@^3.4.0, redux@^3.6.0: +redux@^3.6.0: version "3.7.2" resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" dependencies: @@ -6246,10 +6206,6 @@ rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1: dependencies: glob "^7.0.5" -rimraf@~2.2.8: - version "2.2.8" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" - ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" @@ -6257,28 +6213,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" -s3-image-uploader@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/s3-image-uploader/-/s3-image-uploader-1.0.7.tgz#770ea8e559f524d59ccb549da47baf10e1bc4bc0" - dependencies: - extend "^3.0.0" - gm "~1.16.0" - s3 "~4.2.0" - ws "~0.4.32" - -s3@~4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/s3/-/s3-4.2.0.tgz#7c19255d57a8fd45761d2eeb2f720316aa5775c9" - dependencies: - aws-sdk "~2.0.17" - fd-slicer "~0.1.0" - findit "~2.0.0" - graceful-fs "~3.0.2" - mime "~1.2.11" - mkdirp "~0.5.0" - pend "~1.1.2" - rimraf "~2.2.8" - safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -6297,11 +6231,11 @@ sane@^2.0.0: optionalDependencies: fsevents "^1.1.1" -sax@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/sax/-/sax-0.4.2.tgz#39f3b601733d6bec97105b242a2a40fd6978ac3c" +sax@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" -sax@^1.2.1: +sax@>=0.6.0, sax@^1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -6442,6 +6376,10 @@ shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" +shortid@^2.2.8: + version "2.2.8" + resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.8.tgz#033b117d6a2e975804f6f0969dbe7d3d0b355131" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -6678,9 +6616,9 @@ stream-http@^2.7.2: to-arraybuffer "^1.0.0" xtend "^4.0.0" -stream-to-buffer@~0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/stream-to-buffer/-/stream-to-buffer-0.0.1.tgz#ab483d59a1ca71832de379a255f465b665af45c1" +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" strict-uri-encode@^1.0.0: version "1.1.0" @@ -6879,7 +6817,7 @@ sw-toolbox@^3.4.0: path-to-regexp "^1.0.1" serviceworker-cache-polyfill "^4.0.0" -symbol-observable@^1.0.2, symbol-observable@^1.0.3, symbol-observable@^1.0.4: +symbol-observable@^1.0.3, symbol-observable@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32" @@ -6975,10 +6913,6 @@ tiny-emitter@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c" -tinycolor@0.x: - version "0.0.1" - resolved "https://registry.yarnpkg.com/tinycolor/-/tinycolor-0.0.1.tgz#320b5a52d83abb5978d81a3e887d4aefb15a6164" - tlds@^1.189.0: version "1.199.0" resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.199.0.tgz#a4fc8c3058216488a80aaaebb427925007e55217" @@ -7255,6 +7189,13 @@ url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -7292,14 +7233,14 @@ uuid@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.0.tgz#6728fc0459c450d796a99c31837569bdf672d728" +uuid@3.1.0, uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + uuid@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" -uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" - validate-npm-package-license@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" @@ -7422,7 +7363,7 @@ whatwg-encoding@^1.0.1: dependencies: iconv-lite "0.4.19" -whatwg-fetch@>=0.10.0, whatwg-fetch@^2.0.0: +whatwg-fetch@>=0.10.0: version "2.0.3" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" @@ -7533,15 +7474,6 @@ ws@^3.0.0: safe-buffer "~5.1.0" ultron "~1.1.0" -ws@~0.4.32: - version "0.4.32" - resolved "https://registry.yarnpkg.com/ws/-/ws-0.4.32.tgz#787a6154414f3c99ed83c5772153b20feb0cec32" - dependencies: - commander "~2.1.0" - nan "~1.0.0" - options ">=0.0.5" - tinycolor "0.x" - xdg-basedir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" @@ -7556,15 +7488,18 @@ xml-name-validator@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" -xml2js@0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.2.6.tgz#d209c4e4dda1fc9c452141ef41c077f5adfdf6c4" +xml2js@0.4.17: + version "0.4.17" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.17.tgz#17be93eaae3f3b779359c795b419705a8817e868" dependencies: - sax "0.4.2" + sax ">=0.6.0" + xmlbuilder "^4.1.0" -xmlbuilder@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-0.4.2.tgz#1776d65f3fdbad470a08d8604cdeb1c4e540ff83" +xmlbuilder@4.2.1, xmlbuilder@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-4.2.1.tgz#aa58a3041a066f90eaa16c2f5389ff19f3f461a5" + dependencies: + lodash "^4.0.0" xmldom@0.1.x: version "0.1.27" @@ -7665,10 +7600,16 @@ yargs@~3.10.0: decamelize "^1.0.0" window-size "0.1.0" -zen-observable-ts@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.4.4.tgz#c244c71eaebef79a985ccf9895bc90307a6e9712" +zen-observable-ts@^0.8.6: + version "0.8.8" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.8.tgz#1a586dc204fa5632a88057f879500e0d2ba06869" + dependencies: + zen-observable "^0.7.0" zen-observable@^0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.6.1.tgz#01dbed3bc8d02cbe9ee1112c83e04c807f647244" + +zen-observable@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.7.1.tgz#f84075c0ee085594d3566e1d6454207f126411b3" diff --git a/jest.config.js b/jest.config.js index 45f126d04c..cde69f506b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,8 +9,5 @@ module.exports = { ), globalSetup: path.resolve(__dirname, './shared/testing/setup'), globalTeardown: path.resolve(__dirname, './shared/testing/teardown'), - testPathIgnorePatterns: - process.env.E2E || process.env.CI - ? ['/node_modules/', '/mutations/', '/mobile/'] - : ['/node_modules/', '/mutations/', '/mobile/', '/test-e2e/'], + testPathIgnorePatterns: ['/node_modules/', '/mutations/', '/mobile/'], }; diff --git a/package.json b/package.json index 19e710f6d7..51e2ca215d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Spectrum", - "version": "2.1.8", + "version": "2.1.9", "private": true, "devDependencies": { "babel-cli": "^6.24.1", @@ -18,9 +18,10 @@ "backpack-core": "^0.4.1", "cheerio": "^1.0.0-rc.2", "cross-env": "^5.0.5", + "cypress": "^2.1.0", "danger": "^3.1.8", "danger-plugin-jest": "^1.1.0", - "danger-plugin-no-console": "^1.0.0", + "danger-plugin-no-console": "1.1.1", "danger-plugin-yarn": "^1.2.1", "eslint": "^4.1.1", "eslint-config-react-app": "^2.1.0", @@ -55,9 +56,10 @@ "apollo-link-http": "^1.3.2", "apollo-link-retry": "^2.1.2", "apollo-link-ws": "^1.0.4", - "apollo-upload-client": "6.x", - "apollo-upload-server": "^2.0.4", + "apollo-upload-client": "^8.0.0", + "apollo-upload-server": "^5.0.0", "apollo-utilities": "^1.0.4", + "aws-sdk": "2.200.0", "axios": "^0.17.1", "bad-words": "^1.6.1", "body-parser": "^1.17.1", @@ -157,9 +159,9 @@ "rethinkdb-inspector": "^0.3.3", "rethinkdb-migrate": "^1.1.0", "rethinkdbdash": "^2.3.29", - "s3-image-uploader": "^1.0.7", "serialize-javascript": "^1.4.0", "session-rethinkdb": "^2.0.0", + "shortid": "^2.2.8", "slate": "^0.20.1", "slate-markdown": "0.1.0", "slugg": "^1.1.0", @@ -224,6 +226,8 @@ "jest": "cross-env NODE_PATH=./ jest", "test": "npm run jest -- --runInBand --watch", "test:ci": "npm run jest -- --forceExit --outputFile test-results.json --json", + "test:e2e": "cypress run", + "cypress:open": "cypress open", "lint": "eslint .", "flow": "flow", "db:migrate": "npm run rethinkdb:migrate -- up", diff --git a/shared/graphql/mutations/channel/disableChannelTokenJoin.js b/shared/graphql/mutations/channel/disableChannelTokenJoin.js new file mode 100644 index 0000000000..476f34a721 --- /dev/null +++ b/shared/graphql/mutations/channel/disableChannelTokenJoin.js @@ -0,0 +1,47 @@ +// @flow +import gql from 'graphql-tag'; +import { graphql } from 'react-apollo'; +import channelInfoFragment, { + type ChannelInfoType, +} from '../../fragments/channel/channelInfo'; + +export type DisableChannelTokenJoinType = { + data: { + channel: { + ...$Exact, + joinSettings: { + tokenJoinEnabled: boolean, + token: string, + }, + }, + }, +}; + +export const disableChannelTokenJoinMutation = gql` + mutation disableChannelTokenJoin($input: DisableChannelTokenJoinInput!) { + disableChannelTokenJoin(input: $input) { + ...channelInfo + joinSettings { + tokenJoinEnabled + token + } + } + } + ${channelInfoFragment} +`; + +const disableChannelTokenJoinOptions = { + props: ({ mutate }) => ({ + disableChannelTokenJoin: input => + mutate({ + variables: { + input, + }, + }), + }), +}; + +export default graphql( + disableChannelTokenJoinMutation, + disableChannelTokenJoinOptions +); diff --git a/shared/graphql/mutations/channel/enableChannelTokenJoin.js b/shared/graphql/mutations/channel/enableChannelTokenJoin.js new file mode 100644 index 0000000000..32df964241 --- /dev/null +++ b/shared/graphql/mutations/channel/enableChannelTokenJoin.js @@ -0,0 +1,47 @@ +// @flow +import gql from 'graphql-tag'; +import { graphql } from 'react-apollo'; +import channelInfoFragment, { + type ChannelInfoType, +} from '../../fragments/channel/channelInfo'; + +export type EnableChannelTokenJoinType = { + data: { + channel: { + ...$Exact, + joinSettings: { + tokenJoinEnabled: boolean, + token: string, + }, + }, + }, +}; + +export const enableChannelTokenJoinMutation = gql` + mutation enableChannelTokenJoin($input: EnableChannelTokenJoinInput!) { + enableChannelTokenJoin(input: $input) { + ...channelInfo + joinSettings { + tokenJoinEnabled + token + } + } + } + ${channelInfoFragment} +`; + +const enableChannelTokenJoinOptions = { + props: ({ mutate }) => ({ + enableChannelTokenJoin: input => + mutate({ + variables: { + input, + }, + }), + }), +}; + +export default graphql( + enableChannelTokenJoinMutation, + enableChannelTokenJoinOptions +); diff --git a/shared/graphql/mutations/channel/joinChannelWithToken.js b/shared/graphql/mutations/channel/joinChannelWithToken.js new file mode 100644 index 0000000000..f051a6b014 --- /dev/null +++ b/shared/graphql/mutations/channel/joinChannelWithToken.js @@ -0,0 +1,41 @@ +// @flow +import gql from 'graphql-tag'; +import { graphql } from 'react-apollo'; +import channelInfoFragment from '../../fragments/channel/channelInfo'; +import type { ChannelInfoType } from '../../fragments/channel/channelInfo'; + +export type joinChannelWithTokenType = { + data: { + joinChannelWithToken: { + ...$Exact, + }, + }, +}; + +export const joinChannelWithTokenMutation = gql` + mutation joinChannelWithToken($input: JoinChannelWithTokenInput!) { + joinChannelWithToken(input: $input) { + ...channelInfo + } + } + ${channelInfoFragment} +`; + +const joinChannelWithTokenOptions = { + options: { + refetchQueries: ['getCurrentUserProfile', 'getEverythingThreads'], + }, + props: ({ mutate }) => ({ + joinChannelWithToken: input => + mutate({ + variables: { + input, + }, + }), + }), +}; + +export default graphql( + joinChannelWithTokenMutation, + joinChannelWithTokenOptions +); diff --git a/shared/graphql/mutations/channel/resetChannelJoinToken.js b/shared/graphql/mutations/channel/resetChannelJoinToken.js new file mode 100644 index 0000000000..69118fe98c --- /dev/null +++ b/shared/graphql/mutations/channel/resetChannelJoinToken.js @@ -0,0 +1,47 @@ +// @flow +import gql from 'graphql-tag'; +import { graphql } from 'react-apollo'; +import channelInfoFragment, { + type ChannelInfoType, +} from '../../fragments/channel/channelInfo'; + +export type ResetChannelJoinTokenType = { + data: { + channel: { + ...$Exact, + joinSettings: { + tokenJoinEnabled: boolean, + token: string, + }, + }, + }, +}; + +export const resetChannelJoinTokenMutation = gql` + mutation resetChannelJoinToken($input: ResetChannelJoinTokenInput!) { + resetChannelJoinToken(input: $input) { + ...channelInfo + joinSettings { + tokenJoinEnabled + token + } + } + } + ${channelInfoFragment} +`; + +const resetChannelJoinTokenOptions = { + props: ({ mutate }) => ({ + resetChannelJoinToken: input => + mutate({ + variables: { + input, + }, + }), + }), +}; + +export default graphql( + resetChannelJoinTokenMutation, + resetChannelJoinTokenOptions +); diff --git a/shared/graphql/mutations/message/sendMessage.js b/shared/graphql/mutations/message/sendMessage.js index cbe3345f09..81a6c0269e 100644 --- a/shared/graphql/mutations/message/sendMessage.js +++ b/shared/graphql/mutations/message/sendMessage.js @@ -87,15 +87,16 @@ const sendMessageOptions = { ); // Replace the optimistic reponse with the actual db message - if (messageInStore && typeof messageInStore.id === 'number') { + if (messageInStore && typeof messageInStore.node.id === 'number') { data.thread.messageConnection.edges = data.thread.messageConnection.edges.map( edge => { - if (edge.node.id === messageInStore.id) + if (edge.node.id === messageInStore.node.id) { return { ...edge, cursor: btoa(addMessage.id), node: addMessage, }; + } return edge; } ); diff --git a/shared/graphql/queries/channel/getChannelSettings.js b/shared/graphql/queries/channel/getChannelSettings.js new file mode 100644 index 0000000000..54ae8ff874 --- /dev/null +++ b/shared/graphql/queries/channel/getChannelSettings.js @@ -0,0 +1,41 @@ +// @flow +import { graphql } from 'react-apollo'; +import gql from 'graphql-tag'; +import channelInfoFragment, { + type ChannelInfoType, +} from '../../fragments/channel/channelInfo'; + +export type GetChannelSettingsType = { + ...$Exact, + joinSettings: { + tokenJoinEnabled: boolean, + token: string, + }, +}; + +export const getChannelSettingsByIdQuery = gql` + query getChannel($id: ID) { + channel(id: $id) { + ...channelInfo + joinSettings { + tokenJoinEnabled + token + } + } + } + ${channelInfoFragment} +`; + +const getChannelSettingsByIdOptions = { + options: ({ id }) => ({ + variables: { + id, + }, + fetchPolicy: 'cache-first', + }), +}; + +export default graphql( + getChannelSettingsByIdQuery, + getChannelSettingsByIdOptions +); diff --git a/shared/types.js b/shared/types.js index 655b03ff99..4d841b35dd 100644 --- a/shared/types.js +++ b/shared/types.js @@ -41,6 +41,15 @@ export type DBCommunitySettings = { }, }; +export type DBChannelSettings = { + id: string, + channelId: string, + joinSettings: { + tokenJoinEnabled: boolean, + token: string, + }, +}; + export type DBCuratedContent = { type: string, id: string, @@ -359,3 +368,10 @@ export type DBExpoPushSubscription = { token: string, userId: string, }; + +export type FileUpload = { + filename: string, + mimetype: string, + encoding: string, + stream: any, +}; diff --git a/src/components/chatInput/components/mediaUploader.js b/src/components/chatInput/components/mediaUploader.js new file mode 100644 index 0000000000..ae958285b1 --- /dev/null +++ b/src/components/chatInput/components/mediaUploader.js @@ -0,0 +1,115 @@ +// @flow +import * as React from 'react'; +import { MediaLabel, MediaInput, Form } from './style'; +import Icon from 'src/components/icons'; +import { Loading } from 'src/components/loading'; +import { + PRO_USER_MAX_IMAGE_SIZE_STRING, + PRO_USER_MAX_IMAGE_SIZE_BYTES, + FREE_USER_MAX_IMAGE_SIZE_BYTES, + FREE_USER_MAX_IMAGE_SIZE_STRING, +} from 'src/helpers/images'; + +type Props = { + onValidated: Function, + onError: Function, + currentUser: ?Object, + isSendingMediaMessage: boolean, +}; + +class MediaUploader extends React.Component { + form: any; + + validateUpload = (validity: Object, file: ?Object) => { + const { currentUser } = this.props; + + if (!currentUser) + return this.props.onError('You must be signed in to upload images'); + if (!file) return this.props.onError(''); + if (!validity.valid) + return this.props.onError( + "We couldn't validate this upload, please try uploading another file" + ); + + if ( + file && + file.size > FREE_USER_MAX_IMAGE_SIZE_BYTES && + !currentUser.isPro + ) { + return this.props.onError( + `Upgrade to Pro to upload files up to ${PRO_USER_MAX_IMAGE_SIZE_STRING}. Otherwise, try uploading a photo less than ${FREE_USER_MAX_IMAGE_SIZE_STRING}.` + ); + } + + if ( + file && + file.size > PRO_USER_MAX_IMAGE_SIZE_BYTES && + currentUser.isPro + ) { + return this.props.onError( + `Try uploading a file less than ${PRO_USER_MAX_IMAGE_SIZE_STRING}.` + ); + } + + // if it makes it this far, there is not an error we can detect + this.props.onError(''); + // send back the validated file + this.props.onValidated(file); + // clear the form so that another image can be uploaded + return this.clearForm(); + }; + + onChange = (e: any) => { + const { target: { validity, files: [file] } } = e; + + if (!file) return; + + return this.validateUpload(validity, file); + }; + + clearForm = () => { + if (this.form) { + this.form.reset(); + } + }; + + componentDidMount() { + return this.clearForm(); + } + + componentWillUnmount() { + return this.clearForm(); + } + + render() { + const { isSendingMediaMessage } = this.props; + + if (isSendingMediaMessage) { + return ( + + + + ); + } + + return ( +
e.preventDefault()} innerRef={c => (this.form = c)}> + + + + +
+ ); + } +} + +export default MediaUploader; diff --git a/src/components/chatInput/components/style.js b/src/components/chatInput/components/style.js new file mode 100644 index 0000000000..6256d296e9 --- /dev/null +++ b/src/components/chatInput/components/style.js @@ -0,0 +1,34 @@ +// @flow +import styled from 'styled-components'; +import { zIndex } from 'src/components/globals'; + +export const MediaInput = styled.input` + width: 0; + height: 0; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: ${zIndex.hidden}; +`; + +export const MediaLabel = styled.label` + border: none; + outline: 0; + display: inline-block; + background: transparent; + transition: all 0.3s ease-out; + color: ${({ theme }) => theme.text.placeholder}; + height: 32px; + width: 32px; + + &:hover { + cursor: pointer; + color: ${({ theme }) => theme.brand.alt}; + } +`; + +export const Form = styled.form` + display: flex; + justify-content: center; + align-items: center; +`; diff --git a/src/components/chatInput/index.js b/src/components/chatInput/index.js index fc7de3d631..f438be769b 100644 --- a/src/components/chatInput/index.js +++ b/src/components/chatInput/index.js @@ -24,18 +24,13 @@ import { Form, ChatInputWrapper, SendButton, PhotoSizeError } from './style'; import Input from './input'; import sendMessage from 'shared/graphql/mutations/message/sendMessage'; import sendDirectMessage from 'shared/graphql/mutations/message/sendDirectMessage'; -import { - PRO_USER_MAX_IMAGE_SIZE_STRING, - PRO_USER_MAX_IMAGE_SIZE_BYTES, - FREE_USER_MAX_IMAGE_SIZE_BYTES, - FREE_USER_MAX_IMAGE_SIZE_STRING, -} from '../../helpers/images'; -import MediaInput from '../mediaInput'; +import MediaUploader from './components/mediaUploader'; type State = { isFocused: boolean, photoSizeError: string, code: boolean, + isSendingMediaMessage: boolean, }; type Props = { @@ -82,6 +77,7 @@ class ChatInput extends React.Component { isFocused: false, photoSizeError: '', code: false, + isSendingMediaMessage: false, }; editor: any; @@ -90,8 +86,9 @@ class ChatInput extends React.Component { this.props.onRef(this); } - shouldComponentUpdate(next) { + shouldComponentUpdate(next, nextState) { const curr = this.props; + const currState = this.state; // User changed if (curr.currentUser !== next.currentUser) return true; @@ -101,6 +98,8 @@ class ChatInput extends React.Component { // State changed if (curr.state !== next.state) return true; + if (currState.isSendingMediaMessage !== nextState.isSendingMediaMessage) + return true; return false; } @@ -297,10 +296,10 @@ class ChatInput extends React.Component { return 'not-handled'; }; - sendMediaMessage = e => { + sendMediaMessage = file => { // eslint-disable-next-line let reader = new FileReader(); - const file = e.target.files[0]; + const { thread, threadType, @@ -334,30 +333,8 @@ class ChatInput extends React.Component { ); } - if (!file) return; - - if ( - file && - file.size > FREE_USER_MAX_IMAGE_SIZE_BYTES && - !this.props.currentUser.isPro - ) { - return this.setState({ - photoSizeError: `Upgrade to Pro to upload files up to ${PRO_USER_MAX_IMAGE_SIZE_STRING}. Otherwise, try uploading a photo less than ${FREE_USER_MAX_IMAGE_SIZE_STRING}.`, - }); - } - - if ( - file && - file.size > PRO_USER_MAX_IMAGE_SIZE_BYTES && - this.props.currentUser.isPro - ) { - return this.setState({ - photoSizeError: `Try uploading a file less than ${PRO_USER_MAX_IMAGE_SIZE_STRING}.`, - }); - } - this.setState({ - photoSizeError: '', + isSendingMediaMessage: true, }); reader.onloadend = () => { @@ -383,6 +360,9 @@ class ChatInput extends React.Component { file, }) .then(() => { + this.setState({ + isSendingMediaMessage: false, + }); return track( `${threadType} message`, 'media message created', @@ -390,6 +370,9 @@ class ChatInput extends React.Component { ); }) .catch(err => { + this.setState({ + isSendingMediaMessage: false, + }); dispatch(addToastWithTimeout('error', err.message)); }); } else { @@ -403,6 +386,9 @@ class ChatInput extends React.Component { file, }) .then(() => { + this.setState({ + isSendingMediaMessage: false, + }); return track( `${threadType} message`, 'media message created', @@ -410,6 +396,9 @@ class ChatInput extends React.Component { ); }) .catch(err => { + this.setState({ + isSendingMediaMessage: false, + }); dispatch(addToastWithTimeout('error', err.message)); }); } @@ -450,6 +439,12 @@ class ChatInput extends React.Component { this.setState({ photoSizeError: '' }); }; + setMediaMessageError = (error: string) => { + return this.setState({ + photoSizeError: error, + }); + }; + render() { const { state, @@ -457,7 +452,12 @@ class ChatInput extends React.Component { networkOnline, websocketConnection, } = this.props; - const { isFocused, photoSizeError, code } = this.state; + const { + isFocused, + photoSizeError, + code, + isSendingMediaMessage, + } = this.state; const networkDisabled = !networkOnline || @@ -485,7 +485,14 @@ class ChatInput extends React.Component { /> )} - {currentUser && } + {currentUser && ( + + )} { id={props.id} type={props.inputType} defaultValue={props.defaultValue} + value={props.value} placeholder={props.placeholder} onChange={props.onChange} autoFocus={props.autoFocus} diff --git a/src/components/message/index.js b/src/components/message/index.js index 9b6a3d1359..904dcfa2f6 100644 --- a/src/components/message/index.js +++ b/src/components/message/index.js @@ -74,6 +74,7 @@ class Message extends Component { type={emojiOnly ? 'emoji' : message.messageType} openGallery={() => this.toggleOpenGallery(message.id)} message={emojiOnly ? parsedMessage : message.content} + data={message} /> {actionable && ( {reaction && ( { - const { message, openGallery, type, me } = props; + const { message, openGallery, type, me, data } = props; switch (type) { case 'text': default: return {message.body}; - case 'media': + case 'media': { // don't apply imgix url params to optimistic image messages const src = props.id ? message.body : `${message.body}?max-w=${window.innerWidth * 0.6}`; + if (typeof data.id === 'number' && data.id < 0) { + return null; + } return ; + } case 'emoji': return {message}; - case 'draftjs': + case 'draftjs': { const body = JSON.parse(message.body); const isCode = body.blocks[0].type === 'code-block'; @@ -47,6 +52,7 @@ export const Body = (props: { } else { return {redraft(body, messageRenderer)}; } + } } }; @@ -83,6 +89,7 @@ export const Actions = (props: { deleteMessage: Function, isOptimisticMessage: boolean, children: Node, + message: Object, }) => { const { me, @@ -90,8 +97,13 @@ export const Actions = (props: { canModerate, deleteMessage, isOptimisticMessage, + message, } = props; + if (isOptimisticMessage && message.messageType === 'media') { + return null; + } + return ( {props.children} diff --git a/src/routes.js b/src/routes.js index e2de9c43be..44cc46c3ff 100644 --- a/src/routes.js +++ b/src/routes.js @@ -22,6 +22,7 @@ import LoadingDashboard from './views/dashboard/components/dashboardLoading'; import Composer from './components/composer'; import signedOutFallback from './helpers/signed-out-fallback'; import AuthViewHandler from './views/authViewHandler'; +import PrivateChannelJoin from './views/privateChannelJoin'; import ThreadSlider from './views/threadSlider'; import Navbar from './views/navbar'; @@ -282,6 +283,14 @@ class Routes extends React.Component<{}> { path="/:communitySlug/:channelSlug/settings" component={ChannelSettingsFallback} /> + + community.id === channel.communityId -); - -// If DEBUG_E2E is set show a browser and run test in slow mo -const config = process.env.DEBUG_E2E - ? { - headless: false, - slowMo: 100, - } - : {}; - -// Before every test suite set up a new browser and page -beforeAll(async () => { - browser = await puppeteer.launch(config); - page = await browser.newPage(); - // Navigate the page to the login page for all tests - await page.goto(`http://localhost:3000/${community.slug}/${channel.slug}`); -}); - -// Afterwards close the browser -afterAll(async () => { - await browser.close(); -}); - -it('should render', async () => { - await page.waitForSelector('[data-e2e-id="channel-view"]'); -}); - -it('should show the channels data', async () => { - const content = await page.content(); - expect(content).toContain(channel.description); - expect(content).toContain(channel.name); -}); - -it('should show a list of the threads in that channel', async () => { - await page.waitForSelector('[data-e2e-id="thread-feed"]'); - const content = await page.content(); - data.threads.filter(thread => thread.channelId === channel.id).map(thread => { - expect(content).toContain(thread.content.title); - }); -}); diff --git a/src/test-e2e/community.test.js b/src/test-e2e/community.test.js deleted file mode 100644 index 9f305131ec..0000000000 --- a/src/test-e2e/community.test.js +++ /dev/null @@ -1,60 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import data from '../../shared/testing/data'; - -let browser; -let page; -const community = data.communities[0]; - -// If DEBUG_E2E is set show a browser and run test in slow mo -const config = process.env.DEBUG_E2E - ? { - headless: false, - slowMo: 100, - } - : {}; - -// Before every test suite set up a new browser and page -beforeAll(async () => { - browser = await puppeteer.launch(config); - page = await browser.newPage(); - // Navigate the page to the login page for all tests - await page.goto(`http://localhost:3000/${community.slug}`); -}); - -// Afterwards close the browser -afterAll(async () => { - await browser.close(); -}); - -it('should render', async () => { - await page.waitForSelector('[data-e2e-id="community-view"]'); -}); - -it('should show the communities data', async () => { - const content = await page.content(); - expect(content).toContain(community.description); - expect(content).toContain(community.name); - expect(content).toContain(community.website); - expect(content).toContain(community.profilePhoto); - expect(content).toContain(community.coverPhoto); -}); - -it('should show a list of the threads in that community', async () => { - await page.waitForSelector('[data-e2e-id="thread-feed"]'); - const content = await page.content(); - data.threads - .filter(thread => thread.communityId === community.id) - .map(thread => { - expect(content).toContain(thread.content.title); - }); -}); - -it('should show a list of channels in that community', async () => { - const content = await page.content(); - data.channels - .filter(channel => channel.communityId === community.id) - .map(channel => { - expect(content).toContain(channel.name); - }); -}); diff --git a/src/test-e2e/inbox.test.js b/src/test-e2e/inbox.test.js deleted file mode 100644 index 6d08de3e46..0000000000 --- a/src/test-e2e/inbox.test.js +++ /dev/null @@ -1,74 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import { encode } from 'iris/utils/base64'; -import data from '../../shared/testing/data'; - -let browser; -let page; -const user = data.users[0]; -const channelIds = data.usersChannels - .filter(({ userId }) => userId === user.id) - .map(({ channelId }) => channelId); -const dashboardThreads = data.threads.filter(({ channelId }) => - channelIds.includes(channelId) -); - -// If DEBUG_E2E is set show a browser and run test in slow mo -const config = process.env.DEBUG_E2E - ? { - headless: false, - slowMo: 100, - } - : {}; - -// Before every test suite set up a new browser and page -beforeAll(async () => { - browser = await puppeteer.launch(config); - page = await browser.newPage(); - - // Set the right cookie so that we're logged in - await page.setCookie({ - name: 'session', - value: encode(JSON.stringify({ passport: { user: user.id } })), - domain: 'localhost', - path: '/', - httpOnly: true, - secure: false, - }); - // Navigate the page to the inbox page for all tests - await page.goto('http://localhost:3000/'); -}); - -// Afterwards close the browser -afterAll(async () => { - await browser.close(); -}); - -it('should render the inbox view', async () => { - await page.waitForSelector('[data-e2e-id="inbox-view"]'); -}); - -it('should render a list of threads in the channels the user is a member of', async () => { - await page.waitForSelector('[data-e2e-id="inbox-thread-feed"]'); - const content = await page.content(); - dashboardThreads.forEach(thread => { - expect(content).toContain(thread.content.title); - }); -}); - -it('should render a list of communities the user is a member of', async () => { - await page.waitForSelector('[data-e2e-id="inbox-community-list"]'); - const usersCommunities = data.usersCommunities - .filter(({ userId }) => user.id === userId) - .map(({ communityId }) => - data.communities.find(({ id }) => id === communityId) - ); - const content = await page.content(); - usersCommunities.forEach(community => { - expect(content).toContain(community.profilePhoto); - }); -}); - -it('should render a thread view', async () => { - await page.waitForSelector('[data-e2e-id="thread-view"]'); -}); diff --git a/src/test-e2e/login.test.js b/src/test-e2e/login.test.js deleted file mode 100644 index f5856c6553..0000000000 --- a/src/test-e2e/login.test.js +++ /dev/null @@ -1,48 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; - -let browser; -let page; - -// If DEBUG_E2E is set show a browser and run test in slow mo -const config = process.env.DEBUG_E2E - ? { - headless: false, - slowMo: 100, - } - : {}; - -// Before every test suite set up a new browser and page -beforeAll(async () => { - browser = await puppeteer.launch(config); - page = await browser.newPage(); - // Navigate the page to the login page for all tests - await page.goto('http://localhost:3000/login'); -}); - -// Afterwards close the browser -afterAll(async () => { - await browser.close(); -}); - -it('should render the login page', async () => { - await page.waitForSelector('[data-e2e-id="login-page"]'); -}); - -it('should have a link to twitter auth', async () => { - await page.waitForSelector('[href*="/auth/twitter"]'); -}); - -it('should have a link to facebook auth', async () => { - await page.waitForSelector('[href*="/auth/facebook"]'); -}); - -it('should have a link to google auth', async () => { - await page.waitForSelector('[href*="/auth/google"]'); -}); - -it('should have a link to the code of conduct', async () => { - await page.waitForSelector( - '[href*="github.com/withspectrum/code-of-conduct"]' - ); -}); diff --git a/src/test-e2e/splash.test.js b/src/test-e2e/splash.test.js deleted file mode 100644 index 3ff77afcbd..0000000000 --- a/src/test-e2e/splash.test.js +++ /dev/null @@ -1,42 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; - -let browser; -let page; - -// If DEBUG_E2E is set show a browser and run test in slow mo -const config = process.env.DEBUG_E2E - ? { - headless: false, - slowMo: 100, - } - : {}; - -// Before every test suite set up a new browser and page -beforeAll(async () => { - browser = await puppeteer.launch(config); - page = await browser.newPage(); - // Navigate the page to the splash page for all tests - await page.goto('http://localhost:3000/'); -}); - -// Afterwards close the browser -afterAll(async () => { - await browser.close(); -}); - -it('should render the splash page', async () => { - await page.waitForSelector('[data-e2e-id="splash-page"]'); -}); - -it('should have a login button', async () => { - await page.waitForSelector('[href*="/login"]'); -}); - -it('should have a button to explore', async () => { - await page.waitForSelector('[href*="/explore"]'); -}); - -it('should have a button to /new/community', async () => { - await page.waitForSelector('[href*="/new/community"]'); -}); diff --git a/src/test-e2e/thread.test.js b/src/test-e2e/thread.test.js deleted file mode 100644 index a528832ebb..0000000000 --- a/src/test-e2e/thread.test.js +++ /dev/null @@ -1,80 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import { toPlainText, toState } from '../../shared/draft-utils'; -import data from '../../shared/testing/data'; - -let browser; -let page; -const thread = data.threads[0]; -const channel = data.channels.find(channel => channel.id === thread.channelId); -const community = data.communities.find( - community => community.id === thread.communityId -); -const author = data.users.find(user => user.id === thread.creatorId); -const messages = data.messages.filter( - message => message.threadId === thread.id -); - -// If DEBUG_E2E is set show a browser and run test in slow mo -const config = process.env.DEBUG_E2E - ? { - headless: false, - slowMo: 100, - } - : {}; - -// Before every test suite set up a new browser and page -beforeAll(async () => { - browser = await puppeteer.launch(config); - page = await browser.newPage(); - // Navigate the page to the login page for all tests - await page.goto(`http://localhost:3000/thread/${thread.id}`); -}); - -// Afterwards close the browser -afterAll(async () => { - await browser.close(); -}); - -it('should render', async () => { - await page.waitForSelector('[data-e2e-id="thread-view"]'); -}); - -it('should show the threads content', async () => { - const content = await page.content(); - expect(content).toContain(thread.content.title); - expect(content).toContain( - toPlainText(toState(JSON.parse(thread.content.body))).split(' ')[0] - ); -}); - -it('should show the threads author', async () => { - const content = await page.content(); - expect(content).toContain(author.name); - expect(content).toContain(author.username); -}); - -it('should have a link to the author', async () => { - await page.waitForSelector(`[href*="/users/${author.username}"]`); -}); - -it('should have a link to the community', async () => { - await page.waitForSelector(`[href*="/${community.slug}"]`); -}); - -// We don't show links to general channels -if (channel.slug !== 'general') { - it('should have a link to the channel', async () => { - await page.waitForSelector(`[href*="/${community.slug}/${channel.slug}"]`); - }); -} - -it('should show all its messages', async () => { - await page.waitForSelector('[data-e2e-id="message-group"]'); - const content = await page.content(); - messages.forEach(message => { - expect(content).toContain( - toPlainText(toState(JSON.parse(message.content.body))) - ); - }); -}); diff --git a/src/test-e2e/user.test.js b/src/test-e2e/user.test.js deleted file mode 100644 index 91203d4b2f..0000000000 --- a/src/test-e2e/user.test.js +++ /dev/null @@ -1,66 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import data from '../../shared/testing/data'; - -let browser; -let page; -const user = data.users[0]; - -// If DEBUG_E2E is set show a browser and run test in slow mo -const config = process.env.DEBUG_E2E - ? { - headless: false, - slowMo: 100, - } - : {}; - -// Before every test suite set up a new browser and page -beforeAll(async () => { - browser = await puppeteer.launch(config); - page = await browser.newPage(); - // Navigate the page to the login page for all tests - await page.goto(`http://localhost:3000/users/${user.username}`); -}); - -// Afterwards close the browser -afterAll(async () => { - await browser.close(); -}); - -it('should render', async () => { - await page.waitForSelector('[data-e2e-id="user-view"]'); -}); - -it('should show the users data', async () => { - const content = await page.content(); - expect(content).toContain(user.username); - expect(content).toContain(user.name); - expect(content).toContain(user.description); - expect(content).toContain(user.website); -}); - -it('should list threads the users has posted', async () => { - await page.waitForSelector('[data-e2e-id="thread-feed"]'); - const content = await page.content(); - data.threads.filter(thread => thread.creatorId === user.id).map(thread => { - expect(content).toContain(thread.content.title); - }); -}); - -it('should list the communities a user is a member of, including their rep in that community', async () => { - const content = await page.content(); - const usersCommunities = data.usersCommunities.filter( - ({ userId }) => userId === user.id - ); - const communityIds = usersCommunities.map(({ communityId }) => communityId); - const communities = data.communities.filter(({ id }) => - communityIds.includes(id) - ); - communities.map(community => { - expect(content).toContain(community.name); - const userCommunity = usersCommunities.find( - ({ communityId }) => communityId === community.id - ); - expect(content).toContain(userCommunity.reputation); - }); -}); diff --git a/src/views/channelSettings/components/loginTokenSettings.js b/src/views/channelSettings/components/loginTokenSettings.js new file mode 100644 index 0000000000..6fe561f5aa --- /dev/null +++ b/src/views/channelSettings/components/loginTokenSettings.js @@ -0,0 +1,100 @@ +// @flow +import * as React from 'react'; +import compose from 'recompose/compose'; +import { connect } from 'react-redux'; +import getChannelSettings, { + type GetChannelSettingsType, +} from 'shared/graphql/queries/channel/getChannelSettings'; +import Clipboard from 'react-clipboard.js'; +import { Loading } from 'src/components/loading'; +import viewNetworkHandler, { + type ViewNetworkHandlerType, +} from 'src/components/viewNetworkHandler'; +import { + SectionCard, + SectionTitle, + SectionSubtitle, +} from 'src/components/settingsViews/style'; +import LoginTokenToggle from './loginTokenToggle'; +import ResetJoinToken from './resetJoinToken'; +import { Input } from 'src/components/formElements'; +import { addToastWithTimeout } from 'src/actions/toasts'; +import { TokenInputWrapper } from '../style'; + +type Props = { + data: { + channel: GetChannelSettingsType, + }, + ...$Exact, + saveBrandedLoginSettings: Function, + dispatch: Function, +}; + +type State = { + isLoading: boolean, +}; + +class LoginTokenSettings extends React.Component { + state = { + isLoading: false, + }; + + render() { + const { data: { channel }, isLoading } = this.props; + + if (channel) { + const { joinSettings } = channel; + + return ( + + Join channel via link + + Allow people to join this private channel by visiting a unique link. + Anyone with this link will be able to join this channel. + + + + + {joinSettings.tokenJoinEnabled && ( + + this.props.dispatch( + addToastWithTimeout('success', 'Copied to clipboard') + ) + } + > + + {}} + /> + + + )} + + {joinSettings.tokenJoinEnabled && } + + ); + } + + if (isLoading) { + return ( + + + + ); + } + + return null; + } +} + +export default compose(getChannelSettings, viewNetworkHandler, connect())( + LoginTokenSettings +); diff --git a/src/views/channelSettings/components/loginTokenToggle.js b/src/views/channelSettings/components/loginTokenToggle.js new file mode 100644 index 0000000000..b397e48c0c --- /dev/null +++ b/src/views/channelSettings/components/loginTokenToggle.js @@ -0,0 +1,68 @@ +// @flow +import * as React from 'react'; +import { Checkbox } from 'src/components/formElements'; +import { connect } from 'react-redux'; +import compose from 'recompose/compose'; +import enableTokenJoinMutation from 'shared/graphql/mutations/channel/enableChannelTokenJoin'; +import disableTokenJoinMutation from 'shared/graphql/mutations/channel/disableChannelTokenJoin'; +import { addToastWithTimeout } from '../../../actions/toasts'; + +type Props = { + id: string, + settings: { + tokenJoinEnabled: boolean, + }, + enableChannelTokenJoin: Function, + disableChannelTokenJoin: Function, + dispatch: Function, +}; + +class TokenJoinToggle extends React.Component { + init = () => { + return this.props.settings.tokenJoinEnabled + ? this.disable() + : this.enable(); + }; + + disable = () => { + return this.props + .disableChannelTokenJoin({ id: this.props.id }) + .then(() => { + return this.props.dispatch( + addToastWithTimeout('neutral', 'Link disabled') + ); + }) + .catch(err => { + return this.props.dispatch(addToastWithTimeout('error', err.message)); + }); + }; + + enable = () => { + return this.props + .enableChannelTokenJoin({ id: this.props.id }) + .then(() => { + return this.props.dispatch( + addToastWithTimeout('success', 'Link enabled') + ); + }) + .catch(err => { + return this.props.dispatch(addToastWithTimeout('error', err.message)); + }); + }; + + render() { + const { tokenJoinEnabled } = this.props.settings; + + return ( + + Enable users to join via link + + ); + } +} + +export default compose( + connect(), + enableTokenJoinMutation, + disableTokenJoinMutation +)(TokenJoinToggle); diff --git a/src/views/channelSettings/components/overview.js b/src/views/channelSettings/components/overview.js index ef755a3a6a..1f453f059c 100644 --- a/src/views/channelSettings/components/overview.js +++ b/src/views/channelSettings/components/overview.js @@ -8,7 +8,7 @@ import EditForm from './editForm'; import PendingUsers from './pendingUsers'; import BlockedUsers from './blockedUsers'; import ChannelMembers from './channelMembers'; -// import { ChannelInvitationForm } from '../../../components/emailInvitationForm'; +import LoginTokenSettings from './loginTokenSettings'; type Props = { community: Object, @@ -25,6 +25,9 @@ class Overview extends React.Component { + {channel.isPrivate && ( + + )} {/*channel.isPrivate && ( diff --git a/src/views/channelSettings/components/resetJoinToken.js b/src/views/channelSettings/components/resetJoinToken.js new file mode 100644 index 0000000000..02d0a1f0e8 --- /dev/null +++ b/src/views/channelSettings/components/resetJoinToken.js @@ -0,0 +1,64 @@ +// @flow +import * as React from 'react'; +import { connect } from 'react-redux'; +import compose from 'recompose/compose'; +import resetJoinTokenMutation from 'shared/graphql/mutations/channel/resetChannelJoinToken'; +import { addToastWithTimeout } from 'src/actions/toasts'; +import { OutlineButton } from 'src/components/buttons'; + +type Props = { + id: string, + settings: { + tokenJoinEnabled: boolean, + }, + resetChannelJoinToken: Function, + dispatch: Function, +}; + +type State = { + isLoading: boolean, +}; + +class ResetJoinToken extends React.Component { + state = { isLoading: false }; + + reset = () => { + this.setState({ isLoading: true }); + return this.props + .resetChannelJoinToken({ id: this.props.id }) + .then(() => { + this.setState({ + isLoading: false, + }); + return this.props.dispatch( + addToastWithTimeout('success', 'Link reset!') + ); + }) + .catch(err => { + this.setState({ + isLoading: false, + }); + return this.props.dispatch(addToastWithTimeout('error', err.message)); + }); + }; + + render() { + const { isLoading } = this.state; + + return ( +
+ + Reset this link + +
+ ); + } +} + +export default compose(connect(), resetJoinTokenMutation)(ResetJoinToken); diff --git a/src/views/channelSettings/style.js b/src/views/channelSettings/style.js new file mode 100644 index 0000000000..b386ef209b --- /dev/null +++ b/src/views/channelSettings/style.js @@ -0,0 +1,32 @@ +// @flow +import styled from 'styled-components'; + +export const TokenInputWrapper = styled.div` + position: relative; + cursor: pointer; + + input { + cursor: pointer; + } + + &:after { + content: 'Copy link'; + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + font-size: 10px; + text-transform: uppercase; + color: ${props => props.theme.text.reverse}; + background: ${props => props.theme.text.alt}; + padding: 4px 8px; + border-radius: 4px; + font-weight: 700; + } + + &:hover { + &:after { + background: ${props => props.theme.success.alt}; + } + } +`; diff --git a/src/views/privateChannelJoin/index.js b/src/views/privateChannelJoin/index.js new file mode 100644 index 0000000000..a399c57dd3 --- /dev/null +++ b/src/views/privateChannelJoin/index.js @@ -0,0 +1,98 @@ +// @flow +import * as React from 'react'; +import compose from 'recompose/compose'; +import { connect } from 'react-redux'; +import joinChannelWithToken from 'shared/graphql/mutations/channel/joinChannelWithToken'; +import { addToastWithTimeout } from 'src/actions/toasts'; +import CommunityLogin from 'src/views/communityLogin'; +import AppViewWrapper from 'src/components/appViewWrapper'; +import { Loading } from 'src/components/loading'; + +type Props = { + match: Object, + location: Object, + history: Object, + joinChannelWithToken: Function, + currentUser: Object, + dispatch: Function, +}; + +type State = { + isLoading: boolean, +}; + +class PrivateChannelJoin extends React.Component { + state = { + isLoading: false, + }; + + componentDidMount() { + const { match, history, currentUser } = this.props; + const { token, communitySlug, channelSlug } = match.params; + + if (!token) { + return history.push(`/${communitySlug}/${channelSlug}`); + } + + if (!currentUser) { + return; + } + + return this.handleJoin(); + } + + componentDidUpdate(prevProps) { + const curr = this.props; + + if (!prevProps.currentUser && curr.currentUser) { + return this.handleJoin(); + } + } + + handleJoin = () => { + const { match, history, joinChannelWithToken, dispatch } = this.props; + const { token, communitySlug, channelSlug } = match.params; + + this.setState({ isLoading: true }); + + joinChannelWithToken({ channelSlug, token, communitySlug }) + .then(data => { + this.setState({ isLoading: false }); + dispatch(addToastWithTimeout('success', 'Welcome!')); + return history.push(`/${communitySlug}/${channelSlug}`); + }) + .catch(err => { + this.setState({ isLoading: false }); + dispatch(addToastWithTimeout('error', err.message)); + return history.push(`/${communitySlug}/${channelSlug}`); + }); + }; + + render() { + const { currentUser, match } = this.props; + const { isLoading } = this.state; + + if (!currentUser || !currentUser.id) { + return ( + + ); + } + + if (isLoading) { + return ( + + + + ); + } + + return null; + } +} + +const map = state => ({ currentUser: state.users.currentUser }); +// $FlowIssue +export default compose(connect(map), joinChannelWithToken)(PrivateChannelJoin); diff --git a/yarn.lock b/yarn.lock index ac0ee7da45..498d4fcb43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -73,14 +73,7 @@ esutils "^2.0.2" js-tokens "^3.0.0" -"@babel/polyfill@^7.0.0-beta.32": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.0.0-beta.40.tgz#90f447aa04ab54c317dcf0ccb8cb11ad4228fea0" - dependencies: - core-js "^2.5.3" - regenerator-runtime "^0.11.1" - -"@babel/runtime@^7.0.0-beta.32", "@babel/runtime@^7.0.0-beta.37": +"@babel/runtime@^7.0.0-beta.38", "@babel/runtime@^7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.40.tgz#8e3b8f1d2d8639d010e991a7e99c1d9ef578f886" dependencies: @@ -118,6 +111,21 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" +"@cypress/listr-verbose-renderer@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +"@cypress/xvfb@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.1.3.tgz#6294a7d1feb751f12302248f2089fc534c4acb7f" + dependencies: + lodash.once "^4.1.1" + "@octokit/rest@^14.0.4": version "14.0.9" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-14.0.9.tgz#d5e0a00dcb78901dd7b2ef852acfc0aea7c479ef" @@ -133,14 +141,72 @@ version "2.0.47" resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.47.tgz#f49ba1dd1f189486beb6e1d070a850f6ab4bd521" +"@types/blob-util@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@types/blob-util/-/blob-util-1.3.3.tgz#adba644ae34f88e1dd9a5864c66ad651caaf628a" + +"@types/bluebird@3.5.18": + version "3.5.18" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.18.tgz#6a60435d4663e290f3709898a4f75014f279c4d6" + +"@types/chai-jquery@1.1.35": + version "1.1.35" + resolved "https://registry.yarnpkg.com/@types/chai-jquery/-/chai-jquery-1.1.35.tgz#9a8f0a39ec0851b2768a8f8c764158c2a2568d04" + dependencies: + "@types/chai" "*" + "@types/jquery" "*" + +"@types/chai@*": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.2.tgz#f1af664769cfb50af805431c407425ed619daa21" + +"@types/chai@4.0.8": + version "4.0.8" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.8.tgz#d27600e9ba2f371e08695d90a0fe0408d89c7be7" + "@types/graphql@^0.9.0": version "0.9.4" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.4.tgz#cdeb6bcbef9b6c584374b81aa7f48ecf3da404fa" +"@types/jquery@*": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.1.tgz#55758d44d422756d6329cbf54e6d41931d7ba28f" + +"@types/jquery@3.2.16": + version "3.2.16" + resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.2.16.tgz#04419c404a3194350e7d3f339a90e72c88db3111" + +"@types/lodash@4.14.87": + version "4.14.87" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.87.tgz#55f92183b048c2c64402afe472f8333f4e319a6b" + +"@types/minimatch@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.1.tgz#b683eb60be358304ef146f5775db4c0e3696a550" + +"@types/mocha@2.2.44": + version "2.2.44" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e" + "@types/node@*", "@types/node@^9.4.6": version "9.4.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.6.tgz#d8176d864ee48753d053783e4e463aec86b8d82e" +"@types/sinon-chai@2.7.29": + version "2.7.29" + resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-2.7.29.tgz#4db01497e2dd1908b2bd30d1782f456353f5f723" + dependencies: + "@types/chai" "*" + "@types/sinon" "*" + +"@types/sinon@*": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.3.0.tgz#7f53915994a00ccea24f4e0c24709822ed11a3b1" + +"@types/sinon@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.0.0.tgz#9a93ffa4ee1329e85166278a5ed99f81dc4c8362" + "@types/zen-observable@0.5.3", "@types/zen-observable@^0.5.3": version "0.5.3" resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.5.3.tgz#91b728599544efbb7386d8b6633693a3c2e7ade5" @@ -318,7 +384,7 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.0.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.0.0, ansi-styles@^3.1.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" dependencies: @@ -457,20 +523,20 @@ apollo-tracing@^0.1.0: dependencies: graphql-extensions "^0.0.x" -apollo-upload-client@6.x: - version "6.0.3" - resolved "https://registry.yarnpkg.com/apollo-upload-client/-/apollo-upload-client-6.0.3.tgz#9f3249910ede32b7336968b1ae9fa438ed48a247" +apollo-upload-client@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/apollo-upload-client/-/apollo-upload-client-8.0.0.tgz#0067f3b426b3828f971964799bc31f8073bd0607" dependencies: - "@babel/polyfill" "^7.0.0-beta.32" - "@babel/runtime" "^7.0.0-beta.32" - extract-files "^2.0.1" + "@babel/runtime" "^7.0.0-beta.40" + apollo-link-http-common "^0.2.3" + extract-files "^3.1.0" -apollo-upload-server@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/apollo-upload-server/-/apollo-upload-server-2.0.4.tgz#5105081b6c061638ef7a04ef848758d30f3ba96b" +apollo-upload-server@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/apollo-upload-server/-/apollo-upload-server-5.0.0.tgz#c953b523608313966e0c8444637f4ae8ef77d5bc" dependencies: - formidable "^1.1.1" - mkdirp "^0.5.1" + "@babel/runtime" "^7.0.0-beta.40" + busboy "^0.2.14" object-path "^0.11.4" apollo-utilities@^1.0.0, apollo-utilities@^1.0.1, apollo-utilities@^1.0.4, apollo-utilities@^1.0.8: @@ -580,18 +646,10 @@ array-map@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" -array-parallel@~0.1.0: - version "0.1.3" - resolved "https://registry.yarnpkg.com/array-parallel/-/array-parallel-0.1.3.tgz#8f785308926ed5aa478c47e64d1b334b6c0c947d" - array-reduce@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" -array-series@~0.1.0: - version "0.1.5" - resolved "https://registry.yarnpkg.com/array-series/-/array-series-0.1.5.tgz#df5d37bfc5c2ef0755e2aa4f92feae7d4b5a972f" - array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -690,6 +748,12 @@ async-to-gen@1.3.3: babylon "^6.14.0" magic-string "^0.19.0" +async@2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4" + dependencies: + lodash "^4.14.0" + async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -730,12 +794,19 @@ autoprefixer@^6.3.1: postcss "^5.2.16" postcss-value-parser "^3.2.3" -aws-sdk@~2.0.17: - version "2.0.31" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.0.31.tgz#e72cf1fdc69015bd9fd2bdf3d3b88c16507d268e" +aws-sdk@2.200.0: + version "2.200.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.200.0.tgz#f460c96408725b0eb8c658fddea6e0bfe0ef5a44" dependencies: - xml2js "0.2.6" - xmlbuilder "0.4.2" + buffer "4.9.1" + events "^1.1.1" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.1.0" + xml2js "0.4.17" + xmlbuilder "4.2.1" aws-sign2@~0.6.0: version "0.6.0" @@ -1867,6 +1938,10 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" @@ -1879,7 +1954,7 @@ buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" -buffer@^4.3.0: +buffer@4.9.1, buffer@^4.3.0: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" dependencies: @@ -1918,6 +1993,13 @@ bull@3.3.10: semver "^5.4.1" uuid "^3.1.0" +busboy@^0.2.14: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + bytebuffer@~5: version "5.0.1" resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd" @@ -2053,6 +2135,14 @@ chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" @@ -2073,6 +2163,10 @@ charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + check-types@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.3.0.tgz#468f571a4435c24248f5fd0cb0e8d87c3c341e7d" @@ -2344,13 +2438,19 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" +commander@2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" + commander@2.14.x, commander@^2.11.0, commander@^2.13.0, commander@^2.9.0, commander@~2.14.1: version "2.14.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" -commander@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" +common-tags@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0" + dependencies: + babel-runtime "^6.18.0" common-tags@^1.7.2: version "1.7.2" @@ -2772,6 +2872,47 @@ curry2@^1.0.0: dependencies: fast-bind "^1.0.0" +cypress@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-2.1.0.tgz#a8bd7d9b89c38a1e380db83b57d9bba0dbb95ba4" + dependencies: + "@cypress/listr-verbose-renderer" "0.4.1" + "@cypress/xvfb" "1.1.3" + "@types/blob-util" "1.3.3" + "@types/bluebird" "3.5.18" + "@types/chai" "4.0.8" + "@types/chai-jquery" "1.1.35" + "@types/jquery" "3.2.16" + "@types/lodash" "4.14.87" + "@types/minimatch" "3.0.1" + "@types/mocha" "2.2.44" + "@types/sinon" "4.0.0" + "@types/sinon-chai" "2.7.29" + bluebird "3.5.0" + chalk "2.1.0" + check-more-types "2.24.0" + commander "2.11.0" + common-tags "1.4.0" + debug "3.1.0" + extract-zip "1.6.6" + fs-extra "4.0.1" + getos "2.8.4" + glob "7.1.2" + is-ci "1.0.10" + is-installed-globally "0.1.0" + lazy-ass "1.6.0" + listr "0.12.0" + lodash "4.17.4" + minimist "1.2.0" + progress "1.1.8" + ramda "0.24.1" + request "2.81.0" + request-progress "0.3.1" + supports-color "5.1.0" + tmp "0.0.31" + url "0.11.0" + yauzl "2.8.0" + d@1: version "1.0.0" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" @@ -2798,9 +2939,9 @@ danger-plugin-jest@^1.1.0: optionalDependencies: serve "^5.1.5" -danger-plugin-no-console@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/danger-plugin-no-console/-/danger-plugin-no-console-1.0.0.tgz#7ba7107cbc28cc72c0ae5d0c359056364c31cd23" +danger-plugin-no-console@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/danger-plugin-no-console/-/danger-plugin-no-console-1.1.1.tgz#d56ee7318c04605afd4e8881226bdeb2a09fed52" optionalDependencies: esdoc "^0.5.2" @@ -2880,10 +3021,6 @@ debounce@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.1.0.tgz#6a1a4ee2a9dc4b7c24bb012558dbcdb05b37f408" -debug@0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.0.tgz#f5be05ec0434c992d79940e50b2695cfb2e01b08" - debug@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e" @@ -3080,6 +3217,13 @@ detect-port@1.2.1: address "^1.0.1" debug "^2.6.0" +dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + diff@^3.2.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -3884,7 +4028,7 @@ eventemitter3@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba" -events@^1.0.0, events@^1.1.0: +events@^1.0.0, events@^1.1.0, events@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -4107,11 +4251,11 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-files@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-2.1.1.tgz#3e76eaeeccb5789fc369bfc22bdf9c0e6c5d8b1b" +extract-files@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-3.1.0.tgz#b70424c9d4a1a4208efe22069388f428e4ae00f1" dependencies: - "@babel/runtime" "^7.0.0-beta.37" + "@babel/runtime" "^7.0.0-beta.38" extract-text-webpack-plugin@3.0.2: version "3.0.2" @@ -4122,7 +4266,7 @@ extract-text-webpack-plugin@3.0.2: schema-utils "^0.3.0" webpack-sources "^1.0.1" -extract-zip@^1.6.5: +extract-zip@1.6.6, extract-zip@^1.6.5: version "1.6.6" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c" dependencies: @@ -4199,12 +4343,6 @@ fbjs@0.8.16, fbjs@^0.8.1, fbjs@^0.8.15, fbjs@^0.8.16, fbjs@^0.8.5, fbjs@^0.8.9: setimmediate "^1.0.5" ua-parser-js "^0.7.9" -fd-slicer@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-0.1.0.tgz#f597141dfe8a2841756fd54150a78fe0bec4bc03" - dependencies: - pend "~1.1.2" - fd-slicer@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" @@ -4325,10 +4463,6 @@ find-with-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/find-with-regex/-/find-with-regex-1.0.2.tgz#d3b36286539f14c527e31f194159c6d251651a45" -findit@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/findit/-/findit-2.0.0.tgz#6509f0126af4c178551cfa99394e032e13a4d56e" - flat-cache@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" @@ -4410,10 +4544,6 @@ form-data@~2.3.1: combined-stream "1.0.6" mime-types "^2.1.12" -formidable@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9" - forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -4464,6 +4594,14 @@ fs-extra@3.0.1: jsonfile "^3.0.0" universalify "^0.1.0" +fs-extra@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.1.tgz#7fc0c6c8957f983f57f306a24e5b9ddd8d0dd880" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^3.0.0" + universalify "^0.1.0" + fs-extra@^0.30.0: version "0.30.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" @@ -4578,6 +4716,12 @@ get-window@^1.1.1: dependencies: get-document "1" +getos@2.8.4: + version "2.8.4" + resolved "https://registry.yarnpkg.com/getos/-/getos-2.8.4.tgz#7b8603d3619c28e38cb0fe7a4f63c3acb80d5163" + dependencies: + async "2.1.4" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -4618,7 +4762,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: +glob@7.1.2, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -4693,16 +4837,6 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -gm@~1.16.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/gm/-/gm-1.16.0.tgz#2b3d37b25fb349286e829bf1ccf097f4de4fba88" - dependencies: - array-parallel "~0.1.0" - array-series "~0.1.0" - debug "0.7.0" - stream-to-buffer "~0.0.1" - through "~2.3.1" - good-listener@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" @@ -5472,6 +5606,12 @@ is-callable@^1.1.1, is-callable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" +is-ci@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" + dependencies: + ci-info "^1.0.0" + is-ci@^1.0.10: version "1.1.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.1.0.tgz#247e4162e7860cebbdaf30b774d6b0ac7dcfe7a5" @@ -5598,7 +5738,7 @@ is-in-browser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" -is-installed-globally@^0.1.0: +is-installed-globally@0.1.0, is-installed-globally@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" dependencies: @@ -6555,6 +6695,10 @@ jest@^22.1.0: import-local "^1.0.0" jest-cli "^22.4.2" +jmespath@0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" + joi@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/joi/-/joi-13.1.2.tgz#b2db260323cc7f919fafa51e09e2275bd089a97e" @@ -6847,6 +6991,10 @@ latest-version@^3.0.0: dependencies: package-json "^4.0.0" +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" @@ -6928,7 +7076,7 @@ listr-verbose-renderer@^0.4.0: date-fns "^1.27.2" figures "^1.7.0" -listr@^0.12.0: +listr@0.12.0, listr@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a" dependencies: @@ -7131,7 +7279,7 @@ lodash.noop@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c" -lodash.once@^4.0.0: +lodash.once@^4.0.0, lodash.once@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" @@ -7188,7 +7336,11 @@ lodash.values@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" -"lodash@>=3.5 <5", lodash@^4.1.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1: +lodash@4.17.4: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" @@ -7476,10 +7628,6 @@ mime@^1.2.11, mime@^1.3.4, mime@^1.4.1, mime@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" -mime@~1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" - mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -7582,10 +7730,6 @@ nan@^2.3.0: version "2.9.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.9.2.tgz#f564d75f5f8f36a6d9456cca7a6c4fe488ab7866" -nan@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-1.0.0.tgz#ae24f8850818d662fcab5acf7f3b95bfaa2ccf38" - nanomatch@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" @@ -7965,10 +8109,6 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" wordwrap "~1.0.0" -options@>=0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" - ora@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" @@ -8010,7 +8150,7 @@ os-shim@^0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -8295,10 +8435,6 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" -pend@~1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.1.3.tgz#ca68dd39e6dd7f8d3f8801dcdbcb44846c431845" - pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -8737,6 +8873,10 @@ process@~0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" +progress@1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + progress@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" @@ -8901,6 +9041,10 @@ raf@3.4.0: dependencies: performance-now "^2.1.0" +ramda@0.24.1: + version "0.24.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" + ramda@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.23.0.tgz#ccd13fff73497a93974e3e86327bfd87bd6e8e2b" @@ -9260,6 +9404,15 @@ readable-stream@1.1: isarray "0.0.1" string_decoder "~0.10.x" +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.3: version "2.3.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" @@ -9468,6 +9621,12 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" +request-progress@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-0.3.1.tgz#0721c105d8a96ac6b2ce8b2c89ae2d5ecfcf6b3a" + dependencies: + throttleit "~0.0.2" + request-promise-core@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" @@ -9697,10 +9856,6 @@ rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2. dependencies: glob "^7.0.5" -rimraf@~2.2.8: - version "2.2.8" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" - ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" @@ -9730,28 +9885,6 @@ rxjs@^5.0.0-beta.11: dependencies: symbol-observable "1.0.1" -s3-image-uploader@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/s3-image-uploader/-/s3-image-uploader-1.0.7.tgz#770ea8e559f524d59ccb549da47baf10e1bc4bc0" - dependencies: - extend "^3.0.0" - gm "~1.16.0" - s3 "~4.2.0" - ws "~0.4.32" - -s3@~4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/s3/-/s3-4.2.0.tgz#7c19255d57a8fd45761d2eeb2f720316aa5775c9" - dependencies: - aws-sdk "~2.0.17" - fd-slicer "~0.1.0" - findit "~2.0.0" - graceful-fs "~3.0.2" - mime "~1.2.11" - mkdirp "~0.5.0" - pend "~1.1.2" - rimraf "~2.2.8" - safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -9788,11 +9921,11 @@ sane@~1.6.0: walker "~1.0.5" watch "~0.10.0" -sax@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/sax/-/sax-0.4.2.tgz#39f3b601733d6bec97105b242a2a40fd6978ac3c" +sax@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" -sax@^1.1.4, sax@^1.2.1, sax@^1.2.4, sax@~1.2.1: +sax@>=0.6.0, sax@^1.1.4, sax@^1.2.1, sax@^1.2.4, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -10013,6 +10146,10 @@ shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" +shortid@^2.2.8: + version "2.2.8" + resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.8.tgz#033b117d6a2e975804f6f0969dbe7d3d0b355131" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -10320,14 +10457,14 @@ stream-http@^2.7.2: to-arraybuffer "^1.0.0" xtend "^4.0.0" -stream-to-buffer@~0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/stream-to-buffer/-/stream-to-buffer-0.0.1.tgz#ab483d59a1ca71832de379a255f465b665af45c1" - stream-to-observable@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -10492,6 +10629,12 @@ subscriptions-transport-ws@0.9.x: symbol-observable "^1.0.4" ws "^3.0.0" +supports-color@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5" + dependencies: + has-flag "^2.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -10502,7 +10645,7 @@ supports-color@^3.1.2, supports-color@^3.2.3: dependencies: has-flag "^1.0.0" -supports-color@^4.2.1: +supports-color@^4.0.0, supports-color@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" dependencies: @@ -10670,6 +10813,10 @@ throat@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" +throttleit@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf" + through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1, through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -10696,14 +10843,16 @@ tiny-emitter@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c" -tinycolor@0.x: - version "0.0.1" - resolved "https://registry.yarnpkg.com/tinycolor/-/tinycolor-0.0.1.tgz#320b5a52d83abb5978d81a3e887d4aefb15a6164" - tlds@^1.189.0: version "1.199.0" resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.199.0.tgz#a4fc8c3058216488a80aaaebb427925007e55217" +tmp@0.0.31: + version "0.0.31" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" + dependencies: + os-tmpdir "~1.0.1" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -11050,7 +11199,14 @@ url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" -url@^0.11.0: +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +url@0.11.0, url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" dependencies: @@ -11106,6 +11262,10 @@ uuid@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.0.tgz#6728fc0459c450d796a99c31837569bdf672d728" +uuid@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + uuid@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" @@ -11531,15 +11691,6 @@ ws@^4.0.0: async-limiter "~1.0.0" safe-buffer "~5.1.0" -ws@~0.4.32: - version "0.4.32" - resolved "https://registry.yarnpkg.com/ws/-/ws-0.4.32.tgz#787a6154414f3c99ed83c5772153b20feb0cec32" - dependencies: - commander "~2.1.0" - nan "~1.0.0" - options ">=0.0.5" - tinycolor "0.x" - xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" @@ -11556,15 +11707,18 @@ xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" -xml2js@0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.2.6.tgz#d209c4e4dda1fc9c452141ef41c077f5adfdf6c4" +xml2js@0.4.17: + version "0.4.17" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.17.tgz#17be93eaae3f3b779359c795b419705a8817e868" dependencies: - sax "0.4.2" + sax ">=0.6.0" + xmlbuilder "^4.1.0" -xmlbuilder@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-0.4.2.tgz#1776d65f3fdbad470a08d8604cdeb1c4e540ff83" +xmlbuilder@4.2.1, xmlbuilder@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-4.2.1.tgz#aa58a3041a066f90eaa16c2f5389ff19f3f461a5" + dependencies: + lodash "^4.0.0" xmldom@0.1.x: version "0.1.27" @@ -11765,6 +11919,13 @@ yauzl@2.4.1: dependencies: fd-slicer "~1.0.1" +yauzl@2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.8.0.tgz#79450aff22b2a9c5a41ef54e02db907ccfbf9ee2" + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.0.1" + zen-observable-ts@^0.8.6: version "0.8.8" resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.8.tgz#1a586dc204fa5632a88057f879500e0d2ba06869"