• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / storage / 30382005254

28 Jul 2026 05:14PM UTC coverage: 59.206% (-21.1%) from 80.307%
30382005254

Pull #1270

github

web-flow
Merge 66b88da12 into 20927735c
Pull Request #1270: fix(storage): document 200 responses for vector bucket CRUD endpoints

3676 of 7083 branches covered (51.9%)

Branch coverage included in aggregate %.

10 of 70 new or added lines in 3 files covered. (14.29%)

2445 existing lines in 105 files now uncovered.

7649 of 12045 relevant lines covered (63.5%)

363.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.78
/src/http/plugins/log-request.ts
1
import {
2
  getValidTraceparent,
3
  logSchema,
4
  serializeReplyLog,
5
  serializeRequestLog,
6
} from '@internal/monitoring'
7
import type { FastifyReply, FastifyRequest } from 'fastify'
8
import fastifyPlugin from 'fastify-plugin'
9

10
interface RequestLoggerOptions {
11
  excludeUrls?: Set<string>
12
}
13

14
// Fixed positions in a validated W3C traceparent value.
15
const TRACE_ID_START = 3
29✔
16
const TRACE_ID_END = 35
29✔
17
const SPAN_ID_START = 36
29✔
18
const SPAN_ID_END = 52
29✔
19

20
type BivariantHandler<Args extends unknown[], Return> = {
21
  bivarianceHack(...args: Args): Return
22
}['bivarianceHack']
23

24
declare module 'http' {
25
  interface IncomingMessage {
26
    executionError?: Error
27
    resources?: string[]
28
  }
29
}
30

31
declare module 'fastify' {
32
  interface FastifyRequest {
33
    executionError?: Error
34
    operation?: string
35
    resources?: string[]
36
    startTime: number
37
    executionTime?: number
38
  }
39

40
  interface FastifyContextConfig {
41
    operation?: string
42
    // Explicit OpenAPI operationId override - takes precedence over the id derived from
43
    // `operation` (see src/http/routes/openapi-transform.ts). Set this when a route's
44
    // generated SDK method name needs to stay stable regardless of derivation/collision
45
    // rules, or to resolve a genuine duplicate operationId.
46
    operationId?: string
47
    resources?: BivariantHandler<[req: FastifyRequest], string[]>
48
    logMetadata?: BivariantHandler<[req: FastifyRequest], Record<string, unknown>>
49
  }
50
}
51

52
/**
53
 * Request logger plugin
54
 * @param options
55
 */
56
export const logRequest = (options: RequestLoggerOptions) =>
29✔
57
  fastifyPlugin(
302✔
58
    async (fastify) => {
59
      const excludeUrls = options.excludeUrls?.size ? options.excludeUrls : undefined
302!
60

61
      fastify.addHook('onRequest', (req, res, done) => {
302✔
62
        req.startTime = performance.now()
1,885✔
63

64
        if (excludeUrls?.has(req.url)) {
1,885!
UNCOV
65
          done()
×
UNCOV
66
          return
×
67
        }
68

69
        res.raw.once('close', () => {
1,885✔
70
          if (req.raw.aborted) {
1,885✔
71
            doRequestLog(req, {
1✔
72
              statusCode: 'ABORTED REQ',
73
              responseTime: performance.now() - req.startTime,
74
            })
75
            return
1✔
76
          }
77

78
          if (!res.raw.writableFinished) {
1,884✔
79
            doRequestLog(req, {
1,402✔
80
              statusCode: 'ABORTED RES',
81
              responseTime: performance.now() - req.startTime,
82
            })
83
          }
84
        })
85
        done()
1,885✔
86
      })
87

88
      /**
89
       * Adds req.resources and req.operation to the request object
90
       */
91
      fastify.addHook('preHandler', (req, _reply, done) => {
302✔
92
        let resources = req.resources
1,775✔
93

94
        if (resources === undefined) {
1,775!
95
          resources = req.routeOptions.config.resources?.(req)
1,775✔
96
        }
97

98
        if (resources === undefined) {
1,775✔
99
          resources = req.raw.resources
1,692✔
100
        }
101

102
        if (resources === undefined) {
1,775✔
103
          const resourceFromParams = getResourceFromParams(req.params)
1,692✔
104
          resources = resourceFromParams ? [resourceFromParams] : []
1,692✔
105
        }
106

107
        if (resources && resources.length > 0) {
1,775✔
108
          for (let index = 0; index < resources.length; index++) {
1,535✔
109
            const resource = resources[index]
7,567✔
110
            if (!resource.startsWith('/')) {
7,567!
111
              resources[index] = `/${resource}`
7,567✔
112
            }
113
          }
114
        }
115

116
        req.resources = resources
1,775✔
117
        req.operation = req.routeOptions.config.operation
1,775✔
118

119
        if (req.operation && typeof req.opentelemetry === 'function') {
1,775!
120
          req.opentelemetry()?.span?.setAttribute('http.operation', req.operation)
×
121
        }
122
        done()
1,775✔
123
      })
124

125
      fastify.addHook('onSend', (req, _reply, payload, done) => {
302✔
126
        req.executionTime = Math.round(performance.now() - req.startTime)
1,833✔
127
        done(null, payload)
1,833✔
128
      })
129

130
      fastify.addHook('onResponse', (req, reply, done) => {
302✔
131
        if (excludeUrls?.has(req.url)) {
1,884!
UNCOV
132
          done()
×
UNCOV
133
          return
×
134
        }
135

136
        doRequestLog(req, {
1,884✔
137
          reply,
138
          statusCode: reply.statusCode,
139
          responseTime: reply.elapsedTime,
140
          executionTime: req.executionTime,
141
        })
142
        done()
1,884✔
143
      })
144
    },
145
    { name: 'log-request' }
146
  )
147

148
interface LogRequestOptions {
149
  reply?: FastifyReply
150
  statusCode: number | 'ABORTED REQ' | 'ABORTED RES'
151
  responseTime: number
152
  executionTime?: number
153
}
154

155
function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
156
  const requestLog = serializeRequestLog(req)
3,287✔
157
  const replyLog = serializeReplyLog(options.reply)
3,287✔
158
  const rMeth = requestLog.method
3,287✔
159
  const rUrl = requestLog.url
3,287✔
160
  const uAgent = req.headers['user-agent']
3,287✔
161
  const rId = req.id
3,287✔
162
  const cIP = req.ip
3,287✔
163
  const statusCode = options.statusCode
3,287✔
164
  const error = req.raw.executionError || req.executionError
3,287✔
165
  const tenantId = req.tenantId
3,287✔
166
  const traceparent = getValidTraceparent(req.headers)
3,287✔
167
  const traceId = traceparent?.slice(TRACE_ID_START, TRACE_ID_END) ?? ''
3,287✔
168
  const spanId = traceparent?.slice(SPAN_ID_START, SPAN_ID_END) ?? ''
3,287✔
169

170
  let reqMetadata = '{}'
3,287✔
171

172
  if (req.routeOptions.config.logMetadata) {
3,287✔
173
    try {
382✔
174
      const metadata = req.routeOptions.config.logMetadata(req)
382✔
175

176
      if (metadata) {
382!
177
        try {
382✔
178
          reqMetadata = JSON.stringify(metadata)
382✔
179

180
          if (typeof req.opentelemetry === 'function') {
382!
181
            req.opentelemetry()?.span?.setAttribute('http.metadata', reqMetadata)
×
182
          }
183
        } catch (e) {
184
          // do nothing
185
          logSchema.warning(req.log, 'Failed to serialize log metadata', {
×
186
            type: 'otel',
187
            tenantId,
188
            project: tenantId,
189
            reqId: rId,
190
            sbReqId: req.sbReqId,
191
            error: e,
192
          })
193
        }
194
      }
195
    } catch (e) {
196
      logSchema.error(req.log, 'Failed to get log metadata', {
×
197
        type: 'request',
198
        tenantId,
199
        project: tenantId,
200
        reqId: rId,
201
        sbReqId: req.sbReqId,
202
        error: e,
203
      })
204
    }
205
  }
206

207
  const buildLogMessage = `${tenantId} | ${rMeth} | ${statusCode} | ${cIP} | ${rId} | ${rUrl} | ${uAgent}`
3,287✔
208

209
  logSchema.request(req.log, buildLogMessage, {
3,287✔
210
    type: 'request',
211
    tenantId,
212
    project: tenantId,
213
    reqId: rId,
214
    sbReqId: req.sbReqId,
215
    traceId,
216
    spanId,
217
    req: requestLog,
218
    reqMetadata,
219
    res: replyLog,
220
    responseTime: options.responseTime,
221
    executionTime: options.executionTime,
222
    error,
223
    owner: req.owner,
224
    role: req.jwtPayload?.role,
225
    resources: req.resources,
226
    operation: req.operation ?? req.routeOptions.config.operation,
4,462✔
227
    serverTimes: req.serverTimings,
228
  })
229
}
230

231
function getResourceFromParams(params: unknown): string {
232
  if (!params || typeof params !== 'object') {
1,692!
233
    return ''
×
234
  }
235

236
  let resource = ''
1,692✔
237
  let first = true
1,692✔
238

239
  for (const key in params) {
1,692✔
240
    if (!Object.prototype.hasOwnProperty.call(params, key)) {
2,017!
241
      continue
×
242
    }
243

244
    if (!first) {
2,017✔
245
      resource += '/'
565✔
246
    }
247

248
    const value = (params as Record<string, unknown>)[key]
2,017✔
249
    resource += value == null ? '' : String(value)
2,017!
250
    first = false
2,017✔
251
  }
252

253
  return resource
1,692✔
254
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc