Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,55 @@ describe('Testing _build_attribute_object', () => {
it('should correctly format an attribute', () => {
expect(_private._build_attributes_object(valid_attributes_payload)).toEqual(airship_attributes_payload)
})

it('should NOT parse non-date string attributes as dates', () => {
const payload: AttributesPayload = {
named_user_id: 'test-user',
occurred: occurred.toISOString(),
attributes: {
shop_last_store_name: 'SOUMAGNE 2',
shop_visited_store_names: 'BARCHON | HOGNOUL | JEMEPPE | SOUMAGNE 2',
shop_last_store_id: '30290'
}
}
const result = _private._build_attributes_object(payload)

// These should remain as strings, not be converted to dates
const shopNameAttr = result.find((attr: any) => attr.key === 'shop_last_store_name')
expect(shopNameAttr?.value).toBe('SOUMAGNE 2')

const shopNamesAttr = result.find((attr: any) => attr.key === 'shop_visited_store_names')
expect(shopNamesAttr?.value).toBe('BARCHON | HOGNOUL | JEMEPPE | SOUMAGNE 2')

const shopIdAttr = result.find((attr: any) => attr.key === 'shop_last_store_id')
expect(shopIdAttr?.value).toBe('30290')
})

it('should still parse date-like strings as dates', () => {
const payload: AttributesPayload = {
named_user_id: 'test-user',
occurred: occurred.toISOString(),
attributes: {
birthdate: '1965-01-25T00:47:43.378Z',
account_creation: '2023-05-09T00:47:43.378Z',
custom_date_field: '2025-06-11',
another_date: '01/25/1965'
}
}
const result = _private._build_attributes_object(payload)

const birthdateAttr = result.find((attr: any) => attr.key === 'birthdate')
expect(birthdateAttr?.value).toBe('1965-01-25T00:47:43')

const accountCreationAttr = result.find((attr: any) => attr.key === 'account_creation')
expect(accountCreationAttr?.value).toBe('2023-05-09T00:47:43')

const customDateAttr = result.find((attr: any) => attr.key === 'custom_date_field')
expect(customDateAttr?.value).toMatch(/2025-06-11/)

const anotherDateAttr = result.find((attr: any) => attr.key === 'another_date')
expect(anotherDateAttr?.value).toMatch(/1965-01-25/)
})
})

describe('Testing _build_tags_object', () => {
Expand Down Expand Up @@ -131,6 +180,20 @@ describe('Testing _parse_date', () => {
it('should parse a date-looking string into a date object', () => {
expect(_private._parse_date('2025-06-11')).toBeInstanceOf(Date)
})

it('should NOT parse strings without date-like patterns', () => {
expect(_private._parse_date('SOUMAGNE 2')).toBeNull()
expect(_private._parse_date('BARCHON | HOGNOUL | JEMEPPE | SOUMAGNE 2')).toBeNull()
expect(_private._parse_date('30290')).toBeNull()
expect(_private._parse_date('SOUMAGNE')).toBeNull()
})

it('should parse valid date formats', () => {
expect(_private._parse_date('2023-05-09T00:47:43.378Z')).toBeInstanceOf(Date)
expect(_private._parse_date('2025-06-11')).toBeInstanceOf(Date)
expect(_private._parse_date('01/25/1965')).toBeInstanceOf(Date)
expect(_private._parse_date('1965-01-25')).toBeInstanceOf(Date)
})
})

describe('Testing _parse_and_format_date', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,20 +377,39 @@
function _parse_date(attribute_value: any): Date | null {
/*
This function is for converting dates or returning null if they're not valid.
It requires the string to contain date-like patterns to avoid false positives.
*/
if (attribute_value.length < 8) {
return null // Reduce false positive dates
}

// Require the string to contain date-like patterns to avoid false positives
// Common date separators: dashes, slashes, colons, spaces with numbers
// ISO format patterns: YYYY-MM-DD, YYYY-MM-DDTHH:mm:ss, etc.
const dateLikePattern = /(\d{4}[-\/]\d{1,2}[-\/]\d{1,2})|(\d{1,2}[-\/]\d{1,2}[-\/]\d{4})|(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})|(\d{1,2}\/\d{1,2}\/\d{4})/

Check failure on line 389 in packages/destination-actions/src/destinations/airship/utilities.ts

View workflow job for this annotation

GitHub Actions / Lint (22.x)

Unnecessary escape character: \/

Check failure on line 389 in packages/destination-actions/src/destinations/airship/utilities.ts

View workflow job for this annotation

GitHub Actions / Lint (22.x)

Unnecessary escape character: \/

Check failure on line 389 in packages/destination-actions/src/destinations/airship/utilities.ts

View workflow job for this annotation

GitHub Actions / Lint (22.x)

Unnecessary escape character: \/

Check failure on line 389 in packages/destination-actions/src/destinations/airship/utilities.ts

View workflow job for this annotation

GitHub Actions / Lint (22.x)

Unnecessary escape character: \/

// If the string doesn't contain date-like patterns, don't parse it as a date
if (!dateLikePattern.test(attribute_value)) {
return null
}

// Attempt to parse the attribute_value as a Date
const date = new Date(attribute_value)

// Check if the parsing was successful and the result is a valid date
if (!isNaN(date.getTime())) {
return date // Return the parsed Date
if (isNaN(date.getTime())) {
return null
}

// Additional validation: check if the parsed date is reasonable
// Reject dates that are too far in the past (before 1900) or future (after 2100)
// This helps catch cases where JavaScript's Date constructor makes unexpected interpretations
const year = date.getFullYear()
if (year < 1900 || year > 2100) {
return null
}

return null // Return null for invalid dates
return date // Return the parsed Date
}

function _extract_country_language(locale: string): string[] {
Expand Down
Loading