Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.0.0: Fix remaining REST untyped responses #882

Merged
merged 9 commits into from
Aug 15, 2024
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
111 changes: 63 additions & 48 deletions src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,60 @@ interface ErrorWithAdditionalInfo extends Error {
statusCode: number;
}

export interface HTTPClientResponse {
body: Uint8Array | any; // when content-type=JSON, body is a JSON object, otherwise it's a Uint8Array
export class HTTPClientResponse {
/**
* The raw response bytes
*/
body: Uint8Array;
/**
* If the expected response type is JSON, this is the response bytes converted to a string.
*/
text?: string;
format: 'application/msgpack' | 'application/json';
headers: Record<string, string>;
status: number;
ok: boolean;

constructor(options: {
body: Uint8Array;
text?: string;
format: 'application/msgpack' | 'application/json';
headers: Record<string, string>;
status: number;
ok: boolean;
}) {
this.body = options.body;
this.text = options.text;
this.format = options.format;
this.headers = options.headers;
this.status = options.status;
this.ok = options.ok;
}

/**
* Returns the response body as a string, ready to be parsed as JSON.
*/
getJSONText(): string {
jasonpaulos marked this conversation as resolved.
Show resolved Hide resolved
if (this.text === undefined) {
throw new Error(
`Response body does not contain JSON data. Format is ${this.format}`
);
}
return this.text;
}

/**
* Parses the response body as JSON with the given options.
*/
parseBodyAsJSON(jsonOptions: utils.ParseJSONOptions) {
if (this.text === undefined) {
throw new Error(
`Response body does not contain JSON data. Format is ${this.format}`
);
}
// eslint-disable-next-line no-use-before-define
return HTTPClient.parseJSON(this.text, this.status, jsonOptions);
}
}

/**
Expand Down Expand Up @@ -175,27 +223,21 @@ export class HTTPClient {
*/
private static prepareResponse(
res: BaseHTTPClientResponse,
format: 'application/msgpack' | 'application/json',
parseBody: boolean,
jsonOptions: utils.ParseJSONOptions
format: 'application/msgpack' | 'application/json'
): HTTPClientResponse {
let { body } = res;
const { body } = res;
let text: string | undefined;

if (format !== 'application/msgpack') {
text = (body && new TextDecoder().decode(body)) || '';
}

if (parseBody && format === 'application/json') {
body = HTTPClient.parseJSON(text!, res.status, jsonOptions);
}

return {
return new HTTPClientResponse({
...res,
body,
format,
text,
ok: Math.trunc(res.status / 100) === 2,
};
});
}

/**
Expand All @@ -204,17 +246,12 @@ export class HTTPClient {
* by adding the status and preparing the internal response
* @private
*/
private static prepareResponseError(
err: any,
jsonOptions: utils.ParseJSONOptions
) {
private static prepareResponseError(err: any) {
if (err.response) {
// eslint-disable-next-line no-param-reassign
err.response = HTTPClient.prepareResponse(
err.response,
'application/json',
true,
jsonOptions
'application/json'
);
// eslint-disable-next-line no-param-reassign
err.status = err.response.status;
Expand All @@ -237,16 +274,12 @@ export class HTTPClient {
*/
async get({
relativePath,
jsonOptions,
query,
requestHeaders,
parseBody,
}: {
relativePath: string;
jsonOptions: utils.ParseJSONOptions;
query?: Query<any>;
requestHeaders?: Record<string, string>;
parseBody: boolean;
}): Promise<HTTPClientResponse> {
const format = getAcceptFormat(query);
const fullHeaders = { ...(requestHeaders ?? {}), accept: format };
Expand All @@ -258,9 +291,9 @@ export class HTTPClient {
fullHeaders
);

return HTTPClient.prepareResponse(res, format, parseBody, jsonOptions);
return HTTPClient.prepareResponse(res, format);
} catch (err) {
throw HTTPClient.prepareResponseError(err, jsonOptions);
throw HTTPClient.prepareResponseError(err);
}
}

Expand All @@ -273,17 +306,13 @@ export class HTTPClient {
async post({
relativePath,
data,
jsonOptions,
query,
requestHeaders,
parseBody,
}: {
relativePath: string;
data: any;
jsonOptions: utils.ParseJSONOptions;
query?: Query<any>;
requestHeaders?: Record<string, string>;
parseBody: boolean;
}): Promise<HTTPClientResponse> {
const fullHeaders = {
'content-type': 'application/json',
Expand All @@ -298,14 +327,9 @@ export class HTTPClient {
fullHeaders
);

return HTTPClient.prepareResponse(
res,
'application/json',
parseBody,
jsonOptions
);
return HTTPClient.prepareResponse(res, 'application/json');
} catch (err) {
throw HTTPClient.prepareResponseError(err, jsonOptions);
throw HTTPClient.prepareResponseError(err);
}
}

Expand All @@ -318,15 +342,11 @@ export class HTTPClient {
async delete({
relativePath,
data,
jsonOptions,
requestHeaders,
parseBody,
}: {
relativePath: string;
data: any;
jsonOptions: utils.ParseJSONOptions;
requestHeaders?: Record<string, string>;
parseBody: boolean;
}) {
const fullHeaders = {
'content-type': 'application/json',
Expand All @@ -343,14 +363,9 @@ export class HTTPClient {
fullHeaders
);

return HTTPClient.prepareResponse(
res,
'application/json',
parseBody,
jsonOptions
);
return HTTPClient.prepareResponse(res, 'application/json');
} catch (err) {
throw HTTPClient.prepareResponseError(err, jsonOptions);
throw HTTPClient.prepareResponseError(err);
}
}
}
30 changes: 12 additions & 18 deletions src/client/kmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,33 @@ export class KmdClient extends ServiceClient {
private async get(relativePath: string): Promise<any> {
const res = await this.c.get({
relativePath,
jsonOptions: {
// Using SAFE for all KMD endpoints because no integers in responses should ever be too big
intDecoding: IntDecoding.SAFE,
},
parseBody: true,
});
return res.body;
return res.parseBodyAsJSON({
// Using SAFE for all KMD endpoints because no integers in responses should ever be too big
intDecoding: IntDecoding.SAFE,
});
}

private async delete(relativePath: string, data: any): Promise<any> {
const res = await this.c.delete({
relativePath,
data,
jsonOptions: {
// Using SAFE for all KMD endpoints because no integers in responses should ever be too big
intDecoding: IntDecoding.SAFE,
},
parseBody: true,
});
return res.body;
return res.parseBodyAsJSON({
// Using SAFE for all KMD endpoints because no integers in responses should ever be too big
intDecoding: IntDecoding.SAFE,
});
}

private async post(relativePath: string, data: any): Promise<any> {
const res = await this.c.post({
relativePath,
data,
parseBody: true,
jsonOptions: {
// Using SAFE for all KMD endpoints because no integers in responses should ever be too big
intDecoding: IntDecoding.SAFE,
},
});
return res.body;
return res.parseBodyAsJSON({
// Using SAFE for all KMD endpoints because no integers in responses should ever be too big
intDecoding: IntDecoding.SAFE,
});
}

/**
Expand Down
14 changes: 5 additions & 9 deletions src/client/v2/algod/accountApplicationInformation.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import JSONRequest from '../jsonrequest.js';
import { HTTPClient } from '../../client.js';
import { HTTPClient, HTTPClientResponse } from '../../client.js';
import { decodeJSON } from '../../../encoding/encoding.js';
import { AccountApplicationResponse } from './models/types.js';
import { Address } from '../../../encoding/address.js';

export default class AccountApplicationInformation extends JSONRequest<
AccountApplicationResponse,
Record<string, any>
> {
export default class AccountApplicationInformation extends JSONRequest<AccountApplicationResponse> {
private account: string;

constructor(
Expand All @@ -23,9 +21,7 @@ export default class AccountApplicationInformation extends JSONRequest<
}

// eslint-disable-next-line class-methods-use-this
prepare(body: Record<string, any>): AccountApplicationResponse {
return AccountApplicationResponse.fromEncodingData(
AccountApplicationResponse.encodingSchema.fromPreparedJSON(body)
);
prepare(response: HTTPClientResponse): AccountApplicationResponse {
return decodeJSON(response.getJSONText(), AccountApplicationResponse);
}
}
14 changes: 5 additions & 9 deletions src/client/v2/algod/accountAssetInformation.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import JSONRequest from '../jsonrequest.js';
import { HTTPClient } from '../../client.js';
import { HTTPClient, HTTPClientResponse } from '../../client.js';
import { decodeJSON } from '../../../encoding/encoding.js';
import { AccountAssetResponse } from './models/types.js';
import { Address } from '../../../encoding/address.js';

export default class AccountAssetInformation extends JSONRequest<
AccountAssetResponse,
Record<string, any>
> {
export default class AccountAssetInformation extends JSONRequest<AccountAssetResponse> {
private account: string;

constructor(
Expand All @@ -23,9 +21,7 @@ export default class AccountAssetInformation extends JSONRequest<
}

// eslint-disable-next-line class-methods-use-this
prepare(body: Record<string, any>): AccountAssetResponse {
return AccountAssetResponse.fromEncodingData(
AccountAssetResponse.encodingSchema.fromPreparedJSON(body)
);
prepare(response: HTTPClientResponse): AccountAssetResponse {
return decodeJSON(response.getJSONText(), AccountAssetResponse);
}
}
14 changes: 5 additions & 9 deletions src/client/v2/algod/accountInformation.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import JSONRequest from '../jsonrequest.js';
import { HTTPClient } from '../../client.js';
import { HTTPClient, HTTPClientResponse } from '../../client.js';
import { decodeJSON } from '../../../encoding/encoding.js';
import { Account } from './models/types.js';
import { Address } from '../../../encoding/address.js';

export default class AccountInformation extends JSONRequest<
Account,
Record<string, any>
> {
export default class AccountInformation extends JSONRequest<Account> {
private account: string;

constructor(c: HTTPClient, account: string | Address) {
Expand Down Expand Up @@ -38,9 +36,7 @@ export default class AccountInformation extends JSONRequest<
}

// eslint-disable-next-line class-methods-use-this
prepare(body: Record<string, any>): Account {
return Account.fromEncodingData(
Account.encodingSchema.fromPreparedJSON(body)
);
prepare(response: HTTPClientResponse): Account {
return decodeJSON(response.getJSONText(), Account);
}
}
10 changes: 5 additions & 5 deletions src/client/v2/algod/block.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as encoding from '../../../encoding/encoding.js';
import JSONRequest from '../jsonrequest.js';
import { HTTPClient } from '../../client.js';
import { HTTPClient, HTTPClientResponse } from '../../client.js';
import { decodeMsgpack } from '../../../encoding/encoding.js';
import { BlockResponse } from './models/types.js';

/**
* block gets the block info for the given round. this call may block
*/
export default class Block extends JSONRequest<BlockResponse, Uint8Array> {
export default class Block extends JSONRequest<BlockResponse> {
private round: number;

constructor(c: HTTPClient, roundNumber: number) {
Expand All @@ -20,7 +20,7 @@ export default class Block extends JSONRequest<BlockResponse, Uint8Array> {
}

// eslint-disable-next-line class-methods-use-this
prepare(body: Uint8Array): BlockResponse {
return encoding.decodeMsgpack(body, BlockResponse);
prepare(response: HTTPClientResponse): BlockResponse {
return decodeMsgpack(response.body, BlockResponse);
}
}
Loading