Skip to content

Commit

Permalink
refactor(core): improve HTTP API, closes #1098 (#1237)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Feb 16, 2021
1 parent e34ee4c commit a7bc472
Show file tree
Hide file tree
Showing 16 changed files with 1,540 additions and 487 deletions.
7 changes: 7 additions & 0 deletions .changes/http-api-refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"api": minor
"tauri-api": minor
"tauri": minor
---

The HTTP API was improved with client caching and better payload and response types.
305 changes: 188 additions & 117 deletions api/src/http.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
import { promisified } from './tauri'
import { invoke, promisified } from './tauri'

export interface ClientOptions {
maxRedirections: boolean
connectTimeout: number
}

export enum ResponseType {
JSON = 1,
Text = 2,
Binary = 3
}

export enum BodyType {
Form = 1,
File = 2,
Auto = 3
}
export type Part = 'string' | number[]

export class Body {
type: string
payload: unknown

export type Body = object | string | BinaryType
constructor(type: string, payload: unknown) {
this.type = type
this.payload = payload
}

static form(data: Record<string, Part>): Body {
return new Body('Form', data)
}

static json(data: Record<any, any>): Body {
return new Body('Json', data)
}

static text(value: string): Body {
return new Body('Text', value)
}

static bytes(bytes: number[]): Body {
return new Body('Bytes', bytes)
}
}

export type HttpVerb =
| 'GET'
Expand All @@ -29,130 +54,176 @@ export interface HttpOptions {
method: HttpVerb
url: string
headers?: Record<string, any>
params?: Record<string, any>
query?: Record<string, any>
body?: Body
followRedirects: boolean
maxRedirections: boolean
connectTimeout: number
readTimeout: number
timeout: number
allowCompression: boolean
timeout?: number
responseType?: ResponseType
bodyType: BodyType
}

export type PartialOptions = Omit<HttpOptions, 'method' | 'url'>

/**
* makes a HTTP request
*
* @param options request options
*
* @return promise resolving to the response
*/
async function request<T>(options: HttpOptions): Promise<T> {
return await promisified({
module: 'Http',
message: {
cmd: 'httpRequest',
options: options
}
})
}
export type RequestOptions = Omit<HttpOptions, 'method' | 'url'>
export type FetchOptions = Omit<HttpOptions, 'url'>

/**
* makes a GET request
*
* @param url request URL
* @param options request options
*
* @return promise resolving to the response
*/
async function get<T>(url: string, options: PartialOptions): Promise<T> {
return await request({
method: 'GET',
url,
...options
})
export interface Response<T> {
url: string
status: number
headers: Record<string, string>
data: T
}

/**
* makes a POST request
*
* @param url request URL
* @param body request body
* @param options request options
*
* @return promise resolving to the response
*/
async function post<T>(
url: string,
body: Body,
options: PartialOptions
): Promise<T> {
return await request({
method: 'POST',
url,
body,
...options
})
}
export class Client {
id: number
constructor(id: number) {
this.id = id
}

/**
* makes a PUT request
*
* @param url request URL
* @param body request body
* @param options request options
*
* @return promise resolving to the response
*/
async function put<T>(
url: string,
body: Body,
options: PartialOptions
): Promise<T> {
return await request({
method: 'PUT',
url,
body,
...options
})
/**
* drops the client instance
*/
drop(): void {
invoke({
module: 'Http',
message: {
cmd: 'dropClient',
client: this.id
}
})
}

/**
* makes a HTTP request
*
* @param options request options
*
* @return promise resolving to the response
*/
async request<T>(options: HttpOptions): Promise<Response<T>> {
return await promisified({
module: 'Http',
message: {
cmd: 'httpRequest',
client: this.id,
options
}
})
}

/**
* makes a GET request
*
* @param url request URL
* @param options request options
*
* @return promise resolving to the response
*/
async get<T>(url: string, options: RequestOptions): Promise<Response<T>> {
return await this.request({
method: 'GET',
url,
...options
})
}

/**
* makes a POST request
*
* @param url request URL
* @param body request body
* @param options request options
*
* @return promise resolving to the response
*/
async post<T>(
url: string,
body: Body,
options: RequestOptions
): Promise<Response<T>> {
return await this.request({
method: 'POST',
url,
body,
...options
})
}

/**
* makes a PUT request
*
* @param url request URL
* @param body request body
* @param options request options
*
* @return promise resolving to the response
*/
async put<T>(
url: string,
body: Body,
options: RequestOptions
): Promise<Response<T>> {
return await this.request({
method: 'PUT',
url,
body,
...options
})
}

/**
* makes a PATCH request
*
* @param url request URL
* @param options request options
*
* @return promise resolving to the response
*/
async patch<T>(url: string, options: RequestOptions): Promise<Response<T>> {
return await this.request({
method: 'PATCH',
url,
...options
})
}

/**
* makes a DELETE request
*
* @param url request URL
* @param options request options
*
* @return promise resolving to the response
*/
async delete<T>(
url: string,
options: RequestOptions
): Promise<Response<T>> {
return await this.request({
method: 'DELETE',
url,
...options
})
}
}

/**
* makes a PATCH request
*
* @param url request URL
* @param options request options
*
* @return promise resolving to the response
*/
async function patch<T>(url: string, options: PartialOptions): Promise<T> {
return await request({
method: 'PATCH',
url,
...options
})
async function getClient(options?: ClientOptions): Promise<Client> {
return await promisified<number>({
module: 'Http',
message: {
cmd: 'createClient',
options
}
}).then(id => new Client(id))
}

/**
* makes a DELETE request
*
* @param url request URL
* @param options request options
*
* @return promise resolving to the response
*/
async function deleteRequest<T>(
url: string,
options: PartialOptions
): Promise<T> {
return await request({
method: 'DELETE',
let defaultClient: Client | null = null

async function fetch<T>(url: string, options?: FetchOptions): Promise<Response<T>> {
if (defaultClient === null) {
defaultClient = await getClient()
}
return await defaultClient.request({
url,
method: options?.method ?? 'GET',
...options
})
}

export { request, get, post, put, patch, deleteRequest as httpDelete }
export { getClient, fetch }
3 changes: 2 additions & 1 deletion tauri-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ thiserror = "1.0.23"
rand = "0.8"
nfd = "0.0.4"
tauri-dialog = "0.1.0"
attohttpc = { version = "0.16.1", features = [ "json", "form" ] }
reqwest = { version = "0.11", features = [ "json", "multipart" ] }
bytes = { version = "1", features = ["serde"] }
http = "0.2"
tauri-utils = { version = "0.5", path = "../tauri-utils" }
clap = { version = "=3.0.0-beta.2", optional = true }
Expand Down
Loading

0 comments on commit a7bc472

Please sign in to comment.