LogDrains.utils.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /**
  2. * Utility functions for log drain management
  3. * Extracted for testability
  4. */
  5. import { getKeyValueFieldArrayValidationIssues } from 'ui-patterns/form/KeyValueFieldArray/validation'
  6. import { z } from 'zod'
  7. import { LogDrainType } from './LogDrains.constants'
  8. import { httpEndpointUrlSchema } from '@/lib/validation/http-url'
  9. export type LogDrainHeaderRow = {
  10. key: string
  11. value: string
  12. }
  13. /**
  14. * Get the description text for the custom headers section based on log drain type
  15. */
  16. export function getHeadersSectionDescription(type: LogDrainType): string {
  17. if (type === 'webhook') {
  18. return 'Set custom headers when draining logs to the Endpoint URL'
  19. }
  20. if (type === 'loki') {
  21. return 'Set custom headers when draining logs to the Loki HTTP(S) endpoint'
  22. }
  23. if (type === 'otlp') {
  24. return 'Set custom headers for OTLP authentication (e.g., Authorization, X-API-Key)'
  25. }
  26. return ''
  27. }
  28. /**
  29. * Validation errors for header management
  30. */
  31. export const HEADER_VALIDATION_ERRORS = {
  32. MAX_LIMIT: 'You can only have 20 custom headers',
  33. DUPLICATE: 'Header name already exists',
  34. KEY_REQUIRED: 'Header name is required',
  35. VALUE_REQUIRED: 'Header value is required',
  36. } as const
  37. const DEFAULT_HEADERS_BY_TYPE: Partial<Record<LogDrainType, Record<string, string>>> = {
  38. webhook: { 'Content-Type': 'application/json' },
  39. otlp: { 'Content-Type': 'application/x-protobuf' },
  40. }
  41. export function getDefaultHeadersByType(type: LogDrainType): Record<string, string> {
  42. return DEFAULT_HEADERS_BY_TYPE[type] ?? {}
  43. }
  44. export function headerRecordToRows(headers: Record<string, string> = {}): LogDrainHeaderRow[] {
  45. return Object.entries(headers).map(([key, value]) => ({ key, value }))
  46. }
  47. export function headerRowsToRecord(rows: LogDrainHeaderRow[] = []): Record<string, string> {
  48. return rows.reduce<Record<string, string>>((acc, row) => {
  49. const key = row.key.trim()
  50. const value = row.value.trim()
  51. if (key && value) {
  52. acc[key] = value
  53. }
  54. return acc
  55. }, {})
  56. }
  57. export const logDrainHeaderEntriesSchema = z
  58. .array(
  59. z.object({
  60. key: z.string().trim(),
  61. value: z.string().trim(),
  62. })
  63. )
  64. .max(20, HEADER_VALIDATION_ERRORS.MAX_LIMIT)
  65. .superRefine((rows, ctx) => {
  66. getKeyValueFieldArrayValidationIssues({
  67. rows,
  68. keyFieldName: 'key',
  69. valueFieldName: 'value',
  70. keyRequiredMessage: HEADER_VALIDATION_ERRORS.KEY_REQUIRED,
  71. valueRequiredMessage: HEADER_VALIDATION_ERRORS.VALUE_REQUIRED,
  72. duplicateKeyMessage: HEADER_VALIDATION_ERRORS.DUPLICATE,
  73. }).forEach((issue) => {
  74. ctx.addIssue({
  75. code: z.ZodIssueCode.custom,
  76. message: issue.message,
  77. path: issue.path,
  78. })
  79. })
  80. })
  81. /**
  82. * Zod schema for OTLP log drain configuration
  83. * Extracted for testing purposes
  84. */
  85. export const otlpConfigSchema = z.object({
  86. type: z.literal('otlp'),
  87. endpoint: httpEndpointUrlSchema({
  88. requiredMessage: 'OTLP endpoint is required',
  89. invalidMessage: 'OTLP endpoint must be a valid URL',
  90. prefixMessage: 'OTLP endpoint must start with http:// or https://',
  91. }),
  92. protocol: z.string().optional().default('http/protobuf'),
  93. gzip: z.boolean().optional().default(true),
  94. headers: z.record(z.string(), z.string()).optional(),
  95. })
  96. export type OtlpConfig = z.infer<typeof otlpConfigSchema>