diff --git a/typings/index.d.ts b/typings/index.d.ts index 6796f3cb..8b23f8e9 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -122,6 +122,7 @@ export interface Action { export interface Environment { machineId: string sessionId: string + token: string } export interface MachineContext { @@ -142,6 +143,7 @@ export interface MachineStateSchema { Start: { states: { Startup: {} + Authenticate: {} NewOrContinue: {} SelectTutorial: {} ContinueTutorial: {} diff --git a/web-app/src/Routes.tsx b/web-app/src/Routes.tsx index cb3c1184..307ecb6c 100644 --- a/web-app/src/Routes.tsx +++ b/web-app/src/Routes.tsx @@ -19,7 +19,7 @@ const Routes = () => { return ( - + diff --git a/web-app/src/components/ErrorBoundary/index.tsx b/web-app/src/components/ErrorBoundary/index.tsx index ce6a93e3..b855ae68 100644 --- a/web-app/src/components/ErrorBoundary/index.tsx +++ b/web-app/src/components/ErrorBoundary/index.tsx @@ -7,8 +7,8 @@ class ErrorBoundary extends React.Component { // Display fallback UI this.setState({ hasError: true }) // You can also log the error to an error reporting service - console.error(error) - console.log(info) + console.error(JSON.stringify(error)) + console.log(JSON.stringify(info)) } public render() { diff --git a/web-app/src/services/apollo/auth.ts b/web-app/src/services/apollo/auth.ts new file mode 100644 index 00000000..a1637dc3 --- /dev/null +++ b/web-app/src/services/apollo/auth.ts @@ -0,0 +1,17 @@ +import {Operation} from 'apollo-boost' + +let authToken: string | null = null + +export const setAuthToken = (token: string | null) => { + authToken = token +} + +export const authorizeHeaders = (operation: Operation) => { + if (authToken) { + operation.setContext({ + headers: { + 'Authorization': authToken + } + }) + } +} diff --git a/web-app/src/services/apollo/index.ts b/web-app/src/services/apollo/index.ts index 6fb3d296..b5d8708d 100644 --- a/web-app/src/services/apollo/index.ts +++ b/web-app/src/services/apollo/index.ts @@ -1,12 +1,11 @@ import ApolloClient, {InMemoryCache} from 'apollo-boost' +import {authorizeHeaders} from './auth' export const cache = new InMemoryCache() const client = new ApolloClient({ uri: process.env.REACT_APP_GQL_URI, - headers: { - Authorization: process.env.REACT_APP_GQL_AUTH_TOKEN, - }, + request: authorizeHeaders, cache, }) diff --git a/web-app/src/services/apollo/mutations/authenticate.ts b/web-app/src/services/apollo/mutations/authenticate.ts new file mode 100644 index 00000000..1c02a307 --- /dev/null +++ b/web-app/src/services/apollo/mutations/authenticate.ts @@ -0,0 +1,23 @@ +import {gql} from 'apollo-boost' + +export default gql` + mutation Authenticate( + $machineId: String!, + $sessionId: String!, + $editor: EditorEnum! + ) { + editorLogin(input: { + machineId: $machineId, + sessionId: $sessionId, + editor: $editor + }) { + token + user { + id + name + email + avatarUrl + } + } + } +` diff --git a/web-app/src/services/channel/index.ts b/web-app/src/services/channel/index.ts index b51272b1..18d0c700 100644 --- a/web-app/src/services/channel/index.ts +++ b/web-app/src/services/channel/index.ts @@ -38,39 +38,17 @@ class Channel { // messages from core switch (action.type) { case 'ENV_LOAD': - this.machineSend(action) - return + case 'AUTHENTICATED': case 'TUTORIAL_LOADED': - // send action to state machine - this.machineSend('TUTORIAL_LOADED') - console.log('send action to state machine') - return case 'NEW_TUTORIAL': - this.machineSend(action) - return case 'TUTORIAL_CONFIGURED': - this.machineSend(action) - return case 'CONTINUE_TUTORIAL': - this.machineSend(action) - return case 'TEST_PASS': - // { type: 'TEST_PASS', payload: { stepId: string }} - this.machineSend(action) - return case 'TEST_FAIL': - this.machineSend(action) - return case 'TEST_RUNNING': - this.machineSend(action) - return case 'TEST_ERROR': - console.log('TEST_ERROR') this.machineSend(action) return - case 'ACTIONS_LOADED': - // TODO: use this for verifying completion of stepActions - return default: if (action.type) { console.warn(`Unknown received action ${action.type}`, action) diff --git a/web-app/src/services/state/actions/api.ts b/web-app/src/services/state/actions/api.ts index 9b2486cd..f5707c9a 100644 --- a/web-app/src/services/state/actions/api.ts +++ b/web-app/src/services/state/actions/api.ts @@ -1,6 +1,32 @@ import * as CR from 'typings' +import client from '../../apollo' +import authenticateMutation from '../../apollo/mutations/authenticate' +import {setAuthToken} from '../../apollo/auth' +import channel from '../../../services/channel' export default { + authenticate: (async (context: CR.MachineContext): Promise => { + + const result = await client.mutate({ + mutation: authenticateMutation, + variables: { + machineId: context.env.machineId, + sessionId: context.env.sessionId, + editor: 'VSCODE', + } + }) + + + if (!result || !result.data) { + // TODO: handle failed authentication + console.error('ERROR: Authentication failed') + } + const {token} = result.data.editorLogin + // add token to headers + setAuthToken(token) + // pass authenticated action back to state machine + channel.receive({data: {type: 'AUTHENTICATED'}}) + }), userTutorialComplete(context: CR.MachineContext) { console.log('should update user tutorial as complete') } diff --git a/web-app/src/services/state/actions/context.ts b/web-app/src/services/state/actions/context.ts index 2a910cd7..7ac2857f 100644 --- a/web-app/src/services/state/actions/context.ts +++ b/web-app/src/services/state/actions/context.ts @@ -6,7 +6,10 @@ import * as selectors from '../../selectors' export default { setEnv: assign({ env: (context: CR.MachineContext, event: CR.MachineEvent) => { - return event.payload.env + return { + ...context.env, + ...event.payload.env + } } }), continueTutorial: assign({ diff --git a/web-app/src/services/state/machine.ts b/web-app/src/services/state/machine.ts index 70aa4ece..232f61b6 100644 --- a/web-app/src/services/state/machine.ts +++ b/web-app/src/services/state/machine.ts @@ -12,7 +12,7 @@ export const machine = Machine