Skip to content

Commit

Permalink
feat(Executa): Add session parameter to execute functions
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-ketch committed Oct 23, 2019
1 parent 9ba7dce commit 7cf0513
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 17 deletions.
11 changes: 5 additions & 6 deletions src/base/Client.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Node } from '@stencila/schema'
import { Interface, Method, Manifest } from './Executor'
import { Node, SoftwareSession } from '@stencila/schema'
import { Interface, Manifest, Method } from './Executor'
import JsonRpcError, { JsonRpcErrorCode } from './JsonRpcError'
import JsonRpcRequest from './JsonRpcRequest'
import JsonRpcResponse from './JsonRpcResponse'
import { Address } from './Transports'
import JsonRpcError, { JsonRpcErrorCode } from './JsonRpcError'

/**
* A client to a remote, out of process, `Executor`.
Expand Down Expand Up @@ -55,8 +54,8 @@ export default abstract class Client implements Interface {
/**
* Call the remote `Executor`'s `execute` method
*/
public async execute(node: Node): Promise<Node> {
return this.call<Node>(Method.execute, { node })
public async execute(node: Node, session?: SoftwareSession): Promise<Node> {
return this.call<Node>(Method.execute, { node, session })
}

/**
Expand Down
18 changes: 9 additions & 9 deletions src/base/Executor.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { getLogger } from '@stencila/logga'
import { isPrimitive, Node, nodeType } from '@stencila/schema'
import { isPrimitive, Node, nodeType, SoftwareSession } from '@stencila/schema'
import Ajv from 'ajv'
import { JSONSchema7Definition } from 'json-schema'
import { ClientType } from './Client'
import { InternalError } from './InternalError'
import Server from './Server'
import { Address, Transport } from './Transports'
import { InternalError } from './InternalError'

const log = getLogger('executa:executor')

Expand Down Expand Up @@ -123,9 +123,10 @@ export abstract class Interface {
* Execute a `Node`.
*
* @param node The node to execute
* @param session The session that the node will be executed in
* @returns The executed node
*/
abstract async execute(node: Node): Promise<Node>
abstract async execute(node: Node, session?: SoftwareSession): Promise<Node>

/**
* Begin running a `Node`.
Expand Down Expand Up @@ -358,9 +359,7 @@ export class Executor implements Interface {
*/
public async start(): Promise<void> {
if (this.servers.length === 0) {
log.warn(
'No servers configured; executor will not be accessible.'
)
log.warn('No servers configured; executor will not be accessible.')
return
}

Expand Down Expand Up @@ -492,13 +491,14 @@ export class Executor implements Interface {
* (currently `CodeChunk` and `CodeExpression`).
*
* @param node The node to execute
* @param session The session to execute the node within
*/
public execute(node: Node): Promise<Node> {
public execute(node: Node, session?: SoftwareSession): Promise<Node> {
return this.walk(node, node => {
switch (nodeType(node)) {
case 'CodeChunk':
case 'CodeExpression':
return this.delegate(Method.execute, { node }, () =>
return this.delegate(Method.execute, { node, session }, () =>
Promise.resolve({
...(node as object),
errors: [
Expand Down Expand Up @@ -543,7 +543,7 @@ export class Executor implements Interface {
case Method.build:
return this.build(params.node)
case Method.execute:
return this.execute(params.node)
return this.execute(params.node, params.session)
}
}

Expand Down
9 changes: 7 additions & 2 deletions src/base/Server.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Executor } from './Executor'
import { InternalError } from './InternalError'
import JsonRpcError, { JsonRpcErrorCode } from './JsonRpcError'
import JsonRpcRequest from './JsonRpcRequest'
import JsonRpcResponse from './JsonRpcResponse'
import { Address } from './Transports'
import { InternalError } from './InternalError'

/**
* A base server class that passes JSON-RPC requests
Expand Down Expand Up @@ -106,9 +106,14 @@ export default abstract class Server {
param(request, 1, 'format', false)
)
break
case 'execute':
result = await this.executor.execute(
param(request, 0, 'node'),
param(request, 1, 'session', false)
)
break
case 'compile':
case 'build':
case 'execute':
case 'begin':
case 'end':
result = await this.executor[request.method](
Expand Down
64 changes: 64 additions & 0 deletions src/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* A simple console for testing connection to, and configuration of,
* executors.
*
* Usage:
*
* ```bash
* ts-node console
* ```
*
* For full debug level log messages use:
*
* ```bash
* ts-node console --debug
* ```
*
* Sends requests to execute a `CodeChunk` with `programmingLanguage: 'sh'`
* to the VM and prints it's `outputs` to the console.
*/

import { ClientType } from './base/Client'
import { Executor } from './base/Executor'
import discoverHttp from './http/discover'
import HttpClient from './http/HttpClient'
// import discoverTcp from './tcp/discover'
// import TcpClient from './tcp/TcpClient'
import WSClient from './ws/WebSocketClient'

const red = '\u001b[31;1m'
const blue = '\u001b[34;1m'
const reset = '\u001b[0m'

// @ts-ignore
window.process = {
// @ts-ignore
emit: console.log // eslint-disable-line
}

// eslint-disable-next-line
;(async () => {
// Create executor (no need to start it, since it has no servers)
const executor = new Executor(
[discoverHttp],
[HttpClient as ClientType, WSClient as ClientType]
)

const makeCodeChunk = async (text: string): Promise<string> =>
executor.encode({
type: 'CodeChunk',
programmingLanguage: 'python',
text: text
})

const inputs = document.querySelector<HTMLTextAreaElement>('textarea')
if (inputs === null) return

inputs.addEventListener('keydown', e => {
if (e.key === 'Enter' && e.shiftKey === true) {
console.log()
// const result = await makeCodeChunk(inputs.textContent)
// window.alert(result)
}
})
})()
70 changes: 70 additions & 0 deletions src/browser/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import '@stencila/components'
import { Executor } from '../base/Executor'
import discover from '../http/discover'
import { default as HttpClient } from '../http/HttpClient'
import { default as WSClient } from '../ws/WebSocketClient'
import { ClientType } from '../base/Client'
import { codeChunk, SoftwareSession } from '@stencila/schema'

const jwt =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1NzE3ODA0NDd9.Q33AWdLDiJJQrWFfVkFgOT94dipKCXEzSPze0OS49C0'

const discoverStub = [
async () => [
{
addresses: {
ws: {
type: 'ws',
host: '127.0.0.1',
port: '9000',
jwt
}
},
capabilities: {
execute: true
}
}
]
]

// @ts-ignore
const executor = new Executor(discoverStub, [
HttpClient as ClientType,
WSClient as ClientType
])

let sessionRef: undefined | SoftwareSession

const makeCodeChunk = async (text: string): Promise<string> => {
const code = codeChunk(text, { programmingLanguage: 'python' })
// @ts-ignore
console.log(JSON.stringify(code, null, 2))
// TODO: Check for session, if not executor.begin().then(session => seessionRef = session; executor.execute(code, session))
// return executor.execute(code, sessionRef)
return executor.execute(code)
}

const executeHandler = (text: string) => makeCodeChunk(text).then(console.log)

const setCodeChunkProps = () => {
const codeChunks = document.querySelectorAll('stencila-code-chunk')
codeChunks.forEach(chunk => {
// @ts-ignore
chunk.executeHandler = executeHandler
})
}

const onReadyHandler = (): void => {
setCodeChunkProps()
// TODO: Store session info
}

export const init = (): void => {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', onReadyHandler)
} else {
onReadyHandler()
}
}

init()
9 changes: 9 additions & 0 deletions src/test/testClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,13 @@ export const testClient = async (client: Client) => {
)

expect(await client.execute({ type: 'Entity' })).toEqual({ type: 'Entity' })
expect(
await client.execute(
{ type: 'Entity' },
{
type: 'SoftwareSession',
environment: { type: 'Environment', name: 'anything' }
}
)
).toEqual({ type: 'Entity' })
}

0 comments on commit 7cf0513

Please sign in to comment.