mirror of
https://github.com/valitydev/thrift.git
synced 2024-11-06 10:25:18 +00:00
Implement episodic compilation for js code generation
This commit is contained in:
parent
96c6113dda
commit
3f0d444791
@ -65,7 +65,7 @@ thrift_SOURCES = src/thrift/audit/t_audit.cpp \
|
||||
src/thrift/parse/t_type.h \
|
||||
src/thrift/parse/t_typedef.cc \
|
||||
src/thrift/parse/t_typedef.h \
|
||||
src/thrift/platform.h
|
||||
src/thrift/platform.h
|
||||
|
||||
# Specific client generator source
|
||||
thrift_SOURCES += src/thrift/generate/t_as3_generator.cc \
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
@ -38,9 +39,11 @@ using std::ostream;
|
||||
using std::ostringstream;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
using std::unordered_map;
|
||||
using std::vector;
|
||||
|
||||
static const string endl = "\n"; // avoid ostream << std::endl flushes
|
||||
static const string episode_file_name = "thrift.js.episode";
|
||||
// largest consecutive integer representable by a double (2 ^ 53 - 1)
|
||||
static const int64_t max_safe_integer = 0x1fffffffffffff;
|
||||
// smallest consecutive number representable by a double (-2 ^ 53 + 1)
|
||||
@ -65,6 +68,7 @@ public:
|
||||
gen_jquery_ = false;
|
||||
gen_ts_ = false;
|
||||
gen_es6_ = false;
|
||||
gen_episode_file_ = false;
|
||||
|
||||
bool with_ns_ = false;
|
||||
|
||||
@ -79,22 +83,26 @@ public:
|
||||
with_ns_ = true;
|
||||
} else if( iter->first.compare("es6") == 0) {
|
||||
gen_es6_ = true;
|
||||
} else if( iter->first.compare("imports") == 0) {
|
||||
parse_imports(program, iter->second);
|
||||
} else if (iter->first.compare("thrift_package_output_directory") == 0) {
|
||||
parse_thrift_package_output_directory(iter->second);
|
||||
} else {
|
||||
throw "unknown option js:" + iter->first;
|
||||
throw std::invalid_argument("unknown option js:" + iter->first);
|
||||
}
|
||||
}
|
||||
|
||||
if (gen_es6_ && gen_jquery_) {
|
||||
throw "Invalid switch: [-gen js:es6,jquery] options not compatible";
|
||||
throw std::invalid_argument("invalid switch: [-gen js:es6,jquery] options not compatible");
|
||||
}
|
||||
|
||||
if (gen_node_ && gen_jquery_) {
|
||||
throw "Invalid switch: [-gen js:node,jquery] options not compatible, try: [-gen js:node -gen "
|
||||
"js:jquery]";
|
||||
throw std::invalid_argument("invalid switch: [-gen js:node,jquery] options not compatible, try: [-gen js:node -gen "
|
||||
"js:jquery]");
|
||||
}
|
||||
|
||||
if (!gen_node_ && with_ns_) {
|
||||
throw "Invalid switch: [-gen js:with_ns] is only valid when using node.js";
|
||||
throw std::invalid_argument("invalid switch: [-gen js:with_ns] is only valid when using node.js");
|
||||
}
|
||||
|
||||
// Depending on the processing flags, we will update these to be ES6 compatible
|
||||
@ -207,6 +215,7 @@ public:
|
||||
std::string ts_service_includes();
|
||||
std::string render_includes();
|
||||
std::string render_ts_includes();
|
||||
std::string get_import_path(t_program* program);
|
||||
std::string declare_field(t_field* tfield, bool init = false, bool obj = false);
|
||||
std::string function_signature(t_function* tfunction,
|
||||
std::string prefix = "",
|
||||
@ -215,6 +224,13 @@ public:
|
||||
std::string type_to_enum(t_type* ttype);
|
||||
std::string make_valid_nodeJs_identifier(std::string const& name);
|
||||
|
||||
/**
|
||||
* Helper parser functions
|
||||
*/
|
||||
|
||||
void parse_imports(t_program* program, const std::string& imports_string);
|
||||
void parse_thrift_package_output_directory(const std::string& thrift_package_output_directory);
|
||||
|
||||
std::string autogen_comment() override {
|
||||
return std::string("//\n") + "// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
|
||||
+ "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
|
||||
@ -353,6 +369,11 @@ private:
|
||||
*/
|
||||
bool gen_es6_;
|
||||
|
||||
/**
|
||||
* True if we will generate an episode file.
|
||||
*/
|
||||
bool gen_episode_file_;
|
||||
|
||||
/**
|
||||
* The name of the defined module(s), for TypeScript Definition Files.
|
||||
*/
|
||||
@ -363,6 +384,21 @@ private:
|
||||
*/
|
||||
bool no_ns_;
|
||||
|
||||
/**
|
||||
* The node modules to use when importing the previously generated files.
|
||||
*/
|
||||
vector<string> imports;
|
||||
|
||||
/**
|
||||
* Cache for imported modules.
|
||||
*/
|
||||
unordered_map<string, string> module_name_2_import_path;
|
||||
|
||||
/**
|
||||
* The prefix to use when generating the episode file.
|
||||
*/
|
||||
string thrift_package_output_directory_;
|
||||
|
||||
/**
|
||||
* The variable decorator for "const" variables. Will default to "var" if in an incompatible language.
|
||||
*/
|
||||
@ -381,6 +417,7 @@ private:
|
||||
/**
|
||||
* File streams
|
||||
*/
|
||||
ofstream_with_content_based_conditional_update f_episode_;
|
||||
ofstream_with_content_based_conditional_update f_types_;
|
||||
ofstream_with_content_based_conditional_update f_service_;
|
||||
ofstream_with_content_based_conditional_update f_types_ts_;
|
||||
@ -397,14 +434,23 @@ void t_js_generator::init_generator() {
|
||||
// Make output directory
|
||||
MKDIR(get_out_dir().c_str());
|
||||
|
||||
string outdir = get_out_dir();
|
||||
const auto outdir = get_out_dir();
|
||||
|
||||
// Make output file(s)
|
||||
string f_types_name = outdir + program_->get_name() + "_types.js";
|
||||
if (gen_episode_file_) {
|
||||
const auto f_episode_file_path = outdir + episode_file_name;
|
||||
f_episode_.open(f_episode_file_path);
|
||||
}
|
||||
|
||||
const auto f_types_name = outdir + program_->get_name() + "_types.js";
|
||||
f_types_.open(f_types_name.c_str());
|
||||
if (gen_episode_file_) {
|
||||
const auto types_module = program_->get_name() + "_types";
|
||||
f_episode_ << types_module << ":" << thrift_package_output_directory_ << "/" << types_module << endl;
|
||||
}
|
||||
|
||||
if (gen_ts_) {
|
||||
string f_types_ts_name = outdir + program_->get_name() + "_types.d.ts";
|
||||
const auto f_types_ts_name = outdir + program_->get_name() + "_types.d.ts";
|
||||
f_types_ts_.open(f_types_ts_name.c_str());
|
||||
}
|
||||
|
||||
@ -500,8 +546,7 @@ string t_js_generator::render_includes() {
|
||||
if (gen_node_) {
|
||||
const vector<t_program*>& includes = program_->get_includes();
|
||||
for (auto include : includes) {
|
||||
result += js_const_type_ + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('./" + include->get_name()
|
||||
+ "_types');\n";
|
||||
result += js_const_type_ + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('" + get_import_path(include) + "');\n";
|
||||
}
|
||||
if (includes.size() > 0) {
|
||||
result += "\n";
|
||||
@ -522,8 +567,7 @@ string t_js_generator::render_ts_includes() {
|
||||
}
|
||||
const vector<t_program*>& includes = program_->get_includes();
|
||||
for (auto include : includes) {
|
||||
result += "import " + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('./" + include->get_name()
|
||||
+ "_types');\n";
|
||||
result += "import " + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('" + get_import_path(include) + "');\n";
|
||||
}
|
||||
if (includes.size() > 0) {
|
||||
result += "\n";
|
||||
@ -532,6 +576,21 @@ string t_js_generator::render_ts_includes() {
|
||||
return result;
|
||||
}
|
||||
|
||||
string t_js_generator::get_import_path(t_program* program) {
|
||||
const string import_file_name(program->get_name() + "_types");
|
||||
if (program->get_recursive()) {
|
||||
return "./" + import_file_name;
|
||||
}
|
||||
|
||||
const string import_file_name_with_extension = import_file_name + ".js";
|
||||
|
||||
auto module_name_and_import_path_iterator = module_name_2_import_path.find(import_file_name);
|
||||
if (module_name_and_import_path_iterator != module_name_2_import_path.end()) {
|
||||
return module_name_and_import_path_iterator->second;
|
||||
}
|
||||
return "./" + import_file_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close up (or down) some filez.
|
||||
*/
|
||||
@ -546,6 +605,9 @@ void t_js_generator::close_generator() {
|
||||
}
|
||||
f_types_ts_.close();
|
||||
}
|
||||
if (gen_episode_file_){
|
||||
f_episode_.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -657,7 +719,7 @@ string t_js_generator::render_const_value(t_type* type, t_const_value* value) {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
|
||||
throw std::runtime_error("compiler error: no const of base type " + t_base_type::t_base_name(tbase));
|
||||
}
|
||||
} else if (type->is_enum()) {
|
||||
out << value->get_integer();
|
||||
@ -676,7 +738,7 @@ string t_js_generator::render_const_value(t_type* type, t_const_value* value) {
|
||||
}
|
||||
}
|
||||
if (field_type == NULL) {
|
||||
throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
|
||||
throw std::runtime_error("type error: " + type->get_name() + " has no field " + v_iter->first->get_string());
|
||||
}
|
||||
if (v_iter != val.begin())
|
||||
out << ",";
|
||||
@ -698,7 +760,7 @@ string t_js_generator::render_const_value(t_type* type, t_const_value* value) {
|
||||
for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
|
||||
if (v_iter != val.begin())
|
||||
out << "," << endl;
|
||||
|
||||
|
||||
if (ktype->is_base_type() && ((t_base_type*)get_true_type(ktype))->get_base() == t_base_type::TYPE_I64){
|
||||
out << indent() << "\"" << v_iter->first->get_integer() << "\"";
|
||||
} else {
|
||||
@ -846,7 +908,6 @@ void t_js_generator::generate_js_struct_definition(ostream& out,
|
||||
f_types_ts_ << ts_indent() << (*m_iter)->get_name() << ": "
|
||||
<< ts_get_type((*m_iter)->get_type()) << ";" << endl;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1124,6 +1185,9 @@ void t_js_generator::generate_js_struct_writer(ostream& out, t_struct* tstruct)
|
||||
void t_js_generator::generate_service(t_service* tservice) {
|
||||
string f_service_name = get_out_dir() + service_name_ + ".js";
|
||||
f_service_.open(f_service_name.c_str());
|
||||
if (gen_episode_file_) {
|
||||
f_episode_ << service_name_ << ":" << thrift_package_output_directory_ << "/" << service_name_ << endl;
|
||||
}
|
||||
|
||||
if (gen_ts_) {
|
||||
string f_service_ts_name = get_out_dir() + service_name_ + ".d.ts";
|
||||
@ -2136,7 +2200,7 @@ void t_js_generator::generate_deserialize_field(ostream& out,
|
||||
t_type* type = get_true_type(tfield->get_type());
|
||||
|
||||
if (type->is_void()) {
|
||||
throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
|
||||
throw std::runtime_error("CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name());
|
||||
}
|
||||
|
||||
string name = prefix + tfield->get_name();
|
||||
@ -2152,7 +2216,7 @@ void t_js_generator::generate_deserialize_field(ostream& out,
|
||||
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
|
||||
switch (tbase) {
|
||||
case t_base_type::TYPE_VOID:
|
||||
throw "compiler error: cannot serialize void field in a struct: " + name;
|
||||
throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name);
|
||||
break;
|
||||
case t_base_type::TYPE_STRING:
|
||||
out << (type->is_binary() ? "readBinary()" : "readString()");
|
||||
@ -2176,7 +2240,7 @@ void t_js_generator::generate_deserialize_field(ostream& out,
|
||||
out << "readDouble()";
|
||||
break;
|
||||
default:
|
||||
throw "compiler error: no JS name for base type " + t_base_type::t_base_name(tbase);
|
||||
throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase));
|
||||
}
|
||||
} else if (type->is_enum()) {
|
||||
out << "readI32()";
|
||||
@ -2317,7 +2381,7 @@ void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, str
|
||||
|
||||
// Do nothing for void types
|
||||
if (type->is_void()) {
|
||||
throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
|
||||
throw std::runtime_error("CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name());
|
||||
}
|
||||
|
||||
if (type->is_struct() || type->is_xception()) {
|
||||
@ -2338,7 +2402,7 @@ void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, str
|
||||
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
|
||||
switch (tbase) {
|
||||
case t_base_type::TYPE_VOID:
|
||||
throw "compiler error: cannot serialize void field in a struct: " + name;
|
||||
throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name);
|
||||
break;
|
||||
case t_base_type::TYPE_STRING:
|
||||
out << (type->is_binary() ? "writeBinary(" : "writeString(") << name << ")";
|
||||
@ -2362,7 +2426,7 @@ void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, str
|
||||
out << "writeDouble(" << name << ")";
|
||||
break;
|
||||
default:
|
||||
throw "compiler error: no JS name for base type " + t_base_type::t_base_name(tbase);
|
||||
throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase));
|
||||
}
|
||||
} else if (type->is_enum()) {
|
||||
out << "writeI32(" << name << ")";
|
||||
@ -2510,7 +2574,7 @@ string t_js_generator::declare_field(t_field* tfield, bool init, bool obj) {
|
||||
result += " = null";
|
||||
break;
|
||||
default:
|
||||
throw "compiler error: no JS initializer for base type " + t_base_type::t_base_name(tbase);
|
||||
throw std::runtime_error("compiler error: no JS initializer for base type " + t_base_type::t_base_name(tbase));
|
||||
}
|
||||
} else if (type->is_enum()) {
|
||||
result += " = null";
|
||||
@ -2589,7 +2653,7 @@ string t_js_generator::type_to_enum(t_type* type) {
|
||||
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
|
||||
switch (tbase) {
|
||||
case t_base_type::TYPE_VOID:
|
||||
throw "NO T_VOID CONSTRUCT";
|
||||
throw std::runtime_error("NO T_VOID CONSTRUCT");
|
||||
case t_base_type::TYPE_STRING:
|
||||
return "Thrift.Type.STRING";
|
||||
case t_base_type::TYPE_BOOL:
|
||||
@ -2617,7 +2681,7 @@ string t_js_generator::type_to_enum(t_type* type) {
|
||||
return "Thrift.Type.LIST";
|
||||
}
|
||||
|
||||
throw "INVALID TYPE IN type_to_enum: " + type->get_name();
|
||||
throw std::runtime_error("INVALID TYPE IN type_to_enum: " + type->get_name());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2785,10 +2849,80 @@ std::string t_js_generator::make_valid_nodeJs_identifier(std::string const& name
|
||||
return str;
|
||||
}
|
||||
|
||||
void t_js_generator::parse_imports(t_program* program, const std::string& imports_string) {
|
||||
if (program->get_recursive()) {
|
||||
throw std::invalid_argument("[-gen js:imports=] option is not usable in recursive code generation mode");
|
||||
}
|
||||
std::stringstream sstream(imports_string);
|
||||
std::string import;
|
||||
while (std::getline(sstream, import, ':')) {
|
||||
imports.emplace_back(import);
|
||||
}
|
||||
if (imports.empty()) {
|
||||
throw std::invalid_argument("invalid usage: [-gen js:imports=] requires at least one path "
|
||||
"(multiple paths are separated by ':')");
|
||||
}
|
||||
for (auto& import : imports) {
|
||||
// Strip trailing '/'
|
||||
if (!import.empty() && import[import.size() - 1] == '/') {
|
||||
import = import.substr(0, import.size() - 1);
|
||||
}
|
||||
if (import.empty()) {
|
||||
throw std::invalid_argument("empty paths are not allowed in imports");
|
||||
}
|
||||
std::ifstream episode_file;
|
||||
string line;
|
||||
const auto episode_file_path = import + "/" + episode_file_name;
|
||||
episode_file.open(episode_file_path);
|
||||
if (!episode_file) {
|
||||
throw std::runtime_error("failed to open the file '" + episode_file_path + "'");
|
||||
}
|
||||
while (std::getline(episode_file, line)) {
|
||||
const auto separator_position = line.find(':');
|
||||
if (separator_position == string::npos) {
|
||||
// could not find the separator in the line
|
||||
throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the line '" + line
|
||||
+ "' does not have a key:value separator ':'");
|
||||
}
|
||||
const auto module_name = line.substr(0, separator_position);
|
||||
const auto import_path = line.substr(separator_position + 1);
|
||||
if (module_name.empty()) {
|
||||
throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the module name is empty");
|
||||
}
|
||||
if (import_path.empty()) {
|
||||
throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the import path is empty");
|
||||
}
|
||||
const auto module_import_path = import.substr(import.find_last_of('/') + 1) + "/" + import_path;
|
||||
const auto result = module_name_2_import_path.emplace(module_name, module_import_path);
|
||||
if (!result.second) {
|
||||
throw std::runtime_error("multiple providers of import path found for " + module_name
|
||||
+ "\n\t" + module_import_path + "\n\t" + result.first->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void t_js_generator::parse_thrift_package_output_directory(const std::string& thrift_package_output_directory) {
|
||||
thrift_package_output_directory_ = thrift_package_output_directory;
|
||||
// Strip trailing '/'
|
||||
if (!thrift_package_output_directory_.empty() && thrift_package_output_directory_[thrift_package_output_directory_.size() - 1] == '/') {
|
||||
thrift_package_output_directory_ = thrift_package_output_directory_.substr(0, thrift_package_output_directory_.size() - 1);
|
||||
}
|
||||
// Check that the thrift_package_output_directory is not empty after stripping
|
||||
if (thrift_package_output_directory_.empty()) {
|
||||
throw std::invalid_argument("the thrift_package_output_directory argument must not be empty");
|
||||
} else {
|
||||
gen_episode_file_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
THRIFT_REGISTER_GENERATOR(js,
|
||||
"Javascript",
|
||||
" jquery: Generate jQuery compatible code.\n"
|
||||
" node: Generate node.js compatible code.\n"
|
||||
" ts: Generate TypeScript definition files.\n"
|
||||
" with_ns: Create global namespace objects when using node.js\n"
|
||||
" es6: Create ES6 code with Promises\n")
|
||||
" es6: Create ES6 code with Promises\n"
|
||||
" thrift_package_output_directory=<path>:\n"
|
||||
" Generate episode file and use the <path> as prefix\n"
|
||||
" imports=<paths_to_modules>:\n"
|
||||
" ':' separated list of paths of modules that has episode files in their root\n")
|
||||
|
@ -982,6 +982,7 @@ void parse(t_program* program, t_program* parent_program) {
|
||||
void generate(t_program* program, const vector<string>& generator_strings) {
|
||||
// Oooohh, recursive code generation, hot!!
|
||||
if (gen_recurse) {
|
||||
program->set_recursive(true);
|
||||
const vector<t_program*>& includes = program->get_includes();
|
||||
for (auto include : includes) {
|
||||
// Propagate output path from parent to child programs
|
||||
@ -1017,6 +1018,8 @@ void generate(t_program* program, const vector<string>& generator_strings) {
|
||||
failure("Error: %s\n", s.c_str());
|
||||
} catch (const char* exc) {
|
||||
failure("Error: %s\n", exc);
|
||||
} catch (const std::invalid_argument& invalid_argument_exception) {
|
||||
failure("Error: %s\n", invalid_argument_exception.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,9 +58,9 @@
|
||||
class t_program : public t_doc {
|
||||
public:
|
||||
t_program(std::string path, std::string name)
|
||||
: path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false), scope_(new t_scope) {}
|
||||
: path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false), scope_(new t_scope), recursive_(false) {}
|
||||
|
||||
t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false) {
|
||||
t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false), recursive_(false) {
|
||||
name_ = program_name(path);
|
||||
scope_ = new t_scope();
|
||||
}
|
||||
@ -355,6 +355,10 @@ public:
|
||||
|
||||
const std::vector<std::string>& get_c_includes() const { return c_includes_; }
|
||||
|
||||
void set_recursive(const bool recursive) { recursive_ = recursive; }
|
||||
|
||||
bool get_recursive() const { return recursive_; }
|
||||
|
||||
private:
|
||||
// File path
|
||||
std::string path_;
|
||||
@ -400,6 +404,9 @@ private:
|
||||
|
||||
// C extra includes
|
||||
std::vector<std::string> c_includes_;
|
||||
|
||||
// Recursive code generation
|
||||
bool recursive_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -34,6 +34,8 @@ check: deps
|
||||
clean-local:
|
||||
$(RM) -r test/gen-*
|
||||
$(RM) -r $(top_srcdir)/node_modules
|
||||
$(RM) -r test/episodic-code-generation-test/gen*
|
||||
$(RM) -r test/episodic-code-generation-test/node_modules
|
||||
|
||||
EXTRA_DIST = \
|
||||
examples \
|
||||
|
77
lib/nodejs/test/episodic-code-generation-test/client.js
Normal file
77
lib/nodejs/test/episodic-code-generation-test/client.js
Normal file
@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const assert = require("assert");
|
||||
const test = require("tape");
|
||||
const thrift = require("thrift");
|
||||
const program = require("commander");
|
||||
|
||||
program
|
||||
.option("--host <host>", "Set the thrift server host to connect", "localhost")
|
||||
.option("--port <port>", "Set the thrift server port number to connect", 9090)
|
||||
.parse(process.argv);
|
||||
|
||||
const Service = require("./gen-2/second-episode/gen-nodejs/Service");
|
||||
const Types = require("types-package/first-episode/Types_types");
|
||||
|
||||
const host = program.host;
|
||||
const port = program.port;
|
||||
|
||||
const options = {
|
||||
transport: thrift.TBufferedTransport,
|
||||
protocol: thrift.TJSONProtocol
|
||||
};
|
||||
|
||||
const connection = thrift.createConnection(host, port, options);
|
||||
const testDriver = function(client, callback) {
|
||||
test("NodeJS episodic compilation client-server test", function(assert) {
|
||||
const type1Object = new Types.Type1();
|
||||
type1Object.number = 42;
|
||||
type1Object.message = "The answer";
|
||||
client.testEpisode(type1Object, function(err, response) {
|
||||
assert.error(err, "no callback error");
|
||||
assert.equal(response.number, type1Object.number + 1);
|
||||
assert.equal(
|
||||
response.message,
|
||||
type1Object.message + " [Hello from the server]"
|
||||
);
|
||||
assert.end();
|
||||
callback("Server successfully tested");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
connection.on("error", function(err) {
|
||||
assert(false, err);
|
||||
});
|
||||
|
||||
const client = thrift.createClient(Service, connection);
|
||||
|
||||
runTests();
|
||||
|
||||
function runTests() {
|
||||
testDriver(client, function(status) {
|
||||
console.log(status);
|
||||
connection.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
exports.expressoTest = function() {};
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "types-package"
|
||||
}
|
50
lib/nodejs/test/episodic-code-generation-test/server.js
Normal file
50
lib/nodejs/test/episodic-code-generation-test/server.js
Normal file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const thrift = require("../../lib/thrift");
|
||||
const program = require("commander");
|
||||
|
||||
program
|
||||
.option("--port <port>", "Set the thrift server port", 9090)
|
||||
.parse(process.argv);
|
||||
|
||||
const Service = require("./gen-2/second-episode/gen-nodejs/Service");
|
||||
const Types = require("types-package/first-episode/Types_types");
|
||||
|
||||
const port = program.port;
|
||||
|
||||
const options = {
|
||||
transport: thrift.TBufferedTransport,
|
||||
protocol: thrift.TJSONProtocol
|
||||
};
|
||||
|
||||
const ServiceHandler = {
|
||||
testEpisode: function(receivedType1Object) {
|
||||
const type1Object = new Types.Type1();
|
||||
type1Object.number = receivedType1Object.number + 1;
|
||||
type1Object.message =
|
||||
receivedType1Object.message + " [Hello from the server]";
|
||||
return type1Object;
|
||||
}
|
||||
};
|
||||
|
||||
const server = thrift.createServer(Service, ServiceHandler, options);
|
||||
server.listen(port);
|
@ -23,6 +23,12 @@ fi
|
||||
|
||||
DIR="$( cd "$( dirname "$0" )" && pwd )"
|
||||
|
||||
EPISODIC_DIR=${DIR}/episodic-code-generation-test
|
||||
|
||||
THRIFT_FILES_DIR=${DIR}/../../../test
|
||||
|
||||
THRIFT_COMPILER=${DIR}/../../../compiler/cpp/thrift
|
||||
|
||||
ISTANBUL="$DIR/../../../node_modules/istanbul/lib/cli.js"
|
||||
|
||||
REPORT_PREFIX="${DIR}/../coverage/report"
|
||||
@ -54,26 +60,68 @@ testServer()
|
||||
return $RET
|
||||
}
|
||||
|
||||
testEpisodicCompilation()
|
||||
{
|
||||
RET=0
|
||||
if [ -n "${COVER}" ]; then
|
||||
${ISTANBUL} cover ${EPISODIC_DIR}/server.js --dir ${REPORT_PREFIX}${COUNT} --handle-sigint &
|
||||
COUNT=$((COUNT+1))
|
||||
else
|
||||
node ${EPISODIC_DIR}/server.js &
|
||||
fi
|
||||
SERVERPID=$!
|
||||
sleep 0.1
|
||||
if [ -n "${COVER}" ]; then
|
||||
${ISTANBUL} cover ${EPISODIC_DIR}/client.js --dir ${REPORT_PREFIX}${COUNT} || RET=1
|
||||
COUNT=$((COUNT+1))
|
||||
else
|
||||
node ${EPISODIC_DIR}/client.js || RET=1
|
||||
fi
|
||||
kill -2 $SERVERPID || RET=1
|
||||
wait $SERVERPID
|
||||
return $RET
|
||||
}
|
||||
|
||||
|
||||
TESTOK=0
|
||||
|
||||
#generating thrift code
|
||||
# generating Thrift code
|
||||
|
||||
${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node ${DIR}/../../../test/ThriftTest.thrift
|
||||
${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node ${DIR}/../../../test/JsDeepConstructorTest.thrift
|
||||
${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node ${DIR}/../../../test/Int64Test.thrift
|
||||
${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/ThriftTest.thrift
|
||||
${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/JsDeepConstructorTest.thrift
|
||||
${THRIFT_COMPILER} -o ${DIR} --gen js:node ${THRIFT_FILES_DIR}/Int64Test.thrift
|
||||
mkdir ${DIR}/gen-nodejs-es6
|
||||
${DIR}/../../../compiler/cpp/thrift -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${DIR}/../../../test/ThriftTest.thrift
|
||||
${DIR}/../../../compiler/cpp/thrift -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${DIR}/../../../test/JsDeepConstructorTest.thrift
|
||||
${DIR}/../../../compiler/cpp/thrift -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${DIR}/../../../test/Int64Test.thrift
|
||||
${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/ThriftTest.thrift
|
||||
${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/JsDeepConstructorTest.thrift
|
||||
${THRIFT_COMPILER} -out ${DIR}/gen-nodejs-es6 --gen js:node,es6 ${THRIFT_FILES_DIR}/Int64Test.thrift
|
||||
|
||||
#unit tests
|
||||
# generate episodic compilation test code
|
||||
TYPES_PACKAGE=${EPISODIC_DIR}/node_modules/types-package
|
||||
|
||||
# generate the first episode
|
||||
mkdir --parents ${EPISODIC_DIR}/gen-1/first-episode
|
||||
${THRIFT_COMPILER} -o ${EPISODIC_DIR}/gen-1/first-episode --gen js:node,thrift_package_output_directory=first-episode ${THRIFT_FILES_DIR}/Types.thrift
|
||||
|
||||
# create a "package" from the first episode and "install" it, the episode file must be at the module root
|
||||
mkdir --parents ${TYPES_PACKAGE}/first-episode
|
||||
cp --force ${EPISODIC_DIR}/episodic_compilation.package.json ${TYPES_PACKAGE}/package.json
|
||||
cp --force ${EPISODIC_DIR}/gen-1/first-episode/gen-nodejs/Types_types.js ${TYPES_PACKAGE}/first-episode/
|
||||
cp --force ${EPISODIC_DIR}/gen-1/first-episode/gen-nodejs/thrift.js.episode ${TYPES_PACKAGE}
|
||||
|
||||
# generate the second episode
|
||||
mkdir --parents ${EPISODIC_DIR}/gen-2/second-episode
|
||||
${THRIFT_COMPILER} -o ${EPISODIC_DIR}/gen-2/second-episode --gen js:node,imports=${TYPES_PACKAGE} ${THRIFT_FILES_DIR}/Service.thrift
|
||||
if [ -f ${EPISODIC_DIR}/gen-2/second-episode/Types_types.js ]; then
|
||||
TESTOK=1
|
||||
fi
|
||||
|
||||
# unit tests
|
||||
|
||||
node ${DIR}/binary.test.js || TESTOK=1
|
||||
node ${DIR}/int64.test.js || TESTOK=1
|
||||
node ${DIR}/deep-constructor.test.js || TESTOK=1
|
||||
|
||||
#integration tests
|
||||
# integration tests
|
||||
|
||||
for type in tcp multiplex websocket http
|
||||
do
|
||||
@ -91,6 +139,8 @@ do
|
||||
done
|
||||
done
|
||||
|
||||
# episodic compilation test
|
||||
testEpisodicCompilation
|
||||
|
||||
if [ -n "${COVER}" ]; then
|
||||
${ISTANBUL} report --dir "${DIR}/../coverage" --include "${DIR}/../coverage/report*/coverage.json" lcov cobertura html
|
||||
|
24
test/Service.thrift
Normal file
24
test/Service.thrift
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
include "Types.thrift"
|
||||
|
||||
service Service {
|
||||
Types.Type1 testEpisode(1:Types.Type1 arg)
|
||||
}
|
23
test/Types.thrift
Normal file
23
test/Types.thrift
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
struct Type1 {
|
||||
1: i32 number,
|
||||
2: string message,
|
||||
}
|
Loading…
Reference in New Issue
Block a user