Skip to content

Commit

Permalink
docs: Refactor HTTP API docs (#1416)
Browse files Browse the repository at this point in the history
  • Loading branch information
adityapk00 authored Sep 22, 2023
1 parent 31641c1 commit aeab5a4
Show file tree
Hide file tree
Showing 17 changed files with 1,220 additions and 1,138 deletions.
7 changes: 7 additions & 0 deletions .changeset/pretty-dolphins-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@farcaster/hub-nodejs": patch
"@farcaster/core": patch
"@farcaster/hubble": patch
---

docs: Refactor HTTP API docs
80 changes: 55 additions & 25 deletions apps/hubble/scripts/httpapidocs.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { readFileSync } from "fs";
import { readFileSync, readdirSync } from "fs";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
import { remark } from "remark";
Expand Down Expand Up @@ -32,8 +32,9 @@ export function httpapidocs() {
return [...endpointSet];
}

function findMissingEndpointsInDocs(endpoints, docsContent) {
const missingEndpoints = endpoints.filter((endpoint) => !docsContent.includes(`### ${endpoint}`));
function findMissingEndpointsInDocs(endpoints, allDocsContent) {
const docsContent = allDocsContent.join("\n");
const missingEndpoints = endpoints.filter((endpoint) => !docsContent.includes(`## ${endpoint}`));

return missingEndpoints;
}
Expand Down Expand Up @@ -88,34 +89,36 @@ export function httpapidocs() {
return tree;
}

function getParametersForEndpoint(endpoint, tree) {
function getParametersForEndpoint(endpoint, trees) {
let foundEndpoint = false;
let parameters = [];
let line = 0;

tree.children.forEach((node, index) => {
if (node.type === "heading" && node.children[0].value === endpoint) {
foundEndpoint = true;
}
trees.forEach(({ fileName, tree }) => {
tree.children.forEach((node, index) => {
if (node.type === "heading" && node.children[0].value === endpoint) {
foundEndpoint = true;
}

if (foundEndpoint && node.type === "table") {
parameters = node.children
.slice(1)
.map((row) => row.children[0].children[0]?.value)
.filter((p) => p !== undefined);
line = node.position.start.line;
foundEndpoint = false; // Reset to stop looking after finding the table
}
if (foundEndpoint && node.type === "table") {
parameters = node.children
.slice(1)
.map((row) => row.children[0].children[0]?.value)
.filter((p) => p !== undefined);
line = `${fileName}:${node.position.start.line}`;
foundEndpoint = false; // Reset to stop looking after finding the table
}
});
});

return { parameters, line };
}

function checkParametersInDocs(docTags, tree) {
function checkParametersInDocs(docTags, trees) {
// For each endpoint, check if the parameters in the doc tags are present in the docs
let anyError = false;
for (const endpoint in docTags) {
const { parameters, line } = getParametersForEndpoint(endpoint, tree);
const { parameters, line } = getParametersForEndpoint(endpoint, trees);

for (const param of docTags[endpoint].params) {
if (!parameters.includes(param)) {
Expand All @@ -125,7 +128,7 @@ export function httpapidocs() {
endpoint
].lineNumbers.join(
", ",
)}) is missing documentation in the parameters table (on httpapi.md: line ${line}) for endpoint "${endpoint}"`,
)}) is missing documentation in the parameters table (on ${line}) for endpoint "${endpoint}"`,
);
}
}
Expand All @@ -135,7 +138,7 @@ export function httpapidocs() {
if (!docTags[endpoint].params.includes(param)) {
anyError = true;
console.error(
`Parameter "${param}" is documented in the parameters table (on httpapi.md: line ${line}) for endpoint "${endpoint}" but is not specified in the @doc-tag (on httpServer.ts: line ${docTags[
`Parameter "${param}" is documented in the parameters table (on ${line}) for endpoint "${endpoint}" but is not specified in the @doc-tag (on httpServer.ts: line ${docTags[
endpoint
].lineNumbers.join(", ")})`,
);
Expand All @@ -152,21 +155,33 @@ export function httpapidocs() {
const endpoints = extractUniqueEndpoints(contents);
const docTags = extractDocTags(contents);

const docFilePath = join(__dirname, "../www/docs/docs/httpapi.md");
const docsContent = readFileSync(docFilePath, "utf-8");
const tree = parseMarkdown(docsContent);
const docsFolderPath = join(__dirname, "../www/docs/docs/httpapi/");
const fileNames = readdirSync(docsFolderPath);

const trees = [];
const allDocsContent = [];

fileNames.forEach((fileName) => {
const docFilePath = join(docsFolderPath, fileName);
const docsContent = readFileSync(docFilePath, "utf-8");
const tree = parseMarkdown(docsContent);
trees.push({ fileName, tree });
allDocsContent.push(docsContent);
});
// console.log(getParametersForEndpoint("castsByParent", tree));

// First, get all endPoints that are not documented in the docs
const missingEndpoints = findMissingEndpointsInDocs(endpoints, docsContent);
const missingEndpoints = findMissingEndpointsInDocs(endpoints, allDocsContent);

// Make sure the same passes for doc-tags as well, ensuring doc-tags and endpoints are 1-1
const missingDocTagEndpoints = findMissingEndpointsInDocs(Object.keys(docTags), allDocsContent);

// Next, get all endpoints that are documented but are missing doc tags
const missingDocTags = findMissingDocTags(docTags, endpoints);

// console.log(docTags);
// Last, check for parameters
let anyError = checkParametersInDocs(docTags, tree);
let anyError = false;

if (missingEndpoints.length > 0) {
console.error(
Expand All @@ -176,12 +191,27 @@ export function httpapidocs() {
anyError = true;
}

if (missingDocTagEndpoints.length > 0) {
console.error(
"The following endpoints specified as @doc-tags in httpServer.ts are missing from the HTTP API docs in httpapi.md:",
);
// console.error(missingDocTagEndpoints);
for (const endpoint of missingDocTagEndpoints) {
const { lineNumbers } = docTags[endpoint];
console.error(`Endpoint "${endpoint}" (on httpServer.ts: line ${lineNumbers.join(", ")})`);
}

anyError = true;
}

if (missingDocTags.length > 0) {
console.error("The following endpoints specified in httpServer.ts are missing doc tags:");
console.error(missingDocTags);
anyError = true;
}

anyError = checkParametersInDocs(docTags, trees) || anyError;

if (anyError) {
console.log("❌ HTTP API docs are not up to date");
process.exit(1);
Expand Down
2 changes: 1 addition & 1 deletion apps/hubble/src/rpc/httpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function getCallObject<M extends keyof HubServiceServer>(
): CallTypeForMethod<M> {
return {
request: params,
metadata: metadata ?? new Metadata(),
metadata,
getPeer: () => request.ip,
} as CallTypeForMethod<M>;
}
Expand Down
17 changes: 16 additions & 1 deletion apps/hubble/www/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,22 @@ export default defineConfig({
items: [
{ text: "CLI", link: "/docs/cli" },
{ text: "APIs", link: "/docs/api" },
{ text: "HTTP APIs", link: "/docs/httpapi" },
{
text: "HTTP APIs",
items: [
{ text: "Using HTTP APIs", link: "/docs/httpapi/httpapi" },
{ text: "Casts API", link: "/docs/httpapi/casts" },
{ text: "Reactions API", link: "/docs/httpapi/reactions" },
{ text: "Links API", link: "/docs/httpapi/links" },
{ text: "UserData API", link: "/docs/httpapi/userdata" },
{ text: "Storage API", link: "/docs/httpapi/storagelimits" },
{ text: "Username Proofs API", link: "/docs/httpapi/usernameproof" },
{ text: "Verifications API", link: "/docs/httpapi/verification" },
{ text: "On Chain API", link: "/docs/httpapi/onchain" },
{ text: "SubmitMessage API", link: "/docs/httpapi/submitmessage" },
{ text: "Events API", link: "/docs/httpapi/events" },
],
},
{ text: "Messages", link: "/docs/messages" },
{ text: "OnChainEvents", link: "/docs/onchain_events" },
{ text: "Events", link: "/docs/events" },
Expand Down
Loading

0 comments on commit aeab5a4

Please sign in to comment.