From 312362314c23ba199fca5c92868f6411afd2fc63 Mon Sep 17 00:00:00 2001 From: henrique Date: Sun, 23 Feb 2014 20:16:44 +0100 Subject: [PATCH] THRIFT-2376 nodejs: allow Promise style calls for client and server patch: Pierre Lamot --- compiler/cpp/src/generate/t_js_generator.cc | 127 ++++-- lib/nodejs/package.json | 1 + lib/nodejs/test/client.js | 11 +- lib/nodejs/test/server.js | 13 +- lib/nodejs/test/testAll.sh | 2 + lib/nodejs/test/test_handler_promise.js | 194 +++++++++ lib/nodejs/test/thrift_test_driver_promise.js | 394 ++++++++++++++++++ tutorial/nodejs/Makefile.am | 11 +- tutorial/nodejs/NodeClient.js | 2 +- tutorial/nodejs/NodeClientPromise.js | 82 ++++ tutorial/nodejs/NodeServerPromise.js | 80 ++++ 11 files changed, 880 insertions(+), 37 deletions(-) create mode 100644 lib/nodejs/test/test_handler_promise.js create mode 100644 lib/nodejs/test/thrift_test_driver_promise.js create mode 100644 tutorial/nodejs/NodeClientPromise.js create mode 100644 tutorial/nodejs/NodeServerPromise.js diff --git a/compiler/cpp/src/generate/t_js_generator.cc b/compiler/cpp/src/generate/t_js_generator.cc index 65a0219e..7d99ab2f 100644 --- a/compiler/cpp/src/generate/t_js_generator.cc +++ b/compiler/cpp/src/generate/t_js_generator.cc @@ -51,12 +51,12 @@ class t_js_generator : public t_oop_generator { const std::string& option_string) : t_oop_generator(program) { (void) option_string; - + std::map::const_iterator iter; - + iter = parsed_options.find("node"); gen_node_ = (iter != parsed_options.end()); - + iter = parsed_options.find("jquery"); gen_jquery_ = (iter != parsed_options.end()); @@ -302,7 +302,7 @@ void t_js_generator::init_generator() { */ string t_js_generator::js_includes() { if (gen_node_) { - return string("var Thrift = require('thrift').Thrift;"); + return string("var Thrift = require('thrift').Thrift;\nvar Q = require('q');"); } string inc; @@ -859,44 +859,83 @@ void t_js_generator::generate_process_function(t_service* tservice, const std::vector& fields = arg_struct->get_members(); vector::const_iterator f_iter; - f_service_ << - indent() << "this._handler." << tfunction->get_name() << "("; + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + indent(f_service_) << "this._handler." << tfunction->get_name() << "("; - bool first = true; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (first) { - first = false; - } else { - f_service_ << ", "; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); } - f_service_ << "args." << (*f_iter)->get_name(); - } - // Shortcut out here for oneway functions - if (tfunction->is_oneway()) { f_service_ << ")" << endl; scope_down(f_service_); f_service_ << endl; return; } - if (!first) { - f_service_ << ", "; + f_service_ << + indent() << "if (this._handler." << tfunction->get_name() << ".length === " << fields.size() <<") {" << endl; + indent_up(); + indent(f_service_) << "Q.fcall(this._handler." << tfunction->get_name(); + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + f_service_ << ", args." << (*f_iter)->get_name(); + } + + f_service_ << ")" << endl; + indent_up(); + indent(f_service_) << ".then(function(result) {" << endl; + indent_up(); + f_service_ << + indent() << "var result = new " << resultname << "({success: result});" << endl << + indent() << "output.writeMessageBegin(\"" << tfunction->get_name() << + "\", Thrift.MessageType.REPLY, seqid);" << endl << + indent() << "result.write(output);" << endl << + indent() << "output.writeMessageEnd();" << endl << + indent() << "output.flush();" << endl; + indent_down(); + indent(f_service_) << "}, function (err) {" << endl; + indent_up(); + f_service_ << + indent() << "var result = new " << resultname << "(err);" << endl << + indent() << "output.writeMessageBegin(\"" << tfunction->get_name() << + "\", Thrift.MessageType.REPLY, seqid);" << endl << + indent() << "result.write(output);" << endl << + indent() << "output.writeMessageEnd();" << endl << + indent() << "output.flush();" << endl; + indent_down(); + indent(f_service_) << "});" << endl; + indent_down(); + indent_down(); + indent(f_service_) << "} else {" << endl; + indent_up(); + indent(f_service_) << "this._handler." << tfunction->get_name() << "("; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + f_service_ << "args." << (*f_iter)->get_name() << ", "; } - f_service_ << "function (err, result) {" << endl; + + f_service_ << " function (err, result) {" << endl; indent_up(); f_service_ << - indent() << "var result = new " << resultname << "((err != null ? err : {success: result}));" << endl << - indent() << "output.writeMessageBegin(\"" << tfunction->get_name() << + indent() << "var result = new " << resultname << "((err != null ? err : {success: result}));" << endl << + indent() << "output.writeMessageBegin(\"" << tfunction->get_name() << "\", Thrift.MessageType.REPLY, seqid);" << endl << - indent() << "result.write(output);" << endl << - indent() << "output.writeMessageEnd();" << endl << - indent() << "output.flush();" << endl; + indent() << "result.write(output);" << endl << + indent() << "output.writeMessageEnd();" << endl << + indent() << "output.flush();" << endl; indent_down(); - indent(f_service_) << "})" << endl; - + indent(f_service_) << "});" << endl; + indent_down(); + indent(f_service_) << "}" << endl; scope_down(f_service_); f_service_ << endl; } @@ -971,7 +1010,7 @@ void t_js_generator::generate_service_client(t_service* tservice) { if (gen_node_) { f_service_ << - js_namespace(tservice->get_program()) << service_name_ << "Client = " << + js_namespace(tservice->get_program()) << service_name_ << "Client = " << "exports.Client = function(output, pClass) {"<= 0.2.4" }, "dependencies": { "node-int64": "~0.3.0", + "q": "1.0.x", "nodeunit": "~0.8.0" }, "devDependencies": { diff --git a/lib/nodejs/test/client.js b/lib/nodejs/test/client.js index 43d88f08..2aa22953 100644 --- a/lib/nodejs/test/client.js +++ b/lib/nodejs/test/client.js @@ -28,6 +28,7 @@ var ThriftTransports = require('thrift/transport'); var ThriftProtocols = require('thrift/protocol'); var ThriftTest = require('./gen-nodejs/ThriftTest'); var ThriftTestDriver = require('./thrift_test_driver').ThriftTestDriver; +var ThriftTestDriverPromise = require('./thrift_test_driver_promise').ThriftTestDriver; var program = require('commander'); @@ -35,10 +36,12 @@ program .option('-p, --protocol ', 'Set thrift protocol (binary|json) [protocol]') .option('-t, --transport ', 'Set thrift transport (buffered|framed) [transport]') .option('--ssl', 'use SSL transport') + .option('--promise', 'test with promise style functions') .parse(process.argv); var protocol = undefined; var transport = undefined; +var testDriver = undefined; if (program.protocol === "binary") { protocol = ThriftProtocols.TBinaryProtocol; @@ -58,6 +61,12 @@ if (program.transport === "framed") { transport = ThriftTransports.TBufferedTransport; } +if (program.promise) { + testDriver = ThriftTestDriverPromise; +} else { + testDriver = ThriftTestDriver; +} + var options = { transport: transport, protocol: protocol @@ -78,7 +87,7 @@ connection.on('error', function(err) { assert(false, err); }); -ThriftTestDriver(client, function (status) { +testDriver(client, function (status) { console.log(status); connection.end(); }); diff --git a/lib/nodejs/test/server.js b/lib/nodejs/test/server.js index 69519ab1..acc03987 100644 --- a/lib/nodejs/test/server.js +++ b/lib/nodejs/test/server.js @@ -29,6 +29,7 @@ var ThriftTransports = require('thrift/transport'); var ThriftProtocols = require('thrift/protocol'); var ThriftTest = require('./gen-nodejs/ThriftTest'); var ThriftTestHandler = require('./test_handler').ThriftTestHandler; +var ThriftTestHandlerPromise = require('./test_handler_promise').ThriftTestHandler; var program = require('commander'); @@ -37,10 +38,12 @@ program .option('-p, --protocol ', 'Set thift protocol (binary|json) [protocol]') .option('-t, --transport ', 'Set thift transport (buffered|framed) [transport]') .option('--ssl', 'use ssl transport') + .option('--promise', 'test with promise style functions') .parse(process.argv); var protocol = undefined; var transport = undefined; +var handler = undefined; if (program.protocol === "binary") { protocol = ThriftProtocols.TBinaryProtocol; @@ -60,6 +63,12 @@ if (program.transport === "framed") { transport = ThriftTransports.TBufferedTransport; } +if (program.promise) { + handler = ThriftTestHandlerPromise; +} else { + handler = ThriftTestHandler; +} + var options = { protocol: protocol, transport: transport @@ -69,9 +78,9 @@ if (program.ssl) { //ssl options options.key = fs.readFileSync(path.resolve(__dirname, 'server.key')); options.cert = fs.readFileSync(path.resolve(__dirname, 'server.crt')); - thrift.createSSLServer(ThriftTest, ThriftTestHandler, options).listen(9090); + thrift.createSSLServer(ThriftTest, handler, options).listen(9090); } else { //default - thrift.createServer(ThriftTest, ThriftTestHandler, options).listen(9090); + thrift.createServer(ThriftTest, handler, options).listen(9090); } diff --git a/lib/nodejs/test/testAll.sh b/lib/nodejs/test/testAll.sh index cdd0c792..96f8a2ab 100755 --- a/lib/nodejs/test/testAll.sh +++ b/lib/nodejs/test/testAll.sh @@ -75,5 +75,7 @@ testMultiplexedClientServer json framed || TESTOK=1 testClientServer binary framed --ssl || TESTOK=1 testMultiplexedClientServer binary framed --ssl || TESTOK=1 +#test promise style +testClientServer binary framed --promise || TESTOK=1 exit $TESTOK diff --git a/lib/nodejs/test/test_handler_promise.js b/lib/nodejs/test/test_handler_promise.js new file mode 100644 index 00000000..fc698ebb --- /dev/null +++ b/lib/nodejs/test/test_handler_promise.js @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * 'License'); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//This is the server side Node test handler for the standard +// Apache Thrift test service. + +var ttypes = require('./gen-nodejs/ThriftTest_types'); +var TException = require('thrift/thrift').TException; + +var ThriftTestHandler = exports.ThriftTestHandler = { + testVoid: function() { + console.log('testVoid()'); + }, + testString: function(thing) { + console.log('testString(\'' + thing + '\')'); + return thing; + }, + testByte: function(thing) { + console.log('testByte(' + thing + ')'); + return thing; + }, + testI32: function(thing) { + console.log('testI32(' + thing + ')'); + return thing; + }, + testI64: function(thing) { + console.log('testI64(' + thing + ')'); + return thing; + }, + testDouble: function(thing) { + console.log('testDouble(' + thing + ')'); + return thing; + }, + testStruct: function(thing) { + console.log('testStruct('); + console.log(thing); + console.log(')'); + return thing; + }, + testNest: function(nest) { + console.log('testNest('); + console.log(nest); + console.log(')'); + return nest; + }, + testMap: function(thing) { + console.log('testMap('); + console.log(thing); + console.log(')'); + return thing; + }, + testStringMap: function(thing) { + console.log('testStringMap('); + console.log(thing); + console.log(')'); + return thing; + }, + testSet: function(thing, result) { + console.log('testSet('); + console.log(thing); + console.log(')'); + return thing; + }, + testList: function(thing) { + console.log('testList('); + console.log(thing); + console.log(')'); + return thing; + }, + testEnum: function(thing) { + console.log('testEnum(' + thing + ')'); + return thing; + }, + testTypedef: function(thing) { + console.log('testTypedef(' + thing + ')'); + return thing; + }, + testMapMap: function(hello) { + console.log('testMapMap(' + hello + ')'); + + var mapmap = []; + var pos = []; + var neg = []; + for (var i = 1; i < 5; i++) { + pos[i] = i; + neg[-i] = -i; + } + mapmap[4] = pos; + mapmap[-4] = neg; + + return mapmap; + }, + testInsanity: function(argument) { + console.log('testInsanity('); + console.log(argument); + console.log(')'); + + var hello = new ttypes.Xtruct(); + hello.string_thing = 'Hello2'; + hello.byte_thing = 2; + hello.i32_thing = 2; + hello.i64_thing = 2; + + var goodbye = new ttypes.Xtruct(); + goodbye.string_thing = 'Goodbye4'; + goodbye.byte_thing = 4; + goodbye.i32_thing = 4; + goodbye.i64_thing = 4; + + var crazy = new ttypes.Insanity(); + crazy.userMap = []; + crazy.userMap[ttypes.Numberz.EIGHT] = 8; + crazy.userMap[ttypes.Numberz.FIVE] = 5; + crazy.xtructs = [goodbye, hello]; + + var first_map = []; + var second_map = []; + + first_map[ttypes.Numberz.TWO] = crazy; + first_map[ttypes.Numberz.THREE] = crazy; + + var looney = new ttypes.Insanity(); + second_map[ttypes.Numberz.SIX] = looney; + + var insane = []; + insane[1] = first_map; + insane[2] = second_map; + + console.log('insane result:'); + console.log(insane); + return insane; + }, + testMulti: function(arg0, arg1, arg2, arg3, arg4, arg5) { + console.log('testMulti()'); + + var hello = new ttypes.Xtruct(); + hello.string_thing = 'Hello2'; + hello.byte_thing = arg0; + hello.i32_thing = arg1; + hello.i64_thing = arg2; + return hello; + }, + testException: function(arg) { + console.log('testException('+arg+')'); + if (arg === 'Xception') { + var x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = arg; + throw x; + } else if (arg === 'TException') { + throw new TException(arg); + } else { + return; + } + }, + testMultiException: function(arg0, arg1) { + console.log('testMultiException(' + arg0 + ', ' + arg1 + ')'); + if (arg0 === ('Xception')) { + var x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = 'This is an Xception'; + throw x; + } else if (arg0 === ('Xception2')) { + var x2 = new ttypes.Xception2(); + x2.errorCode = 2002; + x2.struct_thing = new ttypes.Xtruct(); + x2.struct_thing.string_thing = 'This is an Xception2'; + throw x2; + } + + var res = new ttypes.Xtruct(); + res.string_thing = arg1; + return res; + }, + testOneway: function(sleepFor) { + console.log('testOneway(' + sleepFor + ') => JavaScript (like Rust) never sleeps!'); + } +}; //ThriftTestSvcHandler diff --git a/lib/nodejs/test/thrift_test_driver_promise.js b/lib/nodejs/test/thrift_test_driver_promise.js new file mode 100644 index 00000000..b5c1b87f --- /dev/null +++ b/lib/nodejs/test/thrift_test_driver_promise.js @@ -0,0 +1,394 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * 'License'); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + // This is the Node test driver for the standard Apache Thrift + // test service. The driver invokes every function defined in the + // Thrift Test service with a representative range of parameters. + // + // The ThriftTestDriver function requires a client object + // connected to a server hosting the Thrift Test service and + // supports an optional callback function which is called with + // a status message when the test is complete. + +var assert = require('assert'); +var ttypes = require('./gen-nodejs/ThriftTest_types'); + +var ThriftTestDriver = exports.ThriftTestDriver = function(client, callback) { + +// deepEqual doesn't work with fields using node-int64 +function checkRecursively(map1, map2) { + if (typeof map1 !== 'function' && typeof map2 !== 'function') { + if (!map1 || typeof map1 !== 'object') { + assert.equal(map1, map2); + } else { + for (var key in map1) { + checkRecursively(map1[key], map2[key]); + } + } + } +} + +client.testVoid() + .then(function(response) { + assert.equal(undefined, response); //void + }) + .fail(function() { + assert(false); + }); + + +client.testString("Test") + .then(function(response) { + assert.equal("Test", response); + }) + .fail(function() { + assert(false); + }); + +client.testString("") + .then(function(response) { + assert.equal("", response); + }) + .fail(function() { + assert(false); + }); + +//all Languages in UTF-8 +var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " + + "Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " + + "Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, " + + "বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, " + + "Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, " + + "Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, " + + "Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, " + + "Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, " + + "Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, " + + "Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, " + + "Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, " + + "ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, " + + "Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, " + + "Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa " + + "Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, مازِرونی, Bahasa " + + "Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪" + + "Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad, " + + "Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو, " + + "Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română, " + + "Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple " + + "English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, " + + "Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, " + + "Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, " + + "Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, " + + "Bân-lâm-gú, 粵語"; + +client.testString(stringTest) + .then(function(response) { + assert.equal(stringTest, response); + }) + .fail(function() { + assert(false); + }); + + +var specialCharacters = 'quote: \" backslash:' + + ' forwardslash-escaped: \/ ' + + ' backspace: \b formfeed: \f newline: \n return: \r tab: ' + + ' now-all-of-them-together: "\\\/\b\n\r\t' + + ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><' + + ' char-to-test-json-parsing: ]] \"]] \\" }}}{ [[[ '; +client.testString(specialCharacters) + .then(function(response) { + assert.equal(specialCharacters, response); + }) + .fail(function() { + assert(false); + }); + + +client.testByte(1) + .then(function(response) { + assert.equal(1, response); + }) + .fail(function() { + assert(false); + }); + + +client.testByte(0) + .then(function(response) { + assert.equal(0, response); + }) + .fail(function() { + assert(false); + }); + +client.testByte(-1) + .then(function(response) { + assert.equal(-1, response); + }) + .fail(function() { + assert(false); + }); + +client.testByte(-127) + .then(function(response) { + assert.equal(-127, response); + }) + .fail(function() { + assert(false); + }); + +client.testI32(-1) + .then(function(response) { + assert.equal(-1, response); + }) + .fail(function() { + assert(false); + }); + +client.testI64(5) + .then(function(response) { + assert.equal(5, response); + }) + .fail(function() { + assert(false); + }); + +client.testI64(-5) + .then(function(response) { + assert.equal(-5, response); + }) + .fail(function() { + assert(false); + }); + +client.testI64(-34359738368) + .then(function(response) { + assert.equal(-34359738368, response); + }) + .fail(function() { + assert(false); + }); + +client.testDouble(-5.2098523) + .then(function(response) { + assert.equal(-5.2098523, response); + }) + .fail(function() { + assert(false); + }); + +client.testDouble(7.012052175215044) + .then(function(response) { + assert.equal(7.012052175215044, response); + }) + .fail(function() { + assert(false); + }); + +var out = new ttypes.Xtruct({ + string_thing: 'Zero', + byte_thing: 1, + i32_thing: -3, + i64_thing: 1000000 +}); +client.testStruct(out) + .then(function(response) { + checkRecursively(out, response); + }) + .fail(function() { + assert(false); + }); + +var out2 = new ttypes.Xtruct2(); +out2.byte_thing = 1; +out2.struct_thing = out; +out2.i32_thing = 5; +client.testNest(out2) + .then(function(response) { + checkRecursively(out2, response); + }) + .fail(function() { + assert(false); + }); + +var mapout = {}; +for (var i = 0; i < 5; ++i) { + mapout[i] = i-10; +} +client.testMap(mapout) + .then(function(response) { + assert.deepEqual(mapout, response); + }) + .fail(function() { + assert(false); + }); + +var mapTestInput = { + "a":"123", "a b":"with spaces ", "same":"same", "0":"numeric key", + "longValue":stringTest, stringTest:"long key" +}; +client.testStringMap(mapTestInput) + .then(function(response) { + assert.deepEqual(mapTestInput, response); + }) + .fail(function() { + assert(false); + }); + +var setTestInput = [1,2,3]; +client.testSet(setTestInput) + .then(function(response) { + assert.deepEqual(setTestInput, response); + }) + .fail(function() { + assert(false); + }); +client.testList(setTestInput) + .then(function(response) { + assert.deepEqual(setTestInput, response); + }) + .fail(function() { + assert(false); + }); + +client.testEnum(ttypes.Numberz.ONE) + .then(function(response) { + assert.equal(ttypes.Numberz.ONE, response); + }) + .fail(function() { + assert(false); + }); + +client.testTypedef(69) + .then(function(response) { + assert.equal(69, response); + }) + .fail(function() { + assert(false); + }); + +var mapMapTest = { + "4": {"1":1, "2":2, "3":3, "4":4}, + "-4": {"-4":-4, "-3":-3, "-2":-2, "-1":-1} +}; +client.testMapMap(mapMapTest) + .then(function(response) { + assert.deepEqual(mapMapTest, response); + }) + .fail(function() { + assert(false); + }); + +var crazy = new ttypes.Insanity({ + "userMap":{ "5":5, "8":8 }, + "xtructs":[new ttypes.Xtruct({ + "string_thing":"Goodbye4", + "byte_thing":4, + "i32_thing":4, + "i64_thing":4 + }), new ttypes.Xtruct({ + "string_thing":"Hello2", + "byte_thing":2, + "i32_thing":2, + "i64_thing":2 + })] +}); + +var insanity = { + "1":{ "2": crazy, "3": crazy }, + "2":{ "6":{ "userMap":null, "xtructs":null } } +}; +client.testInsanity(crazy) + .then(function(response) { + checkRecursively(insanity, response); + }) + .fail(function() { + assert(false); + }); + +client.testException('TException') + .then(function() { + assert(false); + }); + +client.testException('Xception') + .then(function(response) { + assert.equal(err.errorCode, 1001); + assert.equal('Xception', err.message) + }) + .fail(function() { + assert(false); + }); + +client.testException('no Exception') + .then(function(response) { + assert.equal(undefined, response); //void + }) + .fail(function() { + assert(false); + }); + +client.testOneway(0, function(error, response) { + assert(false); //should not answer +}); + +(function() { + var test_complete = false; + var retrys = 0; + var retry_limit = 30; + var retry_interval = 100; + /** + * redo a simple test after the oneway to make sure we aren't "off by one" -- + * if the server treated oneway void like normal void, this next test will + * fail since it will get the void confirmation rather than the correct + * result. In this circumstance, the client will throw the exception: + * + * Because this is the last test against the server, when it completes + * the entire suite is complete by definition (the tests run serially). + */ + client.testI32(-1) + .then(function(response) { + assert.equal(-1, response); + test_complete = true + }) + .fail(function() { + assert(false); + }); + +//We wait up to retry_limit * retry_interval for the test suite to complete + function TestForCompletion() { + if(test_complete) { + if (callback) { + callback("Server successfully tested!"); + } + } else { + if (++retrys < retry_limit) { + setTimeout(TestForCompletion, retry_interval); + } else { + if (callback) { + callback("Server test failed to complete after " + + (retry_limit*retry_interval/1000) + " seconds"); + } + } + } + } + + setTimeout(TestForCompletion, retry_interval); +})(); +} diff --git a/tutorial/nodejs/Makefile.am b/tutorial/nodejs/Makefile.am index 2affb9ff..ecf3b2ba 100644 --- a/tutorial/nodejs/Makefile.am +++ b/tutorial/nodejs/Makefile.am @@ -30,9 +30,18 @@ tutorialserver: all tutorialclient: all NODE_PATH="$(top_builddir)/lib/nodejs:$(top_builddir)/lib/nodejs/lib:$(NODEPATH)" $(NODEJS) NodeClient.js +tutorialserver_promise: all + NODE_PATH="$(top_builddir)/lib/nodejs:$(top_builddir)/lib/nodejs/lib:$(NODEPATH)" $(NODEJS) NodeServerPromise.js + +tutorialclient_promise: all + NODE_PATH="$(top_builddir)/lib/nodejs:$(top_builddir)/lib/nodejs/lib:$(NODEPATH)" $(NODEJS) NodeClientPromise.js + + clean-local: $(RM) -r gen-* EXTRA_DIST = \ NodeServer.js \ - NodeClient.js + NodeClient.js \ + NodeServerPromise.js \ + NodeClientPromise.js diff --git a/tutorial/nodejs/NodeClient.js b/tutorial/nodejs/NodeClient.js index 3d09709b..74aa55ab 100644 --- a/tutorial/nodejs/NodeClient.js +++ b/tutorial/nodejs/NodeClient.js @@ -68,7 +68,7 @@ work.num1 = 15; work.num2 = 10; client.calculate(1, work, function(err, message) { - console.log('15-10=' + message.value); + console.log('15-10=' + message); client.getStruct(1, function(err, message){ console.log('Check log: ' + message.value); diff --git a/tutorial/nodejs/NodeClientPromise.js b/tutorial/nodejs/NodeClientPromise.js new file mode 100644 index 00000000..7bcf884e --- /dev/null +++ b/tutorial/nodejs/NodeClientPromise.js @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var thrift = require('thrift'); +var ThriftTransports = require('thrift/transport'); +var ThriftProtocols = require('thrift/protocol'); +var Calculator = require('./gen-nodejs/Calculator'); +var ttypes = require('./gen-nodejs/tutorial_types'); + + +transport = ThriftTransports.TBufferedTransport() +protocol = ThriftProtocols.TBinaryProtocol() + +var connection = thrift.createConnection("localhost", 9090, { + transport : transport, + protocol : protocol +}); + +connection.on('error', function(err) { + assert(false, err); +}); + +// Create a Calculator client with the connection +var client = thrift.createClient(Calculator, connection); + + +client.ping() + .then(function() { + console.log('ping()'); + }); + +client.add(1,1) + .then(function(response) { + console.log("1+1=" + response); + }); + +work = new ttypes.Work(); +work.op = ttypes.Operation.DIVIDE; +work.num1 = 1; +work.num2 = 0; + +client.calculate(1, work) + .then(function(message) { + console.log('Whoa? You know how to divide by zero?'); + }) + .fail(function(err) { + console.log("InvalidOperation " + err); + }); + + +work.op = ttypes.Operation.SUBTRACT; +work.num1 = 15; +work.num2 = 10; + +client.calculate(1, work) + .then(function(value) { + console.log('15-10=' + value); + return client.getStruct(1); + }) + .then(function(message) { + console.log('Check log: ' + message.value); + }) + .fin(function() { + //close the connection once we're done + connection.end(); + }); diff --git a/tutorial/nodejs/NodeServerPromise.js b/tutorial/nodejs/NodeServerPromise.js new file mode 100644 index 00000000..4fd0a352 --- /dev/null +++ b/tutorial/nodejs/NodeServerPromise.js @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var thrift = require("thrift"); +var Calculator = require("./gen-nodejs/Calculator"); +var ttypes = require("./gen-nodejs/tutorial_types"); +var SharedStruct = require("./gen-nodejs/shared_types").SharedStruct; + +var data = {}; + +var server = thrift.createServer(Calculator, { + ping: function() { + console.log("ping()"); + }, + + add: function(n1, n2) { + console.log("add(", n1, ",", n2, ")"); + return n1 + n2; + }, + + calculate: function(logid, work) { + console.log("calculate(", logid, ",", work, ")"); + + var val = 0; + if (work.op == ttypes.Operation.ADD) { + val = work.num1 + work.num2; + } else if (work.op === ttypes.Operation.SUBTRACT) { + val = work.num1 - work.num2; + } else if (work.op === ttypes.Operation.MULTIPLY) { + val = work.num1 * work.num2; + } else if (work.op === ttypes.Operation.DIVIDE) { + if (work.num2 === 0) { + var x = new ttypes.InvalidOperation(); + x.what = work.op; + x.why = 'Cannot divide by 0'; + throw x; + } + val = work.num1 / work.num2; + } else { + var x = new ttypes.InvalidOperation(); + x.what = work.op; + x.why = 'Invalid operation'; + throw x; + } + + var entry = new SharedStruct(); + entry.key = logid; + entry.value = ""+val; + data[logid] = entry; + return val; + }, + + getStruct: function(key) { + console.log("getStruct(", key, ")"); + return data[key]; + }, + + zip: function() { + console.log("zip()"); + } + +}); + +server.listen(9090); -- 2.17.1