Internal code sync (#1140)

This commit is contained in:
Eryu Xia 2021-09-28 10:48:20 -07:00 committed by GitHub
parent 627e33718d
commit f1fe57473f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 281 additions and 282 deletions

View File

@ -3,11 +3,11 @@ ROOT_DIR := $(shell pwd)
all: clean all: clean
plugin: plugin:
cd "$(ROOT_DIR)"/javascript/net/grpc/web && make cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make
install-plugin: install-plugin:
cd "$(ROOT_DIR)"/javascript/net/grpc/web && make install cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make install
clean: clean:
cd "$(ROOT_DIR)"/javascript/net/grpc/web && make clean cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make clean
cd "$(ROOT_DIR)" cd "$(ROOT_DIR)"

View File

@ -63,64 +63,18 @@ enum ImportStyle {
const char GRPC_PROMISE[] = "grpc.web.promise.GrpcWebPromise"; const char GRPC_PROMISE[] = "grpc.web.promise.GrpcWebPromise";
const char* kKeyword[] = { const char* kKeyword[] = {
"abstract", "abstract", "boolean", "break", "byte", "case",
"boolean", "catch", "char", "class", "const", "continue",
"break", "debugger", "default", "delete", "do", "double",
"byte", "else", "enum", "export", "extends", "false",
"case", "final", "finally", "float", "for", "function",
"catch", "goto", "if", "implements", "import", "in",
"char", "instanceof", "int", "interface", "long", "native",
"class", "new", "null", "package", "private", "protected",
"const", "public", "return", "short", "static", "super",
"continue", "switch", "synchronized", "this", "throw", "throws",
"debugger", "transient", "try", "typeof", "var", "void",
"default", "volatile", "while", "with",
"delete",
"do",
"double",
"else",
"enum",
"export",
"extends",
"false",
"final",
"finally",
"float",
"for",
"function",
"goto",
"if",
"implements",
"import",
"in",
"instanceof",
"int",
"interface",
"long",
"native",
"new",
"null",
"package",
"private",
"protected",
"public",
"return",
"short",
"static",
"super",
"switch",
"synchronized",
"this",
"throw",
"throws",
"transient",
"try",
"typeof",
"var",
"void",
"volatile",
"while",
"with",
}; };
bool IsReserved(const string& ident) { bool IsReserved(const string& ident) {
@ -204,10 +158,9 @@ string Uppercase(string s) {
// The following 5 functions were copied from // The following 5 functions were copied from
// google/protobuf/src/google/protobuf/stubs/strutil.h // google/protobuf/src/google/protobuf/stubs/strutil.h
inline bool HasPrefixString(const string& str, inline bool HasPrefixString(const string& str, const string& prefix) {
const string& prefix) {
return str.size() >= prefix.size() && return str.size() >= prefix.size() &&
str.compare(0, prefix.size(), prefix) == 0; str.compare(0, prefix.size(), prefix) == 0;
} }
inline string StripPrefixString(const string& str, const string& prefix) { inline string StripPrefixString(const string& str, const string& prefix) {
@ -218,10 +171,9 @@ inline string StripPrefixString(const string& str, const string& prefix) {
} }
} }
inline bool HasSuffixString(const string& str, inline bool HasSuffixString(const string& str, const string& suffix) {
const string& suffix) {
return str.size() >= suffix.size() && return str.size() >= suffix.size() &&
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
} }
inline string StripSuffixString(const string& str, const string& suffix) { inline string StripSuffixString(const string& str, const string& suffix) {
@ -232,17 +184,15 @@ inline string StripSuffixString(const string& str, const string& suffix) {
} }
} }
void ReplaceCharacters(string *s, const char *remove, char replacewith) { void ReplaceCharacters(string* s, const char* remove, char replacewith) {
const char *str_start = s->c_str(); const char* str_start = s->c_str();
const char *str = str_start; const char* str = str_start;
for (str = strpbrk(str, remove); for (str = strpbrk(str, remove); str != nullptr;
str != nullptr;
str = strpbrk(str + 1, remove)) { str = strpbrk(str + 1, remove)) {
(*s)[str - str_start] = replacewith; (*s)[str - str_start] = replacewith;
} }
} }
// The following function was copied from // The following function was copied from
// google/protobuf/src/google/protobuf/compiler/cpp/cpp_helpers.cc // google/protobuf/src/google/protobuf/compiler/cpp/cpp_helpers.cc
@ -313,7 +263,7 @@ string ModuleAlias(const string& filename) {
return basename + "_pb"; return basename + "_pb";
} }
string JSMessageType(const Descriptor *desc, const FileDescriptor *file) { string JSMessageType(const Descriptor* desc, const FileDescriptor* file) {
string class_name; string class_name;
class_name = StripPrefixString(desc->full_name(), desc->file()->package()); class_name = StripPrefixString(desc->full_name(), desc->file()->package());
if (!class_name.empty() && class_name[0] == '.') { if (!class_name.empty() && class_name[0] == '.') {
@ -327,11 +277,11 @@ string JSMessageType(const Descriptor *desc, const FileDescriptor *file) {
return ModuleAlias(desc->file()->name()) + "." + class_name; return ModuleAlias(desc->file()->name()) + "." + class_name;
} }
string JSMessageType(const Descriptor *desc) { string JSMessageType(const Descriptor* desc) {
return JSMessageType(desc, nullptr); return JSMessageType(desc, nullptr);
} }
string JSElementType(const FieldDescriptor *desc, const FileDescriptor *file) { string JSElementType(const FieldDescriptor* desc, const FileDescriptor* file) {
switch (desc->type()) { switch (desc->type()) {
case FieldDescriptor::TYPE_DOUBLE: case FieldDescriptor::TYPE_DOUBLE:
case FieldDescriptor::TYPE_FLOAT: case FieldDescriptor::TYPE_FLOAT:
@ -367,15 +317,13 @@ string JSElementType(const FieldDescriptor *desc, const FileDescriptor *file) {
// [for protobuf .d.ts files only] Do not add the module prefix for // [for protobuf .d.ts files only] Do not add the module prefix for
// local messages. // local messages.
string enum_name = string enum_name =
StripPrefixString( StripPrefixString(desc->enum_type()->full_name(),
desc->enum_type()->full_name(), desc->enum_type()->file()->package());
desc->enum_type()->file()->package());
return StripPrefixString(enum_name, "."); return StripPrefixString(enum_name, ".");
} }
return ModuleAlias(desc->enum_type()->file()->name()) return ModuleAlias(desc->enum_type()->file()->name()) +
+ StripPrefixString( StripPrefixString(desc->enum_type()->full_name(),
desc->enum_type()->full_name(), desc->enum_type()->file()->package());
desc->enum_type()->file()->package());
case FieldDescriptor::TYPE_MESSAGE: case FieldDescriptor::TYPE_MESSAGE:
return JSMessageType(desc->message_type(), file); return JSMessageType(desc->message_type(), file);
@ -385,22 +333,21 @@ string JSElementType(const FieldDescriptor *desc, const FileDescriptor *file) {
} }
} }
string JSFieldType(const FieldDescriptor *desc, const FileDescriptor *file) { string JSFieldType(const FieldDescriptor* desc, const FileDescriptor* file) {
string js_field_type = JSElementType(desc, file); string js_field_type = JSElementType(desc, file);
if (desc->is_map()) { if (desc->is_map()) {
string key_type = JSFieldType(desc->message_type()->field(0), file); string key_type = JSFieldType(desc->message_type()->field(0), file);
string value_type = JSFieldType(desc->message_type()->field(1), file); string value_type = JSFieldType(desc->message_type()->field(1), file);
return "jspb.Map<" + key_type + ", " + value_type + ">"; return "jspb.Map<" + key_type + ", " + value_type + ">";
} }
if (desc->is_repeated()) if (desc->is_repeated()) {
{
return "Array<" + js_field_type + ">"; return "Array<" + js_field_type + ">";
} }
return js_field_type; return js_field_type;
} }
string AsObjectFieldType( string AsObjectFieldType(const FieldDescriptor* desc,
const FieldDescriptor *desc, const FileDescriptor *file) { const FileDescriptor* file) {
if (desc->type() != FieldDescriptor::TYPE_MESSAGE) { if (desc->type() != FieldDescriptor::TYPE_MESSAGE) {
return JSFieldType(desc, file); return JSFieldType(desc, file);
} }
@ -417,11 +364,11 @@ string AsObjectFieldType(
return field_type; return field_type;
} }
string JSElementName(const FieldDescriptor *desc) { string JSElementName(const FieldDescriptor* desc) {
return ToUpperCamel(ParseLowerUnderscore(desc->name())); return ToUpperCamel(ParseLowerUnderscore(desc->name()));
} }
string JSFieldName(const FieldDescriptor *desc) { string JSFieldName(const FieldDescriptor* desc) {
string js_field_name = JSElementName(desc); string js_field_name = JSElementName(desc);
if (desc->is_map()) { if (desc->is_map()) {
js_field_name += "Map"; js_field_name += "Map";
@ -432,20 +379,17 @@ string JSFieldName(const FieldDescriptor *desc) {
} }
// Like ToUpperCamel except the first letter is not converted. // Like ToUpperCamel except the first letter is not converted.
string ToCamelCase(const std::vector<string>& words) string ToCamelCase(const std::vector<string>& words) {
{
if (words.empty()) { if (words.empty()) {
return ""; return "";
} }
string result = words[0]; string result = words[0];
return result + ToUpperCamel(std::vector<string>( return result + ToUpperCamel(std::vector<string>(
words.begin()+1, words.begin() + 1, words.begin() + words.size()));
words.begin()+words.size()));
} }
// Like JSFieldName, but with first letter not uppercased // Like JSFieldName, but with first letter not uppercased
string CamelCaseJSFieldName(const FieldDescriptor *desc) string CamelCaseJSFieldName(const FieldDescriptor* desc) {
{
string js_field_name = ToCamelCase(ParseLowerUnderscore(desc->name())); string js_field_name = ToCamelCase(ParseLowerUnderscore(desc->name()));
if (desc->is_map()) { if (desc->is_map()) {
js_field_name += "Map"; js_field_name += "Map";
@ -463,8 +407,8 @@ string GetNestedMessageName(const Descriptor* descriptor) {
if (descriptor == nullptr) { if (descriptor == nullptr) {
return ""; return "";
} }
string result = StripPrefixString(descriptor->full_name(), string result =
descriptor->file()->package()); StripPrefixString(descriptor->full_name(), descriptor->file()->package());
// Add a leading dot if one is not already present. // Add a leading dot if one is not already present.
if (!result.empty() && result[0] != '.') { if (!result.empty() && result[0] != '.') {
result = "." + result; result = "." + result;
@ -534,7 +478,7 @@ std::map<string, const Descriptor*> GetAllMessages(const FileDescriptor* file) {
for (int s = 0; s < file->service_count(); ++s) { for (int s = 0; s < file->service_count(); ++s) {
const ServiceDescriptor* service = file->service(s); const ServiceDescriptor* service = file->service(s);
for (int m = 0; m < service->method_count(); ++m) { for (int m = 0; m < service->method_count(); ++m) {
const MethodDescriptor *method = service->method(m); const MethodDescriptor* method = service->method(m);
messages[method->input_type()->full_name()] = method->input_type(); messages[method->input_type()->full_name()] = method->input_type();
messages[method->output_type()->full_name()] = method->output_type(); messages[method->output_type()->full_name()] = method->output_type();
} }
@ -544,12 +488,10 @@ std::map<string, const Descriptor*> GetAllMessages(const FileDescriptor* file) {
} }
void PrintClosureDependencies(Printer* printer, const FileDescriptor* file) { void PrintClosureDependencies(Printer* printer, const FileDescriptor* file) {
for (const auto &entry : GetAllMessages(file)) { for (const auto& entry : GetAllMessages(file)) {
printer->Print( printer->Print("goog.require('proto.$full_name$');\n", "full_name",
"goog.require('proto.$full_name$');\n", entry.second->full_name());
"full_name", entry.second->full_name());
} }
printer->Print("\n\n\n");
} }
void PrintCommonJsMessagesDeps(Printer* printer, const FileDescriptor* file) { void PrintCommonJsMessagesDeps(Printer* printer, const FileDescriptor* file) {
@ -560,9 +502,7 @@ void PrintCommonJsMessagesDeps(Printer* printer, const FileDescriptor* file) {
vars["alias"] = ModuleAlias(name); vars["alias"] = ModuleAlias(name);
vars["dep_filename"] = GetRootPath(file->name(), name) + StripProto(name); vars["dep_filename"] = GetRootPath(file->name(), name) + StripProto(name);
// we need to give each cross-file import an alias // we need to give each cross-file import an alias
printer->Print( printer->Print(vars, "\nvar $alias$ = require('$dep_filename$_pb.js')\n");
vars,
"\nvar $alias$ = require('$dep_filename$_pb.js')\n");
} }
const string& package = file->package(); const string& package = file->package();
@ -587,13 +527,10 @@ void PrintCommonJsMessagesDeps(Printer* printer, const FileDescriptor* file) {
vars["filename"] = GetBasename(StripProto(file->name())); vars["filename"] = GetBasename(StripProto(file->name()));
if (!package.empty()) { if (!package.empty()) {
printer->Print( printer->Print(vars,
vars, "proto.$package_name$ = require('./$filename$_pb.js');\n\n");
"proto.$package_name$ = require('./$filename$_pb.js');\n\n");
} else { } else {
printer->Print( printer->Print(vars, "const proto = require('./$filename$_pb.js');\n\n");
vars,
"const proto = require('./$filename$_pb.js');\n\n");
} }
} }
@ -603,7 +540,7 @@ void PrintES6Imports(Printer* printer, const FileDescriptor* file) {
printer->Print("import * as grpcWeb from 'grpc-web';\n\n"); printer->Print("import * as grpcWeb from 'grpc-web';\n\n");
std::set<string> imports; std::set<string> imports;
for (const auto &entry : GetAllMessages(file)) { for (const auto& entry : GetAllMessages(file)) {
const string& name = entry.second->file()->name(); const string& name = entry.second->file()->name();
string dep_filename = GetRootPath(file->name(), name) + StripProto(name); string dep_filename = GetRootPath(file->name(), name) + StripProto(name);
if (imports.find(dep_filename) != imports.end()) { if (imports.find(dep_filename) != imports.end()) {
@ -611,10 +548,8 @@ void PrintES6Imports(Printer* printer, const FileDescriptor* file) {
} }
imports.insert(dep_filename); imports.insert(dep_filename);
// We need to give each cross-file import an alias. // We need to give each cross-file import an alias.
printer->Print( printer->Print("import * as $alias$ from '$dep_filename$_pb';\n", "alias",
"import * as $alias$ from '$dep_filename$_pb';\n", ModuleAlias(name), "dep_filename", dep_filename);
"alias", ModuleAlias(name),
"dep_filename", dep_filename);
} }
printer->Print("\n\n"); printer->Print("\n\n");
} }
@ -687,13 +622,12 @@ void PrintTypescriptFile(Printer* printer, const FileDescriptor* file,
"metadata?: grpcWeb.Metadata) {\n"); "metadata?: grpcWeb.Metadata) {\n");
printer->Print(vars, "return this.client_.serverStreaming(\n"); printer->Print(vars, "return this.client_.serverStreaming(\n");
printer->Indent(); printer->Indent();
printer->Print( printer->Print(vars,
vars, "this.hostname_ +\n"
"this.hostname_ +\n" " '/$package_dot$$service_name$/$method_name$',\n"
" '/$package_dot$$service_name$/$method_name$',\n" "request,\n"
"request,\n" "metadata || {},\n"
"metadata || {},\n" "this.methodInfo$method_name$);\n");
"this.methodInfo$method_name$);\n");
printer->Outdent(); printer->Outdent();
printer->Outdent(); printer->Outdent();
printer->Print("}\n\n"); printer->Print("}\n\n");
@ -727,14 +661,13 @@ void PrintTypescriptFile(Printer* printer, const FileDescriptor* file,
printer->Indent(); printer->Indent();
printer->Print(vars, "return this.client_.rpcCall(\n"); printer->Print(vars, "return this.client_.rpcCall(\n");
printer->Indent(); printer->Indent();
printer->Print( printer->Print(vars,
vars, "this.hostname_ +\n"
"this.hostname_ +\n" " '/$package_dot$$service_name$/$method_name$',\n"
" '/$package_dot$$service_name$/$method_name$',\n" "request,\n"
"request,\n" "metadata || {},\n"
"metadata || {},\n" "this.methodInfo$method_name$,\n"
"this.methodInfo$method_name$,\n" "callback);\n");
"callback);\n");
printer->Outdent(); printer->Outdent();
printer->Outdent(); printer->Outdent();
printer->Print(vars, printer->Print(vars,
@ -757,7 +690,7 @@ void PrintTypescriptFile(Printer* printer, const FileDescriptor* file,
} }
void PrintGrpcWebDtsClientClass(Printer* printer, const FileDescriptor* file, void PrintGrpcWebDtsClientClass(Printer* printer, const FileDescriptor* file,
const string &client_type) { const string& client_type) {
std::map<string, string> vars; std::map<string, string> vars;
vars["client_type"] = client_type; vars["client_type"] = client_type;
vars["promise"] = "Promise"; vars["promise"] = "Promise";
@ -824,15 +757,13 @@ void PrintGrpcWebDtsFile(Printer* printer, const FileDescriptor* file) {
PrintGrpcWebDtsClientClass(printer, file, "PromiseClient"); PrintGrpcWebDtsClientClass(printer, file, "PromiseClient");
} }
void PrintProtoDtsEnum(Printer *printer, const EnumDescriptor *desc) void PrintProtoDtsEnum(Printer* printer, const EnumDescriptor* desc) {
{
std::map<string, string> vars; std::map<string, string> vars;
vars["enum_name"] = desc->name(); vars["enum_name"] = desc->name();
printer->Print(vars, "export enum $enum_name$ { \n"); printer->Print(vars, "export enum $enum_name$ { \n");
printer->Indent(); printer->Indent();
for (int i = 0; i < desc->value_count(); i++) for (int i = 0; i < desc->value_count(); i++) {
{
vars["value_name"] = Uppercase(desc->value(i)->name()); vars["value_name"] = Uppercase(desc->value(i)->name());
vars["value_number"] = std::to_string(desc->value(i)->number()); vars["value_number"] = std::to_string(desc->value(i)->number());
printer->Print(vars, "$value_name$ = $value_number$,\n"); printer->Print(vars, "$value_name$ = $value_number$,\n");
@ -841,8 +772,7 @@ void PrintProtoDtsEnum(Printer *printer, const EnumDescriptor *desc)
printer->Print("}\n"); printer->Print("}\n");
} }
void PrintProtoDtsOneofCase(Printer *printer, const OneofDescriptor *desc) void PrintProtoDtsOneofCase(Printer* printer, const OneofDescriptor* desc) {
{
std::map<string, string> vars; std::map<string, string> vars;
vars["oneof_name"] = ToUpperCamel(ParseLowerUnderscore(desc->name())); vars["oneof_name"] = ToUpperCamel(ParseLowerUnderscore(desc->name()));
vars["oneof_name_upper"] = Uppercase(desc->name()); vars["oneof_name_upper"] = Uppercase(desc->name());
@ -851,7 +781,7 @@ void PrintProtoDtsOneofCase(Printer *printer, const OneofDescriptor *desc)
printer->Indent(); printer->Indent();
printer->Print(vars, "$oneof_name_upper$_NOT_SET = 0,\n"); printer->Print(vars, "$oneof_name_upper$_NOT_SET = 0,\n");
for (int i = 0; i < desc->field_count(); i++) { for (int i = 0; i < desc->field_count(); i++) {
const FieldDescriptor *field = desc->field(i); const FieldDescriptor* field = desc->field(i);
vars["field_name"] = Uppercase(field->name()); vars["field_name"] = Uppercase(field->name());
vars["field_number"] = std::to_string(field->number()); vars["field_number"] = std::to_string(field->number());
printer->Print(vars, "$field_name$ = $field_number$,\n"); printer->Print(vars, "$field_name$ = $field_number$,\n");
@ -860,8 +790,8 @@ void PrintProtoDtsOneofCase(Printer *printer, const OneofDescriptor *desc)
printer->Print("}\n"); printer->Print("}\n");
} }
void PrintProtoDtsMessage(Printer *printer, const Descriptor *desc, void PrintProtoDtsMessage(Printer* printer, const Descriptor* desc,
const FileDescriptor *file) { const FileDescriptor* file) {
const string& class_name = desc->name(); const string& class_name = desc->name();
std::map<string, string> vars; std::map<string, string> vars;
vars["class_name"] = class_name; vars["class_name"] = class_name;
@ -874,8 +804,7 @@ void PrintProtoDtsMessage(Printer *printer, const Descriptor *desc,
vars["js_field_type"] = JSFieldType(field, file); vars["js_field_type"] = JSFieldType(field, file);
if (field->type() != FieldDescriptor::TYPE_MESSAGE || if (field->type() != FieldDescriptor::TYPE_MESSAGE ||
field->is_repeated()) { field->is_repeated()) {
printer->Print(vars, printer->Print(vars, "get$js_field_name$(): $js_field_type$;\n");
"get$js_field_name$(): $js_field_type$;\n");
} else { } else {
printer->Print(vars, printer->Print(vars,
"get$js_field_name$(): $js_field_type$ | undefined;\n"); "get$js_field_name$(): $js_field_type$ | undefined;\n");
@ -895,8 +824,8 @@ void PrintProtoDtsMessage(Printer *printer, const Descriptor *desc,
"set$js_field_name$(value?: $js_field_type$): " "set$js_field_name$(value?: $js_field_type$): "
"$class_name$;\n"); "$class_name$;\n");
} }
if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_repeated() if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
&& !field->is_map()) { !field->is_repeated() && !field->is_map()) {
printer->Print(vars, "has$js_field_name$(): boolean;\n"); printer->Print(vars, "has$js_field_name$(): boolean;\n");
} }
if (field->type() == FieldDescriptor::TYPE_MESSAGE || if (field->type() == FieldDescriptor::TYPE_MESSAGE ||
@ -924,8 +853,7 @@ void PrintProtoDtsMessage(Printer *printer, const Descriptor *desc,
const OneofDescriptor* oneof = desc->oneof_decl(i); const OneofDescriptor* oneof = desc->oneof_decl(i);
vars["js_oneof_name"] = ToUpperCamel(ParseLowerUnderscore(oneof->name())); vars["js_oneof_name"] = ToUpperCamel(ParseLowerUnderscore(oneof->name()));
printer->Print( printer->Print(
vars, vars, "get$js_oneof_name$Case(): $class_name$.$js_oneof_name$Case;\n");
"get$js_oneof_name$Case(): $class_name$.$js_oneof_name$Case;\n");
printer->Print("\n"); printer->Print("\n");
} }
@ -988,17 +916,15 @@ void PrintProtoDtsMessage(Printer *printer, const Descriptor *desc,
printer->Print("}\n\n"); printer->Print("}\n\n");
} }
void PrintProtoDtsFile(Printer *printer, const FileDescriptor *file) void PrintProtoDtsFile(Printer* printer, const FileDescriptor* file) {
{
printer->Print("import * as jspb from 'google-protobuf'\n\n"); printer->Print("import * as jspb from 'google-protobuf'\n\n");
for (int i = 0; i < file->dependency_count(); i++) { for (int i = 0; i < file->dependency_count(); i++) {
const string& name = file->dependency(i)->name(); const string& name = file->dependency(i)->name();
// We need to give each cross-file import an alias. // We need to give each cross-file import an alias.
printer->Print( printer->Print("import * as $alias$ from '$dep_filename$_pb';\n", "alias",
"import * as $alias$ from '$dep_filename$_pb';\n", ModuleAlias(name), "dep_filename",
"alias", ModuleAlias(name), GetRootPath(file->name(), name) + StripProto(name));
"dep_filename", GetRootPath(file->name(), name) + StripProto(name));
} }
printer->Print("\n\n"); printer->Print("\n\n");
@ -1080,8 +1006,9 @@ void PrintMethodDescriptorFile(Printer* printer,
" * @param {!proto.$in$} request\n"); " * @param {!proto.$in$} request\n");
printer->Print( printer->Print(
(" * @return {" + GetSerializeMethodReturnType(vars) + "}\n").c_str()); (" * @return {" + GetSerializeMethodReturnType(vars) + "}\n").c_str());
printer->Print(" */\n" printer->Print(
"function(request) {\n"); " */\n"
"function(request) {\n");
printer->Print( printer->Print(
(" return request." + GetSerializeMethodName(vars) + "();\n").c_str()); (" return request." + GetSerializeMethodName(vars) + "();\n").c_str());
printer->Print("},\n"); printer->Print("},\n");
@ -1102,7 +1029,7 @@ void PrintServiceConstructor(Printer* printer, std::map<string, string> vars,
"/**\n" "/**\n"
" * @param {string} hostname\n" " * @param {string} hostname\n"
" * @param {?Object} credentials\n" " * @param {?Object} credentials\n"
" * @param {?Object} options\n" " * @param {?grpc.web.ClientOptions} options\n"
" * @constructor\n" " * @constructor\n"
" * @struct\n" " * @struct\n"
" * @final\n" " * @final\n"
@ -1111,7 +1038,7 @@ void PrintServiceConstructor(Printer* printer, std::map<string, string> vars,
" function(hostname, credentials, options) {\n" " function(hostname, credentials, options) {\n"
" if (!options) options = {};\n"); " if (!options) options = {};\n");
if (vars["mode"] == GetModeVar(Mode::GRPCWEB)) { if (vars["mode"] == GetModeVar(Mode::GRPCWEB)) {
printer->Print(vars, " options['format'] = '$format$';\n\n"); printer->Print(vars, " options.format = '$format$';\n\n");
} }
if (vars["mode"] == GetModeVar(Mode::OP)) { if (vars["mode"] == GetModeVar(Mode::OP)) {
printer->Print( printer->Print(
@ -1213,7 +1140,7 @@ void PrintPromiseUnaryCall(Printer* printer, std::map<string, string> vars) {
"/**\n" "/**\n"
" * @param {!proto.$in$} request The\n" " * @param {!proto.$in$} request The\n"
" * request proto\n" " * request proto\n"
" * @param {?Object<string, string>} metadata User defined\n" " * @param {?Object<string, string>=} metadata User defined\n"
" * call metadata\n" " * call metadata\n"
" * @return {!$promise$<!proto.$out$>}\n" " * @return {!$promise$<!proto.$out$>}\n"
" * Promise that resolves to the response\n" " * Promise that resolves to the response\n"
@ -1243,17 +1170,16 @@ void PrintPromiseUnaryCall(Printer* printer, std::map<string, string> vars) {
} }
void PrintServerStreamingCall(Printer* printer, std::map<string, string> vars) { void PrintServerStreamingCall(Printer* printer, std::map<string, string> vars) {
printer->Print( printer->Print(vars,
vars, "/**\n"
"/**\n" " * @param {!proto.$in$} request The request proto\n"
" * @param {!proto.$in$} request The request proto\n" " * @param {?Object<string, string>=} metadata User defined\n"
" * @param {?Object<string, string>} metadata User defined\n" " * call metadata\n"
" * call metadata\n" " * @return {!grpc.web.ClientReadableStream<!proto.$out$>}\n"
" * @return {!grpc.web.ClientReadableStream<!proto.$out$>}\n" " * The XHR Node Readable Stream\n"
" * The XHR Node Readable Stream\n" " */\n"
" */\n" "proto.$package_dot$$service_name$$client_type$.prototype."
"proto.$package_dot$$service_name$$client_type$.prototype." "$js_method_name$ =\n");
"$js_method_name$ =\n");
printer->Indent(); printer->Indent();
printer->Print( printer->Print(
" function(request, metadata) {\n" " function(request, metadata) {\n"
@ -1359,6 +1285,11 @@ void PrintMultipleFilesMode(const FileDescriptor* file, string file_name,
PrintClosureDependencies(&printer1, file); PrintClosureDependencies(&printer1, file);
PrintClosureDependencies(&printer2, file); PrintClosureDependencies(&printer2, file);
printer1.Print(vars, "\ngoog.requireType('grpc.web.ClientOptions');\n");
printer2.Print(vars, "\ngoog.requireType('grpc.web.ClientOptions');\n");
printer1.Print("\n\n\n");
printer2.Print("\n\n\n");
printer1.Print("goog.scope(function() {\n\n"); printer1.Print("goog.scope(function() {\n\n");
printer2.Print("goog.scope(function() {\n\n"); printer2.Print("goog.scope(function() {\n\n");
@ -1403,20 +1334,20 @@ void PrintMultipleFilesMode(const FileDescriptor* file, string file_name,
printer2.Print("}); // goog.scope\n\n"); printer2.Print("}); // goog.scope\n\n");
} }
void PrintClosureES6Imports( void PrintClosureES6Imports(Printer* printer, const FileDescriptor* file,
Printer* printer, const FileDescriptor* file, string package_dot) { string package_dot) {
for (int i = 0; i < file->service_count(); ++i) { for (int i = 0; i < file->service_count(); ++i) {
const ServiceDescriptor* service = file->service(i); const ServiceDescriptor* service = file->service(i);
string service_namespace = "proto." + package_dot + service->name(); string service_namespace = "proto." + package_dot + service->name();
printer->Print( printer->Print(
"import $service_name$Client_import from 'goog:$namespace$';\n", "import $service_name$Client_import from 'goog:$namespace$';\n",
"service_name", service->name(), "service_name", service->name(), "namespace",
"namespace", service_namespace + "Client"); service_namespace + "Client");
printer->Print( printer->Print(
"import $service_name$PromiseClient_import from 'goog:$namespace$';\n", "import $service_name$PromiseClient_import from 'goog:$namespace$';\n",
"service_name", service->name(), "service_name", service->name(), "namespace",
"namespace", service_namespace + "PromiseClient"); service_namespace + "PromiseClient");
} }
printer->Print("\n\n\n"); printer->Print("\n\n\n");
@ -1441,9 +1372,8 @@ void PrintGrpcWebClosureES6File(Printer* printer, const FileDescriptor* file) {
const ServiceDescriptor* service = file->service(i); const ServiceDescriptor* service = file->service(i);
string service_namespace = "proto." + package_dot + service->name(); string service_namespace = "proto." + package_dot + service->name();
printer->Print( printer->Print("export const $name$Client = $name$Client_import;\n", "name",
"export const $name$Client = $name$Client_import;\n", service->name());
"name", service->name());
printer->Print( printer->Print(
"export const $name$PromiseClient = $name$PromiseClient_import;\n", "export const $name$PromiseClient = $name$PromiseClient_import;\n",
"name", service->name()); "name", service->name());
@ -1664,7 +1594,8 @@ class GrpcCodeGenerator : public CodeGenerator {
printer.Print(vars, "goog.require('grpc.web.RpcError');\n"); printer.Print(vars, "goog.require('grpc.web.RpcError');\n");
PrintClosureDependencies(&printer, file); PrintClosureDependencies(&printer, file);
printer.Print(vars, "\ngoog.requireType('grpc.web.ClientOptions');\n");
printer.Print("\n\n\n");
printer.Print("goog.scope(function() {\n\n"); printer.Print("goog.scope(function() {\n\n");
break; break;
case ImportStyle::COMMONJS: case ImportStyle::COMMONJS:

View File

@ -1,41 +0,0 @@
/**
* @fileoverview base interface for grpc web GenericClient.
*/
goog.module('grpc.web.GenericClient');
goog.module.declareLegacyNamespace();
const MethodDescriptor = goog.require('grpc.web.MethodDescriptor');
const Request = goog.require('grpc.web.Request');
const UnaryResponse = goog.require('grpc.web.UnaryResponse');
/**
* @interface
*/
const GenericClient = function() {};
/**
* @param {!Request<REQUEST, RESPONSE>} request The wrapped gRPC-Web request
* @return {!Promise<!UnaryResponse<RESPONSE>>} A promise that resolves to the
* response message and metadata
* @template REQUEST, RESPONSE
* @abstract
*/
GenericClient.prototype.unaryCall = function(request) {};
/**
* Simplified version of GenericClient.prototype.unaryCall. Users are expected
* to use this method if they don't have the need to customize metadata and
* callOptions .
* @param {!REQUEST} requestMessage The request message
* @param {!MethodDescriptor<REQUEST, RESPONSE>} methodDescriptor Information of
* this RPC method
* @return {!Promise<RESPONSE>} A promise that resolves to the
* response message
* @template REQUEST, RESPONSE
* @abstract
*/
GenericClient.prototype.call = function(requestMessage, methodDescriptor) {};
exports = GenericClient;

View File

@ -42,6 +42,7 @@ const XhrIo = goog.require('goog.net.XhrIo');
const googCrypt = goog.require('goog.crypt.base64'); const googCrypt = goog.require('goog.crypt.base64');
const {Status} = goog.require('grpc.web.Status'); const {Status} = goog.require('grpc.web.Status');
const {StreamInterceptor, UnaryInterceptor} = goog.require('grpc.web.Interceptor'); const {StreamInterceptor, UnaryInterceptor} = goog.require('grpc.web.Interceptor');
const {toObject} = goog.require('goog.collections.maps');
@ -193,10 +194,13 @@ class GrpcWebClientBase {
stream.setResponseDeserializeFn( stream.setResponseDeserializeFn(
methodDescriptor.getResponseDeserializeFn()); methodDescriptor.getResponseDeserializeFn());
xhr.headers.addAll(request.getMetadata()); const metadata = request.getMetadata();
for(const key in metadata) {
xhr.headers.set(key, metadata[key]);
}
this.processHeaders_(xhr); this.processHeaders_(xhr);
if (this.suppressCorsPreflight_) { if (this.suppressCorsPreflight_) {
const headerObject = xhr.headers.toObject(); const headerObject = toObject(xhr.headers);
xhr.headers.clear(); xhr.headers.clear();
path = GrpcWebClientBase.setCorsOverride_(path, headerObject); path = GrpcWebClientBase.setCorsOverride_(path, headerObject);
} }
@ -310,11 +314,11 @@ class GrpcWebClientBase {
} }
xhr.headers.set('X-User-Agent', 'grpc-web-javascript/0.1'); xhr.headers.set('X-User-Agent', 'grpc-web-javascript/0.1');
xhr.headers.set('X-Grpc-Web', '1'); xhr.headers.set('X-Grpc-Web', '1');
if (xhr.headers.containsKey('deadline')) { if (xhr.headers.has('deadline')) {
const deadline = xhr.headers.get('deadline'); // in ms const deadline = Number(xhr.headers.get('deadline')); // in ms
const currentTime = (new Date()).getTime(); const currentTime = (new Date()).getTime();
let timeout = Math.round(deadline - currentTime); let timeout = Math.round(deadline - currentTime);
xhr.headers.remove('deadline'); xhr.headers.delete('deadline');
if (timeout === Infinity) { if (timeout === Infinity) {
// grpc-timeout header defaults to infinity if not set. // grpc-timeout header defaults to infinity if not set.
timeout = 0; timeout = 0;

View File

@ -24,12 +24,17 @@ const MethodDescriptor = goog.require('grpc.web.MethodDescriptor');
const ReadyState = goog.require('goog.net.XmlHttp.ReadyState'); const ReadyState = goog.require('goog.net.XmlHttp.ReadyState');
const Request = goog.requireType('grpc.web.Request'); const Request = goog.requireType('grpc.web.Request');
const RpcError = goog.require('grpc.web.RpcError'); const RpcError = goog.require('grpc.web.RpcError');
const StatusCode = goog.require('grpc.web.StatusCode');
const XhrIo = goog.require('goog.testing.net.XhrIo'); const XhrIo = goog.require('goog.testing.net.XhrIo');
const googCrypt = goog.require('goog.crypt.base64'); const googCrypt = goog.require('goog.crypt.base64');
const testSuite = goog.require('goog.testing.testSuite'); const testSuite = goog.require('goog.testing.testSuite');
const {StreamInterceptor} = goog.require('grpc.web.Interceptor'); const {StreamInterceptor} = goog.require('grpc.web.Interceptor');
goog.require('goog.testing.jsunit'); goog.require('goog.testing.jsunit');
// This parses to [ { DATA: [4, 5, 6] }, { TRAILER: "a: b" } ]
const DEFAULT_RPC_RESPONSE =
new Uint8Array([0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98]);
const DEFAULT_RPC_RESPONSE_DATA = [4, 5, 6];
const DEFAULT_UNARY_HEADERS = const DEFAULT_UNARY_HEADERS =
['Content-Type', 'Accept', 'X-User-Agent', 'X-Grpc-Web']; ['Content-Type', 'Accept', 'X-User-Agent', 'X-Grpc-Web'];
const DEFAULT_UNARY_HEADER_VALUES = [ const DEFAULT_UNARY_HEADER_VALUES = [
@ -47,7 +52,7 @@ testSuite({
const xhr = new XhrIo(); const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr); const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => { const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals([4, 5, 6], [].slice.call(bytes)); assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return new MockReply('value'); return new MockReply('value');
}); });
@ -58,10 +63,8 @@ testSuite({
assertNull(error); assertNull(error);
resolve(response); resolve(response);
}); });
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
xhr.simulatePartialResponse( xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array( googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
[0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98])),
DEFAULT_RESPONSE_HEADERS); DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE); xhr.simulateReadyStateChange(ReadyState.COMPLETE);
}); });
@ -86,10 +89,8 @@ testSuite({
assertNull(error); assertNull(error);
resolve(); resolve();
}); });
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
xhr.simulatePartialResponse( xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array( googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
[0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98])),
DEFAULT_RESPONSE_HEADERS); DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE); xhr.simulateReadyStateChange(ReadyState.COMPLETE);
}); });
@ -122,11 +123,34 @@ testSuite({
assertEquals(3, error.code); assertEquals(3, error.code);
}, },
async testRpcDeserializationError() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const responseDeserializeFn = () => {
throw new Error('Decoding error :)');
};
const methodDescriptor = createMethodDescriptor(responseDeserializeFn);
const error = await new Promise((resolve, reject) => {
client.rpcCall(
'urlurl', new MockRequest(), /* metadata= */ {}, methodDescriptor,
(error, response) => {
assertNull(response);
resolve(error);
});
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
});
assertTrue(error instanceof RpcError);
assertEquals(StatusCode.INTERNAL, error.code);
},
async testRpcResponseHeader() { async testRpcResponseHeader() {
const xhr = new XhrIo(); const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr); const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => { const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals([4, 5, 6], [].slice.call(bytes)); assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return new MockReply('value'); return new MockReply('value');
}); });
@ -139,11 +163,8 @@ testSuite({
call.on('metadata', (metadata) => { call.on('metadata', (metadata) => {
resolve(metadata); resolve(metadata);
}); });
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
xhr.simulatePartialResponse( xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array( googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)), {
[0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98])),
{
'Content-Type': 'application/grpc-web-text', 'Content-Type': 'application/grpc-web-text',
'initial-metadata-key': 'initial-metadata-value', 'initial-metadata-key': 'initial-metadata-value',
}); });
@ -156,7 +177,7 @@ testSuite({
const xhr = new XhrIo(); const xhr = new XhrIo();
const interceptor = new StreamResponseInterceptor(); const interceptor = new StreamResponseInterceptor();
const methodDescriptor = createMethodDescriptor((bytes) => { const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals([4, 5, 6], [].slice.call(bytes)); assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return new MockReply('value'); return new MockReply('value');
}); });
const client = const client =
@ -169,10 +190,8 @@ testSuite({
assertNull(error); assertNull(error);
resolve(response); resolve(response);
}); });
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
xhr.simulatePartialResponse( xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array( googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
[0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98])),
DEFAULT_RESPONSE_HEADERS); DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE); xhr.simulateReadyStateChange(ReadyState.COMPLETE);
}); });

View File

@ -152,16 +152,16 @@ class GrpcWebClientReadableStream {
var byteSource = new Uint8Array( var byteSource = new Uint8Array(
/** @type {!ArrayBuffer} */ (self.xhr_.getResponse())); /** @type {!ArrayBuffer} */ (self.xhr_.getResponse()));
} else { } else {
self.handleError_(new RpcError( self.handleError_(
StatusCode.UNKNOWN, 'Unknown Content-type received.')); new RpcError(StatusCode.UNKNOWN, 'Unknown Content-type received.'));
return; return;
} }
var messages = null; var messages = null;
try { try {
messages = self.parser_.parse(byteSource); messages = self.parser_.parse(byteSource);
} catch (err) { } catch (err) {
self.handleError_(new RpcError( self.handleError_(
StatusCode.UNKNOWN, 'Error in parsing response body')); new RpcError(StatusCode.UNKNOWN, 'Error in parsing response body'));
} }
if (messages) { if (messages) {
var FrameType = GrpcWebStreamParser.FrameType; var FrameType = GrpcWebStreamParser.FrameType;
@ -169,15 +169,16 @@ class GrpcWebClientReadableStream {
if (FrameType.DATA in messages[i]) { if (FrameType.DATA in messages[i]) {
var data = messages[i][FrameType.DATA]; var data = messages[i][FrameType.DATA];
if (data) { if (data) {
let response;
try { try {
var response = self.responseDeserializeFn_(data); response = self.responseDeserializeFn_(data);
if (response) {
self.sendDataCallbacks_(response);
}
} catch (err) { } catch (err) {
self.handleError_(new RpcError( self.handleError_(new RpcError(
StatusCode.UNKNOWN, StatusCode.INTERNAL,
'Error in response deserializer function.')); `Error when deserializing response data: ${response}`));
}
if (response) {
self.sendDataCallbacks_(response);
} }
} }
} }
@ -193,15 +194,16 @@ class GrpcWebClientReadableStream {
var grpcStatusCode = StatusCode.OK; var grpcStatusCode = StatusCode.OK;
var grpcStatusMessage = ''; var grpcStatusMessage = '';
if (GRPC_STATUS in trailers) { if (GRPC_STATUS in trailers) {
grpcStatusCode = trailers[GRPC_STATUS]; grpcStatusCode =
/** @type {!StatusCode} */ (Number(trailers[GRPC_STATUS]));
delete trailers[GRPC_STATUS]; delete trailers[GRPC_STATUS];
} }
if (GRPC_STATUS_MESSAGE in trailers) { if (GRPC_STATUS_MESSAGE in trailers) {
grpcStatusMessage = trailers[GRPC_STATUS_MESSAGE]; grpcStatusMessage = trailers[GRPC_STATUS_MESSAGE];
delete trailers[GRPC_STATUS_MESSAGE]; delete trailers[GRPC_STATUS_MESSAGE];
} }
self.handleError_(new RpcError( self.handleError_(
Number(grpcStatusCode), grpcStatusMessage, trailers)); new RpcError(grpcStatusCode, grpcStatusMessage, trailers));
} }
} }
} }
@ -210,7 +212,7 @@ class GrpcWebClientReadableStream {
events.listen(this.xhr_, EventType.COMPLETE, function(e) { events.listen(this.xhr_, EventType.COMPLETE, function(e) {
var lastErrorCode = self.xhr_.getLastErrorCode(); var lastErrorCode = self.xhr_.getLastErrorCode();
var grpcStatusCode; var grpcStatusCode = StatusCode.UNKNOWN;
var grpcStatusMessage = ''; var grpcStatusMessage = '';
var initialMetadata = /** @type {!Metadata} */ ({}); var initialMetadata = /** @type {!Metadata} */ ({});
@ -249,14 +251,14 @@ class GrpcWebClientReadableStream {
// Check whethere there are grpc specific response headers // Check whethere there are grpc specific response headers
if (GRPC_STATUS in responseHeaders) { if (GRPC_STATUS in responseHeaders) {
grpcStatusCode = self.xhr_.getResponseHeader(GRPC_STATUS); grpcStatusCode = /** @type {!StatusCode} */ (
Number(self.xhr_.getResponseHeader(GRPC_STATUS)));
if (GRPC_STATUS_MESSAGE in responseHeaders) { if (GRPC_STATUS_MESSAGE in responseHeaders) {
grpcStatusMessage = self.xhr_.getResponseHeader(GRPC_STATUS_MESSAGE); grpcStatusMessage = self.xhr_.getResponseHeader(GRPC_STATUS_MESSAGE);
} }
if (Number(grpcStatusCode) != StatusCode.OK) { if (grpcStatusCode != StatusCode.OK) {
self.handleError_(new RpcError( self.handleError_(new RpcError(
Number(grpcStatusCode), grpcStatusMessage || '', grpcStatusCode, grpcStatusMessage || '', responseHeaders));
responseHeaders));
errorEmitted = true; errorEmitted = true;
} }
} }

View File

@ -26,16 +26,21 @@
goog.module('grpc.web.RpcError'); goog.module('grpc.web.RpcError');
const Metadata = goog.require('grpc.web.Metadata'); const Metadata = goog.require('grpc.web.Metadata');
const StatusCode = goog.require('grpc.web.StatusCode');
/**
* gRPC-Web Error object, contains the {@link StatusCode}, a string message
* and {@link Metadata} contained in the error response.
*/
class RpcError extends Error { class RpcError extends Error {
/** /**
* @param {number} code * @param {!StatusCode} code
* @param {string} message * @param {string} message
* @param {!Metadata=} metadata * @param {!Metadata=} metadata
*/ */
constructor(code, message, metadata = {}) { constructor(code, message, metadata = {}) {
super(message); super(message);
/** @type {number} */ /** @type {!StatusCode} */
this.code = code; this.code = code;
/** @type {!Metadata} */ /** @type {!Metadata} */
this.metadata = metadata; this.metadata = metadata;

View File

@ -23,11 +23,9 @@
* @author stanleycheung@google.com (Stanley Cheung) * @author stanleycheung@google.com (Stanley Cheung)
*/ */
goog.module('grpc.web.Status'); goog.module('grpc.web.Status');
goog.module.declareLegacyNamespace(); goog.module.declareLegacyNamespace();
/** @record */ /** @record */
function Status() {} function Status() {}

View File

@ -26,7 +26,8 @@ goog.module('grpc.web.StatusCode');
/** /**
* gRPC Status Codes * gRPC Status Codes
* See: https://github.com/grpc/grpc/blob/master/include/grpc%2B%2B/impl/codegen/status_code_enum.h * See:
* https://github.com/grpc/grpc/blob/master/include/grpcpp/impl/codegen/status_code_enum.h
* @enum {number} * @enum {number}
*/ */
const StatusCode = { const StatusCode = {
@ -140,11 +141,11 @@ const StatusCode = {
/** /**
* Convert HTTP Status code to gRPC Status code * Convert HTTP Status code to gRPC Status code
* @param {number} http_status HTTP Status Code * @param {number} httpStatus HTTP Status Code
* @return {number} gRPC Status Code * @return {!StatusCode} gRPC Status Code
*/ */
StatusCode.fromHttpStatus = function(http_status) { StatusCode.fromHttpStatus = function(httpStatus) {
switch (http_status) { switch (httpStatus) {
case 200: case 200:
return StatusCode.OK; return StatusCode.OK;
case 400: case 400:
@ -178,4 +179,44 @@ StatusCode.fromHttpStatus = function(http_status) {
}; };
/**
* Convert a {@link StatusCode} to an HTTP Status code
* @param {!StatusCode} statusCode GRPC Status Code
* @return {number} HTTP Status code
*/
StatusCode.getHttpStatus = function(statusCode) {
switch (statusCode) {
case StatusCode.OK:
return 200;
case StatusCode.INVALID_ARGUMENT:
return 400;
case StatusCode.UNAUTHENTICATED:
return 401;
case StatusCode.PERMISSION_DENIED:
return 403;
case StatusCode.NOT_FOUND:
return 404;
case StatusCode.ABORTED:
return 409;
case StatusCode.FAILED_PRECONDITION:
return 412;
case StatusCode.RESOURCE_EXHAUSTED:
return 429;
case StatusCode.CANCELLED:
return 499;
case StatusCode.UNKNOWN:
return 500;
case StatusCode.UNIMPLEMENTED:
return 501;
case StatusCode.UNAVAILABLE:
return 503;
case StatusCode.DEADLINE_EXCEEDED:
return 504;
/* everything else is unknown */
default:
return 0;
}
};
exports = StatusCode; exports = StatusCode;

View File

@ -0,0 +1,41 @@
goog.module('grpc.web.StatusCodeTest');
goog.setTestOnly('grpc.web.StatusCodeTest');
const StatusCode = goog.require('grpc.web.StatusCode');
const testSuite = goog.require('goog.testing.testSuite');
/** @type {!Map<number, !StatusCode>} */
const statusMap = new Map([
[200, StatusCode.OK],
[400, StatusCode.INVALID_ARGUMENT],
[401, StatusCode.UNAUTHENTICATED],
[403, StatusCode.PERMISSION_DENIED],
[404, StatusCode.NOT_FOUND],
[409, StatusCode.ABORTED],
[412, StatusCode.FAILED_PRECONDITION],
[429, StatusCode.RESOURCE_EXHAUSTED],
[500, StatusCode.UNKNOWN],
[501, StatusCode.UNIMPLEMENTED],
[503, StatusCode.UNAVAILABLE],
[504, StatusCode.DEADLINE_EXCEEDED],
]);
testSuite({
testFromHttpStatus() {
statusMap.forEach((statusCode, httpStatus) => {
assertEquals(StatusCode.fromHttpStatus(httpStatus), statusCode);
});
},
testGetHttpStatus() {
statusMap.forEach((statusCode, httpStatus) => {
assertEquals(StatusCode.getHttpStatus(statusCode), httpStatus);
});
},
testUnknown() {
assertEquals(StatusCode.getHttpStatus(StatusCode.UNKNOWN), 500);
assertEquals(StatusCode.fromHttpStatus(511), StatusCode.UNKNOWN);
}
});

View File

@ -73,11 +73,10 @@ WORKDIR /github/grpc-web
# Copy only files necessary to build the protoc-gen-grpc-web first as an optimization because they # Copy only files necessary to build the protoc-gen-grpc-web first as an optimization because they
# are rarely updated compared with the javascript files. # are rarely updated compared with the javascript files.
COPY ./WORKSPACE ./WORKSPACE COPY ./WORKSPACE ./WORKSPACE
COPY ./javascript/net/grpc/web/grpc_generator.cc javascript/net/grpc/web/grpc_generator.cc COPY ./javascript/net/grpc/web/generator javascript/net/grpc/web/generator
COPY ./javascript/net/grpc/web/BUILD.bazel javascript/net/grpc/web/BUILD.bazel
RUN bazel build javascript/net/grpc/web:protoc-gen-grpc-web && \ RUN bazel build javascript/net/grpc/web/generator:protoc-gen-grpc-web && \
cp $(bazel info bazel-genfiles)/javascript/net/grpc/web/protoc-gen-grpc-web \ cp $(bazel info bazel-genfiles)/javascript/net/grpc/web/generator/protoc-gen-grpc-web \
/usr/local/bin/protoc-gen-grpc-web /usr/local/bin/protoc-gen-grpc-web
COPY ./javascript ./javascript COPY ./javascript ./javascript

View File

@ -32,5 +32,5 @@ WORKDIR /github/grpc-web
RUN cd ./third_party/protobuf && \ RUN cd ./third_party/protobuf && \
./autogen.sh && ./configure && make && make install && ldconfig ./autogen.sh && ./configure && make && make install && ldconfig
RUN cd ./javascript/net/grpc/web && \ RUN cd ./javascript/net/grpc/web/generator && \
make protoc-gen-grpc-web make protoc-gen-grpc-web

View File

@ -6,6 +6,6 @@
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"google-closure-compiler": "~20200224.0.0", "google-closure-compiler": "~20200224.0.0",
"google-closure-library": "~20201102.0.1" "google-closure-library": "~20210808.0.0"
} }
} }

View File

@ -376,8 +376,8 @@ describe('grpc-web generated code (commonjs+grpcwebtext)', function() {
if (response) { if (response) {
assert.fail('should not receive response'); assert.fail('should not receive response');
} }
assert.equal(2, err.code); assert.equal(13 /* StatusCode.INTERNAL */, err.code);
assert.equal(true, err.message.toLowerCase().includes('deserialize')); assert.equal(true, err.message.toLowerCase().includes('deserializing'));
assert.equal(true, err.message.toLowerCase().includes('error')); assert.equal(true, err.message.toLowerCase().includes('error'));
done(); done();
}); });

View File

@ -16,7 +16,7 @@ in Docker Desktop for Mac (e.g. to 4 - 6GB) if you see the following errors:
``` ```
$ bazel test --cache_test_results=no //javascript/net/grpc/web/... //net/grpc/gateway/examples/... $ bazel build //javascript/net/grpc/web/generator/... //net/grpc/gateway/examples/echo/...
... ...

View File

@ -21,5 +21,5 @@ set -ex
cd /github/grpc-web && \ cd /github/grpc-web && \
bazel clean && \ bazel clean && \
bazel build \ bazel build \
//javascript/net/grpc/web/... \ //javascript/net/grpc/web/generator/... \
//net/grpc/gateway/examples/... //net/grpc/gateway/examples/echo/...