From 29a09c5f98ffedd56565b7d541e13c1b34a153e3 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Thu, 22 Feb 2018 10:08:20 +0100 Subject: [PATCH 01/57] Run all tests on CircleCI Let's see if this works! --- .circleci/config.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index f8ed376fdb..41fa1bbe7c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,6 +29,32 @@ js_defaults: &js_defaults docker: - image: circleci/node:8 +rethinkdb: &rethinkdb + <<: *js_defaults + steps: + - run: source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list + - run: wget -qO- http://download.rethinkdb.com/apt/pubkey.gpg | sudo apt-key add - + - run: sudo apt-get update + - run: sudo apt-get install rethinkdb=2.3.5 + - run: + - command: rethinkdb --bind all + - background: true + +servers: &servers + <<: *rethinkdb + steps: + - run: node -e "const setup = require('./shared/testing/setup.js')().then(() => process.exit())" + - run: yarn run build:web + - run: yarn run build:iris + - run: + - command: TEST_DB=true yarn run dev:iris + - background: true + - run: + - command: yarn run dev:web + - background: true + # This is about how long it takes our servers to start + - run: sleep 60 + macos_defaults: &macos_defaults <<: *defaults macos: @@ -59,6 +85,15 @@ jobs: at: ~/spectrum - run: cd ./mobile && yarn test:unit + test_js: + <<: *servers + steps: + - attach_workspace: + at: ~/spectrum + - run: yarn run flow + - run: yarn run test:ci + + # Tests native code of the mobile app test_mobile_native: <<: *macos_defaults From e60b282b786af9b097fba0a18206482382ca2c9a Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Thu, 22 Feb 2018 10:10:51 +0100 Subject: [PATCH 02/57] Should actually run the job eh --- .circleci/config.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 41fa1bbe7c..ac6a731e96 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -135,3 +135,10 @@ workflows: # - test_mobile_native: # requires: # - checkout_environment + + test_web: + jobs: + - checkout_environment + - test_js: + requires: + - checkout_environment From f416691745d36f19a642697ff369f79843a55577 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Thu, 22 Feb 2018 10:13:45 +0100 Subject: [PATCH 03/57] Maybe fix config --- .circleci/config.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ac6a731e96..f91f93cec6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -85,7 +85,8 @@ jobs: at: ~/spectrum - run: cd ./mobile && yarn test:unit - test_js: + # Test the web JS + test_web_js: <<: *servers steps: - attach_workspace: @@ -139,6 +140,6 @@ workflows: test_web: jobs: - checkout_environment - - test_js: - requires: - - checkout_environment + - test_web_js: + requires: + - checkout_environment From b055b83d6b7489313de3ccf9857f0d34e2c28e93 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Thu, 22 Feb 2018 17:55:08 +0100 Subject: [PATCH 04/57] Fix stuff --- .circleci/config.yml | 46 ++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f91f93cec6..cd125fc169 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,32 +29,6 @@ js_defaults: &js_defaults docker: - image: circleci/node:8 -rethinkdb: &rethinkdb - <<: *js_defaults - steps: - - run: source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list - - run: wget -qO- http://download.rethinkdb.com/apt/pubkey.gpg | sudo apt-key add - - - run: sudo apt-get update - - run: sudo apt-get install rethinkdb=2.3.5 - - run: - - command: rethinkdb --bind all - - background: true - -servers: &servers - <<: *rethinkdb - steps: - - run: node -e "const setup = require('./shared/testing/setup.js')().then(() => process.exit())" - - run: yarn run build:web - - run: yarn run build:iris - - run: - - command: TEST_DB=true yarn run dev:iris - - background: true - - run: - - command: yarn run dev:web - - background: true - # This is about how long it takes our servers to start - - run: sleep 60 - macos_defaults: &macos_defaults <<: *defaults macos: @@ -87,10 +61,28 @@ jobs: # Test the web JS test_web_js: - <<: *servers + <<: *js_defaults steps: - attach_workspace: at: ~/spectrum + - run: source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list + - run: wget -qO- http://download.rethinkdb.com/apt/pubkey.gpg | sudo apt-key add - + - run: sudo apt-get update + - run: sudo apt-get install rethinkdb=2.3.5 + - run: + - command: rethinkdb --bind all + - background: true + - run: node -e "const setup = require('./shared/testing/setup.js')().then(() => process.exit())" + - run: yarn run build:web + - run: yarn run build:iris + - run: + - command: TEST_DB=true yarn run dev:iris + - background: true + - run: + - command: yarn run dev:web + - background: true + # This is about how long it takes our servers to start + - run: sleep 60 - run: yarn run flow - run: yarn run test:ci From 823c74415e4d811e01d00aa7493031633ec11db9 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 23 Feb 2018 10:44:23 +0100 Subject: [PATCH 05/57] Cleanup based on Mikes comments --- .circleci/config.yml | 46 +++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cd125fc169..9de0e69732 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -65,26 +65,40 @@ jobs: steps: - attach_workspace: at: ~/spectrum - - run: source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list - - run: wget -qO- http://download.rethinkdb.com/apt/pubkey.gpg | sudo apt-key add - - - run: sudo apt-get update - - run: sudo apt-get install rethinkdb=2.3.5 - run: - - command: rethinkdb --bind all - - background: true - - run: node -e "const setup = require('./shared/testing/setup.js')().then(() => process.exit())" - - run: yarn run build:web - - run: yarn run build:iris + name: Install RethinkDB 2.3.5 + command: + | + source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME 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 - run: - - command: TEST_DB=true yarn run dev:iris - - background: true + name: Start RethinkDB + command: rethinkdb --bind all + background: true + - run: + 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 + - run: + name: Start Iris in the background + command: TEST_DB=true yarn run dev:iris + background: true - run: - - command: yarn run dev:web - - background: true - # This is about how long it takes our servers to start + name: Start web client in the background + command: yarn run dev:web + background: true - run: sleep 60 - - run: yarn run flow - - run: yarn run test:ci + - run: + name: Run Flow + command: yarn run flow + - run: + name: Run Tests + command: yarn run test:ci # Tests native code of the mobile app From 711b9d15c872e9e8f2b3dd7c2c10080bd875952e Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 23 Feb 2018 10:53:52 +0100 Subject: [PATCH 06/57] Remove failing command --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9de0e69732..b1090992ae 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -69,10 +69,10 @@ jobs: name: Install RethinkDB 2.3.5 command: | - source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list + 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 + sudo apt-get install rethinkdb=2.3.5~0jessie - run: name: Start RethinkDB command: rethinkdb --bind all From 209155aec31d4e2a0b66fc93904af1d32030e09f Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 23 Feb 2018 11:11:17 +0100 Subject: [PATCH 07/57] Sleep after starting RDB --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index b1090992ae..780ed7f10a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -77,6 +77,7 @@ jobs: name: Start RethinkDB command: rethinkdb --bind all background: true + - run: sleep 10 - run: name: Setup and build command: From f2bf5c1ee77deb6030caefb44c9c922afecfdd7e Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 23 Feb 2018 11:26:52 +0100 Subject: [PATCH 08/57] Start Redis? --- .circleci/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 780ed7f10a..d9cdf5aef1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -61,7 +61,10 @@ jobs: # Test the web JS test_web_js: - <<: *js_defaults + <<: *defaults + docker: + - image: circleci/node:8 + - image: redis:3.2.7 steps: - attach_workspace: at: ~/spectrum From 739433d6603ba55bb59852464f788b7114763c63 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 23 Feb 2018 14:25:32 +0100 Subject: [PATCH 09/57] Fix puppeteer on Circle --- .circleci/config.yml | 2 +- src/test-e2e/channel.test.js | 6 +++++- src/test-e2e/community.test.js | 6 +++++- src/test-e2e/inbox.test.js | 6 +++++- src/test-e2e/login.test.js | 6 +++++- src/test-e2e/splash.test.js | 6 +++++- src/test-e2e/thread.test.js | 6 +++++- src/test-e2e/user.test.js | 6 +++++- 8 files changed, 36 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d9cdf5aef1..67b8dcb728 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -63,7 +63,7 @@ jobs: test_web_js: <<: *defaults docker: - - image: circleci/node:8 + - image: circleci/node:8-browsers - image: redis:3.2.7 steps: - attach_workspace: diff --git a/src/test-e2e/channel.test.js b/src/test-e2e/channel.test.js index 190657b96e..456c2a20ad 100644 --- a/src/test-e2e/channel.test.js +++ b/src/test-e2e/channel.test.js @@ -15,7 +15,11 @@ const config = process.env.DEBUG_E2E headless: false, slowMo: 100, } - : {}; + : { + // This is needed, otherwise tests fail in CircleCI + // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }; // Before every test suite set up a new browser and page beforeAll(async () => { diff --git a/src/test-e2e/community.test.js b/src/test-e2e/community.test.js index 9f305131ec..a3bc8a3229 100644 --- a/src/test-e2e/community.test.js +++ b/src/test-e2e/community.test.js @@ -12,7 +12,11 @@ const config = process.env.DEBUG_E2E headless: false, slowMo: 100, } - : {}; + : { + // This is needed, otherwise tests fail in CircleCI + // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }; // Before every test suite set up a new browser and page beforeAll(async () => { diff --git a/src/test-e2e/inbox.test.js b/src/test-e2e/inbox.test.js index 6d08de3e46..3283c33622 100644 --- a/src/test-e2e/inbox.test.js +++ b/src/test-e2e/inbox.test.js @@ -19,7 +19,11 @@ const config = process.env.DEBUG_E2E headless: false, slowMo: 100, } - : {}; + : { + // This is needed, otherwise tests fail in CircleCI + // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }; // Before every test suite set up a new browser and page beforeAll(async () => { diff --git a/src/test-e2e/login.test.js b/src/test-e2e/login.test.js index f5856c6553..61e9af1b55 100644 --- a/src/test-e2e/login.test.js +++ b/src/test-e2e/login.test.js @@ -10,7 +10,11 @@ const config = process.env.DEBUG_E2E headless: false, slowMo: 100, } - : {}; + : { + // This is needed, otherwise tests fail in CircleCI + // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }; // Before every test suite set up a new browser and page beforeAll(async () => { diff --git a/src/test-e2e/splash.test.js b/src/test-e2e/splash.test.js index 3ff77afcbd..51b50d4684 100644 --- a/src/test-e2e/splash.test.js +++ b/src/test-e2e/splash.test.js @@ -10,7 +10,11 @@ const config = process.env.DEBUG_E2E headless: false, slowMo: 100, } - : {}; + : { + // This is needed, otherwise tests fail in CircleCI + // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }; // Before every test suite set up a new browser and page beforeAll(async () => { diff --git a/src/test-e2e/thread.test.js b/src/test-e2e/thread.test.js index a528832ebb..9c8ee2d57f 100644 --- a/src/test-e2e/thread.test.js +++ b/src/test-e2e/thread.test.js @@ -21,7 +21,11 @@ const config = process.env.DEBUG_E2E headless: false, slowMo: 100, } - : {}; + : { + // This is needed, otherwise tests fail in CircleCI + // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }; // Before every test suite set up a new browser and page beforeAll(async () => { diff --git a/src/test-e2e/user.test.js b/src/test-e2e/user.test.js index 91203d4b2f..adb3f02389 100644 --- a/src/test-e2e/user.test.js +++ b/src/test-e2e/user.test.js @@ -12,7 +12,11 @@ const config = process.env.DEBUG_E2E headless: false, slowMo: 100, } - : {}; + : { + // This is needed, otherwise tests fail in CircleCI + // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }; // Before every test suite set up a new browser and page beforeAll(async () => { From 4eeefcd48cd7a4c09d8257b4ce40da6feebd9b94 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 23 Feb 2018 14:48:18 +0100 Subject: [PATCH 10/57] Share puppeteer config --- src/test-e2e/channel.test.js | 13 +------------ src/test-e2e/community.test.js | 13 +------------ src/test-e2e/inbox.test.js | 13 +------------ src/test-e2e/login.test.js | 13 +------------ src/test-e2e/splash.test.js | 13 +------------ src/test-e2e/thread.test.js | 13 +------------ src/test-e2e/user.test.js | 13 +------------ src/test-e2e/utils.js | 13 +++++++++++++ 8 files changed, 20 insertions(+), 84 deletions(-) create mode 100644 src/test-e2e/utils.js diff --git a/src/test-e2e/channel.test.js b/src/test-e2e/channel.test.js index 456c2a20ad..06c14850ec 100644 --- a/src/test-e2e/channel.test.js +++ b/src/test-e2e/channel.test.js @@ -1,6 +1,7 @@ // @flow import puppeteer from 'puppeteer'; import data from '../../shared/testing/data'; +import { config } from './utils'; let browser; let page; @@ -9,18 +10,6 @@ const community = data.communities.find( community => 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, - } - : { - // This is needed, otherwise tests fail in CircleCI - // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 - args: ['--no-sandbox', '--disable-setuid-sandbox'], - }; - // Before every test suite set up a new browser and page beforeAll(async () => { browser = await puppeteer.launch(config); diff --git a/src/test-e2e/community.test.js b/src/test-e2e/community.test.js index a3bc8a3229..d7b58d35c7 100644 --- a/src/test-e2e/community.test.js +++ b/src/test-e2e/community.test.js @@ -1,23 +1,12 @@ // @flow import puppeteer from 'puppeteer'; import data from '../../shared/testing/data'; +import { config } from './utils'; 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, - } - : { - // This is needed, otherwise tests fail in CircleCI - // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 - args: ['--no-sandbox', '--disable-setuid-sandbox'], - }; - // Before every test suite set up a new browser and page beforeAll(async () => { browser = await puppeteer.launch(config); diff --git a/src/test-e2e/inbox.test.js b/src/test-e2e/inbox.test.js index 3283c33622..722f9e8956 100644 --- a/src/test-e2e/inbox.test.js +++ b/src/test-e2e/inbox.test.js @@ -2,6 +2,7 @@ import puppeteer from 'puppeteer'; import { encode } from 'iris/utils/base64'; import data from '../../shared/testing/data'; +import { config } from './utils'; let browser; let page; @@ -13,18 +14,6 @@ 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, - } - : { - // This is needed, otherwise tests fail in CircleCI - // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 - args: ['--no-sandbox', '--disable-setuid-sandbox'], - }; - // Before every test suite set up a new browser and page beforeAll(async () => { browser = await puppeteer.launch(config); diff --git a/src/test-e2e/login.test.js b/src/test-e2e/login.test.js index 61e9af1b55..8b072c422a 100644 --- a/src/test-e2e/login.test.js +++ b/src/test-e2e/login.test.js @@ -1,21 +1,10 @@ // @flow import puppeteer from 'puppeteer'; +import { config } from './utils'; 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, - } - : { - // This is needed, otherwise tests fail in CircleCI - // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 - args: ['--no-sandbox', '--disable-setuid-sandbox'], - }; - // Before every test suite set up a new browser and page beforeAll(async () => { browser = await puppeteer.launch(config); diff --git a/src/test-e2e/splash.test.js b/src/test-e2e/splash.test.js index 51b50d4684..928048394c 100644 --- a/src/test-e2e/splash.test.js +++ b/src/test-e2e/splash.test.js @@ -1,21 +1,10 @@ // @flow import puppeteer from 'puppeteer'; +import { config } from './utils'; 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, - } - : { - // This is needed, otherwise tests fail in CircleCI - // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 - args: ['--no-sandbox', '--disable-setuid-sandbox'], - }; - // Before every test suite set up a new browser and page beforeAll(async () => { browser = await puppeteer.launch(config); diff --git a/src/test-e2e/thread.test.js b/src/test-e2e/thread.test.js index 9c8ee2d57f..0f272bcc60 100644 --- a/src/test-e2e/thread.test.js +++ b/src/test-e2e/thread.test.js @@ -2,6 +2,7 @@ import puppeteer from 'puppeteer'; import { toPlainText, toState } from '../../shared/draft-utils'; import data from '../../shared/testing/data'; +import { config } from './utils'; let browser; let page; @@ -15,18 +16,6 @@ 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, - } - : { - // This is needed, otherwise tests fail in CircleCI - // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 - args: ['--no-sandbox', '--disable-setuid-sandbox'], - }; - // Before every test suite set up a new browser and page beforeAll(async () => { browser = await puppeteer.launch(config); diff --git a/src/test-e2e/user.test.js b/src/test-e2e/user.test.js index adb3f02389..1582237ecf 100644 --- a/src/test-e2e/user.test.js +++ b/src/test-e2e/user.test.js @@ -1,23 +1,12 @@ // @flow import puppeteer from 'puppeteer'; import data from '../../shared/testing/data'; +import { config } from './utils'; 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, - } - : { - // This is needed, otherwise tests fail in CircleCI - // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 - args: ['--no-sandbox', '--disable-setuid-sandbox'], - }; - // Before every test suite set up a new browser and page beforeAll(async () => { browser = await puppeteer.launch(config); diff --git a/src/test-e2e/utils.js b/src/test-e2e/utils.js new file mode 100644 index 0000000000..810b21aa44 --- /dev/null +++ b/src/test-e2e/utils.js @@ -0,0 +1,13 @@ +// @flow + +// If DEBUG_E2E is set show a browser and run test in slow mo +export const config = process.env.DEBUG_E2E + ? { + headless: false, + slowMo: 100, + } + : { + // This is needed, otherwise tests fail in CircleCI + // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }; From 883bfab4172393a5d03010052d0358242deec80f Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Wed, 28 Feb 2018 15:33:09 +0100 Subject: [PATCH 11/57] Add comment --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 67b8dcb728..5464389a24 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -147,6 +147,7 @@ workflows: # requires: # - checkout_environment + # Tests web app test_web: jobs: - checkout_environment From 8cb04f8165af8f066a78f3c06322514bb69c8a95 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Tue, 13 Mar 2018 17:32:47 -0700 Subject: [PATCH 12/57] Update upload client and server versions --- iris/package.json | 4 +- iris/routes/middlewares/index.js | 6 ++- iris/types/Message.js | 2 +- iris/types/scalars.js | 3 ++ package.json | 4 +- yarn.lock | 70 ++++++++++++++++++++------------ 6 files changed, 56 insertions(+), 33 deletions(-) diff --git a/iris/package.json b/iris/package.json index 0aa08ec1bc..471cd40b39 100644 --- a/iris/package.json +++ b/iris/package.json @@ -3,8 +3,8 @@ "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", "axios": "^0.16.2", "babel-plugin-replace-dynamic-import-runtime": "^1.0.2", "babel-plugin-styled-components": "^1.1.7", 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/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/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/package.json b/package.json index 67e55256a4..f2f1fe8276 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,8 @@ "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", "axios": "^0.17.1", "bad-words": "^1.6.1", diff --git a/yarn.lock b/yarn.lock index 590df70402..a3aec1bc5d 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: @@ -457,20 +450,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: @@ -1929,6 +1922,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" @@ -3077,6 +3077,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" @@ -4089,11 +4096,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" @@ -4392,10 +4399,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" @@ -9242,6 +9245,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" @@ -10310,6 +10322,10 @@ 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" From 23fb621358b067d2cf5895453ac576e458248b86 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Tue, 13 Mar 2018 19:43:12 -0700 Subject: [PATCH 13/57] Add and upgrade packages to help with uploading --- flow-typed/npm/mkdirp_vx.x.x.js | 150 +++++++++++++++++++++++++++++++ flow-typed/npm/shortid_vx.x.x.js | 108 ++++++++++++++++++++++ package.json | 2 + yarn.lock | 4 + 4 files changed, 264 insertions(+) create mode 100644 flow-typed/npm/mkdirp_vx.x.x.js create mode 100644 flow-typed/npm/shortid_vx.x.x.js diff --git a/flow-typed/npm/mkdirp_vx.x.x.js b/flow-typed/npm/mkdirp_vx.x.x.js new file mode 100644 index 0000000000..2ed53ff884 --- /dev/null +++ b/flow-typed/npm/mkdirp_vx.x.x.js @@ -0,0 +1,150 @@ +// flow-typed signature: e535b00b54b4d3492d703e6cabff8284 +// flow-typed version: <>/mkdirp_v0.5.1/flow_v0.66.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'mkdirp' + * + * 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 'mkdirp' { + 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 'mkdirp/bin/cmd' { + declare module.exports: any; +} + +declare module 'mkdirp/examples/pow' { + declare module.exports: any; +} + +declare module 'mkdirp/test/chmod' { + declare module.exports: any; +} + +declare module 'mkdirp/test/clobber' { + declare module.exports: any; +} + +declare module 'mkdirp/test/mkdirp' { + declare module.exports: any; +} + +declare module 'mkdirp/test/opts_fs_sync' { + declare module.exports: any; +} + +declare module 'mkdirp/test/opts_fs' { + declare module.exports: any; +} + +declare module 'mkdirp/test/perm_sync' { + declare module.exports: any; +} + +declare module 'mkdirp/test/perm' { + declare module.exports: any; +} + +declare module 'mkdirp/test/race' { + declare module.exports: any; +} + +declare module 'mkdirp/test/rel' { + declare module.exports: any; +} + +declare module 'mkdirp/test/return_sync' { + declare module.exports: any; +} + +declare module 'mkdirp/test/return' { + declare module.exports: any; +} + +declare module 'mkdirp/test/root' { + declare module.exports: any; +} + +declare module 'mkdirp/test/sync' { + declare module.exports: any; +} + +declare module 'mkdirp/test/umask_sync' { + declare module.exports: any; +} + +declare module 'mkdirp/test/umask' { + declare module.exports: any; +} + +// Filename aliases +declare module 'mkdirp/bin/cmd.js' { + declare module.exports: $Exports<'mkdirp/bin/cmd'>; +} +declare module 'mkdirp/examples/pow.js' { + declare module.exports: $Exports<'mkdirp/examples/pow'>; +} +declare module 'mkdirp/index' { + declare module.exports: $Exports<'mkdirp'>; +} +declare module 'mkdirp/index.js' { + declare module.exports: $Exports<'mkdirp'>; +} +declare module 'mkdirp/test/chmod.js' { + declare module.exports: $Exports<'mkdirp/test/chmod'>; +} +declare module 'mkdirp/test/clobber.js' { + declare module.exports: $Exports<'mkdirp/test/clobber'>; +} +declare module 'mkdirp/test/mkdirp.js' { + declare module.exports: $Exports<'mkdirp/test/mkdirp'>; +} +declare module 'mkdirp/test/opts_fs_sync.js' { + declare module.exports: $Exports<'mkdirp/test/opts_fs_sync'>; +} +declare module 'mkdirp/test/opts_fs.js' { + declare module.exports: $Exports<'mkdirp/test/opts_fs'>; +} +declare module 'mkdirp/test/perm_sync.js' { + declare module.exports: $Exports<'mkdirp/test/perm_sync'>; +} +declare module 'mkdirp/test/perm.js' { + declare module.exports: $Exports<'mkdirp/test/perm'>; +} +declare module 'mkdirp/test/race.js' { + declare module.exports: $Exports<'mkdirp/test/race'>; +} +declare module 'mkdirp/test/rel.js' { + declare module.exports: $Exports<'mkdirp/test/rel'>; +} +declare module 'mkdirp/test/return_sync.js' { + declare module.exports: $Exports<'mkdirp/test/return_sync'>; +} +declare module 'mkdirp/test/return.js' { + declare module.exports: $Exports<'mkdirp/test/return'>; +} +declare module 'mkdirp/test/root.js' { + declare module.exports: $Exports<'mkdirp/test/root'>; +} +declare module 'mkdirp/test/sync.js' { + declare module.exports: $Exports<'mkdirp/test/sync'>; +} +declare module 'mkdirp/test/umask_sync.js' { + declare module.exports: $Exports<'mkdirp/test/umask_sync'>; +} +declare module 'mkdirp/test/umask.js' { + declare module.exports: $Exports<'mkdirp/test/umask'>; +} 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/package.json b/package.json index f2f1fe8276..58eee7d2ce 100644 --- a/package.json +++ b/package.json @@ -109,6 +109,7 @@ "linkify-it": "^2.0.3", "lodash": "^4.17.4", "lodash.intersection": "^4.4.0", + "mkdirp": "^0.5.1", "moment": "^2.18.1", "node-env-file": "^0.1.8", "now-env": "^3.0.1", @@ -155,6 +156,7 @@ "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/yarn.lock b/yarn.lock index a3aec1bc5d..cc97f51c0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10007,6 +10007,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" From 959be59806bdbedb1aeac97b0751b33b42a2b242 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Tue, 13 Mar 2018 19:43:28 -0700 Subject: [PATCH 14/57] Refactor iris uploading --- iris/mutations/message/addMessage.js | 25 ++++---- iris/utils/s3.js | 86 ++++++++++++++++++++-------- shared/types.js | 7 +++ 3 files changed, 83 insertions(+), 35 deletions(-) diff --git a/iris/mutations/message/addMessage.js b/iris/mutations/message/addMessage.js index 2e5c0b1d34..d7ffd85129 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, diff --git a/iris/utils/s3.js b/iris/utils/s3.js index d4e4b70001..d8980ff0df 100644 --- a/iris/utils/s3.js +++ b/iris/utils/s3.js @@ -1,7 +1,14 @@ +// @flow require('now-env'); const Uploader = require('s3-image-uploader'); +import mkdirp from 'mkdirp'; +import shortid from 'shortid'; +import fs from 'fs'; 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; @@ -20,52 +27,81 @@ const uploader = new Uploader({ websockets: false, }); +const uploadDir = './uploads'; +mkdirp.sync(uploadDir); + +const removeFS = path => { + return fs.unlinkSync(path); +}; + +const storeFS = ({ stream, filename }) => { + const path = `${uploadDir}/${filename}`; + return new Promise((resolve, reject) => + stream + .on('error', error => { + if (stream.truncated) + // Delete the truncated file + fs.unlinkSync(path); + reject(error); + }) + .on('end', () => resolve({ path })) + .pipe(fs.createWriteStream(path)) + ); +}; + +const getPath = async file => { + const { stream, filename } = await file; + const name = `${filename}-${shortid.generate()}`; + const { path } = await storeFS({ stream, filename: name }); + return { path, name }; +}; + 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; }; -/* - 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); +const upload = (file: FileUpload, entity: EntityTypes, id: string) => + new Promise(async (resolve, reject) => { + const { path, name } = await getPath(file); - return uploader.upload( + return await uploader.upload( { - fileId: fileName, + fileId: name, bucket: `spectrum-chat/${entity}/${id}`, - source: file.path, - name: fileName, + source: path, + name: name, }, data => { const url = generateImageUrl(data.path); - return resolve(encodeURI(url)); + return resolve({ + path: path, + url: encodeURI(url), + }); }, (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); + return reject(errMsg); } ); }); -module.exports = { - uploadImage, +export const uploadImage = async ( + file: FileUpload, + entity: EntityTypes, + id: string +) => { + return await upload(file, entity, id) + .then(({ url, path }) => { + removeFS(path); + return url; + }) + .catch(err => err); }; diff --git a/shared/types.js b/shared/types.js index 655b03ff99..c53536f6da 100644 --- a/shared/types.js +++ b/shared/types.js @@ -359,3 +359,10 @@ export type DBExpoPushSubscription = { token: string, userId: string, }; + +export type FileUpload = { + filename: string, + mimetype: string, + encoding: string, + stream: any, +}; From 43aa1471c7911991472f58d447d69aa14e1ce405 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Tue, 13 Mar 2018 19:43:45 -0700 Subject: [PATCH 15/57] Tweaks in sendMessage mutation --- shared/graphql/mutations/message/sendMessage.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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; } ); From 7232561c688f335ca422156c5066e26028befb41 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Tue, 13 Mar 2018 19:43:55 -0700 Subject: [PATCH 16/57] Refactored media upload component --- src/components/chatInput/index.js | 52 ++++++++++--------------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/src/components/chatInput/index.js b/src/components/chatInput/index.js index fc7de3d631..3391d5dd86 100644 --- a/src/components/chatInput/index.js +++ b/src/components/chatInput/index.js @@ -24,13 +24,7 @@ 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, @@ -297,10 +291,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,32 +328,6 @@ 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: '', - }); - reader.onloadend = () => { if (forceScrollToBottom) { forceScrollToBottom(); @@ -450,6 +418,12 @@ class ChatInput extends React.Component { this.setState({ photoSizeError: '' }); }; + setMediaMessageError = (error: string) => { + return this.setState({ + photoSizeError: error, + }); + }; + render() { const { state, @@ -485,7 +459,13 @@ class ChatInput extends React.Component { /> )} - {currentUser && } + {currentUser && ( + + )} Date: Tue, 13 Mar 2018 19:44:10 -0700 Subject: [PATCH 17/57] Refactor frontend media upload component --- .../chatInput/components/mediaUploader.js | 103 ++++++++++++++++++ src/components/chatInput/components/style.js | 27 +++++ src/components/chatInput/style.js | 8 +- 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/components/chatInput/components/mediaUploader.js create mode 100644 src/components/chatInput/components/style.js diff --git a/src/components/chatInput/components/mediaUploader.js b/src/components/chatInput/components/mediaUploader.js new file mode 100644 index 0000000000..a88d4003a0 --- /dev/null +++ b/src/components/chatInput/components/mediaUploader.js @@ -0,0 +1,103 @@ +// @flow +import * as React from 'react'; +import { MediaLabel, MediaInput } from './style'; +import Icon from 'src/components/icons'; +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, +}; + +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() { + return ( +
e.preventDefault()} ref={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..05120fb922 --- /dev/null +++ b/src/components/chatInput/components/style.js @@ -0,0 +1,27 @@ +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}; + } +`; diff --git a/src/components/chatInput/style.js b/src/components/chatInput/style.js index eadb9ed6df..cc05c3b0ad 100644 --- a/src/components/chatInput/style.js +++ b/src/components/chatInput/style.js @@ -1,7 +1,13 @@ // @flow import styled, { css } from 'styled-components'; import { IconButton } from '../buttons'; -import { FlexRow, hexa, Transition, zIndex, monoStack } from '../globals'; +import { + FlexRow, + hexa, + Transition, + zIndex, + monoStack, +} from 'src/components/globals'; import { Wrapper as EditorWrapper } from '../draftjs-editor/style'; export const ChatInputWrapper = styled(FlexRow)` From 4e495b283945faba95855bfc8c6eab9213a6da71 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Tue, 13 Mar 2018 19:50:17 -0700 Subject: [PATCH 18/57] Swap file types to upload types --- iris/types/Community.js | 8 ++++---- iris/types/DirectMessageThread.js | 2 +- iris/types/Thread.js | 4 ++-- iris/types/User.js | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) 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/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 From d163481eb6ec886a6421a0ce726857cf2b7d52a5 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Tue, 13 Mar 2018 19:57:48 -0700 Subject: [PATCH 19/57] Fix image uploading everywhere else --- iris/models/thread.js | 9 ++------- iris/models/user.js | 6 +++--- .../createDirectMessageThread.js | 16 ++++++---------- iris/mutations/thread/publishThread.js | 12 +++++------- 4 files changed, 16 insertions(+), 27 deletions(-) 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..4091d47fba 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, @@ -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/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/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 From d01b007577dfb8ce072442ecb1611e75bfd341e7 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Thu, 15 Mar 2018 15:11:14 +0100 Subject: [PATCH 20/57] Switch to using the AWS SDK directly for image uploads The s3-image-uploader module doesn't do what we need it to as it can't handle file streams. The AWS SDK can handle them though, so this patch switches us to using that directly. Also removes all that fs code. /cc @brianlovin --- flow-typed/npm/mkdirp_vx.x.x.js | 150 ---------- flow-typed/npm/s3-image-uploader_vx.x.x.js | 33 --- iris/package.json | 3 +- iris/utils/s3.js | 102 +++---- iris/yarn.lock | 313 +++++++++------------ package.json | 3 +- yarn.lock | 158 +++-------- 7 files changed, 208 insertions(+), 554 deletions(-) delete mode 100644 flow-typed/npm/mkdirp_vx.x.x.js delete mode 100644 flow-typed/npm/s3-image-uploader_vx.x.x.js diff --git a/flow-typed/npm/mkdirp_vx.x.x.js b/flow-typed/npm/mkdirp_vx.x.x.js deleted file mode 100644 index 2ed53ff884..0000000000 --- a/flow-typed/npm/mkdirp_vx.x.x.js +++ /dev/null @@ -1,150 +0,0 @@ -// flow-typed signature: e535b00b54b4d3492d703e6cabff8284 -// flow-typed version: <>/mkdirp_v0.5.1/flow_v0.66.0 - -/** - * This is an autogenerated libdef stub for: - * - * 'mkdirp' - * - * 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 'mkdirp' { - 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 'mkdirp/bin/cmd' { - declare module.exports: any; -} - -declare module 'mkdirp/examples/pow' { - declare module.exports: any; -} - -declare module 'mkdirp/test/chmod' { - declare module.exports: any; -} - -declare module 'mkdirp/test/clobber' { - declare module.exports: any; -} - -declare module 'mkdirp/test/mkdirp' { - declare module.exports: any; -} - -declare module 'mkdirp/test/opts_fs_sync' { - declare module.exports: any; -} - -declare module 'mkdirp/test/opts_fs' { - declare module.exports: any; -} - -declare module 'mkdirp/test/perm_sync' { - declare module.exports: any; -} - -declare module 'mkdirp/test/perm' { - declare module.exports: any; -} - -declare module 'mkdirp/test/race' { - declare module.exports: any; -} - -declare module 'mkdirp/test/rel' { - declare module.exports: any; -} - -declare module 'mkdirp/test/return_sync' { - declare module.exports: any; -} - -declare module 'mkdirp/test/return' { - declare module.exports: any; -} - -declare module 'mkdirp/test/root' { - declare module.exports: any; -} - -declare module 'mkdirp/test/sync' { - declare module.exports: any; -} - -declare module 'mkdirp/test/umask_sync' { - declare module.exports: any; -} - -declare module 'mkdirp/test/umask' { - declare module.exports: any; -} - -// Filename aliases -declare module 'mkdirp/bin/cmd.js' { - declare module.exports: $Exports<'mkdirp/bin/cmd'>; -} -declare module 'mkdirp/examples/pow.js' { - declare module.exports: $Exports<'mkdirp/examples/pow'>; -} -declare module 'mkdirp/index' { - declare module.exports: $Exports<'mkdirp'>; -} -declare module 'mkdirp/index.js' { - declare module.exports: $Exports<'mkdirp'>; -} -declare module 'mkdirp/test/chmod.js' { - declare module.exports: $Exports<'mkdirp/test/chmod'>; -} -declare module 'mkdirp/test/clobber.js' { - declare module.exports: $Exports<'mkdirp/test/clobber'>; -} -declare module 'mkdirp/test/mkdirp.js' { - declare module.exports: $Exports<'mkdirp/test/mkdirp'>; -} -declare module 'mkdirp/test/opts_fs_sync.js' { - declare module.exports: $Exports<'mkdirp/test/opts_fs_sync'>; -} -declare module 'mkdirp/test/opts_fs.js' { - declare module.exports: $Exports<'mkdirp/test/opts_fs'>; -} -declare module 'mkdirp/test/perm_sync.js' { - declare module.exports: $Exports<'mkdirp/test/perm_sync'>; -} -declare module 'mkdirp/test/perm.js' { - declare module.exports: $Exports<'mkdirp/test/perm'>; -} -declare module 'mkdirp/test/race.js' { - declare module.exports: $Exports<'mkdirp/test/race'>; -} -declare module 'mkdirp/test/rel.js' { - declare module.exports: $Exports<'mkdirp/test/rel'>; -} -declare module 'mkdirp/test/return_sync.js' { - declare module.exports: $Exports<'mkdirp/test/return_sync'>; -} -declare module 'mkdirp/test/return.js' { - declare module.exports: $Exports<'mkdirp/test/return'>; -} -declare module 'mkdirp/test/root.js' { - declare module.exports: $Exports<'mkdirp/test/root'>; -} -declare module 'mkdirp/test/sync.js' { - declare module.exports: $Exports<'mkdirp/test/sync'>; -} -declare module 'mkdirp/test/umask_sync.js' { - declare module.exports: $Exports<'mkdirp/test/umask_sync'>; -} -declare module 'mkdirp/test/umask.js' { - declare module.exports: $Exports<'mkdirp/test/umask'>; -} 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/iris/package.json b/iris/package.json index 471cd40b39..6275c9ab73 100644 --- a/iris/package.json +++ b/iris/package.json @@ -5,6 +5,7 @@ "apollo-local-query": "^0.3.0", "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/utils/s3.js b/iris/utils/s3.js index d8980ff0df..2bab82629f 100644 --- a/iris/utils/s3.js +++ b/iris/utils/s3.js @@ -1,9 +1,7 @@ // @flow require('now-env'); -const Uploader = require('s3-image-uploader'); -import mkdirp from 'mkdirp'; +import AWS from 'aws-sdk'; import shortid from 'shortid'; -import fs from 'fs'; const IS_PROD = process.env.NODE_ENV === 'production'; import type { FileUpload } from 'shared/types'; @@ -13,95 +11,63 @@ 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 uploadDir = './uploads'; -mkdirp.sync(uploadDir); - -const removeFS = path => { - return fs.unlinkSync(path); -}; - -const storeFS = ({ stream, filename }) => { - const path = `${uploadDir}/${filename}`; - return new Promise((resolve, reject) => - stream - .on('error', error => { - if (stream.truncated) - // Delete the truncated file - fs.unlinkSync(path); - reject(error); - }) - .on('end', () => resolve({ path })) - .pipe(fs.createWriteStream(path)) - ); -}; - -const getPath = async file => { - const { stream, filename } = await file; - const name = `${filename}-${shortid.generate()}`; - const { path } = await storeFS({ stream, filename: name }); - return { path, name }; -}; - -const generateImageUrl = path => { +const generateImageUrl = (path, name) => { // remove the bucket name from the path - const 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 const imgixBase = 'https://spectrum.imgix.net'; // return a new url to update the user object - return imgixBase + newPath; + return imgixBase + '/' + newPath + '/' + name; }; -const upload = (file: FileUpload, entity: EntityTypes, id: string) => - new Promise(async (resolve, reject) => { - const { path, name } = await getPath(file); - - return await 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: name, - bucket: `spectrum-chat/${entity}/${id}`, - source: path, - name: name, + Bucket: path, + Key: fileKey, + Body: stream, + ACL: 'public-read', }, - data => { - const url = generateImageUrl(data.path); - return resolve({ - path: path, - url: encodeURI(url), - }); - }, - (errMsg, errObject) => { - console.error('unable to upload: ' + errMsg + ':', errObject); - return reject(errMsg); + (err, data) => { + if (err) throw new Error(err); + const url = generateImageUrl(data.Bucket, data.Key); + res(encodeURI(url)); } ); }); +}; export const uploadImage = async ( file: FileUpload, entity: EntityTypes, id: string -) => { - return await upload(file, entity, id) - .then(({ url, path }) => { - removeFS(path); - return url; - }) - .catch(err => err); +): 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/package.json b/package.json index 58eee7d2ce..9127b90620 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "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", @@ -109,7 +110,6 @@ "linkify-it": "^2.0.3", "lodash": "^4.17.4", "lodash.intersection": "^4.4.0", - "mkdirp": "^0.5.1", "moment": "^2.18.1", "node-env-file": "^0.1.8", "now-env": "^3.0.1", @@ -153,7 +153,6 @@ "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", diff --git a/yarn.lock b/yarn.lock index cc97f51c0c..1b08214121 100644 --- a/yarn.lock +++ b/yarn.lock @@ -573,18 +573,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" @@ -723,12 +715,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" @@ -1883,7 +1882,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: @@ -2359,10 +2358,6 @@ commander@2.14.x, commander@^2.11.0, commander@^2.13.0, commander@^2.9.0, comman 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.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.7.2.tgz#24d9768c63d253a56ecff93845b44b4df1d52771" @@ -2877,10 +2872,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" @@ -3873,7 +3864,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" @@ -4188,12 +4179,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" @@ -4314,10 +4299,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" @@ -4678,16 +4659,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" @@ -6540,6 +6511,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" @@ -7173,7 +7148,7 @@ 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@>=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" @@ -7461,10 +7436,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" @@ -7567,10 +7538,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" @@ -7950,10 +7917,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" @@ -8280,10 +8243,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" @@ -9691,10 +9650,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" @@ -9724,28 +9679,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" @@ -9782,11 +9715,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" @@ -10318,10 +10251,6 @@ 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" @@ -10698,10 +10627,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" @@ -11052,6 +10977,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" @@ -11108,6 +11040,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" @@ -11533,15 +11469,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" @@ -11558,15 +11485,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" From ada7152ee915fbd50fa3f927b3f49163a37a6094 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Thu, 15 Mar 2018 15:16:49 +0100 Subject: [PATCH 21/57] Fixies --- iris/utils/s3.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/iris/utils/s3.js b/iris/utils/s3.js index 2bab82629f..64cd13932f 100644 --- a/iris/utils/s3.js +++ b/iris/utils/s3.js @@ -24,16 +24,16 @@ AWS.config.update({ }); const s3 = new AWS.S3(); -const generateImageUrl = (path, name) => { +const generateImageUrl = path => { // remove the bucket name from the path - const 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 const imgixBase = 'https://spectrum.imgix.net'; // return a new url to update the user object - return imgixBase + '/' + newPath + '/' + name; + return imgixBase + '/' + newPath; }; const upload = async ( @@ -55,7 +55,8 @@ const upload = async ( }, (err, data) => { if (err) throw new Error(err); - const url = generateImageUrl(data.Bucket, data.Key); + if (!data || !data.Key) throw new Error('Image upload failed.'); + const url = generateImageUrl(data.Key); res(encodeURI(url)); } ); From 990c3dbfa68da0d55d42083cc2fc81791d9ee70a Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Thu, 15 Mar 2018 15:18:25 +0100 Subject: [PATCH 22/57] Add libdef for aws-sdk --- flow-typed/npm/aws-sdk_vx.x.x.js | 1809 ++++++++++++++++++++++++++++++ 1 file changed, 1809 insertions(+) create mode 100644 flow-typed/npm/aws-sdk_vx.x.x.js 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'>; +} From 7cbc91470b0a5b437dac4d293475ce23d99eff28 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Thu, 15 Mar 2018 13:07:10 -0700 Subject: [PATCH 23/57] Dont show optimistic media messages, show spinner while uploading --- .../chatInput/components/mediaUploader.js | 18 +++++++++-- src/components/chatInput/components/style.js | 6 ++++ src/components/chatInput/index.js | 31 +++++++++++++++++-- src/components/message/index.js | 2 ++ src/components/message/view.js | 18 +++++++++-- 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/components/chatInput/components/mediaUploader.js b/src/components/chatInput/components/mediaUploader.js index a88d4003a0..ae958285b1 100644 --- a/src/components/chatInput/components/mediaUploader.js +++ b/src/components/chatInput/components/mediaUploader.js @@ -1,7 +1,8 @@ // @flow import * as React from 'react'; -import { MediaLabel, MediaInput } from './style'; +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, @@ -13,6 +14,7 @@ type Props = { onValidated: Function, onError: Function, currentUser: ?Object, + isSendingMediaMessage: boolean, }; class MediaUploader extends React.Component { @@ -80,8 +82,18 @@ class MediaUploader extends React.Component { } render() { + const { isSendingMediaMessage } = this.props; + + if (isSendingMediaMessage) { + return ( + + + + ); + } + return ( -
e.preventDefault()} ref={c => (this.form = c)}> + e.preventDefault()} innerRef={c => (this.form = c)}> { tipText="Upload photo" /> -
+ ); } } diff --git a/src/components/chatInput/components/style.js b/src/components/chatInput/components/style.js index 05120fb922..e82d2ec35e 100644 --- a/src/components/chatInput/components/style.js +++ b/src/components/chatInput/components/style.js @@ -25,3 +25,9 @@ export const MediaLabel = styled.label` 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 3391d5dd86..f438be769b 100644 --- a/src/components/chatInput/index.js +++ b/src/components/chatInput/index.js @@ -30,6 +30,7 @@ type State = { isFocused: boolean, photoSizeError: string, code: boolean, + isSendingMediaMessage: boolean, }; type Props = { @@ -76,6 +77,7 @@ class ChatInput extends React.Component { isFocused: false, photoSizeError: '', code: false, + isSendingMediaMessage: false, }; editor: any; @@ -84,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; @@ -95,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; } @@ -328,6 +333,10 @@ class ChatInput extends React.Component { ); } + this.setState({ + isSendingMediaMessage: true, + }); + reader.onloadend = () => { if (forceScrollToBottom) { forceScrollToBottom(); @@ -351,6 +360,9 @@ class ChatInput extends React.Component { file, }) .then(() => { + this.setState({ + isSendingMediaMessage: false, + }); return track( `${threadType} message`, 'media message created', @@ -358,6 +370,9 @@ class ChatInput extends React.Component { ); }) .catch(err => { + this.setState({ + isSendingMediaMessage: false, + }); dispatch(addToastWithTimeout('error', err.message)); }); } else { @@ -371,6 +386,9 @@ class ChatInput extends React.Component { file, }) .then(() => { + this.setState({ + isSendingMediaMessage: false, + }); return track( `${threadType} message`, 'media message created', @@ -378,6 +396,9 @@ class ChatInput extends React.Component { ); }) .catch(err => { + this.setState({ + isSendingMediaMessage: false, + }); dispatch(addToastWithTimeout('error', err.message)); }); } @@ -431,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 || @@ -461,6 +487,7 @@ class ChatInput extends React.Component { )} {currentUser && ( 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} From d7d74b823e1b2a01744f8a122a0c0572da278ed6 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 16 Mar 2018 10:39:53 +0100 Subject: [PATCH 24/57] Add eslint running --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5464389a24..cb50286370 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -100,6 +100,9 @@ jobs: - run: name: Run Flow command: yarn run flow + - run: + name: Run ESLint + command: yarn run lint - run: name: Run Tests command: yarn run test:ci From 11f02b160a48901d755ae59361b6ab1303f885ca Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 16 Mar 2018 11:55:37 +0100 Subject: [PATCH 25/57] Move e2e tests to cypress cypress.io is a tool that promises way more stable e2e tests. Ours have been very very flakey in CI, so I'm hoping this resolves it. It also comes with a super nice GUI, which is a big bonus. This moves all our existing e2e tests over to Cypress. --- cypress.json | 4 + cypress/fixtures/example.json | 5 + cypress/integration/channel_spec.js | 24 +++ cypress/integration/community_spec.js | 33 ++++ cypress/integration/inbox_spec.js | 40 +++++ cypress/integration/login_spec.js | 15 ++ cypress/integration/splash_spec.js | 11 ++ cypress/integration/thread_spec.js | 36 ++++ cypress/integration/user_spec.js | 40 +++++ cypress/plugins/index.js | 17 ++ cypress/support/commands.js | 25 +++ cypress/support/index.js | 23 +++ jest.config.js | 5 +- package.json | 2 + src/test-e2e/channel.test.js | 42 ----- src/test-e2e/community.test.js | 53 ------ src/test-e2e/inbox.test.js | 67 -------- src/test-e2e/login.test.js | 41 ----- src/test-e2e/splash.test.js | 35 ---- src/test-e2e/thread.test.js | 73 -------- src/test-e2e/user.test.js | 59 ------- src/test-e2e/utils.js | 13 -- yarn.lock | 229 +++++++++++++++++++++++++- 23 files changed, 496 insertions(+), 396 deletions(-) create mode 100644 cypress.json create mode 100644 cypress/fixtures/example.json create mode 100644 cypress/integration/channel_spec.js create mode 100644 cypress/integration/community_spec.js create mode 100644 cypress/integration/inbox_spec.js create mode 100644 cypress/integration/login_spec.js create mode 100644 cypress/integration/splash_spec.js create mode 100644 cypress/integration/thread_spec.js create mode 100644 cypress/integration/user_spec.js create mode 100644 cypress/plugins/index.js create mode 100644 cypress/support/commands.js create mode 100644 cypress/support/index.js delete mode 100644 src/test-e2e/channel.test.js delete mode 100644 src/test-e2e/community.test.js delete mode 100644 src/test-e2e/inbox.test.js delete mode 100644 src/test-e2e/login.test.js delete mode 100644 src/test-e2e/splash.test.js delete mode 100644 src/test-e2e/thread.test.js delete mode 100644 src/test-e2e/user.test.js delete mode 100644 src/test-e2e/utils.js 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..c5ff547210 --- /dev/null +++ b/cypress/integration/inbox_spec.js @@ -0,0 +1,40 @@ +import { encode } from '../../iris/utils/base64'; +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.setCookie( + 'session', + encode(JSON.stringify({ passport: { user: user.id } })), + { + httpOnly: true, + secure: false, + } + ); + 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..c1f5a772e2 --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,25 @@ +// *********************************************** +// 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 +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add("login", (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This is will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 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/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 6ee653f0b7..286f62d371 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "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", @@ -224,6 +225,7 @@ "build:web": "cross-env NODE_PATH=./ react-app-rewired build", "jest": "cross-env NODE_PATH=./ jest", "test": "npm run jest -- --runInBand --watch", + "cypress:open": "cypress open", "test:ci": "npm run jest -- --forceExit --outputFile test-results.json --json", "lint": "eslint .", "flow": "flow", diff --git a/src/test-e2e/channel.test.js b/src/test-e2e/channel.test.js deleted file mode 100644 index 06c14850ec..0000000000 --- a/src/test-e2e/channel.test.js +++ /dev/null @@ -1,42 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import data from '../../shared/testing/data'; -import { config } from './utils'; - -let browser; -let page; -const channel = data.channels[0]; -const community = data.communities.find( - community => community.id === channel.communityId -); - -// 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 d7b58d35c7..0000000000 --- a/src/test-e2e/community.test.js +++ /dev/null @@ -1,53 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import data from '../../shared/testing/data'; -import { config } from './utils'; - -let browser; -let page; -const community = data.communities[0]; - -// 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 722f9e8956..0000000000 --- a/src/test-e2e/inbox.test.js +++ /dev/null @@ -1,67 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import { encode } from 'iris/utils/base64'; -import data from '../../shared/testing/data'; -import { config } from './utils'; - -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) -); - -// 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 8b072c422a..0000000000 --- a/src/test-e2e/login.test.js +++ /dev/null @@ -1,41 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import { config } from './utils'; - -let browser; -let page; - -// 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 928048394c..0000000000 --- a/src/test-e2e/splash.test.js +++ /dev/null @@ -1,35 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import { config } from './utils'; - -let browser; -let page; - -// 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 0f272bcc60..0000000000 --- a/src/test-e2e/thread.test.js +++ /dev/null @@ -1,73 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import { toPlainText, toState } from '../../shared/draft-utils'; -import data from '../../shared/testing/data'; -import { config } from './utils'; - -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 -); - -// 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 1582237ecf..0000000000 --- a/src/test-e2e/user.test.js +++ /dev/null @@ -1,59 +0,0 @@ -// @flow -import puppeteer from 'puppeteer'; -import data from '../../shared/testing/data'; -import { config } from './utils'; - -let browser; -let page; -const user = data.users[0]; - -// 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/test-e2e/utils.js b/src/test-e2e/utils.js deleted file mode 100644 index 810b21aa44..0000000000 --- a/src/test-e2e/utils.js +++ /dev/null @@ -1,13 +0,0 @@ -// @flow - -// If DEBUG_E2E is set show a browser and run test in slow mo -export const config = process.env.DEBUG_E2E - ? { - headless: false, - slowMo: 100, - } - : { - // This is needed, otherwise tests fail in CircleCI - // Ref: https://github.com/GoogleChrome/puppeteer/issues/1700 - args: ['--no-sandbox', '--disable-setuid-sandbox'], - }; diff --git a/yarn.lock b/yarn.lock index 8ea5a06764..1c86e95efd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -111,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" @@ -126,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" @@ -311,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: @@ -675,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" @@ -1859,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" @@ -2052,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" @@ -2072,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" @@ -2343,10 +2438,20 @@ 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" +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" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.7.2.tgz#24d9768c63d253a56ecff93845b44b4df1d52771" @@ -2767,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" @@ -4120,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: @@ -4448,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" @@ -4562,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" @@ -4602,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: @@ -5446,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" @@ -5572,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: @@ -6825,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" @@ -6906,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: @@ -7109,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" @@ -7166,6 +7336,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.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" @@ -7976,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" @@ -8699,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" @@ -8863,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" @@ -9439,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" @@ -10441,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" @@ -10451,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: @@ -10619,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" @@ -10649,6 +10847,12 @@ 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" @@ -11002,7 +11206,7 @@ url@0.10.3: punycode "1.3.2" querystring "0.2.0" -url@^0.11.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: @@ -11715,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" From e62758b29ed1e3143e3e5d5f7b78b66d86b8fc1a Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 16 Mar 2018 11:59:13 +0100 Subject: [PATCH 26/57] Add custom cypress.auth command --- cypress/integration/inbox_spec.js | 10 +--------- cypress/support/commands.js | 28 ++++++++++++---------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/cypress/integration/inbox_spec.js b/cypress/integration/inbox_spec.js index c5ff547210..f846e0487b 100644 --- a/cypress/integration/inbox_spec.js +++ b/cypress/integration/inbox_spec.js @@ -1,4 +1,3 @@ -import { encode } from '../../iris/utils/base64'; import data from '../../shared/testing/data'; const user = data.users[0]; @@ -11,14 +10,7 @@ const dashboardThreads = data.threads.filter(({ channelId }) => describe('Inbox View', () => { before(() => { - cy.setCookie( - 'session', - encode(JSON.stringify({ passport: { user: user.id } })), - { - httpOnly: true, - secure: false, - } - ); + cy.auth(user.id); cy.visit('/'); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index c1f5a772e2..6e80f0d417 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -7,19 +7,15 @@ // commands please read more here: // https://on.cypress.io/custom-commands // *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This is will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) +import { encode } from '../../iris/utils/base64'; + +Cypress.Commands.add('auth', userId => { + cy.setCookie( + 'session', + encode(JSON.stringify({ passport: { user: userId } })), + { + httpOnly: true, + secure: false, + } + ); +}); From 080fa77a46eb0d06446f07f0751885560bb053d5 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 16 Mar 2018 12:04:26 +0100 Subject: [PATCH 27/57] Run Cypress E2E tests on Cirlce --- .circleci/config.yml | 8 +++++++- package.json | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cb50286370..e5ab291afc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -65,6 +65,9 @@ jobs: docker: - image: circleci/node:8-browsers - image: redis:3.2.7 + - image: cypress/base:6 + environment: + TERM: xterm steps: - attach_workspace: at: ~/spectrum @@ -104,8 +107,11 @@ jobs: name: Run ESLint command: yarn run lint - run: - name: Run Tests + name: Run Unit Tests command: yarn run test:ci + - run: + name: Run E2E Tests + command: yarn run test:e2e # Tests native code of the mobile app diff --git a/package.json b/package.json index 286f62d371..4fb601ed4a 100644 --- a/package.json +++ b/package.json @@ -225,8 +225,9 @@ "build:web": "cross-env NODE_PATH=./ react-app-rewired build", "jest": "cross-env NODE_PATH=./ jest", "test": "npm run jest -- --runInBand --watch", - "cypress:open": "cypress open", "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", From c2cef996adb15caa9fea71adc1f9e422b895f8d0 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 16 Mar 2018 12:11:56 +0100 Subject: [PATCH 28/57] Add cypress folder to danger blacklist --- dangerfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dangerfile.js b/dangerfile.js index af2c15dd8d..9f9f47ebaf 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -80,6 +80,6 @@ schedule( created: 'fail', // Warn on modified untyped files modified: 'warn', - blacklist: ['flow-typed/**/*.js', 'public/**/*.js'], + blacklist: ['flow-typed/**/*.js', 'public/**/*.js', 'cypress/**/*.js'], }) ); From 393b3ae6f696de2364e3903b46f68efa6b59a02a Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Fri, 16 Mar 2018 13:46:20 +0100 Subject: [PATCH 29/57] Run Danger on Circle --- .circleci/config.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index e5ab291afc..84de65228c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -112,6 +112,10 @@ jobs: - run: name: Run E2E Tests command: yarn run test:e2e + - run: + name: Danger + when: always + command: yarn run danger ci # Tests native code of the mobile app From 97aacddfdee249d9e6b04220cd0e4dfea69c55e3 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 13:00:34 -0700 Subject: [PATCH 30/57] Add channelSettings table migration --- ...316195507-create-channel-settings-table.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 iris/migrations/20180316195507-create-channel-settings-table.js 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..1231e3cf78 --- /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.log(err); + throw err; + }); +}; + +exports.down = function(r, conn) { + return Promise.all([r.tableDrop('channelSettings').run(conn)]); +}; From 0d64268b4724d75deff91437090227557a940c2c Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 13:00:57 -0700 Subject: [PATCH 31/57] Add mutation to join private channel with a token --- iris/mutations/channel/index.js | 2 + .../mutations/channel/joinChannelWithToken.js | 84 +++++++++++++++++++ iris/types/Channel.js | 7 ++ .../mutations/channel/joinChannelWithToken.js | 41 +++++++++ shared/types.js | 1 + 5 files changed, 135 insertions(+) create mode 100644 iris/mutations/channel/joinChannelWithToken.js create mode 100644 shared/graphql/mutations/channel/joinChannelWithToken.js diff --git a/iris/mutations/channel/index.js b/iris/mutations/channel/index.js index d13bf7f48f..c933ab8e91 100644 --- a/iris/mutations/channel/index.js +++ b/iris/mutations/channel/index.js @@ -6,6 +6,7 @@ import toggleChannelSubscription from './toggleChannelSubscription'; import toggleChannelNotifications from './toggleChannelNotifications'; import togglePendingUser from './togglePendingUser'; import unblockUser from './unblockUser'; +import joinChannelWithToken from './joinChannelWithToken'; module.exports = { Mutation: { @@ -16,5 +17,6 @@ module.exports = { toggleChannelNotifications, togglePendingUser, unblockUser, + joinChannelWithToken, }, }; diff --git a/iris/mutations/channel/joinChannelWithToken.js b/iris/mutations/channel/joinChannelWithToken.js new file mode 100644 index 0000000000..30a205149a --- /dev/null +++ b/iris/mutations/channel/joinChannelWithToken.js @@ -0,0 +1,84 @@ +// @flow +import type { GraphQLContext } from '../../'; +import UserError from '../../utils/UserError'; +import { + getUserPermissionsInChannel, + createMemberInChannel, + createMemberInDefaultChannels, +} from '../../models/usersChannels'; +import { getChannelBySlug } from '../../models/channel'; +import { + getUserPermissionsInCommunity, + createMemberInCommunity, +} from '../../models/usersCommunities'; + +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'); + + 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 (channel.joinToken && token !== channel.joinToken) { + return new UserError( + "We weren't able to authenticate this request - the token may have changed" + ); + } + + if (!communityPermissions.isMember) { + return await Promise.all([ + createMemberInCommunity(channel.communityId, currentUser.id), + createMemberInDefaultChannels(channel.communityId, currentUser.id), + ]) + .then(async () => { + return await createMemberInChannel(channel.id, currentUser.id); + }) + .then(joinedChannel => joinedChannel); + } + + if (!channel.isMember) { + return await createMemberInChannel(channel.id, currentUser.id); + } + + return new UserError("Couldn't authenticate this request to join a channel"); +}; diff --git a/iris/types/Channel.js b/iris/types/Channel.js index 828cae7dfd..5a9f437a99 100644 --- a/iris/types/Channel.js +++ b/iris/types/Channel.js @@ -85,11 +85,18 @@ const Channel = /* GraphQL */ ` channel(id: ID, channelSlug: String, communitySlug: String): Channel @cost(complexity: 1) } + input JoinChannelWithTokenInput { + communitySlug: String! + channelSlug: String! + token: String! + } + 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 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/types.js b/shared/types.js index c53536f6da..f856057276 100644 --- a/shared/types.js +++ b/shared/types.js @@ -17,6 +17,7 @@ export type DBChannel = { isPrivate: boolean, name: string, slug: string, + joinToken?: string, }; export type DBCommunity = { From 2dfbf23ac50bc61ca996265318d5cfcfd47e3375 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 13:01:12 -0700 Subject: [PATCH 32/57] Add client routes to join a channel with a token --- src/routes.js | 9 ++ src/views/privateChannelJoin/index.js | 113 ++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/views/privateChannelJoin/index.js 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} /> + + { + state = { + isLoading: false, + }; + + componentDidMount() { + const { + match, + history, + joinChannelWithToken, + currentUser, + dispatch, + } = 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, + currentUser, + dispatch, + } = this.props; + const { token, communitySlug, channelSlug } = match.params; + + this.setState({ isLoading: true }); + + joinChannelWithToken({ channelSlug, token, communitySlug }) + .then(data => { + console.log('mutation dat', data); + this.setState({ isLoading: false }); + dispatch(addToastWithTimeout('success', 'Welcome!')); + return history.push(`/${communitySlug}/${channelSlug}`); + }) + .catch(err => { + this.setState({ isLoading: false }); + dispatch(addToastWithTimeout('error', err)); + return history.push(`/${communitySlug}/${channelSlug}`); + }); + }; + + render() { + const { currentUser, match } = this.props; + const { isLoading } = this.state; + + console.log(this.props); + + 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); From 2a02e717a4465c6d9ece893f1ff4301090643c36 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 13:05:55 -0700 Subject: [PATCH 33/57] Handle scenarios where user is already pending in channel --- iris/models/usersChannels.js | 4 +++- iris/mutations/channel/joinChannelWithToken.js | 13 +++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) 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/joinChannelWithToken.js b/iris/mutations/channel/joinChannelWithToken.js index 30a205149a..fc5110e075 100644 --- a/iris/mutations/channel/joinChannelWithToken.js +++ b/iris/mutations/channel/joinChannelWithToken.js @@ -5,6 +5,7 @@ import { getUserPermissionsInChannel, createMemberInChannel, createMemberInDefaultChannels, + approvePendingUserInChannel, } from '../../models/usersChannels'; import { getChannelBySlug } from '../../models/channel'; import { @@ -71,12 +72,20 @@ export default async ( createMemberInDefaultChannels(channel.communityId, currentUser.id), ]) .then(async () => { - return await createMemberInChannel(channel.id, currentUser.id); + if (channelPermissions.isPending) { + return await approvePendingUserInChannel(channel.id, currentUser.id); + } else { + return await createMemberInChannel(channel.id, currentUser.id); + } }) .then(joinedChannel => joinedChannel); } - if (!channel.isMember) { + if (channelPermissions.isPending) { + return await approvePendingUserInChannel(channel.id, currentUser.id); + } + + if (!channelPermissions.isMember) { return await createMemberInChannel(channel.id, currentUser.id); } From 5ee6051cf96c3fe0a00a74577f2cba1d43c3e72e Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 14:21:16 -0700 Subject: [PATCH 34/57] Add channelSettings loader --- iris/loaders/channel.js | 6 ++++++ iris/loaders/index.js | 2 ++ 2 files changed, 8 insertions(+) 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), From 6530b759e518f55bfff457c1150d39d1f847b600 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 14:21:27 -0700 Subject: [PATCH 35/57] Add channelSettings db models --- iris/models/channelSettings.js | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 iris/models/channelSettings.js 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)); +}; From 805f0849e54d0a934eebc22a69f257420a1aea11 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 14:21:54 -0700 Subject: [PATCH 36/57] Add typing to graphQL for new mutations and queries --- iris/types/Channel.js | 21 +++++++++++++++++++++ shared/types.js | 10 +++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/iris/types/Channel.js b/iris/types/Channel.js index 5a9f437a99..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 } @@ -91,6 +97,18 @@ const Channel = /* GraphQL */ ` 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 @@ -101,6 +119,9 @@ const Channel = /* GraphQL */ ` 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/shared/types.js b/shared/types.js index f856057276..4d841b35dd 100644 --- a/shared/types.js +++ b/shared/types.js @@ -17,7 +17,6 @@ export type DBChannel = { isPrivate: boolean, name: string, slug: string, - joinToken?: string, }; export type DBCommunity = { @@ -42,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, From c3fd0a85972f24143ae57be4cad07e6be7d64388 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 14:22:10 -0700 Subject: [PATCH 37/57] Lint fix --- src/views/privateChannelJoin/index.js | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/views/privateChannelJoin/index.js b/src/views/privateChannelJoin/index.js index fa1f21120e..7a87f8a67b 100644 --- a/src/views/privateChannelJoin/index.js +++ b/src/views/privateChannelJoin/index.js @@ -27,13 +27,7 @@ class PrivateChannelJoin extends React.Component { }; componentDidMount() { - const { - match, - history, - joinChannelWithToken, - currentUser, - dispatch, - } = this.props; + const { match, history, currentUser } = this.props; const { token, communitySlug, channelSlug } = match.params; if (!token) { @@ -56,13 +50,7 @@ class PrivateChannelJoin extends React.Component { } handleJoin = () => { - const { - match, - history, - joinChannelWithToken, - currentUser, - dispatch, - } = this.props; + const { match, history, joinChannelWithToken, dispatch } = this.props; const { token, communitySlug, channelSlug } = match.params; this.setState({ isLoading: true }); @@ -76,7 +64,7 @@ class PrivateChannelJoin extends React.Component { }) .catch(err => { this.setState({ isLoading: false }); - dispatch(addToastWithTimeout('error', err)); + dispatch(addToastWithTimeout('error', err.message)); return history.push(`/${communitySlug}/${channelSlug}`); }); }; @@ -85,8 +73,6 @@ class PrivateChannelJoin extends React.Component { const { currentUser, match } = this.props; const { isLoading } = this.state; - console.log(this.props); - if (!currentUser || !currentUser.id) { return ( Date: Fri, 16 Mar 2018 14:22:38 -0700 Subject: [PATCH 38/57] Add client side controls for token based joining in private channels --- src/components/formElements/index.js | 1 + .../components/loginTokenSettings.js | 104 ++++++++++++++++++ .../components/loginTokenToggle.js | 68 ++++++++++++ .../channelSettings/components/overview.js | 5 +- .../components/resetJoinToken.js | 64 +++++++++++ src/views/channelSettings/style.js | 32 ++++++ 6 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 src/views/channelSettings/components/loginTokenSettings.js create mode 100644 src/views/channelSettings/components/loginTokenToggle.js create mode 100644 src/views/channelSettings/components/resetJoinToken.js create mode 100644 src/views/channelSettings/style.js diff --git a/src/components/formElements/index.js b/src/components/formElements/index.js index 6b83c55619..cc797b4903 100644 --- a/src/components/formElements/index.js +++ b/src/components/formElements/index.js @@ -39,6 +39,7 @@ export const Input = (props: InputProps) => { 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/views/channelSettings/components/loginTokenSettings.js b/src/views/channelSettings/components/loginTokenSettings.js new file mode 100644 index 0000000000..38eb78b8f1 --- /dev/null +++ b/src/views/channelSettings/components/loginTokenSettings.js @@ -0,0 +1,104 @@ +// @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 BrandedLoginToggle from './loginTokenToggle'; +import ResetJoinToken from './resetJoinToken'; +import { Input } from 'src/components/formElements'; +import saveBrandedLoginSettings from 'shared/graphql/mutations/community/saveBrandedLoginSettings'; +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 BrandedLogin 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, + saveBrandedLoginSettings, + connect() +)(BrandedLogin); 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}; + } + } +`; From 2cd2075c5df9f4c8167392629ecd6fc2c07531af Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 14:22:58 -0700 Subject: [PATCH 39/57] Add mutations to enable, disable, and reset token based joining --- .../channel/disableChannelTokenJoin.js | 47 +++++++++++++++++++ .../channel/enableChannelTokenJoin.js | 47 +++++++++++++++++++ .../channel/resetChannelJoinToken.js | 47 +++++++++++++++++++ .../queries/channel/getChannelSettings.js | 41 ++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 shared/graphql/mutations/channel/disableChannelTokenJoin.js create mode 100644 shared/graphql/mutations/channel/enableChannelTokenJoin.js create mode 100644 shared/graphql/mutations/channel/resetChannelJoinToken.js create mode 100644 shared/graphql/queries/channel/getChannelSettings.js 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/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/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 +); From abb788d5021f6e4cd175318df91e6a11a1dc6667 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 14:23:20 -0700 Subject: [PATCH 40/57] Add proper joinSettings query resolver on channel --- iris/queries/channel/index.js | 2 ++ iris/queries/channel/joinSettings.js | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 iris/queries/channel/joinSettings.js 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..e11ed01c7d --- /dev/null +++ b/iris/queries/channel/joinSettings.js @@ -0,0 +1,9 @@ +// @flow +import type { DBChannel } from 'shared/types'; +import { getChannelSettings } from '../../models/channelSettings'; + +export default async ({ id }: DBChannel) => { + return await getChannelSettings(id).then(settings => { + return settings.joinSettings; + }); +}; From f4f1dfbf33fe23fdd12fb53e56d09f091422c744 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 14:23:48 -0700 Subject: [PATCH 41/57] Adds api mutations to enable, disable, and reset token based joining of private channels --- .../channel/disableChannelTokenJoin.js | 40 +++++++++++++++++ .../channel/enableChannelTokenJoin.js | 42 ++++++++++++++++++ iris/mutations/channel/index.js | 6 +++ .../channel/resetChannelJoinToken.js | 43 +++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 iris/mutations/channel/disableChannelTokenJoin.js create mode 100644 iris/mutations/channel/enableChannelTokenJoin.js create mode 100644 iris/mutations/channel/resetChannelJoinToken.js 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..f30e747d73 --- /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( + async () => await enableChannelTokenJoin(channelId) + ); + } +}; diff --git a/iris/mutations/channel/index.js b/iris/mutations/channel/index.js index c933ab8e91..2460df9ac9 100644 --- a/iris/mutations/channel/index.js +++ b/iris/mutations/channel/index.js @@ -7,6 +7,9 @@ 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: { @@ -18,5 +21,8 @@ module.exports = { togglePendingUser, unblockUser, joinChannelWithToken, + enableChannelTokenJoin, + disableChannelTokenJoin, + resetChannelJoinToken, }, }; 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) + ); + } +}; From e063229367fd096b9eb09d51f4e81a725bd5a124 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 14:24:11 -0700 Subject: [PATCH 42/57] Fix setting checks when joining a channel via token --- iris/mutations/channel/joinChannelWithToken.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/iris/mutations/channel/joinChannelWithToken.js b/iris/mutations/channel/joinChannelWithToken.js index fc5110e075..fdde7f7541 100644 --- a/iris/mutations/channel/joinChannelWithToken.js +++ b/iris/mutations/channel/joinChannelWithToken.js @@ -12,6 +12,7 @@ import { getUserPermissionsInCommunity, createMemberInCommunity, } from '../../models/usersCommunities'; +import { getChannelSettings } from '../../models/channelSettings'; type JoinChannelWithTokenInput = { input: { @@ -44,6 +45,8 @@ export default async ( if (!channel) return new UserError('No channel found in this community'); + const settings = await getChannelSettings(channel.id); + if (!channel.isPrivate) { return channel; } @@ -60,9 +63,17 @@ export default async ( return new UserError("You don't have permission to view this channel"); } - if (channel.joinToken && token !== channel.joinToken) { + 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( - "We weren't able to authenticate this request - the token may have changed" + "You can't join at this time, the token may have changed" ); } From 1842c52621c21efa1a3544dba64ceb769c1a5723 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 14:31:48 -0700 Subject: [PATCH 43/57] Remove console log --- src/views/privateChannelJoin/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/privateChannelJoin/index.js b/src/views/privateChannelJoin/index.js index 7a87f8a67b..a399c57dd3 100644 --- a/src/views/privateChannelJoin/index.js +++ b/src/views/privateChannelJoin/index.js @@ -57,7 +57,6 @@ class PrivateChannelJoin extends React.Component { joinChannelWithToken({ channelSlug, token, communitySlug }) .then(data => { - console.log('mutation dat', data); this.setState({ isLoading: false }); dispatch(addToastWithTimeout('success', 'Welcome!')); return history.push(`/${communitySlug}/${channelSlug}`); From 00c12a75c6da5915664de018c4589ec944872372 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Fri, 16 Mar 2018 17:31:48 -0700 Subject: [PATCH 44/57] Ignore migration files in danger flow --- dangerfile.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dangerfile.js b/dangerfile.js index af2c15dd8d..ff90ce8114 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -80,6 +80,10 @@ 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', + ], }) ); From e26ed642239d24bf59d8ddc231742cfaf7ebe2cc Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Sat, 17 Mar 2018 11:32:37 +0100 Subject: [PATCH 45/57] Code cleanup --- .../20180316195507-create-channel-settings-table.js | 2 +- iris/mutations/channel/enableChannelTokenJoin.js | 4 ++-- iris/queries/channel/joinSettings.js | 11 ++++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/iris/migrations/20180316195507-create-channel-settings-table.js b/iris/migrations/20180316195507-create-channel-settings-table.js index 1231e3cf78..0c60a9e477 100644 --- a/iris/migrations/20180316195507-create-channel-settings-table.js +++ b/iris/migrations/20180316195507-create-channel-settings-table.js @@ -9,7 +9,7 @@ exports.up = function(r, conn) { .run(conn) ) .catch(err => { - console.log(err); + console.error(err); throw err; }); }; diff --git a/iris/mutations/channel/enableChannelTokenJoin.js b/iris/mutations/channel/enableChannelTokenJoin.js index f30e747d73..caa86cc079 100644 --- a/iris/mutations/channel/enableChannelTokenJoin.js +++ b/iris/mutations/channel/enableChannelTokenJoin.js @@ -35,8 +35,8 @@ export default async ( if (hasSettings) { return await enableChannelTokenJoin(channelId); } else { - return await createChannelSettings(channelId).then( - async () => await enableChannelTokenJoin(channelId) + return await createChannelSettings(channelId).then(() => + enableChannelTokenJoin(channelId) ); } }; diff --git a/iris/queries/channel/joinSettings.js b/iris/queries/channel/joinSettings.js index e11ed01c7d..28966984c9 100644 --- a/iris/queries/channel/joinSettings.js +++ b/iris/queries/channel/joinSettings.js @@ -1,9 +1,14 @@ // @flow -import type { DBChannel } from 'shared/types'; import { getChannelSettings } from '../../models/channelSettings'; +import type { DBChannel } from 'shared/types'; +import type { GraphQLContext } from '../../'; -export default async ({ id }: DBChannel) => { - return await getChannelSettings(id).then(settings => { +export default async ( + { id }: DBChannel, + _: any, + { loaders }: GraphQLContext +) => { + return loaders.channelSettings.load(id).then(settings => { return settings.joinSettings; }); }; From 97b60ccebb0c03eaaa0ba28a7c61ccc1d8530b0b Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Sat, 17 Mar 2018 11:46:10 +0100 Subject: [PATCH 46/57] Update testing docs --- docs/testing.md | 62 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 21 deletions(-) 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. - From f8769fccd3820f7f09762d1c074cfe8cae6ef0db Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Sat, 17 Mar 2018 12:43:36 +0100 Subject: [PATCH 47/57] Update PR template, add issue template Make the PR and issue templates more clear to outside contributors when they come. Note that all of this required/auto-close stuff will be waived for us three and any future employees, and also isn't implemented yet. --- .github/ISSUE_TEMPLATE.md | 15 +++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 27 ++++++++++++--------------- 2 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE.md 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)** - - From a8a3c15ce2cca39545a4de6a1cbc94955793b385 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Sat, 17 Mar 2018 12:14:52 -0700 Subject: [PATCH 48/57] Fix breakage due to loader caching --- iris/queries/channel/joinSettings.js | 8 ++------ .../components/loginTokenSettings.js | 17 ++++++++--------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/iris/queries/channel/joinSettings.js b/iris/queries/channel/joinSettings.js index 28966984c9..ca29a3cd68 100644 --- a/iris/queries/channel/joinSettings.js +++ b/iris/queries/channel/joinSettings.js @@ -3,12 +3,8 @@ import { getChannelSettings } from '../../models/channelSettings'; import type { DBChannel } from 'shared/types'; import type { GraphQLContext } from '../../'; -export default async ( - { id }: DBChannel, - _: any, - { loaders }: GraphQLContext -) => { - return loaders.channelSettings.load(id).then(settings => { +export default async ({ id }: DBChannel, _: any) => { + return getChannelSettings(id).then(settings => { return settings.joinSettings; }); }; diff --git a/src/views/channelSettings/components/loginTokenSettings.js b/src/views/channelSettings/components/loginTokenSettings.js index 38eb78b8f1..075cf9030a 100644 --- a/src/views/channelSettings/components/loginTokenSettings.js +++ b/src/views/channelSettings/components/loginTokenSettings.js @@ -15,7 +15,7 @@ import { SectionTitle, SectionSubtitle, } from 'src/components/settingsViews/style'; -import BrandedLoginToggle from './loginTokenToggle'; +import LoginTokenToggle from './loginTokenToggle'; import ResetJoinToken from './resetJoinToken'; import { Input } from 'src/components/formElements'; import saveBrandedLoginSettings from 'shared/graphql/mutations/community/saveBrandedLoginSettings'; @@ -35,7 +35,7 @@ type State = { isLoading: boolean, }; -class BrandedLogin extends React.Component { +class LoginTokenSettings extends React.Component { state = { isLoading: false, }; @@ -46,6 +46,8 @@ class BrandedLogin extends React.Component { if (channel) { const { joinSettings } = channel; + console.log(channel); + return ( Join channel via link @@ -54,7 +56,7 @@ class BrandedLogin extends React.Component { Anyone with this link will be able to join this channel. - + {joinSettings.tokenJoinEnabled && ( { } } -export default compose( - getChannelSettings, - viewNetworkHandler, - saveBrandedLoginSettings, - connect() -)(BrandedLogin); +export default compose(getChannelSettings, viewNetworkHandler, connect())( + LoginTokenSettings +); From 55ed20f6158b87907309818983869a9f015396cd Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Sat, 17 Mar 2018 12:15:06 -0700 Subject: [PATCH 49/57] Remove console --- src/views/channelSettings/components/loginTokenSettings.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/views/channelSettings/components/loginTokenSettings.js b/src/views/channelSettings/components/loginTokenSettings.js index 075cf9030a..83c65465b1 100644 --- a/src/views/channelSettings/components/loginTokenSettings.js +++ b/src/views/channelSettings/components/loginTokenSettings.js @@ -46,8 +46,6 @@ class LoginTokenSettings extends React.Component { if (channel) { const { joinSettings } = channel; - console.log(channel); - return ( Join channel via link From 45fe12dbdc3ffbc69a1f541fd734fd63b8a886a4 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Sat, 17 Mar 2018 12:24:26 -0700 Subject: [PATCH 50/57] Add a test for fetching channel settings --- .../channel/queries/channelSettings.test.js | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 iris/test/channel/queries/channelSettings.test.js 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(); +}); From 12b640cf2fe583a0a26ff086b8a5882d138373a0 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Sun, 18 Mar 2018 11:02:22 -0700 Subject: [PATCH 51/57] Add snapshot --- .../__snapshots__/channelSettings.test.js.snap | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 iris/test/channel/queries/__snapshots__/channelSettings.test.js.snap 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, + }, + }, + }, +} +`; From 94f1a56d4b000ee27a72bbf09e2e8a37e1263d7d Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Sun, 18 Mar 2018 11:02:40 -0700 Subject: [PATCH 52/57] eslint --- src/views/channelSettings/components/loginTokenSettings.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/channelSettings/components/loginTokenSettings.js b/src/views/channelSettings/components/loginTokenSettings.js index 83c65465b1..6fe561f5aa 100644 --- a/src/views/channelSettings/components/loginTokenSettings.js +++ b/src/views/channelSettings/components/loginTokenSettings.js @@ -18,7 +18,6 @@ import { import LoginTokenToggle from './loginTokenToggle'; import ResetJoinToken from './resetJoinToken'; import { Input } from 'src/components/formElements'; -import saveBrandedLoginSettings from 'shared/graphql/mutations/community/saveBrandedLoginSettings'; import { addToastWithTimeout } from 'src/actions/toasts'; import { TokenInputWrapper } from '../style'; From f3dbc3a2220fab49b56dfa2e68ca0c48472d1639 Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Mon, 19 Mar 2018 10:28:17 +0100 Subject: [PATCH 53/57] Upgrade draft-js-plugin-no-console Fixes Danger --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4fb601ed4a..fb3de2864a 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "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", diff --git a/yarn.lock b/yarn.lock index 1c86e95efd..498d4fcb43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2939,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" From 0e5b238d285274ae86dccba35c05046d22fc031c Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Mon, 19 Mar 2018 11:17:32 +0100 Subject: [PATCH 54/57] Run CircleCI jobs in parallel Runs static checkers (ESlint, Flow) in parallel with web and native tests instead of sequentially --- .circleci/config.yml | 113 ++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 84de65228c..8fad05a4d3 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,16 +83,8 @@ jobs: root: . paths: . - # Tests js of the mobile app - test_mobile_js: - <<: *js_defaults - steps: - - attach_workspace: - at: ~/spectrum - - run: cd ./mobile && yarn test:unit - - # Test the web JS - test_web_js: + # Start db and servers, then run e2e and unit tests + test_web: <<: *defaults docker: - image: circleci/node:8-browsers @@ -71,41 +95,13 @@ jobs: steps: - attach_workspace: at: ~/spectrum - - run: - 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 - - run: - name: Start RethinkDB - command: rethinkdb --bind all - background: true + - run: *install-rethinkdb + - run: *start-rethinkdb - run: sleep 10 - - run: - 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 - - run: - name: Start Iris in the background - command: TEST_DB=true yarn run dev:iris - background: true - - run: - name: Start web client in the background - command: yarn run dev:web - background: true + - run: *setup-and-build-web + - run: *start-iris + - run: *start-web - run: sleep 60 - - run: - name: Run Flow - command: yarn run flow - - run: - name: Run ESLint - command: yarn run lint - run: name: Run Unit Tests command: yarn run test:ci @@ -117,6 +113,24 @@ jobs: when: always command: yarn run danger ci + # Run eslint, flow etc. + test_static_js: + <<: *js_defaults + steps: + - 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 + steps: + - attach_workspace: + at: ~/spectrum + - run: cd ./mobile && yarn test:unit # Tests native code of the mobile app test_mobile_native: @@ -148,8 +162,7 @@ jobs: workflows: version: 2 - # Tests mobile app - test_mobile: + test: jobs: - checkout_environment - test_mobile_js: @@ -159,11 +172,9 @@ workflows: # - test_mobile_native: # requires: # - checkout_environment - - # Tests web app - test_web: - jobs: - - checkout_environment - - test_web_js: + - test_web: + requires: + - checkout_environment + - test_static_js: requires: - checkout_environment From 9eb9efbb1aff5278702353b4829e5fcc3f309cdc Mon Sep 17 00:00:00 2001 From: Maximilian Stoiber Date: Mon, 19 Mar 2018 11:40:25 +0100 Subject: [PATCH 55/57] Fix attaching workspace --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8fad05a4d3..2dad02d4d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -117,6 +117,8 @@ jobs: test_static_js: <<: *js_defaults steps: + - attach_workspace: + at: ~/spectrum - run: name: Run Flow command: yarn run flow From 078a0339049018ab25e2ad6cbfc8a146bc8b1b10 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Mon, 19 Mar 2018 07:30:20 -0700 Subject: [PATCH 56/57] Version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9341b59291..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", From 7eb29f86b5b428aff302f4996b73f4002ddc3a62 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Mon, 19 Mar 2018 07:46:41 -0700 Subject: [PATCH 57/57] Small fixes to help danger pass 2.1.9 --- iris/models/user.js | 2 +- iris/mutations/message/addMessage.js | 2 +- src/components/chatInput/components/style.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/iris/models/user.js b/iris/models/user.js index 4091d47fba..af1254e273 100644 --- a/iris/models/user.js +++ b/iris/models/user.js @@ -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); diff --git a/iris/mutations/message/addMessage.js b/iris/mutations/message/addMessage.js index d7ffd85129..17361e0d18 100644 --- a/iris/mutations/message/addMessage.js +++ b/iris/mutations/message/addMessage.js @@ -175,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/src/components/chatInput/components/style.js b/src/components/chatInput/components/style.js index e82d2ec35e..6256d296e9 100644 --- a/src/components/chatInput/components/style.js +++ b/src/components/chatInput/components/style.js @@ -1,3 +1,4 @@ +// @flow import styled from 'styled-components'; import { zIndex } from 'src/components/globals';