diff options
author | wilfrem <wilfrem@gmail.com> | 2015-04-20 19:24:50 +0900 |
---|---|---|
committer | James E. King III <jking@apache.org> | 2018-11-29 15:28:44 -0500 |
commit | 2c69b5a01e589c19855a00b68596d0564bb2bb5b (patch) | |
tree | 01225d2f123eb383eff790d9e5a572275dde06f1 | |
parent | 7f0fa6c21ec225aebbbc563607646124279e557c (diff) | |
download | thrift-2c69b5a01e589c19855a00b68596d0564bb2bb5b.tar.gz |
THRIFT-3143: Add nodets support
Co-authored-by: Mustafa Senol Cosar <cosar.mustafasenol@gmail.com>
-rwxr-xr-x | Makefile.am | 2 | ||||
-rw-r--r-- | compiler/cpp/src/thrift/generate/t_js_generator.cc | 151 | ||||
-rwxr-xr-x | configure.ac | 18 | ||||
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/nodets/.gitignore | 1 | ||||
-rwxr-xr-x | lib/nodets/Makefile.am | 45 | ||||
-rw-r--r-- | lib/nodets/coding_standards.md | 1 | ||||
-rw-r--r-- | lib/nodets/test/client.ts | 63 | ||||
-rwxr-xr-x | lib/nodets/test/runClient.sh | 18 | ||||
-rwxr-xr-x | lib/nodets/test/runServer.sh | 20 | ||||
-rw-r--r-- | lib/nodets/test/server.ts | 26 | ||||
-rw-r--r-- | lib/nodets/test/test-cases.ts | 113 | ||||
-rwxr-xr-x | lib/nodets/test/testAll.sh | 38 | ||||
-rw-r--r-- | lib/nodets/test/test_driver.ts | 278 | ||||
-rw-r--r-- | lib/nodets/test/test_handler.ts | 299 | ||||
-rw-r--r-- | lib/nodets/test/tsconfig.json | 22 | ||||
-rw-r--r-- | package-lock.json | 18 | ||||
-rw-r--r-- | package.json | 6 | ||||
-rw-r--r-- | test/ThriftTest.thrift | 2 | ||||
-rw-r--r-- | test/known_failures_Linux.json | 3 | ||||
-rw-r--r-- | test/tests.json | 27 |
21 files changed, 1130 insertions, 22 deletions
diff --git a/Makefile.am b/Makefile.am index cdb8bd2f5..511452dfc 100755 --- a/Makefile.am +++ b/Makefile.am @@ -54,7 +54,7 @@ empty := space := $(empty) $(empty) comma := , -CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_CL@ @MAYBE_D@ @MAYBE_JAVA@ @MAYBE_CSHARP@ @MAYBE_PYTHON@ @MAYBE_PY3@ @MAYBE_RUBY@ @MAYBE_HASKELL@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@ @MAYBE_LUA@ @MAYBE_RS@ @MAYBE_DOTNETCORE@ +CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_CL@ @MAYBE_D@ @MAYBE_JAVA@ @MAYBE_CSHARP@ @MAYBE_PYTHON@ @MAYBE_PY3@ @MAYBE_RUBY@ @MAYBE_HASKELL@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@ @MAYBE_LUA@ @MAYBE_RS@ @MAYBE_DOTNETCORE@ @MAYBE_NODETS@ CROSS_LANGS_COMMA_SEPARATED = $(subst $(space),$(comma),$(CROSS_LANGS)) if WITH_PY3 diff --git a/compiler/cpp/src/thrift/generate/t_js_generator.cc b/compiler/cpp/src/thrift/generate/t_js_generator.cc index 840168ef5..61782b9d8 100644 --- a/compiler/cpp/src/thrift/generate/t_js_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_js_generator.cc @@ -64,7 +64,7 @@ public: bool with_ns_ = false; - for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { if( iter->first.compare("node") == 0) { gen_node_ = true; } else if( iter->first.compare("jquery") == 0) { @@ -80,10 +80,6 @@ public: } } - if (gen_node_ && gen_ts_) { - throw "Invalid switch: [-gen js:node,ts] options not compatible"; - } - if (gen_es6_ && gen_jquery_) { throw "Invalid switch: [-gen js:es6,jquery] options not compatible"; } @@ -203,6 +199,7 @@ public: */ std::string js_includes(); + std::string ts_includes(); std::string render_includes(); std::string declare_field(t_field* tfield, bool init = false, bool obj = false); std::string function_signature(t_function* tfunction, @@ -285,7 +282,7 @@ public: * TypeScript Definition File helper functions */ - string ts_function_signature(t_function* tfunction, bool include_callback); + string ts_function_signature(t_function* tfunction, bool include_callback, bool optional_callback); string ts_get_type(t_type* type); /** @@ -302,11 +299,11 @@ public: string ts_declare() { return (ts_module_.empty() ? "declare " : ""); } /** - * Returns "?" if the given field is optional. + * Returns "?" if the given field is optional or has a default value. * @param t_field The field to check * @return string */ - string ts_get_req(t_field* field) { return (field->get_req() == t_field::T_OPTIONAL ? "?" : ""); } + string ts_get_req(t_field* field) {return (field->get_req() == t_field::T_OPTIONAL || field->get_value() != NULL ? "?" : ""); } /** * Returns the documentation, if the provided documentable object has one. @@ -415,7 +412,7 @@ void t_js_generator::init_generator() { f_types_ << js_includes() << endl << render_includes() << endl; if (gen_ts_) { - f_types_ts_ << autogen_comment() << endl; + f_types_ts_ << autogen_comment() << ts_includes() << endl; } if (gen_node_) { @@ -458,6 +455,20 @@ string t_js_generator::js_includes() { } /** + * Prints standard ts imports + */ +string t_js_generator::ts_includes() { + if (gen_node_) { + return string( + "import thrift = require('thrift');\n" + "import Thrift = thrift.Thrift;\n" + "import Q = thrift.Q;\n"); + } + + return ""; +} + +/** * Renders all the imports necessary for including another Thrift program */ string t_js_generator::render_includes() { @@ -518,8 +529,8 @@ void t_js_generator::generate_enum(t_enum* tenum) { indent_up(); - vector<t_enum_value*> constants = tenum->get_constants(); - vector<t_enum_value*>::iterator c_iter; + vector<t_enum_value*> const& constants = tenum->get_constants(); + vector<t_enum_value*>::const_iterator c_iter; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { int value = (*c_iter)->get_value(); if (gen_ts_) { @@ -720,6 +731,11 @@ void t_js_generator::generate_js_struct_definition(ostream& out, string prefix = has_js_namespace(tstruct->get_program()) ? js_namespace(tstruct->get_program()) : js_const_type_; out << prefix << tstruct->get_name() << (is_exported ? " = module.exports." + tstruct->get_name() : ""); + if (gen_ts_) { + f_types_ts_ << ts_print_doc(tstruct) << ts_indent() << ts_declare() << "class " + << tstruct->get_name() << (is_exception ? " extends Thrift.TException" : "") + << " {" << endl; + } } else { out << js_namespace(tstruct->get_program()) << tstruct->get_name(); if (gen_ts_) { @@ -766,8 +782,14 @@ void t_js_generator::generate_js_struct_definition(ostream& out, out << indent() << dval << ";" << endl; } if (gen_ts_) { - f_types_ts_ << ts_indent() << (*m_iter)->get_name() << ": " - << ts_get_type((*m_iter)->get_type()) << ";" << endl; + if (gen_node_) { + f_types_ts_ << ts_indent() << "public " << (*m_iter)->get_name() << ": " + << ts_get_type((*m_iter)->get_type()) << ";" << endl; + } else { + f_types_ts_ << ts_indent() << (*m_iter)->get_name() << ": " + << ts_get_type((*m_iter)->get_type()) << ";" << endl; + } + } } @@ -1065,6 +1087,39 @@ void t_js_generator::generate_service(t_service* tservice) { << ".d.ts\" />" << endl; } f_service_ts_ << autogen_comment() << endl; + if (gen_node_) { + f_service_ts_ << ts_includes() << endl; + f_service_ts_ << "import ttypes = require('./" + program_->get_name() + "_types');" << endl; + // Generate type aliases + // enum + vector<t_enum*> const& enums = program_->get_enums(); + vector<t_enum*>::const_iterator e_iter; + for (e_iter = enums.begin(); e_iter != enums.end(); ++e_iter) { + f_service_ts_ << "import " << (*e_iter)->get_name() << " = ttypes." + << js_namespace(program_) << (*e_iter)->get_name() << endl; + } + // const + vector<t_const*> const& consts = program_->get_consts(); + vector<t_const*>::const_iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + f_service_ts_ << "import " << (*c_iter)->get_name() << " = ttypes." + << js_namespace(program_) << (*c_iter)->get_name() << endl; + } + // exception + vector<t_struct*> const& exceptions = program_->get_xceptions(); + vector<t_struct*>::const_iterator x_iter; + for (x_iter = exceptions.begin(); x_iter != exceptions.end(); ++x_iter) { + f_service_ts_ << "import " << (*x_iter)->get_name() << " = ttypes." + << js_namespace(program_) << (*x_iter)->get_name() << endl; + } + // structs + vector<t_struct*> const& structs = program_->get_structs(); + vector<t_struct*>::const_iterator s_iter; + for (s_iter = structs.begin(); s_iter != structs.end(); ++s_iter) { + f_service_ts_ << "import " << (*s_iter)->get_name() << " = ttypes." + << js_namespace(program_) << (*s_iter)->get_name() << endl; + } + } if (!ts_module_.empty()) { f_service_ts_ << "declare module " << ts_module_ << " {"; } @@ -1112,6 +1167,18 @@ void t_js_generator::generate_service_processor(t_service* tservice) { if (gen_node_) { string prefix = has_js_namespace(tservice->get_program()) ? js_namespace(tservice->get_program()) : js_const_type_; f_service_ << prefix << service_name_ << "Processor = " << "exports.Processor"; + if (gen_ts_) { + f_service_ts_ << endl << "declare class Processor "; + if (tservice->get_extends() != NULL) { + f_service_ts_ << "extends " << tservice->get_extends()->get_name() << "Processor "; + } + f_service_ts_ << "{" << endl; + indent_up(); + f_service_ts_ << ts_indent() << "private _handler: Object;" << endl << endl; + f_service_ts_ << ts_indent() << "constructor(handler: Object);" << endl; + f_service_ts_ << ts_indent() << "process(input: Thrift.TJSONProtocol, output: Thrift.TJSONProtocol): void;" << endl; + indent_down(); + } } else { f_service_ << js_namespace(tservice->get_program()) << service_name_ << "Processor = " << "exports.Processor"; @@ -1193,6 +1260,9 @@ void t_js_generator::generate_service_processor(t_service* tservice) { indent_down(); indent(f_service_) << "};" << endl; } + if (gen_node_ && gen_ts_) { + f_service_ts_ << "}" << endl; + } } /** @@ -1201,7 +1271,6 @@ void t_js_generator::generate_service_processor(t_service* tservice) { * @param tfunction The function to write a dispatcher for */ void t_js_generator::generate_process_function(t_service* tservice, t_function* tfunction) { - if (gen_es6_) { indent(f_service_) << "process_" + tfunction->get_name() + " (seqid, input, output) {" << endl; } else { @@ -1209,6 +1278,11 @@ void t_js_generator::generate_process_function(t_service* tservice, t_function* << "Processor.prototype.process_" + tfunction->get_name() + " = function(seqid, input, output) {" << endl; } + if (gen_ts_) { + indent_up(); + f_service_ts_ << ts_indent() << "process_" << tfunction->get_name() << "(seqid: number, input: Thrift.TJSONProtocol, output: Thrift.TJSONProtocol): void;" << endl; + indent_down(); + } indent_up(); @@ -1475,6 +1549,14 @@ void t_js_generator::generate_service_client(t_service* tservice) { if (gen_node_) { string prefix = has_js_namespace(tservice->get_program()) ? js_namespace(tservice->get_program()) : js_const_type_; f_service_ << prefix << service_name_ << "Client = " << "exports.Client"; + if (gen_ts_) { + f_service_ts_ << ts_print_doc(tservice) << ts_indent() << ts_declare() << "class " + << "Client "; + if (tservice->get_extends() != NULL) { + f_service_ts_ << "extends " << tservice->get_extends()->get_name() << "Client "; + } + f_service_ts_ << "{" << endl; + } } else { f_service_ << js_namespace(tservice->get_program()) << service_name_ << "Client"; @@ -1517,6 +1599,13 @@ void t_js_generator::generate_service_client(t_service* tservice) { indent(f_service_) << "this.pClass = pClass;" << endl; indent(f_service_) << "this._seqid = 0;" << endl; indent(f_service_) << "this._reqs = {};" << endl; + if (gen_ts_) { + f_service_ts_ << ts_indent() << "private input: Thrift.TJSONProtocol;" << endl << ts_indent() + << "private output: Thrift.TJSONProtocol;" << endl << ts_indent() << "private seqid: number;" + << endl << endl << ts_indent() + << "constructor(input: Thrift.TJSONProtocol, output?: Thrift.TJSONProtocol);" + << endl; + } } else { indent(f_service_) << "this.input = input;" << endl; indent(f_service_) << "this.output = (!output) ? input : output;" << endl; @@ -1585,11 +1674,13 @@ void t_js_generator::generate_service_client(t_service* tservice) { if (gen_ts_) { // function definition without callback - f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, false) << endl; - + f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, false, false) << endl; if (!gen_es6_) { // overload with callback - f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true) << endl; + f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true, false) << endl; + } else { + // overload with callback + f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true, true) << endl; } } @@ -2531,7 +2622,7 @@ string t_js_generator::ts_get_type(t_type* type) { * @param bool in-/exclude the callback argument * @return String of rendered function definition */ -std::string t_js_generator::ts_function_signature(t_function* tfunction, bool include_callback) { +std::string t_js_generator::ts_function_signature(t_function* tfunction, bool include_callback, bool optional_callback) { string str; const vector<t_field*>& fields = tfunction->get_arglist()->get_members(); vector<t_field*>::const_iterator f_iter; @@ -2547,7 +2638,29 @@ std::string t_js_generator::ts_function_signature(t_function* tfunction, bool in } if (include_callback) { - str += "callback: (data: " + ts_get_type(tfunction->get_returntype()) + ")=>void): "; + string callback_optional_string = optional_callback ? "?" : ""; + if (gen_node_) { + t_struct* exceptions = tfunction->get_xceptions(); + string exception_types; + if (exceptions) { + const vector<t_field*>& members = exceptions->get_members(); + for (vector<t_field*>::const_iterator it = members.begin(); it != members.end(); ++it) { + t_type* t = get_true_type((*it)->get_type()); + if (it == members.begin()) { + exception_types = t->get_name(); + } else { + exception_types += " | " + t->get_name(); + } + } + } + if (exception_types == "") { + str += "callback" + callback_optional_string + ": (error: void, response: " + ts_get_type(tfunction->get_returntype()) + ")=>void): "; + } else { + str += "callback" + callback_optional_string + ": (error: " + exception_types + ", response: " + ts_get_type(tfunction->get_returntype()) + ")=>void): "; + } + } else { + str += "callback" + callback_optional_string + ": (data: " + ts_get_type(tfunction->get_returntype()) + ")=>void): "; + } if (gen_jquery_) { str += "JQueryPromise<" + ts_get_type(tfunction->get_returntype()) +">;"; diff --git a/configure.ac b/configure.ac index 14a8c2e0b..aca6f30fd 100755 --- a/configure.ac +++ b/configure.ac @@ -109,6 +109,8 @@ fi AM_EXTRA_RECURSIVE_TARGETS([style]) AC_SUBST(CPPSTYLE_CMD, 'find . -type f \( -iname "*.h" -or -iname "*.cpp" -or -iname "*.cc" -or -iname "*.tcc" \) -printf "Reformatting: %h/%f\n" -exec clang-format -i {} \;') +# ' +# The above comment is to fix editor syntax highlighting AC_ARG_ENABLE([libs], AS_HELP_STRING([--enable-libs], [build the Apache Thrift libraries [default=yes]]), @@ -136,6 +138,7 @@ if test "$enable_libs" = "no"; then with_go="no" with_d="no" with_nodejs="no" + with_nodets="no" with_lua="no" with_rs="no" fi @@ -279,6 +282,18 @@ fi AM_CONDITIONAL(WITH_NODEJS, [test "$have_nodejs" = "yes"]) AM_CONDITIONAL(HAVE_NPM, [test "x$NPM" != "x"]) +AX_THRIFT_LIB(nodets, [Nodets], yes) +have_nodets=no +if test "$with_nodets" = "yes"; then + AC_PATH_PROGS([NODETS], [nodets node]) + AC_PATH_PROG([NPM], [npm]) + if test "x$NODETS" != "x" -a "x$NPM" != "x"; then + have_nodets="yes" + fi +fi +AM_CONDITIONAL(WITH_NODETS, [test "$have_nodets" = "yes"]) +AM_CONDITIONAL(HAVE_NPM, [test "x$NPM" != "x"]) + AX_THRIFT_LIB(lua, [Lua], yes) have_lua=no if test "$with_lua" = "yes"; then @@ -825,6 +840,7 @@ AC_CONFIG_FILES([ lib/json/test/Makefile lib/netcore/Makefile lib/nodejs/Makefile + lib/nodets/Makefile lib/perl/Makefile lib/perl/test/Makefile lib/php/Makefile @@ -905,6 +921,8 @@ if test "$have_go" = "yes" ; then MAYBE_GO="go" ; else MAYBE_GO="" ; fi AC_SUBST([MAYBE_GO]) if test "$have_nodejs" = "yes" ; then MAYBE_NODEJS="nodejs" ; else MAYBE_NODEJS="" ; fi AC_SUBST([MAYBE_NODEJS]) +if test "$have_nodets" = "yes" ; then MAYBE_NODETS="nodets" ; else MAYBE_NODETS="" ; fi +AC_SUBST([MAYBE_NODETS]) if test "$have_erlang" = "yes" ; then MAYBE_ERLANG="erl" ; else MAYBE_ERLANG="" ; fi AC_SUBST([MAYBE_ERLANG]) if test "$have_lua" = "yes" ; then MAYBE_LUA="lua" ; else MAYBE_LUA="" ; fi diff --git a/lib/Makefile.am b/lib/Makefile.am index 0401c99e1..b31560915 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -87,6 +87,7 @@ endif if WITH_NODEJS SUBDIRS += nodejs PRECROSS_TARGET += precross-nodejs +SUBDIRS += nodets endif if WITH_LUA diff --git a/lib/nodets/.gitignore b/lib/nodets/.gitignore new file mode 100644 index 000000000..c7aba8924 --- /dev/null +++ b/lib/nodets/.gitignore @@ -0,0 +1 @@ +test-compiled/ diff --git a/lib/nodets/Makefile.am b/lib/nodets/Makefile.am new file mode 100755 index 000000000..ea640cf0e --- /dev/null +++ b/lib/nodets/Makefile.am @@ -0,0 +1,45 @@ +# 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. + +# We call npm twice to work around npm issues + +stubs: $(top_srcdir)/test/ThriftTest.thrift + mkdir -p test-compiled + $(THRIFT) --gen js:node,ts -o test/ $(top_srcdir)/test/ThriftTest.thrift && $(THRIFT) --gen js:node,ts -o test-compiled $(top_srcdir)/test/ThriftTest.thrift + +ts-compile: stubs + mkdir -p test-compiled + ../../node_modules/typescript/bin/tsc --outDir test-compiled/ --project test/tsconfig.json + +deps: $(top_srcdir)/package.json + $(NPM) install $(top_srcdir)/ || $(NPM) install $(top_srcdir)/ + +all-local: deps ts-compile + +precross: deps stubs ts-compile + +check: deps ts-compile + cd $(top_srcdir) && $(NPM) run test-ts && cd lib/nodets + +clean-local: + $(RM) -r test/gen-nodejs + $(RM) -r $(top_srcdir)/node_modules + $(RM) -r test-compiled + +EXTRA_DIST = \ + test \ + coding_standards.md diff --git a/lib/nodets/coding_standards.md b/lib/nodets/coding_standards.md new file mode 100644 index 000000000..fa0390bb5 --- /dev/null +++ b/lib/nodets/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/lib/nodets/test/client.ts b/lib/nodets/test/client.ts new file mode 100644 index 000000000..eb3db79a8 --- /dev/null +++ b/lib/nodets/test/client.ts @@ -0,0 +1,63 @@ +/* + * 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. + */ + +import assert = require("assert"); +import thrift = require("thrift"); +import Thrift = thrift.Thrift; +import ThriftTest = require("./gen-nodejs/ThriftTest"); +import test_driver = require("./test_driver"); +import ThriftTestDriver = test_driver.ThriftTestDriver; +import ThriftTestDriverPromise = test_driver.ThriftTestDriverPromise; + +// var program = require("commander"); +import * as program from "commander"; + +program + .option("--port <port>", "Set thrift server port number to connect", 9090) + .option("--promise", "test with promise style functions") + .option("--protocol", "Set thrift protocol (binary) [protocol]") + .parse(process.argv); + +var port: number = program.port; +var promise = program.promise; + +var options = { + transport: Thrift.TBufferedTransport, + protocol: Thrift.TBinaryProtocol +}; + +var testDriver = promise ? ThriftTestDriverPromise : ThriftTestDriver; + +var connection = thrift.createConnection("localhost", port, options); + +connection.on("error", function(err: string) { + assert(false, err); +}); + +var client = thrift.createClient(ThriftTest.Client, connection); +runTests(); + +function runTests() { + testDriver(client, function (status: string) { + console.log(status); + connection.end(); + }); +} + +exports.expressoTest = function() {}; diff --git a/lib/nodets/test/runClient.sh b/lib/nodets/test/runClient.sh new file mode 100755 index 000000000..8d5e9a33f --- /dev/null +++ b/lib/nodets/test/runClient.sh @@ -0,0 +1,18 @@ +#! /bin/sh + +DIR="$( cd "$( dirname "$0" )" && pwd )" + +mkdir -p $DIR/../test-compiled + +COMPILEDDIR="$(cd $DIR && cd ../test-compiled && pwd)" +export NODE_PATH="${DIR}:${DIR}/../../nodejs/lib:${NODE_PATH}" + +compile() +{ + #generating thrift code + ${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift + ${DIR}/../../../compiler/cpp/thrift -o ${COMPILEDDIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift +} +compile + +node ${COMPILEDDIR}/client.js $* diff --git a/lib/nodets/test/runServer.sh b/lib/nodets/test/runServer.sh new file mode 100755 index 000000000..4eee92717 --- /dev/null +++ b/lib/nodets/test/runServer.sh @@ -0,0 +1,20 @@ +#! /bin/sh + +DIR="$( cd "$( dirname "$0" )" && pwd )" + +mkdir -p $DIR/../test-compiled + +COMPILEDDIR="$(cd $DIR && cd ../test-compiled && pwd)" +export NODE_PATH="${DIR}:${DIR}/../../nodejs/lib:${NODE_PATH}" + +compile() +{ + #generating thrift code + ${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift + ${DIR}/../../../compiler/cpp/thrift -o ${COMPILEDDIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift +} +compile + +node ${COMPILEDDIR}/server.js $* + + diff --git a/lib/nodets/test/server.ts b/lib/nodets/test/server.ts new file mode 100644 index 000000000..2da53aee2 --- /dev/null +++ b/lib/nodets/test/server.ts @@ -0,0 +1,26 @@ +import thrift = require("thrift"); +var program = require('commander'); +import ThriftTest = require('./gen-nodejs/ThriftTest'); +import test_handler = require('./test_handler'); + + +program + .option('--port <port>', 'Set thrift server port', 9090) + .option('--promise', 'test with promise style functions') + .option('--protocol', '"Set thrift protocol (binary) [protocol]"') + .parse(process.argv); + +var port: number = program.port; + +var options: thrift.ServerOptions = { + transport: thrift.TBufferedTransport, + protocol: thrift.TBinaryProtocol +}; + +var server: thrift.Server; +if (program.promise) { + server = thrift.createServer(ThriftTest.Processor, new test_handler.AsyncThriftTestHandler(), options); +} else { + server = thrift.createServer(ThriftTest.Processor, new test_handler.SyncThriftTestHandler(), options); +} +server.listen(port); diff --git a/lib/nodets/test/test-cases.ts b/lib/nodets/test/test-cases.ts new file mode 100644 index 000000000..ca740ec82 --- /dev/null +++ b/lib/nodets/test/test-cases.ts @@ -0,0 +1,113 @@ +'use strict'; + +import ttypes = require('./gen-nodejs/ThriftTest_types'); + +//all Languages in UTF-8 +/*jshint -W100 */ +export 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ú, 粵語"; +/*jshint +W100 */ + +export 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: ]] \"]] \\" }}}{ [[[ '; + +export var mapTestInput = { + "a":"123", "a b":"with spaces ", "same":"same", "0":"numeric key", + "longValue":stringTest, stringTest:"long key" +}; + +export var simple = [ + ['testVoid', undefined], + ['testString', 'Test'], + ['testString', ''], + ['testString', stringTest], + ['testString', specialCharacters], + ['testByte', 1], + ['testByte', 0], + ['testByte', -1], + ['testByte', -127], + ['testI32', -1], + ['testDouble', -5.2098523], + ['testDouble', 7.012052175215044], + ['testEnum', ttypes.Numberz.ONE] +]; + +export var simpleLoose = [ + ['testI64', 5], + ['testI64', -5], + ['testI64', 734359738368], + ['testI64', -34359738368], + ['testI64', -734359738368], + ['testTypedef', 69] +] + +var mapout: {[key: number]: number; } = {}; +for (var i = 0; i < 5; ++i) { + mapout[i] = i-10; +} + +export var deep = [ + ['testMap', mapout], + ['testSet', [1,2,3]], + ['testList', [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]], + ['testStringMap', mapTestInput] +]; + +export var out = new ttypes.Xtruct({ + string_thing: 'Zero', + byte_thing: 1, + i32_thing: -3, + i64_thing: 1000000 +}); + +export var out2 = new ttypes.Xtruct2(); +out2.byte_thing = 1; +out2.struct_thing = out; +out2.i32_thing = 5; + +export 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 + })] +}); + +export var insanity: any = { + "1":{ "2": crazy, "3": crazy }, + "2":{ "6":{ "userMap":{}, "xtructs":[] } } +}; diff --git a/lib/nodets/test/testAll.sh b/lib/nodets/test/testAll.sh new file mode 100755 index 000000000..a7c00bfd9 --- /dev/null +++ b/lib/nodets/test/testAll.sh @@ -0,0 +1,38 @@ +#! /bin/sh + +DIR="$( cd "$( dirname "$0" )" && pwd )" + +mkdir -p $DIR/../test-compiled + +COMPILEDDIR="$(cd $DIR && cd ../test-compiled && pwd)" +export NODE_PATH="${DIR}:${DIR}/../../nodejs/lib:${NODE_PATH}" + +compile() +{ + #generating thrift code + ${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift + ${DIR}/../../../compiler/cpp/thrift -o ${COMPILEDDIR} --gen js:node,ts ${DIR}/../../../test/ThriftTest.thrift + + tsc --outDir $COMPILEDDIR --project $DIR/tsconfig.json +} +compile + +testServer() +{ + echo "start server $1" + RET=0 + node ${COMPILEDDIR}/server.js $1 & + SERVERPID=$! + sleep 1 + echo "start client $1" + node ${COMPILEDDIR}/client.js $1 || RET=1 + kill -2 $SERVERPID || RET=1 + return $RET +} + +#integration tests + +testServer || TESTOK=1 +testServer --promise || TESTOK=1 + +exit $TESTOK diff --git a/lib/nodets/test/test_driver.ts b/lib/nodets/test/test_driver.ts new file mode 100644 index 000000000..2c4152616 --- /dev/null +++ b/lib/nodets/test/test_driver.ts @@ -0,0 +1,278 @@ +/* + * 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.js 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. + +import test = require("tape"); +import ttypes = require("./gen-nodejs/ThriftTest_types"); +import ThriftTest = require("./gen-nodejs/ThriftTest"); +import thrift = require("thrift"); +import Q = thrift.Q; +import TException = thrift.Thrift.TException; +var Int64 = require("node-int64"); +import testCases = require("./test-cases"); + +export function ThriftTestDriver(client: ThriftTest.Client, callback: (status: string) => void) { + + test("NodeJS Style Callback Client Tests", function(assert) { + + var checkRecursively = makeRecursiveCheck(assert); + + function makeAsserter(assertionFn: (a: any, b: any, msg?: string) => void) { + return function(c: (string | any)[]) { + var fnName = c[0]; + var expected = c[1]; + (<any>client)[fnName](expected, function(err: any, actual: any) { + assert.error(err, fnName + ": no callback error"); + assertionFn(actual, expected, fnName); + }) + }; + } + + testCases.simple.forEach(makeAsserter(assert.equal)); + testCases.simpleLoose.forEach(makeAsserter(function(a, e, m){ + assert.ok(a == e, m); + })); + testCases.deep.forEach(makeAsserter(assert.deepEqual)); + + client.testMapMap(42, function(err, response) { + var expected: typeof response = { + "4": {"1":1, "2":2, "3":3, "4":4}, + "-4": {"-4":-4, "-3":-3, "-2":-2, "-1":-1} + }; + assert.error(err, 'testMapMap: no callback error'); + assert.deepEqual(expected, response, "testMapMap"); + }); + + client.testStruct(testCases.out, function(err, response) { + assert.error(err, "testStruct: no callback error"); + checkRecursively(testCases.out, response, "testStruct"); + }); + + client.testNest(testCases.out2, function(err, response) { + assert.error(err, "testNest: no callback error"); + checkRecursively(testCases.out2, response, "testNest"); + }); + + client.testInsanity(testCases.crazy, function(err, response) { + assert.error(err, "testInsanity: no callback error"); + checkRecursively(testCases.insanity, response, "testInsanity"); + }); + + client.testException("TException", function(err, response) { + assert.ok(err instanceof TException, 'testException: correct error type'); + assert.ok(!Boolean(response), 'testException: no response'); + }); + + client.testException("Xception", function(err, response) { + assert.ok(err instanceof ttypes.Xception, 'testException: correct error type'); + assert.ok(!Boolean(response), 'testException: no response'); + assert.equal(err.errorCode, 1001, 'testException: correct error code'); + assert.equal('Xception', err.message, 'testException: correct error message'); + }); + + client.testException("no Exception", function(err, response) { + assert.error(err, 'testException: no callback error'); + assert.ok(!Boolean(response), 'testException: no response'); + }); + + client.testOneway(0, function(err, response) { + assert.error(err, 'testOneway: no callback error'); + assert.strictEqual(response, undefined, 'testOneway: void response'); + }); + + checkOffByOne(function(done) { + client.testI32(-1, function(err, response) { + assert.error(err, "checkOffByOne: no callback error"); + assert.equal(-1, response); + assert.end(); + done(); + }); + }, callback); + + }); +}; + +export function ThriftTestDriverPromise(client: ThriftTest.Client, callback: (status: string) => void) { + + test("Q Promise Client Tests", function(assert) { + + var checkRecursively = makeRecursiveCheck(assert); + + function fail(msg: string) { + return function(error, response) { + if (error !== null) { + assert.fail(msg); + } + } + } + + function makeAsserter(assertionFn: (a: any, b: any, msg?: string) => void) { + return function(c: (string | any)[]) { + var fnName = c[0]; + var expected = c[1]; + (<any>client)[fnName](expected) + .then(function(actual: any) { + assertionFn(actual, expected, fnName); + }) + .fail(fail("fnName")); + }; + } + + testCases.simple.forEach(makeAsserter(assert.equal)); + testCases.simpleLoose.forEach(makeAsserter(function(a, e, m){ + assert.ok(a == e, m); + })); + testCases.deep.forEach(makeAsserter(assert.deepEqual)); + + Q.resolve(client.testStruct(testCases.out)) + .then(function(response) { + checkRecursively(testCases.out, response, "testStruct"); + }) + .fail(fail("testStruct")); + + Q.resolve(client.testNest(testCases.out2)) + .then(function(response) { + checkRecursively(testCases.out2, response, "testNest"); + }) + .fail(fail("testNest")); + + Q.resolve(client.testInsanity(testCases.crazy)) + .then(function(response) { + checkRecursively(testCases.insanity, response, "testInsanity"); + }) + .fail(fail("testInsanity")); + + Q.resolve(client.testException("TException")) + .then(function(response) { + fail("testException: TException"); + }) + .fail(function(err) { + assert.ok(err instanceof TException); + }); + + Q.resolve(client.testException("Xception")) + .then(function(response) { + fail("testException: Xception"); + }) + .fail(function(err) { + assert.ok(err instanceof ttypes.Xception); + assert.equal(err.errorCode, 1001); + assert.equal("Xception", err.message); + }); + + Q.resolve(client.testException("no Exception")) + .then(function(response) { + assert.equal(undefined, response); //void + }) + .fail(fail("testException")); + + client.testOneway(0, fail("testOneway: should not answer")); + + checkOffByOne(function(done) { + Q.resolve(client.testI32(-1)) + .then(function(response) { + assert.equal(-1, response); + assert.end(); + done(); + }) + .fail(fail("checkOffByOne")); + }, callback); + }); +}; + + +// Helper Functions +// ========================================================= + +function makeRecursiveCheck(assert: test.Test) { + + return function (map1: any, map2: any, msg: string) { + var equal = true; + + var equal = checkRecursively(map1, map2); + + assert.ok(equal, msg); + + // deepEqual doesn't work with fields using node-int64 + function checkRecursively(map1: any, map2: any) : boolean { + if (!(typeof map1 !== "function" && typeof map2 !== "function")) { + return false; + } + if (!map1 || typeof map1 !== "object") { + //Handle int64 types (which use node-int64 in Node.js JavaScript) + if ((typeof map1 === "number") && (typeof map2 === "object") && + (map2.buffer) && (map2.buffer instanceof Buffer) && (map2.buffer.length === 8)) { + var n = new Int64(map2.buffer); + return map1 === n.toNumber(); + } else { + return map1 == map2; + } + } else { + return Object.keys(map1).every(function(key) { + return checkRecursively(map1[key], map2[key]); + }); + } + } + } +} + +function checkOffByOne(done: (callback: () => void) => void, callback: (message: string) => void) { + + var retry_limit = 30; + var retry_interval = 100; + var test_complete = false; + var retrys = 0; + + /** + * 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). + */ + done(function() { + test_complete = true; + }); + + //We wait up to retry_limit * retry_interval for the test suite to complete + function TestForCompletion() { + if(test_complete && 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/lib/nodets/test/test_handler.ts b/lib/nodets/test/test_handler.ts new file mode 100644 index 000000000..1bc855a70 --- /dev/null +++ b/lib/nodets/test/test_handler.ts @@ -0,0 +1,299 @@ +/* + * 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. + +import ttypes = require("./gen-nodejs/ThriftTest_types"); +import thrift = require("thrift"); +import Thrift = thrift.Thrift; +import Q = require("q"); + + +export class SyncThriftTestHandler { + testVoid(): Q.IPromise<void> { + //console.log('testVoid()'); + return Q.resolve<void>(undefined); + } + testMapMap(hello: number) { + //console.log('testMapMap(' + hello + ')'); + + var mapmap: {[key: number]: {[key: number]: number; }} = []; + var pos: {[key: number]: number; } = []; + var neg: {[key: number]: number; } = []; + for (var i = 1; i < 5; i++) { + pos[i] = i; + neg[-i] = -i; + } + mapmap[4] = pos; + mapmap[-4] = neg; + + return Q.resolve(mapmap); + } + testInsanity(argument: ttypes.Insanity): Q.IPromise<{ [k: number]: any; }> { + const first_map: { [k: number]: any; } = []; + const second_map: { [k: number]: any; } = []; + + first_map[ttypes.Numberz.TWO] = argument; + first_map[ttypes.Numberz.THREE] = argument; + + const looney = new ttypes.Insanity(); + second_map[ttypes.Numberz.SIX] = looney; + + const insane: { [k: number]: any; } = []; + insane[1] = first_map; + insane[2] = second_map; + + return Q.resolve(insane); + } + testMulti(arg0: any, arg1: number, arg2: number, arg3: { [k: number]: string; }, arg4: ttypes.Numberz, arg5: number) { + var hello = new ttypes.Xtruct(); + hello.string_thing = 'Hello2'; + hello.byte_thing = arg0; + hello.i32_thing = arg1; + hello.i64_thing = arg2; + return Q.resolve(hello); + } + testException(arg: string): Q.IPromise<void> { + if (arg === 'Xception') { + var x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = arg; + throw x; + } else if (arg === 'TException') { + throw new Thrift.TException(arg); + } else { + return Q.resolve(); + } + } + testMultiException(arg0: string, arg1: string) { + 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 Q.resolve(res); + } + testOneway(sleepFor: number) { + } + + testString(thing: string) { + return Q.resolve(thing); + } + testBool(thing: boolean) { + return Q.resolve(thing); + } + testByte(thing: number) { + return Q.resolve(thing); + } + testI32(thing: number) { + return Q.resolve(thing); + } + testI64(thing: number) { + return Q.resolve(thing); + } + testDouble(thing: number) { + return Q.resolve(thing); + } + testBinary(thing: Buffer) { + return Q.resolve(thing); + } + testStruct(thing: ttypes.Xtruct) { + return Q.resolve(thing); + } + testNest(thing: ttypes.Xtruct2) { + return Q.resolve(thing); + } + testMap(thing: { [k: number]: number; }) { + return Q.resolve(thing); + } + testStringMap(thing: { [k: string]: string; }) { + return Q.resolve(thing); + } + testSet(thing: number[]) { + return Q.resolve(thing); + } + testList(thing: number[]) { + return Q.resolve(thing); + } + testEnum(thing: ttypes.Numberz) { + return Q.resolve(thing); + } + testTypedef(thing: number) { + return Q.resolve(thing); + } +} + +export class AsyncThriftTestHandler { + private syncHandler: SyncThriftTestHandler; + constructor() { + this.syncHandler = new SyncThriftTestHandler(); + } + + testVoid(callback: (result: void) => void): Q.IPromise<void> { + callback(undefined); + return Q.resolve(); + } + testMapMap(hello: number, + callback: (err: any, result: { [k: number]: { [k: number]: number; }; }) => void): + Q.IPromise<{ [k: number]: { [k: number]: number; }; }> { + + var mapmap: {[key: number]: {[key: number]: number; }} = []; + var pos: {[key: number]: number; } = []; + var neg: {[key: number]: number; } = []; + for (var i = 1; i < 5; i++) { + pos[i] = i; + neg[-i] = -i; + } + mapmap[4] = pos; + mapmap[-4] = neg; + + callback(null, mapmap); + return Q.resolve(); + } + testInsanity(argument: ttypes.Insanity, callback?: (err: any, result: { [k: number]: any; }) => void): Q.IPromise<{ [k: number]: any; }> { + const first_map: { [k: number]: any; } = []; + const second_map: { [k: number]: any; } = []; + + first_map[ttypes.Numberz.TWO] = argument; + first_map[ttypes.Numberz.THREE] = argument; + + const looney = new ttypes.Insanity(); + second_map[ttypes.Numberz.SIX] = looney; + + const insane: { [k: number]: any; } = []; + insane[1] = first_map; + insane[2] = second_map; + + if (callback !== undefined){ + callback(null, insane); + } + return Q.resolve(); + } + testMulti(arg0: any, arg1: number, arg2: number, arg3: { [k: number]: string; }, arg4: ttypes.Numberz, arg5: number, result: Function): Q.IPromise<ttypes.Xtruct> { + var hello = this.syncHandler.testMulti(arg0, arg1, arg2, arg3, arg4, arg5); + hello.then(hello => result(null, hello)); + return Q.resolve(); + } + testException(arg: string, result: (err: any) => void): Q.IPromise<void> { + if (arg === 'Xception') { + var x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = arg; + result(x); + } else if (arg === 'TException') { + result(new Thrift.TException(arg)); + } else { + result(null); + } + return Q.resolve(); + } + testMultiException(arg0: string, arg1: string, result: (err: any, res?: ttypes.Xtruct) => void): Q.IPromise<ttypes.Xtruct> { + if (arg0 === ('Xception')) { + var x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = 'This is an Xception'; + result(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'; + result(x2); + } else { + var res = new ttypes.Xtruct(); + res.string_thing = arg1; + result(null, res); + } + return Q.resolve(); + } + testOneway(sleepFor: number, result: Function) { + this.syncHandler.testOneway(sleepFor); + } + testString(thing: string, callback: (err: any, result: string) => void): Q.IPromise<string> { + callback(null, thing); + return Q.resolve(); + } + testByte(thing: number, callback: (err: any, result: number) => void): Q.IPromise<number> { + callback(null, thing); + return Q.resolve(); + } + testBool(thing: boolean, callback: (err: any, result: boolean) => void ): Q.IPromise<boolean> { + callback(null, thing); + return Q.resolve(); + } + testI32(thing: number, callback: (err: any, result: number) => void): Q.IPromise<number> { + callback(null, thing); + return Q.resolve(); + } + testI64(thing: number, callback: (err: any, result: number) => void): Q.IPromise<number> { + callback(null, thing); + return Q.resolve(); + } + testDouble(thing: number, callback: (err: any, result: number) => void): Q.IPromise<number> { + callback(null, thing); + return Q.resolve(); + } + testBinary(thing: Buffer, callback: (err: any, result: Buffer) => void): Q.IPromise<Buffer> { + callback(null, thing); + return Q.resolve(); + } + testStruct(thing: ttypes.Xtruct, callback: (err: any, result: ttypes.Xtruct) => void): Q.IPromise<ttypes.Xtruct> { + callback(null, thing); + return Q.resolve(); + } + testNest(thing: ttypes.Xtruct2, callback: (err: any, result: ttypes.Xtruct2) => void): Q.IPromise<ttypes.Xtruct2> { + callback(null, thing); + return Q.resolve(); + } + testMap(thing: { [k: number]: number; }, callback: (err: any, result: { [k: number]: number; }) => void): Q.IPromise<{ [k: number]: number; }> { + callback(null, thing); + return Q.resolve(); + } + testStringMap(thing: { [k: string]: string; }, callback: (err: any, result: { [k: string]: string; }) => void): Q.IPromise<{ [k: string]: string; }> { + callback(null, thing); + return Q.resolve(); + } + testSet(thing: number[], callback: (err: any, result: number[]) => void): Q.IPromise<number[]> { + callback(null, thing); + return Q.resolve(); + } + testList(thing: number[], callback: (err: any, result: number[]) => void): Q.IPromise<number[]> { + callback(null, thing); + return Q.resolve(); + } + testEnum(thing: ttypes.Numberz, callback: (err: any, result: ttypes.Numberz) => void): Q.IPromise<ttypes.Numberz> { + callback(null, thing); + return Q.resolve(); + } + testTypedef(thing: number, callback: (err: any, result: number) => void): Q.IPromise<number> { + callback(null, thing); + return Q.resolve(); + } +} diff --git a/lib/nodets/test/tsconfig.json b/lib/nodets/test/tsconfig.json new file mode 100644 index 000000000..029d06d96 --- /dev/null +++ b/lib/nodets/test/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "allowJs": false, + "alwaysStrict": true, + "baseUrl": ".", + "declaration": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "module": "commonjs", + "moduleResolution": "node", + "noImplicitThis": true, + "noUnusedLocals": true, + "preserveConstEnums": true, + "removeComments": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "target": "es6", + "paths": { + "thrift": ["../../nodejs/lib/thrift"] + } + } +} diff --git a/package-lock.json b/package-lock.json index ea49547f6..e93b89641 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,18 @@ "js-tokens": "^4.0.0" } }, + "@types/node": { + "version": "10.12.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.6.tgz", + "integrity": "sha512-+ZWB5Ec1iki99xQFzBlivlKxSZQ+fuUKBott8StBOnLN4dWbRHlgdg1XknpW6g0tweniN5DcOqA64CJyOUPSAw==", + "dev": true + }, + "@types/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz", + "integrity": "sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA==", + "dev": true + }, "abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -2008,6 +2020,12 @@ "prelude-ls": "~1.1.2" } }, + "typescript": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", + "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==", + "dev": true + }, "uglify-js": { "version": "3.4.9", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", diff --git a/package.json b/package.json index 07607cdb8..d641979d8 100644 --- a/package.json +++ b/package.json @@ -50,11 +50,15 @@ "jsdoc": "^3.5.5", "prettier": "^1.14.3", "tape": "^4.9.0", - "utf-8-validate": "^4.0.0" + "utf-8-validate": "^4.0.0", + "typescript": "^3.1.6", + "@types/node": "^10.12.6", + "@types/q": "^1.5.1" }, "scripts": { "cover": "lib/nodejs/test/testAll.sh COVER", "test": "lib/nodejs/test/testAll.sh", + "test-ts": "lib/nodets/test/testAll.sh", "prettier": "prettier --write '**/*.js'", "lint": "eslint lib/nodejs/. --ext .js", "lint-tests": "eslint lib/nodejs/test/. --ext .js" diff --git a/test/ThriftTest.thrift b/test/ThriftTest.thrift index bff4e5221..3499ab5f8 100644 --- a/test/ThriftTest.thrift +++ b/test/ThriftTest.thrift @@ -196,7 +196,7 @@ service ThriftTest * @return binary - returns the binary 'thing' */ binary testBinary(1: binary thing), - + /** * Prints 'testStruct("{%s}")' where thing has been formatted into a string of comma separated values * @param Xtruct thing - the Xtruct to print diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json index 24ce997fb..6d3a05e4c 100644 --- a/test/known_failures_Linux.json +++ b/test/known_failures_Linux.json @@ -212,6 +212,7 @@ "d-nodejs_json_framed-ip-ssl", "d-nodejs_json_http-ip", "d-nodejs_json_http-ip-ssl", + "d-nodets_binary_buffered-ip", "d-py3_binary-accel_buffered-ip", "d-py3_binary-accel_buffered-ip-ssl", "d-py3_binary-accel_framed-ip", @@ -278,6 +279,7 @@ "erl-csharp_compact_buffered-ip", "erl-nodejs_binary_buffered-ip", "erl-nodejs_compact_buffered-ip", + "erl-nodets_binary_buffered-ip", "erl-rb_binary-accel_buffered-ip", "erl-rb_binary-accel_buffered-ip-ssl", "erl-rb_binary-accel_framed-ip", @@ -319,6 +321,7 @@ "hs-csharp_binary_framed-ip", "hs-csharp_compact_buffered-ip", "hs-csharp_compact_framed-ip", + "hs-nodets_binary_buffered-ip", "nodejs-cpp_binary_http-domain", "nodejs-cpp_binary_http-ip", "nodejs-cpp_binary_http-ip-ssl", diff --git a/test/tests.json b/test/tests.json index 27e75cc21..b70dbd856 100644 --- a/test/tests.json +++ b/test/tests.json @@ -718,5 +718,32 @@ "multic" ], "workdir": "rs/bin" + }, + { + "name": "nodets", + "env": { + "NODE_PATH": "../lib" + }, + "server": { + "command": [ + "runServer.sh" + ] + }, + "client": { + "timeout": 5, + "command": [ + "runClient.sh" + ] + }, + "protocols": [ + "binary" + ], + "sockets": [ + "ip" + ], + "transports": [ + "buffered" + ], + "workdir": "../lib/nodets/test" } ] |