export resource to exporters (#843)
* export resource to exporters * update Zipkin and Stackdriver exporter to use Resource * chore: remove redundant dependency * update Collector exporter to use Resource * rebase with #846 * minor * fix collector resource * fix build
This commit is contained in:
		
							parent
							
								
									02c1d66b76
								
							
						
					
					
						commit
						4627892c4a
					
				|  | @ -50,7 +50,6 @@ | |||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@babel/core": "^7.6.0", | ||||
|     "@opentelemetry/resources": "^0.4.0", | ||||
|     "@types/mocha": "^5.2.5", | ||||
|     "@types/node": "^12.6.8", | ||||
|     "@types/sinon": "^7.0.13", | ||||
|  | @ -83,6 +82,7 @@ | |||
|     "@opentelemetry/api": "^0.4.0", | ||||
|     "@opentelemetry/base": "^0.4.0", | ||||
|     "@opentelemetry/core": "^0.4.0", | ||||
|     "@opentelemetry/resources": "^0.4.0", | ||||
|     "@opentelemetry/tracing": "^0.4.0" | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -19,8 +19,9 @@ import { NoopLogger } from '@opentelemetry/core'; | |||
| import { ReadableSpan, SpanExporter } from '@opentelemetry/tracing'; | ||||
| import { Attributes, Logger } from '@opentelemetry/api'; | ||||
| import * as collectorTypes from './types'; | ||||
| import { toCollectorSpan } from './transform'; | ||||
| import { toCollectorSpan, toCollectorResource } from './transform'; | ||||
| import { onInit, onShutdown, sendSpans } from './platform/index'; | ||||
| import { Resource } from '@opentelemetry/resources'; | ||||
| 
 | ||||
| /** | ||||
|  * Collector Exporter Config | ||||
|  | @ -100,10 +101,13 @@ export class CollectorExporter implements SpanExporter { | |||
|           toCollectorSpan(span) | ||||
|         ); | ||||
|         this.logger.debug('spans to be sent', spansToBeSent); | ||||
|         const resource = toCollectorResource( | ||||
|           spansToBeSent.length > 0 ? spans[0].resource : Resource.empty() | ||||
|         ); | ||||
| 
 | ||||
|         // Send spans to [opentelemetry collector]{@link https://github.com/open-telemetry/opentelemetry-collector}
 | ||||
|         // it will use the appropriate transport layer automatically depends on platform
 | ||||
|         sendSpans(spansToBeSent, resolve, reject, this); | ||||
|         sendSpans(spansToBeSent, resolve, reject, this, resource); | ||||
|       } catch (e) { | ||||
|         reject(e); | ||||
|       } | ||||
|  |  | |||
|  | @ -43,12 +43,14 @@ export function onShutdown(shutdownF: EventListener) { | |||
|  * @param onSuccess | ||||
|  * @param onError | ||||
|  * @param collectorExporter | ||||
|  * @param resource | ||||
|  */ | ||||
| export function sendSpans( | ||||
|   spans: collectorTypes.Span[], | ||||
|   onSuccess: () => void, | ||||
|   onError: (status?: number) => void, | ||||
|   collectorExporter: CollectorExporter | ||||
|   collectorExporter: CollectorExporter, | ||||
|   resource: collectorTypes.Resource | ||||
| ) { | ||||
|   const exportTraceServiceRequest: collectorTypes.ExportTraceServiceRequest = { | ||||
|     node: { | ||||
|  | @ -66,7 +68,7 @@ export function sendSpans( | |||
|       }, | ||||
|       attributes: collectorExporter.attributes, | ||||
|     }, | ||||
|     // resource: '', not implemented
 | ||||
|     resource, | ||||
|     spans, | ||||
|   }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -47,32 +47,20 @@ export function onShutdown(shutdownF: Function) {} | |||
|  * @param onSuccess | ||||
|  * @param onError | ||||
|  * @param collectorExporter | ||||
|  * @param resource | ||||
|  */ | ||||
| export function sendSpans( | ||||
|   spans: collectorTypes.Span[], | ||||
|   onSuccess: () => void, | ||||
|   onError: (status?: number) => void, | ||||
|   collectorExporter: CollectorExporter | ||||
|   collectorExporter: CollectorExporter, | ||||
|   resource: collectorTypes.Resource | ||||
| ) { | ||||
|   const exportTraceServiceRequest: collectorTypes.ExportTraceServiceRequest = { | ||||
|     node: { | ||||
|       identifier: { | ||||
|         hostName: collectorExporter.hostName, | ||||
|         startTimestamp: core.hrTimeToTimeStamp(core.hrTime()), | ||||
|       }, | ||||
|       libraryInfo: { | ||||
|         language: collectorTypes.LibraryInfoLanguage.NODE_JS, | ||||
|         coreLibraryVersion: core.VERSION, | ||||
|         exporterVersion: VERSION, | ||||
|       }, | ||||
|       serviceInfo: { | ||||
|         name: collectorExporter.serviceName, | ||||
|       }, | ||||
|       attributes: collectorExporter.attributes, | ||||
|     }, | ||||
|     // resource: '', not implemented
 | ||||
|   const exportTraceServiceRequest = toCollectorTraceServiceRequest( | ||||
|     spans, | ||||
|   }; | ||||
|     collectorExporter, | ||||
|     resource | ||||
|   ); | ||||
|   const body = JSON.stringify(exportTraceServiceRequest); | ||||
|   const parsedUrl = url.parse(collectorExporter.url); | ||||
| 
 | ||||
|  | @ -105,3 +93,29 @@ export function sendSpans( | |||
|   req.write(body); | ||||
|   req.end(); | ||||
| } | ||||
| 
 | ||||
| export function toCollectorTraceServiceRequest( | ||||
|   spans: collectorTypes.Span[], | ||||
|   collectorExporter: CollectorExporter, | ||||
|   resource: collectorTypes.Resource | ||||
| ): collectorTypes.ExportTraceServiceRequest { | ||||
|   return { | ||||
|     node: { | ||||
|       identifier: { | ||||
|         hostName: collectorExporter.hostName, | ||||
|         startTimestamp: core.hrTimeToTimeStamp(core.hrTime()), | ||||
|       }, | ||||
|       libraryInfo: { | ||||
|         language: collectorTypes.LibraryInfoLanguage.NODE_JS, | ||||
|         coreLibraryVersion: core.VERSION, | ||||
|         exporterVersion: VERSION, | ||||
|       }, | ||||
|       serviceInfo: { | ||||
|         name: collectorExporter.serviceName, | ||||
|       }, | ||||
|       attributes: collectorExporter.attributes, | ||||
|     }, | ||||
|     resource, | ||||
|     spans, | ||||
|   }; | ||||
| } | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ import { hexToBase64, hrTimeToTimeStamp } from '@opentelemetry/core'; | |||
| import { ReadableSpan } from '@opentelemetry/tracing'; | ||||
| import { Attributes, Link, TimedEvent, TraceState } from '@opentelemetry/api'; | ||||
| import * as collectorTypes from './types'; | ||||
| import { Resource } from '@opentelemetry/resources'; | ||||
| 
 | ||||
| const OT_MAX_STRING_LENGTH = 128; | ||||
| 
 | ||||
|  | @ -201,6 +202,21 @@ export function toCollectorSpan(span: ReadableSpan): collectorTypes.Span { | |||
|   }; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * converts span resource | ||||
|  * @param resource | ||||
|  */ | ||||
| export function toCollectorResource( | ||||
|   resource: Resource | ||||
| ): collectorTypes.Resource { | ||||
|   const labels: { [key: string]: string } = {}; | ||||
|   Object.keys(resource.labels).forEach( | ||||
|     name => (labels[name] = String(resource.labels[name])) | ||||
|   ); | ||||
|   // @TODO: add type support
 | ||||
|   return { labels }; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * @param traceState | ||||
|  */ | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ import { Attributes, TimedEvent } from '@opentelemetry/api'; | |||
| import * as assert from 'assert'; | ||||
| import * as transform from '../../src/transform'; | ||||
| import { ensureSpanIsCorrect, mockedReadableSpan } from '../helper'; | ||||
| import { Resource } from '@opentelemetry/resources'; | ||||
| 
 | ||||
| describe('transform', () => { | ||||
|   describe('toCollectorTruncatableString', () => { | ||||
|  | @ -149,4 +150,23 @@ describe('transform', () => { | |||
|       ensureSpanIsCorrect(transform.toCollectorSpan(mockedReadableSpan)); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('toCollectorResource', () => { | ||||
|     it('should convert resource', () => { | ||||
|       const resource = transform.toCollectorResource( | ||||
|         new Resource({ | ||||
|           service: 'ui', | ||||
|           version: 1.0, | ||||
|           success: true, | ||||
|         }) | ||||
|       ); | ||||
|       assert.deepStrictEqual(resource, { | ||||
|         labels: { | ||||
|           service: 'ui', | ||||
|           version: '1', | ||||
|           success: 'true', | ||||
|         }, | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
|  | @ -69,7 +69,11 @@ export const mockedReadableSpan: ReadableSpan = { | |||
|     }, | ||||
|   ], | ||||
|   duration: [0, 8885000], | ||||
|   resource: Resource.empty(), | ||||
|   resource: new Resource({ | ||||
|     service: 'ui', | ||||
|     version: 1, | ||||
|     cost: 112.12, | ||||
|   }), | ||||
| }; | ||||
| 
 | ||||
| export function ensureSpanIsCorrect(span: collectorTypes.Span) { | ||||
|  |  | |||
|  | @ -64,6 +64,12 @@ export function spanToThrift(span: ReadableSpan): ThriftSpan { | |||
|   if (span.kind !== undefined) { | ||||
|     tags.push({ key: 'span.kind', value: SpanKind[span.kind] }); | ||||
|   } | ||||
|   Object.keys(span.resource.labels).forEach(name => | ||||
|     tags.push({ | ||||
|       key: name, | ||||
|       value: toTagValue(span.resource.labels[name]), | ||||
|     }) | ||||
|   ); | ||||
| 
 | ||||
|   const spanTags: ThriftTag[] = ThriftUtils.getThriftTags(tags); | ||||
| 
 | ||||
|  |  | |||
|  | @ -70,7 +70,11 @@ describe('transform', () => { | |||
|           }, | ||||
|         ], | ||||
|         duration: [32, 800000000], | ||||
|         resource: Resource.empty(), | ||||
|         resource: new Resource({ | ||||
|           service: 'ui', | ||||
|           version: 1, | ||||
|           cost: 112.12, | ||||
|         }), | ||||
|       }; | ||||
| 
 | ||||
|       const thriftSpan = spanToThrift(readableSpan); | ||||
|  | @ -95,8 +99,18 @@ describe('transform', () => { | |||
|         thriftSpan.startTime, | ||||
|         Utils.encodeInt64(hrTimeToMicroseconds(readableSpan.startTime)) | ||||
|       ); | ||||
|       assert.strictEqual(thriftSpan.tags.length, 6); | ||||
|       const [tag1, tag2, tag3, tag4, tag5, tag6] = thriftSpan.tags; | ||||
|       assert.strictEqual(thriftSpan.tags.length, 9); | ||||
|       const [ | ||||
|         tag1, | ||||
|         tag2, | ||||
|         tag3, | ||||
|         tag4, | ||||
|         tag5, | ||||
|         tag6, | ||||
|         tag7, | ||||
|         tag8, | ||||
|         tag9, | ||||
|       ] = thriftSpan.tags; | ||||
|       assert.strictEqual(tag1.key, 'testBool'); | ||||
|       assert.strictEqual(tag1.vType, 'BOOL'); | ||||
|       assert.strictEqual(tag1.vBool, true); | ||||
|  | @ -115,6 +129,15 @@ describe('transform', () => { | |||
|       assert.strictEqual(tag6.key, 'span.kind'); | ||||
|       assert.strictEqual(tag6.vType, 'STRING'); | ||||
|       assert.strictEqual(tag6.vStr, 'INTERNAL'); | ||||
|       assert.strictEqual(tag7.key, 'service'); | ||||
|       assert.strictEqual(tag7.vType, 'STRING'); | ||||
|       assert.strictEqual(tag7.vStr, 'ui'); | ||||
|       assert.strictEqual(tag8.key, 'version'); | ||||
|       assert.strictEqual(tag8.vType, 'DOUBLE'); | ||||
|       assert.strictEqual(tag8.vDouble, 1); | ||||
|       assert.strictEqual(tag9.key, 'cost'); | ||||
|       assert.strictEqual(tag9.vType, 'DOUBLE'); | ||||
|       assert.strictEqual(tag9.vDouble, 112.12); | ||||
|       assert.strictEqual(thriftSpan.references.length, 0); | ||||
| 
 | ||||
|       assert.strictEqual(thriftSpan.logs.length, 1); | ||||
|  |  | |||
|  | @ -41,7 +41,6 @@ | |||
|     "access": "public" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@opentelemetry/resources": "^0.4.0", | ||||
|     "@types/mocha": "^5.2.7", | ||||
|     "@types/nock": "^11.1.0", | ||||
|     "@types/node": "^12.6.9", | ||||
|  | @ -63,6 +62,7 @@ | |||
|     "@opentelemetry/api": "^0.4.0", | ||||
|     "@opentelemetry/base": "^0.4.0", | ||||
|     "@opentelemetry/core": "^0.4.0", | ||||
|     "@opentelemetry/resources": "^0.4.0", | ||||
|     "@opentelemetry/tracing": "^0.4.0", | ||||
|     "google-auth-library": "^5.7.0", | ||||
|     "googleapis": "^46.0.0" | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ import { | |||
|   TruncatableString, | ||||
| } from './types'; | ||||
| import { VERSION } from './version'; | ||||
| import { Resource } from '@opentelemetry/resources'; | ||||
| 
 | ||||
| const AGENT_LABEL_KEY = 'g.co/agent'; | ||||
| const AGENT_LABEL_VALUE = `opentelemetry-js [${CORE_VERSION}]; stackdriver-trace-exporter [${VERSION}]`; | ||||
|  | @ -38,10 +39,14 @@ export function getReadableSpanTransformer( | |||
|   projectId: string | ||||
| ): (span: ReadableSpan) => Span { | ||||
|   return span => { | ||||
|     const attributes = transformAttributes(span.attributes, { | ||||
|       project_id: projectId, | ||||
|       [AGENT_LABEL_KEY]: AGENT_LABEL_VALUE, | ||||
|     }); | ||||
|     const attributes = transformAttributes( | ||||
|       span.attributes, | ||||
|       { | ||||
|         project_id: projectId, | ||||
|         [AGENT_LABEL_KEY]: AGENT_LABEL_VALUE, | ||||
|       }, | ||||
|       span.resource | ||||
|     ); | ||||
| 
 | ||||
|     const out: Span = { | ||||
|       attributes, | ||||
|  | @ -85,9 +90,16 @@ function transformLink(link: ot.Link): Link { | |||
| 
 | ||||
| function transformAttributes( | ||||
|   requestAttributes: ot.Attributes = {}, | ||||
|   serviceAttributes: ot.Attributes = {} | ||||
|   serviceAttributes: ot.Attributes = {}, | ||||
|   resource: Resource = Resource.empty() | ||||
| ): Attributes { | ||||
|   const attributes = Object.assign({}, requestAttributes, serviceAttributes); | ||||
|   const attributes = Object.assign( | ||||
|     {}, | ||||
|     requestAttributes, | ||||
|     serviceAttributes, | ||||
|     resource.labels | ||||
|   ); | ||||
| 
 | ||||
|   const attributeMap = transformAttributeValues(attributes); | ||||
|   return { | ||||
|     attributeMap: attributeMap, | ||||
|  |  | |||
|  | @ -51,7 +51,11 @@ describe('transform', () => { | |||
|       name: 'my-span', | ||||
|       spanContext, | ||||
|       status: { code: types.CanonicalCode.OK }, | ||||
|       resource: Resource.empty(), | ||||
|       resource: new Resource({ | ||||
|         service: 'ui', | ||||
|         version: 1, | ||||
|         cost: 112.12, | ||||
|       }), | ||||
|     }; | ||||
|   }); | ||||
| 
 | ||||
|  | @ -67,6 +71,9 @@ describe('transform', () => { | |||
|               value: `opentelemetry-js [${CORE_VERSION}]; stackdriver-trace-exporter [${VERSION}]`, | ||||
|             }, | ||||
|           }, | ||||
|           cost: { intValue: '112' }, | ||||
|           service: { stringValue: { value: 'ui' } }, | ||||
|           version: { intValue: '1' }, | ||||
|         }, | ||||
|         droppedAttributesCount: 0, | ||||
|       }, | ||||
|  | @ -130,7 +137,7 @@ describe('transform', () => { | |||
|     assert.deepStrictEqual(result.attributes!.droppedAttributesCount, 1); | ||||
|     assert.deepStrictEqual( | ||||
|       Object.keys(result.attributes!.attributeMap!).length, | ||||
|       2 | ||||
|       5 | ||||
|     ); | ||||
|   }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -39,7 +39,6 @@ | |||
|     "access": "public" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@opentelemetry/resources": "^0.4.0", | ||||
|     "@types/mocha": "^5.2.7", | ||||
|     "@types/nock": "^10.0.3", | ||||
|     "@types/node": "^12.6.9", | ||||
|  | @ -59,6 +58,7 @@ | |||
|     "@opentelemetry/api": "^0.4.0", | ||||
|     "@opentelemetry/base": "^0.4.0", | ||||
|     "@opentelemetry/core": "^0.4.0", | ||||
|     "@opentelemetry/resources": "^0.4.0", | ||||
|     "@opentelemetry/tracing": "^0.4.0" | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ import * as types from '@opentelemetry/api'; | |||
| import { ReadableSpan } from '@opentelemetry/tracing'; | ||||
| import { hrTimeToMicroseconds } from '@opentelemetry/core'; | ||||
| import * as zipkinTypes from './types'; | ||||
| import { Resource } from '@opentelemetry/resources'; | ||||
| 
 | ||||
| const ZIPKIN_SPAN_KIND_MAPPING = { | ||||
|   [types.SpanKind.CLIENT]: zipkinTypes.SpanKind.CLIENT, | ||||
|  | @ -54,7 +55,8 @@ export function toZipkinSpan( | |||
|       span.attributes, | ||||
|       span.status, | ||||
|       statusCodeTagName, | ||||
|       statusDescriptionTagName | ||||
|       statusDescriptionTagName, | ||||
|       span.resource | ||||
|     ), | ||||
|     annotations: span.events.length | ||||
|       ? _toZipkinAnnotations(span.events) | ||||
|  | @ -69,9 +71,10 @@ export function _toZipkinTags( | |||
|   attributes: types.Attributes, | ||||
|   status: types.Status, | ||||
|   statusCodeTagName: string, | ||||
|   statusDescriptionTagName: string | ||||
|   statusDescriptionTagName: string, | ||||
|   resource: Resource | ||||
| ): zipkinTypes.Tags { | ||||
|   const tags: { [key: string]: string } = {}; | ||||
|   const tags: { [key: string]: unknown } = {}; | ||||
|   for (const key of Object.keys(attributes)) { | ||||
|     tags[key] = String(attributes[key]); | ||||
|   } | ||||
|  | @ -79,6 +82,11 @@ export function _toZipkinTags( | |||
|   if (status.message) { | ||||
|     tags[statusDescriptionTagName] = status.message; | ||||
|   } | ||||
| 
 | ||||
|   Object.keys(resource.labels).forEach( | ||||
|     name => (tags[name] = resource.labels[name]) | ||||
|   ); | ||||
| 
 | ||||
|   return tags; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ import { | |||
|   NoopLogger, | ||||
|   hrTimeToMicroseconds, | ||||
|   hrTimeDuration, | ||||
|   VERSION, | ||||
| } from '@opentelemetry/core'; | ||||
| import { | ||||
|   toZipkinSpan, | ||||
|  | @ -30,6 +31,7 @@ import { | |||
|   statusDescriptionTagName, | ||||
| } from '../src/transform'; | ||||
| import * as zipkinTypes from '../src/types'; | ||||
| import { Resource } from '@opentelemetry/resources'; | ||||
| 
 | ||||
| const logger = new NoopLogger(); | ||||
| const tracer = new BasicTracerProvider({ | ||||
|  | @ -42,6 +44,12 @@ const spanContext: types.SpanContext = { | |||
|   traceFlags: types.TraceFlags.SAMPLED, | ||||
| }; | ||||
| 
 | ||||
| const DUMMY_RESOUCE = new Resource({ | ||||
|   service: 'ui', | ||||
|   version: 1, | ||||
|   cost: 112.12, | ||||
| }); | ||||
| 
 | ||||
| describe('transform', () => { | ||||
|   describe('toZipkinSpan', () => { | ||||
|     it('should convert an OpenTelemetry span to a Zipkin span', () => { | ||||
|  | @ -86,6 +94,9 @@ describe('transform', () => { | |||
|           key1: 'value1', | ||||
|           key2: 'value2', | ||||
|           [statusCodeTagName]: 'OK', | ||||
|           'telemetry.sdk.language': 'nodejs', | ||||
|           'telemetry.sdk.name': 'opentelemetry', | ||||
|           'telemetry.sdk.version': VERSION, | ||||
|         }, | ||||
|         timestamp: hrTimeToMicroseconds(span.startTime), | ||||
|         traceId: span.spanContext.traceId, | ||||
|  | @ -120,6 +131,9 @@ describe('transform', () => { | |||
|         parentId: undefined, | ||||
|         tags: { | ||||
|           [statusCodeTagName]: 'OK', | ||||
|           'telemetry.sdk.language': 'nodejs', | ||||
|           'telemetry.sdk.name': 'opentelemetry', | ||||
|           'telemetry.sdk.version': VERSION, | ||||
|         }, | ||||
|         timestamp: hrTimeToMicroseconds(span.startTime), | ||||
|         traceId: span.spanContext.traceId, | ||||
|  | @ -159,6 +173,9 @@ describe('transform', () => { | |||
|           parentId: undefined, | ||||
|           tags: { | ||||
|             [statusCodeTagName]: 'OK', | ||||
|             'telemetry.sdk.language': 'nodejs', | ||||
|             'telemetry.sdk.name': 'opentelemetry', | ||||
|             'telemetry.sdk.version': VERSION, | ||||
|           }, | ||||
|           timestamp: hrTimeToMicroseconds(span.startTime), | ||||
|           traceId: span.spanContext.traceId, | ||||
|  | @ -184,13 +201,17 @@ describe('transform', () => { | |||
|         span.attributes, | ||||
|         span.status, | ||||
|         statusCodeTagName, | ||||
|         statusDescriptionTagName | ||||
|         statusDescriptionTagName, | ||||
|         DUMMY_RESOUCE | ||||
|       ); | ||||
| 
 | ||||
|       assert.deepStrictEqual(tags, { | ||||
|         key1: 'value1', | ||||
|         key2: 'value2', | ||||
|         [statusCodeTagName]: 'OK', | ||||
|         cost: 112.12, | ||||
|         service: 'ui', | ||||
|         version: 1, | ||||
|       }); | ||||
|     }); | ||||
|     it('should map OpenTelemetry Status.code to a Zipkin tag', () => { | ||||
|  | @ -213,7 +234,8 @@ describe('transform', () => { | |||
|         span.attributes, | ||||
|         span.status, | ||||
|         statusCodeTagName, | ||||
|         statusDescriptionTagName | ||||
|         statusDescriptionTagName, | ||||
|         Resource.empty() | ||||
|       ); | ||||
| 
 | ||||
|       assert.deepStrictEqual(tags, { | ||||
|  | @ -243,7 +265,8 @@ describe('transform', () => { | |||
|         span.attributes, | ||||
|         span.status, | ||||
|         statusCodeTagName, | ||||
|         statusDescriptionTagName | ||||
|         statusDescriptionTagName, | ||||
|         Resource.empty() | ||||
|       ); | ||||
| 
 | ||||
|       assert.deepStrictEqual(tags, { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue