grpc: protobuf cross-language code generation results in naming inconsistency - javascript

I found that using snake_case in protobuf definition will have slightly different generated method/class names across different languages. The difference is in the casing if the protocol field name uses snake_case.
Example
A regular protoc code-generation based on the following protocol
syntax = "proto3";
package myservice;
service Myservice {
rpc MyService(Request) returns (Reply) {}
}
message Request {
bool my_foo = 1;
bool my_bar = 2;
}
message Reply {
bool is_succeeded = 1;
}
yields the following generated naming
Python
python -m grpc_tools.protoc -I="${SRC_DIR}" --python_out="${DST_DIR}" --grpc_python_out=$DST_DIR --proto_path="${SRC_DIR}" my.proto
my_pb2_grpc.py
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import my_pb2 as my__pb2
class MyserviceStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.MyService = channel.unary_unary(
'/myservice.Myservice/MyService',
request_serializer=my__pb2.Request.SerializeToString,
response_deserializer=my__pb2.Reply.FromString,
)
class MyserviceServicer(object):
"""Missing associated documentation comment in .proto file."""
def MyService(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_MyserviceServicer_to_server(servicer, server):
rpc_method_handlers = {
'MyService': grpc.unary_unary_rpc_method_handler(
servicer.MyService,
request_deserializer=my__pb2.Request.FromString,
response_serializer=my__pb2.Reply.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'myservice.Myservice', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class Myservice(object):
"""Missing associated documentation comment in .proto file."""
#staticmethod
def MyService(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/myservice.Myservice/MyService',
my__pb2.Request.SerializeToString,
my__pb2.Reply.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
my_pb2.py
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: my.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# ##protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='my.proto',
package='myservice',
syntax='proto3',
serialized_options=None,
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n\x08my.proto\x12\tmyservice\")\n\x07Request\x12\x0e\n\x06my_foo\x18\x01 \x01(\x08\x12\x0e\n\x06my_bar\x18\x02 \x01(\x08\"\x1d\n\x05Reply\x12\x14\n\x0cis_succeeded\x18\x01 \x01(\x08\x32#\n\tMyservice\x12\x33\n\tMyService\x12\x12.myservice.Request\x1a\x10.myservice.Reply\"\x00\x62\x06proto3'
)
_REQUEST = _descriptor.Descriptor(
name='Request',
full_name='myservice.Request',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='my_foo', full_name='myservice.Request.my_foo', index=0,
number=1, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='my_bar', full_name='myservice.Request.my_bar', index=1,
number=2, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=23,
serialized_end=64,
)
_REPLY = _descriptor.Descriptor(
name='Reply',
full_name='myservice.Reply',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='is_succeeded', full_name='myservice.Reply.is_succeeded', index=0,
number=1, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=66,
serialized_end=95,
)
DESCRIPTOR.message_types_by_name['Request'] = _REQUEST
DESCRIPTOR.message_types_by_name['Reply'] = _REPLY
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
Request = _reflection.GeneratedProtocolMessageType('Request', (_message.Message,), {
'DESCRIPTOR' : _REQUEST,
'__module__' : 'my_pb2'
# ##protoc_insertion_point(class_scope:myservice.Request)
})
_sym_db.RegisterMessage(Request)
Reply = _reflection.GeneratedProtocolMessageType('Reply', (_message.Message,), {
'DESCRIPTOR' : _REPLY,
'__module__' : 'my_pb2'
# ##protoc_insertion_point(class_scope:myservice.Reply)
})
_sym_db.RegisterMessage(Reply)
_MYSERVICE = _descriptor.ServiceDescriptor(
name='Myservice',
full_name='myservice.Myservice',
file=DESCRIPTOR,
index=0,
serialized_options=None,
create_key=_descriptor._internal_create_key,
serialized_start=97,
serialized_end=161,
methods=[
_descriptor.MethodDescriptor(
name='MyService',
full_name='myservice.Myservice.MyService',
index=0,
containing_service=None,
input_type=_REQUEST,
output_type=_REPLY,
serialized_options=None,
create_key=_descriptor._internal_create_key,
),
])
_sym_db.RegisterServiceDescriptor(_MYSERVICE)
DESCRIPTOR.services_by_name['Myservice'] = _MYSERVICE
# ##protoc_insertion_point(module_scope)
Node.js
With grpc-node the result looks like this
grpc_tools_node_protoc --js_out=import_style=commonjs,binary:"${DST_DIR}" --grpc_out=grpc_js:"${DST_DIR}" --proto_path="${SRC_DIR}" my.proto
my_grpc_pb.js
// GENERATED CODE -- DO NOT EDIT!
'use strict';
var grpc = require('#grpc/grpc-js');
var my_pb = require('./my_pb.js');
function serialize_myservice_Reply(arg) {
if (!(arg instanceof my_pb.Reply)) {
throw new Error('Expected argument of type myservice.Reply');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_myservice_Reply(buffer_arg) {
return my_pb.Reply.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_myservice_Request(arg) {
if (!(arg instanceof my_pb.Request)) {
throw new Error('Expected argument of type myservice.Request');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_myservice_Request(buffer_arg) {
return my_pb.Request.deserializeBinary(new Uint8Array(buffer_arg));
}
var MyserviceService = exports.MyserviceService = {
myService: {
path: '/myservice.Myservice/MyService',
requestStream: false,
responseStream: false,
requestType: my_pb.Request,
responseType: my_pb.Reply,
requestSerialize: serialize_myservice_Request,
requestDeserialize: deserialize_myservice_Request,
responseSerialize: serialize_myservice_Reply,
responseDeserialize: deserialize_myservice_Reply,
},
};
exports.MyserviceClient = grpc.makeGenericClientConstructor(MyserviceService);
my_pb.js
// source: my.proto
/**
* #fileoverview
* #enhanceable
* #suppress {missingRequire} reports error on implicit type usages.
* #suppress {messageConventions} JS Compiler reports an error if a variable or
* field starts with 'MSG_' and isn't a translatable message.
* #public
*/
// GENERATED CODE -- DO NOT EDIT!
/* eslint-disable */
// #ts-nocheck
var jspb = require('google-protobuf');
var goog = jspb;
var global = Function('return this')();
goog.exportSymbol('proto.myservice.Reply', null, global);
goog.exportSymbol('proto.myservice.Request', null, global);
/**
* Generated by JsPbCodeGenerator.
* #param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* #extends {jspb.Message}
* #constructor
*/
proto.myservice.Request = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.myservice.Request, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* #public
* #override
*/
proto.myservice.Request.displayName = 'proto.myservice.Request';
}
/**
* Generated by JsPbCodeGenerator.
* #param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* #extends {jspb.Message}
* #constructor
*/
proto.myservice.Reply = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.myservice.Reply, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* #public
* #override
*/
proto.myservice.Reply.displayName = 'proto.myservice.Reply';
}
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* #param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* #return {!Object}
*/
proto.myservice.Request.prototype.toObject = function(opt_includeInstance) {
return proto.myservice.Request.toObject(opt_includeInstance, this);
};
/**
* Static version of the {#see toObject} method.
* #param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* #param {!proto.myservice.Request} msg The msg instance to transform.
* #return {!Object}
* #suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.myservice.Request.toObject = function(includeInstance, msg) {
var f, obj = {
myFoo: jspb.Message.getBooleanFieldWithDefault(msg, 1, false),
myBar: jspb.Message.getBooleanFieldWithDefault(msg, 2, false)
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* #param {jspb.ByteSource} bytes The bytes to deserialize.
* #return {!proto.myservice.Request}
*/
proto.myservice.Request.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.myservice.Request;
return proto.myservice.Request.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* #param {!proto.myservice.Request} msg The message object to deserialize into.
* #param {!jspb.BinaryReader} reader The BinaryReader to use.
* #return {!proto.myservice.Request}
*/
proto.myservice.Request.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = /** #type {boolean} */ (reader.readBool());
msg.setMyFoo(value);
break;
case 2:
var value = /** #type {boolean} */ (reader.readBool());
msg.setMyBar(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* #return {!Uint8Array}
*/
proto.myservice.Request.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.myservice.Request.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* #param {!proto.myservice.Request} message
* #param {!jspb.BinaryWriter} writer
* #suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.myservice.Request.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getMyFoo();
if (f) {
writer.writeBool(
1,
f
);
}
f = message.getMyBar();
if (f) {
writer.writeBool(
2,
f
);
}
};
/**
* optional bool my_foo = 1;
* #return {boolean}
*/
proto.myservice.Request.prototype.getMyFoo = function() {
return /** #type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false));
};
/**
* #param {boolean} value
* #return {!proto.myservice.Request} returns this
*/
proto.myservice.Request.prototype.setMyFoo = function(value) {
return jspb.Message.setProto3BooleanField(this, 1, value);
};
/**
* optional bool my_bar = 2;
* #return {boolean}
*/
proto.myservice.Request.prototype.getMyBar = function() {
return /** #type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false));
};
/**
* #param {boolean} value
* #return {!proto.myservice.Request} returns this
*/
proto.myservice.Request.prototype.setMyBar = function(value) {
return jspb.Message.setProto3BooleanField(this, 2, value);
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* #param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* #return {!Object}
*/
proto.myservice.Reply.prototype.toObject = function(opt_includeInstance) {
return proto.myservice.Reply.toObject(opt_includeInstance, this);
};
/**
* Static version of the {#see toObject} method.
* #param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* #param {!proto.myservice.Reply} msg The msg instance to transform.
* #return {!Object}
* #suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.myservice.Reply.toObject = function(includeInstance, msg) {
var f, obj = {
isSucceeded: jspb.Message.getBooleanFieldWithDefault(msg, 1, false)
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* #param {jspb.ByteSource} bytes The bytes to deserialize.
* #return {!proto.myservice.Reply}
*/
proto.myservice.Reply.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.myservice.Reply;
return proto.myservice.Reply.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* #param {!proto.myservice.Reply} msg The message object to deserialize into.
* #param {!jspb.BinaryReader} reader The BinaryReader to use.
* #return {!proto.myservice.Reply}
*/
proto.myservice.Reply.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = /** #type {boolean} */ (reader.readBool());
msg.setIsSucceeded(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* #return {!Uint8Array}
*/
proto.myservice.Reply.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.myservice.Reply.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* #param {!proto.myservice.Reply} message
* #param {!jspb.BinaryWriter} writer
* #suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.myservice.Reply.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getIsSucceeded();
if (f) {
writer.writeBool(
1,
f
);
}
};
/**
* optional bool is_succeeded = 1;
* #return {boolean}
*/
proto.myservice.Reply.prototype.getIsSucceeded = function() {
return /** #type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false));
};
/**
* #param {boolean} value
* #return {!proto.myservice.Reply} returns this
*/
proto.myservice.Reply.prototype.setIsSucceeded = function(value) {
return jspb.Message.setProto3BooleanField(this, 1, value);
};
goog.object.extend(exports, proto.myservice);
Dart
With the same treatment as above
protoc --plugin=protoc-gen-dart="$HOME/.pub-cache/bin/protoc-gen-dart" --dart_out=grpc:"${DST_DIR}" -I"${SRC_DIR}" -I"$IncludeDir" my.proto
my.pbgrpc.dart
///
// Generated code. Do not modify.
// source: my.proto
//
// #dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
import 'dart:async' as $async;
import 'dart:core' as $core;
import 'package:grpc/service_api.dart' as $grpc;
import 'my.pb.dart' as $0;
export 'my.pb.dart';
class MyserviceClient extends $grpc.Client {
static final _$myService = $grpc.ClientMethod<$0.Request, $0.Reply>(
'/myservice.Myservice/MyService',
($0.Request value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.Reply.fromBuffer(value));
MyserviceClient($grpc.ClientChannel channel,
{$grpc.CallOptions? options,
$core.Iterable<$grpc.ClientInterceptor>? interceptors})
: super(channel, options: options, interceptors: interceptors);
$grpc.ResponseFuture<$0.Reply> myService($0.Request request,
{$grpc.CallOptions? options}) {
return $createUnaryCall(_$myService, request, options: options);
}
}
abstract class MyserviceServiceBase extends $grpc.Service {
$core.String get $name => 'myservice.Myservice';
MyserviceServiceBase() {
$addMethod($grpc.ServiceMethod<$0.Request, $0.Reply>(
'MyService',
myService_Pre,
false,
false,
($core.List<$core.int> value) => $0.Request.fromBuffer(value),
($0.Reply value) => value.writeToBuffer()));
}
$async.Future<$0.Reply> myService_Pre(
$grpc.ServiceCall call, $async.Future<$0.Request> request) async {
return myService(call, await request);
}
$async.Future<$0.Reply> myService($grpc.ServiceCall call, $0.Request request);
}
my.pb.dart
///
// Generated code. Do not modify.
// source: my.proto
//
// #dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb;
class Request extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Request', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'myservice'), createEmptyInstance: create)
..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'myFoo')
..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'myBar')
..hasRequiredFields = false
;
Request._() : super();
factory Request({
$core.bool? myFoo,
$core.bool? myBar,
}) {
final _result = create();
if (myFoo != null) {
_result.myFoo = myFoo;
}
if (myBar != null) {
_result.myBar = myBar;
}
return _result;
}
factory Request.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory Request.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
#$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
Request clone() => Request()..mergeFromMessage(this);
#$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
Request copyWith(void Function(Request) updates) => super.copyWith((message) => updates(message as Request)) as Request; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
#$core.pragma('dart2js:noInline')
static Request create() => Request._();
Request createEmptyInstance() => create();
static $pb.PbList<Request> createRepeated() => $pb.PbList<Request>();
#$core.pragma('dart2js:noInline')
static Request getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Request>(create);
static Request? _defaultInstance;
#$pb.TagNumber(1)
$core.bool get myFoo => $_getBF(0);
#$pb.TagNumber(1)
set myFoo($core.bool v) { $_setBool(0, v); }
#$pb.TagNumber(1)
$core.bool hasMyFoo() => $_has(0);
#$pb.TagNumber(1)
void clearMyFoo() => clearField(1);
#$pb.TagNumber(2)
$core.bool get myBar => $_getBF(1);
#$pb.TagNumber(2)
set myBar($core.bool v) { $_setBool(1, v); }
#$pb.TagNumber(2)
$core.bool hasMyBar() => $_has(1);
#$pb.TagNumber(2)
void clearMyBar() => clearField(2);
}
class Reply extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Reply', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'myservice'), createEmptyInstance: create)
..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isSucceeded')
..hasRequiredFields = false
;
Reply._() : super();
factory Reply({
$core.bool? isSucceeded,
}) {
final _result = create();
if (isSucceeded != null) {
_result.isSucceeded = isSucceeded;
}
return _result;
}
factory Reply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory Reply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
#$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
Reply clone() => Reply()..mergeFromMessage(this);
#$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
Reply copyWith(void Function(Reply) updates) => super.copyWith((message) => updates(message as Reply)) as Reply; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
#$core.pragma('dart2js:noInline')
static Reply create() => Reply._();
Reply createEmptyInstance() => create();
static $pb.PbList<Reply> createRepeated() => $pb.PbList<Reply>();
#$core.pragma('dart2js:noInline')
static Reply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Reply>(create);
static Reply? _defaultInstance;
#$pb.TagNumber(1)
$core.bool get isSucceeded => $_getBF(0);
#$pb.TagNumber(1)
set isSucceeded($core.bool v) { $_setBool(0, v); }
#$pb.TagNumber(1)
$core.bool hasIsSucceeded() => $_has(0);
#$pb.TagNumber(1)
void clearIsSucceeded() => clearField(1);
}
Comparison
If we compare the following generated entities
Service class name
Service method name
Request field names
Reply field names
Here are the results
Python
Myservice
MyService
my_foo
my_bar
is_succeeded
Node.js
MyserviceService
myService
accessor: getMyFoo()
accessor: getMyBar()
accessor: getIsSucceeded()
Dart
MyserviceServiceBase
myService
myFoo
myBar
isSucceeded
Question
Although these naming schemes seem to cater to the language-specific convention,
the differences between them caused some maintenance issues.
I wonder if there is a way to ensure an identical casing treatment across all these languages, and possibly all supported languages.
Thanks!

Code generation plugins have complete freedom to generate whatever code they want. Usually, they try to follow the language's conventions. You'd need to have controls for each language, and most won't provide it.
What are the actual maintenance issues that you are facing? Perhaps there is some other way of solving them.

Related

trying to add jsdocs to dynamically generated functions

I have a class that generates simple CRUD functions of a given table. I have the jsdoc documentation working the way I want it (mostly for autofill purposes) when I init it once. ie const providerService = new CrudFunctions('provider'). However I may be adding more tables in the future, and I'll be wanting to generate all the tables in a loop so I don't have to repeat a bunch of code. Here's what I have so far to generate all the functions so far:
const generateTableFunctions = (tableArray) => {
const tableFunctions = {};
tableArray.forEach((table) => {
tableFunctions[table] = new CrudFunctions(table);
});
return tableFunctions;
};
const foo = generateTableFunctions([
'service',
'payment',
'certification',
'provider',
'provider_certifcation',
'provider_payment',
'provider_service'
]);
What I would really like is for "foo." to suggest a table name, but if nothing else I would really like "foo.provider." to suggest getAll, getOne, and add. I've tried making the generator function a jsdoc template, I've tried using typedef but no matter what I don't get any suggestions for foo. Here's the class:
/** Class representing crud functions of a given table */
class CrudFunctions {
/**
* #param {string} table - the name of the table from the database
*/
constructor(table) {
this.table = table;
}
/**
* get all items from the table
*
* #returns {Object} all rows from table
*/
async getAll() {
const { rows } = await handleQuery(`SELECT * FROM ${this.table}`);
return rows;
}
/**
* gets a single item from the table
*
* #param {number} id - the unique id of the item we're looking up
* #returns {Object} the item from the table
*/
async getOne(id) {
const { rows } = await handleQuery(
`SELECT * FROM ${this.table} WHERE ID=${id}`
);
return rows;
}
/**
*
* #param {object} item - an item to be added to the table. all keys should be valid in the database already
* #returns confirmation that the item got added
*/
async add(item) {
const res = await handleQuery(
buildQuery(`INSERT INTO ${this.table}`, Object.keys(item)),
Object.values(item)
);
return res;
}
}
The following would do the trick:
/**
* #template {string} T
* #param {T[]} tableArray
*/
const generateTableFunctions = (tableArray) => {
const tableFunctions = /** #type {Record<T, CrudFunctions>} */({});
tableArray.forEach((table) => {
tableFunctions[table] = new CrudFunctions(table);
});
return tableFunctions;
};
const foo = generateTableFunctions([
'service',
'payment',
'certification',
'provider',
'provider_certifcation',
'provider_payment',
'provider_service'
]);

AJAX request is cancelled by Chrome when issued from within the extension

I am doing a simple AJAX request within Chrome extension, but Chrome cancels my request each time. I did specify necessary cross-origin permissions in the manifest file. The server also allows cross-origin requests, i.e I am able to execute my ajax script within a normal browser window. I also tried doing the old way via XMLHttpRequest - the result is the same. What am I missing?
The full JS code loaded by extension is below (getUmsIdByEmail_ is the function):
var AssumeIdentityController = function () {
this.button_ = document.getElementById('button');
this.customer_email_ = document.getElementById('emailfield');
this.addListeners_();
};
AssumeIdentityController.prototype = {
/**
* A cached reference to the button element.
*
* #type {Element}
* #private
*/
button_: null,
/**
* A cached reference to the select element.
*
* #type {Element}
* #private
*/
timeframe_: null,
customer_email_: null,
/**
* Adds event listeners to the button in order to capture a user's click, and
* perform some action in response.
*
* #private
*/
addListeners_: function () {
this.button_.addEventListener('click', this.handleClick_.bind(this));
},
/**
* Given a string, return milliseconds since epoch. If the string isn't
* valid, returns undefined.
*
* #param {string} timeframe One of 'hour', 'day', 'week', '4weeks', or
* 'forever'.
* #returns {number} Milliseconds since epoch.
* #private
*/
getUmsIdByEmail_: function(email){
console.log('attempting to fetch');
var opts = {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
};
fetch('<SERVER_WITH_THE_RESOURCE>', opts).then(function (response) {
return response.json();
})
.then(function (body) {
console.log(body)
});
},
// getTokenByUms_: function(umsId) {
// return token
// }
/**
* Handle a success/failure callback from the `browsingData` API methods,
* updating the UI appropriately.
*
* #private
*/
handleCallback_: function () {
var success = document.createElement('div');
success.classList.add('overlay');
success.setAttribute('role', 'alert');
success.textContent = 'Data has been cleared.';
document.body.appendChild(success);
setTimeout(function() { success.classList.add('visible'); }, 10);
setTimeout(function() {
if (close === false)
success.classList.remove('visible');
else
window.close();
}, 4000);
},
/**
* When a user clicks the button, this method is called: it reads the current
* state of `timeframe_` in order to pull a timeframe, then calls the clearing
* method with appropriate arguments.
*
* #private
*/
handleClick_: function () {
this.getUmsIdByEmail_('sd');
if (this.customer_email_ !== undefined)
{
// var token = getTokenByUms(umsId);
// if(replacePreprodToken(token))
// {
// print('successfully logged in. Refreshing the page');
// }
// else
// {
// print('Can't log in. Please try again later');
// }
}
// }
}
};
document.addEventListener('DOMContentLoaded', function () {
console.log('dom content loaded');
window.PC = new AssumeIdentityController();
});
Permissions in manifest look like this:
...
"permissions": [
"browsingData","declarativeContent","storage","https://*/","http://*/"
],
...

Is there a way I can update user profile(details) in postgres and node js witbout having to send all parameters in the request body everytime?

I have a postgres query in this format:
UPDATE farmers SET first_name=$1, last_name=$2, contact_no=$3, cooperative_name=$4 WHERE verification_id=$5
However, this will require me to send all for parameters in the request body whenever I want to update the data, else I will get a null value for parameter not included.
Is there a way I can write this either in node js or postgres or both such that I only include the parameter(s) I want to update in the request body without setting the other values to null.
this is my services where I am using the query
Encountered a similar problem sometime back and I created a function to do just that.
const addComma = (len) => len > 1 ? ', ' : '';
/**
* #param {updateStmtObject} {table, fields, condition}
* #returns {string} query
*/
function createUpdateStatement(table, fields, condition){
let query = {
text: `UPDATE ${table} SET`,
values: []
}
Object.keys(fields).forEach(col => {
query.values.push(fields[col]);
query.text += `${addComma(query.values.length)} ${col} = $${query.values.length}`;
});
if (query.values.length > 0) {
if(condition){
const col = Object.keys(condition)[0];
query.values.push(condition[col]);
query.text += ` WHERE ${col} = $${query.values.length}`;
}
return query;
} else {
//No changes to be made
return null;
}
}
module.exports = {
createUpdateStatement
};
/**
* Update statement object
* #typedef {Object} updateStmtObject
* #property {string} table - Table to be updated
* #property {object} fields - An object of column_name:value pairs for the query
* #property {object} condition - column_name:value pair for update condition.
* Only single condition currently supported. Add other conditions manually.
*/
Just copy and paste it into a util.js file or whatever then you can call it like so to dynamically generate an UPDATE statement:
const { createUpdateStatement } = require('util.js');
const updateData = {first_name: 'Benz', last_name: 'Stevox'};
const query = createUpdateStatement('users', updateData, {verification_id: 5});
console.log(query);
/*
{
text: 'UPDATE users SET first_name = $1, last_name = $2 WHERE verification_id = $3',
values: [ 'Benz', 'Stevox', 5 ]
}
*/
It probably won't hold up for complex queries but simple statements like in your example should work out just fine.

Convert IIFE module to something importable by RollupJS

I am using RollupJS as a bundler, and it can read CommonJS (via a plugin) or ES6 modules. But this module seems to be in UMD format, and I am looking for a quick way I can edit it (without replacing a lot of lines) so that it is in commonJS or ES6 format.
What do folks suggest? I show the top and the bottom of a 5,000 line .js file.
#module vrlinkjs
**/
(function (mak) {
mak.MessageKindEnum = {
Any : -1,
Other : 0,
AttributeUpdate : 1,
Interaction : 2,
Connect : 3,
ObjectDeletion : 4
};
/**
Decodes AttributeUpdate messages into an EnvironmentalStateRepository object.
#class EnvironmentalStateDecoder
#constructor
#augments StateDecoder
#param {WebLVCConnection} webLVCConnection Connection to a WebLVC server
**/
mak.EnvironmentalStateDecoder = function(webLVCConnection) {
mak.StateDecoder.apply(this, arguments);
};
mak.EnvironmentalStateDecoder.prototype = Object.create(mak.StateDecoder.prototype, {
constructor : { value : mak.EnvironmentalStateDecoder },
/**
Decodes a AttributeUpdate message into an EntityStateRepository object.
#method decode
#param {Object} attributeUpdate WebLVC AttributeUpdate message
#param {EntityStateRepository} stateRep State repository to be updated
**/
decode : {
value : function( attributeUpdate, stateRep ) {
// if(this.webLVCConnection.timeStampType == mak.TimeStampType.TimeStampAbsolute &&
// attributeUpdate.TimeStampType == mak.TimeStampType.TimeStampAbsolute) {
// } else {
// stateRep.timeStampType = mak.TimeStampType.TimeStampRelative;
// }
stateRep.timeStampType = mak.TimeStampType.TimeStampRelative;
var curTime = 0.0;
// if (stateRep->timeStampType() == DtTimeStampAbsolute)
// {
// // Use timestamp as time of validity
// curTime = pdu.guessTimeValid(myExConn->clock()->simTime());
// }
// else
// {
// // Use receive time as time of validity
// curTime = myExConn->clock()->simTime();
// }
curTime = this.webLVCConnection.clock.simTime;
if(attributeUpdate.ProcessIdentifier != undefined) {
stateRep.entityIdentifier = attributeUpdate.EntityIdentifier;
}
if(attributeUpdate.Type != undefined) {
stateRep.entityType = attributeUpdate.Type;
}
if(attributeUpdate.ObjectName != undefined) {
stateRep.objectName = attributeUpdate.ObjectName;
}
if(attributeUpdate.GeometryRecords != undefined) {
stateRep.GeometryRecords = attributeUpdate.GeometryRecords;
}
if(attributeUpdate.EnvObjData != undefined) {
if(attributeUpdate.EnvObjData.VrfObjName != undefined) {
stateRep.marking = attributeUpdate.EnvObjData.VrfObjName;
}
}
}
}
});
.....
} (this.mak = this.mak || {}));
UPDATE
I used the ES6 module solution from estus (below), which I really like. It solved the rollup bunding issue, but there is still a runtime error.
But there is a little more that needs to be done. I am getting this error with chrome. I have two varients of the HTML main.html file, one uses the bundle and the other just imports my es6 modules. The error occurs even when I am not using rollup and creating and using the bundle.
Uncaught TypeError: Cannot set property objectName of [object Object] which has only a getter
at mak$1.ReflectedEntity.mak$1.ReflectedObject [as constructor] (vrlink.mjs:818)
at new mak$1.ReflectedEntity (vrlink.mjs:903)
at mak$1.ReflectedEntityList.value (vrlink.mjs:1358)
at mak$1.WebLVCMessageCallbackManager.<anonymous> (vrlink.mjs:1155)
at mak$1.WebLVCMessageCallbackManager.processMessage (vrlink.mjs:1745)
at mak$1.WebLVCConnection.drainInput (vrlink.mjs:2139)
at SimLink.tick (SimLink.js:34)
This seems to be the offender when converting from IIFE modules to ES6. It says that there is no setter.
The code is not my creation, but it seemed like it should not have be a major effort to convert IIFE to ES6. The offending snippet is:
mak.VrfBackendStateRepository = function (objectName) {
/**
Unique string identifying entity
#property objectName
#type String
**/
this.objectName = objectName; //error generated on this line!
If you are wondering what this is, it is a object called mak.webLVConnection, which is created by this function in the IIFE code:
/**
Represents a connection to a WebLVC server.
clientName and port are required. webLVCVersion is optional (current version
supported by the WebLVC server will be in effect). serverLocation is optional
( websocket connection will be made to the host servering the javascript )
#class WebLVCConnection
#constructor
#param {String} clientName String representing name of the client federate
#param {Number} port Websocket port number
#param {Number} webLVCVersion WebLVC version number
#param {String} serverLocation Hostname of websocket server
**/
mak.WebLVCConnection = function (clientName, port, webLVCVersion, serverLocation, url) {
var self = this;
if (clientName == undefined) {
throw new Error("clientName not specified");
}
if (!(typeof clientName == "string" && clientName.length > 0)) {
throw new Error("Invalid ClientName specified");
}
if (port == undefined) {
throw new Error("Port not specified");
}
if (url == undefined) {
url = "/ws";
}
var websocket;
if (serverLocation == undefined) {
if (location.hostname) {
websocket = new WebSocket("ws://" + location.hostname + ":" + port + url);
}
else {
websocket = new WebSocket("ws://localhost:" + port + "/ws");
}
}
else {
websocket = new WebSocket("ws://" + serverLocation + ":" + port + url);
}
/**
Websocket connected to a WebLVC server.
#property websocket
#type WebSocket
**/
this.websocket = websocket;
/**
DIS/RPR-style identifier, used to generate new unique IDs for entities simulated
through this connection. Array of 3 numbers [site ID, host ID, entity number].
#property currentId
#type Array
**/
this.currentId = [1, 1, 0];
/**
Manages registration and invoking of message callbacks.
#property webLVCMessageCallbackManager
#type WebLVCMessageCallbackManager
**/
this.webLVCMessageCallbackManager = new mak.WebLVCMessageCallbackManager();
/**
Simulation clock
#property clock
#type Clock
**/
this.clock = new mak.Clock();
/**
Indicates whether timestamping is relative or absolute
(mak.TimeStampType.TimeStampRelative or
mak.TimeStampType.TimeStampAbsolute).
#property {Number} timeStampType
**/
this.timeStampType = mak.TimeStampType.TimeStampRelative;
/**
List of incoming messages. When messages are received, they are placed
in this queue. The drainInput() member function must be called regularly
to remove and process messages in this queue.
#property {Array} messageQueue
**/
this.messageQueue = new Array();
/**
Callback function invoked on receipt of a message. Calls
webLVCMessageCallbackManager.processMessage().
#method processMessage
#private
**/
this.processMessage = this.webLVCMessageCallbackManager.processMessage.bind(this.webLVCMessageCallbackManager);
/**
Callback function invoked when websocket connection is opened. Sends
the initial WebLVC connect message.
#method onopen
#private
**/
this.websocket.onopen = function () {
var connectMessage = {
MessageKind: mak.MessageKindEnum.Connect,
ClientName: clientName
}
if (webLVCVersion != undefined) {
connectMessage.WebLVCVersion = webLVCVersion;
}
if (self.websocket.readyState == 1) {
self.websocket.send(JSON.stringify(connectMessage));
}
};
/**
Callback function invoked when a WebLVC message is received. Parses the
the JSON message data and passes the resulting object to processMessage.
#method onmessage
#event {Object} JSON message
#private
**/
this.websocket.onmessage = function (event) {
//just in case
if (event.data == "ping")
return;
var message = JSON.parse(event.data);
if (message != null) {
self.messageQueue.push(message);
} else {
console.warn("onmessage - null message received");
}
};
/**
Callback function invoked when the websocket is closed.
#method onclose
#private
**/
this.websocket.onclose = function () {
console.debug("In websocket.onclose");
};
/**
Callback function invoked when an error in the websocket is detected.
Sends warning to console.
#method onerror
#private
**/
this.websocket.onerror = function () {
console.log("websocket onerror");
};
this.isOk = function () {
return this.websocket.readyState == 1;
}
};
mak.WebLVCConnection.prototype = {
constructor: mak.WebLVCConnection,
/**
Set the DIS/RPR-style application ID.
#method set applicationId
#param {Array} applicationId Array of 2 integers [site ID, host ID].
**/
set applicationId(applicationId) {
this.currentId[0] = applicationId[0];
this.currentId[1] = applicationId[1];
this.currentId[2] = 0;
},
/**
Returns next available DIS/RPR-style entity ID.
#method nextId
#return {Array} Array of 3 integers [site ID, host ID, entity number].
**/
get nextId() {
this.currentId[2]++;
return this.currentId;
},
/**
Register callback function for a given kind of message.
#method addMessageCallback
#param {Number} messageKind WebLVC MessageKind
#param callback Function to be invoked
**/
addMessageCallback: function (messageKind, callback) {
this.webLVCMessageCallbackManager.addMessageCallback(messageKind, callback);
},
/**
De-register callback function for a given kind of message.
#method removeMessageCallback
#param messageKind WebLVC MessageKind
#param callback Function to be invoked
**/
removeMessageCallback: function (messageKind, callback) {
this.webLVCMessageCallbackManager.removeMessageCallback(messageKind, callback);
},
/**
Send a WebLVC message to the server.
#method send
#param {Object} message
**/
send: function (message) {
try {
if (this.websocket.readyState == 1) {
this.websocket.send(JSON.stringify(message));
}
} catch (exception) {
console.log("Error sending on websocket - exception: " + exception);
}
},
/**
Send a time-stamped WebLVC message to the server.
#method sendStamped
#param {Object} message
**/
sendStamped: function (message) {
// Timestamp is hex string
var timeStamp = this.currentTimeForStamping().toString(16);
//message.TimeStamp = ""; // timeStamp;
this.send(message);
},
/**
Get the current simulation time for a time stamp.
#method currentTimeForStamping
#return {Number} Simulation time in seconds.
**/
currentTimeForStamping: function () {
if (this.timeStampType == mak.TimeStampType.TimeStampAbsolute) {
return this.clock.simTime();
}
else {
return this.clock.absRealTime();
}
},
/**
Iterate through message queue, calling processMessage() and then
removing each message. Should be called regularly from your
application.
#method drainInput
**/
drainInput: function () {
var message;
while (this.messageQueue.length > 0) {
message = this.messageQueue.shift();
this.processMessage(message);
}
},
/**
Closes the websocket connection. Calls the destroy method on its
WebLVCMessageCallbackManager data member.
#method destroy
**/
destroy: function () {
console.debug("In WebLVCConnection.destroy");
this.webLVCMessageCallbackManager.destroy();
this.websocket.close();
}
};
UMD modules, by definition, are CommonJS. The code above is just IIFE and relies on mak global.
IIFE wrapper function can be replaced with default or named ES module export:
const mak = {};
mak.MessageKindEnum = { ... };
...
export default mak;
Or with CommonJS export:
const mak = {};
mak.MessageKindEnum = { ... };
...
module.exports = mak;
did you try:
(function (mak) {
...
}(module.exports));
// instead of
// } (this.mak = this.mak || {}));

jsdoc documentation functions inside module

I have the following code:
/**
* #fileOverview Various tool functions.
* #version 3.1.2
*/
define(function (require, exports, module) {
"use strict";
/**
* A module that handles file
* #module fileHandler
*/
/// Form to open a new set of files
var newFileForm = require("pvsioweb/forms/newFileForm");
var formEvents = require("pvsioweb/forms/events");
/// Reference to current project, main.js passes it by using fileHandler_setProject
var currentProject;
/**
* this is a function
* #param p1 First parameter
* #param p2 Second parameter
* #return {String} some value
*/
function setProject(project)
{
currentProject = project;
}
/**
* Create a new file, it is going to be shown in the listview: #pvsFiles
*
* #param name: name of the file
* #param content: textual content of the file
*
* #returns void
*
*/
function new_file(name, content )
{
var default_name = "MyTheory.pvs";
var default_content = "MyTheory" + " THEORY BEGIN \nEND MyTheory" ;
if( ! name ) { name = default_name; }
if( ! content ) { content = default_content; }
currentProject.addSpecFile(default_name, default_content);
renderSourceFileList(currentProject.pvsFiles());
}
/**
* Display new file form, invoke function open_file (see below)
*
* #returns void
*
*/
function open_file_form()
{
newFileForm.create().addListener(formEvents.FormCancelled, function (e) {
console.log(e);
e.form.remove();
}).addListener(formEvents.FormSubmitted, function (e) {
console.log(e);
e.form.remove();
open(e.formJSON);
});
}
/**
* Open file specified in data, data must have this structure: ???? FIXME
*
* #param data: ??? FIXME
*
* #returns void
*
*/
function open_file(data)
{
var q = queue(), i;
for (i = 0; i < data.pvsSpec.length; i++) {
q.defer(createFileLoadFunction(data.pvsSpec[i]));
}
q.awaitAll(function (err, res) {
currentProject.saveNew(function (err, res) {
console.log({err: err, res: res});
renderSourceFileList(currentProject.pvsFiles());
});
});
}
/********* Exported Function ******************/
module.exports = {
new_file: function (name, content) {
return new_file(name, content);
},
open_file_form: function () {
return open_file_form();
},
open_file: function () {
return open_file();
},
setProject: function (project) {
return setProject(project);
}
};
/***********************************************/
});
I tried to make some output by using jsdoc, but it seems to recognize just the word module and the header at the beginning of the file.
How can I fix to see also the documentation about the functions?
Thanks
I'm not sure I understand the question. Are you trying to generate documentation with jsdoc? If so, you'll need to add the jsdoc comments to each function. Also, your exports could be greatly simplified:
module.exports = {
new_file: new_file,
open_file_form: open_file_form,
open_file: open_file,
setProject: setProject
};

Categories

Resources