Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve comments when serializing/deserializing with toJSON and fromJSON. #983

Merged
merged 6 commits into from
Feb 12, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions src/enum.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ var Namespace = require("./namespace"),
* @param {string} name Unique name within its namespace
* @param {Object.<string,number>} [values] Enum values as an object, by name
* @param {Object.<string,*>} [options] Declared options
* @param {string} comment The comment for this enum
* @param {Object.<string,string>} comments The value comments for this enum
*/
function Enum(name, values, options) {
function Enum(name, values, options, comment, comments) {
ReflectionObject.call(this, name, options);

if (values && typeof values !== "object")
Expand All @@ -35,11 +37,17 @@ function Enum(name, values, options) {
*/
this.values = Object.create(this.valuesById); // toJSON, marker

/**
* Enum comment text.
* @type {string|undefined}
*/
this.comment = comment;

/**
* Value comment texts, if any.
* @type {Object.<string,string>}
*/
this.comments = {};
this.comments = comments || {};

/**
* Reserved ranges, if any.
Expand Down Expand Up @@ -72,20 +80,24 @@ function Enum(name, values, options) {
* @throws {TypeError} If arguments are invalid
*/
Enum.fromJSON = function fromJSON(name, json) {
var enm = new Enum(name, json.values, json.options);
var enm = new Enum(name, json.values, json.options, json.comment, json.comments);
enm.reserved = json.reserved;
return enm;
};

/**
* Converts this enum to an enum descriptor.
* @param {IToJSONOptions} [toJSONOptions] JSON conversion options
* @returns {IEnum} Enum descriptor
*/
Enum.prototype.toJSON = function toJSON() {
Enum.prototype.toJSON = function toJSON(toJSONOptions) {
var keepComments = toJSONOptions ? (!!toJSONOptions.keepComments) : false;
return util.toObject([
"options" , this.options,
"values" , this.values,
"reserved" , this.reserved && this.reserved.length ? this.reserved : undefined
"reserved" , this.reserved && this.reserved.length ? this.reserved : undefined,
"comment" , keepComments ? this.comment : undefined,
"comments" , keepComments ? this.comments : undefined
]);
};

Expand Down
18 changes: 14 additions & 4 deletions src/field.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var ruleRe = /^required|optional|repeated$/;
* @throws {TypeError} If arguments are invalid
*/
Field.fromJSON = function fromJSON(name, json) {
return new Field(name, json.id, json.type, json.rule, json.extend, json.options);
return new Field(name, json.id, json.type, json.rule, json.extend, json.options, json.comment);
};

/**
Expand All @@ -50,8 +50,9 @@ Field.fromJSON = function fromJSON(name, json) {
* @param {string|Object.<string,*>} [rule="optional"] Field rule
* @param {string|Object.<string,*>} [extend] Extended type if different from parent
* @param {Object.<string,*>} [options] Declared options
* @param {string} comment Comment associated with this field
*/
function Field(name, id, type, rule, extend, options) {
function Field(name, id, type, rule, extend, options, comment) {

if (util.isObject(rule)) {
options = rule;
Expand Down Expand Up @@ -183,6 +184,12 @@ function Field(name, id, type, rule, extend, options) {
* @private
*/
this._packed = null;

/**
* Comment for this field.
* @type {string|undefined}
*/
this.comment = comment;
}

/**
Expand Down Expand Up @@ -227,15 +234,18 @@ Field.prototype.setOption = function setOption(name, value, ifNotSet) {

/**
* Converts this field to a field descriptor.
* @param {IToJSONOptions} [toJSONOptions] JSON conversion options
* @returns {IField} Field descriptor
*/
Field.prototype.toJSON = function toJSON() {
Field.prototype.toJSON = function toJSON(toJSONOptions) {
var keepComments = toJSONOptions ? (!!toJSONOptions.keepComments) : false;
return util.toObject([
"rule" , this.rule !== "optional" && this.rule || undefined,
"type" , this.type,
"id" , this.id,
"extend" , this.extend,
"options" , this.options
"options" , this.options,
"comment" , keepComments ? this.comment : undefined
]);
};

Expand Down
14 changes: 9 additions & 5 deletions src/mapfield.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ var types = require("./types"),
* @param {string} keyType Key type
* @param {string} type Value type
* @param {Object.<string,*>} [options] Declared options
* @param {string} comment Comment associated with this field
*/
function MapField(name, id, keyType, type, options) {
Field.call(this, name, id, type, options);
function MapField(name, id, keyType, type, options, comment) {
Field.call(this, name, id, type, undefined, undefined, options, comment);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please confirm, this looks like an old bug calling base class constructor with wrong params.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe omitting rule and extend and instead passing options should be handled here. Might well be, though, that specifying undefined explicitly is better optimizable by JS engines?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I didn't see the parameter shuffling in the Field constructor...I think it's clearer to explicitly pass undefined values for these, what do you think? Since options can be undefined here as well, the duck typing in Field() gets a little nondeterministic if these values are omitted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added additional parameter shuffling to handle the comment parameter.


/* istanbul ignore if */
if (!util.isString(keyType))
Expand Down Expand Up @@ -64,20 +65,23 @@ function MapField(name, id, keyType, type, options) {
* @throws {TypeError} If arguments are invalid
*/
MapField.fromJSON = function fromJSON(name, json) {
return new MapField(name, json.id, json.keyType, json.type, json.options);
return new MapField(name, json.id, json.keyType, json.type, json.options, json.comment);
};

/**
* Converts this map field to a map field descriptor.
* @param {IToJSONOptions} [toJSONOptions] JSON conversion options
* @returns {IMapField} Map field descriptor
*/
MapField.prototype.toJSON = function toJSON() {
MapField.prototype.toJSON = function toJSON(toJSONOptions) {
var keepComments = toJSONOptions ? (!!toJSONOptions.keepComments) : false;
return util.toObject([
"keyType" , this.keyType,
"type" , this.type,
"id" , this.id,
"extend" , this.extend,
"options" , this.options
"options" , this.options,
"comment" , keepComments ? this.comment : undefined
]);
};

Expand Down
18 changes: 14 additions & 4 deletions src/method.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ var util = require("./util");
* @param {boolean|Object.<string,*>} [requestStream] Whether the request is streamed
* @param {boolean|Object.<string,*>} [responseStream] Whether the response is streamed
* @param {Object.<string,*>} [options] Declared options
* @param {string} comment The comment for this method
*/
function Method(name, type, requestType, responseType, requestStream, responseStream, options) {
function Method(name, type, requestType, responseType, requestStream, responseStream, options, comment) {

/* istanbul ignore next */
if (util.isObject(requestStream)) {
Expand Down Expand Up @@ -86,6 +87,12 @@ function Method(name, type, requestType, responseType, requestStream, responseSt
* @type {Type|null}
*/
this.resolvedResponseType = null;

/**
* Comment for this method
* @type {string|undefined}
*/
this.comment = comment;
}

/**
Expand All @@ -107,21 +114,24 @@ function Method(name, type, requestType, responseType, requestStream, responseSt
* @throws {TypeError} If arguments are invalid
*/
Method.fromJSON = function fromJSON(name, json) {
return new Method(name, json.type, json.requestType, json.responseType, json.requestStream, json.responseStream, json.options);
return new Method(name, json.type, json.requestType, json.responseType, json.requestStream, json.responseStream, json.options, json.comment);
};

/**
* Converts this method to a method descriptor.
* @param {IToJSONOptions} [toJSONOptions] JSON conversion options
* @returns {IMethod} Method descriptor
*/
Method.prototype.toJSON = function toJSON() {
Method.prototype.toJSON = function toJSON(toJSONOptions) {
var keepComments = toJSONOptions ? (!!toJSONOptions.keepComments) : false;
return util.toObject([
"type" , this.type !== "rpc" && /* istanbul ignore next */ this.type || undefined,
"requestType" , this.requestType,
"requestStream" , this.requestStream,
"responseType" , this.responseType,
"responseStream" , this.responseStream,
"options" , this.options
"options" , this.options,
"comment" , keepComments ? this.comment : undefined
]);
};

Expand Down
10 changes: 6 additions & 4 deletions src/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ Namespace.fromJSON = function fromJSON(name, json) {
* Converts an array of reflection objects to JSON.
* @memberof Namespace
* @param {ReflectionObject[]} array Object array
* @param {IToJSONOptions} [toJSONOptions] JSON conversion options
* @returns {Object.<string,*>|undefined} JSON object or `undefined` when array is empty
*/
function arrayToJSON(array) {
function arrayToJSON(array, toJSONOptions) {
if (!(array && array.length))
return undefined;
var obj = {};
for (var i = 0; i < array.length; ++i)
obj[array[i].name] = array[i].toJSON();
obj[array[i].name] = array[i].toJSON(toJSONOptions);
return obj;
}

Expand Down Expand Up @@ -147,12 +148,13 @@ Object.defineProperty(Namespace.prototype, "nestedArray", {

/**
* Converts this namespace to a namespace descriptor.
* @param {IToJSONOptions} [toJSONOptions] JSON conversion options
* @returns {INamespace} Namespace descriptor
*/
Namespace.prototype.toJSON = function toJSON() {
Namespace.prototype.toJSON = function toJSON(toJSONOptions) {
return util.toObject([
"options" , this.options,
"nested" , arrayToJSON(this.nestedArray)
"nested" , arrayToJSON(this.nestedArray, toJSONOptions)
]);
};

Expand Down
18 changes: 14 additions & 4 deletions src/oneof.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ var Field = require("./field"),
* @param {string} name Oneof name
* @param {string[]|Object.<string,*>} [fieldNames] Field names
* @param {Object.<string,*>} [options] Declared options
* @param {string} comment Comment associated with this field
*/
function OneOf(name, fieldNames, options) {
function OneOf(name, fieldNames, options, comment) {
if (!Array.isArray(fieldNames)) {
options = fieldNames;
fieldNames = undefined;
Expand All @@ -40,6 +41,12 @@ function OneOf(name, fieldNames, options) {
* @readonly
*/
this.fieldsArray = []; // declared readonly for conformance, possibly not yet added to parent

/**
* Comment for this field.
* @type {string|undefined}
*/
this.comment = comment;
}

/**
Expand All @@ -57,17 +64,20 @@ function OneOf(name, fieldNames, options) {
* @throws {TypeError} If arguments are invalid
*/
OneOf.fromJSON = function fromJSON(name, json) {
return new OneOf(name, json.oneof, json.options);
return new OneOf(name, json.oneof, json.options, json.comment);
};

/**
* Converts this oneof to a oneof descriptor.
* @param {IToJSONOptions} [toJSONOptions] JSON conversion options
* @returns {IOneOf} Oneof descriptor
*/
OneOf.prototype.toJSON = function toJSON() {
OneOf.prototype.toJSON = function toJSON(toJSONOptions) {
var keepComments = toJSONOptions ? (!!toJSONOptions.keepComments) : false;
return util.toObject([
"options" , this.options,
"oneof" , this.oneof
"oneof" , this.oneof,
"comment" , keepComments ? this.comment : undefined
]);
};

Expand Down
7 changes: 7 additions & 0 deletions src/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ var base10Re = /^[1-9][0-9]*$/,
* Options modifying the behavior of {@link parse}.
* @interface IParseOptions
* @property {boolean} [keepCase=false] Keeps field casing instead of converting to camel case
* @property {boolean} [alternateCommentMode=false] Recognize double-slash comments in addition to doc-block comments.
*/

/**
* Options modifying the behavior of JSON serialization.
* @interface IToJSONOptions
* @property {boolean} [keepComments=false] Serializes comments.
*/

/**
Expand Down
12 changes: 8 additions & 4 deletions src/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,23 @@ Service.fromJSON = function fromJSON(name, json) {
service.add(Method.fromJSON(names[i], json.methods[names[i]]));
if (json.nested)
service.addJSON(json.nested);
service.comment = json.comment;
return service;
};

/**
* Converts this service to a service descriptor.
* @param {IToJSONOptions} [options] JSON conversion options
* @returns {IService} Service descriptor
*/
Service.prototype.toJSON = function toJSON() {
var inherited = Namespace.prototype.toJSON.call(this);
Service.prototype.toJSON = function toJSON(toJSONOptions) {
var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);
var keepComments = toJSONOptions ? (!!toJSONOptions.keepComments) : false;
return util.toObject([
"options" , inherited && inherited.options || undefined,
"methods" , Namespace.arrayToJSON(this.methodsArray) || /* istanbul ignore next */ {},
"nested" , inherited && inherited.nested || undefined
"methods" , Namespace.arrayToJSON(this.methodsArray, toJSONOptions) || /* istanbul ignore next */ {},
"nested" , inherited && inherited.nested || undefined,
"comment" , keepComments ? this.comment : undefined
]);
};

Expand Down
15 changes: 10 additions & 5 deletions src/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,23 +270,28 @@ Type.fromJSON = function fromJSON(name, json) {
type.reserved = json.reserved;
if (json.group)
type.group = true;
if (json.comment)
type.comment = json.comment;
return type;
};

/**
* Converts this message type to a message type descriptor.
* @param {IToJSONOptions} [toJSONOptions] JSON conversion options
* @returns {IType} Message type descriptor
*/
Type.prototype.toJSON = function toJSON() {
var inherited = Namespace.prototype.toJSON.call(this);
Type.prototype.toJSON = function toJSON(toJSONOptions) {
var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);
var keepComments = toJSONOptions ? (!!toJSONOptions.keepComments) : false;
return util.toObject([
"options" , inherited && inherited.options || undefined,
"oneofs" , Namespace.arrayToJSON(this.oneofsArray),
"fields" , Namespace.arrayToJSON(this.fieldsArray.filter(function(obj) { return !obj.declaringField; })) || {},
"oneofs" , Namespace.arrayToJSON(this.oneofsArray, toJSONOptions),
"fields" , Namespace.arrayToJSON(this.fieldsArray.filter(function(obj) { return !obj.declaringField; }), toJSONOptions) || {},
"extensions" , this.extensions && this.extensions.length ? this.extensions : undefined,
"reserved" , this.reserved && this.reserved.length ? this.reserved : undefined,
"group" , this.group || undefined,
"nested" , inherited && inherited.nested || undefined
"nested" , inherited && inherited.nested || undefined,
"comment" , keepComments ? this.comment : undefined
]);
};

Expand Down
2 changes: 1 addition & 1 deletion tests/api_service.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ tape.test("reflected services", function(test) {
MyMethod = MyService.get("MyMethod");

test.end();
});
});
Loading