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

Update API response object #4994

Merged
merged 27 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
04a7227
Use http status for node health
nazarhussain Jan 9, 2023
e73d173
Write only the header for the node health
nazarhussain Jan 9, 2023
9f4137d
Update the comment
nazarhussain Jan 9, 2023
a3865f4
Remove unncessary await
nazarhussain Jan 9, 2023
9a7e52b
Remove unncessary await
nazarhussain Jan 9, 2023
5388ca9
Update api test data
nazarhussain Jan 10, 2023
e99f6ba
Fix lint error
nazarhussain Jan 10, 2023
923137f
Update the api client to response the status code
nazarhussain Jan 16, 2023
cf88c52
Rename res prop to response
nazarhussain Jan 17, 2023
1d44e81
Fix unit tests
nazarhussain Jan 17, 2023
1862dfa
Export common types
nazarhussain Jan 17, 2023
b6bbc76
Fix validator package
nazarhussain Jan 17, 2023
ebdb14c
Fix all packages
nazarhussain Jan 17, 2023
74d6f5c
Merge branch 'unstable' into nh/4993-node-health
nazarhussain Jan 17, 2023
13be9fa
Fix some code break in merge conflicts
nazarhussain Jan 17, 2023
8bb4942
Fix type erorrs for all packages
nazarhussain Jan 18, 2023
c206b38
Merge branch 'unstable' into nh/4993-node-health
nazarhussain Jan 18, 2023
ad0cd24
Update the readme for the api package
nazarhussain Jan 18, 2023
edbfaa4
Merge branch 'nh/4993-node-health' of github.com:ChainSafe/lodestar i…
nazarhussain Jan 18, 2023
b37b071
Fix lint errors
nazarhussain Jan 18, 2023
8309bc6
Fix endpoint sim test
nazarhussain Jan 18, 2023
443cdde
Fix e2e tests
nazarhussain Jan 18, 2023
1f2054b
Update api package to remove optional error response
nazarhussain Jan 18, 2023
dee59bb
Update the api error response and assertion
nazarhussain Jan 19, 2023
916dba6
Fix few breaking tests
nazarhussain Jan 19, 2023
8559b71
Fix failing tests
nazarhussain Jan 19, 2023
f1c419b
Fix failing e2e test
nazarhussain Jan 19, 2023
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
18 changes: 15 additions & 3 deletions packages/api/src/beacon/client/beacon.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import {IChainForkConfig} from "@lodestar/config";
import {Api, ReqTypes, routesData, getReqSerializers, getReturnTypes} from "../routes/beacon/index.js";
import {IHttpClient, generateGenericJsonClient} from "../../utils/client/index.js";
import {IHttpClient, generateGenericJsonClient, ClientOptions} from "../../utils/client/index.js";
import {ReturnTypes} from "../../utils/types.js";
import {defaultClientOptions} from "./index.js";

/**
* REST HTTP client for beacon routes
*/
export function getClient(config: IChainForkConfig, httpClient: IHttpClient): Api {
export function getClient<ErrorAsResponse extends boolean = false>(
config: IChainForkConfig,
httpClient: IHttpClient,
options?: ClientOptions<ErrorAsResponse>
): Api<ErrorAsResponse> {
const reqSerializers = getReqSerializers(config);
const returnTypes = getReturnTypes();
// All routes return JSON, use a client auto-generator
return generateGenericJsonClient<Api, ReqTypes>(routesData, reqSerializers, returnTypes, httpClient);
return generateGenericJsonClient<Api<ErrorAsResponse>, ReqTypes, ErrorAsResponse>(
routesData,
reqSerializers,
returnTypes as ReturnTypes<Api<ErrorAsResponse>>,
httpClient,
options ?? (defaultClientOptions as ClientOptions<ErrorAsResponse>)
);
}
20 changes: 16 additions & 4 deletions packages/api/src/beacon/client/config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import {IChainForkConfig} from "@lodestar/config";
import {Api, ReqTypes, routesData, getReqSerializers, getReturnTypes} from "../routes/config.js";
import {IHttpClient, generateGenericJsonClient} from "../../utils/client/index.js";
import {ClientOptions, generateGenericJsonClient, IHttpClient} from "../../utils/client/index.js";
import {ReturnTypes} from "../../utils/types.js";
import {Api, getReqSerializers, getReturnTypes, ReqTypes, routesData} from "../routes/config.js";
import {defaultClientOptions} from "./index.js";

/**
* REST HTTP client for config routes
*/
export function getClient(config: IChainForkConfig, httpClient: IHttpClient): Api {
export function getClient<ErrorAsResponse extends boolean = false>(
config: IChainForkConfig,
httpClient: IHttpClient,
options?: ClientOptions<ErrorAsResponse>
): Api<ErrorAsResponse> {
const reqSerializers = getReqSerializers();
const returnTypes = getReturnTypes();
// All routes return JSON, use a client auto-generator
return generateGenericJsonClient<Api, ReqTypes>(routesData, reqSerializers, returnTypes, httpClient);
return generateGenericJsonClient<Api<ErrorAsResponse>, ReqTypes, ErrorAsResponse>(
routesData,
reqSerializers,
returnTypes as ReturnTypes<Api<ErrorAsResponse>>,
httpClient,
options ?? (defaultClientOptions as ClientOptions<ErrorAsResponse>)
);
}
54 changes: 41 additions & 13 deletions packages/api/src/beacon/client/debug.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,77 @@
import {IChainForkConfig} from "@lodestar/config";
import {Api, ReqTypes, routesData, getReqSerializers, getReturnTypes, StateFormat} from "../routes/debug.js";
import {IHttpClient, getFetchOptsSerializers, generateGenericJsonClient} from "../../utils/client/index.js";
import {
ClientOptions,
generateGenericJsonClient,
getFetchOptsSerializers,
IHttpClient,
} from "../../utils/client/index.js";
import {ReturnTypes} from "../../utils/types.js";
import {StateId} from "../routes/beacon/state.js";
import {Api, getReqSerializers, getReturnTypes, ReqTypes, routesData, StateFormat} from "../routes/debug.js";
import {defaultClientOptions} from "./index.js";

// As Jul 2022, it takes up to 3 mins to download states so make this 5 mins for reservation
const GET_STATE_TIMEOUT_MS = 5 * 60 * 1000;

/**
* REST HTTP client for debug routes
*/
export function getClient(_config: IChainForkConfig, httpClient: IHttpClient): Api {
export function getClient<ErrorAsResponse extends boolean = false>(
_config: IChainForkConfig,
httpClient: IHttpClient,
options?: ClientOptions<ErrorAsResponse>
): Api<ErrorAsResponse> {
const reqSerializers = getReqSerializers();
const returnTypes = getReturnTypes();
// Some routes return JSON, use a client auto-generator
const client = generateGenericJsonClient<Api, ReqTypes>(routesData, reqSerializers, returnTypes, httpClient);
const client = generateGenericJsonClient<Api<ErrorAsResponse>, ReqTypes, ErrorAsResponse>(
routesData,
reqSerializers,
returnTypes as ReturnTypes<Api<ErrorAsResponse>>,
httpClient,
options ?? (defaultClientOptions as ClientOptions<ErrorAsResponse>)
);
// For `getState()` generate request serializer
const fetchOptsSerializers = getFetchOptsSerializers<Api, ReqTypes>(routesData, reqSerializers);

return {
...client,

// TODO: Debug the type issue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also think about how we would be able to incorporate this? ethereum/beacon-APIs#250

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have support for SZZ encoding for few endpoints, similarly we can support for others when required.

https://github.com/ethereum/beacon-APIs/blob/6669d981486b7034fc9911fdf7be157343b6ea5e/apis/debug/state.v2.yaml#L40-L42

Copy link
Member

@wemeetagain wemeetagain Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This beacon-apis PR is about systematically supporting SSZ for all endpoints, not just supporting one-by-one.

Edit: I'm not suggesting to tackle this in this PR, just more an FYI of possible improvement that will be desired.

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
async getState(stateId: string, format?: StateFormat) {
if (format === "ssz") {
const buffer = await httpClient.arrayBuffer({
const res = await httpClient.arrayBuffer({
...fetchOptsSerializers.getState(stateId, format),
timeoutMs: GET_STATE_TIMEOUT_MS,
});
// Casting to any otherwise Typescript doesn't like the multi-type return
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any
return new Uint8Array(buffer) as any;
} else {
return client.getState(stateId, format);
return {
ok: true,
res: {data: new Uint8Array(res.body)},
status: 200,
};
}
return client.getState(stateId, format);
},
async getStateV2(stateId: string, format?: StateFormat) {

// TODO: Debug the type issue
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
async getStateV2(stateId: StateId, format?: StateFormat) {
if (format === "ssz") {
const buffer = await httpClient.arrayBuffer({
const res = await httpClient.arrayBuffer({
...fetchOptsSerializers.getStateV2(stateId, format),
timeoutMs: GET_STATE_TIMEOUT_MS,
});
// Casting to any otherwise Typescript doesn't like the multi-type return
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any
return new Uint8Array(buffer) as any;
} else {
return client.getStateV2(stateId, format);
return {ok: true, res: {data: new Uint8Array(res.body)}, status: res.status};
}

return client.getStateV2(stateId, format);
},
};
}
3 changes: 3 additions & 0 deletions packages/api/src/beacon/client/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {IChainForkConfig} from "@lodestar/config";
import {Api, BeaconEvent, routesData, getEventSerdes} from "../routes/events.js";
import {stringifyQuery} from "../../utils/client/format.js";
import {getEventSource} from "../../utils/client/eventSource.js";
import {HttpStatusCode} from "../../utils/client/httpStatusCode.js";

/**
* REST HTTP client for events routes
Expand Down Expand Up @@ -50,6 +51,8 @@ export function getClient(_config: IChainForkConfig, baseUrl: string): Api {
} finally {
eventSource.close();
}

return {ok: true, res: undefined, status: HttpStatusCode.OK};
},
};
}
Expand Down
34 changes: 24 additions & 10 deletions packages/api/src/beacon/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import {IChainForkConfig} from "@lodestar/config";
import {Api} from "../routes/index.js";
import {IHttpClient, HttpClient, HttpClientOptions, HttpClientModules} from "../../utils/client/index.js";
import {
IHttpClient,
HttpClient,
HttpClientOptions,
HttpClientModules,
ClientOptions,
} from "../../utils/client/index.js";

import * as beacon from "./beacon.js";
import * as configApi from "./config.js";
Expand All @@ -17,22 +23,30 @@ type ClientModules = HttpClientModules & {
httpClient?: IHttpClient;
};

export const defaultClientOptions: ClientOptions<false> = {
errorAsResponse: false,
};

/**
* REST HTTP client for all routes
*/
export function getClient(opts: HttpClientOptions, modules: ClientModules): Api {
export function getClient<ErrorAsResponse extends boolean = false>(
opts: HttpClientOptions,
modules: ClientModules,
options?: ClientOptions<ErrorAsResponse>
): Api<ErrorAsResponse> {
const {config} = modules;
const httpClient = modules.httpClient ?? new HttpClient(opts, modules);

return {
beacon: beacon.getClient(config, httpClient),
config: configApi.getClient(config, httpClient),
debug: debug.getClient(config, httpClient),
beacon: beacon.getClient<ErrorAsResponse>(config, httpClient, options),
config: configApi.getClient<ErrorAsResponse>(config, httpClient, options),
debug: debug.getClient<ErrorAsResponse>(config, httpClient, options),
events: events.getClient(config, httpClient.baseUrl),
lightclient: lightclient.getClient(config, httpClient),
lodestar: lodestar.getClient(config, httpClient),
node: node.getClient(config, httpClient),
proof: proof.getClient(config, httpClient),
validator: validator.getClient(config, httpClient),
lightclient: lightclient.getClient<ErrorAsResponse>(config, httpClient, options),
lodestar: lodestar.getClient<ErrorAsResponse>(config, httpClient, options),
node: node.getClient<ErrorAsResponse>(config, httpClient, options),
proof: proof.getClient<ErrorAsResponse>(config, httpClient, options),
validator: validator.getClient<ErrorAsResponse>(config, httpClient, options),
};
}
18 changes: 15 additions & 3 deletions packages/api/src/beacon/client/lightclient.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import {IChainForkConfig} from "@lodestar/config";
import {Api, ReqTypes, routesData, getReqSerializers, getReturnTypes} from "../routes/lightclient.js";
import {IHttpClient, generateGenericJsonClient} from "../../utils/client/index.js";
import {IHttpClient, generateGenericJsonClient, ClientOptions} from "../../utils/client/index.js";
import {ReturnTypes} from "../../utils/types.js";
import {defaultClientOptions} from "./index.js";

/**
* REST HTTP client for lightclient routes
*/
export function getClient(_config: IChainForkConfig, httpClient: IHttpClient): Api {
export function getClient<ErrorAsResponse extends boolean = false>(
_config: IChainForkConfig,
httpClient: IHttpClient,
options?: ClientOptions<ErrorAsResponse>
): Api<ErrorAsResponse> {
const reqSerializers = getReqSerializers();
const returnTypes = getReturnTypes();
// All routes return JSON, use a client auto-generator
return generateGenericJsonClient<Api, ReqTypes>(routesData, reqSerializers, returnTypes, httpClient);
return generateGenericJsonClient<Api<ErrorAsResponse>, ReqTypes, ErrorAsResponse>(
routesData,
reqSerializers,
returnTypes as ReturnTypes<Api<ErrorAsResponse>>,
httpClient,
options ?? (defaultClientOptions as ClientOptions<ErrorAsResponse>)
);
}
18 changes: 15 additions & 3 deletions packages/api/src/beacon/client/lodestar.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import {IChainForkConfig} from "@lodestar/config";
import {Api, ReqTypes, routesData, getReqSerializers, getReturnTypes} from "../routes/lodestar.js";
import {IHttpClient, generateGenericJsonClient} from "../../utils/client/index.js";
import {IHttpClient, generateGenericJsonClient, ClientOptions} from "../../utils/client/index.js";
import {ReturnTypes} from "../../utils/types.js";
import {defaultClientOptions} from "./index.js";

/**
* REST HTTP client for lodestar routes
*/
export function getClient(_config: IChainForkConfig, httpClient: IHttpClient): Api {
export function getClient<ErrorAsResponse extends boolean = false>(
_config: IChainForkConfig,
httpClient: IHttpClient,
options?: ClientOptions<ErrorAsResponse>
): Api<ErrorAsResponse> {
const reqSerializers = getReqSerializers();
const returnTypes = getReturnTypes();
// All routes return JSON, use a client auto-generator
return generateGenericJsonClient<Api, ReqTypes>(routesData, reqSerializers, returnTypes, httpClient);
return generateGenericJsonClient<Api<ErrorAsResponse>, ReqTypes, ErrorAsResponse>(
routesData,
reqSerializers,
returnTypes as ReturnTypes<Api<ErrorAsResponse>>,
httpClient,
options ?? (defaultClientOptions as ClientOptions<ErrorAsResponse>)
);
}
20 changes: 16 additions & 4 deletions packages/api/src/beacon/client/node.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import {IChainForkConfig} from "@lodestar/config";
import {Api, ReqTypes, routesData, getReqSerializers, getReturnTypes} from "../routes/node.js";
import {IHttpClient, generateGenericJsonClient} from "../../utils/client/index.js";
import {ClientOptions, generateGenericJsonClient, IHttpClient} from "../../utils/client/index.js";
import {APIServerHandlers} from "../../utils/types.js";
import {Api, getReqSerializers, getReturnTypes, ReqTypes, routesData} from "../routes/node.js";
import {defaultClientOptions} from "./index.js";

/**
* REST HTTP client for beacon routes
*/
export function getClient(_config: IChainForkConfig, httpClient: IHttpClient): Api {
export function getClient<ErrorAsResponse extends boolean = false>(
_config: IChainForkConfig,
httpClient: IHttpClient,
options?: ClientOptions<ErrorAsResponse>
): Api<ErrorAsResponse> {
const reqSerializers = getReqSerializers();
const returnTypes = getReturnTypes();
// All routes return JSON, use a client auto-generator
return generateGenericJsonClient<Api, ReqTypes>(routesData, reqSerializers, returnTypes, httpClient);
return generateGenericJsonClient<Api<ErrorAsResponse>, ReqTypes, ErrorAsResponse>(
routesData,
reqSerializers,
returnTypes,
httpClient,
options ?? (defaultClientOptions as ClientOptions<ErrorAsResponse>)
);
}
38 changes: 32 additions & 6 deletions packages/api/src/beacon/client/proof.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
import {IChainForkConfig} from "@lodestar/config";
import {deserializeProof} from "@chainsafe/persistent-merkle-tree";
import {deserializeProof, Proof} from "@chainsafe/persistent-merkle-tree";
import {Api, ReqTypes, routesData, getReqSerializers} from "../routes/proof.js";
import {IHttpClient, getFetchOptsSerializers} from "../../utils/client/index.js";
import {IHttpClient, getFetchOptsSerializers, ClientOptions, HttpError} from "../../utils/client/index.js";
import {HttpStatusCode} from "../../utils/client/httpStatusCode.js";
import {APIClientResponse} from "../../utils/types.js";

/**
* REST HTTP client for lightclient routes
*/
export function getClient(_config: IChainForkConfig, httpClient: IHttpClient): Api {
export function getClient<ErrorAsResponse extends boolean = false>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you document the behavior of ErrorAsResponse somewhere?

Seems that depending on the option passed, you can make errors either throw or return.

Do we need both behaviors? Might be simpler to pick a convention (ie always return) and stick with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wemeetagain My preference is to use always return case. But to avoid backward compatibility, I made it optional for now. If we all agree to one approach we can stick to it.

_config: IChainForkConfig,
httpClient: IHttpClient,
options?: ClientOptions<ErrorAsResponse>
): Api<ErrorAsResponse> {
const reqSerializers = getReqSerializers();

// For `getStateProof()` generate request serializer
const fetchOptsSerializers = getFetchOptsSerializers<Api, ReqTypes>(routesData, reqSerializers);

return {
async getStateProof(stateId, paths) {
const buffer = await httpClient.arrayBuffer(fetchOptsSerializers.getStateProof(stateId, paths));
const proof = deserializeProof(new Uint8Array(buffer));
return {data: proof};
try {
const res = await httpClient.arrayBuffer(fetchOptsSerializers.getStateProof(stateId, paths));
const proof = deserializeProof(new Uint8Array(res.body));

return {ok: true, res: {data: proof}, status: HttpStatusCode.OK} as APIClientResponse<
{[HttpStatusCode.OK]: {data: Proof}},
HttpStatusCode.INTERNAL_SERVER_ERROR,
ErrorAsResponse
>;
} catch (err) {
if (err instanceof HttpError && options?.errorAsResponse) {
return {
ok: false,
res: {code: err.status, message: err.message},
status: err.status,
} as APIClientResponse<
{[HttpStatusCode.OK]: {data: Proof}},
HttpStatusCode.INTERNAL_SERVER_ERROR,
ErrorAsResponse
>;
}
throw err;
}
},
};
}
18 changes: 15 additions & 3 deletions packages/api/src/beacon/client/validator.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import {IChainForkConfig} from "@lodestar/config";
import {Api, ReqTypes, routesData, getReqSerializers, getReturnTypes} from "../routes/validator.js";
import {IHttpClient, generateGenericJsonClient} from "../../utils/client/index.js";
import {IHttpClient, generateGenericJsonClient, ClientOptions} from "../../utils/client/index.js";
import {ReturnTypes} from "../../utils/types.js";
import {defaultClientOptions} from "./index.js";

/**
* REST HTTP client for validator routes
*/
export function getClient(_config: IChainForkConfig, httpClient: IHttpClient): Api {
export function getClient<ErrorAsResponse extends boolean = false>(
_config: IChainForkConfig,
httpClient: IHttpClient,
options?: ClientOptions<ErrorAsResponse>
): Api<ErrorAsResponse> {
const reqSerializers = getReqSerializers();
const returnTypes = getReturnTypes();
// All routes return JSON, use a client auto-generator
return generateGenericJsonClient<Api, ReqTypes>(routesData, reqSerializers, returnTypes, httpClient);
return generateGenericJsonClient<Api<ErrorAsResponse>, ReqTypes, ErrorAsResponse>(
routesData,
reqSerializers,
returnTypes as ReturnTypes<Api<ErrorAsResponse>>,
httpClient,
options ?? (defaultClientOptions as ClientOptions<ErrorAsResponse>)
);
}
Loading