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

supabase / functions-js / 7262784040

19 Dec 2023 01:53PM UTC coverage: 91.339% (-0.4%) from 91.739%
7262784040

Pull #75

github

web-flow
Merge eaac10d88 into 1470363d7
Pull Request #75: Feat add regional options

26 of 34 branches covered (0.0%)

Branch coverage included in aggregate %.

22 of 24 new or added lines in 2 files covered. (91.67%)

9 existing lines in 1 file now uncovered.

206 of 220 relevant lines covered (93.64%)

6.27 hits per line

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

88.59
/src/FunctionsClient.ts
1
import { resolveFetch } from './helper'
1✔
2
import {
1✔
3
  Fetch,
1✔
4
  FunctionsFetchError,
1✔
5
  FunctionsHttpError,
1✔
6
  FunctionsRelayError,
1✔
7
  FunctionsResponse,
1✔
8
  FunctionInvokeOptions,
1✔
9
} from './types'
1✔
10

1✔
11
export class FunctionsClient {
1✔
12
  protected url: string
1✔
13
  protected headers: Record<string, string>
1✔
14
  protected region: string
1✔
15
  protected fetch: Fetch
1✔
16

1✔
17
  constructor(
1✔
18
    url: string,
20✔
19
    {
20✔
20
      headers = {},
20✔
21
      customFetch,
20✔
22
    }: {
20✔
23
      headers?: Record<string, string>
20✔
24
      customFetch?: Fetch
20✔
25
    } = {}
20✔
26
  ) {
20✔
27
    this.url = url
20✔
28
    this.headers = headers
20✔
29
    this.region = 'any'
20✔
30
    this.fetch = resolveFetch(customFetch)
20✔
31
  }
20✔
32

1✔
33
  /**
1✔
34
   * Updates the authorization header
1✔
35
   * @param token - the new jwt token sent in the authorisation header
1✔
36
   */
1✔
37
  setAuth(token: string) {
1✔
38
    this.headers.Authorization = `Bearer ${token}`
10✔
39
  }
10✔
40

1✔
41
  /**
1✔
42
   * Invokes a function
1✔
43
   * @param functionName - The name of the Function to invoke.
1✔
44
   * @param options - Options for invoking the Function.
1✔
45
   */
1✔
46
  async invoke<T = any>(
1✔
47
    functionName: string,
20✔
48
    options: FunctionInvokeOptions = {}
20✔
49
  ): Promise<FunctionsResponse<T>> {
20✔
50
    try {
20✔
51
      const { headers, method, body: functionArgs } = options
20✔
52
      let _headers: Record<string, string> = {}
20✔
53
      if (this.region !== 'any') {
20!
NEW
UNCOV
54
        _headers['x-region'] = this.region
×
NEW
UNCOV
55
      }
×
56
      let body: any
20✔
57
      if (
20✔
58
        functionArgs &&
20✔
59
        ((headers && !Object.prototype.hasOwnProperty.call(headers, 'Content-Type')) || !headers)
4!
60
      ) {
20✔
61
        if (
4✔
62
          (typeof Blob !== 'undefined' && functionArgs instanceof Blob) ||
4✔
63
          functionArgs instanceof ArrayBuffer
4✔
64
        ) {
4✔
65
          // will work for File as File inherits Blob
2✔
66
          // also works for ArrayBuffer as it is the same underlying structure as a Blob
2✔
67
          _headers['Content-Type'] = 'application/octet-stream'
2✔
68
          body = functionArgs
2✔
69
        } else if (typeof functionArgs === 'string') {
2✔
70
          // plain string
1✔
71
          _headers['Content-Type'] = 'text/plain'
1✔
72
          body = functionArgs
1✔
73
        } else if (typeof FormData !== 'undefined' && functionArgs instanceof FormData) {
1✔
74
          // don't set content-type headers
1✔
75
          // Request will automatically add the right boundary value
1✔
76
          body = functionArgs
1✔
77
        } else {
1!
UNCOV
78
          // default, assume this is JSON
×
UNCOV
79
          _headers['Content-Type'] = 'application/json'
×
UNCOV
80
          body = JSON.stringify(functionArgs)
×
UNCOV
81
        }
×
82
      }
4✔
83

20✔
84
      const response = await this.fetch(`${this.url}/${functionName}`, {
20✔
85
        method: method || 'POST',
20✔
86
        // headers priority is (high to low):
20✔
87
        // 1. invoke-level headers
20✔
88
        // 2. client-level headers
20✔
89
        // 3. default Content-Type header
20✔
90
        headers: { ..._headers, ...this.headers, ...headers },
20✔
91
        body,
20✔
92
      }).catch((fetchError) => {
20✔
93
        throw new FunctionsFetchError(fetchError)
1✔
94
      })
20✔
95

19✔
96
      const isRelayError = response.headers.get('x-relay-error')
19✔
97
      if (isRelayError && isRelayError === 'true') {
20✔
98
        throw new FunctionsRelayError(response)
3✔
99
      }
3✔
100

16✔
101
      if (!response.ok) {
19!
UNCOV
102
        throw new FunctionsHttpError(response)
×
UNCOV
103
      }
×
104

16✔
105
      let responseType = (response.headers.get('Content-Type') ?? 'text/plain').split(';')[0].trim()
20!
106
      let data: any
20✔
107
      if (responseType === 'application/json') {
20✔
108
        data = await response.json()
8✔
109
      } else if (responseType === 'application/octet-stream') {
8!
110
        data = await response.blob()
×
111
      } else if (responseType === 'multipart/form-data') {
8!
UNCOV
112
        data = await response.formData()
×
113
      } else {
8✔
114
        // default to text
8✔
115
        data = await response.text()
8✔
116
      }
8✔
117

16✔
118
      return { data, error: null }
16✔
119
    } catch (error) {
20✔
120
      return { data: null, error }
4✔
121
    }
4✔
122
  }
20✔
123
}
1✔
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