-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add beta support for export commands (#568)
* Add TSV colorization * Add create, list, and get export commands --------- Co-authored-by: James Rodewig <james.rodewig@fauna.com>
- Loading branch information
Showing
22 changed files
with
1,959 additions
and
414 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// @ts-check | ||
|
||
import { container } from "../../config/container.mjs"; | ||
import { EXPORT_TERMINAL_STATES } from "../../lib/account-api.mjs"; | ||
import { ValidationError } from "../../lib/errors.mjs"; | ||
import { colorize, Format } from "../../lib/formatting/colorize.mjs"; | ||
import { DATABASE_PATH_OPTIONS } from "../../lib/options.mjs"; | ||
import { WAIT_OPTIONS, waitUntilExportIsReady } from "./wait.mjs"; | ||
|
||
async function createS3Export(argv) { | ||
const { | ||
database, | ||
path, | ||
bucket, | ||
format, | ||
json, | ||
color, | ||
collection: collections, | ||
wait, | ||
maxWait, | ||
quiet, | ||
} = argv; | ||
const logger = container.resolve("logger"); | ||
const { createExport } = container.resolve("accountAPI"); | ||
|
||
let createdExport = await createExport({ | ||
database, | ||
collections, | ||
destination: { | ||
s3: { | ||
bucket, | ||
path, | ||
}, | ||
}, | ||
format, | ||
}); | ||
|
||
if (wait && !EXPORT_TERMINAL_STATES.includes(createdExport.state)) { | ||
createdExport = await waitUntilExportIsReady({ | ||
id: createdExport.id, | ||
opts: { | ||
maxWait, | ||
quiet, | ||
}, | ||
}); | ||
} | ||
|
||
if (json) { | ||
logger.stdout(colorize(createdExport, { color, format: Format.JSON })); | ||
} else { | ||
logger.stdout(colorize(createdExport, { color, format: Format.YAML })); | ||
} | ||
} | ||
|
||
const sharedExamples = [ | ||
[ | ||
"$0 export create s3 --database us/my_db --bucket my-bucket --path exports/my_db", | ||
"Export the 'us-std/my_db' database to the 'exports/my_db' path of the 'my-bucket' S3 bucket. Outputs the export ID.", | ||
], | ||
[ | ||
"$0 export create s3 --database us/my_db --bucket my-bucket --path my-prefix --json", | ||
"Output the full JSON of the export request.", | ||
], | ||
[ | ||
"$0 export create s3 --database us/my_db --bucket my-bucket --path my-prefix --collection my-collection", | ||
"Export the 'my-collection' collection only.", | ||
], | ||
[ | ||
"$0 export create s3 --database us/my_db --bucket my-bucket --path my-prefix --format tagged", | ||
"Encode the export's document data using the 'tagged' format.", | ||
], | ||
[ | ||
"$0 export create s3 --database us/my_db --bucket my-bucket --path my-prefix --wait --max-wait 180", | ||
"Wait for the export to complete or fail before exiting. Waits up to 180 minutes.", | ||
], | ||
]; | ||
|
||
function buildCreateS3ExportCommand(yargs) { | ||
return yargs | ||
.options({ | ||
bucket: { | ||
type: "string", | ||
required: true, | ||
description: "Name of the S3 bucket where the export will be stored.", | ||
group: "API:", | ||
}, | ||
path: { | ||
type: "string", | ||
required: true, | ||
description: | ||
"Path prefix for the S3 bucket. Separate subfolders using a slash (`/`).", | ||
group: "API:", | ||
}, | ||
format: { | ||
type: "string", | ||
required: true, | ||
description: | ||
"Data format used to encode the exported FQL document data as JSON.", | ||
choices: ["simple", "tagged"], | ||
default: "simple", | ||
group: "API:", | ||
}, | ||
}) | ||
.options(WAIT_OPTIONS) | ||
.check((argv) => { | ||
if (!argv.database) { | ||
throw new ValidationError( | ||
"--database is required to create an export.", | ||
); | ||
} | ||
|
||
return true; | ||
}) | ||
.example(sharedExamples); | ||
} | ||
|
||
function buildCreateCommand(yargs) { | ||
return yargs | ||
.options(DATABASE_PATH_OPTIONS) | ||
.options({ | ||
collection: { | ||
type: "array", | ||
required: false, | ||
description: | ||
"Used-defined collections to export. Pass values as a space-separated list. If omitted, all user-defined collections are exported.", | ||
default: [], | ||
group: "API:", | ||
}, | ||
}) | ||
.command({ | ||
command: "s3", | ||
description: "Export to an S3 bucket.", | ||
builder: buildCreateS3ExportCommand, | ||
handler: createS3Export, | ||
}) | ||
.example(sharedExamples) | ||
.demandCommand(); | ||
} | ||
|
||
export default { | ||
command: "create <destination-type>", | ||
description: | ||
"Start the export of a database or collections. Outputs the export ID.", | ||
builder: buildCreateCommand, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import chalk from "chalk"; | ||
|
||
import { container } from "../../config/container.mjs"; | ||
import { ValidationError } from "../../lib/errors.mjs"; | ||
import { ACCOUNT_OPTIONS } from "../../lib/options.mjs"; | ||
import createCommand from "./create.mjs"; | ||
import getCommand from "./get.mjs"; | ||
import listCommand from "./list.mjs"; | ||
|
||
/** | ||
* Validates the arguments do not include Core API authentication options. | ||
* In the CLI, we don't validate unknown options, but because these commands are unique and | ||
* only used the Account API, we aggressively validate the options here to avoid confusion. | ||
* @param {import("yargs").Arguments} argv | ||
* @returns {boolean} | ||
*/ | ||
function validateAccountOnlyOptions(argv) { | ||
const { secret, local } = argv; | ||
|
||
if (local) { | ||
throw new ValidationError( | ||
"Exports do not support --local or Fauna containers.", | ||
); | ||
} | ||
|
||
if (secret) { | ||
throw new ValidationError("Exports do not support --secret."); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
function buildExportCommand(yargs) { | ||
return yargs | ||
.options(ACCOUNT_OPTIONS) | ||
.middleware(() => { | ||
const logger = container.resolve("logger"); | ||
logger.stderr( | ||
chalk.yellow( | ||
`Warning: fauna export is currently in beta. To learn more, visit https://docs.fauna.com/fauna/current/build/cli/v4/commands/export/\n`, | ||
), | ||
); | ||
}) | ||
.check(validateAccountOnlyOptions) | ||
.command(createCommand) | ||
.command(listCommand) | ||
.command(getCommand) | ||
.example([ | ||
[ | ||
"$0 export create s3 --database us/my_db --bucket my-bucket --path exports/my_db", | ||
"Export the 'us-std/my_db' database to the 'exports/my_db' path of the 'my-bucket' S3 bucket. Outputs the export ID.", | ||
], | ||
[ | ||
"$0 export get 123456789", | ||
"Output the YAML for the export with an ID of '123456789'.", | ||
], | ||
["$0 export list", "List exports in TSV format."], | ||
]) | ||
.demandCommand(); | ||
} | ||
|
||
export default { | ||
command: "export <method>", | ||
description: "Create and manage exports. Currently in beta.", | ||
builder: buildExportCommand, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { container } from "../../config/container.mjs"; | ||
import { EXPORT_TERMINAL_STATES } from "../../lib/account-api.mjs"; | ||
import { colorize, Format } from "../../lib/formatting/colorize.mjs"; | ||
import { WAIT_OPTIONS, waitUntilExportIsReady } from "./wait.mjs"; | ||
|
||
async function getExport(argv) { | ||
const logger = container.resolve("logger"); | ||
const { getExport } = container.resolve("accountAPI"); | ||
const { exportId, json, color, wait, maxWait, quiet } = argv; | ||
|
||
let response = await getExport({ exportId }); | ||
if (wait && !EXPORT_TERMINAL_STATES.includes(response.state)) { | ||
response = await waitUntilExportIsReady({ | ||
id: exportId, | ||
opts: { | ||
maxWait, | ||
quiet, | ||
}, | ||
}); | ||
} | ||
|
||
if (json) { | ||
logger.stdout(colorize(response, { color, format: Format.JSON })); | ||
} else { | ||
logger.stdout(colorize(response, { color, format: Format.YAML })); | ||
} | ||
} | ||
|
||
function buildGetExportCommand(yargs) { | ||
return yargs | ||
.positional("exportId", { | ||
type: "string", | ||
description: "ID of the export to retrieve.", | ||
nargs: 1, | ||
required: true, | ||
}) | ||
.options(WAIT_OPTIONS) | ||
.example([ | ||
[ | ||
"$0 export get 123456789", | ||
"Output the YAML for the export with an ID of '123456789'.", | ||
], | ||
["$0 export get 123456789 --json", "Output the export as JSON."], | ||
[ | ||
"$0 export get 123456789 --wait", | ||
"Wait for the export to complete or fail before exiting.", | ||
], | ||
]); | ||
} | ||
|
||
export default { | ||
command: "get <exportId>", | ||
description: "Get an export by ID.", | ||
builder: buildGetExportCommand, | ||
handler: getExport, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { container } from "../../config/container.mjs"; | ||
import { EXPORT_STATES } from "../../lib/account-api.mjs"; | ||
import { colorize, Format } from "../../lib/formatting/colorize.mjs"; | ||
|
||
const COLUMN_SEPARATOR = "\t"; | ||
const COLLECTION_SEPARATOR = ","; | ||
|
||
async function listExports(argv) { | ||
const logger = container.resolve("logger"); | ||
const { json, color, maxResults, state } = argv; | ||
const { listExports } = container.resolve("accountAPI"); | ||
|
||
const { results } = await listExports({ | ||
maxResults, | ||
state: state, | ||
}); | ||
|
||
if (json) { | ||
logger.stdout(colorize(results, { color, format: Format.JSON })); | ||
} else { | ||
if (!results.length) { | ||
return; | ||
} | ||
|
||
results.forEach((r) => { | ||
const row = [ | ||
r.id, | ||
r.database, | ||
(r.collections ?? []).join(COLLECTION_SEPARATOR), | ||
r.destination_uri, | ||
r.state, | ||
]; | ||
logger.stdout( | ||
colorize(row.join(COLUMN_SEPARATOR), { | ||
color, | ||
format: Format.TSV, | ||
}), | ||
); | ||
}); | ||
} | ||
} | ||
|
||
function buildListExportsCommand(yargs) { | ||
return yargs | ||
.options({ | ||
"max-results": { | ||
alias: "max", | ||
type: "number", | ||
description: "Maximum number of exports to return. Defaults to 10.", | ||
default: 10, | ||
group: "API:", | ||
}, | ||
state: { | ||
type: "array", | ||
description: "Filter exports by state.", | ||
default: [], | ||
group: "API:", | ||
choices: EXPORT_STATES, | ||
}, | ||
}) | ||
.example([ | ||
[ | ||
"$0 export list", | ||
"List exports in TSV format with export ID, database, collections, destination, and state as the columns.", | ||
], | ||
["$0 export list --json", "List exports in JSON format."], | ||
["$0 export list --max-results 50", "List up to 50 exports."], | ||
[ | ||
"$0 export list --states Pending Complete", | ||
"List exports in the 'Pending' or 'Complete' state.", | ||
], | ||
]); | ||
} | ||
|
||
export default { | ||
command: "list", | ||
describe: "List exports.", | ||
builder: buildListExportsCommand, | ||
handler: listExports, | ||
}; |
Oops, something went wrong.