Skip to content

Commit 2a5ff8f

Browse files
authored
move HttpClient into Api (#313)
1 parent 3327a39 commit 2a5ff8f

File tree

4 files changed

+103
-104
lines changed

4 files changed

+103
-104
lines changed

oxide-api/src/Api.ts

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@
88

99
/* eslint-disable */
1010

11-
import type { FetchParams } from "./http-client";
12-
import { HttpClient, toQueryString } from "./http-client";
13-
14-
export type {
15-
ApiConfig,
16-
ApiResult,
17-
ErrorBody,
18-
ErrorResult,
11+
import type { FetchParams, FullParams, ApiResult } from "./http-client";
12+
import {
13+
dateReplacer,
14+
handleResponse,
15+
mergeParams,
16+
toQueryString,
1917
} from "./http-client";
18+
import { snakeify } from "./util";
19+
20+
export type { ApiResult, ErrorBody, ErrorResult } from "./http-client";
2021

2122
/**
2223
* An IPv4 subnet
@@ -6668,7 +6669,47 @@ export interface WebhookSecretsDeletePathParams {
66686669
}
66696670

66706671
type EmptyObj = Record<string, never>;
6671-
export class Api extends HttpClient {
6672+
export interface ApiConfig {
6673+
/**
6674+
* No host means requests will be sent to the current host. This is used in
6675+
* the web console.
6676+
*/
6677+
host?: string;
6678+
token?: string;
6679+
baseParams?: FetchParams;
6680+
}
6681+
6682+
export class Api {
6683+
host: string;
6684+
token?: string;
6685+
baseParams: FetchParams;
6686+
6687+
constructor({ host = "", baseParams = {}, token }: ApiConfig = {}) {
6688+
this.host = host;
6689+
this.token = token;
6690+
6691+
const headers = new Headers({ "Content-Type": "application/json" });
6692+
if (token) {
6693+
headers.append("Authorization", `Bearer ${token}`);
6694+
}
6695+
this.baseParams = mergeParams({ headers }, baseParams);
6696+
}
6697+
6698+
public async request<Data>({
6699+
body,
6700+
path,
6701+
query,
6702+
host,
6703+
...fetchParams
6704+
}: FullParams): Promise<ApiResult<Data>> {
6705+
const url = (host || this.host) + path + toQueryString(query);
6706+
const init = {
6707+
...mergeParams(this.baseParams, fetchParams),
6708+
body: JSON.stringify(snakeify(body), dateReplacer),
6709+
};
6710+
return handleResponse(await fetch(url, init));
6711+
}
6712+
66726713
methods = {
66736714
/**
66746715
* Start an OAuth 2.0 Device Authorization Grant

oxide-api/src/http-client.ts

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Copyright Oxide Computer Company
77
*/
88

9-
import { camelToSnake, processResponseBody, snakeify, isNotNull } from "./util";
9+
import { camelToSnake, processResponseBody, isNotNull } from "./util";
1010

1111
/** Success responses from the API */
1212
export type ApiSuccess<Data> = {
@@ -47,7 +47,7 @@ export type ApiResult<Data> = ApiSuccess<Data> | ErrorResult;
4747
* body and query params.
4848
*/
4949
// eslint-disable-next-line @typescript-eslint/no-explicit-any
50-
function replacer(_key: string, value: any) {
50+
export function dateReplacer(_key: string, value: any) {
5151
if (value instanceof Date) {
5252
return value.toISOString();
5353
}
@@ -56,7 +56,7 @@ function replacer(_key: string, value: any) {
5656

5757
function encodeQueryParam(key: string, value: unknown) {
5858
return `${encodeURIComponent(camelToSnake(key))}=${encodeURIComponent(
59-
replacer(key, value),
59+
dateReplacer(key, value),
6060
)}`;
6161
}
6262

@@ -117,48 +117,6 @@ export interface FullParams extends FetchParams {
117117
method?: string;
118118
}
119119

120-
export interface ApiConfig {
121-
/**
122-
* No host means requests will be sent to the current host. This is used in
123-
* the web console.
124-
*/
125-
host?: string;
126-
token?: string;
127-
baseParams?: FetchParams;
128-
}
129-
130-
export class HttpClient {
131-
host: string;
132-
token?: string;
133-
baseParams: FetchParams;
134-
135-
constructor({ host = "", baseParams = {}, token }: ApiConfig = {}) {
136-
this.host = host;
137-
this.token = token;
138-
139-
const headers = new Headers({ "Content-Type": "application/json" });
140-
if (token) {
141-
headers.append("Authorization", `Bearer ${token}`);
142-
}
143-
this.baseParams = mergeParams({ headers }, baseParams);
144-
}
145-
146-
public async request<Data>({
147-
body,
148-
path,
149-
query,
150-
host,
151-
...fetchParams
152-
}: FullParams): Promise<ApiResult<Data>> {
153-
const url = (host || this.host) + path + toQueryString(query);
154-
const init = {
155-
...mergeParams(this.baseParams, fetchParams),
156-
body: JSON.stringify(snakeify(body), replacer),
157-
};
158-
return handleResponse(await fetch(url, init));
159-
}
160-
}
161-
162120
export function mergeParams(a: FetchParams, b: FetchParams): FetchParams {
163121
// calling `new Headers()` normalizes `HeadersInit`, which could be a Headers
164122
// object, a plain object, or an array of tuples

oxide-openapi-gen-ts/src/client/api.ts

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,11 @@ export function generateApi(spec: OpenAPIV3.Document, destDir: string) {
126126

127127
w(`/* eslint-disable */
128128
129-
import type { FetchParams } from './http-client'
130-
import { HttpClient, toQueryString } from './http-client'
129+
import type { FetchParams, FullParams, ApiResult } from "./http-client";
130+
import { dateReplacer, handleResponse, mergeParams, toQueryString } from './http-client'
131+
import { snakeify } from './util'
131132
132-
export type { ApiConfig, ApiResult, ErrorBody, ErrorResult, } from './http-client'
133+
export type { ApiResult, ErrorBody, ErrorResult } from './http-client'
133134
`);
134135

135136
const schemaNames = getSortedSchemas(spec);
@@ -169,8 +170,49 @@ export function generateApi(spec: OpenAPIV3.Document, destDir: string) {
169170

170171
w("type EmptyObj = Record<string, never>;");
171172

172-
w(`export class Api extends HttpClient {
173-
methods = {`);
173+
w(`export interface ApiConfig {
174+
/**
175+
* No host means requests will be sent to the current host. This is used in
176+
* the web console.
177+
*/
178+
host?: string;
179+
token?: string;
180+
baseParams?: FetchParams;
181+
}
182+
183+
export class Api {
184+
host: string;
185+
token?: string;
186+
baseParams: FetchParams;
187+
188+
189+
constructor({ host = "", baseParams = {}, token }: ApiConfig = {}) {
190+
this.host = host;
191+
this.token = token;
192+
193+
const headers = new Headers({ "Content-Type": "application/json" });
194+
if (token) {
195+
headers.append("Authorization", \`Bearer \${token}\`);
196+
}
197+
this.baseParams = mergeParams({ headers }, baseParams);
198+
}
199+
200+
public async request<Data>({
201+
body,
202+
path,
203+
query,
204+
host,
205+
...fetchParams
206+
}: FullParams): Promise<ApiResult<Data>> {
207+
const url = (host || this.host) + path + toQueryString(query);
208+
const init = {
209+
...mergeParams(this.baseParams, fetchParams),
210+
body: JSON.stringify(snakeify(body), dateReplacer),
211+
};
212+
return handleResponse(await fetch(url, init));
213+
}
214+
215+
methods = {`);
174216

175217
for (const { conf, opId, method, path } of iterPathConfig(spec.paths)) {
176218
// websockets handled in the next loop

oxide-openapi-gen-ts/src/client/static/http-client.ts

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Copyright Oxide Computer Company
77
*/
88

9-
import { camelToSnake, processResponseBody, snakeify, isNotNull } from "./util";
9+
import { camelToSnake, processResponseBody, isNotNull } from "./util";
1010

1111
/** Success responses from the API */
1212
export type ApiSuccess<Data> = {
@@ -47,7 +47,7 @@ export type ApiResult<Data> = ApiSuccess<Data> | ErrorResult;
4747
* body and query params.
4848
*/
4949
// eslint-disable-next-line @typescript-eslint/no-explicit-any
50-
function replacer(_key: string, value: any) {
50+
export function dateReplacer(_key: string, value: any) {
5151
if (value instanceof Date) {
5252
return value.toISOString();
5353
}
@@ -56,7 +56,7 @@ function replacer(_key: string, value: any) {
5656

5757
function encodeQueryParam(key: string, value: unknown) {
5858
return `${encodeURIComponent(camelToSnake(key))}=${encodeURIComponent(
59-
replacer(key, value)
59+
dateReplacer(key, value)
6060
)}`;
6161
}
6262

@@ -117,48 +117,6 @@ export interface FullParams extends FetchParams {
117117
method?: string;
118118
}
119119

120-
export interface ApiConfig {
121-
/**
122-
* No host means requests will be sent to the current host. This is used in
123-
* the web console.
124-
*/
125-
host?: string;
126-
token?: string;
127-
baseParams?: FetchParams;
128-
}
129-
130-
export class HttpClient {
131-
host: string;
132-
token?: string;
133-
baseParams: FetchParams;
134-
135-
constructor({ host = "", baseParams = {}, token }: ApiConfig = {}) {
136-
this.host = host;
137-
this.token = token;
138-
139-
const headers = new Headers({ "Content-Type": "application/json" });
140-
if (token) {
141-
headers.append("Authorization", `Bearer ${token}`);
142-
}
143-
this.baseParams = mergeParams({ headers }, baseParams);
144-
}
145-
146-
public async request<Data>({
147-
body,
148-
path,
149-
query,
150-
host,
151-
...fetchParams
152-
}: FullParams): Promise<ApiResult<Data>> {
153-
const url = (host || this.host) + path + toQueryString(query);
154-
const init = {
155-
...mergeParams(this.baseParams, fetchParams),
156-
body: JSON.stringify(snakeify(body), replacer),
157-
};
158-
return handleResponse(await fetch(url, init));
159-
}
160-
}
161-
162120
export function mergeParams(a: FetchParams, b: FetchParams): FetchParams {
163121
// calling `new Headers()` normalizes `HeadersInit`, which could be a Headers
164122
// object, a plain object, or an array of tuples

0 commit comments

Comments
 (0)