diff --git a/.gitignore b/.gitignore index adeedae8..4f88c408 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ yarn-error.log .DS_Store .env .env.* +data/squid-js.json diff --git a/.gitmodules b/.gitmodules index e4e9225c..cf63a225 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = external/dev-ocean url = https://github.com/oceanprotocol/dev-ocean branch = master +[submodule "external/squid-js"] + path = external/squid-js + url = https://github.com/oceanprotocol/squid-js.git diff --git a/README.md b/README.md index 4770b5ee..c0311b52 100644 --- a/README.md +++ b/README.md @@ -82,37 +82,37 @@ All Markdown files should use 1. The file must begin with a section called YAML frontmatter that looks like this: - ```md - --- - title: This is the Title in Title Case - description: A short description of the page. - --- +```md +--- +title: This is the Title in Title Case +description: A short description of the page. +--- - Markdown content begins here. - ``` +Markdown content begins here. +``` - For external documents in other repos, defining the `slug` and `section` is required: +For external documents in other repos, defining the `slug` and `section` is required: - ```md - --- - title: This is the Title in Title Case - description: A short description of the page. - slug: /concepts/architecture/ - section: concepts - --- +```md +--- +title: This is the Title in Title Case +description: A short description of the page. +slug: /concepts/architecture/ +section: concepts +--- - Markdown content begins here. - ``` +Markdown content begins here. +``` - Note: The `description` value will be rendered on-page below the title, and it will also be used for description tags in the HTML head. +Note: The `description` value will be rendered on-page below the title, and it will also be used for description tags in the HTML head. -2. Don't include the page title or description in the Markdown section. That is, don't begin the Markdown content with `# This is the Title in Title Case`. Just write as if that were already there. -3. start your heading levels with `h2`, so `## My heading` -4. Internal links to other docs pages should be: +1. Don't include the page title or description in the Markdown section. That is, don't begin the Markdown content with `# This is the Title in Title Case`. Just write as if that were already there. +2. start your heading levels with `h2`, so `## My heading` +3. Internal links to other docs pages should be: - to a absolute URL without the host, that looks like `/concepts/terminology/` with slashes on the beginning and end, and with no `.md` or `.html` at the end (before the last slash). - when linking from external repos, to the _full absolute URL_, such as `https://docs.oceanprotocol.com/hello/you-are-awesome/` -5. no TOC please, this will be generated automatically from all headings -6. for images and media, you can keep them in the original repo. Images will be automatically grabbed by the docs site on querying. When doing that, docs site will generate all sorts of image sizes to handle proper responsive images, so no need to keep an eye on image dimensions or file sizes +4. no TOC please, this will be generated automatically from all headings +5. for images and media, you can keep them in the original repo. Images will be automatically grabbed by the docs site on querying. When doing that, docs site will generate all sorts of image sizes to handle proper responsive images, so no need to keep an eye on image dimensions or file sizes **Have a look at [docs.oceanprotocol.com/test/](https://docs.oceanprotocol.com/test/) to see what content elements can be used in all Markdown files included in docs site.** @@ -207,9 +207,11 @@ For more information about stylistic issues, take a look at the section in the t #### TypeDoc specs -Reference pages based on generated `json` file from TypeDoc. +Reference pages based on generated `json` file from TypeDoc. TypeScript-based projects are included as git submodules under `./external`. -_Coming soon... see [#45](https://github.com/oceanprotocol/docs/issues/45)_ +On site build, TypeDoc will automatically generate the required `json` spec file into `./data/squid-js.json` and create pages from it. The data from these json files is then used by the TypeDoc template. + +To update the specs to the most recent version, the `./external/squid-js` submodule needs to be manuall updated. That's it, on next site build a new spec will be used. ## Development diff --git a/content/references/introduction.md b/content/references/introduction.md index 0d43324f..6d818d9a 100644 --- a/content/references/introduction.md +++ b/content/references/introduction.md @@ -12,7 +12,7 @@ The following components expose a consumable REST API which are documented on th Those are sourced from their respective Swagger specs. On this site you can't execute the documented API calls yet. If you need this, you can run a component's local Swagger UI as outlined in the repository instructions on GitHub. -References of all the functions and methods used in our libraries are not yet integrated here. For now, have a look at the respective repos for more infos: +References of all the functions and methods used in our libraries: diff --git a/content/references/squid-py.md b/content/references/squid-py.md index 266104dd..ca6c773b 100644 --- a/content/references/squid-py.md +++ b/content/references/squid-py.md @@ -1,6 +1,5 @@ --- -title: API Reference -description: +title: Squid-py API Reference --- ReadTheDocs hosts the [squid-py API Reference Docs](https://squid-py.readthedocs.io/en/latest/). diff --git a/data/repositories.yml b/data/repositories.yml index 76b40b6b..58e3199b 100644 --- a/data/repositories.yml +++ b/data/repositories.yml @@ -20,7 +20,13 @@ - group: Libraries items: - name: squid-js + links: + - name: API reference + url: /references/squid-js/ - name: squid-py + links: + - name: API reference + url: /references/squid-py/ - name: squid-java - group: OceanDB diff --git a/data/sidebars/references.yml b/data/sidebars/references.yml index 10a4448f..87de05af 100644 --- a/data/sidebars/references.yml +++ b/data/sidebars/references.yml @@ -3,16 +3,6 @@ - title: API References link: /references/introduction/ -# - group: squid-js -# items: -# - title: API Reference -# link: /references/squid-js/ - -- group: squid-py - items: - - title: API Reference - link: /references/squid-py/ - - group: aquarius items: - title: API Reference @@ -22,3 +12,13 @@ items: - title: API Reference link: /references/brizo/ + +- group: squid-js + items: + - title: API Reference + link: /references/squid-js/ + +- group: squid-py + items: + - title: API Reference + link: /references/squid-py/ diff --git a/external/dev-ocean b/external/dev-ocean index 79786228..bb4bd802 160000 --- a/external/dev-ocean +++ b/external/dev-ocean @@ -1 +1 @@ -Subproject commit 79786228757f8e39092d9dd5adf387097f0b02f6 +Subproject commit bb4bd802b318f68b2f4a9568bbb0d25bde7e24a9 diff --git a/external/squid-js b/external/squid-js new file mode 160000 index 00000000..89cd8fd7 --- /dev/null +++ b/external/squid-js @@ -0,0 +1 @@ +Subproject commit 89cd8fd744ad5c61273b5da595e33f3b80c29c4f diff --git a/gatsby-node.js b/gatsby-node.js index 7850bee1..6684049a 100755 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -153,7 +153,7 @@ exports.createPages = ({ graphql, actions }) => { // Create pages from swagger json files // const apiSwaggerTemplate = path.resolve( - './src/templates/ApiSwagger.jsx' + './src/templates/Swagger/index.jsx' ) const petStoreSlug = '/references/petstore/' @@ -197,6 +197,58 @@ exports.createPages = ({ graphql, actions }) => { } }) + // + // Create pages from TypeDoc json files + // + const typeDocSpecs = ['./data/squid-js.json'] + const typedocTemplate = path.resolve( + './src/templates/Typedoc/index.jsx' + ) + + typeDocSpecs.forEach(spec => { + const typedoc = require(spec) // eslint-disable-line + + const name = path + .basename(spec) + .split('.json') + .join('') + + const slug = `/references/${name}/` + + createPage({ + path: slug, + component: typedocTemplate, + context: { + slug, + typedoc, + // TODO: defining these classes for inclusion + // needs to be handled somewhere else to keep + // it generic for all TypeDoc specs + classes: [ + 'ocean/Ocean', + 'ocean/OceanAccounts', + 'ocean/OceanAssets', + 'ocean/OceanAgreements', + 'ocean/Account', + 'ocean/DID', + 'ocean/ServiceAgreements/ServiceAgreement', + 'ddo/DDO', + 'ddo/Service', + 'aquarius/AquariusProvider', + 'aquarius/Aquarius', + 'aquarius/query/SearchQuery', + 'brizo/BrizoProvider', + 'brizo/Brizo', + 'keeper/Keeper', + 'keeper/Web3Provider', + 'secretstore/SecretStoreProvider', + 'models/Config', + 'models/Balance' + ] + } + }) + }) + // // create redirects // diff --git a/package.json b/package.json index c9ae8d3a..257b26fb 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "author": "Ocean Protocol ", "license": "Apache-2.0", "scripts": { - "build": "gatsby build", - "start": "gatsby develop", + "build": "npm run typedoc && gatsby build", + "start": "npm run typedoc && gatsby develop", "ssr": "npm run build && serve -s public/", "format:js": "prettier --write '**/*.{js,jsx}'", "format:css": "prettier-stylelint --write --quiet 'src/**/*.{css,scss}'", @@ -19,7 +19,8 @@ "lint:yml": "prettier '**/*.{yml,yaml}' --list-different", "lint": "run-p --continue-on-error lint:js lint:css lint:md lint:yml", "test": "npm run lint", - "deploy": "./scripts/deploy.sh" + "deploy": "./scripts/deploy.sh", + "typedoc": "node ./scripts/typedoc.js" }, "dependencies": { "@oceanprotocol/art": "^2.1.0", @@ -33,7 +34,7 @@ "gatsby-plugin-offline": "^2.0.19", "gatsby-plugin-react-helmet": "^3.0.4", "gatsby-plugin-sass": "^2.0.7", - "gatsby-plugin-sharp": "^2.0.15", + "gatsby-plugin-sharp": "^2.0.17", "gatsby-plugin-sitemap": "^2.0.3", "gatsby-plugin-svgr": "^2.0.1", "gatsby-remark-autolink-headers": "^2.0.12", @@ -57,10 +58,12 @@ "react": "^16.6.3", "react-dom": "^16.6.3", "react-helmet": "^5.2.0", + "react-scrollspy": "^3.3.5", "rehype-react": "^3.1.0", "remark": "^10.0.1", "remark-react": "^5.0.0", "slugify": "^1.3.4", + "smoothscroll-polyfill": "^0.4.3", "swagger-client": "^3.8.22" }, "devDependencies": { @@ -72,12 +75,14 @@ "eslint-plugin-prettier": "^3.0.0", "markdownlint-cli": "^0.13.0", "npm-run-all": "^4.1.5", + "ora": "^3.0.0", "prettier": "^1.15.3", "prettier-stylelint": "^0.4.2", "stylelint": "^9.9.0", "stylelint-config-bigchaindb": "^1.2.1", "stylelint-config-css-modules": "^1.3.0", - "stylelint-config-standard": "^18.2.0" + "stylelint-config-standard": "^18.2.0", + "typedoc": "^0.14.2" }, "repository": { "type": "git", diff --git a/scripts/typedoc.js b/scripts/typedoc.js new file mode 100644 index 00000000..1953ddd8 --- /dev/null +++ b/scripts/typedoc.js @@ -0,0 +1,66 @@ +#!/usr/bin/env node + +/* eslint-disable no-console, security/detect-child-process, security/detect-non-literal-fs-filename */ + +const fs = require('fs') +const typedoc = require('typedoc') +const typescript = require('typescript') +const ora = require('ora') +const squidJsPackage = require('../external/squid-js/package.json') +const { exec } = require('child_process') + +const { description, version } = squidJsPackage + +// Setup our paths, relative to project root +const outPath = './data/squid-js.json' +const files = ['./external/squid-js/src/squid.ts'] + +// specifically point to tsconfig, otherwise TypeDoc fails +const config = typescript.findConfigFile( + './external/squid-js', + typescript.sys.fileExists +) + +// npm install for squid-js +const spinnerNpm = ora('Installing submodule dependencies...').start() +const install = exec( + 'cd ./external/squid-js && npm i && git checkout package-lock.json' +) + +install.on('exit', () => { + spinnerNpm.succeed('Installed submodule dependencies.') + generateJson() +}) + +const generateJson = () => { + const spinnerTypedoc = ora('Generating TypeDoc json...').start() + + // Setup our TypeDoc app + const app = new typedoc.Application({ + tsconfig: config + }) + + const src = app.expandInputFiles(files) + const project = app.convert(src) + + // Generate the JSON file + app.generateJson(project, outPath) + + // Parse and modify json output + const jsonOrig = JSON.parse(fs.readFileSync(outPath, 'utf8')) + + const jsonFinal = { + info: { + title: 'Squid-js', + description, + version, + sourceUrl: + 'https://github.com/oceanprotocol/squid-js/tree/develop/src/' + }, + ...jsonOrig + } + + fs.writeFileSync(outPath, JSON.stringify(jsonFinal, null, 4)) + + spinnerTypedoc.succeed('Generated TypeDoc json.') +} diff --git a/src/components/Scroll.jsx b/src/components/Scroll.jsx new file mode 100644 index 00000000..06fbfae4 --- /dev/null +++ b/src/components/Scroll.jsx @@ -0,0 +1,85 @@ +import smoothscroll from 'smoothscroll-polyfill' +import React from 'react' +import PropTypes from 'prop-types' + +export default class TocScroll extends React.Component { + static propTypes = { + type: PropTypes.string, + element: PropTypes.string, + offset: PropTypes.number, + timeout: PropTypes.number, + children: PropTypes.node.isRequired + } + + componentDidMount() { + smoothscroll.polyfill() + } + + handleClick = e => { + e.preventDefault() + + let elem = 0 + let scroll = true + const { type, element, offset, timeout } = this.props + + if (type && element) { + switch (type) { + case 'class': + // eslint-disable-next-line prefer-destructuring + elem = document.getElementsByClassName(element)[0] + scroll = !!elem + break + case 'id': + elem = document.getElementById(element) + scroll = !!elem + break + default: + } + } + + if (scroll) { + this.scrollTo(elem, offset, timeout) + + // update browser url + if (typeof window !== 'undefined') { + window.history.pushState({}, null, `#${element}`) + } + } else { + console.log(`Element not found: ${element}`) // eslint-disable-line + } + } + + scrollTo(element, offSet = 0, timeout = null) { + const elemPos = element + ? element.getBoundingClientRect().top + window.pageYOffset + : 0 + + if (timeout) { + setTimeout(() => { + window.scroll({ + top: elemPos + offSet, + left: 0, + behavior: 'smooth' + }) + }, timeout) + } else { + window.scroll({ + top: elemPos + offSet, + left: 0, + behavior: 'smooth' + }) + } + } + + render() { + return ( + + {this.props.children} + + ) + } +} diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 9ef37a36..770a708d 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -34,13 +34,10 @@ SidebarLink.propTypes = { linkClasses: PropTypes.string } -const SidebarList = ({ items, location, toc, tableOfContents }) => ( +const SidebarList = ({ items, location, toc, tocComponent }) => (
{toc ? ( -
+
{tocComponent}
) : (
    {items.map((item, j) => ( @@ -65,7 +62,7 @@ SidebarList.propTypes = { items: PropTypes.array.isRequired, location: PropTypes.object.isRequired, toc: PropTypes.bool, - tableOfContents: PropTypes.string + tocComponent: PropTypes.object } const SidebarGroupTitle = ({ group }) => ( @@ -104,19 +101,13 @@ export default class Sidebar extends Component { location: PropTypes.object.isRequired, collapsed: PropTypes.bool, toc: PropTypes.bool, - tableOfContents: PropTypes.string + tocComponent: PropTypes.element } static defaultProps = { location: { pathname: '/' } } render() { - const { - sidebar, - location, - collapsed, - toc, - tableOfContents - } = this.props + const { sidebar, location, collapsed, toc, tocComponent } = this.props if (sidebar) { try { @@ -143,7 +134,7 @@ export default class Sidebar extends Component { group={group} location={location} toc={toc} - tableOfContents={tableOfContents} + tocComponent={tocComponent} /> ) : ( diff --git a/src/components/Sidebar.module.scss b/src/components/Sidebar.module.scss index 4fb09dc0..3b2ea2f4 100644 --- a/src/components/Sidebar.module.scss +++ b/src/components/Sidebar.module.scss @@ -16,8 +16,8 @@ } &::-webkit-scrollbar { - width: 6px; - height: 6px; + width: 8px; + height: 8px; } &::-webkit-scrollbar-thumb { @@ -79,22 +79,50 @@ &:focus { transform: none; color: $brand-purple; + + :global(.setup) & { + color: $brand-blue; + } + + :global(.tutorials) & { + color: $orange; + } + + :global(.references) & { + color: $green; + } } } .toc { ul { padding-left: 0; + margin: 0; ul { - padding-left: $spacer / 2; - margin: 0; + border-left: 1px solid $brand-grey-lighter; + margin-left: $spacer; + font-size: $font-size-small; } } li { margin: 0; } + + a { + padding-top: $spacer / 12; + padding-bottom: $spacer / 12; + } + + code { + background: none; + color: inherit; + } + + [data-deprecated='true'] code { + opacity: .5; + } } .active { @@ -111,4 +139,16 @@ color: $orange; border-left-color: $orange; } + + :global(.references) & { + color: $green; + border-left-color: $green; + } +} + +.scrollspyActive { + > a { + color: $green; + border-left-color: $green; + } } diff --git a/src/styles/global.scss b/src/styles/global.scss index 30455358..cc372c02 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -283,11 +283,11 @@ samp { } :not(pre) > code { - background: darken($brand-white, 5%) !important; - color: $brand-grey-dark !important; + background: darken($brand-white, 5%); + color: $brand-grey-dark; display: inline-block; - padding-left: .3rem !important; - padding-right: .3rem !important; + padding-left: .3rem; + padding-right: .3rem; } pre { diff --git a/src/templates/ApiSwagger.jsx b/src/templates/ApiSwagger.jsx deleted file mode 100644 index 1635122e..00000000 --- a/src/templates/ApiSwagger.jsx +++ /dev/null @@ -1,321 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { graphql } from 'gatsby' -import Helmet from 'react-helmet' -import slugify from 'slugify' -import Layout from '../components/Layout' -import Content from '../components/Content' -import HeaderSection from '../components/HeaderSection' -import Sidebar from '../components/Sidebar' -import DocHeader from '../components/DocHeader' -import DocFooter from '../components/DocFooter' -import SEO from '../components/Seo' -import stylesDoc from './Doc.module.scss' -import styles from './ApiSwagger.module.scss' - -const cleanKey = key => { - let keyCleaned = key - - if (key.includes('aquarius')) { - keyCleaned = key.replace(/\/api\/v1\/aquarius/gi, '') - } - - if (key.includes('brizo')) { - keyCleaned = key.replace(/\/api\/v1\/brizo/gi, '') - } - - return keyCleaned -} - -const toc = api => { - const items = Object.keys(api.paths).map( - key => `
  • - ${cleanKey( - key - )} -
  • ` - ) - - return `
      ${items}
    ` -} - -const SwaggerMeta = ({ contact, license }) => ( - -) - -SwaggerMeta.propTypes = { - contact: PropTypes.object, - license: PropTypes.object -} - -const ParameterExample = ({ properties }) => ( - // - // HEADS UP! - // - // Constructing the example request body here based on the defined properties - // where `key` is the name of the property, and `properties[key].example` is - // the value for it. - // - // Making prism.js pick up on the strings only output didn't work out so well - // so the spans and classes this plugin would add are added manually here. Since we - // include the prism css file globally this is picked up by that. - // - // But this can only work if all keys and values are manually constructed here, which - // is almost impossible to do for deep nested objects or arrays as example value. Running - // that code through `JSON.stringify` won't syntax highlight that part of the code. - // -
    -        
    -            {'{'}
    -            {properties &&
    -                Object.keys(properties).map(key => (
    -                    
    - {` "${key}"`} - {`: `} - {properties[key].type === 'string' && ( - {`"${ - properties[key].example - }"`} - )} - {(properties[key].type === 'integer' || - properties[key].type === 'number') && ( - - {`${properties[key].example}`} - - )} - {(properties[key].type === 'array' || - properties[key].type === 'object') && - JSON.stringify(properties[key].example, null, 2)} - , -
    - ))} - {'}'} -
    -
    -) - -ParameterExample.propTypes = { - properties: PropTypes.object -} - -const Parameters = ({ parameters }) => ( - <> -

    Parameters

    - - {parameters.map(parameter => { - const { name, type, required, description, schema } = parameter - - return ( -
    -
    - {name} - {required && ( - - * - - )} - {type} -
    - -

    {description}

    - - {schema && ( - - )} -
    - ) - })} - -) - -const Responses = ({ responses }) => ( - <> -

    Responses

    - {Object.keys(responses).map(key => ( -
    - {key} {responses[key].description} -
    - ))} - -) - -const Method = ({ keyName, value }) => { - const { summary, description, parameters, responses } = value - - return ( -
    -

    - {keyName} -

    - -

    {summary}

    - - {description &&

    {description}

    } - - {/* - {consumes && - consumes.map((item, i) => ( -
    - {item} -
    - ))} - */} - - {parameters && parameters.length && ( - - )} - {responses && Object.keys(responses).length !== 0 && ( - - )} -
    - ) -} - -Method.propTypes = { - keyName: PropTypes.string, - value: PropTypes.object -} - -const Paths = ({ paths }) => - Object.entries(paths).map(([key, value]) => ( -
    -

    - {cleanKey(key)} -

    - - {Object.entries(value).map(([key, value]) => ( - - ))} -
    - )) - -const BasePath = ({ host, basePath }) => ( -
    -

    Base Path

    - - {host} - {basePath} - -
    -) - -BasePath.propTypes = { - host: PropTypes.string, - basePath: PropTypes.string -} - -export default class ApiSwaggerTemplate extends Component { - static propTypes = { - data: PropTypes.object.isRequired, - location: PropTypes.object.isRequired, - pageContext: PropTypes.object.isRequired - } - - render() { - const { location, data, pageContext } = this.props - const sections = data.allSectionsYaml.edges - const { api } = pageContext - const { host, basePath, info, paths } = api - const { title, description, version, license, contact } = info - - // output section title as defined in sections.yml - const sectionTitle = sections.map(({ node }) => { - // compare section against section title from sections.yml - if (node.title.toLowerCase().includes('references')) { - return node.title - } - }) - - return ( - <> - - - - - - - - - - -
    - -
    - - {version} - - } - /> - - {(contact || license) && ( - - )} - - {(host || basePath) && ( - - )} - - - - -
    -
    -
    -
    - - ) - } -} - -export const apiSwaggerQuery = graphql` - query { - allSectionsYaml { - edges { - node { - title - description - link - } - } - } - } -` diff --git a/src/templates/Doc.jsx b/src/templates/Doc.jsx index 37bfbd68..d92cde7e 100644 --- a/src/templates/Doc.jsx +++ b/src/templates/Doc.jsx @@ -26,8 +26,8 @@ const DocMain = ({ title, description, tableOfContents, post, single }) => ( DocMain.propTypes = { title: PropTypes.string.isRequired, - description: PropTypes.string.isRequired, - tableOfContents: PropTypes.string, + description: PropTypes.string, + tableOfContents: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), post: PropTypes.object.isRequired, single: PropTypes.bool } @@ -38,22 +38,25 @@ export default class DocTemplate extends Component { location: PropTypes.object.isRequired } + // output section title as defined in sections.yml + sectionTitle = this.props.data.allSectionsYaml.edges.map(({ node }) => { + // compare section against section title from sections.yml + if ( + node.title + .toLowerCase() + .includes(this.props.data.markdownRemark.fields.section) + ) { + return node.title + } + }) + render() { const { location } = this.props const post = this.props.data.markdownRemark - const sections = this.props.data.allSectionsYaml.edges const { section, slug } = post.fields const { title, description } = post.frontmatter const { tableOfContents } = post - // output section title as defined in sections.yml - const sectionTitle = sections.map(({ node }) => { - // compare section against section title from sections.yml - if (node.title.toLowerCase().includes(section)) { - return node.title - } - }) - const isApiSection = location.pathname.includes('/references/') return ( @@ -70,7 +73,9 @@ export default class DocTemplate extends Component { /> - + {section ? ( diff --git a/src/templates/Doc.module.scss b/src/templates/Doc.module.scss index 47300dcb..6e8e7a9e 100644 --- a/src/templates/Doc.module.scss +++ b/src/templates/Doc.module.scss @@ -14,6 +14,7 @@ @media (min-width: $break-point--medium) { width: 27%; margin-bottom: 0; + margin-top: 0; order: 1; + .main { @@ -47,3 +48,9 @@ max-width: 73%; margin: auto; } + +.version { + font-size: $font-size-base; + font-family: $font-family-monospace; + color: $brand-grey-light; +} diff --git a/src/templates/Swagger/Paths.jsx b/src/templates/Swagger/Paths.jsx new file mode 100644 index 00000000..b4583de5 --- /dev/null +++ b/src/templates/Swagger/Paths.jsx @@ -0,0 +1,151 @@ +import React from 'react' +import PropTypes from 'prop-types' +import slugify from 'slugify' +import { cleanPathKey } from './utils' +import styles from './Paths.module.scss' + +const ParameterExample = ({ properties }) => ( + // + // HEADS UP! + // + // Constructing the example request body here based on the defined properties + // where `key` is the name of the property, and `properties[key].example` is + // the value for it. + // + // Making prism.js pick up on the strings only output didn't work out so well + // so the spans and classes this plugin would add are added manually here. Since we + // include the prism css file globally this is picked up by that. + // + // But this can only work if all keys and values are manually constructed here, which + // is almost impossible to do for deep nested objects or arrays as example value. Running + // that code through `JSON.stringify` won't syntax highlight that part of the code. + // +
    +        
    +            {'{'}
    +            {properties &&
    +                Object.keys(properties).map(key => (
    +                    
    + {` "${key}"`} + {`: `} + {properties[key].type === 'string' && ( + {`"${ + properties[key].example + }"`} + )} + {(properties[key].type === 'integer' || + properties[key].type === 'number') && ( + + {`${properties[key].example}`} + + )} + {(properties[key].type === 'array' || + properties[key].type === 'object') && + JSON.stringify(properties[key].example, null, 2)} + , +
    + ))} + {'}'} +
    +
    +) + +ParameterExample.propTypes = { + properties: PropTypes.object +} + +const Parameters = ({ parameters }) => ( + <> +

    Parameters

    + + {parameters.map(parameter => { + const { name, type, required, description, schema } = parameter + + return ( +
    +
    + {name} + {required && ( + + * + + )} + {type} +
    + +

    {description}

    + + {schema && ( + + )} +
    + ) + })} + +) + +const Responses = ({ responses }) => ( + <> +

    Responses

    + {Object.keys(responses).map(key => ( +
    + {key} {responses[key].description} +
    + ))} + +) + +const Method = ({ keyName, value }) => { + const { summary, description, parameters, responses } = value + + return ( +
    +

    + {keyName} +

    + +

    {summary}

    + + {description &&

    {description}

    } + + {/* + {consumes && + consumes.map((item, i) => ( +
    + {item} +
    + ))} + */} + + {parameters && parameters.length && ( + + )} + {responses && Object.keys(responses).length !== 0 && ( + + )} +
    + ) +} + +Method.propTypes = { + keyName: PropTypes.string, + value: PropTypes.object +} + +const Paths = ({ paths }) => + Object.entries(paths).map(([key, value]) => ( +
    +

    + {cleanPathKey(key)} +

    + + {Object.entries(value).map(([key, value]) => ( + + ))} +
    + )) + +export default Paths diff --git a/src/templates/ApiSwagger.module.scss b/src/templates/Swagger/Paths.module.scss similarity index 65% rename from src/templates/ApiSwagger.module.scss rename to src/templates/Swagger/Paths.module.scss index a029bcc6..9c48a7c4 100644 --- a/src/templates/ApiSwagger.module.scss +++ b/src/templates/Swagger/Paths.module.scss @@ -1,52 +1,5 @@ @import 'variables'; -.version { - font-size: $font-size-base; - font-family: $font-family-monospace; - color: $brand-grey-light; -} - -.swaggerMeta { - margin-top: -($spacer); - padding: 0; - font-size: $font-size-small; - - li { - display: inline-block; - margin-left: $spacer; - - &:first-child { - margin-left: 0; - } - - &:before { - display: none; - } - } -} - -.basePath { - margin-top: $spacer; - - h2 { - font-size: $font-size-h3; - border-bottom: 1px solid $brand-grey-lighter; - padding-bottom: $spacer / 2; - margin-top: $spacer * 2; - margin-bottom: $spacer; - } - - span { - color: $brand-grey-light; - } - - code { - // stylelint-disable-next-line - font-size: $font-size-large !important; - font-weight: $font-weight-bold; - } -} - .pathName { font-size: $font-size-h3; border-bottom: 1px solid $brand-grey-lighter; diff --git a/src/templates/Swagger/Toc.jsx b/src/templates/Swagger/Toc.jsx new file mode 100644 index 00000000..c7e24cd4 --- /dev/null +++ b/src/templates/Swagger/Toc.jsx @@ -0,0 +1,43 @@ +import React from 'react' +import PropTypes from 'prop-types' +import slugify from 'slugify' +import Scrollspy from 'react-scrollspy' +import Scroll from '../../components/Scroll' +import { cleanPathKey } from './utils' +import stylesSidebar from '../../components/Sidebar.module.scss' + +const Toc = ({ data }) => { + let Ids = [] + + const items = Object.keys(data.paths).map(key => { + Ids.push(slugify(cleanPathKey(key))) + + return ( +
  • + + {cleanPathKey(key)} + +
  • + ) + }) + + return ( + + {items} + + ) +} + +Toc.propTypes = { + data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]) +} + +export default Toc diff --git a/src/templates/Swagger/index.jsx b/src/templates/Swagger/index.jsx new file mode 100644 index 00000000..4cd75ad5 --- /dev/null +++ b/src/templates/Swagger/index.jsx @@ -0,0 +1,152 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { graphql } from 'gatsby' +import Helmet from 'react-helmet' + +import Layout from '../../components/Layout' +import Content from '../../components/Content' +import HeaderSection from '../../components/HeaderSection' +import Sidebar from '../../components/Sidebar' +import DocHeader from '../../components/DocHeader' +import DocFooter from '../../components/DocFooter' +import SEO from '../../components/Seo' + +import Toc from './Toc' +import Paths from './Paths' + +import stylesDoc from '../Doc.module.scss' +import styles from './index.module.scss' + +const SwaggerMeta = ({ contact, license }) => ( + +) + +SwaggerMeta.propTypes = { + contact: PropTypes.object, + license: PropTypes.object +} + +const BasePath = ({ host, basePath }) => ( +
    +

    Base Path

    + + {host} + {basePath} + +
    +) + +BasePath.propTypes = { + host: PropTypes.string, + basePath: PropTypes.string +} + +export default class ApiSwaggerTemplate extends Component { + static propTypes = { + data: PropTypes.object.isRequired, + location: PropTypes.object.isRequired, + pageContext: PropTypes.object.isRequired + } + + // output section title as defined in sections.yml + sectionTitle = this.props.data.allSectionsYaml.edges.map(({ node }) => { + // compare section against section title from sections.yml + if (node.title.toLowerCase().includes('references')) { + return node.title + } + }) + + render() { + const { location, pageContext } = this.props + const { api } = pageContext + const { host, basePath, info, paths } = api + const { title, description, version, license, contact } = info + + return ( + <> + + + + + + + + + + +
    + +
    + + {version} + + } + /> + + {(contact || license) && ( + + )} + + {(host || basePath) && ( + + )} + + + + +
    +
    +
    +
    + + ) + } +} + +export const apiSwaggerQuery = graphql` + query { + allSectionsYaml { + edges { + node { + title + description + link + } + } + } + } +` diff --git a/src/templates/Swagger/index.module.scss b/src/templates/Swagger/index.module.scss new file mode 100644 index 00000000..5b40c0d6 --- /dev/null +++ b/src/templates/Swagger/index.module.scss @@ -0,0 +1,42 @@ +@import 'variables'; + +.swaggerMeta { + margin-top: -($spacer); + padding: 0; + font-size: $font-size-small; + + li { + display: inline-block; + margin-left: $spacer; + + &:first-child { + margin-left: 0; + } + + &:before { + display: none; + } + } +} + +.basePath { + margin-top: $spacer; + + h2 { + font-size: $font-size-h3; + border-bottom: 1px solid $brand-grey-lighter; + padding-bottom: $spacer / 2; + margin-top: $spacer * 2; + margin-bottom: $spacer; + } + + span { + color: $brand-grey-light; + } + + code { + // stylelint-disable-next-line + font-size: $font-size-large !important; + font-weight: $font-weight-bold; + } +} diff --git a/src/templates/Swagger/utils.js b/src/templates/Swagger/utils.js new file mode 100644 index 00000000..5f431dd3 --- /dev/null +++ b/src/templates/Swagger/utils.js @@ -0,0 +1,13 @@ +export const cleanPathKey = key => { + let keyCleaned = key + + if (key.includes('aquarius')) { + keyCleaned = key.replace(/\/api\/v1\/aquarius/gi, '') + } + + if (key.includes('brizo')) { + keyCleaned = key.replace(/\/api\/v1\/brizo/gi, '') + } + + return keyCleaned +} diff --git a/src/templates/Typedoc/Entities.jsx b/src/templates/Typedoc/Entities.jsx new file mode 100644 index 00000000..d2b0da69 --- /dev/null +++ b/src/templates/Typedoc/Entities.jsx @@ -0,0 +1,252 @@ +import React from 'react' +import PropTypes from 'prop-types' +import slugify from 'slugify' +import Scroll from '../../components/Scroll' +import styles from './Entities.module.scss' +import { filterByKindOfProperty } from './utils' + +const Type = ({ type }) => { + let isArray = false + if (type.type === 'array') { + isArray = true + type = type.elementType + } + const { name, type: typeOfType, typeArguments, id } = type + const isInternal = typeOfType === 'reference' && id + + return ( +
    + + {isInternal && ( + + {type.name} + + )} + {!isInternal && {type.name}} + + + {typeArguments && ( + + < + + {typeArguments.map((typeArgument, i) => ( + + {i !== 0 && ( + + ,{' '} + + )} + + + ))} + + > + + )} + + {isArray && []} +
    + ) +} + +Type.propTypes = { + type: PropTypes.object.isRequired +} + +const PropertyDetails = ({ property }) => { + const { type } = property + return ( +
    + +
    + ) +} + +PropertyDetails.propTypes = { + property: PropTypes.object +} + +const MethodDetails = ({ property }) => { + const signature = property.signatures[0] + const { parameters, type } = signature + return ( + <> + {parameters && parameters.length && ( +
    +

    Parameters

    + + {parameters.map(parameter => { + const { name, type, flags, comment } = parameter + const { isOptional } = flags + const description = + comment && (comment.text || comment.shortText) + + return ( +
    +
    + {name} + {isOptional && ( + + ? + + )} +
    + + +

    {description}

    +
    + ) + })} +
    + )} + + {type && ( +
    +

    Returns

    + + +
    + )} + + ) +} + +MethodDetails.propTypes = { + property: PropTypes.object +} + +const PropertyWrapper = ({ property, sourceUrl, parentAnchor }) => { + const { + name, + kindString, + flags, + signatures, + sources, + decorators + } = property + const { isPublic, isStatic } = flags + const signature = signatures && signatures[0] + const comment = (signature && signature.comment) || property.comment + const { fileName, line } = sources[0] + const deprecation = (decorators || []).filter( + ({ name }) => name === 'deprecated' + )[0] // Assuming deprecated annotation + let deprecatedUse, deprecatedSlug + if (deprecation) { + deprecatedUse = deprecation.arguments.alternative.replace(/('|")/g, '') + deprecatedSlug = slugify(deprecatedUse.replace('.', '-')) + } + + const sourceLink = `${sourceUrl}${fileName}#L${line}` + + return ( +
    +

    {name}

    + +
    + {kindString} +
    + + {isStatic &&
    static
    } + {!isPublic && ( +
    + private +
    + )} + + {comment && !deprecation && ( +
    + {comment.text || comment.shortText} +
    + )} + + {deprecation && ( +
    + Deprecated: use{' '} + + + {deprecatedUse} + + {' '} + instead +
    + )} + + {!deprecation && + (() => { + switch (kindString) { + case 'Method': + return + case 'Property': + return + } + })()} + + {fileName && ( + + {`${fileName}#L${line}`} + + )} +
    + ) +} + +PropertyWrapper.propTypes = { + property: PropTypes.object, + sourceUrl: PropTypes.string, + parentAnchor: PropTypes.string +} + +const Entities = ({ entities, sourceUrl }) => + entities.map(({ name, comment, children }) => ( +
    +

    + {name} +

    + + {comment && ( +
    + {comment.text || comment.shortText} +
    + )} + + {children.filter(filterByKindOfProperty).map(property => ( + + ))} +
    + )) + +Entities.propTypes = { + entities: PropTypes.array.isRequired, + sourceUrl: PropTypes.string +} + +export default Entities diff --git a/src/templates/Typedoc/Entities.module.scss b/src/templates/Typedoc/Entities.module.scss new file mode 100644 index 00000000..3455e215 --- /dev/null +++ b/src/templates/Typedoc/Entities.module.scss @@ -0,0 +1,173 @@ +@import 'variables'; + +.entityName { + font-size: $font-size-h2; + border-bottom: 1px solid $brand-grey-lighter; + padding-bottom: $spacer / 2; + margin-top: $spacer * 2; + margin-bottom: $spacer; + + code { + // stylelint-disable declaration-no-important + background: none !important; + padding: 0 !important; + // stylelint-enable declaration-no-important + } +} + +.entityDescription { + padding-bottom: $spacer; + white-space: pre-line; +} + +.property { + padding: $spacer / 2; + border: 1px solid $brand-grey-lighter; + margin-bottom: $spacer; + border-radius: $border-radius; + position: relative; + + &[data-deprecated='true'] { + > *:not(.deprecation) { + opacity: .5; + } + + code { + opacity: 1; + background: none; + padding: 0; + } + + .sourceLink { + display: none; + } + } +} + +.propertyName, +.propertyType, +.propertyModifier { + font-size: $font-size-base; + font-family: $font-family-monospace; + margin-bottom: $spacer / 4; + margin-top: 0; + display: inline-block; + padding: 0 $spacer / 4; + border-radius: $border-radius; + vertical-align: middle; +} + +.propertyName { + font-size: $font-size-large; + padding: 0; + margin-right: $spacer / 3; +} + +.propertyType, +.propertyModifier { + font-size: $font-size-small; + color: $brand-grey; + margin-right: $spacer / 4; +} + +.propertyType { + &[data-type='method'] { + background: rgba($green, .3); + } + + &[data-type='property'] { + background: rgba($yellow, .3); + } +} + +.propertyModifier { + background: rgba($red, .2); + + &[data-secondary] { + background: rgba($brand-blue, .2); + } +} + +.propertyDescription { + margin-bottom: $spacer; +} + +.sourceLink { + display: block; + font-size: $font-size-mini; + color: $brand-grey-light; + margin-top: $spacer / 2; + + &:hover, + &:focus { + transform: none; + } + + @media (min-width: $break-point--large) { + margin-top: 0; + position: absolute; + bottom: $spacer / 2; + right: $spacer / 2; + } +} + +.deprecation { + font-size: $font-size-small; + margin-top: $spacer / 4; + + strong { + color: $brand-grey-light; + } +} + +.type { + display: inline-block; + color: $brand-grey; + font-family: $font-family-monospace; + font-size: $font-size-small; + + a { + &:hover { + opacity: .6; + } + } +} + +.typeSymbol { + opacity: .6; +} + +.subHeading { + font-size: $font-size-base; + border-bottom: 1px solid $brand-grey-lighter; + padding-bottom: $spacer / 4; + margin-bottom: $spacer / 4; + color: $brand-grey; +} + +.parameters { + margin-top: $spacer / 1.5; + + h5, + p { + margin: 0; + } + + h5 { + font-size: $font-size-small; + margin-bottom: $spacer / 4; + margin-right: $spacer / 4; + margin-left: -(.2rem); + display: inline-block; + } + + code { + padding: .2rem; + } +} + +.parameterOptional { + font-size: $font-size-small; + vertical-align: top; + color: $brand-purple; +} diff --git a/src/templates/Typedoc/Toc.jsx b/src/templates/Typedoc/Toc.jsx new file mode 100644 index 00000000..d66d77c4 --- /dev/null +++ b/src/templates/Typedoc/Toc.jsx @@ -0,0 +1,63 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import slugify from 'slugify' +import Scrollspy from 'react-scrollspy' +import Scroll from '../../components/Scroll' +import { filterByKindOfProperty } from './utils' +import stylesSidebar from '../../components/Sidebar.module.scss' + +export default class Toc extends PureComponent { + static propTypes = { + data: PropTypes.array + } + + subItems = (children, parentName) => + children.filter(filterByKindOfProperty).map(({ name, decorators }) => { + const deprecation = (decorators || []).filter( + ({ name }) => name === 'deprecated' + )[0] // Assuming deprecated annotation + + return ( +
  • + + {name} + +
  • + ) + }) + + items = this.props.data.map(({ name, children }) => { + let subIds = [] + const parentName = name + + subIds.push( + children.filter(filterByKindOfProperty).map(({ name }) => { + return `${parentName}-${slugify(name)}` + }) + ) + + return ( +
  • + + {name} + + + {this.subItems(children, name)} + +
  • + ) + }) + + render() { + return
      {this.items}
    + } +} diff --git a/src/templates/Typedoc/index.jsx b/src/templates/Typedoc/index.jsx new file mode 100644 index 00000000..a696578f --- /dev/null +++ b/src/templates/Typedoc/index.jsx @@ -0,0 +1,109 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { graphql } from 'gatsby' +import Helmet from 'react-helmet' +import Layout from '../../components/Layout' +import Content from '../../components/Content' +import HeaderSection from '../../components/HeaderSection' +import Sidebar from '../../components/Sidebar' +import DocHeader from '../../components/DocHeader' +import SEO from '../../components/Seo' +import { cleanTypedocData } from './utils' + +import Entities from './Entities' +import Toc from './Toc' + +import stylesDoc from '../Doc.module.scss' + +export default class TypedocTemplate extends Component { + static propTypes = { + data: PropTypes.object.isRequired, + location: PropTypes.object.isRequired, + pageContext: PropTypes.object.isRequired + } + + typedocCleaned = cleanTypedocData( + this.props.pageContext.typedoc, + this.props.pageContext.classes + ) + + // output section title as defined in sections.yml + sectionTitle = this.props.data.allSectionsYaml.edges.map(({ node }) => { + // compare section against section title from sections.yml + if (node.title.toLowerCase().includes('references')) { + return node.title + } + }) + + render() { + const { location, pageContext } = this.props + const { typedoc } = pageContext + const { info } = typedoc + const { title, description, version, sourceUrl } = info + + return ( + <> + + + + + + + + + + +
    + +
    + + {version} + + } + /> + + +
    +
    +
    +
    + + ) + } +} + +export const TypedocQuery = graphql` + query { + allSectionsYaml { + edges { + node { + title + description + link + } + } + } + } +` diff --git a/src/templates/Typedoc/utils.js b/src/templates/Typedoc/utils.js new file mode 100644 index 00000000..3b2be881 --- /dev/null +++ b/src/templates/Typedoc/utils.js @@ -0,0 +1,34 @@ +export const cleanTypedocData = (data, useClasses) => { + const nodes = data.children + + const cleanData = nodes + .map(node => ({ + ...node, + name: node.name.replace(/"/g, ''), + child: node.children && node.children[0] + })) + .filter(({ name }) => (useClasses || []).includes(name)) + .sort((a, b) => useClasses.indexOf(a.name) - useClasses.indexOf(b.name)) + .map(({ child }) => child) + .map(node => ({ + ...node, + children: node.children.sort((a, b) => a.id - b.id) + })) + + return cleanData +} + +// more kinds: 'Property', 'Class' +const showKindOfProperty = { + Method: { onlyPublic: true }, + Property: { onlyPublic: true } +} + +export const filterByKindOfProperty = ({ kindString, flags }) => { + const config = showKindOfProperty[kindString] + if (!config) return + + if (config.onlyPublic && !flags.isPublic) return + + return true +}