-
Notifications
You must be signed in to change notification settings - Fork 16
chore: small fixes for ensapi and openapi doc generation #1872
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
base: main
Are you sure you want to change the base?
Changes from all commits
9f99256
47bbca0
2265bf3
a9b62f4
e0fa2d4
324dd4b
c355312
054f680
95eceb3
d17133e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "ensapi": patch | ||
| --- | ||
|
|
||
| Fixes error handling in app.onError to return correct HTTP status codes and resolves OpenAPI schema generation issue |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,5 @@ | ||||||
| --- | ||||||
| "ensapi": patch | ||||||
| --- | ||||||
|
|
||||||
| use example requests for openapi doc | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
These are example responses, right? |
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@ensnode/ensnode-sdk": patch | ||
| --- | ||
|
|
||
| add examples for type system | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as a reader i'm not sure what this is referring to |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,7 +75,7 @@ app.get("/health", async (c) => { | |
| // log hono errors to console | ||
| app.onError((error, ctx) => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this fallback catch-all error handler, what do you think about adding a prefix to the log messages / `errorResponse that would clearly identify for us that the error went through this fallback catch-all error handler. Goal: Help us with debugging. For example, maybe a little prefix such as "Unexpected server error: ..."? Appreciate your advice. |
||
| logger.error(error); | ||
sevenzing marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return errorResponse(ctx, "Internal Server Error"); | ||
| return errorResponse(ctx, error); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is the wrong approach: this would catch any uncaught or unexpected errors and leak internal error messages to the public. instead, the handlers should catch errors and return an error response, and this function should never be executed. by definition if this fn is executed, there's some unexpected internal server error, and that's what we should return |
||
| }); | ||
|
|
||
| export default app; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,10 +6,14 @@ import { | |
| RegistrarActionsOrders, | ||
| } from "@ensnode/ensnode-sdk"; | ||
| import { | ||
| ErrorResponseSchema, | ||
| makeLowercaseAddressSchema, | ||
| makeNodeSchema, | ||
| makePositiveIntegerSchema, | ||
| makeRegistrarActionsResponseErrorSchema, | ||
| makeSerializedRegistrarActionsResponseOkSchema, | ||
| makeUnixTimestampSchema, | ||
| registrarActionsResponseOkExample, | ||
| } from "@ensnode/ensnode-sdk/internal"; | ||
|
|
||
| import { params } from "@/lib/handlers/params.schema"; | ||
|
|
@@ -59,12 +63,14 @@ export const registrarActionsQuerySchema = z | |
| .pipe(z.coerce.number()) | ||
| .pipe(makeUnixTimestampSchema("beginTimestamp")) | ||
| .optional() | ||
| .openapi({ type: "integer" }) | ||
| .describe("Filter actions at or after this Unix timestamp"), | ||
|
|
||
| endTimestamp: params.queryParam | ||
| .pipe(z.coerce.number()) | ||
| .pipe(makeUnixTimestampSchema("endTimestamp")) | ||
| .optional() | ||
| .openapi({ type: "integer" }) | ||
| .describe("Filter actions at or before this Unix timestamp"), | ||
| }) | ||
| .refine( | ||
|
|
@@ -96,12 +102,27 @@ export const getRegistrarActionsRoute = createRoute({ | |
| responses: { | ||
| 200: { | ||
| description: "Successfully retrieved registrar actions", | ||
| content: { | ||
| "application/json": { | ||
| schema: makeSerializedRegistrarActionsResponseOkSchema( | ||
| "Registrar Actions Response", | ||
| ).openapi({ | ||
| example: registrarActionsResponseOkExample, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be awesome if you could make it so that we could produce multiple examples in the OpenAPI spec. For example, both an Ok and and Error example. It's important that we document examples for error cases too. For some of our APIs, it would also be valuable if we could document various edge cases for success responses too. What do you think? Please feel welcome to create a separate follow-up issue and PR for this goal. |
||
| }), | ||
| }, | ||
| }, | ||
sevenzing marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
| 400: { | ||
| description: "Invalid query", | ||
| content: { "application/json": { schema: ErrorResponseSchema } }, | ||
| }, | ||
| 500: { | ||
| description: "Internal server error", | ||
| content: { | ||
| "application/json": { | ||
| schema: makeRegistrarActionsResponseErrorSchema("Registrar Actions Error Response"), | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
@@ -125,14 +146,29 @@ export const getRegistrarActionsByParentNodeRoute = createRoute({ | |
| responses: { | ||
| 200: { | ||
| description: "Successfully retrieved registrar actions", | ||
| content: { | ||
| "application/json": { | ||
| schema: makeSerializedRegistrarActionsResponseOkSchema( | ||
| "Registrar Actions By ParentNode Response", | ||
| ).openapi({ | ||
| example: registrarActionsResponseOkExample, | ||
| }), | ||
| }, | ||
| }, | ||
| }, | ||
| 400: { | ||
| description: "Invalid input", | ||
| content: { "application/json": { schema: ErrorResponseSchema } }, | ||
| }, | ||
| 500: { | ||
| description: "Internal server error", | ||
| content: { | ||
| "application/json": { | ||
| schema: makeRegistrarActionsResponseErrorSchema( | ||
| "Registrar Actions By ParentNode Error Response", | ||
| ), | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| export const routes = [getRegistrarActionsRoute, getRegistrarActionsByParentNodeRoute]; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,5 +46,3 @@ export const realtimeGetMeta = createRoute({ | |
| }, | ||
| }, | ||
| }); | ||
|
|
||
| export const routes = [realtimeGetMeta]; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -37,33 +37,67 @@ const stringarray = z | |||||||||||||||||||||||||||||||||||||||||
| const name = z | ||||||||||||||||||||||||||||||||||||||||||
| .string() | ||||||||||||||||||||||||||||||||||||||||||
| .refine(isNormalizedName, "Must be normalized, see https://docs.ens.domains/resolution/names/") | ||||||||||||||||||||||||||||||||||||||||||
| .transform((val) => val as Name); | ||||||||||||||||||||||||||||||||||||||||||
| .transform((val) => val as Name) | ||||||||||||||||||||||||||||||||||||||||||
| .describe("ENS name to resolve (e.g. 'vitalik.eth'). Must be normalized per ENSIP-15."); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const trace = z.optional(boolstring).default(false).openapi({ default: false }); | ||||||||||||||||||||||||||||||||||||||||||
| const accelerate = z.optional(boolstring).default(false).openapi({ default: false }); | ||||||||||||||||||||||||||||||||||||||||||
| const address = makeLowercaseAddressSchema(); | ||||||||||||||||||||||||||||||||||||||||||
| const defaultableChainId = makeDefaultableChainIdStringSchema(); | ||||||||||||||||||||||||||||||||||||||||||
| const coinType = makeCoinTypeStringSchema(); | ||||||||||||||||||||||||||||||||||||||||||
| const trace = z | ||||||||||||||||||||||||||||||||||||||||||
| .optional(boolstring) | ||||||||||||||||||||||||||||||||||||||||||
| .default(false) | ||||||||||||||||||||||||||||||||||||||||||
| .describe("Include detailed resolution trace information in the response.") | ||||||||||||||||||||||||||||||||||||||||||
| .openapi({ default: false }); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const chainIdsWithoutDefaultChainId = z.optional( | ||||||||||||||||||||||||||||||||||||||||||
| stringarray.pipe(z.array(defaultableChainId.pipe(excludingDefaultChainId))), | ||||||||||||||||||||||||||||||||||||||||||
| const accelerate = z | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I believe we should rename this param as suggested above. This is because We need to soften the language. Ex: |
||||||||||||||||||||||||||||||||||||||||||
| .optional(boolstring) | ||||||||||||||||||||||||||||||||||||||||||
| .default(false) | ||||||||||||||||||||||||||||||||||||||||||
| .describe("Attempt accelerated CCIP-Read resolution using L1 data.") | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Our acceleration is not constrained only to L1 data. We can fully accelerate resolutions for some "L2 names" such as jesse.base.eth
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah maybe just |
||||||||||||||||||||||||||||||||||||||||||
| .openapi({ | ||||||||||||||||||||||||||||||||||||||||||
| default: false, | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe we should update this to accelerate by default. Please feel welcome to create a separate follow-up issue / PR for this. |
||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
| const address = makeLowercaseAddressSchema().describe( | ||||||||||||||||||||||||||||||||||||||||||
| "EVM wallet address (e.g. '0xd8da6bf26964af9d7eed9e03e53415d37aa96045').", | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we note in this description that it must be all lowercase?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i believe we accept checksummed addresses and the lowercase schema normalizes them |
||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||
| const defaultableChainId = makeDefaultableChainIdStringSchema().describe( | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I note how these I'm curious though about putting these In other words, this |
||||||||||||||||||||||||||||||||||||||||||
| "Chain ID as a string (e.g. '1' for Ethereum mainnet). Use '0' for the default EVM chain.", | ||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||
| const coinType = makeCoinTypeStringSchema(); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const chainIdsWithoutDefaultChainId = z | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume we should invert the strategy used here? Here, I see we are starting with defaultable and then removing the default chain id case. Why don't we start with the regular (non-defaultable) chainid case and then add the special defaultable case? That mirrors the approach taken here:
Additionally, the idea of a "defaultable chain id" only exists within the context of ENS resolution, while the idea of a chain id exists in many other contexts. Therefore I don't think we should include ideas such as "... The default EVM chain ID (0) is not allowed." when describing a regular (non-defaultable) chainid field. |
||||||||||||||||||||||||||||||||||||||||||
| .optional(stringarray.pipe(z.array(defaultableChainId.pipe(excludingDefaultChainId)))) | ||||||||||||||||||||||||||||||||||||||||||
| .describe( | ||||||||||||||||||||||||||||||||||||||||||
| "Comma-separated list of chain IDs to resolve primary names for (e.g. '1,10,8453'). The default EVM chain ID (0) is not allowed.", | ||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const rawSelectionParams = z.object({ | ||||||||||||||||||||||||||||||||||||||||||
| name: z.string().optional(), | ||||||||||||||||||||||||||||||||||||||||||
| addresses: z.string().optional(), | ||||||||||||||||||||||||||||||||||||||||||
| texts: z.string().optional(), | ||||||||||||||||||||||||||||||||||||||||||
| nameRecord: z | ||||||||||||||||||||||||||||||||||||||||||
| .string() | ||||||||||||||||||||||||||||||||||||||||||
| .optional() | ||||||||||||||||||||||||||||||||||||||||||
| .describe("Whether to include the ENS name record in the response.") | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please see my other comments. We should significantly refine the way we are describing this field. |
||||||||||||||||||||||||||||||||||||||||||
| .openapi({ | ||||||||||||||||||||||||||||||||||||||||||
| enum: ["true", "false"], | ||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||
sevenzing marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
| addresses: z | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
70
to
+78
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
A more accurate OpenAPI annotation would be:
Suggested change
Alternatively, simply reuse |
||||||||||||||||||||||||||||||||||||||||||
| .string() | ||||||||||||||||||||||||||||||||||||||||||
| .optional() | ||||||||||||||||||||||||||||||||||||||||||
| .describe( | ||||||||||||||||||||||||||||||||||||||||||
| "Comma-separated list of coin types to resolve addresses for (e.g. '60' for ETH, '2147483658' for OP).", | ||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||
| texts: z | ||||||||||||||||||||||||||||||||||||||||||
| .string() | ||||||||||||||||||||||||||||||||||||||||||
| .optional() | ||||||||||||||||||||||||||||||||||||||||||
| .describe( | ||||||||||||||||||||||||||||||||||||||||||
| "Comma-separated list of text record keys to resolve (e.g. 'avatar,description,url').", | ||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const selection = z | ||||||||||||||||||||||||||||||||||||||||||
| .object({ | ||||||||||||||||||||||||||||||||||||||||||
| name: z.optional(boolstring), | ||||||||||||||||||||||||||||||||||||||||||
| nameRecord: z.optional(boolstring), | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
It will help if we document in appropriate places what this I'm going to invest some meaningful effort in writing ideas for you here that I hope will be a good resource for you in taking the lead on building the ultimate ENS resolution APIs. There's some meaningful complexity here. It's ok to not 100% master all the details just yet but please take special note of everything I write here for your future work. Here's the main documentation about reverse resolution:
In other words:
Also highly suggest to add documentation about this
Please note how above I noted how this In other words, maybe we should move this field out of the root of the We should still expose the ability to query the For example, maybe add nesting for this field in the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Appreciate if you can put a special effort into adding some nice docs about this idea in all the relevant places across our whole monorepo. It's a complex idea and I really want to see us investing strongly in our docs. This includes docs for this field within the Drizzle schemas that are defined in ENSDb. |
||||||||||||||||||||||||||||||||||||||||||
| addresses: z.optional(stringarray.pipe(z.array(coinType))), | ||||||||||||||||||||||||||||||||||||||||||
| texts: z.optional(stringarray), | ||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||
| .transform((value, ctx) => { | ||||||||||||||||||||||||||||||||||||||||||
| const selection: ResolverRecordsSelection = { | ||||||||||||||||||||||||||||||||||||||||||
| ...(value.name && { name: true }), | ||||||||||||||||||||||||||||||||||||||||||
| ...(value.nameRecord && { name: true }), | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Should we be renaming other things too? For example, I'm surprised this PR is not making updates to our ENS Protocol Acceleration logic inside ENSApi. I would have expected more things to change if we rename this idea. Appreciate your advice.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. imo we should leave the protocol acceleration lib as-is — so the current change is acceptable — the handler accepts finally, if we're going to rename |
||||||||||||||||||||||||||||||||||||||||||
| ...(value.addresses && { addresses: value.addresses }), | ||||||||||||||||||||||||||||||||||||||||||
| ...(value.texts && { texts: value.texts }), | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.