Merge pull request #1755 from kchadha/cloud-var-create

Create cloud variables
This commit is contained in:
Karishma Chadha 2018-11-14 21:39:29 -05:00 committed by GitHub
commit 0c9da5d963
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 176 additions and 12 deletions

View file

@ -373,7 +373,7 @@ class Blocks {
// into a state where a local var was requested for the stage, // into a state where a local var was requested for the stage,
// create a stage (global) var after checking for name conflicts // create a stage (global) var after checking for name conflicts
// on all the sprites. // on all the sprites.
if (e.isLocal && editingTarget && !editingTarget.isStage) { if (e.isLocal && editingTarget && !editingTarget.isStage && !e.isCloud) {
if (!editingTarget.lookupVariableById(e.varId)) { if (!editingTarget.lookupVariableById(e.varId)) {
editingTarget.createVariable(e.varId, e.varName, e.varType); editingTarget.createVariable(e.varId, e.varName, e.varType);
} }
@ -385,7 +385,7 @@ class Blocks {
return; return;
} }
} }
stage.createVariable(e.varId, e.varName, e.varType); stage.createVariable(e.varId, e.varName, e.varType, e.isCloud);
} }
break; break;
case 'var_rename': case 'var_rename':

View file

@ -322,7 +322,7 @@ class Runtime extends EventEmitter {
/** @type {Object.<string, Object>} */ /** @type {Object.<string, Object>} */
this.ioDevices = { this.ioDevices = {
clock: new Clock(), clock: new Clock(),
cloud: new Cloud(), cloud: new Cloud(this),
deviceManager: new DeviceManager(), deviceManager: new DeviceManager(),
keyboard: new Keyboard(this), keyboard: new Keyboard(this),
mouse: new Mouse(this), mouse: new Mouse(this),

View file

@ -233,11 +233,16 @@ class Target extends EventEmitter {
* @param {string} id Id of variable * @param {string} id Id of variable
* @param {string} name Name of variable. * @param {string} name Name of variable.
* @param {string} type Type of variable, '', 'broadcast_msg', or 'list' * @param {string} type Type of variable, '', 'broadcast_msg', or 'list'
* @param {boolean} isCloud Whether the variable to create has the isCloud flag set.
* Additional checks are made that the variable can be created as a cloud variable.
*/ */
createVariable (id, name, type) { createVariable (id, name, type, isCloud) {
if (!this.variables.hasOwnProperty(id)) { if (!this.variables.hasOwnProperty(id)) {
const newVariable = new Variable(id, name, type, false); const newVariable = new Variable(id, name, type, false);
this.variables[id] = newVariable; this.variables[id] = newVariable;
if (isCloud && this.isStage) {
this.runtime.ioDevices.cloud.requestCreateCloudVariable(newVariable);
}
} }
} }

View file

@ -26,6 +26,13 @@ class Cloud {
* @property {(number | string)} value The scalar value to update the variable with * @property {(number | string)} value The scalar value to update the variable with
*/ */
/**
* Part of a cloud io data post indicating a cloud variable was successfully
* created.
* @typedef {object} VarCreateData
* @property {string} name The name of the variable to create
*/
/** /**
* A cloud io data post message. * A cloud io data post message.
* @typedef {object} CloudIOData * @typedef {object} CloudIOData
@ -38,8 +45,9 @@ class Cloud {
* Cloud IO Device responsible for sending and receiving messages from * Cloud IO Device responsible for sending and receiving messages from
* cloud provider (mananging the cloud server connection) and interacting * cloud provider (mananging the cloud server connection) and interacting
* with cloud variables in the current project. * with cloud variables in the current project.
* @param {Runtime} runtime The runtime context for this cloud io device.
*/ */
constructor () { constructor (runtime) {
/** /**
* Reference to the cloud data provider, responsible for mananging * Reference to the cloud data provider, responsible for mananging
* the web socket connection to the cloud data server. * the web socket connection to the cloud data server.
@ -47,6 +55,12 @@ class Cloud {
*/ */
this.provider = null; this.provider = null;
/**
* Reference to the runtime that owns this cloud io device.
* @type {!Runtime}
*/
this.runtime = runtime;
/** /**
* Reference to the stage target which owns the cloud variables * Reference to the stage target which owns the cloud variables
* in the project. * in the project.
@ -80,6 +94,21 @@ class Cloud {
if (data.varUpdate) { if (data.varUpdate) {
this.updateCloudVariable(data.varUpdate); this.updateCloudVariable(data.varUpdate);
} }
if (data.varCreate) {
this.createCloudVariable(data.varCreate);
}
}
requestCreateCloudVariable (variable) {
if (this.runtime.canAddCloudVariable()) {
if (this.provider) {
this.provider.createVariable(variable.name, variable.value);
// We'll set the cloud flag and update the
// cloud variable limit when we actually
// get a confirmation from the cloud data server
}
}
} }
/** /**
@ -94,10 +123,28 @@ class Cloud {
} }
} }
/**
* Create a cloud variable based on the message
* received from the cloud provider.
* @param {VarCreateData} varCreate A {@link VarCreateData} object received from the
* cloud data provider confirming the creation of a cloud variable,
* providing its name and value.
*/
createCloudVariable (varCreate) {
const varName = varCreate.name;
const variable = this.stage.lookupVariableByNameAndType(varName, Variable.SCALAR_TYPE);
if (!variable) {
log.error(`Could not find cloud variable with name: ${varName}`);
}
variable.isCloud = true;
this.runtime.addCloudVariable();
}
/** /**
* Update a cloud variable in the runtime based on the message received * Update a cloud variable in the runtime based on the message received
* from the cloud provider. * from the cloud provider.
* @param {VarUpdateData} varUpdate A {@link VarUpdateData} object describing * @param {VarData} varUpdate A {@link VarData} object describing
* a cloud variable update received from the cloud data provider. * a cloud variable update received from the cloud data provider.
*/ */
updateCloudVariable (varUpdate) { updateCloudVariable (varUpdate) {

View file

@ -71,6 +71,58 @@ test('createListVariable creates a list', t => {
t.end(); t.end();
}); });
test('createVariable calls cloud io device\'s requestCreateCloudVariable', t => {
const runtime = new Runtime();
// Mock the requestCreateCloudVariable function
let requestCreateCloudWasCalled = false;
runtime.ioDevices.cloud.requestCreateCloudVariable = () => {
requestCreateCloudWasCalled = true;
};
const target = new Target(runtime);
target.isStage = true;
target.createVariable('foo', 'bar', Variable.SCALAR_TYPE, true /* isCloud */);
const variables = target.variables;
t.equal(Object.keys(variables).length, 1);
const variable = variables[Object.keys(variables)[0]];
t.equal(variable.id, 'foo');
t.equal(variable.name, 'bar');
t.equal(variable.type, Variable.SCALAR_TYPE);
t.equal(variable.value, 0);
// isCloud flag doesn't get set by the target createVariable function
t.equal(variable.isCloud, false);
t.equal(requestCreateCloudWasCalled, true);
t.end();
});
test('createVariable does not call cloud io device\'s requestCreateCloudVariable if target is not stage', t => {
const runtime = new Runtime();
// Mock the requestCreateCloudVariable function
let requestCreateCloudWasCalled = false;
runtime.ioDevices.cloud.requestCreateCloudVariable = () => {
requestCreateCloudWasCalled = true;
};
const target = new Target(runtime);
target.isStage = false;
target.createVariable('foo', 'bar', Variable.SCALAR_TYPE, true /* isCloud */);
const variables = target.variables;
t.equal(Object.keys(variables).length, 1);
const variable = variables[Object.keys(variables)[0]];
t.equal(variable.id, 'foo');
t.equal(variable.name, 'bar');
t.equal(variable.type, Variable.SCALAR_TYPE);
t.equal(variable.value, 0);
// isCloud flag doesn't get set by the target createVariable function
t.equal(variable.isCloud, false);
t.equal(requestCreateCloudWasCalled, false);
t.end();
});
test('createVariable throws when given invalid type', t => { test('createVariable throws when given invalid type', t => {
const target = new Target(); const target = new Target();
t.throws( t.throws(

View file

@ -2,14 +2,18 @@ const test = require('tap').test;
const Cloud = require('../../src/io/cloud'); const Cloud = require('../../src/io/cloud');
const Target = require('../../src/engine/target'); const Target = require('../../src/engine/target');
const Variable = require('../../src/engine/variable'); const Variable = require('../../src/engine/variable');
const Runtime = require('../../src/engine/runtime');
test('spec', t => { test('spec', t => {
const cloud = new Cloud(); const runtime = new Runtime();
const cloud = new Cloud(runtime);
t.type(cloud, 'object'); t.type(cloud, 'object');
t.type(cloud.postData, 'function'); t.type(cloud.postData, 'function');
t.type(cloud.requestUpdateVariable, 'function'); t.type(cloud.requestUpdateVariable, 'function');
t.type(cloud.updateCloudVariable, 'function'); t.type(cloud.updateCloudVariable, 'function');
t.type(cloud.requestCreateCloudVariable, 'function');
t.type(cloud.createCloudVariable, 'function');
t.type(cloud.setProvider, 'function'); t.type(cloud.setProvider, 'function');
t.type(cloud.setStage, 'function'); t.type(cloud.setStage, 'function');
t.type(cloud.clear, 'function'); t.type(cloud.clear, 'function');
@ -17,7 +21,8 @@ test('spec', t => {
}); });
test('stage and provider are null initially', t => { test('stage and provider are null initially', t => {
const cloud = new Cloud(); const runtime = new Runtime();
const cloud = new Cloud(runtime);
t.strictEquals(cloud.provider, null); t.strictEquals(cloud.provider, null);
t.strictEquals(cloud.stage, null); t.strictEquals(cloud.stage, null);
@ -25,7 +30,8 @@ test('stage and provider are null initially', t => {
}); });
test('setProvider sets the provider', t => { test('setProvider sets the provider', t => {
const cloud = new Cloud(); const runtime = new Runtime();
const cloud = new Cloud(runtime);
const provider = { const provider = {
foo: 'a fake provider' foo: 'a fake provider'
@ -37,7 +43,7 @@ test('setProvider sets the provider', t => {
t.end(); t.end();
}); });
test('postData updates the variable', t => { test('postData update message updates the variable', t => {
const stage = new Target(); const stage = new Target();
const fooVar = new Variable( const fooVar = new Variable(
'a fake var id', 'a fake var id',
@ -49,7 +55,8 @@ test('postData updates the variable', t => {
t.strictEquals(fooVar.value, 0); t.strictEquals(fooVar.value, 0);
const cloud = new Cloud(); const runtime = new Runtime();
const cloud = new Cloud(runtime);
cloud.setStage(stage); cloud.setStage(stage);
cloud.postData({varUpdate: { cloud.postData({varUpdate: {
name: 'foo', name: 'foo',
@ -59,6 +66,30 @@ test('postData updates the variable', t => {
t.end(); t.end();
}); });
test('postData create message sets isCloud flag on the variable and updates runtime cloud limit', t => {
const stage = new Target();
const fooVar = new Variable(
'a fake var id',
'foo',
Variable.SCALAR_TYPE,
false /* isCloud */
);
stage.variables[fooVar.id] = fooVar;
t.strictEquals(fooVar.value, 0);
t.strictEquals(fooVar.isCloud, false);
const runtime = new Runtime();
const cloud = new Cloud(runtime);
cloud.setStage(stage);
cloud.postData({varCreate: {
name: 'foo'
}});
t.strictEquals(fooVar.isCloud, true);
t.strictEquals(runtime.hasCloudData(), true);
t.end();
});
test('requestUpdateVariable calls provider\'s updateVariable function', t => { test('requestUpdateVariable calls provider\'s updateVariable function', t => {
let updateVariableCalled = false; let updateVariableCalled = false;
let mockVarName = ''; let mockVarName = '';
@ -74,7 +105,8 @@ test('requestUpdateVariable calls provider\'s updateVariable function', t => {
updateVariable: mockUpdateVariable updateVariable: mockUpdateVariable
}; };
const cloud = new Cloud(); const runtime = new Runtime();
const cloud = new Cloud(runtime);
cloud.setProvider(provider); cloud.setProvider(provider);
cloud.requestUpdateVariable('foo', 3); cloud.requestUpdateVariable('foo', 3);
t.equals(updateVariableCalled, true); t.equals(updateVariableCalled, true);
@ -82,3 +114,31 @@ test('requestUpdateVariable calls provider\'s updateVariable function', t => {
t.strictEquals(mockVarValue, 3); t.strictEquals(mockVarValue, 3);
t.end(); t.end();
}); });
test('requestCreateCloudVariable calls provider\'s createVariable function', t => {
let createVariableCalled = false;
const mockVariable = new Variable('a var id', 'my var', Variable.SCALAR_TYPE, false);
let mockVarName;
let mockVarValue;
const mockCreateVariable = (name, value) => {
createVariableCalled = true;
mockVarName = name;
mockVarValue = value;
return;
};
const provider = {
createVariable: mockCreateVariable
};
const runtime = new Runtime();
const cloud = new Cloud(runtime);
cloud.setProvider(provider);
cloud.requestCreateCloudVariable(mockVariable);
t.equals(createVariableCalled, true);
t.strictEquals(mockVarName, 'my var');
t.strictEquals(mockVarValue, 0);
// Calling requestCreateCloudVariable does not set isCloud flag on variable
t.strictEquals(mockVariable.isCloud, false);
t.end();
});