feat(cli,client): 将分页参数改为 pageToken/pageSize 并支持全量拉取
This commit is contained in:
@@ -35,11 +35,11 @@ pub struct CreateCommand {
|
|||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
pub struct ListCommand {
|
pub struct ListCommand {
|
||||||
#[arg(short, long, default_value = "20", help = "Number of memos to list")]
|
#[arg(short, long, default_value = "50", help = "Number of memos to list per page")]
|
||||||
pub limit: i32,
|
pub page_size: i32,
|
||||||
|
|
||||||
#[arg(short, long, default_value = "0", help = "Offset for pagination")]
|
#[arg(short, long, help = "Page token for pagination")]
|
||||||
pub offset: i32,
|
pub page_token: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
|||||||
@@ -22,16 +22,11 @@ impl MemosClient {
|
|||||||
pub async fn create_memo(&self, content: &str, visibility: Option<&str>) -> Result<Memo> {
|
pub async fn create_memo(&self, content: &str, visibility: Option<&str>) -> Result<Memo> {
|
||||||
let url = format!("{}/api/v1/memos", self.base_url);
|
let url = format!("{}/api/v1/memos", self.base_url);
|
||||||
|
|
||||||
let visibility_str = visibility.map(|v| {
|
let visibility_str = visibility.map(|v| v.to_string());
|
||||||
if v.starts_with("VISIBILITY_") {
|
|
||||||
v.to_string()
|
|
||||||
} else {
|
|
||||||
format!("VISIBILITY_{}", v.to_uppercase())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let request = CreateMemoRequest {
|
let request = CreateMemoRequest {
|
||||||
content: content.to_string(),
|
content: content.to_string(),
|
||||||
|
state: "STATE_UNSPECIFIED".to_string(),
|
||||||
visibility: visibility_str,
|
visibility: visibility_str,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,7 +50,7 @@ impl MemosClient {
|
|||||||
Ok(memo)
|
Ok(memo)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_memos(&self, limit: Option<i32>, offset: Option<i32>) -> Result<Vec<Memo>> {
|
pub async fn list_memos(&self, page_size: Option<i32>, page_token: Option<&str>) -> Result<Vec<Memo>> {
|
||||||
let url = format!("{}/api/v1/memos", self.base_url);
|
let url = format!("{}/api/v1/memos", self.base_url);
|
||||||
|
|
||||||
let mut request = self
|
let mut request = self
|
||||||
@@ -63,11 +58,11 @@ impl MemosClient {
|
|||||||
.get(&url)
|
.get(&url)
|
||||||
.header("Authorization", format!("Bearer {}", self.user_token));
|
.header("Authorization", format!("Bearer {}", self.user_token));
|
||||||
|
|
||||||
if let Some(l) = limit {
|
if let Some(ps) = page_size {
|
||||||
request = request.query(&[("limit", l.to_string())]);
|
request = request.query(&[("pageSize", ps.to_string())]);
|
||||||
}
|
}
|
||||||
if let Some(o) = offset {
|
if let Some(token) = page_token {
|
||||||
request = request.query(&[("offset", o.to_string())]);
|
request = request.query(&[("pageToken", token)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = request.send().await?;
|
let response = request.send().await?;
|
||||||
@@ -81,4 +76,55 @@ impl MemosClient {
|
|||||||
let result: ListMemosResponse = response.json().await?;
|
let result: ListMemosResponse = response.json().await?;
|
||||||
Ok(result.memos)
|
Ok(result.memos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn list_memos_with_token(&self, page_size: Option<i32>, page_token: Option<&str>) -> Result<ListMemosResponse> {
|
||||||
|
let url = format!("{}/api/v1/memos", self.base_url);
|
||||||
|
|
||||||
|
let mut request = self
|
||||||
|
.client
|
||||||
|
.get(&url)
|
||||||
|
.header("Authorization", format!("Bearer {}", self.user_token));
|
||||||
|
|
||||||
|
if let Some(ps) = page_size {
|
||||||
|
request = request.query(&[("pageSize", ps.to_string())]);
|
||||||
|
}
|
||||||
|
if let Some(token) = page_token {
|
||||||
|
request = request.query(&[("pageToken", token)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = request.send().await?;
|
||||||
|
|
||||||
|
if !response.status().is_success() {
|
||||||
|
let status = response.status();
|
||||||
|
let error_text = response.text().await.unwrap_or_default();
|
||||||
|
anyhow::bail!("Failed to list memos: {} - {}", status, error_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: ListMemosResponse = response.json().await?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list_all_memos(&self) -> Result<Vec<Memo>> {
|
||||||
|
let mut all_memos = Vec::new();
|
||||||
|
let mut page_token: Option<String> = None;
|
||||||
|
let page_size = 100;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let response = self.list_memos_with_token(Some(page_size), page_token.as_deref()).await?;
|
||||||
|
let memos = response.memos;
|
||||||
|
|
||||||
|
if memos.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
all_memos.extend(memos);
|
||||||
|
page_token = response.next_page_token;
|
||||||
|
|
||||||
|
if page_token.is_none() || page_token.as_ref().map_or(true, |t| t.is_empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(all_memos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ async fn run_cli_command(command: Command) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::List(cmd) => match client.list_memos(Some(cmd.limit), Some(cmd.offset)).await {
|
Command::List(cmd) => match client.list_memos(Some(cmd.page_size), cmd.page_token.as_deref()).await {
|
||||||
Ok(memos) => {
|
Ok(memos) => {
|
||||||
if memos.is_empty() {
|
if memos.is_empty() {
|
||||||
println!("No memos found.");
|
println!("No memos found.");
|
||||||
@@ -341,7 +341,7 @@ async fn run_tui_mode() -> Result<()> {
|
|||||||
|
|
||||||
async fn load_memos(app: &App) -> Result<Vec<Memo>> {
|
async fn load_memos(app: &App) -> Result<Vec<Memo>> {
|
||||||
let client = app.get_client().await?;
|
let client = app.get_client().await?;
|
||||||
let memos = client.list_memos(None, None).await?;
|
let memos = client.list_all_memos().await?;
|
||||||
Ok(memos)
|
Ok(memos)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@ async fn handle_list_memos_input(app: &mut App, key: &KeyEvent) -> Result<()> {
|
|||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Char('r') | KeyCode::Char('R') => {
|
KeyCode::Char('r') | KeyCode::Char('R') => {
|
||||||
let client = app.get_client().await?;
|
let client = app.get_client().await?;
|
||||||
match client.list_memos(None, None).await {
|
match client.list_all_memos().await {
|
||||||
Ok(memos) => {
|
Ok(memos) => {
|
||||||
app.memos = memos;
|
app.memos = memos;
|
||||||
app.current_page = 0;
|
app.current_page = 0;
|
||||||
|
|||||||
@@ -3,10 +3,16 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct CreateMemoRequest {
|
pub struct CreateMemoRequest {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
|
#[serde(default = "default_state")]
|
||||||
|
pub state: String,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub visibility: Option<String>,
|
pub visibility: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_state() -> String {
|
||||||
|
"STATE_UNSPECIFIED".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Memo {
|
pub struct Memo {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
|
|||||||
Reference in New Issue
Block a user