@zendesk/retrace
Version:
define and capture Product Operation Traces along with computed metrics with an optional friendly React beacon API
199 lines • 7.07 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.withName = withName;
exports.withLabel = withLabel;
exports.withPerformanceEntryName = withPerformanceEntryName;
exports.withType = withType;
exports.withStatus = withStatus;
exports.withAttributes = withAttributes;
exports.withMatchingRelations = withMatchingRelations;
exports.withOccurrence = withOccurrence;
exports.withComponentRenderCount = withComponentRenderCount;
exports.whenIdle = whenIdle;
exports.continueWithErrorStatus = continueWithErrorStatus;
exports.withAllConditions = withAllConditions;
exports.withOneOfConditions = withOneOfConditions;
exports.not = not;
exports.fromDefinition = fromDefinition;
/**
* The common name of the span to match. Can be a string, RegExp, or function.
*/
function withName(value) {
return ({ span }, { input: { relatedTo } }) => {
if (typeof value === 'string')
return span.name === value;
if (value instanceof RegExp)
return value.test(span.name);
return value(span.name, relatedTo);
};
}
// DRAFT TODO: make test case if one doesnt exist yet
// withName((name, relatedTo) => !relatedTo ? false : name === `OmniLog/${relatedTo.ticketId}`)
function withLabel(value) {
return ({ annotation }) => annotation.labels?.includes(value) ?? false;
}
/**
* The PerformanceEntry.name of the entry to match. Can be a string, RegExp, or function.
*/
function withPerformanceEntryName(value) {
return ({ span }, { input: { relatedTo } }) => {
const entryName = span.performanceEntry?.name;
if (!entryName)
return false;
if (typeof value === 'string')
return entryName === value;
if (value instanceof RegExp)
return value.test(entryName);
return value(entryName, relatedTo);
};
}
function withType(value) {
return ({ span }) => span.type === value;
}
function withStatus(value) {
return ({ span }) => span.status === value;
}
/**
* The subset of attributes (metadata) to match against the span.
*/
function withAttributes(attrs) {
return ({ span }) => {
if (!span.attributes)
return false;
return Object.entries(attrs).every(([key, value]) => span.attributes[key] === value);
};
}
/**
* A list of keys of trace's relations to match against the span's.
*/
function withMatchingRelations(keys = true) {
return ({ span }, { input: { relatedTo: r }, definition: { relationSchema } }) => {
// DRAFT TODO: add test case when relatedTo is missing
// if the relatedTo isn't set on the trace yet, we can't match against it, so we return early
// similarly, if the span doesn't have any relatedTo set
const relatedToInput = r;
if (!span.relatedTo || !relatedToInput)
return false;
const spanRelatedTo = span.relatedTo;
const resolvedKeys = typeof keys === 'boolean' && keys
? Object.keys(relationSchema)
: keys;
if (!resolvedKeys)
return false;
return resolvedKeys.every((key) => key in spanRelatedTo && spanRelatedTo[key] === relatedToInput[key]);
};
}
/**
* The occurrence of the span with the same name within the operation.
*/
function withOccurrence(value) {
return ({ annotation }) => {
if (typeof value === 'number')
return annotation.occurrence === value;
return value(annotation.occurrence);
};
}
function withComponentRenderCount(name, renderCount) {
return ({ span }) => {
if (!('renderCount' in span))
return false;
return span.name === name && span.renderCount === renderCount;
};
}
/**
* only applicable for component-lifecycle entries
*/
function whenIdle(value = true) {
const matcherFn = ({ span }) => ('isIdle' in span ? span.isIdle === value : false);
return Object.assign(matcherFn,
// add a tag to the function if set to true
value ? { idleCheck: value } : {});
}
/**
* Only applicable for 'requiredSpans' list: it will opt-out of the default behavior,
* which interrupts the trace if the requiredSpan has an error status.
*/
function continueWithErrorStatus() {
return Object.assign(() => true,
// add a tag to the function if set to true
{ continueWithErrorStatus: true });
}
// logical combinators:
// AND
function withAllConditions(...matchers) {
const tags = {};
const definition = {};
for (const matcher of matchers) {
// carry over tags from sub-matchers
Object.assign(tags, matcher);
if (matcher.fromDefinition) {
// carry over definition from sub-matchers
Object.assign(definition, matcher.fromDefinition);
}
}
const matcherFn = (...args) => matchers.every((matcher) => matcher(...args));
return Object.assign(matcherFn, tags, { fromDefinition: definition });
}
// OR
function withOneOfConditions(...matchers) {
const tags = {};
for (const matcher of matchers) {
// carry over tags from sub-matchers
Object.assign(tags, matcher);
}
const matcherFn = (...args) => matchers.some((matcher) => matcher(...args));
return Object.assign(matcherFn, tags, { fromDefinition: { oneOf: matchers } });
}
function not(matcher) {
// since not is a negation, we don't carry over tags
return (...args) => !matcher(...args);
}
function fromDefinition(definition) {
const matchers = [];
if (definition.name) {
matchers.push(withName(definition.name));
}
if (definition.performanceEntryName) {
matchers.push(withPerformanceEntryName(definition.performanceEntryName));
}
if (definition.type) {
matchers.push(withType(definition.type));
}
if (definition.status) {
matchers.push(withStatus(definition.status));
}
if (definition.attributes) {
matchers.push(withAttributes(definition.attributes));
}
if (definition.matchingRelations) {
matchers.push(withMatchingRelations(definition.matchingRelations));
}
if (definition.occurrence) {
matchers.push(withOccurrence(definition.occurrence));
}
if (definition.isIdle) {
matchers.push(whenIdle(definition.isIdle));
}
if (definition.label) {
matchers.push(withLabel(definition.label));
}
if (definition.fn) {
matchers.push(definition.fn);
}
let combined;
// Check if definition has oneOf property
if (definition.oneOf) {
// Convert each definition in oneOf array to a matcher and combine with OR
const oneOfMatchers = definition.oneOf.map((def) => fromDefinition(def));
combined = withAllConditions(...matchers, withOneOfConditions(...oneOfMatchers));
}
else {
combined = withAllConditions(...matchers);
}
combined.fromDefinition = definition;
if (typeof definition.matchingIndex === 'number') {
combined.matchingIndex = definition.matchingIndex;
}
return combined;
}
//# sourceMappingURL=matchSpan.js.map