-
Notifications
You must be signed in to change notification settings - Fork 828
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add TraceState * Fix review comments * Minor Fix, add TODOs * Use Map to preserve ordering * Add underscore for private methods & rename variable - Give more descriptive name than `s` to variable. - Private properties prefixed with an underscore for JavaScript users. - Private methods prefixed with an underscore for JavaScript users. * Remove TEST_ONLY methods * Add comment about TraceState class * Add unset method
- Loading branch information
1 parent
0494ddc
commit 0ce8fda
Showing
3 changed files
with
217 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/** | ||
* Copyright 2019, OpenTelemetry Authors | ||
* | ||
* 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 | ||
* | ||
* https://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 * as types from '@opentelemetry/types'; | ||
|
||
// TODO validate maximum number of items | ||
// const MAX_TRACE_STATE_ITEMS = 32; | ||
|
||
const MAX_TRACE_STATE_LEN = 512; | ||
const LIST_MEMBERS_SEPARATOR = ','; | ||
const LIST_MEMBER_KEY_VALUE_SPLITTER = '='; | ||
|
||
/** | ||
* TraceState must be a class and not a simple object type because of the spec | ||
* requirement (https://www.w3.org/TR/trace-context/#tracestate-field). | ||
* | ||
* Here is the list of allowed mutations: | ||
* - New key-value pair should be added into the beginning of the list | ||
* - The value of any key can be updated. Modified keys MUST be moved to the | ||
* beginning of the list. | ||
*/ | ||
export class TraceState implements types.TraceState { | ||
private _internalState: Map<string, string> = new Map(); | ||
|
||
constructor(rawTraceState?: string) { | ||
if (rawTraceState) this._parse(rawTraceState); | ||
} | ||
|
||
set(key: string, value: string): void { | ||
// TODO: Benchmark the different approaches(map vs list) and | ||
// use the faster one. | ||
if (this._internalState.has(key)) this._internalState.delete(key); | ||
this._internalState.set(key, value); | ||
} | ||
|
||
unset(key: string): void { | ||
this._internalState.delete(key); | ||
} | ||
|
||
get(key: string): string | undefined { | ||
return this._internalState.get(key); | ||
} | ||
|
||
serialize(): string { | ||
return this._keys() | ||
.reduce((agg: string[], key) => { | ||
agg.push(key + LIST_MEMBER_KEY_VALUE_SPLITTER + this.get(key)); | ||
return agg; | ||
}, []) | ||
.join(LIST_MEMBERS_SEPARATOR); | ||
} | ||
|
||
private _parse(rawTraceState: string) { | ||
if (rawTraceState.length > MAX_TRACE_STATE_LEN) return; | ||
// TODO validate maximum number of items | ||
this._internalState = rawTraceState | ||
.split(LIST_MEMBERS_SEPARATOR) | ||
.reverse() | ||
.reduce((agg: Map<string, string>, part: string) => { | ||
const i = part.indexOf(LIST_MEMBER_KEY_VALUE_SPLITTER); | ||
if (i !== -1) { | ||
// TODO validate key/value constraints defined in the spec | ||
agg.set(part.slice(0, i), part.slice(i + 1, part.length)); | ||
} | ||
return agg; | ||
}, new Map()); | ||
} | ||
|
||
private _keys(): string[] { | ||
return Array.from(this._internalState.keys()).reverse(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/** | ||
* Copyright 2019, OpenTelemetry Authors | ||
* | ||
* 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 | ||
* | ||
* https://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 * as assert from 'assert'; | ||
import { TraceState } from '../src/trace/TraceState'; | ||
|
||
describe('TraceState', () => { | ||
describe('serialize', () => { | ||
it('returns serialize string', () => { | ||
const state = new TraceState('a=1,b=2'); | ||
assert.deepStrictEqual(state.serialize(), 'a=1,b=2'); | ||
}); | ||
|
||
it('must replace keys and move them to the front', () => { | ||
const state = new TraceState('a=1,b=2'); | ||
state.set('b', '3'); | ||
assert.deepStrictEqual(state.serialize(), 'b=3,a=1'); | ||
}); | ||
|
||
it('must add new keys to the front', () => { | ||
const state = new TraceState(); | ||
state.set('vendorname1', 'opaqueValue1'); | ||
assert.deepStrictEqual(state.serialize(), 'vendorname1=opaqueValue1'); | ||
|
||
state.set('vendorname2', 'opaqueValue2'); | ||
assert.deepStrictEqual( | ||
state.serialize(), | ||
'vendorname2=opaqueValue2,vendorname1=opaqueValue1' | ||
); | ||
}); | ||
|
||
it('must unset the entries', () => { | ||
const state = new TraceState('c=4,b=3,a=1'); | ||
state.unset('b'); | ||
assert.deepStrictEqual(state.serialize(), 'c=4,a=1'); | ||
state.unset('c'); | ||
state.unset('A'); | ||
assert.deepStrictEqual(state.serialize(), 'a=1'); | ||
}); | ||
}); | ||
|
||
describe('parse', () => { | ||
it('must successfully parse valid state value', () => { | ||
const state = new TraceState( | ||
'vendorname2=opaqueValue2,vendorname1=opaqueValue1' | ||
); | ||
assert.deepStrictEqual(state.get('vendorname1'), 'opaqueValue1'); | ||
assert.deepStrictEqual(state.get('vendorname2'), 'opaqueValue2'); | ||
assert.deepStrictEqual( | ||
state.serialize(), | ||
'vendorname2=opaqueValue2,vendorname1=opaqueValue1' | ||
); | ||
}); | ||
|
||
it('must drop states when the items are too long', () => { | ||
const state = new TraceState('a=' + 'b'.repeat(512)); | ||
assert.deepStrictEqual(state.get('a'), undefined); | ||
assert.deepStrictEqual(state.serialize(), ''); | ||
}); | ||
|
||
it('must drop states which cannot be parsed', () => { | ||
const state = new TraceState('a=1,b,c=3'); | ||
assert.deepStrictEqual(state.get('a'), '1'); | ||
assert.deepStrictEqual(state.get('b'), undefined); | ||
assert.deepStrictEqual(state.get('c'), '3'); | ||
assert.deepStrictEqual(state.serialize(), 'a=1,c=3'); | ||
}); | ||
|
||
it('must parse states that only have a single value with an equal sign', () => { | ||
const state = new TraceState('a=1='); | ||
assert.deepStrictEqual(state.get('a'), '1='); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters