Skip to content

Commit 1406f0a

Browse files
committed
Add cli
1 parent 43b778f commit 1406f0a

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
@@ -493,6 +493,27 @@ quickwit source create
493493
|-----------------|-------------|
494494
| `--index` | ID of the target index |
495495
| `--source-config` | Path to source config file. Please, refer to the documentation for more details. |
496+
### source update
497+
498+
Update an existing source.
499+
`quickwit source update [args]`
500+
501+
*Synopsis*
502+
503+
```bash
504+
quickwit source update
505+
--index <index>
506+
--source <source>
507+
--source-config <source-config>
508+
```
509+
510+
*Options*
511+
512+
| Option | Description |
513+
|-----------------|-------------|
514+
| `--index` | ID of the target index |
515+
| `--source` | ID of the source |
516+
| `--source-config` | Path to source config file. Please, refer to the documentation for more details. |
496517
### source enable
497518

498519
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
@@ -540,6 +540,30 @@ impl<'a> SourceClient<'a> {
540540
Ok(source_config)
541541
}
542542

543+
pub async fn update(
544+
&self,
545+
source_id: &str,
546+
source_config_input: impl AsRef<[u8]>,
547+
config_format: ConfigFormat,
548+
) -> Result<SourceConfig, Error> {
549+
let header_map = header_from_config_format(config_format);
550+
let source_config_bytes = Bytes::copy_from_slice(source_config_input.as_ref());
551+
let path = format!("{}/{source_id}", self.sources_root_url());
552+
let response = self
553+
.transport
554+
.send::<()>(
555+
Method::PUT,
556+
&path,
557+
Some(header_map),
558+
None,
559+
Some(source_config_bytes),
560+
self.timeout,
561+
)
562+
.await?;
563+
let source_config = response.deserialize().await?;
564+
Ok(source_config)
565+
}
566+
543567
pub async fn get(&self, source_id: &str) -> Result<SourceConfig, Error> {
544568
let path = format!("{}/{source_id}", self.sources_root_url());
545569
let response = self
@@ -1133,6 +1157,25 @@ mod test {
11331157
source_config
11341158
);
11351159

1160+
// PUT update source with yaml
1161+
Mock::given(method("PUT"))
1162+
.and(path("/api/v1/indexes/my-index/sources/my-source-1"))
1163+
.and(header(CONTENT_TYPE.as_str(), "application/yaml"))
1164+
.respond_with(
1165+
ResponseTemplate::new(StatusCode::OK).set_body_json(source_config.clone()),
1166+
)
1167+
.up_to_n_times(1)
1168+
.mount(&mock_server)
1169+
.await;
1170+
assert_eq!(
1171+
qw_client
1172+
.sources("my-index")
1173+
.update("my-source-1", "", ConfigFormat::Yaml)
1174+
.await
1175+
.unwrap(),
1176+
source_config
1177+
);
1178+
11361179
// GET sources
11371180
Mock::given(method("GET"))
11381181
.and(path("/api/v1/indexes/my-index/sources"))

0 commit comments

Comments
 (0)