diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a4f6fb6eb24..fd30e7822cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -192,12 +192,20 @@ Be sure to check out the [API `README`][api-readme] for additional instructions. ```shell # run API with virtual robot -make -C api dev ENABLE_VIRTUAL_SMOOTHIE=true -# run API with robot's motor driver connected via USB to UART cable make -C api dev +# run API with robot's motor driver connected via USB to UART cable +make -C api dev ENABLE_VIRTUAL_SMOOTHIE=false # push the current contents of the api directory to robot for testing +# defaults to currently connected ethernet robot make push-api +# takes optional host variable for other robots +make push-api host=${some_other_ip_address} + +# SSH into the currently connected ethernet robot +make term +# takes optional host variable for other robots +make term host=${some_other_ip_address} ``` ### Releasing (for Opentrons developers) diff --git a/Makefile b/Makefile index 61d0b879a35..f451ad5007a 100755 --- a/Makefile +++ b/Makefile @@ -26,6 +26,9 @@ ifeq ($(watch), true) cover := false endif +# run at usage (=), not on makefile parse (:=) +usb_host = $(shell yarn run -s discovery find -i 169.254 fd00 -c "[fd00:0:cafe:fefe::1]") + # install all project dependencies # front-end dependecies handled by yarn .PHONY: install @@ -53,6 +56,7 @@ install-types: flow-typed install --overwrite --flowVersion=0.61.0 .PHONY: push-api +push-api: export host = $(usb_host) push-api: $(MAKE) -C $(API_LIB_DIR) push $(MAKE) -C $(API_DIR) push @@ -60,7 +64,16 @@ push-api: .PHONY: api-local-container api-local-container: - docker build --no-cache --build-arg base_image=resin/amd64-alpine-python:3.6-slim-20180123 --build-arg running_on_pi=0 --build-arg data_mkdir_path_slash_if_none=/data/system . + docker build . \ + --no-cache \ + --build-arg base_image=resin/amd64-alpine-python:3.6-slim-20180123 \ + --build-arg running_on_pi=0 \ + --build-arg data_mkdir_path_slash_if_none=/data/system + +.PHONY: term +term: export host = $(usb_host) +term: + $(MAKE) -C $(API_DIR) term # all tests .PHONY: test diff --git a/app-shell/README.md b/app-shell/README.md index 94e4858db27..41f083178d9 100644 --- a/app-shell/README.md +++ b/app-shell/README.md @@ -220,15 +220,6 @@ Full name of app user to populate "Name" in support conversations. Email of app user to populate "Email" in support conversations. -##### discovery.enabled - -* CLI argument: `--discovery.enabled` -* Environment variable: `OT_APP_DISCOVERY__ENABLED` -* JSON path: `discovery.enabled` -* Default: `false` - -Enables experimental robot discovery client for improved robot tracking. - ##### discovery.candidates * CLI argument: `--discovery.candidates` diff --git a/app-shell/src/config.js b/app-shell/src/config.js index 5dc212a2371..aaa6315cba4 100644 --- a/app-shell/src/config.js +++ b/app-shell/src/config.js @@ -75,8 +75,6 @@ const DEFAULTS: Config = { // robot discovery discovery: { - // new discovery client feature flag - enabled: false, candidates: [] } } diff --git a/app-shell/src/discovery.js b/app-shell/src/discovery.js index 53e5feff223..3f9e3e5ad6e 100644 --- a/app-shell/src/discovery.js +++ b/app-shell/src/discovery.js @@ -3,7 +3,6 @@ import assert from 'assert' import groupBy from 'lodash/groupBy' import map from 'lodash/map' -import noop from 'lodash/noop' import uniqBy from 'lodash/uniqBy' import Store from 'electron-store' @@ -33,9 +32,6 @@ let client export function registerDiscovery (dispatch: Action => void) { config = getConfig('discovery') - - if (!config.enabled) return noop - store = new Store({name: 'discovery', defaults: {services: []}}) client = DiscoveryClient({ @@ -73,7 +69,7 @@ export function registerDiscovery (dispatch: Action => void) { } export function getRobots () { - if (!client || !config || !config.enabled) return [] + if (!client) return [] return servicesToRobots(client.services) } diff --git a/app/Makefile b/app/Makefile index db7ab94c75e..b442faf51e2 100644 --- a/app/Makefile +++ b/app/Makefile @@ -40,20 +40,14 @@ dist: .PHONY: dev dev: - concurrently --no-color --kill-others --names "server,mdns,shell"\ + concurrently --no-color --kill-others --names "server,shell" \ "$(MAKE) dev-server" \ - "$(MAKE) dev-mdns" \ "$(MAKE) dev-shell" .PHONY: dev-server dev-server: $(env)=development PORT=$(port) webpack-dev-server --hot -# TODO(mc, 2017-10-31): remove when API is capable of advertising itself -.PHONY: dev-mdns -dev-mdns: - $(env)=development node scripts/advertise-local-api.js - .PHONY: dev-shell dev-shell: wait-on http-get://localhost:$(port) && \ diff --git a/app/package.json b/app/package.json index 9581fbf045e..bd5178bd5be 100644 --- a/app/package.json +++ b/app/package.json @@ -24,7 +24,6 @@ "@opentrons/components": "3.3.0-beta.1", "@opentrons/shared-data": "3.3.0-beta.1", "@thi.ng/paths": "^1.3.8", - "bonjour": "^3.5.0", "classnames": "^2.2.5", "electron": "1.8.3", "history": "^4.7.2", diff --git a/app/scripts/advertise-local-api.js b/app/scripts/advertise-local-api.js deleted file mode 100644 index cc9533b5de0..00000000000 --- a/app/scripts/advertise-local-api.js +++ /dev/null @@ -1,75 +0,0 @@ -// simple script to advertise a MDNS service for local API -// TODO(mc, 2017-10-31): remove this file once API can advertise for itself -const os = require('os') -const Bonjour = require('bonjour') -const request = require('superagent') - -const LOCAL_API_POLL_INTERVAL_MS = 5000 -const LOCAL_API_HOST = os.hostname() -const LOCAL_API_PORT = 31950 -const LOCAL_API_HEALTH_URL = `http://localhost:${LOCAL_API_PORT}/health` - -const NAME = `Opentrons on ${LOCAL_API_HOST}` -const SERVICE = { - name: NAME, - host: NAME, - port: LOCAL_API_PORT, - type: 'http' -} - -let bonjour - -startPolling() - -process.on('SIGINT', exit) -process.on('SIGTERM', exit) - -function startPolling () { - setTimeout(pollAndPublish, LOCAL_API_POLL_INTERVAL_MS) -} - -function exit () { - if (bonjour) { - console.log('Unpublishing all dev MDNS services') - - bonjour.unpublishAll(() => { - console.log('All MDNS services unpublished') - process.exit(0) - }) - - setTimeout(() => process.exit(1), 1000) - } else { - process.exit(0) - } -} - -function pollAndPublish () { - request.get(LOCAL_API_HEALTH_URL) - .ok((response) => response.status === 200) - .then(() => { - console.log(`Found local API at ${LOCAL_API_HOST}:${LOCAL_API_PORT}`) - publish() - }) - .catch(startPolling) -} - -function publish () { - bonjour = bonjour || Bonjour() - - if ( - process.env.OT_APP_DISCOVERY__ENABLED && - process.env.OT_APP_DISCOVERY__ENABLED !== '0' - ) { - return console.log('New discovery enabled; not publishing') - } - - bonjour.publish(SERVICE) - .on('up', () => { - console.log(`Published MDNS service "${NAME}" on ${LOCAL_API_HOST}`) - }) - .on('error', (error) => { - console.error('Error publishing MDNS service', error) - // retry - startPolling() - }) -} diff --git a/app/src/components/calibrate-pipettes/Pipettes.js b/app/src/components/calibrate-pipettes/Pipettes.js index a9eef99ac2f..8973adf56f4 100644 --- a/app/src/components/calibrate-pipettes/Pipettes.js +++ b/app/src/components/calibrate-pipettes/Pipettes.js @@ -12,7 +12,6 @@ import {InstrumentGroup, AlertItem} from '@opentrons/components' import styles from './styles.css' type Props = { - name: string, pipettes: Array, currentPipette: ?Pipette, actualPipettes: ?PipettesResponse @@ -23,7 +22,7 @@ const ATTACH_ALERT = 'Pipette missing' const CHANGE_ALERT = 'Incorrect pipette attached' export default function Pipettes (props: Props) { - const {name, currentPipette, pipettes, actualPipettes} = props + const {currentPipette, pipettes, actualPipettes} = props const currentMount = currentPipette && currentPipette.mount const infoByMount = PIPETTE_MOUNTS.reduce((result, mount) => { @@ -66,7 +65,7 @@ export default function Pipettes (props: Props) { />

{'Go to the '} - + robot settings {` panel to ${alertType} pipette.`} diff --git a/app/src/components/calibrate-pipettes/index.js b/app/src/components/calibrate-pipettes/index.js index 66294a84fb4..13ecd817858 100644 --- a/app/src/components/calibrate-pipettes/index.js +++ b/app/src/components/calibrate-pipettes/index.js @@ -1,3 +1,4 @@ +// @flow // instrument setup components import PipetteTabs from './PipetteTabs' import Pipettes from './Pipettes' diff --git a/app/src/config/index.js b/app/src/config/index.js index 1bb09e4a36f..9044ff85f61 100644 --- a/app/src/config/index.js +++ b/app/src/config/index.js @@ -55,7 +55,6 @@ export type Config = { }, discovery: { - enabled: boolean, candidates: string | Array }, } diff --git a/app/src/discovery/__tests__/actions.test.js b/app/src/discovery/__tests__/actions.test.js index 92716c6b45a..cc6286e65e7 100644 --- a/app/src/discovery/__tests__/actions.test.js +++ b/app/src/discovery/__tests__/actions.test.js @@ -30,17 +30,4 @@ describe('discovery actions', () => { jest.runTimersToTime(expectedTimeout) expect(store.getActions()).toEqual([expectedStart, expectedFinish]) }) - - // TODO(mc, 2018-08-10): legacy discovery support; remove - test('startDiscovery with discovery disabled', () => { - store = mockStore({config: {discovery: {enabled: false}}}) - - const expectedStart = { - type: 'robot:DISCOVER', - meta: {robotCommand: true} - } - - store.dispatch(startDiscovery()) - expect(store.getActions()).toEqual([expectedStart]) - }) }) diff --git a/app/src/discovery/__tests__/reducer.test.js b/app/src/discovery/__tests__/reducer.test.js index d84329ca8f8..f8a2344f262 100644 --- a/app/src/discovery/__tests__/reducer.test.js +++ b/app/src/discovery/__tests__/reducer.test.js @@ -26,20 +26,6 @@ describe('discoveryReducer', () => { } } }, - // TODO(mc, 2018-08-10): legacy; remove when DC enabled by default - { - name: 'robot:DISCOVER sets scanning: true', - action: {type: 'robot:DISCOVER'}, - initialState: {scanning: false}, - expectedState: {scanning: true} - }, - // TODO(mc, 2018-08-10): legacy; remove when DC enabled by default - { - name: 'robot:DISCOVER_FINISH sets scanning: false', - action: {type: 'robot:DISCOVER_FINISH'}, - initialState: {scanning: true}, - expectedState: {scanning: false} - }, { name: 'discovery:START sets scanning: true', action: {type: 'discovery:START'}, @@ -52,44 +38,6 @@ describe('discoveryReducer', () => { initialState: {scanning: true}, expectedState: {scanning: false} }, - // TODO(mc, 2018-08-10): legacy; remove when DC enabled by default - { - name: 'robot:ADD_DISCOVERED adds robot to list', - action: { - type: 'robot:ADD_DISCOVERED', - payload: {name: 'foo', ip: '192.168.1.42', port: 31950, wired: false} - }, - initialState: {robotsByName: {}}, - expectedState: { - robotsByName: { - foo: { - name: 'foo', - connections: [ - {ip: '192.168.1.42', port: 31950, ok: true, local: false} - ] - } - } - } - }, - // TODO(mc, 2018-08-10): legacy; remove when DC enabled by default - { - name: 'robot:REMOVE_DISCOVERED sets ok to false', - action: { - type: 'robot:REMOVE_DISCOVERED', - payload: {name: 'foo', ip: '192.168.1.42', port: 31950, wired: false} - }, - initialState: {robotsByName: {}}, - expectedState: { - robotsByName: { - foo: { - name: 'foo', - connections: [ - {ip: '192.168.1.42', port: 31950, ok: false, local: false} - ] - } - } - } - }, { name: 'discovery:UPDATE_LIST resets discovered list', action: { diff --git a/app/src/discovery/index.js b/app/src/discovery/index.js index 3520290b8be..ac54b7a0796 100644 --- a/app/src/discovery/index.js +++ b/app/src/discovery/index.js @@ -3,7 +3,6 @@ import {getShellRobots} from '../shell' import type {State, Action, ThunkAction} from '../types' -import type {RobotService} from '../robot' import type {DiscoveredRobot} from './types' type RobotsMap = {[name: string]: DiscoveredRobot} @@ -38,12 +37,7 @@ export function startDiscovery (): ThunkAction { const start: StartAction = {type: 'discovery:START', meta: {shell: true}} const finish: FinishAction = {type: 'discovery:FINISH', meta: {shell: true}} - return (dispatch, getState) => { - // TODO(mc, 2018-08-10): remove legacy discovery - if (!getState().config.discovery.enabled) { - return dispatch({type: 'robot:DISCOVER', meta: {robotCommand: true}}) - } - + return dispatch => { setTimeout(() => dispatch(finish), DISCOVERY_TIMEOUT) return dispatch(start) } @@ -65,7 +59,7 @@ export function getDiscoveredRobotsByName (state: State) { return state.discovery.robotsByName } -// TODO(mc, 2018-08-10): implement +// TODO(mc, 2018-08-10): implement in favor of robotSelectors.getDiscovered // export function getRobots (state: State) { // // } @@ -76,48 +70,17 @@ const initialState: DiscoveryState = { robotsByName: normalizeRobots(getShellRobots()) } -// TODO(mc, 2018-08-09): implement this reducer export function discoveryReducer ( state: DiscoveryState = initialState, action: Action ): DiscoveryState { switch (action.type) { - // TODO(mc, 2018-08-10): remove robot:DISCOVER - case 'robot:DISCOVER': case 'discovery:START': return {...state, scanning: true} - // TODO(mc, 2018-08-10): remove robot:DISCOVER_FINISH - case 'robot:DISCOVER_FINISH': case 'discovery:FINISH': return {...state, scanning: false} - // TODO(mc, 2018-08-10): remove robot:ADD_DISCOVERED - case 'robot:ADD_DISCOVERED': - return { - ...state, - robotsByName: { - ...state.robotsByName, - [action.payload.name]: robotServiceToDiscoveredRobot( - action.payload, - true - ) - } - } - - // TODO(mc, 2018-08-10): remove robot:REMOVE_DISCOVERED - case 'robot:REMOVE_DISCOVERED': - return { - ...state, - robotsByName: { - ...state.robotsByName, - [action.payload.name]: robotServiceToDiscoveredRobot( - action.payload, - false - ) - } - } - case 'discovery:UPDATE_LIST': return { ...state, @@ -137,14 +100,3 @@ function normalizeRobots (robots: Array = []): RobotsMap { {} ) } - -// TODO(mc, 2018-08-10): remove this function when no longer needed -function robotServiceToDiscoveredRobot ( - robot: RobotService, - ok: boolean -): DiscoveredRobot { - return { - name: robot.name, - connections: [{ip: robot.ip, port: robot.port, local: !!robot.wired, ok}] - } -} diff --git a/app/src/health-check/index.js b/app/src/health-check/index.js index e9a1cf61232..4319e5f654b 100644 --- a/app/src/health-check/index.js +++ b/app/src/health-check/index.js @@ -1,17 +1,14 @@ // @flow // health check module for keeping tabs on connected robots +// TODO(mc, 2018-08-21): remove this module in favor of discovery-client and +// websocket level ping-pong import {createSelector} from 'reselect' import type {Middleware, State, Action} from '../types' import type {BaseRobot, RobotService} from '../robot' // TODO(mc, 2018-02-26): figure out this circular dependency -import { - getDiscovered, - getConnectRequest, - getConnectedRobotName -} from '../robot/selectors' - +import {getDiscovered, getConnectRequest} from '../robot/selectors' import {fetchHealth} from '../http-api-client' // since middleware triggers before actions are reduced, health check failure @@ -130,10 +127,16 @@ export const healthCheckMiddleware: Middleware = } break - case 'robot:DISCONNECT_RESPONSE': - const robot = {name: getConnectedRobotName(store.getState())} - store.dispatch(stopHealthCheck(robot)) - store.dispatch(resetHealthCheck(robot)) + case 'robot:DISCONNECT_RESPONSE': { + // selector is not appropriate for this routine for reasons + // this module should be considered deprecated and will be removed + const robot = store.getState().robot.connection.connectedTo + + if (robot) { + store.dispatch(stopHealthCheck({name: robot})) + store.dispatch(resetHealthCheck({name: robot})) + } + } } return next(action) diff --git a/app/src/pages/Calibrate/Pipettes.js b/app/src/pages/Calibrate/Pipettes.js index 1bcb70940b5..5e87766791e 100644 --- a/app/src/pages/Calibrate/Pipettes.js +++ b/app/src/pages/Calibrate/Pipettes.js @@ -6,6 +6,8 @@ import {Route, Redirect, type Match} from 'react-router' import type {State} from '../../types' import type {Pipette, Robot} from '../../robot' +import type {PipettesResponse} from '../../http-api-client' + import {selectors as robotSelectors} from '../../robot' import {makeGetRobotPipettes, fetchPipettes} from '../../http-api-client' @@ -19,6 +21,7 @@ import SessionHeader from '../../components/SessionHeader' type SP = { pipettes: Array, currentPipette: ?Pipette, + actualPipettes: ?PipettesResponse, _robot: ?Robot, } @@ -77,16 +80,14 @@ function makeMapStateToProps (): (State, OP) => SP { const getAttachedPipettes = makeGetRobotPipettes() return (state, props) => { - const name = robotSelectors.getConnectedRobotName(state) const _robot = robotSelectors.getConnectedRobot(state) - const pipettesResponse = getAttachedPipettes(state, {name}) + const pipettesCall = _robot && getAttachedPipettes(state, _robot) return { - name, _robot, pipettes: robotSelectors.getPipettes(state), currentPipette: getCurrentPipette(state, props), - actualPipettes: pipettesResponse.response + actualPipettes: pipettesCall && pipettesCall.response } } } diff --git a/app/src/pages/Robots/RobotSettings.js b/app/src/pages/Robots/RobotSettings.js index b8c5ae0c181..4be60c6a1ed 100644 --- a/app/src/pages/Robots/RobotSettings.js +++ b/app/src/pages/Robots/RobotSettings.js @@ -26,7 +26,6 @@ import ConnectBanner from '../../components/RobotSettings/ConnectBanner' type SP = { showUpdateModal: boolean, - connectedName: string, showConnectAlert: boolean, homeInProgress: ?boolean, homeError: ?Error, @@ -36,7 +35,7 @@ type DP = {dispatch: Dispatch} type OP = { robot: Robot, - match: Match + match: Match, } type Props = SP & OP & { @@ -118,10 +117,10 @@ function makeMapStateToProps (): (state: State, ownProps: OP) => SP { const getHomeRequest = makeGetRobotHome() const getUpdateIgnoredRequest = makeGetRobotIgnoredUpdateRequest() const getAvailableRobotUpdate = makeGetAvailableRobotUpdate() + return (state, ownProps) => { const {robot} = ownProps const connectRequest = robotSelectors.getConnectRequest(state) - const connectedName = robotSelectors.getConnectedRobotName(state) const homeRequest = getHomeRequest(state, robot) const ignoredRequest = getUpdateIgnoredRequest(state, robot) const availableUpdate = getAvailableRobotUpdate(state, robot) @@ -133,7 +132,6 @@ function makeMapStateToProps (): (state: State, ownProps: OP) => SP { ) return { - connectedName, showUpdateModal: !!showUpdateModal, homeInProgress: homeRequest && homeRequest.inProgress, homeError: homeRequest && homeRequest.error, diff --git a/app/src/pages/Robots/index.js b/app/src/pages/Robots/index.js index ab404d602e0..2870b4a138d 100644 --- a/app/src/pages/Robots/index.js +++ b/app/src/pages/Robots/index.js @@ -17,7 +17,7 @@ import InstrumentSettings from './InstrumentSettings' type SP = { robot: ?Robot, - connectedName: string, + connectedName: ?string, } type OP = {match: Match} diff --git a/app/src/robot/actions.js b/app/src/robot/actions.js index 417f8c714cc..4a23dadce37 100755 --- a/app/src/robot/actions.js +++ b/app/src/robot/actions.js @@ -6,7 +6,6 @@ import type { Slot, Axis, Direction, - RobotService, ProtocolFile, SessionUpdate } from './types' @@ -17,27 +16,6 @@ const tagForRobotApi = (action) => ({...action, meta: {robotCommand: true}}) type Error = {message: string} -export type DiscoverAction = {| - type: 'robot:DISCOVER', - meta: {| - robotCommand: true, - |}, -|} - -export type DiscoverFinishAction = {| - type: 'robot:DISCOVER_FINISH', -|} - -export type AddDiscoveredAction = {| - type: 'robot:ADD_DISCOVERED', - payload: RobotService, -|} - -export type RemoveDiscoveredAction = {| - type: 'robot:REMOVE_DISCOVERED', - payload: RobotService, -|} - export type ConnectAction = {| type: 'robot:CONNECT', payload: {| @@ -218,10 +196,6 @@ export const actionTypes = { // TODO(mc, 2018-01-23): NEW ACTION TYPES GO HERE export type Action = - | DiscoverAction - | DiscoverFinishAction - | AddDiscoveredAction - | RemoveDiscoveredAction | ConnectAction | ConnectResponseAction | DisconnectAction @@ -241,16 +215,6 @@ export type Action = | ClearSessionAction export const actions = { - // TODO(mc, 2018-08-10): remove - discover (): DiscoverAction { - return {type: 'robot:DISCOVER', meta: {robotCommand: true}} - }, - - // TODO(mc, 2018-08-10): remove - discoverFinish (): DiscoverFinishAction { - return {type: 'robot:DISCOVER_FINISH'} - }, - connect (name: string): ConnectAction { return { type: 'robot:CONNECT', @@ -285,16 +249,6 @@ export const actions = { return {type: 'robot:UNEXPECTED_DISCONNECT'} }, - // TODO(mc, 2018-08-10): remove - addDiscovered (service: RobotService): AddDiscoveredAction { - return {type: 'robot:ADD_DISCOVERED', payload: service} - }, - - // TODO(mc, 2018-08-10): remove - removeDiscovered (service: RobotService): RemoveDiscoveredAction { - return {type: 'robot:REMOVE_DISCOVERED', payload: service} - }, - // make new session with protocol file session (file: ProtocolFile) { return tagForRobotApi({type: actionTypes.SESSION, payload: {file}}) diff --git a/app/src/robot/api-client/client.js b/app/src/robot/api-client/client.js index bbb2d16d571..91cefd54e59 100755 --- a/app/src/robot/api-client/client.js +++ b/app/src/robot/api-client/client.js @@ -7,7 +7,6 @@ import RpcClient from '../../rpc/client' import {actions, actionTypes} from '../actions' import * as constants from '../constants' import * as selectors from '../selectors' -import {handleDiscover} from './discovery' const RUN_TIME_TICK_INTERVAL_MS = 1000 const NO_INTERVAL = -1 @@ -26,7 +25,6 @@ export default function client (dispatch) { const {type} = action switch (type) { - case 'robot:DISCOVER': return handleDiscover(dispatch, state, action) case 'robot:CONNECT': return connect(state, action) case 'robot:DISCONNECT': return disconnect(state, action) case actionTypes.SESSION: return createSession(state, action) diff --git a/app/src/robot/api-client/discovery.js b/app/src/robot/api-client/discovery.js deleted file mode 100644 index 752c835ae3b..00000000000 --- a/app/src/robot/api-client/discovery.js +++ /dev/null @@ -1,106 +0,0 @@ -// mdns-based api server discovery with direct ethernet connection discovery -import os from 'os' -import net from 'net' -import Bonjour from 'bonjour' - -// import directly from health to avoid importing other parts -// of the api client (most importantly, the electron remote) -import {fetchHealth} from '../../http-api-client/health' - -import {actions} from '../actions' - -// mdns discovery constants -const NAME_RE = /^opentrons/i -const DISCOVERY_TIMEOUT_MS = 30000 -const UP_EVENT = 'up' -const DOWN_EVENT = 'down' - -// direct discovery constants -// see compute/scripts/setup.sh -const DIRECT_SERVICE = { - name: 'Opentrons USB', - ip: '[fd00:0:cafe:fefe::1]', - port: 31950, - wired: true -} -const DIRECT_POLL_INTERVAL_MS = 1000 - -const SKIP_WIRED_POLL = process.env.SKIP_WIRED_POLL - -export function handleDiscover (dispatch, state, action) { - // disable legacy discovery if new discovery feature flag is set - if (state.config.discovery.enabled) return - - // don't duplicate discovery requests - // TODO(mc, 2018-09-10): did not use selector to avoid circular dependency - // this file is getting ditched so ¯\_(ツ)_/¯ - if (state.discovery.scanning) return - - // TODO(mc, 2017-10-26): we're relying right now on the fact that resin - // advertises an SSH service. Instead, we should be registering an HTTP - // service on port 31950 and listening for that instead - const browser = Bonjour().find({type: 'http'}) - .on(UP_EVENT, handleServiceUp) - .on(DOWN_EVENT, handleServiceDown) - - let pollInterval - if (!SKIP_WIRED_POLL) { - pollInterval = setInterval(pollDirectConnection, DIRECT_POLL_INTERVAL_MS) - } - - setTimeout(finishDiscovery, DISCOVERY_TIMEOUT_MS) - - function handleServiceUp (service) { - if (NAME_RE.test(service.name)) { - const serviceWithIp = withIp(service) - dispatch(actions.addDiscovered(serviceWithIp)) - - // fetchHealth is a thunk action, so give it dispatch - return fetchHealth(serviceWithIp)(dispatch) - } - } - - function handleServiceDown (service) { - if (NAME_RE.test(service.name)) { - dispatch(actions.removeDiscovered(service)) - } - } - - function finishDiscovery () { - browser.removeListener(UP_EVENT, handleServiceUp) - browser.removeListener(DOWN_EVENT, handleServiceDown) - browser.stop() - clearInterval(pollInterval) - dispatch(actions.discoverFinish()) - } - - function pollDirectConnection () { - fetchHealth(DIRECT_SERVICE)(dispatch).then(result => { - const action = result.type === 'api:SUCCESS' - ? actions.addDiscovered(DIRECT_SERVICE) - : actions.removeDiscovered(DIRECT_SERVICE) - dispatch(action) - }) - } -} - -// grab IP address from service -// prefer IPv4, then IPv6, then hostname (with override for localhost) -function withIp (service) { - if (service.ip) return service - - const addresses = service.addresses || [] - let ip = addresses.find((address) => net.isIPv4(address)) - if (!ip) ip = addresses.find((address) => net.isIP(address)) - if (!ip) ip = service.host - - // API doesn't listen on all interfaces when running locally - // this hostname check is only for handling that situation - if (service.host && service.host.endsWith(os.hostname())) { - ip = 'localhost' - // emulate a wired robot when running locally - service = {...service, wired: true} - } - - return Object.assign({ip}, service) -} diff --git a/app/src/robot/selectors.js b/app/src/robot/selectors.js index b11d1cd8981..6c875d003d9 100644 --- a/app/src/robot/selectors.js +++ b/app/src/robot/selectors.js @@ -85,15 +85,14 @@ export function getConnectRequest (state: State) { return connection(state).connectRequest } -export function getConnectedRobotName (state: State): string { - return connection(state).connectedTo -} - export const getConnectedRobot: Selector = createSelector( getDiscovered, - (discovered) => discovered.find((r) => r.isConnected) + discovered => discovered.find(r => r.isConnected) ) +export const getConnectedRobotName: Selector = + createSelector(getConnectedRobot, r => r && r.name) + export const getConnectionStatus: Selector = createSelector( getConnectedRobotName, diff --git a/app/src/robot/test/actions.test.js b/app/src/robot/test/actions.test.js index 239b274c074..a7b4e740094 100644 --- a/app/src/robot/test/actions.test.js +++ b/app/src/robot/test/actions.test.js @@ -2,44 +2,6 @@ import {actions, actionTypes} from '../' describe('robot actions', () => { - test('DISCOVER action', () => { - const expected = { - type: 'robot:DISCOVER', - meta: {robotCommand: true} - } - - expect(actions.discover()).toEqual(expected) - }) - - test('DISCOVER_FINISH action', () => { - const expected = { - type: 'robot:DISCOVER_FINISH' - } - - expect(actions.discoverFinish()).toEqual(expected) - }) - - test('ADD_DISCOVERED action', () => { - const name = 'Opentrons XYZ123' - const host = '123456.local' - const expected = { - type: 'robot:ADD_DISCOVERED', - payload: {host, name} - } - - expect(actions.addDiscovered({host, name})).toEqual(expected) - }) - - test('REMOVE_DISCOVERED action', () => { - const service = {name: 'ot'} - const expected = { - type: 'robot:REMOVE_DISCOVERED', - payload: service - } - - expect(actions.removeDiscovered(service)).toEqual(expected) - }) - test('CONNECT action', () => { const expected = { type: 'robot:CONNECT', diff --git a/app/src/robot/test/api-client-discovery.test.js b/app/src/robot/test/api-client-discovery.test.js deleted file mode 100644 index 48fa0395529..00000000000 --- a/app/src/robot/test/api-client-discovery.test.js +++ /dev/null @@ -1,198 +0,0 @@ -// test the MDNS discovery mechanisms of the API client -import Bonjour from 'bonjour' -import EventEmitter from 'events' - -import {delay as _delay} from '../../util' -import {fetchHealth, __mockThunk, __setResponse} from '../../http-api-client/health' -import client from '../api-client/client' -import {actions} from '../' - -jest.mock('bonjour') -jest.mock('../../http-api-client/health') - -jest.useFakeTimers() - -const EXPECTED_DISCOVERY_MS = 30000 - -// custom delay function to deal with fake timers -const delay = (time) => { - const wait = _delay(time) - // TODO(mc, 2017-10-30): jest v21 renamed this method advanceTimersByTime - jest.runTimersToTime(time) - return wait -} - -const notScanningState = { - config: {discovery: {enabled: false}}, - discovery: {scanning: false} -} - -describe('api client - discovery', () => { - let bonjour - let browser - let dispatch - let sendToClient - - beforeEach(() => { - // mock mdns browser - browser = new EventEmitter() - browser.stop = jest.fn() - - bonjour = {find: jest.fn(() => browser)} - Bonjour.mockReturnValue(bonjour) - - __setResponse({type: 'api:SUCCESS'}) - - dispatch = jest.fn() - const _receive = client(dispatch) - - sendToClient = (state, action) => { - _receive(state, action) - return delay(1) - } - }) - - afterEach(() => { - jest.clearAllMocks() - jest.clearAllTimers() - delay(EXPECTED_DISCOVERY_MS) - }) - - test('searches for HTTP services', () => { - return sendToClient(notScanningState, actions.discover()) - .then(() => expect(bonjour.find).toHaveBeenCalledWith({type: 'http'})) - }) - - test('sets a 30 second discovery timeout', () => { - const expectedFinish = actions.discoverFinish() - - return sendToClient(notScanningState, actions.discover()) - .then(() => jest.runTimersToTime(EXPECTED_DISCOVERY_MS)) - .then(() => { - expect(browser.stop).toHaveBeenCalled() - expect(dispatch).toHaveBeenCalledWith(expectedFinish) - }) - }) - - test('dispatches ADD_DISCOVEREDs on new services', () => { - const services = [ - {name: 'opentrons-1', port: '31950', addresses: ['192.168.1.1']}, - {name: 'opentrons-2', port: '31950', addresses: ['192.168.1.2']}, - {name: 'opentrons-3', port: '31950', addresses: ['192.168.1.3']} - ] - - return sendToClient(notScanningState, actions.discover()) - .then(() => services.forEach((s) => browser.emit('up', s))) - .then(() => services.forEach((s) => { - const robot = Object.assign({}, s, {ip: s.addresses[0]}) - expect(dispatch).toHaveBeenCalledWith(actions.addDiscovered(robot)) - })) - }) - - test('dispatches fetchHealth on new services', () => { - const services = [ - {name: 'opentrons-1', port: '31950', addresses: ['192.168.1.1']}, - {name: 'opentrons-2', port: '31950', addresses: ['192.168.1.2']}, - {name: 'opentrons-3', port: '31950', addresses: ['192.168.1.3']} - ] - - return sendToClient(notScanningState, actions.discover()) - .then(() => services.forEach((s) => browser.emit('up', s))) - .then(() => services.forEach((s) => { - const robot = Object.assign({}, s, {ip: s.addresses[0]}) - expect(fetchHealth).toHaveBeenCalledWith(robot) - expect(__mockThunk).toHaveBeenCalledWith(dispatch) - })) - }) - - test('dispatches REMOVE_DISCOVEREDs on service downs', () => { - const services = [ - {name: 'opentrons-1', host: 'ot-1.local', port: '31950', type: 'http'}, - {name: 'opentrons-2', host: 'ot-2.local', port: '31950', type: 'http'}, - {name: 'opentrons-3', host: 'ot-3.local', port: '31950', type: 'http'} - ] - - return sendToClient(notScanningState, actions.discover()) - .then(() => services.forEach((s) => browser.emit('down', s))) - .then(() => services.forEach((s) => { - expect(dispatch) - .toHaveBeenCalledWith(actions.removeDiscovered(s)) - })) - }) - - test('will not dispatch ADD_DISCOVERED after discovery period', () => { - return sendToClient(notScanningState, actions.discover()) - .then(() => jest.runTimersToTime(EXPECTED_DISCOVERY_MS)) - .then(() => browser.emit('up', { - name: 'opentrons-1', - host: 'ot-1.local', - port: '31950', - type: 'http' - })) - .then(() => expect(dispatch).not.toHaveBeenCalledWith( - actions.addDiscovered(expect.anything()) - )) - }) - - test('will not dispatch REMOVE_DISCOVERED after discovery period', () => { - return sendToClient(notScanningState, actions.discover()) - .then(() => jest.runTimersToTime(EXPECTED_DISCOVERY_MS)) - .then(() => browser.emit('down', { - name: 'opentrons-1', - host: 'ot-1.local', - port: '31950', - type: 'http' - })) - .then(() => expect(dispatch).not.toHaveBeenCalledWith( - actions.removeDiscovered(expect.anything()) - )) - }) - - test('only dispatches for hosts with "opentrons" in the name', () => { - const services = [ - {name: 'opentrons-1', host: 'ot-1.local', port: '31950', type: 'http'}, - {name: 'nope', host: 'nope.local', port: '31950', type: 'http'}, - {name: 'opentrons-3', host: 'ot-3.local', port: '31950', type: 'http'} - ] - - return sendToClient(notScanningState, actions.discover()) - .then(() => services.forEach((s) => browser.emit('up', s))) - .then(() => expect(dispatch).not.toHaveBeenCalledWith( - actions.addDiscovered(services[1]) - )) - .then(() => services.forEach((s) => browser.emit('down', s))) - .then(() => expect(dispatch).not.toHaveBeenCalledWith( - actions.removeDiscovered(services[1]) - )) - }) - - test('dispatches fetchHealth for wired host every second', () => { - const expectedRobot = { - name: 'Opentrons USB', - ip: '[fd00:0:cafe:fefe::1]', - port: 31950, - wired: true - } - - return sendToClient(notScanningState, actions.discover()) - .then(() => delay(EXPECTED_DISCOVERY_MS)) - .then(() => { - expect(fetchHealth).toHaveBeenCalledTimes(30) - expect(fetchHealth).toHaveBeenCalledWith(expectedRobot) - expect(__mockThunk).toHaveBeenCalledTimes(30) - expect(__mockThunk).toHaveBeenCalledWith(dispatch) - }) - }) - - test('does not start new discovery if already in progress', () => { - const state = { - config: {discovery: {enabled: false}}, - discovery: {scanning: true} - } - - return sendToClient(state, actions.discover()) - .then(() => delay(EXPECTED_DISCOVERY_MS)) - .then(() => expect(bonjour.find).not.toHaveBeenCalled()) - .then(() => expect(fetchHealth).not.toHaveBeenCalled()) - }) -}) diff --git a/app/src/robot/test/selectors.test.js b/app/src/robot/test/selectors.test.js index 6c16908520e..73ee7c29bb3 100644 --- a/app/src/robot/test/selectors.test.js +++ b/app/src/robot/test/selectors.test.js @@ -1,10 +1,13 @@ // robot selectors test +import {setIn} from '@thi.ng/paths' import {NAME, selectors, constants} from '../' const makeState = (state) => ({[NAME]: state}) const { getDiscovered, + getConnectedRobot, + getConnectedRobotName, getConnectionStatus, getSessionLoadInProgress, getUploadError, @@ -31,103 +34,126 @@ const { } = selectors describe('robot selectors', () => { - test('getDiscovered', () => { - const state = { - robot: {connection: {connectedTo: 'bar'}}, - discovery: { - robotsByName: { - foo: { - name: 'foo', - connections: [ - {ip: '10.10.1.2', port: 31950, ok: true, local: false} - ] - }, - bar: { - name: 'bar', - connections: [ - {ip: '10.10.3.4', port: 31950, ok: true, local: false}, - {ip: '169.254.3.4', port: 31950, ok: true, local: true} - ] - }, - baz: { - name: 'baz', - connections: [ - {ip: '10.10.5.6', port: 31950, ok: true, local: false}, - {ip: '169.254.5.6', port: 31950, ok: false, local: true} - ] - }, - qux: { - name: 'qux', - connections: [ - {ip: '169.254.7.8', port: 31950, ok: true, local: true} - ] + describe('robot list', () => { + let state + + beforeEach(() => { + state = { + robot: {connection: {connectedTo: 'bar'}}, + discovery: { + robotsByName: { + foo: { + name: 'foo', + connections: [ + {ip: '10.10.1.2', port: 31950, ok: true, local: false} + ] + }, + bar: { + name: 'bar', + connections: [ + {ip: '10.10.3.4', port: 31950, ok: true, local: false}, + {ip: '169.254.3.4', port: 31950, ok: true, local: true} + ] + }, + baz: { + name: 'baz', + connections: [ + {ip: '10.10.5.6', port: 31950, ok: true, local: false}, + {ip: '169.254.5.6', port: 31950, ok: false, local: true} + ] + }, + qux: { + name: 'qux', + connections: [ + {ip: '169.254.7.8', port: 31950, ok: true, local: true} + ] + } } } } - } + }) + + test('getDiscovered', () => { + expect(getDiscovered(state)).toEqual([ + { + name: 'bar', + ip: '169.254.3.4', + port: 31950, + wired: true, + isConnected: true + }, + { + name: 'qux', + ip: '169.254.7.8', + port: 31950, + isConnected: false, + wired: true + }, + { + name: 'baz', + ip: '10.10.5.6', + port: 31950, + wired: false, + isConnected: false + }, + { + name: 'foo', + ip: '10.10.1.2', + port: 31950, + wired: false, + isConnected: false + } + ]) + }) - expect(getDiscovered(state)).toEqual([ - { + test('getConnectedRobot', () => { + expect(getConnectedRobot(state)).toEqual({ name: 'bar', ip: '169.254.3.4', port: 31950, wired: true, isConnected: true - }, - { - name: 'qux', - ip: '169.254.7.8', - port: 31950, - isConnected: false, - wired: true - }, - { - name: 'baz', - ip: '10.10.5.6', - port: 31950, - wired: false, - isConnected: false - }, - { - name: 'foo', - ip: '10.10.1.2', - port: 31950, - wired: false, - isConnected: false - } - ]) - }) + }) - test('getConnectionStatus', () => { - const state = { - connection: { + state = setIn(state, 'robot.connection.connectedTo', 'not-found') + expect(getConnectedRobot(state)).toBeUndefined() + }) + + test('getConnectedRobotName', () => { + expect(getConnectedRobotName(state)).toEqual('bar') + state = setIn(state, 'robot.connection.connectedTo', 'not-found') + expect(getConnectedRobotName(state)).toBeUndefined() + }) + + test('getConnectionStatus', () => { + state = setIn(state, 'robot.connection', { connectedTo: '', connectRequest: {inProgress: false}, disconnectRequest: {inProgress: false} - } - } - expect(getConnectionStatus(makeState(state))).toBe(constants.DISCONNECTED) + }) + expect(getConnectionStatus(state)).toBe(constants.DISCONNECTED) - state.connection = { - connectedTo: '', - connectRequest: {inProgress: true}, - disconnectRequest: {inProgress: false} - } - expect(getConnectionStatus(makeState(state))).toBe(constants.CONNECTING) + state = setIn(state, 'robot.connection', { + connectedTo: '', + connectRequest: {inProgress: true}, + disconnectRequest: {inProgress: false} + }) + expect(getConnectionStatus(state)).toBe(constants.CONNECTING) - state.connection = { - connectedTo: 'ot', - connectRequest: {inProgress: false}, - disconnectRequest: {inProgress: false} - } - expect(getConnectionStatus(makeState(state))).toBe(constants.CONNECTED) + state = setIn(state, 'robot.connection', { + connectedTo: 'foo', + connectRequest: {inProgress: false}, + disconnectRequest: {inProgress: false} + }) + expect(getConnectionStatus(state)).toBe(constants.CONNECTED) - state.connection = { - connectedTo: 'ot', - connectRequest: {inProgress: false}, - disconnectRequest: {inProgress: true} - } - expect(getConnectionStatus(makeState(state))).toBe(constants.DISCONNECTING) + state = setIn(state, 'robot.connection', { + connectedTo: 'foo', + connectRequest: {inProgress: false}, + disconnectRequest: {inProgress: true} + }) + expect(getConnectionStatus(state)).toBe(constants.DISCONNECTING) + }) }) test('getSessionLoadInProgress', () => { diff --git a/flow-typed/npm/superagent_vx.x.x.js b/flow-typed/npm/superagent_vx.x.x.js deleted file mode 100644 index 8f645fcd06a..00000000000 --- a/flow-typed/npm/superagent_vx.x.x.js +++ /dev/null @@ -1,137 +0,0 @@ -// flow-typed signature: b83bfe08d15425a18aee3d79bb53862f -// flow-typed version: <>/superagent_v^3.8.1/flow_v0.66.0 - -/** - * This is an autogenerated libdef stub for: - * - * 'superagent' - * - * 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 'superagent' { - 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 'superagent/lib/agent-base' { - declare module.exports: any; -} - -declare module 'superagent/lib/client' { - declare module.exports: any; -} - -declare module 'superagent/lib/is-object' { - declare module.exports: any; -} - -declare module 'superagent/lib/node/agent' { - declare module.exports: any; -} - -declare module 'superagent/lib/node/index' { - declare module.exports: any; -} - -declare module 'superagent/lib/node/parsers/image' { - declare module.exports: any; -} - -declare module 'superagent/lib/node/parsers/index' { - declare module.exports: any; -} - -declare module 'superagent/lib/node/parsers/json' { - declare module.exports: any; -} - -declare module 'superagent/lib/node/parsers/text' { - declare module.exports: any; -} - -declare module 'superagent/lib/node/parsers/urlencoded' { - declare module.exports: any; -} - -declare module 'superagent/lib/node/response' { - declare module.exports: any; -} - -declare module 'superagent/lib/node/unzip' { - declare module.exports: any; -} - -declare module 'superagent/lib/request-base' { - declare module.exports: any; -} - -declare module 'superagent/lib/response-base' { - declare module.exports: any; -} - -declare module 'superagent/lib/utils' { - declare module.exports: any; -} - -declare module 'superagent/superagent' { - declare module.exports: any; -} - -// Filename aliases -declare module 'superagent/lib/agent-base.js' { - declare module.exports: $Exports<'superagent/lib/agent-base'>; -} -declare module 'superagent/lib/client.js' { - declare module.exports: $Exports<'superagent/lib/client'>; -} -declare module 'superagent/lib/is-object.js' { - declare module.exports: $Exports<'superagent/lib/is-object'>; -} -declare module 'superagent/lib/node/agent.js' { - declare module.exports: $Exports<'superagent/lib/node/agent'>; -} -declare module 'superagent/lib/node/index.js' { - declare module.exports: $Exports<'superagent/lib/node/index'>; -} -declare module 'superagent/lib/node/parsers/image.js' { - declare module.exports: $Exports<'superagent/lib/node/parsers/image'>; -} -declare module 'superagent/lib/node/parsers/index.js' { - declare module.exports: $Exports<'superagent/lib/node/parsers/index'>; -} -declare module 'superagent/lib/node/parsers/json.js' { - declare module.exports: $Exports<'superagent/lib/node/parsers/json'>; -} -declare module 'superagent/lib/node/parsers/text.js' { - declare module.exports: $Exports<'superagent/lib/node/parsers/text'>; -} -declare module 'superagent/lib/node/parsers/urlencoded.js' { - declare module.exports: $Exports<'superagent/lib/node/parsers/urlencoded'>; -} -declare module 'superagent/lib/node/response.js' { - declare module.exports: $Exports<'superagent/lib/node/response'>; -} -declare module 'superagent/lib/node/unzip.js' { - declare module.exports: $Exports<'superagent/lib/node/unzip'>; -} -declare module 'superagent/lib/request-base.js' { - declare module.exports: $Exports<'superagent/lib/request-base'>; -} -declare module 'superagent/lib/response-base.js' { - declare module.exports: $Exports<'superagent/lib/response-base'>; -} -declare module 'superagent/lib/utils.js' { - declare module.exports: $Exports<'superagent/lib/utils'>; -} -declare module 'superagent/superagent.js' { - declare module.exports: $Exports<'superagent/superagent'>; -} diff --git a/package.json b/package.json index 4f457fa048e..cc716e1e4f7 100755 --- a/package.json +++ b/package.json @@ -111,7 +111,6 @@ "style-loader": "^0.18.2", "stylelint": "^8.4.0", "stylelint-config-standard": "^18.0.0", - "superagent": "^3.8.1", "url-loader": "^0.6.2", "wait-on": "^2.1.0", "webpack": "^3.1.0", diff --git a/update-server/Makefile b/update-server/Makefile index ddaf046ce77..eecced90af1 100644 --- a/update-server/Makefile +++ b/update-server/Makefile @@ -16,8 +16,9 @@ install: pipenv install $(pipenv_opts) .PHONY: dev +dev: export ENABLE_VIRTUAL_SMOOTHIE := true dev: - ENABLE_VIRTUAL_SMOOTHIE=true $(python) -m otupdate --debug --port $(port) + $(python) -m otupdate --debug --port $(port) .PHONY: clean clean: diff --git a/yarn.lock b/yarn.lock index 5f59326c8b4..80bfb35483d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2636,7 +2636,7 @@ compare-version@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080" -component-emitter@^1.2.0, component-emitter@^1.2.1: +component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -2846,10 +2846,6 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" -cookiejar@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a" - copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -4668,26 +4664,22 @@ forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" -form-data@^2.3.1, form-data@~2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" dependencies: asynckit "^0.4.0" combined-stream "^1.0.5" mime-types "^2.1.12" -form-data@~2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" +form-data@~2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" dependencies: asynckit "^0.4.0" 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" @@ -7006,7 +6998,7 @@ merge@^1.1.3: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" -methods@^1.1.1, methods@~1.1.2: +methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -8763,7 +8755,7 @@ qap@^3.1.2: version "3.3.1" resolved "https://registry.yarnpkg.com/qap/-/qap-3.3.1.tgz#11f9e8fa8890fe7cb99210c0f44d0613b7372cac" -qs@6.5.1, qs@^6.5.1, qs@~6.5.1: +qs@6.5.1, qs@~6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" @@ -9176,7 +9168,7 @@ read@1, read@~1.0.1: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.2.9, readable-stream@^2.3.3: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.2.9, readable-stream@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" dependencies: @@ -10537,21 +10529,6 @@ sumchecker@^2.0.2: dependencies: debug "^2.2.0" -superagent@^3.8.1: - version "3.8.2" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.2.tgz#e4a11b9d047f7d3efeb3bbe536d9ec0021d16403" - dependencies: - component-emitter "^1.2.0" - cookiejar "^2.1.0" - debug "^3.1.0" - extend "^3.0.0" - form-data "^2.3.1" - formidable "^1.1.1" - methods "^1.1.1" - mime "^1.4.1" - qs "^6.5.1" - readable-stream "^2.0.5" - supports-color@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a"