feat(xo-cli rest get): new --output parameter

It can be used to save the response in a file instead of parsing it.
This commit is contained in:
Julien Fontanet
2023-06-27 14:27:09 +02:00
parent 88063d4d87
commit 6b2ad5a7cc
5 changed files with 56 additions and 16 deletions

View File

@@ -76,9 +76,15 @@ Usage:
xo-cli rest get tasks filter='status:pending'
xo-cli rest get vms fields=name_label,power_state
xo-cli rest get <object> [wait | wait=result]
xo-cli rest get [--output <file>] <object> [wait | wait=result]
Show an object from the REST API.
--output <file>
If specified, the response will be saved in <file> instead of being parsed.
If <file> ends with `/`, it will be considered as the directory in which
to save the response, and the filename will be last part of the <object> path.
<object>
Full path of the object to show

View File

@@ -297,9 +297,15 @@ const help = wrap(
$name rest get tasks filter='status:pending'
$name rest get vms fields=name_label,power_state
$name rest get <object> [wait | wait=result]
$name rest get [--output <file>] <object> [wait | wait=result]
Show an object from the REST API.
--output <file>
If specified, the response will be saved in <file> instead of being parsed.
If <file> ends with \`/\`, it will be considered as the directory in which
to save the response, and the filename will be last part of the <object> path.
<object>
Full path of the object to show

View File

@@ -30,6 +30,7 @@
},
"dependencies": {
"chalk": "^5.0.1",
"content-type": "^1.0.5",
"fs-extra": "^11.1.0",
"getopts": "^2.3.0",
"http-request-plus": "^1.0.0",

View File

@@ -1,4 +1,8 @@
import { basename, join } from 'node:path'
import { createWriteStream } from 'node:fs'
import { normalize } from 'node:path/posix'
import { parse as parseContentType } from 'content-type'
import { pipeline } from 'node:stream/promises'
import getopts from 'getopts'
import hrp from 'http-request-plus'
import merge from 'lodash/merge.js'
@@ -43,23 +47,46 @@ const COMMANDS = {
return await response.text()
},
async get([path, ...args]) {
const response = await this.exec(path, { query: parseParams(args) })
async get(args) {
const {
_: [path, ...rest],
output,
} = getopts(args, {
alias: { output: 'o' },
string: 'output',
stopEarly: true,
})
const result = await response.json()
const response = await this.exec(path, { query: parseParams(rest) })
if (Array.isArray(result)) {
for (let i = 0, n = result.length; i < n; ++i) {
const value = result[i]
if (typeof value === 'string') {
result[i] = stripPrefix(value)
} else if (value != null && typeof value.href === 'string') {
value.href = stripPrefix(value.href)
}
}
if (output !== '') {
return pipeline(
response,
output === '-'
? process.stdout
: createWriteStream(output.endsWith('/') ? join(output, basename(path)) : output, { flags: 'wx' })
)
}
return this.json ? JSON.stringify(result, null, 2) : result
const { type } = parseContentType(response)
if (type === 'application/json') {
const result = await response.json()
if (Array.isArray(result)) {
for (let i = 0, n = result.length; i < n; ++i) {
const value = result[i]
if (typeof value === 'string') {
result[i] = stripPrefix(value)
} else if (value != null && typeof value.href === 'string') {
value.href = stripPrefix(value.href)
}
}
}
return this.json ? JSON.stringify(result, null, 2) : result
} else {
throw new Error('unsupported content-type ' + type)
}
},
async patch([path, ...params]) {

View File

@@ -6863,7 +6863,7 @@ content-security-policy-builder@2.1.0:
resolved "https://registry.yarnpkg.com/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz#0a2364d769a3d7014eec79ff7699804deb8cfcbb"
integrity sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==
content-type@^1.0.4, content-type@~1.0.1, content-type@~1.0.4, content-type@~1.0.5:
content-type@^1.0.4, content-type@^1.0.5, content-type@~1.0.1, content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==