Generate documentation example snippets (#2329)
* Update docs example generation script * Add docs examples generation to codegen job
This commit is contained in:
@ -125,6 +125,13 @@ async function codegen (args) {
|
||||
await $`cp -R ${join(import.meta.url, '..', '..', 'elastic-client-generator-js', 'output')}/* ${join(import.meta.url, '..', 'src', 'api')}`
|
||||
await $`mv ${join(import.meta.url, '..', 'src', 'api', 'reference.asciidoc')} ${join(import.meta.url, '..', 'docs', 'reference.asciidoc')}`
|
||||
await $`npm run build`
|
||||
|
||||
// run docs example generation
|
||||
if (version === 'main') {
|
||||
await $`node ./scripts/generate-docs-examples.js`
|
||||
} else {
|
||||
await $`node ./scripts/generate-docs-examples.js ${version.split('.').slice(0, 2).join('.')}`
|
||||
}
|
||||
}
|
||||
|
||||
function onError (err) {
|
||||
|
||||
@ -50,6 +50,7 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@elastic/request-converter": "^8.16.0",
|
||||
"@sinonjs/fake-timers": "github:sinonjs/fake-timers#0bfffc1",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/ms": "^0.7.31",
|
||||
|
||||
@ -17,147 +17,96 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* To run this generator you must have the
|
||||
* `alternatives_report.spec.json` placed in the root of this project.
|
||||
* To get the `alternatives_report.spec.json` you must run the script
|
||||
* to parse the original `alternatives_report.json`, which is not yet public
|
||||
* and lives in github.com/elastic/clients-team/tree/master/scripts/docs-json-generator
|
||||
*
|
||||
* This script will remove the content of the `docs/doc_examples` folder and generate
|
||||
* all the files present in the `enabledFiles` list below.
|
||||
* You can run it with the following command:
|
||||
*
|
||||
* ```bash
|
||||
* $ node scripts/generate-docs-examples.js
|
||||
* ```
|
||||
*/
|
||||
|
||||
const { join } = require('path')
|
||||
const { writeFileSync } = require('fs')
|
||||
const { writeFile } = require('fs/promises')
|
||||
const fetch = require('node-fetch')
|
||||
const rimraf = require('rimraf')
|
||||
const standard = require('standard')
|
||||
const dedent = require('dedent')
|
||||
const ora = require('ora')
|
||||
const { convertRequests } = require('@elastic/request-converter')
|
||||
const minimist = require('minimist')
|
||||
|
||||
const docsExamplesDir = join('docs', 'doc_examples')
|
||||
|
||||
const enabledFiles = [
|
||||
'docs/delete.asciidoc',
|
||||
'docs/get.asciidoc',
|
||||
'docs/index_.asciidoc',
|
||||
'getting-started.asciidoc',
|
||||
'query-dsl/query-string-query.asciidoc',
|
||||
'query-dsl.asciidoc',
|
||||
'search/request-body.asciidoc',
|
||||
'setup/install/check-running.asciidoc',
|
||||
'mapping.asciidoc',
|
||||
'query-dsl/query_filter_context.asciidoc',
|
||||
'query-dsl/bool-query.asciidoc',
|
||||
'query-dsl/match-query.asciidoc',
|
||||
'indices/create-index.asciidoc',
|
||||
'docs/index_.asciidoc',
|
||||
'aggregations/bucket/terms-aggregation.asciidoc',
|
||||
'query-dsl/range-query.asciidoc',
|
||||
'search/search.asciidoc',
|
||||
'query-dsl/multi-match-query.asciidoc',
|
||||
'docs/bulk.asciidoc',
|
||||
'indices/delete-index.asciidoc',
|
||||
'indices/put-mapping.asciidoc',
|
||||
'query-dsl/match-all-query.asciidoc',
|
||||
'query-dsl/term-query.asciidoc',
|
||||
'docs/update.asciidoc',
|
||||
'docs/reindex.asciidoc',
|
||||
'indices/templates.asciidoc',
|
||||
'query-dsl/exists-query.asciidoc',
|
||||
'query-dsl/terms-query.asciidoc',
|
||||
'query-dsl/wildcard-query.asciidoc',
|
||||
'mapping/types/nested.asciidoc',
|
||||
'mapping/params/format.asciidoc',
|
||||
'docs/delete-by-query.asciidoc',
|
||||
'search/request/sort.asciidoc',
|
||||
'query-dsl/function-score-query.asciidoc',
|
||||
'query-dsl/nested-query.asciidoc',
|
||||
'query-dsl/regexp-query.asciidoc',
|
||||
'mapping/types/array.asciidoc',
|
||||
'mapping/types/date.asciidoc',
|
||||
'mapping/types/keyword.asciidoc',
|
||||
'mapping/params/fielddata.asciidoc',
|
||||
'cluster/health.asciidoc',
|
||||
'docs/bulk.asciidoc',
|
||||
'indices/aliases.asciidoc',
|
||||
'indices/update-settings.asciidoc',
|
||||
'search/request/from-size.asciidoc',
|
||||
'search/count.asciidoc',
|
||||
'setup/logging-config.asciidoc',
|
||||
'search/request/from-size.asciidoc',
|
||||
'query-dsl/match-phrase-query.asciidoc',
|
||||
'aggregations/metrics/valuecount-aggregation.asciidoc',
|
||||
'aggregations/bucket/datehistogram-aggregation.asciidoc',
|
||||
'aggregations/bucket/filter-aggregation.asciidoc',
|
||||
'mapping/types/numeric.asciidoc',
|
||||
'mapping/fields/id-field.asciidoc',
|
||||
'mapping/params/multi-fields.asciidoc',
|
||||
'api-conventions.asciidoc',
|
||||
'cat/indices.asciidoc',
|
||||
'docs/update-by-query.asciidoc',
|
||||
'indices/get-index.asciidoc',
|
||||
'indices/get-mapping.asciidoc',
|
||||
'search.asciidoc',
|
||||
'search/request/scroll.asciidoc',
|
||||
'search/suggesters.asciidoc'
|
||||
]
|
||||
const log = ora('Generating example snippets')
|
||||
|
||||
const failures = {}
|
||||
|
||||
async function getAlternativesReport (version = 'master') {
|
||||
const reportUrl = `https://raw.githubusercontent.com/elastic/built-docs/master/raw/en/elasticsearch/reference/${version}/alternatives_report.json`
|
||||
const response = await fetch(reportUrl)
|
||||
if (!response.ok) {
|
||||
log.fail(`unexpected response ${response.statusText}`)
|
||||
process.exit(1)
|
||||
}
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
async function makeSnippet (example) {
|
||||
const { source, digest } = example
|
||||
const fileName = `${digest}.asciidoc`
|
||||
const filePath = join(docsExamplesDir, fileName)
|
||||
|
||||
try {
|
||||
const code = await convertRequests(source, 'javascript', {
|
||||
complete: false,
|
||||
printResponse: true
|
||||
})
|
||||
await writeFile(filePath, asciidocWrapper(code), 'utf8')
|
||||
} catch (err) {
|
||||
failures[digest] = err.message
|
||||
}
|
||||
}
|
||||
|
||||
async function generate (version) {
|
||||
log.start()
|
||||
|
||||
function generate () {
|
||||
rimraf.sync(join(docsExamplesDir, '*'))
|
||||
const examples = require(join(__dirname, '..', 'alternatives_report.spec.json'))
|
||||
|
||||
log.text = `Downloading alternatives report for version ${version}`
|
||||
const examples = await getAlternativesReport(version)
|
||||
|
||||
let counter = 1
|
||||
for (const example of examples) {
|
||||
if (example.lang !== 'console') continue
|
||||
if (!enabledFiles.includes(example.source_location.file)) continue
|
||||
log.text = `${counter++}/${examples.length}: ${example.digest}`
|
||||
|
||||
const asciidoc = generateAsciidoc(example.parsed_source)
|
||||
writeFileSync(
|
||||
join(docsExamplesDir, `${example.digest}.asciidoc`),
|
||||
asciidoc,
|
||||
'utf8'
|
||||
)
|
||||
// skip over bad request definitions
|
||||
if (example.source.startsWith('{') || example.source.endsWith('...')) {
|
||||
failures[example.digest] = 'Incomplete request syntax'
|
||||
continue
|
||||
}
|
||||
|
||||
await makeSnippet(example)
|
||||
}
|
||||
}
|
||||
|
||||
function generateAsciidoc (source) {
|
||||
let asciidoc = '// This file is autogenerated, DO NOT EDIT\n'
|
||||
asciidoc += '// Use `node scripts/generate-docs-examples.js` to generate the docs examples\n\n'
|
||||
let code = 'async function run (client) {\n// START\n'
|
||||
function asciidocWrapper (source) {
|
||||
return `// This file is autogenerated, DO NOT EDIT
|
||||
// Use \`node scripts/generate-docs-examples.js\` to generate the docs examples
|
||||
|
||||
for (let i = 0; i < source.length; i++) {
|
||||
const { api, query, params, body } = source[i]
|
||||
const apiArguments = Object.assign({}, params, query, body ? { body } : body)
|
||||
const serializedApiArguments = Object.keys(apiArguments).length > 0
|
||||
? JSON.stringify(apiArguments, null, 2)
|
||||
: ''
|
||||
code += `const response${getResponsePostfix(i)} = await client.${api.replace(/_([a-z])/g, g => g[1].toUpperCase())}(${serializedApiArguments})
|
||||
console.log(response${getResponsePostfix(i)})
|
||||
\n`
|
||||
}
|
||||
|
||||
code += '// END\n}'
|
||||
const { results } = standard.lintTextSync(code, { fix: true })
|
||||
code = results[0].output
|
||||
code = code.slice(code.indexOf('// START\n') + 9, code.indexOf('\n\n// END'))
|
||||
|
||||
asciidoc += `[source, js]
|
||||
[source, js]
|
||||
----
|
||||
${dedent(code)}
|
||||
${source.trim()}
|
||||
----
|
||||
|
||||
`
|
||||
return asciidoc
|
||||
|
||||
function getResponsePostfix (i) {
|
||||
if (source.length === 1) return ''
|
||||
return String(i)
|
||||
}
|
||||
}
|
||||
|
||||
generate()
|
||||
const options = minimist(process.argv.slice(2), {
|
||||
string: ['version'],
|
||||
default: {
|
||||
version: 'master'
|
||||
}
|
||||
})
|
||||
|
||||
generate(options.version)
|
||||
.then(() => log.succeed('done!'))
|
||||
.catch(err => log.fail(err.message))
|
||||
.finally(() => {
|
||||
const keys = Object.keys(failures)
|
||||
if (keys.length > 0) {
|
||||
let message = 'Some examples failed to generate:\n\n'
|
||||
for (const key of keys) {
|
||||
message += `${key}: ${failures[key]}\n`
|
||||
}
|
||||
console.error(message)
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user