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
2019-04-12 22:17:39 +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 = "/lbry/docs/api.json" ;
const rawGitHubBase = "https://raw.githubusercontent.com/lbryio/" ;
if ( ! process . env . GITHUB _OAUTH _TOKEN ) // No point in rendering this page
throw new Error ( "Missing GitHub token" ) ;
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-02-15 17:50:56 +01:00
state . lbry = {
2019-07-10 19:34:19 +02:00
title : "API Documentation" ,
description : "See API documentation, signatures, and sample calls for the LBRY APIs."
2019-02-15 17:50:56 +01:00
} ;
2018-11-30 21:46:22 +01:00
2019-07-10 19:34:19 +02:00
const { wildcard } = state . params ;
const repository = wildcard === "sdk" ?
"lbry-sdk" :
"lbrycrd" ;
const tags = await getTags ( repository ) ;
2018-11-30 21:46:22 +01:00
try {
2019-07-10 19:34:19 +02:00
const apiResponse = await parseApiFile ( { repo : repository , tag : tags [ 0 ] } ) ;
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);" >
$ { renderVersionSelector ( wildcard , tags ) }
< / s e l e c t >
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 ; < / d i v >
< ul class = "api-toc__search-results" > < / u l >
2018-11-30 21:46:22 +01:00
< / d i v >
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
< / u l >
2018-11-30 21:46:22 +01:00
< / a s i d e >
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 > < / d i v >
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
< / n a v >
2019-07-10 19:34:19 +02:00
$ { createApiHeader ( wildcard ) }
$ { wildcard === "sdk" ? createSdkContent ( apiResponse ) : createApiContent ( apiResponse ) }
2018-11-30 21:46:22 +01:00
< / d i v >
< / s e c t i o n >
< / d i v >
< script src = "/assets/scripts/plugins/jets.js" > < / s c r i p t >
< script src = "/assets/scripts/api.js" > < / s c r i p t >
2019-01-30 23:15:10 +01:00
< script >
2019-05-10 00:37:52 +02:00
if ( window . location . pathname === "/api/blockchain" )
document . getElementById ( "toggle-cli" ) . click ( ) ;
else
document . getElementById ( "toggle-curl" ) . click ( ) ;
2019-01-30 23:15:10 +01:00
< / s c r i p t >
2018-11-30 21:46:22 +01:00
` ;
}
2019-01-08 18:25:34 +01: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 < / h 1 >
< / d i v >
2018-10-01 22:47:10 +02:00
< / d i v >
2018-10-06 22:59:46 +02:00
< / h e a d e r >
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 } < / s t r o n g > < / p >
< / d i v >
< / s e c t i o n >
< / a r t i c l e >
< script >
setTimeout ( ( ) => {
window . location . href = "${redirectUrl}" ;
} , 2000 ) ;
< / s c r i p t >
` ;
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 = [ ] ;
2019-05-10 00:37:52 +02:00
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 } < / h 2 >
< 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> ` : "" }
2019-05-10 00:37:52 +02:00
$ { apiDetail . returns ? ` <h3>Returns</h3><pre><code> ${ dedent ( apiDetailsReturns ) } </code></pre> ` : "" }
2018-08-29 01:57:18 +02:00
< / d i v >
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
< / d i v >
` );
2019-05-10 00:37:52 +02:00
} ) ;
2018-08-29 01:57:18 +02:00
return apiContent ;
}
2018-10-01 22:47:10 +02:00
function createApiHeader ( slug ) {
switch ( slug ) {
case "blockchain" :
return headerBlockchain ( ) ;
case "sdk" :
return headerSdk ( ) ;
default :
break ;
}
}
2018-08-29 01:57:18 +02:00
function createApiSidebar ( apiDetails ) {
const apiSidebar = [ ] ;
2019-05-10 00:37:52 +02:00
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 >
< / l i >
` );
2019-05-10 00:37:52 +02:00
} ) ;
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 ) ;
2019-05-10 00:37:52 +02:00
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-05-10 00:37:52 +02:00
} ) ;
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 } < / h 2 >
< p > $ { sectionDetails . description } < / p >
< h3 > Arguments < / h 3 >
< ul class = "api-content__body-arguments" >
$ { renderArguments ( sectionDetails . arguments ) . join ( "" ) }
< / u l >
< h3 > Returns < / h 3 >
< pre > < code > $ { renderReturns ( sectionDetails . returns ) } < / c o d e > < / p r e >
< / d i v >
< div class = "api-content__example" >
$ { renderExamples ( sectionDetails . examples ) . join ( "" ) }
< / d i v >
` ;
}
function createSdkSidebar ( apiDetails ) {
const sectionTitles = Object . keys ( apiDetails ) ;
const apiSidebar = [ ] ;
2019-05-10 00:37:52 +02:00
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 } < / l i >
$ { ( commands . map ( command => ` <li class="api-toc__command"><a href="# ${ command . name } " title="Go to ${ command . name } section"> ${ command . name } </a></li> ` ) ) . join ( "" ) }
< / u l >
` );
2019-05-10 00:37:52 +02:00
} ) ;
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 = [ ] ;
// 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.38.0" &&
tag . name !== "v0.38.0rc7" &&
tag . name !== "v0.38.0rc6" &&
tag . name !== "v0.38.0rc5" &&
tag . name !== "v0.38.0rc4" &&
tag . name !== "v0.38.0rc3" &&
tag . name !== "v0.38.0rc2" &&
tag . name !== "v0.38.0rc1"
) tags . push ( tag . name ) ;
} ) ;
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 ;
2019-05-10 00:37:52 +02:00
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/ >
$ { arg . is _required === true ? "" : "<span>optional</span>" } < span > $ { arg . type } < / s p a n >
< / d i v >
< div class = "right" > $ { typeof arg . description === "string" ? arg . description . replace ( /</g , "<" ) . replace ( />/g , ">" ) : "" } < / d i v >
< / l i >
` );
2019-05-10 00:37:52 +02:00
} ) ;
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 ;
}
2019-05-10 00:37:52 +02:00
args . forEach ( arg => {
2019-01-30 23:15:10 +01:00
exampleContent . push ( `
2019-05-10 00:37:52 +02:00
$ { 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 } < / c o d e > < / p r e >
< hr / >
` : ""}
2019-01-30 23:15:10 +01:00
` );
2019-05-10 00:37:52 +02: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-05-10 00:37:52 +02:00
2019-07-10 19:34:19 +02:00
function renderVersionSelector ( pageSlug , versions ) {
const options = [
"<option disabled>Select a version</option>"
] ;
versions . forEach ( version => {
options . push ( ` <option value=" ${ pageSlug } - ${ version } "> ${ version } </option> ` ) ;
} ) ;
return options ;
}
function renderCodeLanguageToggles ( pageSlug ) {
const onSdkPage = pageSlug === "sdk" ;
2019-05-10 00:37:52 +02:00
return [
2019-05-14 21:40:35 +02:00
"<button class='api-content__item menu' id='toggle-menu'>menu</button>" ,
2019-05-10 00:37:52 +02:00
! 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>" : ""
] ;
}