-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2532e29
commit c5f1367
Showing
7 changed files
with
251 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { BunFile } from "bun"; | ||
|
||
export const CORS_HEADERS = new Headers({ | ||
"Access-Control-Allow-Origin": "*", | ||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS", | ||
"Access-Control-Allow-Headers": "Content-Type, WWW-Authenticate", | ||
}); | ||
export const JSON_HEADERS = new Headers({"Content-Type": "application/json"}); | ||
export const TEXT_HEADERS = new Headers({"Content-Type": "text/plain; version=0.0.4; charset=utf-8"}); | ||
|
||
export function appendHeaders(...args: Headers[]) { | ||
const headers = new Headers(CORS_HEADERS); // CORS as default headers | ||
for (const arg of args) { | ||
for (const [key, value] of arg.entries()) { | ||
headers.set(key, value); | ||
} | ||
} | ||
return headers; | ||
}; | ||
|
||
export function toJSON(body: any, status = 200, headers = new Headers()) { | ||
const data = typeof body == "string" ? body : JSON.stringify(body, null, 2); | ||
return new Response(data, { status, headers: appendHeaders(JSON_HEADERS, headers) }); | ||
} | ||
|
||
export function toText(body: string, status = 200, headers = new Headers()) { | ||
return new Response(body, { status, headers: appendHeaders(TEXT_HEADERS, headers) }); | ||
} | ||
|
||
export function toFile(body: BunFile, status = 200, headers = new Headers()) { | ||
const fileHeaders = new Headers({"Content-Type": body.type}); | ||
return new Response(body, { status, headers: appendHeaders(fileHeaders, headers) }); | ||
} | ||
|
||
export const BadRequest = toText('Bad Request', 400); | ||
export const NotFound = toText('Not Found', 404); | ||
export const InternalServerError = toText("Internal Server Error", 500); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import pkg from "../../package.json" assert { type: "json" }; | ||
|
||
import { OpenApiBuilder, SchemaObject, ExampleObject, ParameterObject } from "openapi3-ts/oas31"; | ||
import { config } from "../config.js"; | ||
import { getBlock } from "../queries.js"; | ||
import { registry } from "../prometheus.js"; | ||
import { makeQuery } from "../clickhouse/makeQuery.js"; | ||
import { supportedChainsQuery } from "./chains.js"; | ||
|
||
const TAGS = { | ||
MONITORING: "Monitoring", | ||
HEALTH: "Health", | ||
USAGE: "Usage", | ||
DOCS: "Documentation", | ||
} as const; | ||
|
||
const chains = await supportedChainsQuery(); | ||
const block_example = (await makeQuery(await getBlock( new URLSearchParams({limit: "2"})))).data; | ||
|
||
const timestampSchema: SchemaObject = { anyOf: [ | ||
{type: "number"}, | ||
{type: "string", format: "date"}, | ||
{type: "string", format: "date-time"} | ||
] | ||
}; | ||
const timestampExamples: ExampleObject = { | ||
unix: { summary: `Unix Timestamp (seconds)` }, | ||
date: { summary: `Full-date notation`, value: '2023-10-18' }, | ||
datetime: { summary: `Date-time notation`, value: '2023-10-18T00:00:00Z'}, | ||
} | ||
|
||
export default new OpenApiBuilder() | ||
.addInfo({ | ||
title: pkg.name, | ||
version: pkg.version, | ||
description: pkg.description, | ||
license: {name: pkg.license}, | ||
}) | ||
.addExternalDocs({ url: pkg.homepage, description: "Extra documentation" }) | ||
.addSecurityScheme("auth-key", { type: "http", scheme: "bearer" }) | ||
.addPath("/chains", { | ||
get: { | ||
tags: [TAGS.USAGE], | ||
summary: 'Supported chains', | ||
responses: { | ||
200: { | ||
description: "Array of chains", | ||
content: { | ||
"application/json": { | ||
schema: { type: "array" }, | ||
example: chains, | ||
} | ||
}, | ||
}, | ||
}, | ||
}, | ||
}) | ||
.addPath("/block", { | ||
get: { | ||
tags: [TAGS.USAGE], | ||
summary: "Get block", | ||
description: "Get block by `block_number`, `block_id` or `timestamp`", | ||
parameters: [ | ||
{ | ||
name: "chain", | ||
in: "query", | ||
description: "Filter by chain", | ||
required: false, | ||
schema: {enum: chains}, | ||
}, | ||
{ | ||
name: "block_number", | ||
description: "Filter by Block number (ex: 18399498)", | ||
in: "query", | ||
required: false, | ||
schema: { type: "number" }, | ||
}, | ||
{ | ||
name: "block_id", | ||
in: "query", | ||
description: "Filter by Block hash ID (ex: 00fef8cf2a2c73266f7c0b71fb5762f9a36419e51a7c05b0e82f9e3bacb859bc)", | ||
required: false, | ||
schema: { type: "string" }, | ||
}, | ||
{ | ||
name: 'timestamp', | ||
in: 'query', | ||
description: 'Filter by exact timestamp', | ||
required: false, | ||
schema: timestampSchema, | ||
examples: timestampExamples, | ||
}, | ||
{ | ||
name: "final_block", | ||
description: "If true, only returns final blocks", | ||
in: "query", | ||
required: false, | ||
schema: { type: "boolean" }, | ||
}, | ||
{ | ||
name: "sort_by", | ||
in: "query", | ||
description: "Sort by `block_number`", | ||
required: false, | ||
schema: {enum: ['ASC', 'DESC'] }, | ||
}, | ||
...["greater_or_equals_by_timestamp", "greater_by_timestamp", "less_or_equals_by_timestamp", "less_by_timestamp"].map(name => { | ||
return { | ||
name, | ||
in: "query", | ||
description: "Filter " + name.replace(/_/g, " "), | ||
required: false, | ||
schema: timestampSchema, | ||
examples: timestampExamples, | ||
} as ParameterObject | ||
}), | ||
...["greater_or_equals_by_block_number", "greater_by_block_number", "less_or_equals_by_block_number", "less_by_block_number"].map(name => { | ||
return { | ||
name, | ||
in: "query", | ||
description: "Filter " + name.replace(/_/g, " "), | ||
required: false, | ||
schema: { type: "number" }, | ||
} as ParameterObject | ||
}), | ||
{ | ||
name: "limit", | ||
in: "query", | ||
description: "Used to specify the number of records to return.", | ||
required: false, | ||
schema: { type: "number", maximum: config.maxLimit, minimum: 1 }, | ||
}, | ||
], | ||
responses: { | ||
200: { description: "Array of blocks", content: { "application/json": { example: block_example, schema: { type: "array" } } } }, | ||
400: { description: "Bad request" }, | ||
}, | ||
}, | ||
}) | ||
.addPath("/health", { | ||
get: { | ||
tags: [TAGS.HEALTH], | ||
summary: "Performs health checks and checks if the database is accessible", | ||
responses: {200: { description: "OK", content: { "text/plain": {example: "OK"}} } }, | ||
}, | ||
}) | ||
.addPath("/metrics", { | ||
get: { | ||
tags: [TAGS.MONITORING], | ||
summary: "Prometheus metrics", | ||
responses: {200: { description: "Prometheus metrics", content: { "text/plain": { example: await registry.metrics(), schema: { type: "string" } } }}}, | ||
}, | ||
}) | ||
.addPath("/openapi", { | ||
get: { | ||
tags: [TAGS.DOCS], | ||
summary: "OpenAPI specification", | ||
responses: {200: {description: "OpenAPI JSON Specification", content: { "application/json": { schema: { type: "string" } } } }}, | ||
}, | ||
}) | ||
.getSpecAsJson(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
declare module "*.png" { | ||
const content: string; | ||
export default content; | ||
} | ||
|
||
declare module "*.html" { | ||
const content: string; | ||
export default content; | ||
} | ||
|
||
declare module "*.sql" { | ||
const content: string; | ||
export default content; | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<meta | ||
name="description" | ||
content="SwaggerUI" | ||
/> | ||
<title>Websocket API - SwaggerUI</title> | ||
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@latest/swagger-ui.css" /> | ||
<link href="/favicon.png" rel="icon" type="image/x-icon"> | ||
</head> | ||
<body> | ||
<div id="swagger-ui"></div> | ||
<script src="https://unpkg.com/swagger-ui-dist@latest/swagger-ui-bundle.js" crossorigin></script> | ||
<script src="https://unpkg.com/swagger-ui-dist@latest/swagger-ui-standalone-preset.js" crossorigin></script> | ||
<script> | ||
window.onload = () => { | ||
window.ui = SwaggerUIBundle({ | ||
url: 'https://substreams-erc20-api-production.up.railway.app/openapi', | ||
dom_id: '#swagger-ui', | ||
presets: [ | ||
SwaggerUIBundle.presets.apis, | ||
SwaggerUIStandalonePreset | ||
], | ||
layout: "StandaloneLayout", | ||
}); | ||
}; | ||
</script> | ||
</body> | ||
</html> |