Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vtex/api",
"version": "7.2.3",
"version": "7.2.4-beta.0",
"description": "VTEX I/O API client",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
244 changes: 243 additions & 1 deletion src/constants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,36 @@ import {
PRODUCTION,
INSPECT_DEBUGGER_PORT,
cancellableMethods,
LOG_CLIENT_INIT_TIMEOUT_MS
LOG_CLIENT_INIT_TIMEOUT_MS,
// Backward-compatible individual header constants
CACHE_CONTROL_HEADER,
SEGMENT_HEADER,
SESSION_HEADER,
PRODUCT_HEADER,
LOCALE_HEADER,
FORWARDED_HOST_HEADER,
TENANT_HEADER,
BINDING_HEADER,
META_HEADER,
META_HEADER_BUCKET,
ETAG_HEADER,
ACCOUNT_HEADER,
CREDENTIAL_HEADER,
REQUEST_ID_HEADER,
ROUTER_CACHE_HEADER,
OPERATION_ID_HEADER,
PLATFORM_HEADER,
WORKSPACE_IS_PRODUCTION_HEADER,
WORKSPACE_HEADER,
EVENT_KEY_HEADER,
EVENT_SENDER_HEADER,
EVENT_SUBJECT_HEADER,
EVENT_HANDLER_ID_HEADER,
COLOSSUS_ROUTE_DECLARER_HEADER,
COLOSSUS_ROUTE_ID_HEADER,
COLOSSUS_PARAMS_HEADER,
TRACE_ID_HEADER,
PROVIDER_HEADER
} from './constants'

describe('constants', () => {
Expand Down Expand Up @@ -426,5 +455,218 @@ describe('constants', () => {
expect(APP).toHaveProperty(prop)
})
})

describe('Individual header constants (deprecated)', () => {
test('all individual header constants should be defined', () => {
expect(CACHE_CONTROL_HEADER).toBeDefined()
expect(SEGMENT_HEADER).toBeDefined()
expect(SESSION_HEADER).toBeDefined()
expect(PRODUCT_HEADER).toBeDefined()
expect(LOCALE_HEADER).toBeDefined()
expect(FORWARDED_HOST_HEADER).toBeDefined()
expect(TENANT_HEADER).toBeDefined()
expect(BINDING_HEADER).toBeDefined()
expect(META_HEADER).toBeDefined()
expect(META_HEADER_BUCKET).toBeDefined()
expect(ETAG_HEADER).toBeDefined()
expect(ACCOUNT_HEADER).toBeDefined()
expect(CREDENTIAL_HEADER).toBeDefined()
expect(REQUEST_ID_HEADER).toBeDefined()
expect(ROUTER_CACHE_HEADER).toBeDefined()
expect(OPERATION_ID_HEADER).toBeDefined()
expect(PLATFORM_HEADER).toBeDefined()
expect(WORKSPACE_IS_PRODUCTION_HEADER).toBeDefined()
expect(WORKSPACE_HEADER).toBeDefined()
expect(EVENT_KEY_HEADER).toBeDefined()
expect(EVENT_SENDER_HEADER).toBeDefined()
expect(EVENT_SUBJECT_HEADER).toBeDefined()
expect(EVENT_HANDLER_ID_HEADER).toBeDefined()
expect(COLOSSUS_ROUTE_DECLARER_HEADER).toBeDefined()
expect(COLOSSUS_ROUTE_ID_HEADER).toBeDefined()
expect(COLOSSUS_PARAMS_HEADER).toBeDefined()
expect(TRACE_ID_HEADER).toBeDefined()
expect(PROVIDER_HEADER).toBeDefined()
})

test('individual constants should equal HeaderKeys values', () => {
expect(CACHE_CONTROL_HEADER).toBe(HeaderKeys.CACHE_CONTROL)
expect(SEGMENT_HEADER).toBe(HeaderKeys.SEGMENT)
expect(SESSION_HEADER).toBe(HeaderKeys.SESSION)
expect(PRODUCT_HEADER).toBe(HeaderKeys.PRODUCT)
expect(LOCALE_HEADER).toBe(HeaderKeys.LOCALE)
expect(FORWARDED_HOST_HEADER).toBe(HeaderKeys.FORWARDED_HOST)
expect(TENANT_HEADER).toBe(HeaderKeys.TENANT)
expect(BINDING_HEADER).toBe(HeaderKeys.BINDING)
expect(META_HEADER).toBe(HeaderKeys.META)
expect(META_HEADER_BUCKET).toBe(HeaderKeys.META_BUCKET)
expect(ETAG_HEADER).toBe(HeaderKeys.ETAG)
expect(ACCOUNT_HEADER).toBe(HeaderKeys.ACCOUNT)
expect(CREDENTIAL_HEADER).toBe(HeaderKeys.CREDENTIAL)
expect(REQUEST_ID_HEADER).toBe(HeaderKeys.REQUEST_ID)
expect(ROUTER_CACHE_HEADER).toBe(HeaderKeys.ROUTER_CACHE)
expect(OPERATION_ID_HEADER).toBe(HeaderKeys.OPERATION_ID)
expect(PLATFORM_HEADER).toBe(HeaderKeys.PLATFORM)
expect(WORKSPACE_IS_PRODUCTION_HEADER).toBe(HeaderKeys.WORKSPACE_IS_PRODUCTION)
expect(WORKSPACE_HEADER).toBe(HeaderKeys.WORKSPACE)
expect(EVENT_KEY_HEADER).toBe(HeaderKeys.EVENT_KEY)
expect(EVENT_SENDER_HEADER).toBe(HeaderKeys.EVENT_SENDER)
expect(EVENT_SUBJECT_HEADER).toBe(HeaderKeys.EVENT_SUBJECT)
expect(EVENT_HANDLER_ID_HEADER).toBe(HeaderKeys.EVENT_HANDLER_ID)
expect(COLOSSUS_ROUTE_DECLARER_HEADER).toBe(HeaderKeys.COLOSSUS_ROUTE_DECLARER)
expect(COLOSSUS_ROUTE_ID_HEADER).toBe(HeaderKeys.COLOSSUS_ROUTE_ID)
expect(COLOSSUS_PARAMS_HEADER).toBe(HeaderKeys.COLOSSUS_PARAMS)
expect(TRACE_ID_HEADER).toBe(HeaderKeys.TRACE_ID)
expect(PROVIDER_HEADER).toBe(HeaderKeys.PROVIDER)
})

test('critical individual constants should have expected string values', () => {
expect(TENANT_HEADER).toBe('x-vtex-tenant')
expect(BINDING_HEADER).toBe('x-vtex-binding')
expect(LOCALE_HEADER).toBe('x-vtex-locale')
expect(SEGMENT_HEADER).toBe('x-vtex-segment')
expect(SESSION_HEADER).toBe('x-vtex-session')
expect(ACCOUNT_HEADER).toBe('x-vtex-account')
expect(WORKSPACE_HEADER).toBe('x-vtex-workspace')
})

test('individual constants should be strings', () => {
expect(typeof TENANT_HEADER).toBe('string')
expect(typeof BINDING_HEADER).toBe('string')
expect(typeof LOCALE_HEADER).toBe('string')
expect(typeof SEGMENT_HEADER).toBe('string')
})

test('constants can be used as object keys without runtime errors', () => {
// This is how IO apps use them in practice
const headers = {
[TENANT_HEADER]: 'example-value',
[BINDING_HEADER]: 'example-binding',
[LOCALE_HEADER]: 'en-US',
[SEGMENT_HEADER]: 'segment-token',
[SESSION_HEADER]: 'session-token',
[ACCOUNT_HEADER]: 'account-name',
[WORKSPACE_HEADER]: 'master'
}

expect(headers['x-vtex-tenant']).toBe('example-value')
expect(headers['x-vtex-binding']).toBe('example-binding')
expect(headers['x-vtex-locale']).toBe('en-US')
expect(headers['x-vtex-segment']).toBe('segment-token')
expect(Object.keys(headers)).toHaveLength(7)

// Verify no undefined keys were created
Object.keys(headers).forEach(key => {
expect(key).not.toBe('undefined')
expect(headers[key]).toBeDefined()
})
})

test('constants can be destructured from module exports', () => {
// Simulates: import { TENANT_HEADER, BINDING_HEADER } from '@vtex/api'
const constants = require('./constants')
const {
TENANT_HEADER: tenant,
BINDING_HEADER: binding,
LOCALE_HEADER: locale,
SEGMENT_HEADER: segment
} = constants

expect(tenant).toBeDefined()
expect(binding).toBeDefined()
expect(locale).toBeDefined()
expect(segment).toBeDefined()

expect(tenant).toBe('x-vtex-tenant')
expect(binding).toBe('x-vtex-binding')
expect(locale).toBe('x-vtex-locale')
expect(segment).toBe('x-vtex-segment')

// Ensure they're not undefined
expect(tenant).not.toBe(undefined)
expect(binding).not.toBe(undefined)
})

test('individual constants are compatible with VaryHeaders type', () => {
// VaryHeaders type uses HeaderKeys internally, but should accept old constants
const varyHeaderValues: string[] = [SEGMENT_HEADER, SESSION_HEADER, PRODUCT_HEADER, LOCALE_HEADER]

varyHeaderValues.forEach(header => {
expect(typeof header).toBe('string')
expect(header.length).toBeGreaterThan(0)
// VTEX headers follow x-vtex- pattern, except standard headers like cache-control
expect(header).toMatch(/^x-vtex-|^cache-control$|^etag$/)
})

// Verify they match the type definition (HeaderKeys values)
const expectedVaryHeaders = [
HeaderKeys.SEGMENT,
HeaderKeys.SESSION,
HeaderKeys.PRODUCT,
HeaderKeys.LOCALE
]

expect(varyHeaderValues).toEqual(expectedVaryHeaders)

// Ensure VaryHeaders type inference works
expect(SEGMENT_HEADER).toBe(HeaderKeys.SEGMENT)
expect(SESSION_HEADER).toBe(HeaderKeys.SESSION)
expect(PRODUCT_HEADER).toBe(HeaderKeys.PRODUCT)
expect(LOCALE_HEADER).toBe(HeaderKeys.LOCALE)
})

test('constants work correctly as header keys in realistic scenarios', () => {
// Simulates IO apps usage patterns
const mockBinding = { locale: 'en-US', currency: 'USD' }
const mockTenant = { locale: 'pt-BR' }
const mockSegmentToken = 'eyJjYW1wYWlnbnMiOm51bGx9'
const mockSessionToken = 'session-abc-123'

// Pattern 1: Building headers object
const requestHeaders = {
[BINDING_HEADER]: JSON.stringify(mockBinding),
[TENANT_HEADER]: mockTenant.locale,
[LOCALE_HEADER]: 'en-US',
[SEGMENT_HEADER]: mockSegmentToken,
[SESSION_HEADER]: mockSessionToken,
[ACCOUNT_HEADER]: 'vtexstore',
[WORKSPACE_HEADER]: 'master'
}

expect(requestHeaders['x-vtex-binding']).toBe(JSON.stringify(mockBinding))
expect(requestHeaders['x-vtex-tenant']).toBe('pt-BR')
expect(requestHeaders['x-vtex-locale']).toBe('en-US')
expect(requestHeaders['x-vtex-segment']).toBe(mockSegmentToken)
expect(requestHeaders['x-vtex-session']).toBe(mockSessionToken)

// Pattern 2: Conditional header setting
const conditionalHeaders: Record<string, string> = {}
if (mockSegmentToken) {
conditionalHeaders[SEGMENT_HEADER] = mockSegmentToken
}
if (mockSessionToken) {
conditionalHeaders[SESSION_HEADER] = mockSessionToken
}

expect(conditionalHeaders['x-vtex-segment']).toBe(mockSegmentToken)
expect(conditionalHeaders['x-vtex-session']).toBe(mockSessionToken)
expect(Object.keys(conditionalHeaders)).toHaveLength(2)

// Pattern 3: Reading from headers object
const incomingHeaders = {
'x-vtex-tenant': 'es-AR',
'x-vtex-binding': '{"locale":"es-AR"}',
'x-vtex-account': 'mystore'
}

expect(incomingHeaders[TENANT_HEADER]).toBe('es-AR')
expect(incomingHeaders[BINDING_HEADER]).toBe('{"locale":"es-AR"}')
expect(incomingHeaders[ACCOUNT_HEADER]).toBe('mystore')

// Verify no undefined keys in any pattern
expect(TENANT_HEADER).not.toBe('undefined')
expect(BINDING_HEADER).not.toBe('undefined')
expect(SEGMENT_HEADER).not.toBe('undefined')
})
})
})
})
84 changes: 84 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,90 @@ export const AttributeKeys = {
VTEX_IO_APP_AUTHOR_TYPE: ATTR_VTEX_IO_APP_AUTHOR_TYPE,
}

/** @deprecated Use HeaderKeys.CACHE_CONTROL instead */
export const CACHE_CONTROL_HEADER = HeaderKeys.CACHE_CONTROL

/** @deprecated Use HeaderKeys.SEGMENT instead */
export const SEGMENT_HEADER = HeaderKeys.SEGMENT

/** @deprecated Use HeaderKeys.SESSION instead */
export const SESSION_HEADER = HeaderKeys.SESSION

/** @deprecated Use HeaderKeys.PRODUCT instead */
export const PRODUCT_HEADER = HeaderKeys.PRODUCT

/** @deprecated Use HeaderKeys.LOCALE instead */
export const LOCALE_HEADER = HeaderKeys.LOCALE

/** @deprecated Use HeaderKeys.FORWARDED_HOST instead */
export const FORWARDED_HOST_HEADER = HeaderKeys.FORWARDED_HOST

/** @deprecated Use HeaderKeys.TENANT instead */
export const TENANT_HEADER = HeaderKeys.TENANT

/** @deprecated Use HeaderKeys.BINDING instead */
export const BINDING_HEADER = HeaderKeys.BINDING

/** @deprecated Use HeaderKeys.META instead */
export const META_HEADER = HeaderKeys.META

/** @deprecated Use HeaderKeys.META_BUCKET instead */
export const META_HEADER_BUCKET = HeaderKeys.META_BUCKET

/** @deprecated Use HeaderKeys.ETAG instead */
export const ETAG_HEADER = HeaderKeys.ETAG

/** @deprecated Use HeaderKeys.ACCOUNT instead */
export const ACCOUNT_HEADER = HeaderKeys.ACCOUNT

/** @deprecated Use HeaderKeys.CREDENTIAL instead */
export const CREDENTIAL_HEADER = HeaderKeys.CREDENTIAL

/** @deprecated Use HeaderKeys.REQUEST_ID instead */
export const REQUEST_ID_HEADER = HeaderKeys.REQUEST_ID

/** @deprecated Use HeaderKeys.ROUTER_CACHE instead */
export const ROUTER_CACHE_HEADER = HeaderKeys.ROUTER_CACHE

/** @deprecated Use HeaderKeys.OPERATION_ID instead */
export const OPERATION_ID_HEADER = HeaderKeys.OPERATION_ID

/** @deprecated Use HeaderKeys.PLATFORM instead */
export const PLATFORM_HEADER = HeaderKeys.PLATFORM

/** @deprecated Use HeaderKeys.WORKSPACE_IS_PRODUCTION instead */
export const WORKSPACE_IS_PRODUCTION_HEADER = HeaderKeys.WORKSPACE_IS_PRODUCTION

/** @deprecated Use HeaderKeys.WORKSPACE instead */
export const WORKSPACE_HEADER = HeaderKeys.WORKSPACE

/** @deprecated Use HeaderKeys.EVENT_KEY instead */
export const EVENT_KEY_HEADER = HeaderKeys.EVENT_KEY

/** @deprecated Use HeaderKeys.EVENT_SENDER instead */
export const EVENT_SENDER_HEADER = HeaderKeys.EVENT_SENDER

/** @deprecated Use HeaderKeys.EVENT_SUBJECT instead */
export const EVENT_SUBJECT_HEADER = HeaderKeys.EVENT_SUBJECT

/** @deprecated Use HeaderKeys.EVENT_HANDLER_ID instead */
export const EVENT_HANDLER_ID_HEADER = HeaderKeys.EVENT_HANDLER_ID

/** @deprecated Use HeaderKeys.COLOSSUS_ROUTE_DECLARER instead */
export const COLOSSUS_ROUTE_DECLARER_HEADER = HeaderKeys.COLOSSUS_ROUTE_DECLARER

/** @deprecated Use HeaderKeys.COLOSSUS_ROUTE_ID instead */
export const COLOSSUS_ROUTE_ID_HEADER = HeaderKeys.COLOSSUS_ROUTE_ID

/** @deprecated Use HeaderKeys.COLOSSUS_PARAMS instead */
export const COLOSSUS_PARAMS_HEADER = HeaderKeys.COLOSSUS_PARAMS

/** @deprecated Use HeaderKeys.TRACE_ID instead */
export const TRACE_ID_HEADER = HeaderKeys.TRACE_ID

/** @deprecated Use HeaderKeys.PROVIDER instead */
export const PROVIDER_HEADER = HeaderKeys.PROVIDER

export type VaryHeaders = typeof HeaderKeys.SEGMENT | typeof HeaderKeys.SESSION | typeof HeaderKeys.PRODUCT | typeof HeaderKeys.LOCALE

export const BODY_HASH = '__graphqlBodyHash'
Expand Down
Loading