lbry.tech/app/views/api.js

399 lines
11 KiB
JavaScript
Raw Permalink Normal View History

2018-08-29 01:57:18 +02:00
"use strict";
2018-10-01 22:47:10 +02:00
2018-11-30 21:46:22 +01:00
// I M P O R T S
2018-10-01 22:47:10 +02:00
2018-08-29 18:58:55 +02:00
import asyncHtml from "choo-async/html";
2018-08-29 01:57:18 +02:00
import dedent from "dedent";
2019-01-08 18:25:34 +01:00
import got from "got";
2019-07-10 19:34:19 +02:00
import Octokit from "@octokit/rest";
2018-10-01 22:47:10 +02:00
2018-10-10 19:56:35 +02:00
// U T I L S
2018-08-29 01:57:18 +02:00
import headerBlockchain from "~component/api/header-blockchain";
import headerSdk from "~component/api/header-sdk";
import redirects from "~data/redirects.json";
2018-11-30 21:46:22 +01:00
2019-04-16 21:52:51 +02:00
const cache = new Map();
2019-07-10 19:34:19 +02:00
const filePathBlockchain = "/contrib/devtools/generated/api_v1.json";
const filePathSdk = "/docs/api.json";
2019-07-10 19:34:19 +02:00
const rawGitHubBase = "https://raw.githubusercontent.com/lbryio/";
const octokit = new Octokit({
auth: `token ${process.env.GITHUB_OAUTH_TOKEN}`
});
2018-10-01 22:47:10 +02:00
// E X P O R T
2018-08-29 01:57:18 +02:00
2018-11-30 21:46:22 +01:00
export default async(state) => {
2019-07-10 23:48:45 +02:00
const { tag } = state;
2019-07-10 19:34:19 +02:00
const { wildcard } = state.params;
const repository = wildcard === "sdk" ?
"lbry-sdk" :
"lbrycrd";
2019-07-10 23:48:45 +02:00
state.lbry = {
title: tag ? tag + " API Documentation" : "API Documentation",
description: "See API documentation, signatures, and sample calls for the LBRY APIs."
};
2019-07-10 19:34:19 +02:00
const tags = await getTags(repository);
2019-07-12 00:46:38 +02:00
const currentTag = tag && tag.length ? tag : tags[0];
2019-07-10 19:34:19 +02:00
2018-11-30 21:46:22 +01:00
try {
2019-07-12 00:09:36 +02:00
const apiResponse = await parseApiFile({ repo: repository, tag: currentTag });
2018-11-30 21:46:22 +01:00
return asyncHtml`
<div class="__slate">
2019-01-30 23:15:10 +01:00
<aside class="api-toc">
2019-07-10 19:34:19 +02:00
<select class="api-toc__select" onchange="changeDocumentationVersion(value);">
2019-07-10 23:48:45 +02:00
${renderVersionSelector(wildcard, tags, tag)}
2019-07-10 19:34:19 +02:00
</select>
2019-07-10 23:48:45 +02:00
2019-01-30 23:15:10 +01:00
<div class="api-toc__search">
<input class="api-toc__search-field" id="input-search" placeholder="Search" type="search"/>
<div class="api-toc__search-clear" id="clear-search" title="Clear search query">&times;</div>
<ul class="api-toc__search-results"></ul>
2018-11-30 21:46:22 +01:00
</div>
2019-04-12 21:08:35 +02:00
<ul class="api-toc__commands" id="toc" role="navigation">
2019-07-10 19:34:19 +02:00
${wildcard === "sdk" ? createSdkSidebar(apiResponse) : createApiSidebar(apiResponse)}
2019-04-12 21:08:35 +02:00
</ul>
2018-11-30 21:46:22 +01:00
</aside>
2019-07-10 23:48:45 +02:00
2019-01-30 23:15:10 +01:00
<section class="api-content">
<div class="api-documentation" id="toc-content">
2019-02-20 00:42:52 +01:00
<div></div>
2019-07-10 23:48:45 +02:00
2019-01-30 23:15:10 +01:00
<nav class="api-content__items">
2019-07-10 19:34:19 +02:00
${renderCodeLanguageToggles(wildcard)}
2019-01-30 23:15:10 +01:00
</nav>
2019-07-12 00:09:36 +02:00
${createApiHeader(wildcard, currentTag)}
2019-07-10 19:34:19 +02:00
${wildcard === "sdk" ? createSdkContent(apiResponse) : createApiContent(apiResponse)}
2018-11-30 21:46:22 +01:00
</div>
</section>
2019-07-10 23:48:45 +02:00
<script src="/assets/scripts/plugins/jets.js"></script>
<script src="/assets/scripts/api.js"></script>
2019-01-30 23:15:10 +01:00
2019-07-10 23:48:45 +02:00
<script>
initializeApiFunctionality();
if (window.location.pathname === "/api/blockchain")
document.getElementById("toggle-cli").click();
else
document.getElementById("toggle-curl").click();
</script>
</div>
2018-11-30 21:46:22 +01:00
`;
2019-07-12 00:09:36 +02:00
} catch(error) {
2018-10-06 22:53:01 +02:00
const redirectUrl = redirects[state.href];
2018-08-29 01:57:18 +02:00
2018-10-06 22:53:01 +02:00
return asyncHtml`
2018-10-06 22:59:46 +02:00
<article class="page" itemtype="http://schema.org/BlogPosting">
<header class="page__header">
<div class="page__header-wrap">
<div class="inner-wrap">
<h1 class="page__header__title" itemprop="name headline">404</h1>
</div>
2018-10-01 22:47:10 +02:00
</div>
2018-10-06 22:59:46 +02:00
</header>
2018-08-29 01:57:18 +02:00
2018-10-06 22:59:46 +02:00
<section class="page__content page__markup" itemprop="articleBody">
<div class="inner-wrap">
<p>Redirecting you to <strong>${redirectUrl}</strong></p>
</div>
</section>
</article>
<script>
setTimeout(() => {
window.location.href = "${redirectUrl}";
}, 2000);
</script>
`;
2018-11-30 21:46:22 +01:00
}
};
2018-09-30 19:34:29 +02:00
2018-08-29 01:57:18 +02:00
2018-10-01 22:47:10 +02:00
// H E L P E R S
2018-10-01 06:40:24 +02:00
2018-08-29 01:57:18 +02:00
function createApiContent(apiDetails) {
const apiContent = [];
apiDetails.forEach(apiDetail => {
2018-08-29 01:57:18 +02:00
let apiDetailsReturns = "";
2018-10-06 22:53:01 +02:00
2018-11-30 21:46:22 +01:00
if (apiDetail.returns)
apiDetailsReturns = JSON.parse(JSON.stringify(apiDetail.returns));
2018-08-29 01:57:18 +02:00
apiContent.push(`
2019-01-30 23:15:10 +01:00
<div class="api-content__body">
2018-08-29 01:57:18 +02:00
<h2 id="${apiDetail.name}">${apiDetail.name}</h2>
<p>${apiDetail.description}</p>
2019-01-30 23:15:10 +01:00
${apiDetail.arguments.length ? `<h3>Arguments</h3><ul class="api-content__body-arguments">${renderArguments(apiDetail.arguments).join("")}</ul>` : ""}
${apiDetail.returns ? `<h3>Returns</h3><pre><code>${dedent(apiDetailsReturns)}</code></pre>` : ""}
2018-08-29 01:57:18 +02:00
</div>
2019-01-30 23:15:10 +01:00
<div class="api-content__example">
${apiDetail.examples && apiDetail.examples.length ? renderExamples(apiDetail.examples).join("") : `<pre><code>// example(s) for ${apiDetail.name} to come later</code></pre>`}
2018-08-29 01:57:18 +02:00
</div>
`);
});
2018-08-29 01:57:18 +02:00
return apiContent;
}
2019-07-10 23:48:45 +02:00
function createApiHeader(slug, apiVersion) {
2018-10-01 22:47:10 +02:00
switch(slug) {
case "blockchain":
2019-07-10 23:48:45 +02:00
return headerBlockchain(apiVersion);
2018-10-01 22:47:10 +02:00
case "sdk":
2019-07-10 23:48:45 +02:00
return headerSdk(apiVersion);
2018-10-01 22:47:10 +02:00
default:
break;
}
}
2018-08-29 01:57:18 +02:00
function createApiSidebar(apiDetails) {
const apiSidebar = [];
apiDetails.forEach(apiDetail => {
2018-08-29 01:57:18 +02:00
apiSidebar.push(`
2019-04-12 21:08:35 +02:00
<li class="api-toc__command">
2018-08-29 01:57:18 +02:00
<a href="#${apiDetail.name}" title="Go to ${apiDetail.name} section">
${apiDetail.name}
</a>
</li>
`);
});
2018-08-29 01:57:18 +02:00
return apiSidebar;
}
2019-04-12 21:08:35 +02:00
function createSdkContent(apiDetails) {
const apiContent = [];
const sectionTitles = Object.keys(apiDetails);
sectionTitles.forEach(title => {
2019-04-12 21:08:35 +02:00
const commands = apiDetails[title].commands;
const description = apiDetails[title].doc;
apiContent.push(
commands.length ?
commands.map(command => createSdkContentSections(title, description, command)).join("") :
""
);
});
2019-04-12 21:08:35 +02:00
return apiContent;
}
function createSdkContentSections(sectionTitle, sectionDescription, sectionDetails) {
return `
<div class="api-content__body">
<h2 id="${sectionDetails.name}">${sectionDetails.name}</h2>
<p>${sectionDetails.description}</p>
<h3>Arguments</h3>
<ul class="api-content__body-arguments">
${renderArguments(sectionDetails.arguments).join("")}
</ul>
<h3>Returns</h3>
<pre><code>${renderReturns(sectionDetails.returns)}</code></pre>
</div>
<div class="api-content__example">
${renderExamples(sectionDetails.examples).join("")}
</div>
`;
}
function createSdkSidebar(apiDetails) {
const sectionTitles = Object.keys(apiDetails);
const apiSidebar = [];
sectionTitles.forEach(title => {
2019-04-12 21:08:35 +02:00
const commands = apiDetails[title].commands;
apiSidebar.push(`
<ul class="api-toc__section">
<li class="api-toc__title">${title}</li>
${(commands.map(command => `<li class="api-toc__command"><a href="#${command.name}" title="Go to ${command.name} section">${command.name}</a></li>`)).join("")}
</ul>
`);
});
2019-04-12 21:08:35 +02:00
return apiSidebar;
}
2019-07-10 19:34:19 +02:00
async function getTags(repositoryName) {
const { data } = await octokit.repos.listTags({
owner: "lbryio",
repo: repositoryName
});
const tags = ["master"];
2019-07-10 19:34:19 +02:00
// NOTE:
// The versioning in our repos do not make sense so extra
// exclusion code is needed to make this work.
//
// Documentation is only available after specific versions.
2018-08-29 01:57:18 +02:00
2018-10-01 22:47:10 +02:00
switch(true) {
2019-07-10 19:34:19 +02:00
case repositoryName === "lbry-sdk":
data.forEach(tag => {
if (tag.name >= "v0.52.0") tags.push(tag.name);
2019-07-10 19:34:19 +02:00
});
2018-10-01 22:47:10 +02:00
break;
2018-08-29 01:57:18 +02:00
2019-07-10 19:34:19 +02:00
case repositoryName === "lbrycrd":
data.forEach(tag => {
if (
tag.name >= "v0.17.1.0" &&
tag.name !== "v0.3.16" &&
tag.name !== "v0.3.15" &&
tag.name !== "v0.3-osx" &&
tag.name !== "v0.2-alpha"
) tags.push(tag.name);
});
2018-10-01 22:47:10 +02:00
break;
default:
break;
}
2018-08-29 01:57:18 +02:00
2019-07-10 19:34:19 +02:00
return tags;
}
async function parseApiFile({ repo, tag }) {
let apiFileLink = `${rawGitHubBase}${repo}/${tag}`;
switch(true) {
case (repo === "lbrycrd"):
apiFileLink = `${apiFileLink}${filePathBlockchain}`;
break;
case (repo === "lbry-sdk"):
apiFileLink = `${apiFileLink}${filePathSdk}`;
break;
default:
return Promise.reject(new Error("Failed to fetch API docs"));
}
2019-01-08 18:25:34 +01:00
2019-04-16 21:52:51 +02:00
const response = await got(apiFileLink, { cache, json: true });
2019-01-08 18:25:34 +01:00
try {
return response.body;
} catch(error) {
return "Issue loading API documentation";
}
2018-08-29 01:57:18 +02:00
}
function renderArguments(args) {
const argumentContent = [];
2019-04-12 21:08:35 +02:00
if (!args || args.length === 0)
return argumentContent;
args.forEach(arg => {
2018-08-29 01:57:18 +02:00
argumentContent.push(`
2019-01-30 23:15:10 +01:00
<li class="api-content__body-argument">
2018-08-29 01:57:18 +02:00
<div class="left">
<strong>${arg.name}</strong><br/>
2019-07-12 00:09:36 +02:00
${arg.is_required === true ? "" : "<span>optional</span>"}<span>${arg.type}</span>
2018-08-29 01:57:18 +02:00
</div>
<div class="right">${typeof arg.description === "string" ? arg.description.replace(/</g, "&lt;").replace(/>/g, "&gt;") : ""}</div>
</li>
`);
});
2018-08-29 01:57:18 +02:00
return argumentContent;
}
2019-01-30 23:15:10 +01:00
function renderExamples(args) {
const exampleContent = [];
2019-04-12 21:08:35 +02:00
if (!args || args.length === 0) {
exampleContent.push("<pre><code>// example(s) to come later</code></pre>");
return exampleContent;
}
args.forEach(arg => {
2019-01-30 23:15:10 +01:00
exampleContent.push(`
${arg.title ? `<h3>${arg.title}</h3><br/>` : ""}
${arg.cli ? `<pre data-api-example-type="cli"><code>${arg.cli}</code></pre>` : ""}
${arg.curl ? `<pre data-api-example-type="curl"><code>${arg.curl}</code></pre>` : ""}
${arg.lbrynet ? `<pre data-api-example-type="lbrynet"><code>${arg.lbrynet}</code></pre>` : ""}
${arg.python ? `<pre data-api-example-type="python"><code>${arg.python}</code></pre>` : ""}
${arg.output ? `
<h3>Output</h3><br/>
<pre><code>${arg.output}</code></pre>
<hr/>
` : ""}
2019-01-30 23:15:10 +01:00
`);
});
2019-01-30 23:15:10 +01:00
return exampleContent;
}
2019-04-12 21:08:35 +02:00
function renderReturns(args) {
let returnContent = [];
if (!args || args.length === 0)
return returnContent;
returnContent = dedent(JSON.parse(JSON.stringify(args)));
return returnContent;
}
2019-07-10 23:48:45 +02:00
function renderVersionSelector(pageSlug, versions, desiredTag) {
2019-07-10 19:34:19 +02:00
const options = [
"<option disabled>Select a version</option>"
];
2019-07-10 23:48:45 +02:00
let optionIndex = 0;
2019-07-10 19:34:19 +02:00
versions.forEach(version => {
2019-07-10 23:48:45 +02:00
optionIndex++;
let selectedOption = false;
if (desiredTag && desiredTag === version)
selectedOption = true;
else if (optionIndex === 1)
selectedOption = true;
options.push(
`<option value="${pageSlug}-${version}"${selectedOption ? " selected" : ""}>${version}</option>`
);
2019-07-10 19:34:19 +02:00
});
return options;
}
function renderCodeLanguageToggles(pageSlug) {
const onSdkPage = pageSlug === "sdk";
return [
2019-05-14 21:40:35 +02:00
"<button class='api-content__item menu' id='toggle-menu'>menu</button>",
!onSdkPage ? "<button class='api-content__item' id='toggle-cli' type='button'>cli</button>" : "",
"<button class='api-content__item' id='toggle-curl' type='button'>curl</button>",
onSdkPage ? "<button class='api-content__item' id='toggle-lbrynet' type='button'>lbrynet</button>" : "",
onSdkPage ? "<button class='api-content__item' id='toggle-python' type='button'>python</button>" : ""
];
}