From aedc36c3f8736eff1cb781b9e05457463481b3d6 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Wed, 14 Apr 2021 20:26:00 +0100 Subject: [PATCH] fix: added support for Opentelemetry 0.18 (#1234) * fix: added support for Opentelemetry 0.18 Introduces support for Opentelemetry 0.18 API which implements the Tracing 1.0 specification. * style: remove unused import declaration * refactor: removed the `OpenTelemetryTracer`-class and just expose createSpan as function * test: updated the span related tests * test: updated the tests * test: updated the tests * refactor: move the tracer out of the `createSpan`-function Small refactor to ensure no new tracer gets created for each call to the `createSpan`-functio by initiating the `libraryTracer`-variable * fix: get the unit test for publisher opentelemetry working again * test: fix the subscriber unit tests for opentelemetry * chore: remove describe.only * chore: latest changes * test: updated the tests * test: improve the tests * chore: maybe its need to be Google LLC? * feat: add messaging attributes to otel spans * fix: include package.json version as instrumentation version * docs: update the opentelemetry example code * style: remove unused code * fix: remove peer name for now * style: improve explanation regarding otel trace provider * chore: update the opentelemetry versios in the samples `package.json` * test: remove extranous logging in the opentelemetry example app * test: remove the check for `traceId` that doesn't get outputted * fix: remove unnecessary messaging span attribute and added comments * style: use shiny new syntax for undefined check in `subscriber`-class * fix: match the span name with the operation kind in `subscriber`-class * chore: improved code based on PR feedback * fix: BREAKING CHANGE: The Opentelemetry tracing span doesn't expose the `data`-attribute anymore to avoid accidentally exposing privacy/personal data * fix: only include `googclient_OpenTelemetrySpanContext`-attribute when valid span context exists * fix: change the way `package.json` is being imported * fix: revert bad merge that removed tracing on devDependencies At least I think that's what happened... * style: remove unnecessary comment * style: remove unused imports * build: downgrade @sinonjs/fake-timers to v6 The v10 of sinonjs comes with v7 of fake-timers which comes with typescript type definitions that are generated by jsdoc comments which seems to cause trouble when compiling the app it. Downgrading to v6 to avoid needing to solve this problem now. * style: cleanup type definition Removed the `| undefined` for the `span` in the `Publisher`-class Co-authored-by: Aaron Abbott * build: remove `package.json` after compilation step * build: put back package.json in the build * build: explicitly add package.json to the npm pack build * fix: revert "fix: change the way `package.json` is being imported" This reverts commit 0c2c76f8efa66e5f66d97430964fd30ac7e19261. * fix: revert "build: explicitly add package.json to the npm pack build" This reverts commit 14de6257bf63acc6168c4b51c18088a5c74e673b. Co-authored-by: Weyert de Boer Co-authored-by: Megan Potter <57276408+feywind@users.noreply.github.com> Co-authored-by: Megan Potter Co-authored-by: Aaron Abbott --- package.json | 7 +- protos/protos.d.ts | 249 +++++++ protos/protos.js | 688 +++++++++++++++++- protos/protos.json | 79 +- samples/openTelemetryTracing.js | 29 +- samples/package.json | 4 +- .../system-test/openTelemetryTracing.test.js | 1 - src/opentelemetry-tracing.ts | 63 +- src/publisher/index.ts | 46 +- src/subscriber.ts | 41 +- test/opentelemetry-tracing.ts | 39 +- test/publisher/index.ts | 95 ++- test/subscriber.ts | 122 ++-- test/tracing.ts | 41 ++ 14 files changed, 1328 insertions(+), 176 deletions(-) create mode 100644 test/tracing.ts diff --git a/package.json b/package.json index ccacebd6e..5c2a98526 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,8 @@ "@google-cloud/precise-date": "^2.0.0", "@google-cloud/projectify": "^2.0.0", "@google-cloud/promisify": "^2.0.0", - "@opentelemetry/api": "^0.12.0", - "@opentelemetry/tracing": "^0.12.0", + "@opentelemetry/api": "^0.18.1", + "@opentelemetry/semantic-conventions": "^0.18.2", "@types/duplexify": "^3.6.0", "@types/long": "^4.0.0", "arrify": "^2.0.0", @@ -69,6 +69,7 @@ "@grpc/proto-loader": "^0.6.0", "@microsoft/api-documenter": "^7.8.10", "@microsoft/api-extractor": "^7.8.10", + "@opentelemetry/tracing": "^0.18.0", "@types/execa": "^0.9.0", "@types/extend": "^3.0.0", "@types/lodash.snakecase": "^4.1.6", @@ -77,7 +78,7 @@ "@types/ncp": "^2.0.1", "@types/node": "^12.12.30", "@types/proxyquire": "^1.3.28", - "@types/sinon": "^9.0.0", + "@types/sinon": "^9.0.11", "@types/tmp": "^0.2.0", "@types/uuid": "^8.0.0", "c8": "^7.0.0", diff --git a/protos/protos.d.ts b/protos/protos.d.ts index 6ae2e2767..2bc15192a 100644 --- a/protos/protos.d.ts +++ b/protos/protos.d.ts @@ -6210,6 +6210,9 @@ export namespace google { /** Http rules */ rules?: (google.api.IHttpRule[]|null); + + /** Http fullyDecodeReservedExpansion */ + fullyDecodeReservedExpansion?: (boolean|null); } /** Represents a Http. */ @@ -6224,6 +6227,9 @@ export namespace google { /** Http rules. */ public rules: google.api.IHttpRule[]; + /** Http fullyDecodeReservedExpansion. */ + public fullyDecodeReservedExpansion: boolean; + /** * Creates a new Http instance using the specified properties. * @param [properties] Properties to set @@ -6322,6 +6328,9 @@ export namespace google { /** HttpRule body */ body?: (string|null); + /** HttpRule responseBody */ + responseBody?: (string|null); + /** HttpRule additionalBindings */ additionalBindings?: (google.api.IHttpRule[]|null); } @@ -6359,6 +6368,9 @@ export namespace google { /** HttpRule body. */ public body: string; + /** HttpRule responseBody. */ + public responseBody: string; + /** HttpRule additionalBindings. */ public additionalBindings: google.api.IHttpRule[]; @@ -7185,6 +7197,9 @@ export namespace google { /** ExtensionRange end */ end?: (number|null); + + /** ExtensionRange options */ + options?: (google.protobuf.IExtensionRangeOptions|null); } /** Represents an ExtensionRange. */ @@ -7202,6 +7217,9 @@ export namespace google { /** ExtensionRange end. */ public end: number; + /** ExtensionRange options. */ + public options?: (google.protobuf.IExtensionRangeOptions|null); + /** * Creates a new ExtensionRange instance using the specified properties. * @param [properties] Properties to set @@ -7370,6 +7388,96 @@ export namespace google { } } + /** Properties of an ExtensionRangeOptions. */ + interface IExtensionRangeOptions { + + /** ExtensionRangeOptions uninterpretedOption */ + uninterpretedOption?: (google.protobuf.IUninterpretedOption[]|null); + } + + /** Represents an ExtensionRangeOptions. */ + class ExtensionRangeOptions implements IExtensionRangeOptions { + + /** + * Constructs a new ExtensionRangeOptions. + * @param [properties] Properties to set + */ + constructor(properties?: google.protobuf.IExtensionRangeOptions); + + /** ExtensionRangeOptions uninterpretedOption. */ + public uninterpretedOption: google.protobuf.IUninterpretedOption[]; + + /** + * Creates a new ExtensionRangeOptions instance using the specified properties. + * @param [properties] Properties to set + * @returns ExtensionRangeOptions instance + */ + public static create(properties?: google.protobuf.IExtensionRangeOptions): google.protobuf.ExtensionRangeOptions; + + /** + * Encodes the specified ExtensionRangeOptions message. Does not implicitly {@link google.protobuf.ExtensionRangeOptions.verify|verify} messages. + * @param message ExtensionRangeOptions message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: google.protobuf.IExtensionRangeOptions, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified ExtensionRangeOptions message, length delimited. Does not implicitly {@link google.protobuf.ExtensionRangeOptions.verify|verify} messages. + * @param message ExtensionRangeOptions message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: google.protobuf.IExtensionRangeOptions, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes an ExtensionRangeOptions message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns ExtensionRangeOptions + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): google.protobuf.ExtensionRangeOptions; + + /** + * Decodes an ExtensionRangeOptions message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns ExtensionRangeOptions + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): google.protobuf.ExtensionRangeOptions; + + /** + * Verifies an ExtensionRangeOptions message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates an ExtensionRangeOptions message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns ExtensionRangeOptions + */ + public static fromObject(object: { [k: string]: any }): google.protobuf.ExtensionRangeOptions; + + /** + * Creates a plain object from an ExtensionRangeOptions message. Also converts values to other types if specified. + * @param message ExtensionRangeOptions + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: google.protobuf.ExtensionRangeOptions, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this ExtensionRangeOptions to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + /** Properties of a FieldDescriptorProto. */ interface IFieldDescriptorProto { @@ -7402,6 +7510,9 @@ export namespace google { /** FieldDescriptorProto options */ options?: (google.protobuf.IFieldOptions|null); + + /** FieldDescriptorProto proto3Optional */ + proto3Optional?: (boolean|null); } /** Represents a FieldDescriptorProto. */ @@ -7443,6 +7554,9 @@ export namespace google { /** FieldDescriptorProto options. */ public options?: (google.protobuf.IFieldOptions|null); + /** FieldDescriptorProto proto3Optional. */ + public proto3Optional: boolean; + /** * Creates a new FieldDescriptorProto instance using the specified properties. * @param [properties] Properties to set @@ -7653,6 +7767,12 @@ export namespace google { /** EnumDescriptorProto options */ options?: (google.protobuf.IEnumOptions|null); + + /** EnumDescriptorProto reservedRange */ + reservedRange?: (google.protobuf.EnumDescriptorProto.IEnumReservedRange[]|null); + + /** EnumDescriptorProto reservedName */ + reservedName?: (string[]|null); } /** Represents an EnumDescriptorProto. */ @@ -7673,6 +7793,12 @@ export namespace google { /** EnumDescriptorProto options. */ public options?: (google.protobuf.IEnumOptions|null); + /** EnumDescriptorProto reservedRange. */ + public reservedRange: google.protobuf.EnumDescriptorProto.IEnumReservedRange[]; + + /** EnumDescriptorProto reservedName. */ + public reservedName: string[]; + /** * Creates a new EnumDescriptorProto instance using the specified properties. * @param [properties] Properties to set @@ -7744,6 +7870,105 @@ export namespace google { public toJSON(): { [k: string]: any }; } + namespace EnumDescriptorProto { + + /** Properties of an EnumReservedRange. */ + interface IEnumReservedRange { + + /** EnumReservedRange start */ + start?: (number|null); + + /** EnumReservedRange end */ + end?: (number|null); + } + + /** Represents an EnumReservedRange. */ + class EnumReservedRange implements IEnumReservedRange { + + /** + * Constructs a new EnumReservedRange. + * @param [properties] Properties to set + */ + constructor(properties?: google.protobuf.EnumDescriptorProto.IEnumReservedRange); + + /** EnumReservedRange start. */ + public start: number; + + /** EnumReservedRange end. */ + public end: number; + + /** + * Creates a new EnumReservedRange instance using the specified properties. + * @param [properties] Properties to set + * @returns EnumReservedRange instance + */ + public static create(properties?: google.protobuf.EnumDescriptorProto.IEnumReservedRange): google.protobuf.EnumDescriptorProto.EnumReservedRange; + + /** + * Encodes the specified EnumReservedRange message. Does not implicitly {@link google.protobuf.EnumDescriptorProto.EnumReservedRange.verify|verify} messages. + * @param message EnumReservedRange message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: google.protobuf.EnumDescriptorProto.IEnumReservedRange, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified EnumReservedRange message, length delimited. Does not implicitly {@link google.protobuf.EnumDescriptorProto.EnumReservedRange.verify|verify} messages. + * @param message EnumReservedRange message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: google.protobuf.EnumDescriptorProto.IEnumReservedRange, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes an EnumReservedRange message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns EnumReservedRange + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): google.protobuf.EnumDescriptorProto.EnumReservedRange; + + /** + * Decodes an EnumReservedRange message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns EnumReservedRange + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): google.protobuf.EnumDescriptorProto.EnumReservedRange; + + /** + * Verifies an EnumReservedRange message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates an EnumReservedRange message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns EnumReservedRange + */ + public static fromObject(object: { [k: string]: any }): google.protobuf.EnumDescriptorProto.EnumReservedRange; + + /** + * Creates a plain object from an EnumReservedRange message. Also converts values to other types if specified. + * @param message EnumReservedRange + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: google.protobuf.EnumDescriptorProto.EnumReservedRange, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this EnumReservedRange to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } + } + /** Properties of an EnumValueDescriptorProto. */ interface IEnumValueDescriptorProto { @@ -8101,6 +8326,9 @@ export namespace google { /** FileOptions pyGenericServices */ pyGenericServices?: (boolean|null); + /** FileOptions phpGenericServices */ + phpGenericServices?: (boolean|null); + /** FileOptions deprecated */ deprecated?: (boolean|null); @@ -8119,6 +8347,15 @@ export namespace google { /** FileOptions phpClassPrefix */ phpClassPrefix?: (string|null); + /** FileOptions phpNamespace */ + phpNamespace?: (string|null); + + /** FileOptions phpMetadataNamespace */ + phpMetadataNamespace?: (string|null); + + /** FileOptions rubyPackage */ + rubyPackage?: (string|null); + /** FileOptions uninterpretedOption */ uninterpretedOption?: (google.protobuf.IUninterpretedOption[]|null); @@ -8165,6 +8402,9 @@ export namespace google { /** FileOptions pyGenericServices. */ public pyGenericServices: boolean; + /** FileOptions phpGenericServices. */ + public phpGenericServices: boolean; + /** FileOptions deprecated. */ public deprecated: boolean; @@ -8183,6 +8423,15 @@ export namespace google { /** FileOptions phpClassPrefix. */ public phpClassPrefix: string; + /** FileOptions phpNamespace. */ + public phpNamespace: string; + + /** FileOptions phpMetadataNamespace. */ + public phpMetadataNamespace: string; + + /** FileOptions rubyPackage. */ + public rubyPackage: string; + /** FileOptions uninterpretedOption. */ public uninterpretedOption: google.protobuf.IUninterpretedOption[]; diff --git a/protos/protos.js b/protos/protos.js index 456396fe5..2d3c27e71 100644 --- a/protos/protos.js +++ b/protos/protos.js @@ -14123,6 +14123,7 @@ * @memberof google.api * @interface IHttp * @property {Array.|null} [rules] Http rules + * @property {boolean|null} [fullyDecodeReservedExpansion] Http fullyDecodeReservedExpansion */ /** @@ -14149,6 +14150,14 @@ */ Http.prototype.rules = $util.emptyArray; + /** + * Http fullyDecodeReservedExpansion. + * @member {boolean} fullyDecodeReservedExpansion + * @memberof google.api.Http + * @instance + */ + Http.prototype.fullyDecodeReservedExpansion = false; + /** * Creates a new Http instance using the specified properties. * @function create @@ -14176,6 +14185,8 @@ if (message.rules != null && message.rules.length) for (var i = 0; i < message.rules.length; ++i) $root.google.api.HttpRule.encode(message.rules[i], writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.fullyDecodeReservedExpansion != null && Object.hasOwnProperty.call(message, "fullyDecodeReservedExpansion")) + writer.uint32(/* id 2, wireType 0 =*/16).bool(message.fullyDecodeReservedExpansion); return writer; }; @@ -14215,6 +14226,9 @@ message.rules = []; message.rules.push($root.google.api.HttpRule.decode(reader, reader.uint32())); break; + case 2: + message.fullyDecodeReservedExpansion = reader.bool(); + break; default: reader.skipType(tag & 7); break; @@ -14259,6 +14273,9 @@ return "rules." + error; } } + if (message.fullyDecodeReservedExpansion != null && message.hasOwnProperty("fullyDecodeReservedExpansion")) + if (typeof message.fullyDecodeReservedExpansion !== "boolean") + return "fullyDecodeReservedExpansion: boolean expected"; return null; }; @@ -14284,6 +14301,8 @@ message.rules[i] = $root.google.api.HttpRule.fromObject(object.rules[i]); } } + if (object.fullyDecodeReservedExpansion != null) + message.fullyDecodeReservedExpansion = Boolean(object.fullyDecodeReservedExpansion); return message; }; @@ -14302,11 +14321,15 @@ var object = {}; if (options.arrays || options.defaults) object.rules = []; + if (options.defaults) + object.fullyDecodeReservedExpansion = false; if (message.rules && message.rules.length) { object.rules = []; for (var j = 0; j < message.rules.length; ++j) object.rules[j] = $root.google.api.HttpRule.toObject(message.rules[j], options); } + if (message.fullyDecodeReservedExpansion != null && message.hasOwnProperty("fullyDecodeReservedExpansion")) + object.fullyDecodeReservedExpansion = message.fullyDecodeReservedExpansion; return object; }; @@ -14338,6 +14361,7 @@ * @property {string|null} [patch] HttpRule patch * @property {google.api.ICustomHttpPattern|null} [custom] HttpRule custom * @property {string|null} [body] HttpRule body + * @property {string|null} [responseBody] HttpRule responseBody * @property {Array.|null} [additionalBindings] HttpRule additionalBindings */ @@ -14421,6 +14445,14 @@ */ HttpRule.prototype.body = ""; + /** + * HttpRule responseBody. + * @member {string} responseBody + * @memberof google.api.HttpRule + * @instance + */ + HttpRule.prototype.responseBody = ""; + /** * HttpRule additionalBindings. * @member {Array.} additionalBindings @@ -14486,6 +14518,8 @@ if (message.additionalBindings != null && message.additionalBindings.length) for (var i = 0; i < message.additionalBindings.length; ++i) $root.google.api.HttpRule.encode(message.additionalBindings[i], writer.uint32(/* id 11, wireType 2 =*/90).fork()).ldelim(); + if (message.responseBody != null && Object.hasOwnProperty.call(message, "responseBody")) + writer.uint32(/* id 12, wireType 2 =*/98).string(message.responseBody); return writer; }; @@ -14544,6 +14578,9 @@ case 7: message.body = reader.string(); break; + case 12: + message.responseBody = reader.string(); + break; case 11: if (!(message.additionalBindings && message.additionalBindings.length)) message.additionalBindings = []; @@ -14634,6 +14671,9 @@ if (message.body != null && message.hasOwnProperty("body")) if (!$util.isString(message.body)) return "body: string expected"; + if (message.responseBody != null && message.hasOwnProperty("responseBody")) + if (!$util.isString(message.responseBody)) + return "responseBody: string expected"; if (message.additionalBindings != null && message.hasOwnProperty("additionalBindings")) { if (!Array.isArray(message.additionalBindings)) return "additionalBindings: array expected"; @@ -14677,6 +14717,8 @@ } if (object.body != null) message.body = String(object.body); + if (object.responseBody != null) + message.responseBody = String(object.responseBody); if (object.additionalBindings) { if (!Array.isArray(object.additionalBindings)) throw TypeError(".google.api.HttpRule.additionalBindings: array expected"); @@ -14708,6 +14750,7 @@ if (options.defaults) { object.selector = ""; object.body = ""; + object.responseBody = ""; } if (message.selector != null && message.hasOwnProperty("selector")) object.selector = message.selector; @@ -14748,6 +14791,8 @@ for (var j = 0; j < message.additionalBindings.length; ++j) object.additionalBindings[j] = $root.google.api.HttpRule.toObject(message.additionalBindings[j], options); } + if (message.responseBody != null && message.hasOwnProperty("responseBody")) + object.responseBody = message.responseBody; return object; }; @@ -16999,6 +17044,7 @@ * @interface IExtensionRange * @property {number|null} [start] ExtensionRange start * @property {number|null} [end] ExtensionRange end + * @property {google.protobuf.IExtensionRangeOptions|null} [options] ExtensionRange options */ /** @@ -17032,6 +17078,14 @@ */ ExtensionRange.prototype.end = 0; + /** + * ExtensionRange options. + * @member {google.protobuf.IExtensionRangeOptions|null|undefined} options + * @memberof google.protobuf.DescriptorProto.ExtensionRange + * @instance + */ + ExtensionRange.prototype.options = null; + /** * Creates a new ExtensionRange instance using the specified properties. * @function create @@ -17060,6 +17114,8 @@ writer.uint32(/* id 1, wireType 0 =*/8).int32(message.start); if (message.end != null && Object.hasOwnProperty.call(message, "end")) writer.uint32(/* id 2, wireType 0 =*/16).int32(message.end); + if (message.options != null && Object.hasOwnProperty.call(message, "options")) + $root.google.protobuf.ExtensionRangeOptions.encode(message.options, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); return writer; }; @@ -17100,6 +17156,9 @@ case 2: message.end = reader.int32(); break; + case 3: + message.options = $root.google.protobuf.ExtensionRangeOptions.decode(reader, reader.uint32()); + break; default: reader.skipType(tag & 7); break; @@ -17141,6 +17200,11 @@ if (message.end != null && message.hasOwnProperty("end")) if (!$util.isInteger(message.end)) return "end: integer expected"; + if (message.options != null && message.hasOwnProperty("options")) { + var error = $root.google.protobuf.ExtensionRangeOptions.verify(message.options); + if (error) + return "options." + error; + } return null; }; @@ -17160,6 +17224,11 @@ message.start = object.start | 0; if (object.end != null) message.end = object.end | 0; + if (object.options != null) { + if (typeof object.options !== "object") + throw TypeError(".google.protobuf.DescriptorProto.ExtensionRange.options: object expected"); + message.options = $root.google.protobuf.ExtensionRangeOptions.fromObject(object.options); + } return message; }; @@ -17179,11 +17248,14 @@ if (options.defaults) { object.start = 0; object.end = 0; + object.options = null; } if (message.start != null && message.hasOwnProperty("start")) object.start = message.start; if (message.end != null && message.hasOwnProperty("end")) object.end = message.end; + if (message.options != null && message.hasOwnProperty("options")) + object.options = $root.google.protobuf.ExtensionRangeOptions.toObject(message.options, options); return object; }; @@ -17414,6 +17486,214 @@ return DescriptorProto; })(); + protobuf.ExtensionRangeOptions = (function() { + + /** + * Properties of an ExtensionRangeOptions. + * @memberof google.protobuf + * @interface IExtensionRangeOptions + * @property {Array.|null} [uninterpretedOption] ExtensionRangeOptions uninterpretedOption + */ + + /** + * Constructs a new ExtensionRangeOptions. + * @memberof google.protobuf + * @classdesc Represents an ExtensionRangeOptions. + * @implements IExtensionRangeOptions + * @constructor + * @param {google.protobuf.IExtensionRangeOptions=} [properties] Properties to set + */ + function ExtensionRangeOptions(properties) { + this.uninterpretedOption = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ExtensionRangeOptions uninterpretedOption. + * @member {Array.} uninterpretedOption + * @memberof google.protobuf.ExtensionRangeOptions + * @instance + */ + ExtensionRangeOptions.prototype.uninterpretedOption = $util.emptyArray; + + /** + * Creates a new ExtensionRangeOptions instance using the specified properties. + * @function create + * @memberof google.protobuf.ExtensionRangeOptions + * @static + * @param {google.protobuf.IExtensionRangeOptions=} [properties] Properties to set + * @returns {google.protobuf.ExtensionRangeOptions} ExtensionRangeOptions instance + */ + ExtensionRangeOptions.create = function create(properties) { + return new ExtensionRangeOptions(properties); + }; + + /** + * Encodes the specified ExtensionRangeOptions message. Does not implicitly {@link google.protobuf.ExtensionRangeOptions.verify|verify} messages. + * @function encode + * @memberof google.protobuf.ExtensionRangeOptions + * @static + * @param {google.protobuf.IExtensionRangeOptions} message ExtensionRangeOptions message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ExtensionRangeOptions.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.uninterpretedOption != null && message.uninterpretedOption.length) + for (var i = 0; i < message.uninterpretedOption.length; ++i) + $root.google.protobuf.UninterpretedOption.encode(message.uninterpretedOption[i], writer.uint32(/* id 999, wireType 2 =*/7994).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified ExtensionRangeOptions message, length delimited. Does not implicitly {@link google.protobuf.ExtensionRangeOptions.verify|verify} messages. + * @function encodeDelimited + * @memberof google.protobuf.ExtensionRangeOptions + * @static + * @param {google.protobuf.IExtensionRangeOptions} message ExtensionRangeOptions message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ExtensionRangeOptions.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes an ExtensionRangeOptions message from the specified reader or buffer. + * @function decode + * @memberof google.protobuf.ExtensionRangeOptions + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {google.protobuf.ExtensionRangeOptions} ExtensionRangeOptions + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ExtensionRangeOptions.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.google.protobuf.ExtensionRangeOptions(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 999: + if (!(message.uninterpretedOption && message.uninterpretedOption.length)) + message.uninterpretedOption = []; + message.uninterpretedOption.push($root.google.protobuf.UninterpretedOption.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes an ExtensionRangeOptions message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof google.protobuf.ExtensionRangeOptions + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {google.protobuf.ExtensionRangeOptions} ExtensionRangeOptions + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ExtensionRangeOptions.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies an ExtensionRangeOptions message. + * @function verify + * @memberof google.protobuf.ExtensionRangeOptions + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ExtensionRangeOptions.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.uninterpretedOption != null && message.hasOwnProperty("uninterpretedOption")) { + if (!Array.isArray(message.uninterpretedOption)) + return "uninterpretedOption: array expected"; + for (var i = 0; i < message.uninterpretedOption.length; ++i) { + var error = $root.google.protobuf.UninterpretedOption.verify(message.uninterpretedOption[i]); + if (error) + return "uninterpretedOption." + error; + } + } + return null; + }; + + /** + * Creates an ExtensionRangeOptions message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof google.protobuf.ExtensionRangeOptions + * @static + * @param {Object.} object Plain object + * @returns {google.protobuf.ExtensionRangeOptions} ExtensionRangeOptions + */ + ExtensionRangeOptions.fromObject = function fromObject(object) { + if (object instanceof $root.google.protobuf.ExtensionRangeOptions) + return object; + var message = new $root.google.protobuf.ExtensionRangeOptions(); + if (object.uninterpretedOption) { + if (!Array.isArray(object.uninterpretedOption)) + throw TypeError(".google.protobuf.ExtensionRangeOptions.uninterpretedOption: array expected"); + message.uninterpretedOption = []; + for (var i = 0; i < object.uninterpretedOption.length; ++i) { + if (typeof object.uninterpretedOption[i] !== "object") + throw TypeError(".google.protobuf.ExtensionRangeOptions.uninterpretedOption: object expected"); + message.uninterpretedOption[i] = $root.google.protobuf.UninterpretedOption.fromObject(object.uninterpretedOption[i]); + } + } + return message; + }; + + /** + * Creates a plain object from an ExtensionRangeOptions message. Also converts values to other types if specified. + * @function toObject + * @memberof google.protobuf.ExtensionRangeOptions + * @static + * @param {google.protobuf.ExtensionRangeOptions} message ExtensionRangeOptions + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ExtensionRangeOptions.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.uninterpretedOption = []; + if (message.uninterpretedOption && message.uninterpretedOption.length) { + object.uninterpretedOption = []; + for (var j = 0; j < message.uninterpretedOption.length; ++j) + object.uninterpretedOption[j] = $root.google.protobuf.UninterpretedOption.toObject(message.uninterpretedOption[j], options); + } + return object; + }; + + /** + * Converts this ExtensionRangeOptions to JSON. + * @function toJSON + * @memberof google.protobuf.ExtensionRangeOptions + * @instance + * @returns {Object.} JSON object + */ + ExtensionRangeOptions.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return ExtensionRangeOptions; + })(); + protobuf.FieldDescriptorProto = (function() { /** @@ -17430,6 +17710,7 @@ * @property {number|null} [oneofIndex] FieldDescriptorProto oneofIndex * @property {string|null} [jsonName] FieldDescriptorProto jsonName * @property {google.protobuf.IFieldOptions|null} [options] FieldDescriptorProto options + * @property {boolean|null} [proto3Optional] FieldDescriptorProto proto3Optional */ /** @@ -17527,6 +17808,14 @@ */ FieldDescriptorProto.prototype.options = null; + /** + * FieldDescriptorProto proto3Optional. + * @member {boolean} proto3Optional + * @memberof google.protobuf.FieldDescriptorProto + * @instance + */ + FieldDescriptorProto.prototype.proto3Optional = false; + /** * Creates a new FieldDescriptorProto instance using the specified properties. * @function create @@ -17571,6 +17860,8 @@ writer.uint32(/* id 9, wireType 0 =*/72).int32(message.oneofIndex); if (message.jsonName != null && Object.hasOwnProperty.call(message, "jsonName")) writer.uint32(/* id 10, wireType 2 =*/82).string(message.jsonName); + if (message.proto3Optional != null && Object.hasOwnProperty.call(message, "proto3Optional")) + writer.uint32(/* id 17, wireType 0 =*/136).bool(message.proto3Optional); return writer; }; @@ -17635,6 +17926,9 @@ case 8: message.options = $root.google.protobuf.FieldOptions.decode(reader, reader.uint32()); break; + case 17: + message.proto3Optional = reader.bool(); + break; default: reader.skipType(tag & 7); break; @@ -17729,6 +18023,9 @@ if (error) return "options." + error; } + if (message.proto3Optional != null && message.hasOwnProperty("proto3Optional")) + if (typeof message.proto3Optional !== "boolean") + return "proto3Optional: boolean expected"; return null; }; @@ -17851,6 +18148,8 @@ throw TypeError(".google.protobuf.FieldDescriptorProto.options: object expected"); message.options = $root.google.protobuf.FieldOptions.fromObject(object.options); } + if (object.proto3Optional != null) + message.proto3Optional = Boolean(object.proto3Optional); return message; }; @@ -17878,6 +18177,7 @@ object.options = null; object.oneofIndex = 0; object.jsonName = ""; + object.proto3Optional = false; } if (message.name != null && message.hasOwnProperty("name")) object.name = message.name; @@ -17899,6 +18199,8 @@ object.oneofIndex = message.oneofIndex; if (message.jsonName != null && message.hasOwnProperty("jsonName")) object.jsonName = message.jsonName; + if (message.proto3Optional != null && message.hasOwnProperty("proto3Optional")) + object.proto3Optional = message.proto3Optional; return object; }; @@ -18202,6 +18504,8 @@ * @property {string|null} [name] EnumDescriptorProto name * @property {Array.|null} [value] EnumDescriptorProto value * @property {google.protobuf.IEnumOptions|null} [options] EnumDescriptorProto options + * @property {Array.|null} [reservedRange] EnumDescriptorProto reservedRange + * @property {Array.|null} [reservedName] EnumDescriptorProto reservedName */ /** @@ -18214,6 +18518,8 @@ */ function EnumDescriptorProto(properties) { this.value = []; + this.reservedRange = []; + this.reservedName = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -18244,6 +18550,22 @@ */ EnumDescriptorProto.prototype.options = null; + /** + * EnumDescriptorProto reservedRange. + * @member {Array.} reservedRange + * @memberof google.protobuf.EnumDescriptorProto + * @instance + */ + EnumDescriptorProto.prototype.reservedRange = $util.emptyArray; + + /** + * EnumDescriptorProto reservedName. + * @member {Array.} reservedName + * @memberof google.protobuf.EnumDescriptorProto + * @instance + */ + EnumDescriptorProto.prototype.reservedName = $util.emptyArray; + /** * Creates a new EnumDescriptorProto instance using the specified properties. * @function create @@ -18275,6 +18597,12 @@ $root.google.protobuf.EnumValueDescriptorProto.encode(message.value[i], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); if (message.options != null && Object.hasOwnProperty.call(message, "options")) $root.google.protobuf.EnumOptions.encode(message.options, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); + if (message.reservedRange != null && message.reservedRange.length) + for (var i = 0; i < message.reservedRange.length; ++i) + $root.google.protobuf.EnumDescriptorProto.EnumReservedRange.encode(message.reservedRange[i], writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim(); + if (message.reservedName != null && message.reservedName.length) + for (var i = 0; i < message.reservedName.length; ++i) + writer.uint32(/* id 5, wireType 2 =*/42).string(message.reservedName[i]); return writer; }; @@ -18320,6 +18648,16 @@ case 3: message.options = $root.google.protobuf.EnumOptions.decode(reader, reader.uint32()); break; + case 4: + if (!(message.reservedRange && message.reservedRange.length)) + message.reservedRange = []; + message.reservedRange.push($root.google.protobuf.EnumDescriptorProto.EnumReservedRange.decode(reader, reader.uint32())); + break; + case 5: + if (!(message.reservedName && message.reservedName.length)) + message.reservedName = []; + message.reservedName.push(reader.string()); + break; default: reader.skipType(tag & 7); break; @@ -18372,6 +18710,22 @@ if (error) return "options." + error; } + if (message.reservedRange != null && message.hasOwnProperty("reservedRange")) { + if (!Array.isArray(message.reservedRange)) + return "reservedRange: array expected"; + for (var i = 0; i < message.reservedRange.length; ++i) { + var error = $root.google.protobuf.EnumDescriptorProto.EnumReservedRange.verify(message.reservedRange[i]); + if (error) + return "reservedRange." + error; + } + } + if (message.reservedName != null && message.hasOwnProperty("reservedName")) { + if (!Array.isArray(message.reservedName)) + return "reservedName: array expected"; + for (var i = 0; i < message.reservedName.length; ++i) + if (!$util.isString(message.reservedName[i])) + return "reservedName: string[] expected"; + } return null; }; @@ -18404,6 +18758,23 @@ throw TypeError(".google.protobuf.EnumDescriptorProto.options: object expected"); message.options = $root.google.protobuf.EnumOptions.fromObject(object.options); } + if (object.reservedRange) { + if (!Array.isArray(object.reservedRange)) + throw TypeError(".google.protobuf.EnumDescriptorProto.reservedRange: array expected"); + message.reservedRange = []; + for (var i = 0; i < object.reservedRange.length; ++i) { + if (typeof object.reservedRange[i] !== "object") + throw TypeError(".google.protobuf.EnumDescriptorProto.reservedRange: object expected"); + message.reservedRange[i] = $root.google.protobuf.EnumDescriptorProto.EnumReservedRange.fromObject(object.reservedRange[i]); + } + } + if (object.reservedName) { + if (!Array.isArray(object.reservedName)) + throw TypeError(".google.protobuf.EnumDescriptorProto.reservedName: array expected"); + message.reservedName = []; + for (var i = 0; i < object.reservedName.length; ++i) + message.reservedName[i] = String(object.reservedName[i]); + } return message; }; @@ -18420,8 +18791,11 @@ if (!options) options = {}; var object = {}; - if (options.arrays || options.defaults) + if (options.arrays || options.defaults) { object.value = []; + object.reservedRange = []; + object.reservedName = []; + } if (options.defaults) { object.name = ""; object.options = null; @@ -18435,6 +18809,16 @@ } if (message.options != null && message.hasOwnProperty("options")) object.options = $root.google.protobuf.EnumOptions.toObject(message.options, options); + if (message.reservedRange && message.reservedRange.length) { + object.reservedRange = []; + for (var j = 0; j < message.reservedRange.length; ++j) + object.reservedRange[j] = $root.google.protobuf.EnumDescriptorProto.EnumReservedRange.toObject(message.reservedRange[j], options); + } + if (message.reservedName && message.reservedName.length) { + object.reservedName = []; + for (var j = 0; j < message.reservedName.length; ++j) + object.reservedName[j] = message.reservedName[j]; + } return object; }; @@ -18449,6 +18833,216 @@ return this.constructor.toObject(this, $protobuf.util.toJSONOptions); }; + EnumDescriptorProto.EnumReservedRange = (function() { + + /** + * Properties of an EnumReservedRange. + * @memberof google.protobuf.EnumDescriptorProto + * @interface IEnumReservedRange + * @property {number|null} [start] EnumReservedRange start + * @property {number|null} [end] EnumReservedRange end + */ + + /** + * Constructs a new EnumReservedRange. + * @memberof google.protobuf.EnumDescriptorProto + * @classdesc Represents an EnumReservedRange. + * @implements IEnumReservedRange + * @constructor + * @param {google.protobuf.EnumDescriptorProto.IEnumReservedRange=} [properties] Properties to set + */ + function EnumReservedRange(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * EnumReservedRange start. + * @member {number} start + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @instance + */ + EnumReservedRange.prototype.start = 0; + + /** + * EnumReservedRange end. + * @member {number} end + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @instance + */ + EnumReservedRange.prototype.end = 0; + + /** + * Creates a new EnumReservedRange instance using the specified properties. + * @function create + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @static + * @param {google.protobuf.EnumDescriptorProto.IEnumReservedRange=} [properties] Properties to set + * @returns {google.protobuf.EnumDescriptorProto.EnumReservedRange} EnumReservedRange instance + */ + EnumReservedRange.create = function create(properties) { + return new EnumReservedRange(properties); + }; + + /** + * Encodes the specified EnumReservedRange message. Does not implicitly {@link google.protobuf.EnumDescriptorProto.EnumReservedRange.verify|verify} messages. + * @function encode + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @static + * @param {google.protobuf.EnumDescriptorProto.IEnumReservedRange} message EnumReservedRange message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + EnumReservedRange.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.start != null && Object.hasOwnProperty.call(message, "start")) + writer.uint32(/* id 1, wireType 0 =*/8).int32(message.start); + if (message.end != null && Object.hasOwnProperty.call(message, "end")) + writer.uint32(/* id 2, wireType 0 =*/16).int32(message.end); + return writer; + }; + + /** + * Encodes the specified EnumReservedRange message, length delimited. Does not implicitly {@link google.protobuf.EnumDescriptorProto.EnumReservedRange.verify|verify} messages. + * @function encodeDelimited + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @static + * @param {google.protobuf.EnumDescriptorProto.IEnumReservedRange} message EnumReservedRange message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + EnumReservedRange.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes an EnumReservedRange message from the specified reader or buffer. + * @function decode + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {google.protobuf.EnumDescriptorProto.EnumReservedRange} EnumReservedRange + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + EnumReservedRange.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.google.protobuf.EnumDescriptorProto.EnumReservedRange(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.start = reader.int32(); + break; + case 2: + message.end = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes an EnumReservedRange message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {google.protobuf.EnumDescriptorProto.EnumReservedRange} EnumReservedRange + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + EnumReservedRange.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies an EnumReservedRange message. + * @function verify + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + EnumReservedRange.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.start != null && message.hasOwnProperty("start")) + if (!$util.isInteger(message.start)) + return "start: integer expected"; + if (message.end != null && message.hasOwnProperty("end")) + if (!$util.isInteger(message.end)) + return "end: integer expected"; + return null; + }; + + /** + * Creates an EnumReservedRange message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @static + * @param {Object.} object Plain object + * @returns {google.protobuf.EnumDescriptorProto.EnumReservedRange} EnumReservedRange + */ + EnumReservedRange.fromObject = function fromObject(object) { + if (object instanceof $root.google.protobuf.EnumDescriptorProto.EnumReservedRange) + return object; + var message = new $root.google.protobuf.EnumDescriptorProto.EnumReservedRange(); + if (object.start != null) + message.start = object.start | 0; + if (object.end != null) + message.end = object.end | 0; + return message; + }; + + /** + * Creates a plain object from an EnumReservedRange message. Also converts values to other types if specified. + * @function toObject + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @static + * @param {google.protobuf.EnumDescriptorProto.EnumReservedRange} message EnumReservedRange + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + EnumReservedRange.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.start = 0; + object.end = 0; + } + if (message.start != null && message.hasOwnProperty("start")) + object.start = message.start; + if (message.end != null && message.hasOwnProperty("end")) + object.end = message.end; + return object; + }; + + /** + * Converts this EnumReservedRange to JSON. + * @function toJSON + * @memberof google.protobuf.EnumDescriptorProto.EnumReservedRange + * @instance + * @returns {Object.} JSON object + */ + EnumReservedRange.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return EnumReservedRange; + })(); + return EnumDescriptorProto; })(); @@ -19267,12 +19861,16 @@ * @property {boolean|null} [ccGenericServices] FileOptions ccGenericServices * @property {boolean|null} [javaGenericServices] FileOptions javaGenericServices * @property {boolean|null} [pyGenericServices] FileOptions pyGenericServices + * @property {boolean|null} [phpGenericServices] FileOptions phpGenericServices * @property {boolean|null} [deprecated] FileOptions deprecated * @property {boolean|null} [ccEnableArenas] FileOptions ccEnableArenas * @property {string|null} [objcClassPrefix] FileOptions objcClassPrefix * @property {string|null} [csharpNamespace] FileOptions csharpNamespace * @property {string|null} [swiftPrefix] FileOptions swiftPrefix * @property {string|null} [phpClassPrefix] FileOptions phpClassPrefix + * @property {string|null} [phpNamespace] FileOptions phpNamespace + * @property {string|null} [phpMetadataNamespace] FileOptions phpMetadataNamespace + * @property {string|null} [rubyPackage] FileOptions rubyPackage * @property {Array.|null} [uninterpretedOption] FileOptions uninterpretedOption * @property {Array.|null} [".google.api.resourceDefinition"] FileOptions .google.api.resourceDefinition */ @@ -19374,6 +19972,14 @@ */ FileOptions.prototype.pyGenericServices = false; + /** + * FileOptions phpGenericServices. + * @member {boolean} phpGenericServices + * @memberof google.protobuf.FileOptions + * @instance + */ + FileOptions.prototype.phpGenericServices = false; + /** * FileOptions deprecated. * @member {boolean} deprecated @@ -19388,7 +19994,7 @@ * @memberof google.protobuf.FileOptions * @instance */ - FileOptions.prototype.ccEnableArenas = false; + FileOptions.prototype.ccEnableArenas = true; /** * FileOptions objcClassPrefix. @@ -19422,6 +20028,30 @@ */ FileOptions.prototype.phpClassPrefix = ""; + /** + * FileOptions phpNamespace. + * @member {string} phpNamespace + * @memberof google.protobuf.FileOptions + * @instance + */ + FileOptions.prototype.phpNamespace = ""; + + /** + * FileOptions phpMetadataNamespace. + * @member {string} phpMetadataNamespace + * @memberof google.protobuf.FileOptions + * @instance + */ + FileOptions.prototype.phpMetadataNamespace = ""; + + /** + * FileOptions rubyPackage. + * @member {string} rubyPackage + * @memberof google.protobuf.FileOptions + * @instance + */ + FileOptions.prototype.rubyPackage = ""; + /** * FileOptions uninterpretedOption. * @member {Array.} uninterpretedOption @@ -19494,6 +20124,14 @@ writer.uint32(/* id 39, wireType 2 =*/314).string(message.swiftPrefix); if (message.phpClassPrefix != null && Object.hasOwnProperty.call(message, "phpClassPrefix")) writer.uint32(/* id 40, wireType 2 =*/322).string(message.phpClassPrefix); + if (message.phpNamespace != null && Object.hasOwnProperty.call(message, "phpNamespace")) + writer.uint32(/* id 41, wireType 2 =*/330).string(message.phpNamespace); + if (message.phpGenericServices != null && Object.hasOwnProperty.call(message, "phpGenericServices")) + writer.uint32(/* id 42, wireType 0 =*/336).bool(message.phpGenericServices); + if (message.phpMetadataNamespace != null && Object.hasOwnProperty.call(message, "phpMetadataNamespace")) + writer.uint32(/* id 44, wireType 2 =*/354).string(message.phpMetadataNamespace); + if (message.rubyPackage != null && Object.hasOwnProperty.call(message, "rubyPackage")) + writer.uint32(/* id 45, wireType 2 =*/362).string(message.rubyPackage); if (message.uninterpretedOption != null && message.uninterpretedOption.length) for (var i = 0; i < message.uninterpretedOption.length; ++i) $root.google.protobuf.UninterpretedOption.encode(message.uninterpretedOption[i], writer.uint32(/* id 999, wireType 2 =*/7994).fork()).ldelim(); @@ -19564,6 +20202,9 @@ case 18: message.pyGenericServices = reader.bool(); break; + case 42: + message.phpGenericServices = reader.bool(); + break; case 23: message.deprecated = reader.bool(); break; @@ -19582,6 +20223,15 @@ case 40: message.phpClassPrefix = reader.string(); break; + case 41: + message.phpNamespace = reader.string(); + break; + case 44: + message.phpMetadataNamespace = reader.string(); + break; + case 45: + message.rubyPackage = reader.string(); + break; case 999: if (!(message.uninterpretedOption && message.uninterpretedOption.length)) message.uninterpretedOption = []; @@ -19663,6 +20313,9 @@ if (message.pyGenericServices != null && message.hasOwnProperty("pyGenericServices")) if (typeof message.pyGenericServices !== "boolean") return "pyGenericServices: boolean expected"; + if (message.phpGenericServices != null && message.hasOwnProperty("phpGenericServices")) + if (typeof message.phpGenericServices !== "boolean") + return "phpGenericServices: boolean expected"; if (message.deprecated != null && message.hasOwnProperty("deprecated")) if (typeof message.deprecated !== "boolean") return "deprecated: boolean expected"; @@ -19681,6 +20334,15 @@ if (message.phpClassPrefix != null && message.hasOwnProperty("phpClassPrefix")) if (!$util.isString(message.phpClassPrefix)) return "phpClassPrefix: string expected"; + if (message.phpNamespace != null && message.hasOwnProperty("phpNamespace")) + if (!$util.isString(message.phpNamespace)) + return "phpNamespace: string expected"; + if (message.phpMetadataNamespace != null && message.hasOwnProperty("phpMetadataNamespace")) + if (!$util.isString(message.phpMetadataNamespace)) + return "phpMetadataNamespace: string expected"; + if (message.rubyPackage != null && message.hasOwnProperty("rubyPackage")) + if (!$util.isString(message.rubyPackage)) + return "rubyPackage: string expected"; if (message.uninterpretedOption != null && message.hasOwnProperty("uninterpretedOption")) { if (!Array.isArray(message.uninterpretedOption)) return "uninterpretedOption: array expected"; @@ -19746,6 +20408,8 @@ message.javaGenericServices = Boolean(object.javaGenericServices); if (object.pyGenericServices != null) message.pyGenericServices = Boolean(object.pyGenericServices); + if (object.phpGenericServices != null) + message.phpGenericServices = Boolean(object.phpGenericServices); if (object.deprecated != null) message.deprecated = Boolean(object.deprecated); if (object.ccEnableArenas != null) @@ -19758,6 +20422,12 @@ message.swiftPrefix = String(object.swiftPrefix); if (object.phpClassPrefix != null) message.phpClassPrefix = String(object.phpClassPrefix); + if (object.phpNamespace != null) + message.phpNamespace = String(object.phpNamespace); + if (object.phpMetadataNamespace != null) + message.phpMetadataNamespace = String(object.phpMetadataNamespace); + if (object.rubyPackage != null) + message.rubyPackage = String(object.rubyPackage); if (object.uninterpretedOption) { if (!Array.isArray(object.uninterpretedOption)) throw TypeError(".google.protobuf.FileOptions.uninterpretedOption: array expected"); @@ -19810,11 +20480,15 @@ object.javaGenerateEqualsAndHash = false; object.deprecated = false; object.javaStringCheckUtf8 = false; - object.ccEnableArenas = false; + object.ccEnableArenas = true; object.objcClassPrefix = ""; object.csharpNamespace = ""; object.swiftPrefix = ""; object.phpClassPrefix = ""; + object.phpNamespace = ""; + object.phpGenericServices = false; + object.phpMetadataNamespace = ""; + object.rubyPackage = ""; } if (message.javaPackage != null && message.hasOwnProperty("javaPackage")) object.javaPackage = message.javaPackage; @@ -19848,6 +20522,14 @@ object.swiftPrefix = message.swiftPrefix; if (message.phpClassPrefix != null && message.hasOwnProperty("phpClassPrefix")) object.phpClassPrefix = message.phpClassPrefix; + if (message.phpNamespace != null && message.hasOwnProperty("phpNamespace")) + object.phpNamespace = message.phpNamespace; + if (message.phpGenericServices != null && message.hasOwnProperty("phpGenericServices")) + object.phpGenericServices = message.phpGenericServices; + if (message.phpMetadataNamespace != null && message.hasOwnProperty("phpMetadataNamespace")) + object.phpMetadataNamespace = message.phpMetadataNamespace; + if (message.rubyPackage != null && message.hasOwnProperty("rubyPackage")) + object.rubyPackage = message.rubyPackage; if (message.uninterpretedOption && message.uninterpretedOption.length) { object.uninterpretedOption = []; for (var j = 0; j < message.uninterpretedOption.length; ++j) diff --git a/protos/protos.json b/protos/protos.json index ac1aae86d..f81292a93 100644 --- a/protos/protos.json +++ b/protos/protos.json @@ -1637,6 +1637,10 @@ "rule": "repeated", "type": "HttpRule", "id": 1 + }, + "fullyDecodeReservedExpansion": { + "type": "bool", + "id": 2 } } }, @@ -1686,6 +1690,10 @@ "type": "string", "id": 7 }, + "responseBody": { + "type": "string", + "id": 12 + }, "additionalBindings": { "rule": "repeated", "type": "HttpRule", @@ -1819,11 +1827,12 @@ }, "protobuf": { "options": { - "go_package": "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor", + "go_package": "google.golang.org/protobuf/types/descriptorpb", "java_package": "com.google.protobuf", "java_outer_classname": "DescriptorProtos", "csharp_namespace": "Google.Protobuf.Reflection", "objc_class_prefix": "GPB", + "cc_enable_arenas": true, "optimize_for": "SPEED" }, "nested": { @@ -1962,6 +1971,10 @@ "end": { "type": "int32", "id": 2 + }, + "options": { + "type": "ExtensionRangeOptions", + "id": 3 } } }, @@ -1979,6 +1992,21 @@ } } }, + "ExtensionRangeOptions": { + "fields": { + "uninterpretedOption": { + "rule": "repeated", + "type": "UninterpretedOption", + "id": 999 + } + }, + "extensions": [ + [ + 1000, + 536870911 + ] + ] + }, "FieldDescriptorProto": { "fields": { "name": { @@ -2020,6 +2048,10 @@ "options": { "type": "FieldOptions", "id": 8 + }, + "proto3Optional": { + "type": "bool", + "id": 17 } }, "nested": { @@ -2080,6 +2112,30 @@ "options": { "type": "EnumOptions", "id": 3 + }, + "reservedRange": { + "rule": "repeated", + "type": "EnumReservedRange", + "id": 4 + }, + "reservedName": { + "rule": "repeated", + "type": "string", + "id": 5 + } + }, + "nested": { + "EnumReservedRange": { + "fields": { + "start": { + "type": "int32", + "id": 1 + }, + "end": { + "type": "int32", + "id": 2 + } + } } } }, @@ -2213,6 +2269,13 @@ "default": false } }, + "phpGenericServices": { + "type": "bool", + "id": 42, + "options": { + "default": false + } + }, "deprecated": { "type": "bool", "id": 23, @@ -2224,7 +2287,7 @@ "type": "bool", "id": 31, "options": { - "default": false + "default": true } }, "objcClassPrefix": { @@ -2243,6 +2306,18 @@ "type": "string", "id": 40 }, + "phpNamespace": { + "type": "string", + "id": 41 + }, + "phpMetadataNamespace": { + "type": "string", + "id": 44 + }, + "rubyPackage": { + "type": "string", + "id": 45 + }, "uninterpretedOption": { "rule": "repeated", "type": "UninterpretedOption", diff --git a/samples/openTelemetryTracing.js b/samples/openTelemetryTracing.js index 6c0190110..5d6c29569 100644 --- a/samples/openTelemetryTracing.js +++ b/samples/openTelemetryTracing.js @@ -62,9 +62,13 @@ function main( const provider = new BasicTracerProvider(); const exporter = new ConsoleSpanExporter(); provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); + // Enable the diagnostic logger for Opentelemetry + opentelemetry.diag.setLogger( + new opentelemetry.DiagConsoleLogger(), + opentelemetry.DiagLogLevel.INFO + ); provider.register(); - opentelemetry.trace.setGlobalTracerProvider(provider); // OpenTelemetry tracing is an optional feature and can be enabled by setting // enableOpenTelemetryTraceing as a publisher or subscriber option @@ -89,17 +93,32 @@ function main( const messageHandler = message => { console.log(`Message ${message.id} received.`); message.ack(); - process.exit(0); + + // Ensure that all spans got flushed by the exporter + console.log('Cleaning up Opentelemetry exporter...'); + exporter.shutdown().then(() => { + // Cleaned up exporter. + process.exit(0); + }); }; const errorHandler = error => { console.log('Received error:', error); - process.exit(0); + + console.log('Cleaning up Opentelemetry exporter...'); + exporter.shutdown().then(() => { + // Cleaned up exporter. + process.exit(0); + }); }; // Listens for new messages from the topic - pubSubClient.subscription(subscriptionName).on('message', messageHandler); - pubSubClient.subscription(subscriptionName).on('error', errorHandler); + pubSubClient + .subscription(subscriptionName, enableOpenTelemetryTracing) + .on('message', messageHandler); + pubSubClient + .subscription(subscriptionName, enableOpenTelemetryTracing) + .on('error', errorHandler); setTimeout(() => { pubSubClient diff --git a/samples/package.json b/samples/package.json index b008e7c01..4f7ea1ee8 100644 --- a/samples/package.json +++ b/samples/package.json @@ -15,8 +15,8 @@ }, "dependencies": { "@google-cloud/pubsub": "^2.10.0", - "@opentelemetry/api": "^0.11.0", - "@opentelemetry/tracing": "^0.11.0" + "@opentelemetry/api": "^0.18.1", + "@opentelemetry/tracing": "^0.18.2" }, "devDependencies": { "chai": "^4.2.0", diff --git a/samples/system-test/openTelemetryTracing.test.js b/samples/system-test/openTelemetryTracing.test.js index bcc069084..c91f174e2 100644 --- a/samples/system-test/openTelemetryTracing.test.js +++ b/samples/system-test/openTelemetryTracing.test.js @@ -42,7 +42,6 @@ describe('openTelemetry', () => { const stdout = execSync( `node openTelemetryTracing ${topicName} ${subName}` ); - assert.match(stdout, /traceId/); assert.match(stdout, /Message .* published./); assert.match(stdout, /Message .* received/); assert.notMatch(stdout, /Received error/); diff --git a/src/opentelemetry-tracing.ts b/src/opentelemetry-tracing.ts index 3c51dd440..42261a91a 100644 --- a/src/opentelemetry-tracing.ts +++ b/src/opentelemetry-tracing.ts @@ -13,31 +13,50 @@ * limitations under the License. */ -import {Attributes, SpanContext, Span, trace} from '@opentelemetry/api'; -import {Tracer} from '@opentelemetry/tracing'; +import { + Tracer, + SpanAttributes, + SpanContext, + Span, + context, + trace, + setSpanContext, + SpanKind, +} from '@opentelemetry/api'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const PKG = require('../../package.json'); + +/** + * @internal + * Instantiates a Opentelemetry tracer for the library + */ +const libraryTracer: Tracer = trace.getTracer( + '@google-cloud/pubsub', + PKG.version +); /** - * Wrapper for creating OpenTelemetry Spans + * Creates a new span with the given properties * - * @class + * @param {string} spanName the name for the span + * @param {Attributes?} attributes an object containing the attributes to be set for the span + * @param {SpanContext?} parent the context of the parent span to link to the span */ -export class OpenTelemetryTracer { - /** - * Creates a new span with the given properties - * - * @param {string} spanName the name for the span - * @param {Attributes?} attributes an object containing the attributes to be set for the span - * @param {SpanContext?} parent the context of the parent span to link to the span - */ - createSpan( - spanName: string, - attributes?: Attributes, - parent?: SpanContext - ): Span { - const tracerProvider: Tracer = trace.getTracer('default') as Tracer; - return tracerProvider.startSpan(spanName, { - parent: parent, +export function createSpan( + spanName: string, + kind: SpanKind, + attributes?: SpanAttributes, + parent?: SpanContext +): Span { + return libraryTracer.startSpan( + spanName, + { + // set the kind of the span + kind, + // set the attributes of the span attributes: attributes, - }); - } + }, + parent ? setSpanContext(context.active(), parent) : undefined + ); } diff --git a/src/publisher/index.ts b/src/publisher/index.ts index 483990f96..bcd8d9e33 100644 --- a/src/publisher/index.ts +++ b/src/publisher/index.ts @@ -17,7 +17,8 @@ import {promisify, promisifyAll} from '@google-cloud/promisify'; import * as extend from 'extend'; import {CallOptions} from 'google-gax'; -import {Span} from '@opentelemetry/api'; +import {MessagingAttribute} from '@opentelemetry/semantic-conventions'; +import {isSpanContextValid, Span, SpanKind} from '@opentelemetry/api'; import {BatchPublishOptions} from './message-batch'; import {Queue, OrderedQueue} from './message-queues'; @@ -25,7 +26,7 @@ import {Topic} from '../topic'; import {RequestCallback, EmptyCallback} from '../pubsub'; import {google} from '../../protos/protos'; import {defaultOptions} from '../default-options'; -import {OpenTelemetryTracer} from '../opentelemetry-tracing'; +import {createSpan} from '../opentelemetry-tracing'; export type PubsubMessage = google.pubsub.v1.IPubsubMessage; @@ -75,16 +76,11 @@ export class Publisher { settings!: PublishOptions; queue: Queue; orderedQueues: Map; - tracing: OpenTelemetryTracer | undefined; constructor(topic: Topic, options?: PublishOptions) { this.setOptions(options); this.topic = topic; this.queue = new Queue(this); this.orderedQueues = new Map(); - this.tracing = - this.settings && this.settings.enableOpenTelemetryTracing - ? new OpenTelemetryTracer() - : undefined; } flush(): Promise; @@ -238,10 +234,6 @@ export class Publisher { enableOpenTelemetryTracing, } = extend(true, defaults, options); - this.tracing = enableOpenTelemetryTracing - ? new OpenTelemetryTracer() - : undefined; - this.settings = { batching: { maxBytes: Math.min(batching.maxBytes, BATCH_LIMITS.maxBytes!), @@ -262,13 +254,33 @@ export class Publisher { * @param {PubsubMessage} message The message to create a span for */ constructSpan(message: PubsubMessage): Span | undefined { + if (!this.settings.enableOpenTelemetryTracing) { + return undefined; + } + const spanAttributes = { - data: message.data, + // Add Opentelemetry semantic convention attributes to the span, based on: + // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.1.0/specification/trace/semantic_conventions/messaging.md + [MessagingAttribute.MESSAGING_TEMP_DESTINATION]: false, + [MessagingAttribute.MESSAGING_SYSTEM]: 'pubsub', + [MessagingAttribute.MESSAGING_OPERATION]: 'send', + [MessagingAttribute.MESSAGING_DESTINATION]: this.topic.name, + [MessagingAttribute.MESSAGING_DESTINATION_KIND]: 'topic', + [MessagingAttribute.MESSAGING_MESSAGE_ID]: message.messageId, + [MessagingAttribute.MESSAGING_PROTOCOL]: 'pubsub', + [MessagingAttribute.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES]: + message.data?.length, + 'messaging.pubsub.ordering_key': message.orderingKey, } as Attributes; - const span: Span | undefined = this.tracing - ? this.tracing.createSpan(`${this.topic.name} publisher`, spanAttributes) - : undefined; - if (span) { + + const span: Span = createSpan( + `${this.topic.name} send`, + SpanKind.PRODUCER, + spanAttributes + ); + + // If the span's context is valid we should pass the span context special attribute + if (isSpanContextValid(span.context())) { if ( message.attributes && message.attributes['googclient_OpenTelemetrySpanContext'] @@ -280,10 +292,12 @@ export class Publisher { if (!message.attributes) { message.attributes = {}; } + message.attributes[ 'googclient_OpenTelemetrySpanContext' ] = JSON.stringify(span.context()); } + return span; } } diff --git a/src/subscriber.ts b/src/subscriber.ts index 97b863094..bff1bb98d 100644 --- a/src/subscriber.ts +++ b/src/subscriber.ts @@ -18,7 +18,8 @@ import {DateStruct, PreciseDate} from '@google-cloud/precise-date'; import {replaceProjectIdToken} from '@google-cloud/projectify'; import {promisify} from '@google-cloud/promisify'; import {EventEmitter} from 'events'; -import {SpanContext, Span} from '@opentelemetry/api'; +import {SpanContext, Span, SpanKind} from '@opentelemetry/api'; +import {MessagingAttribute} from '@opentelemetry/semantic-conventions'; import {google} from '../protos/protos'; import {Histogram} from './histogram'; @@ -28,7 +29,7 @@ import {MessageStream, MessageStreamOptions} from './message-stream'; import {Subscription} from './subscription'; import {defaultOptions} from './default-options'; import {SubscriberClient} from './v1'; -import {OpenTelemetryTracer} from './opentelemetry-tracing'; +import {createSpan} from './opentelemetry-tracing'; export type PullResponse = google.pubsub.v1.IPullResponse; @@ -239,13 +240,13 @@ export class Subscriber extends EventEmitter { private _histogram: Histogram; private _inventory!: LeaseManager; private _isUserSetDeadline: boolean; + private _useOpentelemetry: boolean; private _latencies: Histogram; private _modAcks!: ModAckQueue; private _name!: string; private _options!: SubscriberOptions; private _stream!: MessageStream; private _subscription: Subscription; - private _tracing: OpenTelemetryTracer | undefined; constructor(subscription: Subscription, options = {}) { super(); @@ -255,6 +256,7 @@ export class Subscriber extends EventEmitter { this.useLegacyFlowControl = false; this.isOpen = false; this._isUserSetDeadline = false; + this._useOpentelemetry = false; this._histogram = new Histogram({min: 10, max: 600}); this._latencies = new Histogram(); this._subscription = subscription; @@ -403,6 +405,8 @@ export class Subscriber extends EventEmitter { setOptions(options: SubscriberOptions): void { this._options = options; + this._useOpentelemetry = options.enableOpenTelemetryTracing || false; + if (options.ackDeadline) { this.ackDeadline = options.ackDeadline; this._isUserSetDeadline = true; @@ -433,9 +437,6 @@ export class Subscriber extends EventEmitter { this.maxMessages ); } - this._tracing = options.enableOpenTelemetryTracing - ? new OpenTelemetryTracer() - : undefined; } /** @@ -447,24 +448,47 @@ export class Subscriber extends EventEmitter { private _constructSpan(message: Message): Span | undefined { // Handle cases where OpenTelemetry is disabled or no span context was sent through message if ( - !this._tracing || + !this._useOpentelemetry || !message.attributes || !message.attributes['googclient_OpenTelemetrySpanContext'] ) { return undefined; } + const spanValue = message.attributes['googclient_OpenTelemetrySpanContext']; const parentSpanContext: SpanContext | undefined = spanValue ? JSON.parse(spanValue) : undefined; const spanAttributes = { + // Original span attributes ackId: message.ackId, deliveryAttempt: message.deliveryAttempt, + // + // based on https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#topic-with-multiple-consumers + [MessagingAttribute.MESSAGING_SYSTEM]: 'pubsub', + [MessagingAttribute.MESSAGING_OPERATION]: 'process', + [MessagingAttribute.MESSAGING_DESTINATION]: this.name, + [MessagingAttribute.MESSAGING_DESTINATION_KIND]: 'topic', + [MessagingAttribute.MESSAGING_MESSAGE_ID]: message.id, + [MessagingAttribute.MESSAGING_PROTOCOL]: 'pubsub', + [MessagingAttribute.MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES]: (message.data as Buffer) + .length, + // Not in Opentelemetry semantic convention but mimics naming + 'messaging.pubsub.received_at': message.received, + 'messaging.pubsub.acknowlege_id': message.ackId, + 'messaging.pubsub.delivery_attempt': message.deliveryAttempt, }; + // Subscriber spans should always have a publisher span as a parent. // Return undefined if no parent is provided + const spanName = `${this.name} process`; const span = parentSpanContext - ? this._tracing.createSpan(this._name, spanAttributes, parentSpanContext) + ? createSpan( + spanName.trim(), + SpanKind.CONSUMER, + spanAttributes, + parentSpanContext + ) : undefined; return span; } @@ -491,6 +515,7 @@ export class Subscriber extends EventEmitter { const message = new Message(this, data); const span: Span | undefined = this._constructSpan(message); + if (this.isOpen) { message.modAck(this.ackDeadline); this._inventory.add(message); diff --git a/test/opentelemetry-tracing.ts b/test/opentelemetry-tracing.ts index 032a23533..dc6a1423e 100644 --- a/test/opentelemetry-tracing.ts +++ b/test/opentelemetry-tracing.ts @@ -15,15 +15,15 @@ */ import * as assert from 'assert'; -import {describe, it, before, beforeEach, afterEach} from 'mocha'; +import {describe, it, beforeEach} from 'mocha'; import * as api from '@opentelemetry/api'; import * as trace from '@opentelemetry/tracing'; -import {OpenTelemetryTracer} from '../src/opentelemetry-tracing'; -import {SimpleSpanProcessor} from '@opentelemetry/tracing'; +import {createSpan} from '../src/opentelemetry-tracing'; +import {exporter} from './tracing'; +import {SpanKind} from '@opentelemetry/api'; describe('OpenTelemetryTracer', () => { - let tracing: OpenTelemetryTracer; let span: trace.Span; const spanName = 'test-span'; const spanContext: api.SpanContext = { @@ -31,33 +31,30 @@ describe('OpenTelemetryTracer', () => { spanId: '6e0c63257de34c92', traceFlags: api.TraceFlags.SAMPLED, }; - const spanAttributes: api.Attributes = { + const spanAttributes: api.SpanAttributes = { foo: 'bar', }; - before(() => { - const provider = new trace.BasicTracerProvider(); - const exporter = new trace.InMemorySpanExporter(); - provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); - api.trace.setGlobalTracerProvider(provider); - }); - beforeEach(() => { - tracing = new OpenTelemetryTracer(); - }); - - afterEach(() => { - span.end(); + exporter.reset(); }); it('creates a span', () => { - span = tracing.createSpan( + span = createSpan( spanName, + SpanKind.PRODUCER, spanAttributes, spanContext ) as trace.Span; - assert.strictEqual(span.name, spanName); - assert.deepStrictEqual(span.attributes, spanAttributes); - assert.strictEqual(span.parentSpanId, spanContext.spanId); + span.end(); + + const spans = exporter.getFinishedSpans(); + assert.notStrictEqual(spans.length, 0); + const exportedSpan = spans.concat().pop()!; + + assert.strictEqual(exportedSpan.name, spanName); + assert.deepStrictEqual(exportedSpan.attributes, spanAttributes); + assert.strictEqual(exportedSpan.parentSpanId, spanContext.spanId); + assert.strictEqual(exportedSpan.kind, SpanKind.PRODUCER); }); }); diff --git a/test/publisher/index.ts b/test/publisher/index.ts index ad59e3604..7440bad66 100644 --- a/test/publisher/index.ts +++ b/test/publisher/index.ts @@ -16,23 +16,20 @@ import * as pfy from '@google-cloud/promisify'; import * as assert from 'assert'; -import {describe, it, before, beforeEach, afterEach} from 'mocha'; +import {describe, it, beforeEach, afterEach} from 'mocha'; import {EventEmitter} from 'events'; import * as proxyquire from 'proxyquire'; import * as sinon from 'sinon'; -import { - BasicTracerProvider, - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; import * as opentelemetry from '@opentelemetry/api'; - import {Topic} from '../../src'; import * as p from '../../src/publisher'; import * as q from '../../src/publisher/message-queues'; import {PublishError} from '../../src/publisher/publish-error'; import {defaultOptions} from '../../src/default-options'; +import {exporter} from '../tracing'; +import {SpanKind} from '@opentelemetry/api'; +import {MessagingAttribute} from '@opentelemetry/semantic-conventions'; let promisified = false; const fakePromisify = Object.assign({}, pfy, { @@ -94,14 +91,21 @@ class FakeOrderedQueue extends FakeQueue { } describe('Publisher', () => { - const sandbox = sinon.createSandbox(); - const topic = {} as Topic; + let sandbox: sinon.SinonSandbox; + let spy: sinon.SinonSpyStatic; + const topic = { + name: 'topic-name', + pubsub: {projectId: 'PROJECT_ID'}, + } as Topic; // tslint:disable-next-line variable-name let Publisher: typeof p.Publisher; let publisher: p.Publisher; - before(() => { + beforeEach(() => { + sandbox = sinon.createSandbox(); + spy = sandbox.spy(); + const mocked = proxyquire('../../src/publisher/index.js', { '@google-cloud/promisify': fakePromisify, './message-queues': { @@ -111,9 +115,7 @@ describe('Publisher', () => { }); Publisher = mocked.Publisher; - }); - beforeEach(() => { publisher = new Publisher(topic); }); @@ -151,7 +153,6 @@ describe('Publisher', () => { describe('publish', () => { const buffer = Buffer.from('Hello, world!'); - const spy = sandbox.spy(); it('should call through to publishMessage', () => { const stub = sandbox.stub(publisher, 'publishMessage'); @@ -180,56 +181,52 @@ describe('Publisher', () => { const enableTracing: p.PublishOptions = { enableOpenTelemetryTracing: true, }; - const disableTracing: p.PublishOptions = { - enableOpenTelemetryTracing: false, - }; const buffer = Buffer.from('Hello, world!'); beforeEach(() => { - // Declare tracingPublisher as type any and pre-define _tracing - // to gain access to the private field after publisher init - tracingPublisher['tracing'] = undefined; - }); - it('should not instantiate a tracer when tracing is disabled', () => { - tracingPublisher = new Publisher(topic); - assert.strictEqual(tracingPublisher['tracing'], undefined); - }); - - it('should instantiate a tracer when tracing is enabled through constructor', () => { - tracingPublisher = new Publisher(topic, enableTracing); - assert.ok(tracingPublisher['tracing']); - }); - - it('should instantiate a tracer when tracing is enabled through setOptions', () => { - tracingPublisher = new Publisher(topic); - tracingPublisher.setOptions(enableTracing); - assert.ok(tracingPublisher['tracing']); - }); - - it('should disable tracing when tracing is disabled through setOptions', () => { - tracingPublisher = new Publisher(topic, enableTracing); - tracingPublisher.setOptions(disableTracing); - assert.strictEqual(tracingPublisher['tracing'], undefined); + exporter.reset(); }); it('export created spans', () => { - tracingPublisher = new Publisher(topic, enableTracing); - // Setup trace exporting - const provider: BasicTracerProvider = new BasicTracerProvider(); - const exporter: InMemorySpanExporter = new InMemorySpanExporter(); - provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); - provider.register(); - opentelemetry.trace.setGlobalTracerProvider(provider); + tracingPublisher = new Publisher(topic, enableTracing); tracingPublisher.publish(buffer); - assert.ok(exporter.getFinishedSpans()); + const spans = exporter.getFinishedSpans(); + assert.notStrictEqual(spans.length, 0, 'has span'); + const createdSpan = spans.concat().pop()!; + assert.strictEqual( + createdSpan.status.code, + opentelemetry.SpanStatusCode.UNSET + ); + assert.strictEqual( + createdSpan.attributes[MessagingAttribute.MESSAGING_OPERATION], + 'send' + ); + assert.strictEqual( + createdSpan.attributes[MessagingAttribute.MESSAGING_SYSTEM], + 'pubsub' + ); + assert.strictEqual( + createdSpan.attributes[MessagingAttribute.MESSAGING_DESTINATION], + topic.name + ); + assert.strictEqual( + createdSpan.attributes[MessagingAttribute.MESSAGING_DESTINATION_KIND], + 'topic' + ); + assert.strictEqual(createdSpan.name, 'topic-name send'); + assert.strictEqual( + createdSpan.kind, + SpanKind.PRODUCER, + 'span kind should be PRODUCER' + ); + assert.ok(spans); }); }); describe('publishMessage', () => { const data = Buffer.from('hello, world!'); - const spy = sandbox.spy(); it('should throw an error if data is not a Buffer', () => { const badData = {} as Buffer; diff --git a/test/subscriber.ts b/test/subscriber.ts index 82064954f..031934e03 100644 --- a/test/subscriber.ts +++ b/test/subscriber.ts @@ -14,19 +14,15 @@ * limitations under the License. */ +import {exporter} from './tracing'; import * as assert from 'assert'; -import {describe, it, before, beforeEach, afterEach} from 'mocha'; +import {describe, it, beforeEach, afterEach} from 'mocha'; import {EventEmitter} from 'events'; import {common as protobuf} from 'protobufjs'; import * as proxyquire from 'proxyquire'; import * as sinon from 'sinon'; import {PassThrough} from 'stream'; import * as uuid from 'uuid'; -import { - SimpleSpanProcessor, - BasicTracerProvider, - InMemorySpanExporter, -} from '@opentelemetry/tracing'; import * as opentelemetry from '@opentelemetry/api'; import {HistogramOptions} from '../src/histogram'; @@ -35,6 +31,8 @@ import {BatchOptions} from '../src/message-queues'; import {MessageStreamOptions} from '../src/message-stream'; import * as s from '../src/subscriber'; import {Subscription} from '../src/subscription'; +import {SpanKind} from '@opentelemetry/api'; +import {MessagingAttribute} from '@opentelemetry/semantic-conventions'; const stubs = new Map(); @@ -150,10 +148,9 @@ const RECEIVED_MESSAGE = { }; describe('Subscriber', () => { - const sandbox = sinon.createSandbox(); - - const fakeProjectify = {replaceProjectIdToken: sandbox.stub()}; + let sandbox: sinon.SinonSandbox; + let fakeProjectify: any; let subscription: Subscription; // tslint:disable-next-line variable-name @@ -163,7 +160,14 @@ describe('Subscriber', () => { let Subscriber: typeof s.Subscriber; let subscriber: s.Subscriber; - before(() => { + beforeEach(() => { + sandbox = sinon.createSandbox(); + fakeProjectify = { + replaceProjectIdToken: sandbox.stub().callsFake((name, projectId) => { + return `projects/${projectId}/name/${name}`; + }), + }; + const s = proxyquire('../src/subscriber.js', { '@google-cloud/precise-date': {PreciseDate: FakePreciseDate}, '@google-cloud/projectify': fakeProjectify, @@ -178,9 +182,8 @@ describe('Subscriber', () => { Message = s.Message; Subscriber = s.Subscriber; - }); - beforeEach(() => { + // Create standard instance subscription = (new FakeSubscription() as {}) as Subscription; subscriber = new Subscriber(subscription); message = new Message(subscriber, RECEIVED_MESSAGE); @@ -638,7 +641,6 @@ describe('Subscriber', () => { }); describe('OpenTelemetry tracing', () => { - let tracingSubscriber: s.Subscriber = {} as s.Subscriber; const enableTracing: s.SubscriberOptions = { enableOpenTelemetryTracing: true, }; @@ -647,41 +649,42 @@ describe('Subscriber', () => { }; beforeEach(() => { - // Pre-define _tracing to gain access to the private field after subscriber init - tracingSubscriber['_tracing'] = undefined; + exporter.reset(); + }); + + afterEach(() => { + exporter.reset(); + subscriber.close(); }); it('should not instantiate a tracer when tracing is disabled', () => { - tracingSubscriber = new Subscriber(subscription); - assert.strictEqual(tracingSubscriber['_tracing'], undefined); + subscriber = new Subscriber(subscription, {}); + assert.strictEqual(subscriber['_useOpentelemetry'], false); }); it('should instantiate a tracer when tracing is enabled through constructor', () => { - tracingSubscriber = new Subscriber(subscription, enableTracing); - assert.ok(tracingSubscriber['_tracing']); + subscriber = new Subscriber(subscription, enableTracing); + assert.ok(subscriber['_useOpentelemetry']); }); it('should instantiate a tracer when tracing is enabled through setOptions', () => { - tracingSubscriber = new Subscriber(subscription); - tracingSubscriber.setOptions(enableTracing); - assert.ok(tracingSubscriber['_tracing']); + subscriber = new Subscriber(subscription, {}); + subscriber.setOptions(enableTracing); + assert.ok(subscriber['_useOpentelemetry']); }); it('should disable tracing when tracing is disabled through setOptions', () => { - tracingSubscriber = new Subscriber(subscription, enableTracing); - tracingSubscriber.setOptions(disableTracing); - assert.strictEqual(tracingSubscriber['_tracing'], undefined); + subscriber = new Subscriber(subscription, enableTracing); + subscriber.setOptions(disableTracing); + assert.strictEqual(subscriber['_useOpentelemetry'], false); }); it('exports a span once it is created', () => { - tracingSubscriber = new Subscriber(subscription, enableTracing); - - // Setup trace exporting - const provider: BasicTracerProvider = new BasicTracerProvider(); - const exporter: InMemorySpanExporter = new InMemorySpanExporter(); - provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); - provider.register(); - opentelemetry.trace.setGlobalTracerProvider(provider); + subscription = (new FakeSubscription() as {}) as Subscription; + subscriber = new Subscriber(subscription, enableTracing); + message = new Message(subscriber, RECEIVED_MESSAGE); + assert.strictEqual(subscriber['_useOpentelemetry'], true); + subscriber.open(); // Construct mock of received message with span context const parentSpanContext: opentelemetry.SpanContext = { @@ -708,20 +711,51 @@ describe('Subscriber', () => { }; // Receive message and assert that it was exported - const stream: FakeMessageStream = stubs.get('messageStream'); - stream.emit('data', pullResponse); - assert.ok(exporter.getFinishedSpans()); + const msgStream = stubs.get('messageStream'); + msgStream.emit('data', pullResponse); + + const spans = exporter.getFinishedSpans(); + assert.strictEqual(spans.length, 1); + const firstSpan = spans.concat().shift(); + assert.ok(firstSpan); + assert.strictEqual(firstSpan.parentSpanId, parentSpanContext.spanId); + assert.strictEqual( + firstSpan.name, + `${subscriber.name} process`, + 'name of span should match' + ); + assert.strictEqual( + firstSpan.kind, + SpanKind.CONSUMER, + 'span kind should be CONSUMER' + ); + assert.strictEqual( + firstSpan.attributes[MessagingAttribute.MESSAGING_OPERATION], + 'process', + 'span messaging operation should match' + ); + assert.strictEqual( + firstSpan.attributes[MessagingAttribute.MESSAGING_SYSTEM], + 'pubsub' + ); + assert.strictEqual( + firstSpan.attributes[MessagingAttribute.MESSAGING_MESSAGE_ID], + messageWithSpanContext.message.messageId, + 'span messaging id should match' + ); + assert.strictEqual( + firstSpan.attributes[MessagingAttribute.MESSAGING_DESTINATION], + subscriber.name, + 'span messaging destination should match' + ); + assert.strictEqual( + firstSpan.attributes[MessagingAttribute.MESSAGING_DESTINATION_KIND], + 'topic' + ); }); it('does not export a span when a span context is not present on message', () => { - tracingSubscriber = new Subscriber(subscription, enableTracing); - - // Setup trace exporting - const provider: BasicTracerProvider = new BasicTracerProvider(); - const exporter: InMemorySpanExporter = new InMemorySpanExporter(); - provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); - provider.register(); - opentelemetry.trace.setGlobalTracerProvider(provider); + subscriber = new Subscriber(subscription, enableTracing); const pullResponse: s.PullResponse = { receivedMessages: [RECEIVED_MESSAGE], diff --git a/test/tracing.ts b/test/tracing.ts new file mode 100644 index 000000000..8b1b31146 --- /dev/null +++ b/test/tracing.ts @@ -0,0 +1,41 @@ +/*! + * Copyright 2021 Google LLC. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + BasicTracerProvider, + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracing'; + +/** + * This file is used to initialise a global tracing provider and span exporter + * for our tests used in our project. This was the only way I was able to get + * the tracing tests to work. + * + * Now before each test related or touches Opentelemetry + * we are resetting the exporter defined below to ensure there are no spans + * from previous tests still in memory. This is achived by calling `reset` + * on the exporter in the unit tests while keeping one instance of + * the trace provider and exporter. + * + * The tracing provider is being registered as a global trace provider before + * we are importing our actual code which uses the Opentelemetry API to ensure + * its defined beforehand. + */ +export const exporter: InMemorySpanExporter = new InMemorySpanExporter(); +export const provider: BasicTracerProvider = new BasicTracerProvider(); +provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); +provider.register();