Skip to content

Commit c6c85b0

Browse files
committed
fix: add functions test
1 parent 32b98f1 commit c6c85b0

File tree

5 files changed

+71
-38
lines changed

5 files changed

+71
-38
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sqlitecloud/drivers",
3-
"version": "1.0.360",
3+
"version": "1.0.361",
44
"description": "SQLiteCloud drivers for Typescript/Javascript in edge, web and node clients",
55
"main": "./lib/index.js",
66
"types": "./lib/index.d.ts",

src/packages/_functions/FunctionsClient.ts

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import { Fetch, resolveFetch } from '../utils/fetch'
1010
* @param headers - The headers to pass to the function.
1111
*/
1212
interface FunctionInvokeOptions {
13-
args: any[]
13+
params: Record<string, any>
1414
headers?: Record<string, string>
15+
apiKey?: string
1516
}
1617

1718
/**
@@ -27,13 +28,15 @@ export class FunctionsClient {
2728
constructor(
2829
connectionString: string,
2930
options: {
30-
customFetch?: Fetch,
31+
fetch?: Fetch,
3132
headers?: Record<string, string>
32-
} = {}
33+
} = {
34+
headers: {}
35+
}
3336
) {
3437
this.url = getAPIUrl(connectionString, FUNCTIONS_ROOT_PATH)
35-
this.fetch = resolveFetch(options.customFetch)
36-
this.headers = options.headers ? { ...DEFAULT_HEADERS, ...options.headers } : { ...DEFAULT_HEADERS }
38+
this.fetch = resolveFetch(options.fetch)
39+
this.headers = { ...DEFAULT_HEADERS, ...options.headers }
3740
}
3841
// TODO: check authorization and api key setup in Gateway
3942
setAuth(token: string) {
@@ -43,56 +46,42 @@ export class FunctionsClient {
4346
async invoke(functionId: string, options: FunctionInvokeOptions) {
4447
let body;
4548
let _headers: Record<string, string> = {}
46-
if (options.args &&
49+
if (options.params &&
4750
((options.headers && !Object.prototype.hasOwnProperty.call(options.headers, 'Content-Type')) || !options.headers)
4851
) {
4952
if (
50-
(typeof Blob !== 'undefined' && options.args instanceof Blob) ||
51-
options.args instanceof ArrayBuffer
53+
(typeof Blob !== 'undefined' && options.params instanceof Blob) ||
54+
options.params instanceof ArrayBuffer
5255
) {
5356
// will work for File as File inherits Blob
5457
// also works for ArrayBuffer as it is the same underlying structure as a Blob
5558
_headers['Content-Type'] = 'application/octet-stream'
56-
body = options.args
57-
} else if (typeof options.args === 'string') {
59+
body = options.params
60+
} else if (typeof options.params === 'string') {
5861
// plain string
5962
_headers['Content-Type'] = 'text/plain'
60-
body = options.args
61-
} else if (typeof FormData !== 'undefined' && options.args instanceof FormData) {
63+
body = options.params
64+
} else if (typeof FormData !== 'undefined' && options.params instanceof FormData) {
6265
_headers['Content-Type'] = 'multipart/form-data'
63-
body = options.args
66+
body = options.params
6467
} else {
6568
// default, assume this is JSON
6669
_headers['Content-Type'] = 'application/json'
67-
body = JSON.stringify(options.args)
70+
body = JSON.stringify(options.params)
6871
}
6972
}
7073

7174
try {
7275
const response = await this.fetch(`${this.url}/${functionId}`, {
7376
method: 'POST',
74-
body: JSON.stringify(options.args),
77+
body,
7578
headers: { ..._headers, ...this.headers, ...options.headers }
7679
})
7780

7881
if (!response.ok) {
7982
throw new SQLiteCloudError(`Failed to invoke function: ${response.statusText}`)
8083
}
81-
82-
let responseType = (response.headers.get('Content-Type') ?? 'text/plain').split(';')[0].trim()
83-
let data: any
84-
if (responseType === 'application/json') {
85-
data = await response.json()
86-
} else if (responseType === 'application/octet-stream') {
87-
data = await response.blob()
88-
} else if (responseType === 'text/event-stream') {
89-
data = response
90-
} else if (responseType === 'multipart/form-data') {
91-
data = await response.formData()
92-
} else {
93-
data = await response.text()
94-
}
95-
return { ...data, error: null }
84+
return { error: null, ...(await response.json()) }
9685
} catch (error) {
9786
return { data: null, error }
9887
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
// Test functions client
3+
// invoke
4+
5+
import { CHINOOK_API_KEY, CHINOOK_DATABASE_URL } from "../../../test/shared"
6+
import { FunctionsClient } from "../_functions/FunctionsClient"
7+
8+
const TEST_SQL_FUNCTION_ID = 'test-1-sql'
9+
const TEST_JS_FUNCTION_ID = 'test-1-js'
10+
11+
const TEST_FUNCTION_ARG = {
12+
filter: 'a',
13+
limit: 10
14+
}
15+
16+
const functions = new FunctionsClient(CHINOOK_DATABASE_URL)
17+
18+
describe('FunctionsClient', () => {
19+
it('should invoke a JS function', async () => {
20+
21+
const { data, error } = await functions.invoke(TEST_JS_FUNCTION_ID, {
22+
params: TEST_FUNCTION_ARG,
23+
headers: {
24+
'Authorization': `Bearer ${CHINOOK_API_KEY}`
25+
}
26+
})
27+
expect(data.message).toBeDefined()
28+
expect(data.result).toBeDefined()
29+
expect(error).toBeNull()
30+
})
31+
32+
it('should invoke a SQL function', async () => {
33+
const { data, error } = await functions.invoke(TEST_SQL_FUNCTION_ID, {
34+
params: TEST_FUNCTION_ARG,
35+
headers: {
36+
'Authorization': `Bearer ${CHINOOK_API_KEY}`
37+
}
38+
})
39+
expect(data).toBeDefined()
40+
expect(data.length > 0).toBeTruthy()
41+
expect(error).toBeNull()
42+
})
43+
})
44+

src/packages/utils/fetch.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ export const resolveFetch = (customFetch?: Fetch): Fetch => {
77
if (customFetch) {
88
_fetch = customFetch
99
} else if (typeof fetch !== 'undefined') {
10-
_fetch = nodeFetch as unknown as Fetch
11-
} else {
1210
_fetch = fetch
11+
} else {
12+
_fetch = nodeFetch as unknown as Fetch
1313
}
14-
return (...args: Parameters<Fetch>) => _fetch(...args)
14+
return _fetch
1515
}
1616

1717
export const resolveHeadersConstructor = () => {

src/packages/weblite/WebliteClient.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class WebliteClient {
2121
) {
2222
this.baseUrl = getAPIUrl(connectionString, 'weblite')
2323
this.fetch = options?.fetch || fetchWithAuth(connectionString)
24-
this.headers = { ...options.headers }
24+
this.headers = { ...DEFAULT_HEADERS, ...options.headers }
2525
this._defaultDatabase = getDefaultDatabase(connectionString)
2626
}
2727

@@ -120,7 +120,7 @@ export class WebliteClient {
120120
const filenamePath = encodeURIComponent(filename)
121121
const url = `${this.baseUrl}/${filenamePath}`
122122
try {
123-
const response = await this.fetch(url, { method: 'GET', headers: { ...this.headers } })
123+
const response = await this.fetch(url, { method: 'GET', headers: this.headers })
124124
if (!response.ok) {
125125
throw new SQLiteCloudError(`Failed to download database: ${response.statusText}`)
126126
}
@@ -141,7 +141,7 @@ export class WebliteClient {
141141
url,
142142
{
143143
method: 'DELETE',
144-
headers: { ...this.headers }
144+
headers: this.headers
145145
}
146146
)
147147
if (!response.ok) {
@@ -156,7 +156,7 @@ export class WebliteClient {
156156
async listDatabases() {
157157
const url = `${this.baseUrl}/databases`
158158
try {
159-
const response = await this.fetch(url, { method: 'GET', headers: { ...this.headers } })
159+
const response = await this.fetch(url, { method: 'GET', headers: this.headers })
160160
if (!response.ok) {
161161
throw new SQLiteCloudError(`Failed to list databases: ${response.statusText}`)
162162
}

0 commit comments

Comments
 (0)