-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add S3 Downstream Span Pointers #587
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import { getSpanPointerAttributes } from "./span-pointers"; | ||
import { eventTypes } from "../trace/trigger"; | ||
import { SPAN_LINK_KIND, S3_PTR_KIND, SPAN_POINTER_DIRECTION } from "dd-trace/packages/dd-trace/src/span_pointers"; | ||
import * as spanPointers from "dd-trace/packages/dd-trace/src/span_pointers"; | ||
|
||
// Mock the external dependencies | ||
jest.mock("./log", () => ({ | ||
logDebug: jest.fn(), | ||
})); | ||
|
||
describe("span-pointers utils", () => { | ||
const mockS3PointerHash = "mock-hash-123"; | ||
|
||
beforeEach(() => { | ||
// Mock the generateS3PointerHash function | ||
jest.spyOn(spanPointers, "generateS3PointerHash").mockReturnValue(mockS3PointerHash); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe("getSpanPointerAttributes", () => { | ||
it("returns undefined when eventSource is undefined", () => { | ||
const result = getSpanPointerAttributes(undefined, {}); | ||
expect(result).toBeUndefined(); | ||
}); | ||
|
||
it("returns undefined for unsupported event types", () => { | ||
const result = getSpanPointerAttributes("unsupported" as eventTypes, {}); | ||
expect(result).toBeUndefined(); | ||
}); | ||
|
||
describe("S3 event processing", () => { | ||
it("processes single S3 record correctly", () => { | ||
const event = { | ||
Records: [ | ||
{ | ||
s3: { | ||
bucket: { name: "test-bucket" }, | ||
object: { | ||
key: "test-key", | ||
eTag: "test-etag", | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
const expected = [ | ||
{ | ||
"ptr.kind": S3_PTR_KIND, | ||
"ptr.dir": SPAN_POINTER_DIRECTION.UPSTREAM, | ||
"ptr.hash": mockS3PointerHash, | ||
"link.kind": SPAN_LINK_KIND, | ||
}, | ||
]; | ||
|
||
const result = getSpanPointerAttributes(eventTypes.s3, event); | ||
expect(result).toEqual(expected); | ||
expect(spanPointers.generateS3PointerHash).toHaveBeenCalledWith("test-bucket", "test-key", "test-etag"); | ||
}); | ||
|
||
it("processes multiple S3 records correctly", () => { | ||
const event = { | ||
Records: [ | ||
{ | ||
s3: { | ||
bucket: { name: "bucket1" }, | ||
object: { | ||
key: "key1", | ||
eTag: "etag1", | ||
}, | ||
}, | ||
}, | ||
{ | ||
s3: { | ||
bucket: { name: "bucket2" }, | ||
object: { | ||
key: "key2", | ||
eTag: "etag2", | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
const expected = [ | ||
{ | ||
"ptr.kind": S3_PTR_KIND, | ||
"ptr.dir": SPAN_POINTER_DIRECTION.UPSTREAM, | ||
"ptr.hash": mockS3PointerHash, | ||
"link.kind": SPAN_LINK_KIND, | ||
}, | ||
{ | ||
"ptr.kind": S3_PTR_KIND, | ||
"ptr.dir": SPAN_POINTER_DIRECTION.UPSTREAM, | ||
"ptr.hash": mockS3PointerHash, | ||
"link.kind": SPAN_LINK_KIND, | ||
}, | ||
]; | ||
|
||
const result = getSpanPointerAttributes(eventTypes.s3, event); | ||
expect(result).toEqual(expected); | ||
}); | ||
|
||
it("handles empty Records array", () => { | ||
const event = { Records: [] }; | ||
const result = getSpanPointerAttributes(eventTypes.s3, event); | ||
expect(result).toEqual([]); | ||
}); | ||
|
||
it("handles missing Records property", () => { | ||
const event = {}; | ||
const result = getSpanPointerAttributes(eventTypes.s3, event); | ||
expect(result).toEqual([]); | ||
}); | ||
|
||
it("skips invalid records but processes valid ones", () => { | ||
const event = { | ||
Records: [ | ||
{ | ||
// Invalid record missing s3 property | ||
}, | ||
{ | ||
s3: { | ||
bucket: { name: "valid-bucket" }, | ||
object: { | ||
key: "valid-key", | ||
eTag: "valid-etag", | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
const expected = [ | ||
{ | ||
"ptr.kind": S3_PTR_KIND, | ||
"ptr.dir": SPAN_POINTER_DIRECTION.UPSTREAM, | ||
"ptr.hash": mockS3PointerHash, | ||
"link.kind": SPAN_LINK_KIND, | ||
}, | ||
]; | ||
|
||
const result = getSpanPointerAttributes(eventTypes.s3, event); | ||
expect(result).toEqual(expected); | ||
}); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { eventTypes } from "../trace/trigger"; | ||
import { logDebug } from "./log"; | ||
import { | ||
SPAN_LINK_KIND, | ||
S3_PTR_KIND, | ||
SPAN_POINTER_DIRECTION, | ||
generateS3PointerHash, | ||
} from "dd-trace/packages/dd-trace/src/span_pointers"; | ||
Check failure on line 8 in src/utils/span-pointers.ts GitHub Actions / unit-test (16.14)
Check failure on line 8 in src/utils/span-pointers.ts GitHub Actions / unit-test (18.12)
|
||
|
||
interface SpanPointerAttributes { | ||
"ptr.kind": string; | ||
"ptr.dir": string; | ||
"ptr.hash": string; | ||
"link.kind": string; | ||
} | ||
|
||
/** | ||
* Computes span pointer attributes | ||
* | ||
* @param {eventTypes} eventSource - The type of event being processed (e.g., S3, DynamoDB). | ||
* @param {any} event - The event object containing source-specific data. | ||
* @returns {SpanPointerAttributes[] | undefined} An array of span pointer attribute objects, or undefined if none could be computed. | ||
*/ | ||
export function getSpanPointerAttributes( | ||
eventSource: eventTypes | undefined, | ||
event: any, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
): SpanPointerAttributes[] | undefined { | ||
if (!eventSource) { | ||
return; | ||
} | ||
|
||
switch (eventSource) { | ||
case eventTypes.s3: | ||
return processS3Event(event); | ||
default: | ||
logDebug(`Event type ${eventSource} not supported by span pointers.`); | ||
return; | ||
} | ||
} | ||
|
||
function processS3Event(event: any): SpanPointerAttributes[] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
const records = event.Records || []; | ||
const spanPointerAttributesList = []; | ||
const linkKind = SPAN_LINK_KIND; | ||
|
||
for (const record of records) { | ||
const eventName = record.eventName; | ||
if (!["ObjectCreated:Put", "ObjectCreated:Copy", "ObjectCreated:CompleteMultipartUpload"].includes(eventName)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why restrict us to only these particular object created types instead of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We only support creating span pointers for these three operations on the upstream case. I guess it doesn't hurt to create span pointers downstream in other cases; we could change the check to
|
||
continue; | ||
} | ||
// Values are stored in the same place, regardless of AWS SDK v2/v3 or the event type. | ||
// https://docs.aws.amazon.com/AmazonS3/latest/userguide/notification-content-structure.html | ||
const s3Event = record?.s3; | ||
const bucketName = s3Event?.bucket?.name; | ||
const objectKey = s3Event?.object?.key; | ||
const eTag = s3Event?.object?.eTag; | ||
|
||
if (!bucketName || !objectKey || !eTag) { | ||
logDebug("Unable to calculate span pointer hash because of missing parameters."); | ||
continue; | ||
} | ||
|
||
const pointerHash = generateS3PointerHash(bucketName, objectKey, eTag); | ||
const spanPointerAttributes = { | ||
"ptr.kind": S3_PTR_KIND, | ||
"ptr.dir": SPAN_POINTER_DIRECTION.UPSTREAM, | ||
"ptr.hash": pointerHash, | ||
"link.kind": linkKind, | ||
nhulston marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
spanPointerAttributesList.push(spanPointerAttributes); | ||
} | ||
|
||
return spanPointerAttributesList; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⚪ Code Quality Violation
Unexpected any. Specify a different type. (...read more)
Do not use the
any
type, as it is too broad and can lead to unexpected behavior.