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

supabase / storage / 24447693422

15 Apr 2026 09:45AM UTC coverage: 82.475% (-0.001%) from 82.476%
24447693422

Pull #1020

github

web-flow
Merge 4dc4b685d into c86312671
Pull Request #1020: fix: cap expires in and return bad request

3685 of 7463 branches covered (49.38%)

Branch coverage included in aggregate %.

15 of 32 new or added lines in 3 files covered. (46.88%)

2791 existing lines in 97 files now uncovered.

30504 of 33991 relevant lines covered (89.74%)

348.96 hits per line

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

87.5
/src/http/plugins/log-request.ts
1
import { logger, logSchema, redactQueryParamFromRequest } from '@internal/monitoring'
1✔
2
import { FastifyReply } from 'fastify/types/reply'
1✔
3
import { FastifyRequest } from 'fastify/types/request'
1✔
4
import fastifyPlugin from 'fastify-plugin'
1✔
5

1✔
6
interface RequestLoggerOptions {
1✔
7
  excludeUrls?: string[]
1✔
8
}
1✔
9

1✔
10
declare module 'fastify' {
1✔
11
  interface FastifyRequest {
1✔
12
    executionError?: Error
1✔
13
    operation?: { type: string }
1✔
14
    resources?: string[]
1✔
15
    startTime: number
1✔
16
    executionTime?: number
1✔
17
  }
1✔
18

1✔
19
  interface FastifyContextConfig {
1✔
20
    operation?: { type: string }
1✔
21
    resources?: (req: FastifyRequest<any>) => string[]
1✔
22
    logMetadata?: (req: FastifyRequest<any>) => Record<string, unknown>
1✔
23
  }
1✔
24
}
1✔
25

1✔
26
/**
1✔
27
 * Request logger plugin
1✔
28
 * @param options
1✔
29
 */
1✔
30
export const logRequest = (options: RequestLoggerOptions) =>
3✔
31
  fastifyPlugin(
276✔
32
    async (fastify) => {
276✔
33
      fastify.addHook('onRequest', async (req, res) => {
270✔
34
        req.startTime = Date.now()
1,521✔
35

1,519✔
36
        res.raw.once('close', () => {
1,521✔
37
          if (req.raw.aborted) {
1,521!
38
            doRequestLog(req, {
×
39
              excludeUrls: options.excludeUrls,
40
              statusCode: 'ABORTED REQ',
41
              responseTime: (Date.now() - req.startTime) / 1000,
42
            })
43
            return
×
44
          }
45

1,519✔
46
          if (!res.raw.writableFinished) {
1,521!
47
            doRequestLog(req, {
1,174✔
48
              excludeUrls: options.excludeUrls,
1,172✔
49
              statusCode: 'ABORTED RES',
1,172✔
50
              responseTime: (Date.now() - req.startTime) / 1000,
1,172✔
51
            })
1,172✔
52
          }
1,172✔
53
        })
1,519✔
54
      })
268✔
55

268✔
56
      /**
268✔
57
       * Adds req.resources and req.operation to the request object
268✔
58
       */
268✔
59
      fastify.addHook('preHandler', async (req) => {
270✔
60
        let resources = req.resources
1,448✔
61

1,446✔
62
        if (resources === undefined) {
1,448!
63
          resources = req.routeOptions.config.resources?.(req)
1,448✔
64
        }
1,446✔
65

1,446✔
66
        if (resources === undefined) {
1,448✔
67
          resources = (req.raw as any).resources
1,383✔
68
        }
1,382✔
69

1,446✔
70
        if (resources === undefined) {
1,448✔
71
          const params = req.params as Record<string, unknown> | undefined
1,383✔
72
          let resourceFromParams = ''
1,383✔
73

1,382✔
74
          if (params) {
1,383!
75
            let first = true
1,383✔
76
            for (const key in params) {
1,383✔
77
              if (!Object.prototype.hasOwnProperty.call(params, key)) {
1,689!
78
                continue
×
79
              }
80

1,687✔
81
              if (!first) {
1,689✔
82
                resourceFromParams += '/'
483✔
83
              }
482✔
84

1,687✔
85
              const value = params[key]
1,689✔
86
              resourceFromParams += value == null ? '' : String(value)
1,689!
87
              first = false
1,689✔
88
            }
1,687✔
89
          }
1,382✔
90

1,382✔
91
          resources = resourceFromParams ? [resourceFromParams] : []
1,383!
92
        }
1,382✔
93

1,446✔
94
        if (resources && resources.length > 0) {
1,448!
95
          for (let index = 0; index < resources.length; index++) {
1,271✔
96
            const resource = resources[index]
41,306✔
97
            if (!resource.startsWith('/')) {
41,306✔
98
              resources[index] = `/${resource}`
41,305✔
99
            }
41,303✔
100
          }
41,303✔
101
        }
1,269✔
102

1,446✔
103
        req.resources = resources
1,448✔
104
        req.operation = req.routeOptions.config.operation
1,448✔
105

1,446✔
106
        if (req.operation && typeof req.opentelemetry === 'function') {
1,448!
107
          req.opentelemetry()?.span?.setAttribute('http.operation', req.operation.type)
×
108
        }
109
      })
268✔
110

268✔
111
      fastify.addHook('onSend', async (req, _, payload) => {
270✔
112
        req.executionTime = Date.now() - req.startTime
1,501✔
113
        return payload
1,501✔
114
      })
268✔
115

268✔
116
      fastify.addHook('onResponse', async (req, reply) => {
270✔
117
        doRequestLog(req, {
1,521✔
118
          reply,
1,519✔
119
          excludeUrls: options.excludeUrls,
1,519✔
120
          statusCode: reply.statusCode,
1,519✔
121
          responseTime: reply.elapsedTime,
1,519✔
122
          executionTime: req.executionTime,
1,519✔
123
        })
1,519✔
124
      })
268✔
125
    },
268✔
126
    { name: 'log-request' }
276✔
127
  )
1✔
128

1✔
129
interface LogRequestOptions {
1✔
130
  reply?: FastifyReply
1✔
131
  excludeUrls?: string[]
1✔
132
  statusCode: number | 'ABORTED REQ' | 'ABORTED RES'
1✔
133
  responseTime: number
1✔
134
  executionTime?: number
1✔
135
}
1✔
136

1✔
137
function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
2,691✔
138
  if (options.excludeUrls?.includes(req.url)) {
2,695!
139
    return
×
140
  }
141

2,691✔
142
  const rMeth = req.method
2,695✔
143
  const rUrl = redactQueryParamFromRequest(req, [
2,695✔
144
    'token',
2,691✔
145
    'X-Amz-Credential',
2,691✔
146
    'X-Amz-Signature',
2,691✔
147
    'X-Amz-Security-Token',
2,691✔
148
  ])
2,691✔
149
  const uAgent = req.headers['user-agent']
2,695✔
150
  const rId = req.id
2,695✔
151
  const cIP = req.ip
2,695✔
152
  const statusCode = options.statusCode
2,695✔
153
  const error = (req.raw as any).executionError || req.executionError
2,695✔
154
  const tenantId = req.tenantId
2,695✔
155

2,691✔
156
  let reqMetadata: Record<string, unknown> = {}
2,695✔
157

2,691✔
158
  if (req.routeOptions.config.logMetadata) {
2,695!
UNCOV
159
    try {
380✔
UNCOV
160
      reqMetadata = req.routeOptions.config.logMetadata(req)
380✔
161

380✔
UNCOV
162
      if (reqMetadata) {
380!
UNCOV
163
        try {
380✔
UNCOV
164
          if (typeof req.opentelemetry === 'function') {
380!
165
            req.opentelemetry()?.span?.setAttribute('http.metadata', JSON.stringify(reqMetadata))
×
166
          }
167
        } catch (e) {
380!
168
          // do nothing
169
          logSchema.warning(logger, 'Failed to serialize log metadata', {
×
170
            type: 'otel',
171
            error: e,
172
          })
173
        }
174
      }
380✔
175
    } catch (e) {
380!
176
      logSchema.error(logger, 'Failed to get log metadata', {
×
177
        type: 'request',
178
        error: e,
179
      })
180
    }
181
  }
380✔
182

2,691✔
183
  const buildLogMessage = `${tenantId} | ${rMeth} | ${statusCode} | ${cIP} | ${rId} | ${rUrl} | ${uAgent}`
2,695✔
184

2,691✔
185
  logSchema.request(req.log, buildLogMessage, {
2,695✔
186
    type: 'request',
2,691✔
187
    req,
2,691✔
188
    reqMetadata,
2,691✔
189
    res: options.reply,
2,691✔
190
    responseTime: options.responseTime,
2,691✔
191
    executionTime: options.executionTime,
2,691✔
192
    error,
2,691✔
193
    owner: req.owner,
2,691✔
194
    role: req.jwtPayload?.role,
2,691✔
195
    resources: req.resources,
2,691✔
196
    operation: req.operation?.type ?? req.routeOptions.config.operation?.type,
2,691✔
197
    serverTimes: req.serverTimings,
2,691✔
198
  })
2,691✔
199
}
2,691✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc