Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/endpoint.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { deleteData, fetchData, getData, insertParams, stringifyQuery } from './utils'
import { fetchData, getData, insertParams, stringifyQuery } from './utils'
import type { DeleteEndpoint, GetEndpoint, paths, PostEndpoint, Primitive, PutEndpoint } from './types/api'

function makeUrl(
Expand Down Expand Up @@ -30,6 +30,15 @@ export function putEndpoint<T extends keyof paths>(
return fetchData(url, 'PUT', params?.body, params?.headers)
}

export function deleteEndpoint<T extends keyof paths>(
baseUrl: string,
path: T,
params?: paths[T] extends DeleteEndpoint ? paths[T]['delete']['parameters'] : never,
): Promise<paths[T] extends DeleteEndpoint ? paths[T]['delete']['responses'][200]['schema'] : never> {
const url = makeUrl(baseUrl, path as string, params?.path, params?.query)
return fetchData(url, 'DELETE', params?.body, params?.headers)
}

export function getEndpoint<T extends keyof paths>(
baseUrl: string,
path: T,
Expand All @@ -42,12 +51,3 @@ export function getEndpoint<T extends keyof paths>(
const url = makeUrl(baseUrl, path as string, params?.path, params?.query)
return getData(url, params?.headers)
}

export function deleteEndpoint<T extends keyof paths>(
baseUrl: string,
path: T,
params?: paths[T] extends DeleteEndpoint ? paths[T]['delete']['parameters'] : never,
): Promise<paths[T] extends DeleteEndpoint ? paths[T]['delete']['responses'][200]['schema'] : never> {
const url = makeUrl(baseUrl, path as string, params?.path)
return deleteData(url, params?.headers)
}
39 changes: 16 additions & 23 deletions src/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import type {
ChangeEmailRequestBody,
GetEmailResponse,
RegisterEmailRequestBody,
AuthorizationEmailRequestHeader,
AuthorizationEmailRequestHeaders,
VerifyEmailRequestBody,
} from './emails'

Expand All @@ -43,14 +43,11 @@ export type Primitive = string | number | boolean | null
interface Params {
path?: { [key: string]: Primitive }
headers?: Record<string, string>
}

interface GetParams extends Params {
query?: { [key: string]: Primitive }
}

interface PostParams extends GetParams {
body: string | Record<string, unknown>
interface BodyParams extends Params {
body?: string | Record<string, unknown>
}

interface Responses {
Expand All @@ -64,32 +61,28 @@ interface Endpoint {
} | null
}

interface WriteMethod {
parameters: BodyParams | null
responses: Responses
}

export interface GetEndpoint extends Endpoint {
get: {
parameters: GetParams | null
parameters: Params | null
responses: Responses
}
}

export interface PostEndpoint extends Endpoint {
post: {
parameters: PostParams | null
responses: Responses
}
post: WriteMethod
}

export interface PutEndpoint extends Endpoint {
put: {
parameters: PostParams | null
responses: Responses
}
put: WriteMethod
}

export interface DeleteEndpoint extends Endpoint {
delete: {
parameters: Params | null
responses: Responses
}
delete: WriteMethod
}

interface PathRegistry {
Expand Down Expand Up @@ -896,7 +889,7 @@ export interface operations {
safe_address: string
}
body: RegisterEmailRequestBody
headers: AuthorizationEmailRequestHeader
headers: AuthorizationEmailRequestHeaders
}
responses: {
200: {
Expand All @@ -915,7 +908,7 @@ export interface operations {
signer: string
}
body: ChangeEmailRequestBody
headers: AuthorizationEmailRequestHeader
headers: AuthorizationEmailRequestHeaders
}
responses: {
200: {
Expand All @@ -933,7 +926,7 @@ export interface operations {
safe_address: string
signer: string
}
headers: AuthorizationEmailRequestHeader
headers: AuthorizationEmailRequestHeaders
}
responses: {
200: {
Expand Down Expand Up @@ -986,7 +979,7 @@ export interface operations {
safe_address: string
signer: string
}
headers: AuthorizationEmailRequestHeader
headers: AuthorizationEmailRequestHeaders
}

responses: {
Expand Down
2 changes: 1 addition & 1 deletion src/types/emails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type ChangeEmailRequestBody = {
emailAddress: string
}

export type AuthorizationEmailRequestHeader = {
export type AuthorizationEmailRequestHeaders = {
['Safe-Wallet-Signature']: string
['Safe-Wallet-Signature-Timestamp']: string
}
Expand Down
35 changes: 10 additions & 25 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,47 +58,32 @@ async function parseResponse<T>(resp: Response): Promise<T> {

export async function fetchData<T>(
url: string,
method: 'POST' | 'PUT',
method: 'POST' | 'PUT' | 'DELETE',
body?: unknown,
headers?: Record<string, string>,
): Promise<T> {
let options: RequestInit | undefined

if (body != null) {
const requestHeaders: Record<string, string> = headers ?? {}
requestHeaders['Content-Type'] = 'application/json'
options = {
method: method ?? 'POST',
body: typeof body === 'string' ? body : JSON.stringify(body),
headers: requestHeaders,
}
const requestHeaders: Record<string, string> = {
'Content-Type': 'application/json',
...headers,
}

const resp = await fetch(url, options)

return parseResponse<T>(resp)
}

export async function getData<T>(url: string, headers?: Record<string, string>): Promise<T> {
const options: RequestInit = {
method: 'GET',
method: method ?? 'POST',
headers: requestHeaders,
}

if (headers) {
options['headers'] = {
...headers,
'Content-Type': 'application/json',
}
if (body != null) {
options.body = typeof body === 'string' ? body : JSON.stringify(body)
}

const resp = await fetch(url, options)

return parseResponse<T>(resp)
}

export async function deleteData<T>(url: string, headers?: Record<string, string>): Promise<T> {
export async function getData<T>(url: string, headers?: Record<string, string>): Promise<T> {
const options: RequestInit = {
method: 'DELETE',
method: 'GET',
}

if (headers) {
Expand Down
22 changes: 21 additions & 1 deletion tests/endpoint.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getData, fetchData } from '../src/utils'
import { getEndpoint, postEndpoint, putEndpoint } from '../src/endpoint'
import { deleteEndpoint, getEndpoint, postEndpoint, putEndpoint } from '../src/endpoint'

jest.mock('../src/utils', () => {
const originalModule = jest.requireActual('../src/utils')
Expand Down Expand Up @@ -143,4 +143,24 @@ describe('getEndpoint', () => {
headers,
)
})

it('should send a DELETE request with body', async () => {
const body = {
signature: '0x123',
}

await expect(
deleteEndpoint('https://test.test', '/v1/chains/{chainId}/transactions/{safeTxHash}', {
path: { chainId: '4', safeTxHash: '0x456' },
body,
}),
).resolves.toEqual({ success: true })

expect(fetchData).toHaveBeenCalledWith(
'https://test.test/v1/chains/4/transactions/0x456',
'DELETE',
body,
undefined,
)
})
})
13 changes: 9 additions & 4 deletions tests/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// <reference lib="dom" />
import { fetchData, getData, deleteData, insertParams, stringifyQuery } from '../src/utils'
import { fetchData, getData, insertParams, stringifyQuery } from '../src/utils'

const fetchMock = jest.spyOn(global, 'fetch') as typeof fetch & jest.Mock

Expand Down Expand Up @@ -156,7 +156,7 @@ describe('utils', () => {
})
})

describe('deleteData', () => {
describe('fetchData DELETE', () => {
it('should make a DELETE request', async () => {
fetchMock.mockImplementation(() => {
return Promise.resolve({
Expand All @@ -166,10 +166,13 @@ describe('utils', () => {
})
})

await expect(deleteData('/test/safe')).resolves.toEqual({ success: true })
await expect(fetchData('/test/safe', 'DELETE')).resolves.toEqual({ success: true })

expect(fetch).toHaveBeenCalledWith('/test/safe', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
})
})

Expand All @@ -182,7 +185,9 @@ describe('utils', () => {
})
})

await expect(deleteData('/test/safe', { TestHeader: '123456' })).resolves.toEqual({ success: true })
await expect(fetchData('/test/safe', 'DELETE', undefined, { TestHeader: '123456' })).resolves.toEqual({
success: true,
})

expect(fetch).toHaveBeenCalledWith('/test/safe', {
method: 'DELETE',
Expand Down