Skip to content

Commit

Permalink
fix: renterd-js and core request api
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfreska committed Apr 18, 2024
1 parent 1bb2087 commit c6e0b02
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 90 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-fans-know.md
@@ -0,0 +1,5 @@
---
'@siafoundation/renterd-js': patch
---

Fixed an issue with upload and download content type and encoding. Closes https://github.com/SiaFoundation/web/issues/591
5 changes: 0 additions & 5 deletions libs/renterd-js/README.md
Expand Up @@ -84,11 +84,6 @@ export async function example() {
key: 'path/to/file.txt',
bucket: 'my-bucket',
},
config: {
onDownloadProgress: (progress) => {
console.log(progress.loaded / progress.total)
},
},
})
}
```
1 change: 1 addition & 0 deletions libs/renterd-js/src/autopilot.ts
Expand Up @@ -30,6 +30,7 @@ export function Autopilot({
}) {
const axios = initAxios(api, password)
return {
axios,
state: buildRequestHandler<
AutopilotStateParams,
AutopilotStatePayload,
Expand Down
1 change: 1 addition & 0 deletions libs/renterd-js/src/bus.ts
Expand Up @@ -268,6 +268,7 @@ import { buildRequestHandler, initAxios } from '@siafoundation/request'
export function Bus({ api, password }: { api: string; password?: string }) {
const axios = initAxios(api, password)
return {
axios,
busState: buildRequestHandler<
BusStateParams,
BusStatePayload,
Expand Down
5 changes: 0 additions & 5 deletions libs/renterd-js/src/example.ts
Expand Up @@ -73,10 +73,5 @@ export async function example() {
key: 'path/to/file.txt',
bucket: 'my-bucket',
},
config: {
onDownloadProgress: (progress) => {
console.log(progress.loaded / progress.total)
},
},
})
}
80 changes: 80 additions & 0 deletions libs/renterd-js/src/index.spec.ts
@@ -0,0 +1,80 @@
import MockAdapter from 'axios-mock-adapter'
import { Bus } from './bus'
import { Worker } from './worker'

describe('renterd-js', () => {
const api = 'https://sia.tech/api'
const password = 'password1337'

it('default data and headers support json', async () => {
const bus = Bus({
api,
password,
})
const mock = new MockAdapter(bus.axios)

const bucket = 'newbucket'
const url = `/bus/buckets`
const fullUrl = `${api}${url}`

mock.onPost(fullUrl).reply((config) => {
expect(config.url).toBe(url)
expect(config.method).toBe('post')
expect(config.headers?.['Content-Type']).toMatch(/application\/json/)
expect(config.data).toEqual(
JSON.stringify({
name: bucket,
})
)
return [200, { name: bucket }]
})

const response = await bus.bucketCreate({
data: {
name: bucket,
},
})

expect(response.status).toBe(200)
})

it('object upload should send file as multipart form data', async () => {
const worker = Worker({
api,
password,
})
const mock = new MockAdapter(worker.axios)

const bucket = 'default'
const name = 'test.txt'
const fileKey = `directory/${name}`
const url = `/worker/objects/${fileKey}?bucket=${bucket}`
const fullUrl = `${api}${url}`

mock.onPut(fullUrl).reply((config) => {
expect(config.url).toBe(url)
expect(config.method).toBe('put')
expect(config.headers?.['Content-Type']).toMatch(/multipart\/form-data/)
expect(config.data).toBeInstanceOf(File)
if (config.onUploadProgress) {
config.onUploadProgress({ loaded: 500, total: 1000 })
}
return [200, undefined]
})

const response = await worker.objectUpload({
params: {
key: fileKey,
bucket: bucket,
},
data: new File(['hello world'], name),
config: {
onUploadProgress: (progress) => {
expect(progress.loaded / progress.total).toBe(0.5)
},
},
})

expect(response.status).toBe(200)
})
})
23 changes: 20 additions & 3 deletions libs/renterd-js/src/worker.ts
Expand Up @@ -24,6 +24,7 @@ import { buildRequestHandler, initAxios } from '@siafoundation/request'
export function Worker({ api, password }: { api: string; password?: string }) {
const axios = initAxios(api, password)
return {
axios,
workerState: buildRequestHandler<
WorkerStateParams,
WorkerStatePayload,
Expand All @@ -33,17 +34,33 @@ export function Worker({ api, password }: { api: string; password?: string }) {
ObjectDownloadParams,
ObjectDownloadPayload,
ObjectDownloadResponse
>(axios, 'get', workerObjectsKeyRoute),
>(axios, 'get', workerObjectsKeyRoute, {
config: {
responseType: 'blob',
},
}),
objectUpload: buildRequestHandler<
ObjectUploadParams,
ObjectUploadPayload,
ObjectUploadResponse
>(axios, 'put', workerObjectsKeyRoute),
>(axios, 'put', workerObjectsKeyRoute, {
config: {
headers: {
'Content-Type': 'multipart/form-data',
},
},
}),
multipartUploadPart: buildRequestHandler<
MultipartUploadPartParams,
MultipartUploadPartPayload,
MultipartUploadPartResponse
>(axios, 'put', workerMultipartKeyRoute),
>(axios, 'put', workerMultipartKeyRoute, {
config: {
headers: {
'Content-Type': 'multipart/form-data',
},
},
}),
rhpScan: buildRequestHandler<
RhpScanParams,
RhpScanPayload,
Expand Down
3 changes: 2 additions & 1 deletion libs/request/package.json
Expand Up @@ -4,7 +4,8 @@
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"axios": "^0.27.2"
"axios": "^0.27.2",
"@technically/lodash": "^4.17.0"
},
"types": "./src/index.d.ts"
}
54 changes: 31 additions & 23 deletions libs/request/src/index.ts
@@ -1,6 +1,7 @@
import {
Axios,
import { merge } from '@technically/lodash'
import axios, {
AxiosError,
AxiosInstance,
AxiosRequestConfig,
AxiosRequestHeaders,
AxiosResponse,
Expand Down Expand Up @@ -39,7 +40,15 @@ export function buildRequestHandler<
Params = void,
Data = void,
Response = void
>(axios: Axios, method: Method, route: string, defaultParams?: Params) {
>(
axios: AxiosInstance,
method: Method,
route: string,
options: {
defaultParams?: Params
config?: AxiosRequestConfig<Data>
} = {}
) {
type Args = Params extends void
? Data extends void
? { config?: AxiosRequestConfig<Data> } | void
Expand All @@ -55,26 +64,32 @@ export function buildRequestHandler<
config?: AxiosRequestConfig<Data>
}

type MaybeArgs = {
params?: Params
data?: Data
config?: AxiosRequestConfig<Data>
}

return (args: Args) => {
// args is sometimes undefined
const a = {
// Force remove the void type
const nonVoidArgs: MaybeArgs = {
...args,
} as {
params?: Params
data?: Data
config?: AxiosRequestConfig<Data>
}
const mergedArgs: MaybeArgs = {
...nonVoidArgs,
config: merge(options.config, nonVoidArgs.config),
}
const params = {
...defaultParams,
...a.params,
...options.defaultParams,
...mergedArgs.params,
}

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const paramRoute = parameterizeRoute(route, params as RequestParams)!

const data = 'data' in a ? JSON.stringify(a.data) : undefined
const data = 'data' in mergedArgs ? mergedArgs.data : undefined

return axios[method]<Response>(paramRoute, data, a?.config)
return axios[method]<Response>(paramRoute, data, mergedArgs?.config)
}
}

Expand All @@ -89,28 +104,21 @@ export async function to<T>(
> {
try {
const response = await promise
// Just in case the response is not already JSON
try {
const data = JSON.parse(response.data as string)
return [data, undefined, response]
} catch (e) {
return [response.data, undefined, response]
}
return [response.data, undefined, response]
} catch (error) {
return [undefined, error as AxiosError<T>, undefined]
}
}

export function initAxios(api: string, password?: string): Axios {
export function initAxios(api: string, password?: string): AxiosInstance {
const headers: AxiosRequestHeaders = {
'Content-Type': 'application/json',
}
if (password) {
headers['Authorization'] = `Basic ${btoa(`:${password}`)}`
}
return new Axios({
return axios.create({
baseURL: api,
headers,
responseType: 'json',
})
}
18 changes: 11 additions & 7 deletions libs/sia-central-js/src/index.ts
Expand Up @@ -72,7 +72,9 @@ export function SiaCentral({ api }: { api: string } = { api: defaultApi }) {
SiaCentralExchangeRatesPayload,
SiaCentralExchangeRatesResponse
>(axios, 'get', '/market/exchange-rate', {
currencies: 'sc',
defaultParams: {
currencies: 'sc',
},
}),
host: buildRequestHandler<
SiaCentralHostParams,
Expand All @@ -84,12 +86,14 @@ export function SiaCentral({ api }: { api: string } = { api: defaultApi }) {
SiaCentralHostsPayload,
SiaCentralHostsResponse
>(axios, 'get', '/hosts/list', {
showinactive: false,
sort: 'used_storage',
dir: 'desc',
protocol: 'rhp3',
limit: 10,
page: 1,
defaultParams: {
showinactive: false,
sort: 'used_storage',
dir: 'desc',
protocol: 'rhp3',
limit: 10,
page: 1,
},
}),
hostsNetworkAverages: buildRequestHandler<
SiaCentralHostsNetworkAveragesParams,
Expand Down

0 comments on commit c6e0b02

Please sign in to comment.