import { Tabs, Tab } from '@hashicorp/nextjs-scripts/lib/providers/docs' import EnterpriseAlertBase from '@hashicorp/react-enterprise-alert' /** * ConfigEntryReference renders the reference docs for a config entry. * It creates two tabs, one for HCL docs and one for Kubernetes docs. * * @param {array} keys Array of objects, that describe all * keys that can be set for this config entry. * @param {boolean} topLevel Indicates this is a reference block that contains * the top level keys vs a reference block that documents * nested keys and that is separated out for clarity. * * The objects in the keys array support the following keys: * - name : the name of the HCL key, e.g. Name, Listener. This case sensitive. * - description : the description of the key. If this key has different descriptions * for HCL vs. Kube YAML then description can be an object: * description: { * hcl: 'HCL description', * yaml: 'YAML description' * } * - hcl : a boolean to indicate if this key should be shown in the HCL * documentation. Defaults to true. * - yaml : a boolean to indicate if this key should be shown in the YAML * documentation. Defaults to true. * - enterprise : a boolean to indicate if this key is Consul Enterprise * only. Defaults to false. * - children : accepts an array of keys that must be set under this key. * The schema for these keys is the same as the top level keys. * - type : the type and default of this key, e.g. string: "default". */ export default function ConfigEntryReference({ keys, topLevel = true }) { // Kube needs to have its non-top-level keys nested under a "spec" key. const kubeKeys = topLevel ? toKubeKeys(keys) : keys return ( {renderKeys(keys, true)} {renderKeys(kubeKeys, false)} ) } /** * Renders keys as HTML. It works recursively through all keys. * @param {array} keys * @param {boolean} isHCLTab * @returns {JSX.Element|null} */ function renderKeys(keys, isHCLTab) { if (!keys) return null return
    {keys.map((key) => renderKey(key, isHCLTab))}
} /** * Renders a single key as its HTML element. * * @param {object} key * @param {boolean} isHCLTab * @returns {JSX.Element|null} */ function renderKey(key, isHCLTab) { if (!key.name) return null if (isHCLTab && key.hcl === false) return null if (!isHCLTab && key.yaml === false) return null const keyName = isHCLTab ? key.name : toYAMLKeyName(key.name) let description = '' if (key.description) { if (typeof key.description === 'string') { description = key.description } else if (!isHCLTab && key.description.yaml) { description = key.description.yaml } else if (key.description.hcl) { description = key.description.hcl } } const htmlDescription = description && markdownToHtml(' - ' + description) const type = key.type && {`(${key.type})`} const enterpriseAlert = key.enterprise && const keyLower = keyName.toLowerCase() // NOTE: This code copies from https://github.com/hashicorp/remark-plugins/blob/df606efc844319a2532ec54e4cf6ff2d575108ff/plugins/anchor-links/index.js // to ensure the styling of each bullet is correct. The two locations should be kept // in sync. return (
  • {keyName} {' '} {type} {enterpriseAlert}

    {renderKeys(key.children, isHCLTab)}
  • ) } /** * Constructs a keys object for Kubernetes out of HCL keys. * Really all this entails is nesting the correct keys under the Kubernetes * 'spec' key since in HCL there is no 'spec' key. * * @param {array} keys * @returns {array} */ function toKubeKeys(keys) { const topLevelKeys = keys.filter((key) => isTopLevelKubeKey(key.name)) const keysUnderSpec = keys.filter((key) => !isTopLevelKubeKey(key.name)) return topLevelKeys.concat([{ name: 'spec', children: keysUnderSpec }]) } /** * Converts an HCL key name to a kube yaml key name. * * Examples: * - Protocol => protocol * - MeshGateway => meshGateway * - ACLToken => aclToken * - HTTP => http * * @param {string} hclKey * @returns {string} */ function toYAMLKeyName(hclKey) { // Handle something like HTTP. if (hclKey.toUpperCase() === hclKey) { return hclKey.toLowerCase() } let indexFirstLowercaseChar = hclKey .split('') .findIndex((c) => c === c.toLowerCase()) // Special case to handle something like ACLToken => aclToken. if (indexFirstLowercaseChar > 1) { indexFirstLowercaseChar-- } let lowercasePortion = '' for (let i = 0; i < indexFirstLowercaseChar; i++) { lowercasePortion += hclKey[i].toLowerCase() } return ( lowercasePortion + hclKey.split('').slice(indexFirstLowercaseChar).join('') ) } /** * Converts a markdown string to its HTML representation. * Currently it only supports inline code blocks (e.g. `code here`) and * links (e.g. [link text](http://link-url) because these were the most * commonly used markdown features in the key descriptions. * * @param {string} markdown the input markdown * @returns {string} */ function markdownToHtml(markdown) { let html = markdown // Replace inline code blocks defined by backticks with . while (html.indexOf('`') > 0) { html = html.replace('`', '') if (html.indexOf('`') <= 0) { throw new Error(`'${markdown} does not have matching '\`' characters`) } html = html.replace('`', '') } // Replace links, e.g. [link text](http://link-url), // with link text. return html.replace(/\[(.*?)]\((.*?)\)/g, '$1') } /** * Returns true if key is a key used at the top level of a CRD. By top level we * mean not nested under any other key. * * @param {string} name name of the key * * @return {boolean} */ function isTopLevelKubeKey(name) { return ( name.toLowerCase() === 'metadata' || name.toLowerCase() === 'kind' || name.toLowerCase() === 'apiversion' ) } function EnterpriseAlert(props) { return }