Skip to content

Commit cc21afd

Browse files
committed
Add cli
1 parent 29ed93c commit cc21afd

File tree

3 files changed

+152
-0
lines changed

3 files changed

+152
-0
lines changed

docs/reference/cli.md

+21
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,27 @@ quickwit source create
491491
|-----------------|-------------|
492492
| `--index` | ID of the target index |
493493
| `--source-config` | Path to source config file. Please, refer to the documentation for more details. |
494+
### source update
495+
496+
Update an existing source.
497+
`quickwit source update [args]`
498+
499+
*Synopsis*
500+
501+
```bash
502+
quickwit source update
503+
--index <index>
504+
--source <source>
505+
--source-config <source-config>
506+
```
507+
508+
*Options*
509+
510+
| Option | Description |
511+
|-----------------|-------------|
512+
| `--index` | ID of the target index |
513+
| `--source` | ID of the source |
514+
| `--source-config` | Path to source config file. Please, refer to the documentation for more details. |
494515
### source enable
495516

496517
Enables a source for an index.

quickwit/quickwit-cli/src/source.rs

+88
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ pub fn build_source_command() -> Command {
5050
.required(true),
5151
])
5252
)
53+
.subcommand(
54+
Command::new("update")
55+
.about("Update an existing source.")
56+
.args(&[
57+
arg!(--index <INDEX_ID> "ID of the target index")
58+
.display_order(1)
59+
.required(true),
60+
arg!(--source <SOURCE_ID> "ID of the source")
61+
.display_order(2)
62+
.required(true),
63+
arg!(--"source-config" <SOURCE_CONFIG> "Path to source config file. Please, refer to the documentation for more details.")
64+
.required(true),
65+
])
66+
)
5367
.subcommand(
5468
Command::new("enable")
5569
.about("Enables a source for an index.")
@@ -147,6 +161,14 @@ pub struct CreateSourceArgs {
147161
pub source_config_uri: Uri,
148162
}
149163

164+
#[derive(Debug, Eq, PartialEq)]
165+
pub struct UpdateSourceArgs {
166+
pub client_args: ClientArgs,
167+
pub index_id: IndexId,
168+
pub source_id: SourceId,
169+
pub source_config_uri: Uri,
170+
}
171+
150172
#[derive(Debug, Eq, PartialEq)]
151173
pub struct ToggleSourceArgs {
152174
pub client_args: ClientArgs,
@@ -187,6 +209,7 @@ pub struct ResetCheckpointArgs {
187209
#[derive(Debug, Eq, PartialEq)]
188210
pub enum SourceCliCommand {
189211
CreateSource(CreateSourceArgs),
212+
UpdateSource(UpdateSourceArgs),
190213
ToggleSource(ToggleSourceArgs),
191214
DeleteSource(DeleteSourceArgs),
192215
DescribeSource(DescribeSourceArgs),
@@ -198,6 +221,7 @@ impl SourceCliCommand {
198221
pub async fn execute(self) -> anyhow::Result<()> {
199222
match self {
200223
Self::CreateSource(args) => create_source_cli(args).await,
224+
Self::UpdateSource(args) => update_source_cli(args).await,
201225
Self::ToggleSource(args) => toggle_source_cli(args).await,
202226
Self::DeleteSource(args) => delete_source_cli(args).await,
203227
Self::DescribeSource(args) => describe_source_cli(args).await,
@@ -212,6 +236,7 @@ impl SourceCliCommand {
212236
.context("failed to parse source subcommand")?;
213237
match subcommand.as_str() {
214238
"create" => Self::parse_create_args(submatches).map(Self::CreateSource),
239+
"update" => Self::parse_update_args(submatches).map(Self::UpdateSource),
215240
"enable" => {
216241
Self::parse_toggle_source_args(&subcommand, submatches).map(Self::ToggleSource)
217242
}
@@ -244,6 +269,26 @@ impl SourceCliCommand {
244269
})
245270
}
246271

272+
fn parse_update_args(mut matches: ArgMatches) -> anyhow::Result<UpdateSourceArgs> {
273+
let client_args = ClientArgs::parse(&mut matches)?;
274+
let index_id = matches
275+
.remove_one::<String>("index")
276+
.expect("`index` should be a required arg.");
277+
let source_id = matches
278+
.remove_one::<String>("source")
279+
.expect("`source` should be a required arg.");
280+
let source_config_uri = matches
281+
.remove_one::<String>("source-config")
282+
.map(|uri_str| Uri::from_str(&uri_str))
283+
.expect("`source-config` should be a required arg.")?;
284+
Ok(UpdateSourceArgs {
285+
client_args,
286+
index_id,
287+
source_id,
288+
source_config_uri,
289+
})
290+
}
291+
247292
fn parse_toggle_source_args(
248293
subcommand: &str,
249294
mut matches: ArgMatches,
@@ -342,6 +387,23 @@ async fn create_source_cli(args: CreateSourceArgs) -> anyhow::Result<()> {
342387
Ok(())
343388
}
344389

390+
async fn update_source_cli(args: UpdateSourceArgs) -> anyhow::Result<()> {
391+
debug!(args=?args, "update-source");
392+
println!("❯ Updating source...");
393+
let storage_resolver = StorageResolver::unconfigured();
394+
let source_config_content = load_file(&storage_resolver, &args.source_config_uri).await?;
395+
let source_config_str: &str = std::str::from_utf8(&source_config_content)
396+
.with_context(|| format!("source config is not utf-8: {}", args.source_config_uri))?;
397+
let config_format = ConfigFormat::sniff_from_uri(&args.source_config_uri)?;
398+
let qw_client = args.client_args.client();
399+
qw_client
400+
.sources(&args.index_id)
401+
.update(&args.source_id, source_config_str, config_format)
402+
.await?;
403+
println!("{} Source successfully updated.", "✔".color(GREEN_COLOR));
404+
Ok(())
405+
}
406+
345407
async fn toggle_source_cli(args: ToggleSourceArgs) -> anyhow::Result<()> {
346408
debug!(args=?args, "toggle-source");
347409
println!("❯ Toggling source...");
@@ -604,6 +666,32 @@ mod tests {
604666
assert_eq!(command, expected_command);
605667
}
606668

669+
#[test]
670+
fn test_parse_update_source_args() {
671+
let app = build_cli().no_binary_name(true);
672+
let matches = app
673+
.try_get_matches_from(vec![
674+
"source",
675+
"update",
676+
"--index",
677+
"hdfs-logs",
678+
"--source",
679+
"kafka-foo",
680+
"--source-config",
681+
"/source-conf.yaml",
682+
])
683+
.unwrap();
684+
let command = CliCommand::parse_cli_args(matches).unwrap();
685+
let expected_command =
686+
CliCommand::Source(SourceCliCommand::UpdateSource(UpdateSourceArgs {
687+
client_args: ClientArgs::default(),
688+
index_id: "hdfs-logs".to_string(),
689+
source_id: "kafka-foo".to_string(),
690+
source_config_uri: Uri::from_str("file:///source-conf.yaml").unwrap(),
691+
}));
692+
assert_eq!(command, expected_command);
693+
}
694+
607695
#[test]
608696
fn test_parse_toggle_source_args() {
609697
{

quickwit/quickwit-rest-client/src/rest_client.rs

+43
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,30 @@ impl<'a> SourceClient<'a> {
513513
Ok(source_config)
514514
}
515515

516+
pub async fn update(
517+
&self,
518+
source_id: &str,
519+
source_config_input: impl AsRef<[u8]>,
520+
config_format: ConfigFormat,
521+
) -> Result<SourceConfig, Error> {
522+
let header_map = header_from_config_format(config_format);
523+
let source_config_bytes = Bytes::copy_from_slice(source_config_input.as_ref());
524+
let path = format!("{}/{source_id}", self.sources_root_url());
525+
let response = self
526+
.transport
527+
.send::<()>(
528+
Method::PUT,
529+
&path,
530+
Some(header_map),
531+
None,
532+
Some(source_config_bytes),
533+
self.timeout,
534+
)
535+
.await?;
536+
let source_config = response.deserialize().await?;
537+
Ok(source_config)
538+
}
539+
516540
pub async fn get(&self, source_id: &str) -> Result<SourceConfig, Error> {
517541
let path = format!("{}/{source_id}", self.sources_root_url());
518542
let response = self
@@ -1083,6 +1107,25 @@ mod test {
10831107
source_config
10841108
);
10851109

1110+
// PUT update source with yaml
1111+
Mock::given(method("PUT"))
1112+
.and(path("/api/v1/indexes/my-index/sources/my-source-1"))
1113+
.and(header(CONTENT_TYPE.as_str(), "application/yaml"))
1114+
.respond_with(
1115+
ResponseTemplate::new(StatusCode::OK).set_body_json(source_config.clone()),
1116+
)
1117+
.up_to_n_times(1)
1118+
.mount(&mock_server)
1119+
.await;
1120+
assert_eq!(
1121+
qw_client
1122+
.sources("my-index")
1123+
.update("my-source-1", "", ConfigFormat::Yaml)
1124+
.await
1125+
.unwrap(),
1126+
source_config
1127+
);
1128+
10861129
// GET sources
10871130
Mock::given(method("GET"))
10881131
.and(path("/api/v1/indexes/my-index/sources"))

0 commit comments

Comments
 (0)