From afc69d605dd0d1d72e200f7ca1b182a0b98a674b Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Thu, 12 Dec 2024 19:49:36 +0100 Subject: [PATCH 01/18] added: error handling and nice output for cell set --- module/move/gspread/src/actions/gspread.rs | 9 ++++++- .../gspread/src/actions/gspread_cell_set.rs | 26 +++++++++++++------ .../move/gspread/src/commands/gspread_cell.rs | 4 +-- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/module/move/gspread/src/actions/gspread.rs b/module/move/gspread/src/actions/gspread.rs index 60b0fd980c..176be99829 100644 --- a/module/move/gspread/src/actions/gspread.rs +++ b/module/move/gspread/src/actions/gspread.rs @@ -25,11 +25,17 @@ mod private google_sheets4::Error ), - #[ error( "Invalid URL format: {0}" ) ] + #[ error( "Invalid URL format:\n{0}" ) ] InvalidUrl ( String ), + + #[ error( "Cell error:\n{0}" ) ] + CellError + ( + String + ), } pub fn get_spreadsheet_id_from_url @@ -60,6 +66,7 @@ crate::mod_interface! { own use { + Error, Result, get_spreadsheet_id_from_url, }; diff --git a/module/move/gspread/src/actions/gspread_cell_set.rs b/module/move/gspread/src/actions/gspread_cell_set.rs index 818a667f1c..6d4aeb160f 100644 --- a/module/move/gspread/src/actions/gspread_cell_set.rs +++ b/module/move/gspread/src/actions/gspread_cell_set.rs @@ -9,7 +9,7 @@ mod private { use google_sheets4::api::ValueRange; use crate::*; - use actions::gspread::Result; + use actions::gspread::{ Result, Error }; use client::SheetsType; use ser::JsonValue; @@ -20,7 +20,7 @@ mod private table_name : &str, cell_id : &str, value : &str - ) -> Result< i32 > + ) -> Result< String > { let value = JsonValue::String( value.to_string() ); @@ -30,17 +30,27 @@ mod private ..ValueRange::default() }; - let result = hub + match hub .spreadsheets() .values_update( value_range, spreadsheet_id, format!( "{}!{}", table_name, cell_id ).as_str() ) .value_input_option( "USER_ENTERED" ) .doit() - .await? - .1 - .updated_cells - .unwrap(); + .await + { + Ok( ( _, response) ) => + { + match response.updated_cells + { + Some( number ) => Ok( format!( "You successfully update {} cell!", number ) ), + None => Err( Error::CellError( "Some problem with cell updating".to_string() ) ) + } + } + Err( error) => + { + Err( Error::ApiError( error ) ) + } + } - Ok( result ) } } diff --git a/module/move/gspread/src/commands/gspread_cell.rs b/module/move/gspread/src/commands/gspread_cell.rs index 057da2dd09..6312cbfc85 100644 --- a/module/move/gspread/src/commands/gspread_cell.rs +++ b/module/move/gspread/src/commands/gspread_cell.rs @@ -103,8 +103,8 @@ mod private match result { - Ok( value ) => println!( "Success: {:?}", value ), - Err( error ) => println!( "Error: {}", error ), + Ok( msg ) => println!( "{}", msg ), + Err( error ) => println!( "Error:\n{}", error ), } } From 9a0921b8cd61ebaf9aab36d37aec25bb52f08ca7 Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Fri, 13 Dec 2024 03:59:59 +0100 Subject: [PATCH 02/18] fixed: cell get --- .../gspread/src/actions/gspread_cell_get.rs | 28 +++++++++++++------ .../gspread/src/actions/gspread_cell_set.rs | 6 +++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/module/move/gspread/src/actions/gspread_cell_get.rs b/module/move/gspread/src/actions/gspread_cell_get.rs index 3a4d6b1be3..033aa3372c 100644 --- a/module/move/gspread/src/actions/gspread_cell_get.rs +++ b/module/move/gspread/src/actions/gspread_cell_get.rs @@ -6,8 +6,14 @@ mod private { + + use crate::*; - use actions::gspread::Result; + use actions::gspread:: + { + Error, + Result + }; use client::SheetsType; use ser::JsonValue; @@ -19,18 +25,22 @@ mod private cell_id : &str, ) -> Result< JsonValue > { - let result = hub + match hub .spreadsheets() .values_get( spreadsheet_id, format!( "{}!{}", table_name, cell_id ).as_str() ) .doit() - .await? - .1 - .values; - - match result + .await { - Some( values ) => Ok( values.get( 0 ).unwrap().get( 0 ).unwrap().clone() ), - None => Ok( JsonValue::Null.clone() ) + Ok( (_, response ) ) => + match response.values + { + Some( values ) => Ok( values.get( 0 ).unwrap().get( 0 ).unwrap().clone() ), + None => Ok( JsonValue::Null.clone() ) + } + Err( error ) => + { + Err( Error::ApiError( error ) ) + } } } diff --git a/module/move/gspread/src/actions/gspread_cell_set.rs b/module/move/gspread/src/actions/gspread_cell_set.rs index 6d4aeb160f..70e667726f 100644 --- a/module/move/gspread/src/actions/gspread_cell_set.rs +++ b/module/move/gspread/src/actions/gspread_cell_set.rs @@ -9,7 +9,11 @@ mod private { use google_sheets4::api::ValueRange; use crate::*; - use actions::gspread::{ Result, Error }; + use actions::gspread:: + { + Result, + Error + }; use client::SheetsType; use ser::JsonValue; From aea57b0936002468f29e8c2fd2f9630d7494df3b Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Fri, 13 Dec 2024 04:33:03 +0100 Subject: [PATCH 03/18] fixed: error handling for all actions --- module/move/gspread/src/actions/gspread.rs | 12 ++++++ .../gspread/src/actions/gspread_cells_set.rs | 37 ++++++++++++------- .../gspread/src/actions/gspread_get_header.rs | 30 ++++++++++----- .../gspread/src/actions/gspread_get_rows.rs | 26 +++++++++---- .../move/gspread/src/commands/gspread_cell.rs | 14 +++---- .../gspread/src/commands/gspread_cells.rs | 9 ++--- .../gspread/src/commands/gspread_header.rs | 19 +++++----- .../move/gspread/src/commands/gspread_rows.rs | 17 ++++----- 8 files changed, 102 insertions(+), 62 deletions(-) diff --git a/module/move/gspread/src/actions/gspread.rs b/module/move/gspread/src/actions/gspread.rs index 176be99829..528c631bb8 100644 --- a/module/move/gspread/src/actions/gspread.rs +++ b/module/move/gspread/src/actions/gspread.rs @@ -36,6 +36,18 @@ mod private ( String ), + + #[ error( "Invalid JSON format:\n{0}" ) ] + InvalidJSON + ( + String + ), + + #[ error( "Parse error:\n{0}" ) ] + ParseError + ( + String + ) } pub fn get_spreadsheet_id_from_url diff --git a/module/move/gspread/src/actions/gspread_cells_set.rs b/module/move/gspread/src/actions/gspread_cells_set.rs index a6528b6c4b..9d38a93d96 100644 --- a/module/move/gspread/src/actions/gspread_cells_set.rs +++ b/module/move/gspread/src/actions/gspread_cells_set.rs @@ -5,6 +5,11 @@ mod private { use crate::*; + use actions::gspread:: + { + Error, + Result + }; use google_sheets4::api:: { BatchUpdateValuesRequest, @@ -29,11 +34,11 @@ mod private fn parse_json ( json_str : &str - ) -> Result< ParsedJson, String > + ) -> Result< ParsedJson > { serde_json::from_str::< ParsedJson >( json_str ).map_err ( - | err | format!( "Failed to parse JSON: {}", err ) + | error | Error::InvalidJSON( format!( "Failed to parse JSON: {}", error ) ) ) } @@ -42,7 +47,7 @@ mod private fn check_select_row_by_key ( key : &str - ) -> Result< (), String > + ) -> Result< () > { let keys = vec![ "id" ]; if keys.contains( &key ) @@ -51,14 +56,17 @@ mod private } else { - Err( format!( "Invalid select_row_by_key: '{}'. Allowed keys: {:?}", key, keys ) ) + Err + ( + Error::ParseError( format!( "Invalid select_row_by_key: '{}'. Allowed keys: {:?}", key, keys ) ) + ) } } fn is_all_uppercase_letters ( s : &str - ) -> Result< (), String > + ) -> Result< () > { if s.chars().all( | c | c.is_ascii_uppercase() ) { @@ -66,7 +74,10 @@ mod private } else { - Err( format!( "The string '{}' contains invalid characters. Only uppercase letters (A-Z) are allowed.", s ) ) + Err + ( + Error::ParseError( format!( "The string '{}' contains invalid characters. Only uppercase letters (A-Z) are allowed.", s ) ) + ) } } @@ -77,7 +88,7 @@ mod private json_str : &str, spreadsheet_id : &str, table_name : &str - ) -> Result< String, String > + ) -> Result< String > { check_select_row_by_key( select_row_by_key )?; @@ -86,7 +97,8 @@ mod private let row_id = pairs .columns .remove( select_row_by_key ) - .ok_or_else( || format!( "Key '{}' not found in JSON", select_row_by_key ) )?; + .ok_or_else( || Error::ParseError( format!( "Key '{}' not found in JSON", select_row_by_key ) ) )?; + let mut value_ranges= Vec::new(); @@ -112,18 +124,17 @@ mod private ..Default::default() }; - let result = hub + match hub .spreadsheets() .values_batch_update( req, spreadsheet_id ) .doit() - .await; - - match result + .await { Ok( _ ) => Ok( format!( "Cells were sucsessfully updated!" ) ), - Err( error ) => Err( format!( "{}", error ) ) + Err( error ) => Err( Error::ApiError( error ) ) } } + } crate::mod_interface! diff --git a/module/move/gspread/src/actions/gspread_get_header.rs b/module/move/gspread/src/actions/gspread_get_header.rs index 8f7b83c477..e8de1dc4bc 100644 --- a/module/move/gspread/src/actions/gspread_get_header.rs +++ b/module/move/gspread/src/actions/gspread_get_header.rs @@ -10,7 +10,11 @@ mod private use std::fmt; use crate::*; use client::SheetsType; - use actions::gspread::Result; + use actions::gspread:: + { + Error, + Result + }; use format_tools::AsTable; use util::display_table::display_header; use ser::JsonValue; @@ -37,19 +41,27 @@ mod private ( hub : &SheetsType, spreadsheet_id : &str, - table_name: &str) -> Result< Vec< Vec< JsonValue > > > + table_name : &str + ) -> Result< Vec< Vec< JsonValue > > > { - let result = hub + match hub .spreadsheets() .values_get( spreadsheet_id, format!( "{}!A1:Z1", table_name ).as_str() ) .doit() - .await? - .1 - .values - .unwrap_or_else( | | Vec::new() ); - - Ok( result ) + .await + { + Ok( ( _, response ) ) => + { + match response.values + { + Some( values ) => Ok( values ), + None => Ok( Vec::new() ) + } + }, + Err( error ) => Err( Error::ApiError( error ) ) + } } + } crate::mod_interface! diff --git a/module/move/gspread/src/actions/gspread_get_rows.rs b/module/move/gspread/src/actions/gspread_get_rows.rs index 3a083217ed..7f1f7a5c26 100644 --- a/module/move/gspread/src/actions/gspread_get_rows.rs +++ b/module/move/gspread/src/actions/gspread_get_rows.rs @@ -9,7 +9,11 @@ mod private { use crate::*; use client::SheetsType; - use actions::gspread::Result; + use actions::gspread:: + { + Error, + Result + }; use ser::JsonValue; pub async fn action @@ -19,16 +23,22 @@ mod private table_name : &str ) -> Result< Vec< Vec < JsonValue > > > { - let result = hub + match hub .spreadsheets() .values_get( spreadsheet_id, format!( "{}!A2:Z", table_name ).as_str() ) .doit() - .await? - .1 - .values - .unwrap_or_else( | | Vec::new() ); - - Ok( result ) + .await + { + Ok( ( _, response ) ) => + { + match response.values + { + Some( values ) => Ok( values ), + None => Ok( Vec::new() ) + } + }, + Err( error ) => Err( Error::ApiError( error ) ) + } } } diff --git a/module/move/gspread/src/commands/gspread_cell.rs b/module/move/gspread/src/commands/gspread_cell.rs index 6312cbfc85..bcdc68cc1a 100644 --- a/module/move/gspread/src/commands/gspread_cell.rs +++ b/module/move/gspread/src/commands/gspread_cell.rs @@ -65,15 +65,14 @@ mod private } }; - let result = actions::gspread_cell_get::action + match actions::gspread_cell_get::action ( hub, spreadsheet_id, tab.as_str(), cel.as_str() - ).await; - - match result + ) + .await { Ok( value ) => println!( "Value: {}", value ), Err( error ) => println!( "Error: {}", error ), @@ -92,16 +91,15 @@ mod private } }; - let result = actions::gspread_cell_set::action + match actions::gspread_cell_set::action ( hub, spreadsheet_id, tab.as_str(), cel.as_str(), val.as_str() - ).await; - - match result + ) + .await { Ok( msg ) => println!( "{}", msg ), Err( error ) => println!( "Error:\n{}", error ), diff --git a/module/move/gspread/src/commands/gspread_cells.rs b/module/move/gspread/src/commands/gspread_cells.rs index 13ecf1e378..99e252e371 100644 --- a/module/move/gspread/src/commands/gspread_cells.rs +++ b/module/move/gspread/src/commands/gspread_cells.rs @@ -51,19 +51,18 @@ mod private } }; - let result = actions::gspread_cells_set::action + match actions::gspread_cells_set::action ( &hub, select_row_by_key.as_str(), json.as_str(), spreadsheet_id, tab.as_str() - ).await; - - match result + ) + .await { Ok( msg ) => println!( "{}", msg ), - Err( error ) => println!( "{}", error ) + Err( error ) => println!( "Error:\n{}", error ) } } } diff --git a/module/move/gspread/src/commands/gspread_header.rs b/module/move/gspread/src/commands/gspread_header.rs index 5048d3e4ed..0fd56741e2 100644 --- a/module/move/gspread/src/commands/gspread_header.rs +++ b/module/move/gspread/src/commands/gspread_header.rs @@ -51,14 +51,13 @@ mod private } }; - let result = actions::gspread_get_header::action - ( - hub, - spreadsheet_id, - tab.as_str() - ).await; - - match result + match actions::gspread_get_header::action + ( + hub, + spreadsheet_id, + tab.as_str() + ) + .await { Ok( header ) => { @@ -67,9 +66,9 @@ mod private .map( | row | RowWrapper{ max_len: row.len(), row } ) .collect(); - println!( "Header: \n {}", Report{ rows: header_wrapped } ); + println!( "Header:\n{}", Report{ rows: header_wrapped } ); } - Err( error ) => println!( "Error: {}", error ), + Err( error ) => eprintln!( "Error:\n{}", error ), } } } diff --git a/module/move/gspread/src/commands/gspread_rows.rs b/module/move/gspread/src/commands/gspread_rows.rs index 426d7f2dde..6c526d0f78 100644 --- a/module/move/gspread/src/commands/gspread_rows.rs +++ b/module/move/gspread/src/commands/gspread_rows.rs @@ -50,26 +50,25 @@ mod private } }; - let result = actions::gspread_get_rows::action + match actions::gspread_get_rows::action ( hub, spreadsheet_id, tab.as_str() - ).await; - - match result + ) + .await { Ok( rows ) => { let max_len = rows.iter().map(|row| row.len()).max().unwrap_or(0); let rows_wrapped: Vec = rows - .into_iter() - .map(|row| RowWrapper { row, max_len }) - .collect(); + .into_iter() + .map(|row| RowWrapper { row, max_len }) + .collect(); - println!( "Rows: \n {}", Report{ rows: rows_wrapped } ); + println!( "Rows:\n{}", Report{ rows: rows_wrapped } ); } - Err( error ) => println!( "Error: {}", error ), + Err( error ) => eprintln!( "Error:\n{}", error ), } } } From 6516d0a3bb8b0faed556c4e29f156aa4325021a3 Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Fri, 13 Dec 2024 05:30:45 +0100 Subject: [PATCH 04/18] added: help description --- .../gspread/src/actions/gspread_cell_get.rs | 5 +-- .../gspread/src/actions/gspread_cell_set.rs | 5 +-- module/move/gspread/src/commands/gspread.rs | 12 ++++-- .../move/gspread/src/commands/gspread_cell.rs | 38 ++++++++++++------- .../gspread/src/commands/gspread_cells.rs | 22 ++++++++--- 5 files changed, 52 insertions(+), 30 deletions(-) diff --git a/module/move/gspread/src/actions/gspread_cell_get.rs b/module/move/gspread/src/actions/gspread_cell_get.rs index 033aa3372c..effa3a9170 100644 --- a/module/move/gspread/src/actions/gspread_cell_get.rs +++ b/module/move/gspread/src/actions/gspread_cell_get.rs @@ -37,10 +37,7 @@ mod private Some( values ) => Ok( values.get( 0 ).unwrap().get( 0 ).unwrap().clone() ), None => Ok( JsonValue::Null.clone() ) } - Err( error ) => - { - Err( Error::ApiError( error ) ) - } + Err( error ) => Err( Error::ApiError( error ) ) } } diff --git a/module/move/gspread/src/actions/gspread_cell_set.rs b/module/move/gspread/src/actions/gspread_cell_set.rs index 70e667726f..62297b7331 100644 --- a/module/move/gspread/src/actions/gspread_cell_set.rs +++ b/module/move/gspread/src/actions/gspread_cell_set.rs @@ -49,10 +49,7 @@ mod private None => Err( Error::CellError( "Some problem with cell updating".to_string() ) ) } } - Err( error) => - { - Err( Error::ApiError( error ) ) - } + Err( error) => Err( Error::ApiError( error ) ) } } diff --git a/module/move/gspread/src/commands/gspread.rs b/module/move/gspread/src/commands/gspread.rs index 8398aa3ec6..5cbaaeeb13 100644 --- a/module/move/gspread/src/commands/gspread.rs +++ b/module/move/gspread/src/commands/gspread.rs @@ -22,35 +22,41 @@ mod private #[ derive( Debug, Parser ) ] pub struct CommonArgs { - #[ arg( long ) ] + #[ arg( long, help = "Full URL of Google Sheet.\n\ + It has to be inside of '' to avoid parse errors.\n\ + Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] pub url : String, - #[ arg( long ) ] + #[ arg( long, help = "Sheet name.\nExample: List1" ) ] pub tab : String } #[ derive( Debug, Subcommand ) ] pub enum Command { - + + /// Command to get header of a sheet. Header is a first raw. #[ command ( name = "header" ) ] Header ( CommonArgs ), + /// Command to get all raws of a sheet but not header. #[ command( name = "rows" ) ] Rows ( CommonArgs ), + /// Command to get or update a cell from a sheet. #[ command ( subcommand, name = "cell" ) ] Cell ( gspread_cell::Commands ), + /// Commands to set a new value to a cell or get a value from a cell. #[ command ( subcommand, name = "cells" ) ] Cells ( diff --git a/module/move/gspread/src/commands/gspread_cell.rs b/module/move/gspread/src/commands/gspread_cell.rs index bcdc68cc1a..ea213d92a2 100644 --- a/module/move/gspread/src/commands/gspread_cell.rs +++ b/module/move/gspread/src/commands/gspread_cell.rs @@ -15,32 +15,42 @@ mod private #[ derive( Debug, Subcommand ) ] pub enum Commands { + /// Command to get a value from a sheet's cell #[ command( name = "get" ) ] Get { - #[ arg( long ) ] + #[ arg( long, help = "Full URL of Google Sheet.\n\ + It has to be inside of '' to avoid parse errors.\n\ + Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] url : String, - #[ arg( long ) ] + #[ arg( long, help = "Sheet name.\nExample: List1" ) ] tab : String, - #[ arg( long ) ] - cel : String, + #[ arg( long, help = "Cell id. You can set it in format:\n \ + - A1, where A is column name and 1 is row number\n\ + Example: --cell A4" ) ] + cell : String, }, + /// Command to set a new value to a sheet's cell. #[ command( name = "set" ) ] Set { - #[ arg( long ) ] + #[ arg( long, help = "Full URL of Google Sheet.\n\ + It has to be inside of '' to avoid parse errors.\n\ + Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] url : String, - #[ arg( long ) ] + #[ arg( long, help = "Sheet name.\nExample: List1" ) ] tab : String, - #[ arg( long ) ] - cel : String, + #[ arg( long, help = "Cell id. You can set it in format:\n \ + - A1, where A is column name and 1 is row number\n\ + Example: --cell A4" ) ] + cell : String, - #[ arg( long ) ] + #[ arg( long, help = "Value you want to set. It can be written on any language.\nExample: --val hello" ) ] val : String } } @@ -53,7 +63,7 @@ mod private { match commands { - Commands::Get { url, tab, cel } => + Commands::Get { url, tab, cell } => { let spreadsheet_id = match get_spreadsheet_id_from_url( url.as_str() ) { @@ -70,16 +80,16 @@ mod private hub, spreadsheet_id, tab.as_str(), - cel.as_str() + cell.as_str() ) .await { Ok( value ) => println!( "Value: {}", value ), - Err( error ) => println!( "Error: {}", error ), + Err( error ) => println!( "Error:\n{}", error ), } }, - Commands::Set { url, tab, cel, val } => + Commands::Set { url, tab, cell, val } => { let spreadsheet_id = match get_spreadsheet_id_from_url( url.as_str() ) { @@ -96,7 +106,7 @@ mod private hub, spreadsheet_id, tab.as_str(), - cel.as_str(), + cell.as_str(), val.as_str() ) .await diff --git a/module/move/gspread/src/commands/gspread_cells.rs b/module/move/gspread/src/commands/gspread_cells.rs index 99e252e371..3c0f2538be 100644 --- a/module/move/gspread/src/commands/gspread_cells.rs +++ b/module/move/gspread/src/commands/gspread_cells.rs @@ -13,19 +13,31 @@ mod private #[ derive( Debug, Subcommand ) ] pub enum Commands { + /// Command to set values range to a google sheet #[ command( name = "set" ) ] Set { - #[ arg( long ) ] + #[ arg( long, help = "Identifier of a row. Available identifiers: id (row's unique identifier).\n\ + Example: --select_row_by_key \"id\"" ) ] select_row_by_key : String, - - #[ arg( long ) ] + + #[ arg( long, help = "Value range. It must contain select_row_by_key. + The key is a column name (not a header name, but a column name, which can only contain Latin letters). + Every key and value must be a string. + Depending on the shell, different handling might be required.\n\ + Examples:\n\ + 1. --json '\"id\": \"3\", \"A\": \"1\", \"B\": \"2\"'\n\ + 2. --json \"\"id\": \"3\", \"A\": \"1\", \"B\": \"2\"\"\n\ + 3. --json '\\\"id\\\": \\\"3\\\", \\\"A\\\": \\\"1\\\", \\\"B\\\": \\\"2\\\"'\n\ + 4. --json \"\\\"id\\\": \\\"3\\\", \\\"A\\\": \\\"1\\\", \\\"B\\\": \\\"2\\\"\" " ) ] json : String, - #[ arg( long ) ] + #[ arg( long, help = "Full URL of Google Sheet.\n\ + It has to be inside of '' to avoid parse errors.\n\ + Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] url : String, - #[ arg( long ) ] + #[ arg( long, help = "Sheet name.\nExample: List1" ) ] tab : String } From afb293ce376589205cbe5e2976c9403ef5d3c496 Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Fri, 20 Dec 2024 13:57:38 +0100 Subject: [PATCH 05/18] fixed gspread_cell_set action returning value --- module/move/gspread/Cargo.toml | 1 + module/move/gspread/src/actions/gspread_cell_set.rs | 4 ++-- module/move/gspread/src/commands/gspread_cell.rs | 2 +- module/move/gspread/src/commands/gspread_header.rs | 2 +- module/move/gspread/src/debug/row_wrapper.rs | 11 ++++++++++- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/module/move/gspread/Cargo.toml b/module/move/gspread/Cargo.toml index 8d1d86b4a3..a75b3a2d2b 100644 --- a/module/move/gspread/Cargo.toml +++ b/module/move/gspread/Cargo.toml @@ -44,6 +44,7 @@ error_tools = "0.19.0" derive_tools = { version = "0.32.0", features = ["full"] } serde_json = "1.0.132" regex = "1.11.1" +unicode-width = "0.2.0" [dev-dependencies] test_tools = { workspace = true } diff --git a/module/move/gspread/src/actions/gspread_cell_set.rs b/module/move/gspread/src/actions/gspread_cell_set.rs index 62297b7331..5743a45f97 100644 --- a/module/move/gspread/src/actions/gspread_cell_set.rs +++ b/module/move/gspread/src/actions/gspread_cell_set.rs @@ -24,7 +24,7 @@ mod private table_name : &str, cell_id : &str, value : &str - ) -> Result< String > + ) -> Result< i32 > { let value = JsonValue::String( value.to_string() ); @@ -45,7 +45,7 @@ mod private { match response.updated_cells { - Some( number ) => Ok( format!( "You successfully update {} cell!", number ) ), + Some( number ) => Ok( number ), None => Err( Error::CellError( "Some problem with cell updating".to_string() ) ) } } diff --git a/module/move/gspread/src/commands/gspread_cell.rs b/module/move/gspread/src/commands/gspread_cell.rs index ea213d92a2..84a0cd2116 100644 --- a/module/move/gspread/src/commands/gspread_cell.rs +++ b/module/move/gspread/src/commands/gspread_cell.rs @@ -111,7 +111,7 @@ mod private ) .await { - Ok( msg ) => println!( "{}", msg ), + Ok( number ) => println!( "You successfully update {} cell!", number ), Err( error ) => println!( "Error:\n{}", error ), } } diff --git a/module/move/gspread/src/commands/gspread_header.rs b/module/move/gspread/src/commands/gspread_header.rs index 0fd56741e2..6ee00db284 100644 --- a/module/move/gspread/src/commands/gspread_header.rs +++ b/module/move/gspread/src/commands/gspread_header.rs @@ -65,7 +65,7 @@ mod private .into_iter() .map( | row | RowWrapper{ max_len: row.len(), row } ) .collect(); - + println!( "Header:\n{}", Report{ rows: header_wrapped } ); } Err( error ) => eprintln!( "Error:\n{}", error ), diff --git a/module/move/gspread/src/debug/row_wrapper.rs b/module/move/gspread/src/debug/row_wrapper.rs index b8e1635ac7..23051e6a7a 100644 --- a/module/move/gspread/src/debug/row_wrapper.rs +++ b/module/move/gspread/src/debug/row_wrapper.rs @@ -7,7 +7,7 @@ use super::*; use crate::*; use ser::JsonValue; - +use unicode_width::UnicodeWidthStr; #[ derive( Debug ) ] pub struct RowWrapper @@ -43,6 +43,15 @@ for RowWrapper { let column_name = format!( "Column{}", index ); let title = Box::leak( column_name.into_boxed_str() ) as &str; + + let value_str = value.to_string().trim_matches('"').to_string(); + let char_count = value_str.chars().count(); + let byte_count = value_str.as_bytes().len(); + let display_width = UnicodeWidthStr::width(value_str.as_str()); + + eprintln!("DEBUG: Value: {}, Chars: {}, Bytes: {}, Display Width: {}", + value_str, char_count, byte_count, display_width); + dst.push( ( title, Some( Cow::Owned( value.to_string() ) ) ) ) } From fcb2f10f55a503ecf5e9eabcc50817325009d4e0 Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Fri, 20 Dec 2024 14:05:19 +0100 Subject: [PATCH 06/18] fixed: sheet name in description --- module/move/gspread/src/commands/gspread.rs | 2 +- module/move/gspread/src/commands/gspread_cell.rs | 4 ++-- module/move/gspread/src/commands/gspread_cells.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/module/move/gspread/src/commands/gspread.rs b/module/move/gspread/src/commands/gspread.rs index 5cbaaeeb13..479eda899a 100644 --- a/module/move/gspread/src/commands/gspread.rs +++ b/module/move/gspread/src/commands/gspread.rs @@ -27,7 +27,7 @@ mod private Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] pub url : String, - #[ arg( long, help = "Sheet name.\nExample: List1" ) ] + #[ arg( long, help = "Sheet name.\nExample: Sheet1" ) ] pub tab : String } diff --git a/module/move/gspread/src/commands/gspread_cell.rs b/module/move/gspread/src/commands/gspread_cell.rs index 84a0cd2116..7f632ab724 100644 --- a/module/move/gspread/src/commands/gspread_cell.rs +++ b/module/move/gspread/src/commands/gspread_cell.rs @@ -24,7 +24,7 @@ mod private Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] url : String, - #[ arg( long, help = "Sheet name.\nExample: List1" ) ] + #[ arg( long, help = "Sheet name.\nExample: Sheet1" ) ] tab : String, #[ arg( long, help = "Cell id. You can set it in format:\n \ @@ -42,7 +42,7 @@ mod private Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] url : String, - #[ arg( long, help = "Sheet name.\nExample: List1" ) ] + #[ arg( long, help = "Sheet name.\nExample: Sheet1" ) ] tab : String, #[ arg( long, help = "Cell id. You can set it in format:\n \ diff --git a/module/move/gspread/src/commands/gspread_cells.rs b/module/move/gspread/src/commands/gspread_cells.rs index 3c0f2538be..87762be0e3 100644 --- a/module/move/gspread/src/commands/gspread_cells.rs +++ b/module/move/gspread/src/commands/gspread_cells.rs @@ -37,7 +37,7 @@ mod private Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] url : String, - #[ arg( long, help = "Sheet name.\nExample: List1" ) ] + #[ arg( long, help = "Sheet name.\nExample: Sheet1" ) ] tab : String } From b319241b67d8034efbc6c4467a56a64ededc1d9f Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Fri, 20 Dec 2024 15:05:39 +0100 Subject: [PATCH 07/18] fixed: commands examples --- module/move/gspread/src/commands/gspread.rs | 12 ++++++++++++ module/move/gspread/src/commands/gspread_cell.rs | 15 +++++++++++++++ .../move/gspread/src/commands/gspread_cells.rs | 16 ++++++++++++---- module/move/gspread/src/debug/row_wrapper.rs | 12 ++++++------ 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/module/move/gspread/src/commands/gspread.rs b/module/move/gspread/src/commands/gspread.rs index 479eda899a..59fe8cae55 100644 --- a/module/move/gspread/src/commands/gspread.rs +++ b/module/move/gspread/src/commands/gspread.rs @@ -36,6 +36,12 @@ mod private { /// Command to get header of a sheet. Header is a first raw. + /// + /// Command example: + /// + /// gspread header + /// --url 'https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0' + /// --tab tab1 #[ command ( name = "header" ) ] Header ( @@ -43,6 +49,12 @@ mod private ), /// Command to get all raws of a sheet but not header. + /// + /// Command example: + /// + /// gspread rows + /// --url 'https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0' + /// --tab tab1 #[ command( name = "rows" ) ] Rows ( diff --git a/module/move/gspread/src/commands/gspread_cell.rs b/module/move/gspread/src/commands/gspread_cell.rs index 7f632ab724..a0fd55f361 100644 --- a/module/move/gspread/src/commands/gspread_cell.rs +++ b/module/move/gspread/src/commands/gspread_cell.rs @@ -16,6 +16,13 @@ mod private pub enum Commands { /// Command to get a value from a sheet's cell + /// + /// Command example: + /// + /// gspread cell get + /// --url 'https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0' + /// --tab tab1 + /// --cell A1 #[ command( name = "get" ) ] Get { @@ -34,6 +41,14 @@ mod private }, /// Command to set a new value to a sheet's cell. + /// + /// Command example: + /// + /// gspread cell set + /// --url 'https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0' + /// --tab tab1 + /// --cell A1 + /// --val 13 #[ command( name = "set" ) ] Set { diff --git a/module/move/gspread/src/commands/gspread_cells.rs b/module/move/gspread/src/commands/gspread_cells.rs index 87762be0e3..5308ad7eaa 100644 --- a/module/move/gspread/src/commands/gspread_cells.rs +++ b/module/move/gspread/src/commands/gspread_cells.rs @@ -14,6 +14,14 @@ mod private pub enum Commands { /// Command to set values range to a google sheet + /// + /// Command example: + /// + /// gspread cells set + /// --url 'https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0' + /// --tab tab1 + /// --select-row-by-key "id" + /// --json '{"id": "2", "A": "1", "B": "2"}' #[ command( name = "set" ) ] Set { @@ -26,10 +34,10 @@ mod private Every key and value must be a string. Depending on the shell, different handling might be required.\n\ Examples:\n\ - 1. --json '\"id\": \"3\", \"A\": \"1\", \"B\": \"2\"'\n\ - 2. --json \"\"id\": \"3\", \"A\": \"1\", \"B\": \"2\"\"\n\ - 3. --json '\\\"id\\\": \\\"3\\\", \\\"A\\\": \\\"1\\\", \\\"B\\\": \\\"2\\\"'\n\ - 4. --json \"\\\"id\\\": \\\"3\\\", \\\"A\\\": \\\"1\\\", \\\"B\\\": \\\"2\\\"\" " ) ] + 1. --json '{\"id\": \"3\", \"A\": \"1\", \"B\": \"2\"}'\n\ + 2. --json \"{\"id\": \"3\", \"A\": \"1\", \"B\": \"2\"}\"\n\ + 3. --json '{\\\"id\\\": \\\"3\\\", \\\"A\\\": \\\"1\\\", \\\"B\\\": \\\"2\\\"}'\n\ + 4. --json \"{\\\"id\\\": \\\"3\\\", \\\"A\\\": \\\"1\\\", \\\"B\\\": \\\"2\\\"}\" " ) ] json : String, #[ arg( long, help = "Full URL of Google Sheet.\n\ diff --git a/module/move/gspread/src/debug/row_wrapper.rs b/module/move/gspread/src/debug/row_wrapper.rs index 23051e6a7a..45d3bea21c 100644 --- a/module/move/gspread/src/debug/row_wrapper.rs +++ b/module/move/gspread/src/debug/row_wrapper.rs @@ -44,13 +44,13 @@ for RowWrapper let column_name = format!( "Column{}", index ); let title = Box::leak( column_name.into_boxed_str() ) as &str; - let value_str = value.to_string().trim_matches('"').to_string(); - let char_count = value_str.chars().count(); - let byte_count = value_str.as_bytes().len(); - let display_width = UnicodeWidthStr::width(value_str.as_str()); + // let value_str = value.to_string().trim_matches('"').to_string(); + // let char_count = value_str.chars().count(); + // let byte_count = value_str.as_bytes().len(); + // let display_width = UnicodeWidthStr::width(value_str.as_str()); - eprintln!("DEBUG: Value: {}, Chars: {}, Bytes: {}, Display Width: {}", - value_str, char_count, byte_count, display_width); + // eprintln!("DEBUG: Value: {}, Chars: {}, Bytes: {}, Display Width: {}", + // value_str, char_count, byte_count, display_width); dst.push( ( title, Some( Cow::Owned( value.to_string() ) ) ) ) } From 4e20ccf1225c3283edd303397fdc0073bd74fda5 Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Tue, 24 Dec 2024 03:50:20 +0100 Subject: [PATCH 08/18] added mock for oauth2 --- module/move/gspread/Cargo.toml | 2 + .../move/gspread/tests/mock/header_tests.rs | 80 +++++++++++++++++++ module/move/gspread/tests/mock/mod.rs | 4 + module/move/gspread/tests/smoke_test.rs | 1 - module/move/gspread/tests/tests.rs | 7 +- 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 module/move/gspread/tests/mock/header_tests.rs create mode 100644 module/move/gspread/tests/mock/mod.rs diff --git a/module/move/gspread/Cargo.toml b/module/move/gspread/Cargo.toml index a75b3a2d2b..d4207112f6 100644 --- a/module/move/gspread/Cargo.toml +++ b/module/move/gspread/Cargo.toml @@ -48,3 +48,5 @@ unicode-width = "0.2.0" [dev-dependencies] test_tools = { workspace = true } +httpmock = "0.7.0-rc.1" +reqwest = "0.12.9" \ No newline at end of file diff --git a/module/move/gspread/tests/mock/header_tests.rs b/module/move/gspread/tests/mock/header_tests.rs new file mode 100644 index 0000000000..6f0666dd27 --- /dev/null +++ b/module/move/gspread/tests/mock/header_tests.rs @@ -0,0 +1,80 @@ +use httpmock::prelude::*; +use reqwest; + +#[ tokio::test ] +async fn oauth2_first_endpoint_with_mock() +{ + let server = MockServer::start(); + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/o/oauth2/auth" ) + .query_param( "scope", "https://www.googleapis.com/auth/drive.readonly" ) + .query_param( "access_type", "offline" ) + .query_param( "redirect_uri", "http://localhost:44444" ) + .query_param( "response_type", "code" ) + .query_param( "client_id", "YOUR_CLIENT_ID" ); + then.status( 302 ); + }); + + let response = reqwest::get + ( + server.url + ( + "/o/oauth2/auth?\ + scope=https://www.googleapis.com/auth/drive.readonly&\ + access_type=offline&\ + redirect_uri=http://localhost:44444&\ + response_type=code&\ + client_id=YOUR_CLIENT_ID" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 302 ); +} + + +#[ tokio::test ] +async fn oauth2_second_endpoint_with_mock() +{ + let server = MockServer::start(); + + // url == first endpoint + let mock = server.mock( | when, then | { + when.path( "/o/oauth2/auth" ) + .query_param( "scope", "https://..." ); + then.status( 200 ); + } ); + + // in real program at that point we have to open generated url and give access to our program from browser + let response = reqwest::get( server.url( "/o/oauth2/auth?scope=https://..." ) ).await.unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} + +// #[ tokio::test ] +// async fn get_header_with_mock_test() +// { +// let server = MockServer::start(); +// let mock = server.mock( | when, then | { +// when.method( GET ) +// .path( "/spreadsheets/d/1234" ) +// .query_param( "tab1!A1:Z1" ) +// then.status( 200 ) +// .header( "content-type", "application/json" ) +// .body( r#"["Name", "Surname", "Age"]"# ); +// }); + +// let response = reqwest::get( server.url( "/spreadsheets/d/1234?tab1!A1:Z1" ) ) +// .await +// .unwrap(); + +// mock.assert(); + +// assert_eq!( response.status(), 200 ) +// } \ No newline at end of file diff --git a/module/move/gspread/tests/mock/mod.rs b/module/move/gspread/tests/mock/mod.rs new file mode 100644 index 0000000000..72b0547b7e --- /dev/null +++ b/module/move/gspread/tests/mock/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod header_tests; \ No newline at end of file diff --git a/module/move/gspread/tests/smoke_test.rs b/module/move/gspread/tests/smoke_test.rs index c3163b32ed..28e533e551 100644 --- a/module/move/gspread/tests/smoke_test.rs +++ b/module/move/gspread/tests/smoke_test.rs @@ -1,4 +1,3 @@ - #[ test ] fn local_smoke_test() { diff --git a/module/move/gspread/tests/tests.rs b/module/move/gspread/tests/tests.rs index 201ae26926..4e3c6add8f 100644 --- a/module/move/gspread/tests/tests.rs +++ b/module/move/gspread/tests/tests.rs @@ -1,9 +1,10 @@ - - #[ allow( unused_imports ) ] use gspread as the_module; #[ allow( unused_imports ) ] use test_tools::exposed::*; #[ cfg( feature = "enabled" ) ] -mod inc; \ No newline at end of file +mod inc; + +#[ cfg( feature = "enabled" ) ] +mod mock; \ No newline at end of file From 782c744b98e3c7627fb27d46391f8d19d8ccca2a Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Tue, 24 Dec 2024 03:53:16 +0100 Subject: [PATCH 09/18] with_online added to tests --- module/move/gspread/tests/inc/cell_tests.rs | 8 ++++---- module/move/gspread/tests/inc/cells_tests.rs | 4 ++-- module/move/gspread/tests/inc/header_tests.rs | 8 ++++---- module/move/gspread/tests/inc/rows_tests.rs | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/module/move/gspread/tests/inc/cell_tests.rs b/module/move/gspread/tests/inc/cell_tests.rs index f93ec7b1a4..75c2e75e42 100644 --- a/module/move/gspread/tests/inc/cell_tests.rs +++ b/module/move/gspread/tests/inc/cell_tests.rs @@ -21,7 +21,7 @@ async fn setup() -> ( SheetsType, &'static str, &'static str ) } #[ tokio::test ] -async fn test_get_cell() +async fn test_get_cell_with_online() { let ( hub, spreadsheet_id, table_name ) = setup().await; let cell_id = "R2C1"; @@ -40,7 +40,7 @@ async fn test_get_cell() } #[ tokio::test ] -async fn test_get_cell_empty() +async fn test_get_cell_empty_with_online() { let ( hub, spreadsheet_id, table_name ) = setup().await; let cell_id = "R4C1"; @@ -59,7 +59,7 @@ async fn test_get_cell_empty() } #[ tokio::test ] -async fn test_set_cell() +async fn test_set_cell_with_online() { let ( hub, spreadsheet_id, table_name ) = setup().await; let cell_id = "R2C1"; @@ -79,7 +79,7 @@ async fn test_set_cell() } #[ tokio::test ] -async fn test_set_empty_cell() +async fn test_set_empty_cell_with_online() { let ( hub, spreadsheet_id, table_name ) = setup().await; let cell_id = "R4C1"; diff --git a/module/move/gspread/tests/inc/cells_tests.rs b/module/move/gspread/tests/inc/cells_tests.rs index 4c91a9a19c..64d08a6231 100644 --- a/module/move/gspread/tests/inc/cells_tests.rs +++ b/module/move/gspread/tests/inc/cells_tests.rs @@ -21,7 +21,7 @@ async fn setup() -> ( SheetsType, &'static str, &'static str, &'static str ) } #[ tokio::test ] -async fn test_set_cells() +async fn test_set_cells_with_online() { let ( @@ -48,7 +48,7 @@ async fn test_set_cells() } #[ tokio::test ] -async fn test_set_cells_wrong_row() +async fn test_set_cells_wrong_row_with_online() { let ( diff --git a/module/move/gspread/tests/inc/header_tests.rs b/module/move/gspread/tests/inc/header_tests.rs index 3009a63bb1..fabc7cf6d8 100644 --- a/module/move/gspread/tests/inc/header_tests.rs +++ b/module/move/gspread/tests/inc/header_tests.rs @@ -18,7 +18,7 @@ async fn setup() -> ( SheetsType, &'static str ) ( hub, spreadsheet_id ) } #[ tokio::test ] -async fn test_get_header() +async fn test_get_header_with_online() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab1"; @@ -36,7 +36,7 @@ async fn test_get_header() } #[ tokio::test ] -async fn test_get_header_with_spaces() +async fn test_get_header_with_spaces_with_online() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab2"; @@ -54,7 +54,7 @@ async fn test_get_header_with_spaces() } #[ tokio::test ] -async fn test_get_header_empty() +async fn test_get_header_empty_with_online() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab3"; @@ -72,7 +72,7 @@ async fn test_get_header_empty() } #[ tokio::test ] -async fn test_get_header_with_empty_end() +async fn test_get_header_with_empty_end_with_online() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab4"; diff --git a/module/move/gspread/tests/inc/rows_tests.rs b/module/move/gspread/tests/inc/rows_tests.rs index d9032f8544..0cc87e3ac0 100644 --- a/module/move/gspread/tests/inc/rows_tests.rs +++ b/module/move/gspread/tests/inc/rows_tests.rs @@ -19,7 +19,7 @@ async fn setup() -> ( SheetsType, &'static str ) } #[ tokio::test ] -async fn test_get_rows() +async fn test_get_rows_with_online() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab1"; @@ -47,7 +47,7 @@ async fn test_get_rows() } #[ tokio::test ] -async fn test_get_rows_with_spaces() +async fn test_get_rows_with_spaces_with_online() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab2"; @@ -75,7 +75,7 @@ async fn test_get_rows_with_spaces() } #[ tokio::test ] -async fn test_get_rows_empty() +async fn test_get_rows_empty_with_online() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab3"; From aa1ca668b2163846cec587946d8d8b85c1d1aad8 Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Tue, 24 Dec 2024 04:24:54 +0100 Subject: [PATCH 10/18] added third oauth2 endpoint --- .../move/gspread/tests/mock/header_tests.rs | 59 ++++++++++++------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/module/move/gspread/tests/mock/header_tests.rs b/module/move/gspread/tests/mock/header_tests.rs index 6f0666dd27..b427c0c7ce 100644 --- a/module/move/gspread/tests/mock/header_tests.rs +++ b/module/move/gspread/tests/mock/header_tests.rs @@ -1,5 +1,6 @@ use httpmock::prelude::*; use reqwest; +use serde_json::json; #[ tokio::test ] async fn oauth2_first_endpoint_with_mock() @@ -46,7 +47,7 @@ async fn oauth2_second_endpoint_with_mock() let mock = server.mock( | when, then | { when.path( "/o/oauth2/auth" ) .query_param( "scope", "https://..." ); - then.status( 200 ); + then.status( 302 ); } ); // in real program at that point we have to open generated url and give access to our program from browser @@ -54,27 +55,45 @@ async fn oauth2_second_endpoint_with_mock() mock.assert(); - assert_eq!( response.status(), 200 ); + assert_eq!( response.status(), 302 ); } -// #[ tokio::test ] -// async fn get_header_with_mock_test() -// { -// let server = MockServer::start(); -// let mock = server.mock( | when, then | { -// when.method( GET ) -// .path( "/spreadsheets/d/1234" ) -// .query_param( "tab1!A1:Z1" ) -// then.status( 200 ) -// .header( "content-type", "application/json" ) -// .body( r#"["Name", "Surname", "Age"]"# ); -// }); +#[ tokio::test ] +async fn oauth2_third_endpoint_with_mock() +{ + let server = MockServer::start(); + let mock = server.mock( | when, then | { + when.method( POST ) + .path( "/token" ) + .header("Content-Type", "application/json" ) + .body( r#"code=AUTHORIZATION_CODE&client_secret=YOUR_CLIENT_SECRET&"# ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .json_body + ( + json! + ( + { + "access_token" : "access_token", + "token_type" : "Bearer", + "expires_in" : "3600", + "scope" : "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email openid" + } + ) + ); + }); + + let body = r#"code=AUTHORIZATION_CODE&client_secret=YOUR_CLIENT_SECRET&"#; -// let response = reqwest::get( server.url( "/spreadsheets/d/1234?tab1!A1:Z1" ) ) -// .await -// .unwrap(); + let response = reqwest::Client::new() + .post( server.url( "/token" ) ) + .header( "Content-Type", "application/json" ) + .body( body ) + .send() + .await + .unwrap(); -// mock.assert(); + mock.assert(); -// assert_eq!( response.status(), 200 ) -// } \ No newline at end of file + assert_eq!( response.status(), 200 ); +} \ No newline at end of file From 118819a36a65967bbc67a6b880457a3827bb72cf Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Tue, 24 Dec 2024 14:28:47 +0100 Subject: [PATCH 11/18] feature with_online --- module/move/gspread/Cargo.toml | 1 + module/move/gspread/tests/inc/cell_tests.rs | 15 ++- module/move/gspread/tests/inc/cells_tests.rs | 9 +- module/move/gspread/tests/inc/header_tests.rs | 16 ++- module/move/gspread/tests/inc/rows_tests.rs | 12 ++- .../move/gspread/tests/mock/header_tests.rs | 98 ------------------ module/move/gspread/tests/mock/mod.rs | 2 +- module/move/gspread/tests/mock/oauth_tests.rs | 99 +++++++++++++++++++ 8 files changed, 140 insertions(+), 112 deletions(-) create mode 100644 module/move/gspread/tests/mock/oauth_tests.rs diff --git a/module/move/gspread/Cargo.toml b/module/move/gspread/Cargo.toml index d4207112f6..21a63abb7b 100644 --- a/module/move/gspread/Cargo.toml +++ b/module/move/gspread/Cargo.toml @@ -18,6 +18,7 @@ name = "main" path = "src/bin/main.rs" [features] +with_online = [] default = [ "enabled" ] full = [ "enabled" ] enabled = [ diff --git a/module/move/gspread/tests/inc/cell_tests.rs b/module/move/gspread/tests/inc/cell_tests.rs index 75c2e75e42..55d45ab68b 100644 --- a/module/move/gspread/tests/inc/cell_tests.rs +++ b/module/move/gspread/tests/inc/cell_tests.rs @@ -1,6 +1,8 @@ +#[ cfg( feature = "with_online" ) ] #[ allow( unused_imports ) ] use super::*; +#[ cfg( feature = "with_online" ) ] use the_module:: { hub, @@ -10,6 +12,7 @@ use the_module:: ser::JsonValue }; +#[ cfg( feature = "with_online" ) ] async fn setup() -> ( SheetsType, &'static str, &'static str ) { let secret = Secret::load().expect( "Failed to load secret" ); @@ -20,8 +23,9 @@ async fn setup() -> ( SheetsType, &'static str, &'static str ) ( hub, spreadsheet_id, table_name ) } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_get_cell_with_online() +async fn test_get_cell() { let ( hub, spreadsheet_id, table_name ) = setup().await; let cell_id = "R2C1"; @@ -39,8 +43,9 @@ async fn test_get_cell_with_online() assert_eq!( result, "Vsevolod" ) } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_get_cell_empty_with_online() +async fn test_get_cell_empty() { let ( hub, spreadsheet_id, table_name ) = setup().await; let cell_id = "R4C1"; @@ -58,8 +63,9 @@ async fn test_get_cell_empty_with_online() assert_eq!( result, JsonValue::Null ) } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_set_cell_with_online() +async fn test_set_cell() { let ( hub, spreadsheet_id, table_name ) = setup().await; let cell_id = "R2C1"; @@ -78,8 +84,9 @@ async fn test_set_cell_with_online() assert!( result.is_ok() ); } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_set_empty_cell_with_online() +async fn test_set_empty_cell() { let ( hub, spreadsheet_id, table_name ) = setup().await; let cell_id = "R4C1"; diff --git a/module/move/gspread/tests/inc/cells_tests.rs b/module/move/gspread/tests/inc/cells_tests.rs index 64d08a6231..8a86f59c24 100644 --- a/module/move/gspread/tests/inc/cells_tests.rs +++ b/module/move/gspread/tests/inc/cells_tests.rs @@ -1,6 +1,8 @@ +#[ cfg( feature = "with_online" ) ] #[ allow( unused_imports ) ] use super::*; +#[ cfg( feature = "with_online" ) ] use the_module:: { hub, @@ -9,6 +11,7 @@ use the_module:: SheetsType, }; +#[ cfg( feature = "with_online" ) ] async fn setup() -> ( SheetsType, &'static str, &'static str, &'static str ) { let secret = Secret::load().expect( "Failed to load secret" ); @@ -20,8 +23,9 @@ async fn setup() -> ( SheetsType, &'static str, &'static str, &'static str ) ( hub, select_row_by_key, spreadsheet_id, table_name ) } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_set_cells_with_online() +async fn test_set_cells() { let ( @@ -47,8 +51,9 @@ async fn test_set_cells_with_online() assert_eq!( result, "Cells were sucsessfully updated!" ) } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_set_cells_wrong_row_with_online() +async fn test_set_cells_wrong_row() { let ( diff --git a/module/move/gspread/tests/inc/header_tests.rs b/module/move/gspread/tests/inc/header_tests.rs index fabc7cf6d8..497e8c3209 100644 --- a/module/move/gspread/tests/inc/header_tests.rs +++ b/module/move/gspread/tests/inc/header_tests.rs @@ -1,6 +1,8 @@ +#[ cfg( feature = "with_online" ) ] #[ allow( unused_imports ) ] use super::*; +#[ cfg( feature = "with_online" ) ] use the_module:: { hub, @@ -9,6 +11,7 @@ use the_module:: SheetsType }; +#[ cfg( feature = "with_online" ) ] async fn setup() -> ( SheetsType, &'static str ) { let secret = Secret::load().expect( "Failed to load secret" ); @@ -17,8 +20,10 @@ async fn setup() -> ( SheetsType, &'static str ) ( hub, spreadsheet_id ) } + +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_get_header_with_online() +async fn test_get_header() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab1"; @@ -35,8 +40,9 @@ async fn test_get_header_with_online() assert_eq!( result, vec![ vec![ "Name", "Surname", "Age" ] ] ); } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_get_header_with_spaces_with_online() +async fn test_get_header_with_spaces() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab2"; @@ -53,8 +59,9 @@ async fn test_get_header_with_spaces_with_online() assert_eq!( result, vec![ vec![ "Name", "", "Age" ] ] ); } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_get_header_empty_with_online() +async fn test_get_header_empty() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab3"; @@ -71,8 +78,9 @@ async fn test_get_header_empty_with_online() assert_eq!( result, Vec::< Vec< String > >::new() ); } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_get_header_with_empty_end_with_online() +async fn test_get_header_with_empty_end() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab4"; diff --git a/module/move/gspread/tests/inc/rows_tests.rs b/module/move/gspread/tests/inc/rows_tests.rs index 0cc87e3ac0..afa2350970 100644 --- a/module/move/gspread/tests/inc/rows_tests.rs +++ b/module/move/gspread/tests/inc/rows_tests.rs @@ -1,6 +1,8 @@ +#[ cfg( feature = "with_online" ) ] #[ allow( unused_imports ) ] use super::*; +#[ cfg( feature = "with_online" ) ] use the_module:: { hub, @@ -9,6 +11,7 @@ use the_module:: SheetsType }; +#[ cfg( feature = "with_online" ) ] async fn setup() -> ( SheetsType, &'static str ) { let secret = Secret::load().expect( "Failed to load secret" ); @@ -18,8 +21,9 @@ async fn setup() -> ( SheetsType, &'static str ) ( hub, spreadsheet_id ) } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_get_rows_with_online() +async fn test_get_rows() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab1"; @@ -46,8 +50,9 @@ async fn test_get_rows_with_online() ) } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_get_rows_with_spaces_with_online() +async fn test_get_rows_with_spaces() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab2"; @@ -74,8 +79,9 @@ async fn test_get_rows_with_spaces_with_online() ) } +#[ cfg( feature = "with_online" ) ] #[ tokio::test ] -async fn test_get_rows_empty_with_online() +async fn test_get_rows_empty() { let ( hub, spreadsheet_id ) = setup().await; let table_name = "tab3"; diff --git a/module/move/gspread/tests/mock/header_tests.rs b/module/move/gspread/tests/mock/header_tests.rs index b427c0c7ce..dae29afaef 100644 --- a/module/move/gspread/tests/mock/header_tests.rs +++ b/module/move/gspread/tests/mock/header_tests.rs @@ -1,99 +1 @@ use httpmock::prelude::*; -use reqwest; -use serde_json::json; - -#[ tokio::test ] -async fn oauth2_first_endpoint_with_mock() -{ - let server = MockServer::start(); - let mock = server.mock( | when, then | { - when.method( GET ) - .path( "/o/oauth2/auth" ) - .query_param( "scope", "https://www.googleapis.com/auth/drive.readonly" ) - .query_param( "access_type", "offline" ) - .query_param( "redirect_uri", "http://localhost:44444" ) - .query_param( "response_type", "code" ) - .query_param( "client_id", "YOUR_CLIENT_ID" ); - then.status( 302 ); - }); - - let response = reqwest::get - ( - server.url - ( - "/o/oauth2/auth?\ - scope=https://www.googleapis.com/auth/drive.readonly&\ - access_type=offline&\ - redirect_uri=http://localhost:44444&\ - response_type=code&\ - client_id=YOUR_CLIENT_ID" - ) - ) - .await - .unwrap(); - - mock.assert(); - - assert_eq!( response.status(), 302 ); -} - - -#[ tokio::test ] -async fn oauth2_second_endpoint_with_mock() -{ - let server = MockServer::start(); - - // url == first endpoint - let mock = server.mock( | when, then | { - when.path( "/o/oauth2/auth" ) - .query_param( "scope", "https://..." ); - then.status( 302 ); - } ); - - // in real program at that point we have to open generated url and give access to our program from browser - let response = reqwest::get( server.url( "/o/oauth2/auth?scope=https://..." ) ).await.unwrap(); - - mock.assert(); - - assert_eq!( response.status(), 302 ); -} - -#[ tokio::test ] -async fn oauth2_third_endpoint_with_mock() -{ - let server = MockServer::start(); - let mock = server.mock( | when, then | { - when.method( POST ) - .path( "/token" ) - .header("Content-Type", "application/json" ) - .body( r#"code=AUTHORIZATION_CODE&client_secret=YOUR_CLIENT_SECRET&"# ); - then.status( 200 ) - .header("Content-Type", "application/json" ) - .json_body - ( - json! - ( - { - "access_token" : "access_token", - "token_type" : "Bearer", - "expires_in" : "3600", - "scope" : "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email openid" - } - ) - ); - }); - - let body = r#"code=AUTHORIZATION_CODE&client_secret=YOUR_CLIENT_SECRET&"#; - - let response = reqwest::Client::new() - .post( server.url( "/token" ) ) - .header( "Content-Type", "application/json" ) - .body( body ) - .send() - .await - .unwrap(); - - mock.assert(); - - assert_eq!( response.status(), 200 ); -} \ No newline at end of file diff --git a/module/move/gspread/tests/mock/mod.rs b/module/move/gspread/tests/mock/mod.rs index 72b0547b7e..b7b0f2fa9d 100644 --- a/module/move/gspread/tests/mock/mod.rs +++ b/module/move/gspread/tests/mock/mod.rs @@ -1,4 +1,4 @@ #[ allow( unused_imports ) ] use super::*; -mod header_tests; \ No newline at end of file +mod oauth_tests; \ No newline at end of file diff --git a/module/move/gspread/tests/mock/oauth_tests.rs b/module/move/gspread/tests/mock/oauth_tests.rs new file mode 100644 index 0000000000..b427c0c7ce --- /dev/null +++ b/module/move/gspread/tests/mock/oauth_tests.rs @@ -0,0 +1,99 @@ +use httpmock::prelude::*; +use reqwest; +use serde_json::json; + +#[ tokio::test ] +async fn oauth2_first_endpoint_with_mock() +{ + let server = MockServer::start(); + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/o/oauth2/auth" ) + .query_param( "scope", "https://www.googleapis.com/auth/drive.readonly" ) + .query_param( "access_type", "offline" ) + .query_param( "redirect_uri", "http://localhost:44444" ) + .query_param( "response_type", "code" ) + .query_param( "client_id", "YOUR_CLIENT_ID" ); + then.status( 302 ); + }); + + let response = reqwest::get + ( + server.url + ( + "/o/oauth2/auth?\ + scope=https://www.googleapis.com/auth/drive.readonly&\ + access_type=offline&\ + redirect_uri=http://localhost:44444&\ + response_type=code&\ + client_id=YOUR_CLIENT_ID" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 302 ); +} + + +#[ tokio::test ] +async fn oauth2_second_endpoint_with_mock() +{ + let server = MockServer::start(); + + // url == first endpoint + let mock = server.mock( | when, then | { + when.path( "/o/oauth2/auth" ) + .query_param( "scope", "https://..." ); + then.status( 302 ); + } ); + + // in real program at that point we have to open generated url and give access to our program from browser + let response = reqwest::get( server.url( "/o/oauth2/auth?scope=https://..." ) ).await.unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 302 ); +} + +#[ tokio::test ] +async fn oauth2_third_endpoint_with_mock() +{ + let server = MockServer::start(); + let mock = server.mock( | when, then | { + when.method( POST ) + .path( "/token" ) + .header("Content-Type", "application/json" ) + .body( r#"code=AUTHORIZATION_CODE&client_secret=YOUR_CLIENT_SECRET&"# ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .json_body + ( + json! + ( + { + "access_token" : "access_token", + "token_type" : "Bearer", + "expires_in" : "3600", + "scope" : "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email openid" + } + ) + ); + }); + + let body = r#"code=AUTHORIZATION_CODE&client_secret=YOUR_CLIENT_SECRET&"#; + + let response = reqwest::Client::new() + .post( server.url( "/token" ) ) + .header( "Content-Type", "application/json" ) + .body( body ) + .send() + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} \ No newline at end of file From 556bd632abf7c8b6de283dd352714a240c05ef91 Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Tue, 24 Dec 2024 16:32:10 +0100 Subject: [PATCH 12/18] added mock tests --- module/move/gspread/tests/mock/cell_tests.rs | 93 +++++++++++++ module/move/gspread/tests/mock/cells_tests.rs | 67 ++++++++++ .../move/gspread/tests/mock/header_tests.rs | 122 ++++++++++++++++++ module/move/gspread/tests/mock/mod.rs | 6 +- module/move/gspread/tests/mock/oauth_tests.rs | 4 + module/move/gspread/tests/mock/rows_tests.rs | 93 +++++++++++++ 6 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 module/move/gspread/tests/mock/cell_tests.rs create mode 100644 module/move/gspread/tests/mock/cells_tests.rs create mode 100644 module/move/gspread/tests/mock/rows_tests.rs diff --git a/module/move/gspread/tests/mock/cell_tests.rs b/module/move/gspread/tests/mock/cell_tests.rs new file mode 100644 index 0000000000..488eb60d02 --- /dev/null +++ b/module/move/gspread/tests/mock/cell_tests.rs @@ -0,0 +1,93 @@ +//! +//! Get and set cell tests. +//! In these examples: +//! - url is /v4/spreadsheets/{spreadsheet_id}}/values/{range} +//! - everything is fake: spreadsheet_id, sheet's name, range and response json +//! + +use httpmock::prelude::*; +use reqwest; + +#[ tokio::test ] +async fn test_get_cell_with_mock() +{ + let server = MockServer::start(); + let body = r#"{ "A2": "Steeve" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab2!R2C1" ); + then.status( 200 ) + .header( "Content-Type", "application/json" ) + .body( body ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab2!R2C1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} + +#[ tokio::test ] +async fn test_get_cell_empty_with_mock() +{ + let server = MockServer::start(); + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab2!R2C1" ); + then.status( 200 ) + .header( "Content-Type", "application/json" ) + .body( r#"{}"# ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab2!R2C1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} + +#[ tokio::test ] +async fn test_set_cell_with_mock() +{ + let server = MockServer::start(); + let body = r#"A2": "Some value"#; + let mock = server.mock( | when, then | { + when.method( POST ) + .path( "/v4/spreadsheets/12345/values/tab2!R2C1" ) + .header("content-type", "application/json") + .body( body ); + // returns amount of updated cells + then.status( 201 ) + .header( "Content-Type", "application/json" ) + .body( "1" ); + } ); + + let response = reqwest::Client::new() + .post( server.url( "/v4/spreadsheets/12345/values/tab2!R2C1" ) ) + .header( "Content-Type", "application/json" ) + .body( body ) + .send() + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 201 ); +} \ No newline at end of file diff --git a/module/move/gspread/tests/mock/cells_tests.rs b/module/move/gspread/tests/mock/cells_tests.rs new file mode 100644 index 0000000000..b03b78fd71 --- /dev/null +++ b/module/move/gspread/tests/mock/cells_tests.rs @@ -0,0 +1,67 @@ +//! +//! Set cells tests. +//! In these examples: +//! - url is /v4/spreadsheets/{spreadsheet_id}}/values/{range} +//! - everything is fake: spreadsheet_id, sheet's name, range and response json +//! + +use httpmock::prelude::*; +use reqwest; + +#[ tokio::test ] +async fn test_set_cells_with_mock() +{ + let server = MockServer::start(); + let body = r#"{ "id": "2", "A": "new_val1", "B": "new_val2"}"#; + let mock = server.mock( | when, then | { + when.method( POST ) + .path( "/v4/spreadsheets/12345/values/tab3!A2:B2" ) + .header( "Content-Type", "application/json" ) + .body( body ); + // returns amount of updated cells + then.status( 201 ) + .header( "Content-Type", "application/json" ) + .body( "2" ); + } ); + + let response = reqwest::Client::new() + .post( server.url( "/v4/spreadsheets/12345/values/tab3!A2:B2" ) ) + .header( "Content-Type", "application/json" ) + .body( body ) + .send() + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 201 ); +} + +#[ tokio::test ] +async fn test_set_cells_wrong_row_with_mock() +{ + let server = MockServer::start(); + let body = r#"{ "id": "a", "A": "new_val1", "B": "new_val2"}"#; + let response_body = r#"{"error":{"code":400,"message":"Invalid data[0]: Unable to parse range: tab3!Aa","status":"INVALID_ARGUMENT"}}"#; + let mock = server.mock( | when, then | { + when.method( POST ) + .path( "/v4/spreadsheets/12345/values/tab3!Aa:Ba" ) + .header( "Content-Type", "application/json" ) + .body( body ); + then.status( 400 ) + .header( "Content-Type", "application/json" ) + .body( response_body ); + } ); + + let response = reqwest::Client::new() + .post( server.url( "/v4/spreadsheets/12345/values/tab3!Aa:Ba" ) ) + .header( "Content-Type", "application/json" ) + .body( body ) + .send() + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 400 ); +} \ No newline at end of file diff --git a/module/move/gspread/tests/mock/header_tests.rs b/module/move/gspread/tests/mock/header_tests.rs index dae29afaef..5927b1a8a6 100644 --- a/module/move/gspread/tests/mock/header_tests.rs +++ b/module/move/gspread/tests/mock/header_tests.rs @@ -1 +1,123 @@ +//! +//! Get header tests. +//! In these examples: +//! - url is /v4/spreadsheets/{spreadsheet_id}}/values/{range} +//! - everything is fake: spreadsheet_id, sheet's name, range and response json +//! + use httpmock::prelude::*; +use reqwest; + + +#[ tokio::test ] +async fn test_get_header() +{ + let server = MockServer::start(); + let body = r#"{ "A1": "Name", "B1": "Surname", "C1": "Age" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab1!A1:Z1" ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .body( body ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab1!A1:Z1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ) + +} + +#[ tokio::test ] +async fn test_get_header_with_spaces_with_mock() +{ + let server = MockServer::start(); + let body = r#"{ "A1": "Name", "B1": "", "C1": "Age" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab1!A1:Z1" ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .body( body ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab1!A1:Z1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ) +} + +#[ tokio::test ] +async fn test_get_header_empty_with_mock() +{ + let server = MockServer::start(); + + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab1!A1:Z1" ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .body( r#"{}"# ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab1!A1:Z1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ) +} + +#[ tokio::test ] +async fn test_get_header_with_empty_end_with_mock() +{ + let server = MockServer::start(); + let body = r#"{ "A1": "Name", "B1": "Surname" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab1!A1:Z1" ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .body( body ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab1!A1:Z1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ) +} \ No newline at end of file diff --git a/module/move/gspread/tests/mock/mod.rs b/module/move/gspread/tests/mock/mod.rs index b7b0f2fa9d..1c6c49e281 100644 --- a/module/move/gspread/tests/mock/mod.rs +++ b/module/move/gspread/tests/mock/mod.rs @@ -1,4 +1,8 @@ #[ allow( unused_imports ) ] use super::*; -mod oauth_tests; \ No newline at end of file +mod oauth_tests; +mod header_tests; +mod cell_tests; +mod cells_tests; +mod rows_tests; \ No newline at end of file diff --git a/module/move/gspread/tests/mock/oauth_tests.rs b/module/move/gspread/tests/mock/oauth_tests.rs index b427c0c7ce..6fd0f5c327 100644 --- a/module/move/gspread/tests/mock/oauth_tests.rs +++ b/module/move/gspread/tests/mock/oauth_tests.rs @@ -1,3 +1,7 @@ +//! +//! OAuth2 tests. +//! + use httpmock::prelude::*; use reqwest; use serde_json::json; diff --git a/module/move/gspread/tests/mock/rows_tests.rs b/module/move/gspread/tests/mock/rows_tests.rs new file mode 100644 index 0000000000..05b43663b3 --- /dev/null +++ b/module/move/gspread/tests/mock/rows_tests.rs @@ -0,0 +1,93 @@ +//! +//! Get rows tests. +//! In these examples: +//! - url is /v4/spreadsheets/{spreadsheet_id}}/values/{range} +//! - everything is fake: spreadsheet_id, sheet's name, range and response json +//! + +use httpmock::prelude::*; +use reqwest; + +#[ tokio::test ] +async fn test_get_rows_with_mock() +{ + let server = MockServer::start(); + let body = r#"{"A2" : "Steeve", "B2": "John", "A3": "Seva", "B3": "Oleg" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/A2:B3" ); + then.status( 200 ) + .header( "Content-Type", "application/json" ) + .body( body ); + }); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/A2:B3" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} + +#[ tokio::test ] +async fn test_get_rows_with_spaces_with_mock() +{ + let server = MockServer::start(); + let body = r#"{"A2" : "Steeve", "B2": "", "A3": "Seva", "B3": "Oleg" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/A2:B3" ); + then.status( 200 ) + .header( "Content-Type", "application/json" ) + .body( body ); + }); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/A2:B3" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} + +#[ tokio::test ] +async fn test_get_rows_empty_with_mock() +{ + let server = MockServer::start(); + let body = r#"{}"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/A2:B3" ); + then.status( 200 ) + .header( "Content-Type", "application/json" ) + .body( body ); + }); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/A2:B3" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} \ No newline at end of file From 780c76188027649e6bebb9d37ff2065e42ed132a Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Tue, 24 Dec 2024 16:40:11 +0100 Subject: [PATCH 13/18] with_online fixed --- module/move/gspread/tests/inc/cell_tests.rs | 7 ------- module/move/gspread/tests/inc/cells_tests.rs | 5 ----- module/move/gspread/tests/inc/header_tests.rs | 7 ------- module/move/gspread/tests/inc/rows_tests.rs | 6 ------ module/move/gspread/tests/mock/oauth_tests.rs | 5 ++--- module/move/gspread/tests/tests.rs | 4 ++-- 6 files changed, 4 insertions(+), 30 deletions(-) diff --git a/module/move/gspread/tests/inc/cell_tests.rs b/module/move/gspread/tests/inc/cell_tests.rs index 55d45ab68b..f93ec7b1a4 100644 --- a/module/move/gspread/tests/inc/cell_tests.rs +++ b/module/move/gspread/tests/inc/cell_tests.rs @@ -1,8 +1,6 @@ -#[ cfg( feature = "with_online" ) ] #[ allow( unused_imports ) ] use super::*; -#[ cfg( feature = "with_online" ) ] use the_module:: { hub, @@ -12,7 +10,6 @@ use the_module:: ser::JsonValue }; -#[ cfg( feature = "with_online" ) ] async fn setup() -> ( SheetsType, &'static str, &'static str ) { let secret = Secret::load().expect( "Failed to load secret" ); @@ -23,7 +20,6 @@ async fn setup() -> ( SheetsType, &'static str, &'static str ) ( hub, spreadsheet_id, table_name ) } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_get_cell() { @@ -43,7 +39,6 @@ async fn test_get_cell() assert_eq!( result, "Vsevolod" ) } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_get_cell_empty() { @@ -63,7 +58,6 @@ async fn test_get_cell_empty() assert_eq!( result, JsonValue::Null ) } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_set_cell() { @@ -84,7 +78,6 @@ async fn test_set_cell() assert!( result.is_ok() ); } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_set_empty_cell() { diff --git a/module/move/gspread/tests/inc/cells_tests.rs b/module/move/gspread/tests/inc/cells_tests.rs index 8a86f59c24..4c91a9a19c 100644 --- a/module/move/gspread/tests/inc/cells_tests.rs +++ b/module/move/gspread/tests/inc/cells_tests.rs @@ -1,8 +1,6 @@ -#[ cfg( feature = "with_online" ) ] #[ allow( unused_imports ) ] use super::*; -#[ cfg( feature = "with_online" ) ] use the_module:: { hub, @@ -11,7 +9,6 @@ use the_module:: SheetsType, }; -#[ cfg( feature = "with_online" ) ] async fn setup() -> ( SheetsType, &'static str, &'static str, &'static str ) { let secret = Secret::load().expect( "Failed to load secret" ); @@ -23,7 +20,6 @@ async fn setup() -> ( SheetsType, &'static str, &'static str, &'static str ) ( hub, select_row_by_key, spreadsheet_id, table_name ) } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_set_cells() { @@ -51,7 +47,6 @@ async fn test_set_cells() assert_eq!( result, "Cells were sucsessfully updated!" ) } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_set_cells_wrong_row() { diff --git a/module/move/gspread/tests/inc/header_tests.rs b/module/move/gspread/tests/inc/header_tests.rs index 497e8c3209..046d8e1d69 100644 --- a/module/move/gspread/tests/inc/header_tests.rs +++ b/module/move/gspread/tests/inc/header_tests.rs @@ -1,8 +1,6 @@ -#[ cfg( feature = "with_online" ) ] #[ allow( unused_imports ) ] use super::*; -#[ cfg( feature = "with_online" ) ] use the_module:: { hub, @@ -11,7 +9,6 @@ use the_module:: SheetsType }; -#[ cfg( feature = "with_online" ) ] async fn setup() -> ( SheetsType, &'static str ) { let secret = Secret::load().expect( "Failed to load secret" ); @@ -21,7 +18,6 @@ async fn setup() -> ( SheetsType, &'static str ) ( hub, spreadsheet_id ) } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_get_header() { @@ -40,7 +36,6 @@ async fn test_get_header() assert_eq!( result, vec![ vec![ "Name", "Surname", "Age" ] ] ); } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_get_header_with_spaces() { @@ -59,7 +54,6 @@ async fn test_get_header_with_spaces() assert_eq!( result, vec![ vec![ "Name", "", "Age" ] ] ); } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_get_header_empty() { @@ -78,7 +72,6 @@ async fn test_get_header_empty() assert_eq!( result, Vec::< Vec< String > >::new() ); } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_get_header_with_empty_end() { diff --git a/module/move/gspread/tests/inc/rows_tests.rs b/module/move/gspread/tests/inc/rows_tests.rs index afa2350970..d9032f8544 100644 --- a/module/move/gspread/tests/inc/rows_tests.rs +++ b/module/move/gspread/tests/inc/rows_tests.rs @@ -1,8 +1,6 @@ -#[ cfg( feature = "with_online" ) ] #[ allow( unused_imports ) ] use super::*; -#[ cfg( feature = "with_online" ) ] use the_module:: { hub, @@ -11,7 +9,6 @@ use the_module:: SheetsType }; -#[ cfg( feature = "with_online" ) ] async fn setup() -> ( SheetsType, &'static str ) { let secret = Secret::load().expect( "Failed to load secret" ); @@ -21,7 +18,6 @@ async fn setup() -> ( SheetsType, &'static str ) ( hub, spreadsheet_id ) } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_get_rows() { @@ -50,7 +46,6 @@ async fn test_get_rows() ) } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_get_rows_with_spaces() { @@ -79,7 +74,6 @@ async fn test_get_rows_with_spaces() ) } -#[ cfg( feature = "with_online" ) ] #[ tokio::test ] async fn test_get_rows_empty() { diff --git a/module/move/gspread/tests/mock/oauth_tests.rs b/module/move/gspread/tests/mock/oauth_tests.rs index 6fd0f5c327..a22ef9a792 100644 --- a/module/move/gspread/tests/mock/oauth_tests.rs +++ b/module/move/gspread/tests/mock/oauth_tests.rs @@ -66,11 +66,12 @@ async fn oauth2_second_endpoint_with_mock() async fn oauth2_third_endpoint_with_mock() { let server = MockServer::start(); + let body = r#"code=AUTHORIZATION_CODE&client_secret=YOUR_CLIENT_SECRET&"#; let mock = server.mock( | when, then | { when.method( POST ) .path( "/token" ) .header("Content-Type", "application/json" ) - .body( r#"code=AUTHORIZATION_CODE&client_secret=YOUR_CLIENT_SECRET&"# ); + .body( body ); then.status( 200 ) .header("Content-Type", "application/json" ) .json_body @@ -87,8 +88,6 @@ async fn oauth2_third_endpoint_with_mock() ); }); - let body = r#"code=AUTHORIZATION_CODE&client_secret=YOUR_CLIENT_SECRET&"#; - let response = reqwest::Client::new() .post( server.url( "/token" ) ) .header( "Content-Type", "application/json" ) diff --git a/module/move/gspread/tests/tests.rs b/module/move/gspread/tests/tests.rs index 4e3c6add8f..31c81bb6b2 100644 --- a/module/move/gspread/tests/tests.rs +++ b/module/move/gspread/tests/tests.rs @@ -3,8 +3,8 @@ use gspread as the_module; #[ allow( unused_imports ) ] use test_tools::exposed::*; -#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "with_online" ) ] mod inc; -#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "default" ) ] mod mock; \ No newline at end of file From efbb8750942e895d71cc82e114051bf673c83cdd Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Tue, 24 Dec 2024 16:58:08 +0100 Subject: [PATCH 14/18] fixed gspread_cells_set return type --- .../move/gspread/src/actions/gspread_cells_set.rs | 13 ++++++++++--- module/move/gspread/src/commands/gspread_cells.rs | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/module/move/gspread/src/actions/gspread_cells_set.rs b/module/move/gspread/src/actions/gspread_cells_set.rs index 9d38a93d96..cb6c9531ee 100644 --- a/module/move/gspread/src/actions/gspread_cells_set.rs +++ b/module/move/gspread/src/actions/gspread_cells_set.rs @@ -88,7 +88,7 @@ mod private json_str : &str, spreadsheet_id : &str, table_name : &str - ) -> Result< String > + ) -> Result< i32 > { check_select_row_by_key( select_row_by_key )?; @@ -130,8 +130,15 @@ mod private .doit() .await { - Ok( _ ) => Ok( format!( "Cells were sucsessfully updated!" ) ), - Err( error ) => Err( Error::ApiError( error ) ) + Ok( ( _, values ) ) => + { + match values.total_updated_cells + { + Some( val ) => Ok( val ), + None => Ok( 0 ), + } + } + Err( error ) => Err( Error::ApiError( error ) ), } } diff --git a/module/move/gspread/src/commands/gspread_cells.rs b/module/move/gspread/src/commands/gspread_cells.rs index 5308ad7eaa..0d828a8cb9 100644 --- a/module/move/gspread/src/commands/gspread_cells.rs +++ b/module/move/gspread/src/commands/gspread_cells.rs @@ -81,7 +81,7 @@ mod private ) .await { - Ok( msg ) => println!( "{}", msg ), + Ok( val ) => println!( "{} cells were sucsessfully updated!", val ), Err( error ) => println!( "Error:\n{}", error ) } } From 0d453a3666d596b89d502d6fb2a81427e224c943 Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Wed, 25 Dec 2024 02:48:12 +0100 Subject: [PATCH 15/18] fixed display --- module/move/gspread/src/debug/row_wrapper.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/module/move/gspread/src/debug/row_wrapper.rs b/module/move/gspread/src/debug/row_wrapper.rs index 45d3bea21c..cd211391f8 100644 --- a/module/move/gspread/src/debug/row_wrapper.rs +++ b/module/move/gspread/src/debug/row_wrapper.rs @@ -41,24 +41,22 @@ for RowWrapper for ( index, value ) in self.row.iter().enumerate() { - let column_name = format!( "Column{}", index ); + let column_name = format!( "{} ", index ); let title = Box::leak( column_name.into_boxed_str() ) as &str; + let cleaned: String = value + .to_string() + .chars() + .skip( 1 ) + .take( value.to_string().chars().count() - 2 ) + .collect(); - // let value_str = value.to_string().trim_matches('"').to_string(); - // let char_count = value_str.chars().count(); - // let byte_count = value_str.as_bytes().len(); - // let display_width = UnicodeWidthStr::width(value_str.as_str()); - - // eprintln!("DEBUG: Value: {}, Chars: {}, Bytes: {}, Display Width: {}", - // value_str, char_count, byte_count, display_width); - - dst.push( ( title, Some( Cow::Owned( value.to_string() ) ) ) ) + dst.push( ( title, Some( Cow::Owned( cleaned ) ) ) ) } //adding empty values for missing cells for index in self.row.len()..self.max_len { - let column_name = format!( "Column{}", index ); + let column_name = format!( "{}", index ); let title = Box::leak( column_name.into_boxed_str() ) as &str; dst.push( ( title, Some( Cow::Owned( "".to_string() ) ) ) ); } From 19bd3c300fed42fb352e136b5ab4410e5525be15 Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Sat, 28 Dec 2024 02:18:31 +0100 Subject: [PATCH 16/18] draft wrap_function --- module/core/format_tools/src/format/print.rs | 2 + .../gspread/src/actions/gspread_cells_set.rs | 8 +- module/move/gspread/src/debug.rs | 8 +- module/move/gspread/src/debug/row_wrapper.rs | 66 ----------- module/move/gspread/src/debug/rows.rs | 108 ++++++++++++++++++ 5 files changed, 122 insertions(+), 70 deletions(-) delete mode 100644 module/move/gspread/src/debug/row_wrapper.rs create mode 100644 module/move/gspread/src/debug/rows.rs diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index f1aa104c24..8ad31f189b 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -229,6 +229,8 @@ mod private #[ derive( Debug, Default ) ] pub struct RowDescriptor { + + /// Index of the row. pub irow : usize, /// Height of the row. diff --git a/module/move/gspread/src/actions/gspread_cells_set.rs b/module/move/gspread/src/actions/gspread_cells_set.rs index cb6c9531ee..23eb888047 100644 --- a/module/move/gspread/src/actions/gspread_cells_set.rs +++ b/module/move/gspread/src/actions/gspread_cells_set.rs @@ -63,6 +63,7 @@ mod private } } + fn is_all_uppercase_letters ( s : &str @@ -99,23 +100,26 @@ mod private .remove( select_row_by_key ) .ok_or_else( || Error::ParseError( format!( "Key '{}' not found in JSON", select_row_by_key ) ) )?; - let mut value_ranges= Vec::new(); + // wrap_row() for ( key, value ) in pairs.columns.into_iter() { + // There is no sense to use this function, instead we can just check if a char is uppercase if not then convert it to uppercase is_all_uppercase_letters( key.as_str() )?; value_ranges.push ( - ValueRange + ValueRange { range: Some( format!( "{}!{}{}", table_name, key, row_id ) ), values: Some( vec![ vec![ JsonValue::String( value.to_string() ) ] ] ), + // Change to rows ..Default::default() } ); }; + // Also read about it and change values let req = BatchUpdateValuesRequest { value_input_option: Some( "USER_ENTERED".to_string() ), diff --git a/module/move/gspread/src/debug.rs b/module/move/gspread/src/debug.rs index 11f63d821e..8095603737 100644 --- a/module/move/gspread/src/debug.rs +++ b/module/move/gspread/src/debug.rs @@ -9,12 +9,16 @@ use format_tools:: }; use std::borrow::Cow; -pub mod row_wrapper; +pub mod rows; crate::mod_interface! { exposed use { - row_wrapper::RowWrapper, + rows:: + { + RowWrapper, + wrap_row + } }; } diff --git a/module/move/gspread/src/debug/row_wrapper.rs b/module/move/gspread/src/debug/row_wrapper.rs deleted file mode 100644 index cd211391f8..0000000000 --- a/module/move/gspread/src/debug/row_wrapper.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! -//! Gspread wrapper for outputting data to console -//! -//! It is used for "header" and "rows" commands -//! - -use super::*; -use crate::*; -use ser::JsonValue; -use unicode_width::UnicodeWidthStr; - -#[ derive( Debug ) ] -pub struct RowWrapper -{ - pub row: Vec< JsonValue >, - pub max_len: usize -} - -impl Clone for RowWrapper -{ - fn clone( &self ) -> Self - { - Self - { - row: self.row.clone(), - max_len: self.max_len.clone() - } - } -} - -impl TableWithFields for RowWrapper {} -impl Fields< &'_ str, Option< Cow< '_, str > > > -for RowWrapper -{ - type Key< 'k > = &'k str; - type Val< 'v > = Option< Cow< 'v, str > >; - - fn fields( &self ) -> impl IteratorTrait< Item= ( &'_ str, Option > ) > - { - let mut dst = Vec::new(); - - for ( index, value ) in self.row.iter().enumerate() - { - let column_name = format!( "{} ", index ); - let title = Box::leak( column_name.into_boxed_str() ) as &str; - let cleaned: String = value - .to_string() - .chars() - .skip( 1 ) - .take( value.to_string().chars().count() - 2 ) - .collect(); - - dst.push( ( title, Some( Cow::Owned( cleaned ) ) ) ) - } - - //adding empty values for missing cells - for index in self.row.len()..self.max_len - { - let column_name = format!( "{}", index ); - let title = Box::leak( column_name.into_boxed_str() ) as &str; - dst.push( ( title, Some( Cow::Owned( "".to_string() ) ) ) ); - } - - dst.into_iter() - } -} \ No newline at end of file diff --git a/module/move/gspread/src/debug/rows.rs b/module/move/gspread/src/debug/rows.rs new file mode 100644 index 0000000000..95b11a10ba --- /dev/null +++ b/module/move/gspread/src/debug/rows.rs @@ -0,0 +1,108 @@ + + + +mod private +{ + use super::*; + use crate::*; + + use std::borrow::Cow; + use std::collections::HashMap; + use actions::gspread::Result; + use format_tools::TableWithFields; + + /// Struct to keep row and its id. + #[ derive( Debug, Clone ) ] + pub struct RowWrapper + { + /// Row's id + pub id : usize, + /// Row wich is represented as an array of typle, where tuple is a pair of key value, where key is a column's name and value is a value of a cell. + pub row : Vec< ( String, String ) >, + } + + impl RowWrapper + { + /// Just constructor + pub fn new( id : usize, row : Vec< ( String, String ) > ) -> Self + { + Self + { + id, + row, + } + } + } + + impl TableWithFields for RowWrapper {} + impl Fields< &'_ str, Option< Cow< '_, str > > > + for RowWrapper + { + type Key< 'k > = &'k str; + type Val< 'v > = Option< Cow< 'v, str > >; + + fn fields( &self ) -> impl IteratorTrait< Item= ( &'_ str, Option > ) > + { + self.row.iter().map( | ( key, value ) | { + ( key.as_str(), Some( Cow::Borrowed( value.as_str() ) ) ) + } ) + // let mut dst = Vec::new(); + + // for ( index, value ) in self.row.iter().enumerate() + // { + // let column_name = format!( "{} ", index ); + // let title = Box::leak( column_name.into_boxed_str() ) as &str; + // let cleaned: String = value + // .to_string() + // .chars() + // .skip( 1 ) + // .take( value.to_string().chars().count() - 2 ) + // .collect(); + + // dst.push( ( title, Some( Cow::Owned( cleaned ) ) ) ) + // } + + // //adding empty values for missing cells + // for index in self.row.len()..self.max_len + // { + // let column_name = format!( "{}", index ); + // let title = Box::leak( column_name.into_boxed_str() ) as &str; + // dst.push( ( title, Some( Cow::Owned( "".to_string() ) ) ) ); + // } + + // dst.into_iter() + } + } + + /// Function to wrap a row. + /// + /// It converts from HashMap to a row wich is actually sorted array, by column name. + /// + /// **Params** + /// - `id` : Row's id. + /// - `vales` : Pairs of key value, where key is a clomun's name and value is a value of cell. + /// + /// **Returns** + /// - `RowWrapper` object. + pub fn wrap_row + ( + id : usize, + values : HashMap< String, String > + ) -> Result< RowWrapper > + { + let mut row: Vec< ( String, String ) > = values.into_iter().collect(); + + row.sort_by( | a, b | a.0.cmp( &b.0 ) ); + + Ok ( RowWrapper::new( id, row ) ) + } +} + +crate::mod_interface! +{ + own use + { + wrap_row, + RowWrapper + }; +} \ No newline at end of file From 450328f150cb961319bd3171c5364aa36075983b Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Sat, 28 Dec 2024 10:56:07 +0100 Subject: [PATCH 17/18] something bad --- module/move/gspread/src/actions/gspread.rs | 39 ++++- .../gspread/src/actions/gspread_cells_set.rs | 146 ++++++++++++------ module/move/gspread/src/debug/rows.rs | 135 ++++++---------- 3 files changed, 179 insertions(+), 141 deletions(-) diff --git a/module/move/gspread/src/actions/gspread.rs b/module/move/gspread/src/actions/gspread.rs index 528c631bb8..90d4ae75b2 100644 --- a/module/move/gspread/src/actions/gspread.rs +++ b/module/move/gspread/src/actions/gspread.rs @@ -10,7 +10,9 @@ mod private use error_tools::typed::Error; use derive_tools::AsRefStr; use crate::*; - use ser::DisplayFromStr; + use ser::{DisplayFromStr, JsonValue}; + use std::collections::HashMap; + use google_sheets4::api::{BatchUpdateValuesResponse, ValueRange}; #[ ser::serde_as ] #[ derive( Debug, Error, AsRefStr, ser::Serialize ) ] @@ -71,6 +73,40 @@ mod private ) } + /// Function to update a row on a Google Sheet. + /// + /// It converts from HashMap to a row wich is actually sorted array, by column name. + /// + /// **Params** + /// - `id` : Row's id. + /// - `vales` : Pairs of key value, where key is a clomun's name and value is a value of cell. + /// + /// **Returns** + /// - `RowWrapper` object. + pub async fn update_row + ( + row_key : usize, + values : HashMap< String, String >, + sheet_name : &str + ) -> Result< Vec< ValueRange > > + { + let mut value_ranges = Vec::with_capacity( values.len() ); + + for ( col_name, value ) in values { + value_ranges.push + ( + ValueRange + { + major_dimension: Some( String::from( "ROWS" ) ), + values: Some( vec![ vec![ JsonValue::String( value ) ] ] ), + range: Some( format!( "{}!{}{}", sheet_name, col_name, row_key ) ), + } + ) + } + + Ok( value_ranges ) + } + pub type Result< T > = core::result::Result< T, Error >; } @@ -80,6 +116,7 @@ crate::mod_interface! { Error, Result, + update_row, get_spreadsheet_id_from_url, }; } \ No newline at end of file diff --git a/module/move/gspread/src/actions/gspread_cells_set.rs b/module/move/gspread/src/actions/gspread_cells_set.rs index 23eb888047..0ccc68a77f 100644 --- a/module/move/gspread/src/actions/gspread_cells_set.rs +++ b/module/move/gspread/src/actions/gspread_cells_set.rs @@ -8,7 +8,8 @@ mod private use actions::gspread:: { Error, - Result + Result, + update_row }; use google_sheets4::api:: { @@ -22,7 +23,7 @@ mod private }; use std::collections::HashMap; - /// Structure for --json value + /// TODO: Add documentation #[ derive( Deserialize, Debug ) ] struct ParsedJson { @@ -30,16 +31,36 @@ mod private columns : HashMap< String, String > } - /// Parse --json value + /// TODO: Add documentation fn parse_json ( json_str : &str ) -> Result< ParsedJson > { - serde_json::from_str::< ParsedJson >( json_str ).map_err - ( - | error | Error::InvalidJSON( format!( "Failed to parse JSON: {}", error ) ) - ) + let parsed_json = serde_json::from_str::< HashMap< String, String > >( json_str ) + .map_err( | error | format!( "Failed to parse JSON: {}", error ) ) + .and_then + ( | map | + { + for ( col_name, _value ) in &map + { + if !col_name.chars().all( | c | c.is_alphabetic() && c.is_uppercase() ) + { + return Err + ( + format!( "Invalid column name: {}. Allowed only uppercase alphabetic letters (A-Z)", col_name ) + ); + } + } + Ok( map ) + }); + + match parsed_json + { + Ok( map ) => Ok( ParsedJson{ columns: map } ), + Err( error ) => Err( Error::InvalidJSON( error ) ) + } + } /// Check availables keys. @@ -64,23 +85,23 @@ mod private } - fn is_all_uppercase_letters - ( - s : &str - ) -> Result< () > - { - if s.chars().all( | c | c.is_ascii_uppercase() ) - { - Ok( () ) - } - else - { - Err - ( - Error::ParseError( format!( "The string '{}' contains invalid characters. Only uppercase letters (A-Z) are allowed.", s ) ) - ) - } - } + // fn check_col_names + // ( + // s : &str + // ) -> Result< () > + // { + // if s.chars().all( | c | c.is_ascii_uppercase() ) + // { + // Ok( () ) + // } + // else + // { + // Err + // ( + // Error::ParseError( format!( "The string '{}' contains invalid characters. Only uppercase letters (A-Z) are allowed.", s ) ) + // ) + // } + // } pub async fn action ( @@ -93,33 +114,26 @@ mod private { check_select_row_by_key( select_row_by_key )?; - let mut pairs = parse_json( json_str )?; - - let row_id = pairs - .columns - .remove( select_row_by_key ) - .ok_or_else( || Error::ParseError( format!( "Key '{}' not found in JSON", select_row_by_key ) ) )?; - - let mut value_ranges= Vec::new(); - - // wrap_row() - for ( key, value ) in pairs.columns.into_iter() + match parse_json( json_str ) { - // There is no sense to use this function, instead we can just check if a char is uppercase if not then convert it to uppercase - is_all_uppercase_letters( key.as_str() )?; - value_ranges.push - ( - ValueRange - { - range: Some( format!( "{}!{}{}", table_name, key, row_id ) ), - values: Some( vec![ vec![ JsonValue::String( value.to_string() ) ] ] ), - // Change to rows - ..Default::default() - } - ); - }; + Ok( parsed_json ) => + match update_row( parsed_json.columns ) + { + Ok( ( _, values ) ) => + { + match values.total_updated_cells + { + Some( val ) => Ok( val ), + None => Ok( 0 ), + } + } + } + Err( error ) => Err( error ), + } + + let secret = Secret::read(); + let hub = hub( &secret ).await.context( "Failed to create a hub" ); - // Also read about it and change values let req = BatchUpdateValuesRequest { value_input_option: Some( "USER_ENTERED".to_string() ), @@ -144,6 +158,40 @@ mod private } Err( error ) => Err( Error::ApiError( error ) ), } + + // let row_id = pairs + // .columns + // .remove( select_row_by_key ) + // .ok_or_else( || Error::ParseError( format!( "Key '{}' not found in JSON", select_row_by_key ) ) )?; + + // wrap_row() + + + // // Also read about it and change values + // let req = BatchUpdateValuesRequest + // { + // value_input_option: Some( "USER_ENTERED".to_string() ), + // data: Some( value_ranges ), + // include_values_in_response: Some( true ), + // ..Default::default() + // }; + + // match hub + // .spreadsheets() + // .values_batch_update( req, spreadsheet_id ) + // .doit() + // .await + // { + // Ok( ( _, values ) ) => + // { + // match values.total_updated_cells + // { + // Some( val ) => Ok( val ), + // None => Ok( 0 ), + // } + // } + // Err( error ) => Err( Error::ApiError( error ) ), + // } } } diff --git a/module/move/gspread/src/debug/rows.rs b/module/move/gspread/src/debug/rows.rs index 95b11a10ba..3b907bbf9c 100644 --- a/module/move/gspread/src/debug/rows.rs +++ b/module/move/gspread/src/debug/rows.rs @@ -7,102 +7,55 @@ mod private use crate::*; use std::borrow::Cow; - use std::collections::HashMap; use actions::gspread::Result; use format_tools::TableWithFields; /// Struct to keep row and its id. - #[ derive( Debug, Clone ) ] - pub struct RowWrapper - { - /// Row's id - pub id : usize, - /// Row wich is represented as an array of typle, where tuple is a pair of key value, where key is a column's name and value is a value of a cell. - pub row : Vec< ( String, String ) >, - } + // #[ derive( Debug, Clone ) ] + // pub struct RowWrapper + // { + // /// Row's id + // pub row_key : usize, + // /// Row wich is represented as an array of typle, where tuple is a pair of key value, where key is a column's name and value is a value of a cell. + // pub row : Vec< ( String, String ) >, + // } + + // impl RowWrapper + // { + // /// Just constructor + // pub fn new( row_key : usize, row : Vec< ( String, String ) > ) -> Self + // { + // Self + // { + // row_key, + // row, + // } + // } + // } + + // impl TableWithFields for RowWrapper {} + // impl Fields< &'_ str, Option< Cow< '_, str > > > + // for RowWrapper + // { + // type Key< 'k > = &'k str; + // type Val< 'v > = Option< Cow< 'v, str > >; + + // fn fields( &self ) -> impl IteratorTrait< Item= ( &'_ str, Option > ) > + // { + // self.row.iter().map( | ( key, value ) | { + // ( key.as_str(), Some( Cow::Borrowed( value.as_str() ) ) ) + // } ) + // } + // } - impl RowWrapper - { - /// Just constructor - pub fn new( id : usize, row : Vec< ( String, String ) > ) -> Self - { - Self - { - id, - row, - } - } - } - - impl TableWithFields for RowWrapper {} - impl Fields< &'_ str, Option< Cow< '_, str > > > - for RowWrapper - { - type Key< 'k > = &'k str; - type Val< 'v > = Option< Cow< 'v, str > >; - - fn fields( &self ) -> impl IteratorTrait< Item= ( &'_ str, Option > ) > - { - self.row.iter().map( | ( key, value ) | { - ( key.as_str(), Some( Cow::Borrowed( value.as_str() ) ) ) - } ) - // let mut dst = Vec::new(); - - // for ( index, value ) in self.row.iter().enumerate() - // { - // let column_name = format!( "{} ", index ); - // let title = Box::leak( column_name.into_boxed_str() ) as &str; - // let cleaned: String = value - // .to_string() - // .chars() - // .skip( 1 ) - // .take( value.to_string().chars().count() - 2 ) - // .collect(); - - // dst.push( ( title, Some( Cow::Owned( cleaned ) ) ) ) - // } - - // //adding empty values for missing cells - // for index in self.row.len()..self.max_len - // { - // let column_name = format!( "{}", index ); - // let title = Box::leak( column_name.into_boxed_str() ) as &str; - // dst.push( ( title, Some( Cow::Owned( "".to_string() ) ) ) ); - // } + +} - // dst.into_iter() - } - } - /// Function to wrap a row. - /// - /// It converts from HashMap to a row wich is actually sorted array, by column name. - /// - /// **Params** - /// - `id` : Row's id. - /// - `vales` : Pairs of key value, where key is a clomun's name and value is a value of cell. - /// - /// **Returns** - /// - `RowWrapper` object. - pub fn wrap_row - ( - id : usize, - values : HashMap< String, String > - ) -> Result< RowWrapper > - { - let mut row: Vec< ( String, String ) > = values.into_iter().collect(); - - row.sort_by( | a, b | a.0.cmp( &b.0 ) ); +// crate::mod_interface! +// { +// own use +// { - Ok ( RowWrapper::new( id, row ) ) - } -} - -crate::mod_interface! -{ - own use - { - wrap_row, - RowWrapper - }; -} \ No newline at end of file +// }; +// } \ No newline at end of file From bc30e7a087189f42e81729e1eace90f5128ba4f0 Mon Sep 17 00:00:00 2001 From: Vsevolod Date: Sat, 28 Dec 2024 13:02:38 +0100 Subject: [PATCH 18/18] update_row function and documentation for some functions and structures were added --- module/move/gspread/src/actions/gspread.rs | 116 +++++++++-- .../gspread/src/actions/gspread_cells_set.rs | 189 ++++++------------ module/move/gspread/src/commands/gspread.rs | 3 +- .../gspread/src/commands/gspread_cells.rs | 4 +- module/move/gspread/src/debug.rs | 7 +- module/move/gspread/src/debug/row_wrapper.rs | 62 ++++++ module/move/gspread/src/debug/rows.rs | 61 ------ 7 files changed, 233 insertions(+), 209 deletions(-) create mode 100644 module/move/gspread/src/debug/row_wrapper.rs delete mode 100644 module/move/gspread/src/debug/rows.rs diff --git a/module/move/gspread/src/actions/gspread.rs b/module/move/gspread/src/actions/gspread.rs index 90d4ae75b2..711547926d 100644 --- a/module/move/gspread/src/actions/gspread.rs +++ b/module/move/gspread/src/actions/gspread.rs @@ -10,15 +10,35 @@ mod private use error_tools::typed::Error; use derive_tools::AsRefStr; use crate::*; - use ser::{DisplayFromStr, JsonValue}; + use ser:: + { + DisplayFromStr, + JsonValue + }; use std::collections::HashMap; - use google_sheets4::api::{BatchUpdateValuesResponse, ValueRange}; + use google_sheets4::api:: + { + BatchUpdateValuesResponse, + BatchUpdateValuesRequest, + ValueRange + }; #[ ser::serde_as ] #[ derive( Debug, Error, AsRefStr, ser::Serialize ) ] #[ serde( tag = "type", content = "data" ) ] + + /// Represents errors that can occur while interacting with the Google Sheets API + /// or during related operations in the application. pub enum Error { + /// Represents an error returned by the Google Sheets API. + /// + /// # Details + /// This error occurs when the API returns a specific error message. + /// The error message from the Google Sheets API is stored and displayed. + /// + /// # Fields + /// - `google_sheets4::Error`: The raw error returned by the API. #[ error( "Google Sheets returned error:\n{0}" ) ] ApiError ( @@ -27,24 +47,67 @@ mod private google_sheets4::Error ), + /// Represents an error that occurs while initializing Google Sheets Hub. + /// + /// # Details + /// This error indicates that the application failed to properly configure with the Google Sheets Hub. + /// + /// # Fields + /// - `String`: A detailed error message describing the issue. + #[ error( "Hub Error:\n{0}" ) ] + HubError + ( + String + ), + + /// Represents an error caused by an invalid URL format. + /// + /// # Details + /// This error occurs when the provided URL does not match the expected format + /// + /// # Fields + /// - `String`: The invalid URL or a message describing the issue. #[ error( "Invalid URL format:\n{0}" ) ] InvalidUrl ( String ), + /// Represents an error related to a cell in the spreadsheet. + /// + /// # Details + /// This error indicates that a cell was not got or updated + /// + /// # Fields + /// - `String`: A message describing the issue with the cell. #[ error( "Cell error:\n{0}" ) ] CellError ( String ), + /// Represents an error caused by invalid JSON input or parsing issues. + /// + /// # Details + /// This error occurs when the provided JSON data does not conform to the expected + /// structure or format. + /// + /// # Fields + /// - `String`: A detailed error message describing the JSON issue. #[ error( "Invalid JSON format:\n{0}" ) ] InvalidJSON ( String ), + /// Represents a generic parsing error. + /// + /// # Details + /// This error is raised when a string or other input cannot be parsed + /// into the expected format or structure. + /// + /// # Fields + /// - `String`: A message describing the parse error. #[ error( "Parse error:\n{0}" ) ] ParseError ( @@ -52,6 +115,7 @@ mod private ) } + /// Retrive spreadsheet id from url pub fn get_spreadsheet_id_from_url ( url : &str @@ -75,24 +139,34 @@ mod private /// Function to update a row on a Google Sheet. /// - /// It converts from HashMap to a row wich is actually sorted array, by column name. + /// It sends HTTP request to Google Sheets API and change row wich provided values. /// /// **Params** - /// - `id` : Row's id. - /// - `vales` : Pairs of key value, where key is a clomun's name and value is a value of cell. + /// - `spreadsheet_id` : Spreadsheet identifire. + /// - `sheet_name` : Sheet name. + /// - `row_key` : row's key. + /// - `row_key_val` : pairs of key value, where key is a column name and value is a new value. /// /// **Returns** - /// - `RowWrapper` object. + /// - `Result` pub async fn update_row ( - row_key : usize, - values : HashMap< String, String >, - sheet_name : &str - ) -> Result< Vec< ValueRange > > + spreadsheet_id : &str, + sheet_name : &str, + row_key : &str, + row_key_val : HashMap< String, String > + ) -> Result< BatchUpdateValuesResponse > { - let mut value_ranges = Vec::with_capacity( values.len() ); + let secret = Secret::read(); + let hub = hub(&secret) + .await + .map_err( | _ | { + Error::HubError( format!( "Failed to create a hub. Ensure that you have a .env file with Secrets" ) ) + })?; - for ( col_name, value ) in values { + let mut value_ranges = Vec::with_capacity( row_key_val.len() ); + + for ( col_name, value ) in row_key_val { value_ranges.push ( ValueRange @@ -104,7 +178,23 @@ mod private ) } - Ok( value_ranges ) + let req = BatchUpdateValuesRequest + { + value_input_option: Some( "USER_ENTERED".to_string() ), + data: Some( value_ranges ), + include_values_in_response: Some( true ), + ..Default::default() + }; + + match hub + .spreadsheets() + .values_batch_update( req, spreadsheet_id ) + .doit() + .await + { + Ok( ( _, response ) ) => Ok( response ), + Err( error ) => Err( Error::ApiError( error ) ), + } } pub type Result< T > = core::result::Result< T, Error >; diff --git a/module/move/gspread/src/actions/gspread_cells_set.rs b/module/move/gspread/src/actions/gspread_cells_set.rs index 0ccc68a77f..1099a613d4 100644 --- a/module/move/gspread/src/actions/gspread_cells_set.rs +++ b/module/move/gspread/src/actions/gspread_cells_set.rs @@ -11,56 +11,70 @@ mod private Result, update_row }; - use google_sheets4::api:: - { - BatchUpdateValuesRequest, - ValueRange - }; - use ser:: - { - Deserialize, - JsonValue - }; + use ser:: Deserialize; use std::collections::HashMap; - /// TODO: Add documentation + /// Structure to keep rows key and new values for cells updating. #[ derive( Deserialize, Debug ) ] - struct ParsedJson + struct ParsedJson< 'a > { - #[ serde( flatten ) ] - columns : HashMap< String, String > + row_key : &'a str, + row_key_val : HashMap< String, String > } - /// TODO: Add documentation - fn parse_json + /// Function to parse `--json` flag. + /// + /// It retirive `--select-row-by-key` flag from json and set it to `row_key` field. + /// Other pairs it set to `row_key_val` + /// + /// **Returns** + /// - `ParsedJson` object + fn parse_json< 'a > ( - json_str : &str - ) -> Result< ParsedJson > + json_str : &'a str, + select_row_by_key : &str, + ) -> Result< ParsedJson< 'a > > { - let parsed_json = serde_json::from_str::< HashMap< String, String > >( json_str ) - .map_err( | error | format!( "Failed to parse JSON: {}", error ) ) - .and_then - ( | map | - { - for ( col_name, _value ) in &map - { - if !col_name.chars().all( | c | c.is_alphabetic() && c.is_uppercase() ) - { - return Err - ( - format!( "Invalid column name: {}. Allowed only uppercase alphabetic letters (A-Z)", col_name ) - ); - } - } - Ok( map ) - }); - - match parsed_json + let mut parsed_json: HashMap< String, String > = serde_json::from_str( json_str ) + .map_err( | error | Error::InvalidJSON( format!( "Failed to parse JSON: {}", error ) ) )?; + + let row_key = if let Some( row_key ) = parsed_json.remove( select_row_by_key ) { - Ok( map ) => Ok( ParsedJson{ columns: map } ), - Err( error ) => Err( Error::InvalidJSON( error ) ) - } - + Box::leak( row_key.into_boxed_str() ) + } + else + { + return Err + ( + Error::InvalidJSON + ( + format!( "Key '{}' not found in JSON", select_row_by_key) + ) + ); + }; + + for ( col_name, _ ) in &parsed_json + { + if !col_name.chars().all( | c | c.is_alphabetic() && c.is_uppercase() ) + { + return Err + ( + Error::InvalidJSON + ( + format!( "Invalid column name: {}. Allowed only uppercase alphabetic letters (A-Z)", col_name ) + ) + ); + } + }; + + Ok + ( + ParsedJson + { + row_key : row_key, + row_key_val : parsed_json + } + ) } /// Check availables keys. @@ -84,28 +98,8 @@ mod private } } - - // fn check_col_names - // ( - // s : &str - // ) -> Result< () > - // { - // if s.chars().all( | c | c.is_ascii_uppercase() ) - // { - // Ok( () ) - // } - // else - // { - // Err - // ( - // Error::ParseError( format!( "The string '{}' contains invalid characters. Only uppercase letters (A-Z) are allowed.", s ) ) - // ) - // } - // } - pub async fn action ( - hub : &SheetsType, select_row_by_key : &str, json_str : &str, spreadsheet_id : &str, @@ -114,84 +108,23 @@ mod private { check_select_row_by_key( select_row_by_key )?; - match parse_json( json_str ) + match parse_json( json_str, select_row_by_key ) { Ok( parsed_json ) => - match update_row( parsed_json.columns ) + match update_row( spreadsheet_id, table_name, parsed_json.row_key, parsed_json.row_key_val ).await { - Ok( ( _, values ) ) => + Ok( response ) => { - match values.total_updated_cells + match response.total_updated_cells { Some( val ) => Ok( val ), None => Ok( 0 ), } - } + }, + Err( error ) => Err( error ) } Err( error ) => Err( error ), } - - let secret = Secret::read(); - let hub = hub( &secret ).await.context( "Failed to create a hub" ); - - let req = BatchUpdateValuesRequest - { - value_input_option: Some( "USER_ENTERED".to_string() ), - data: Some( value_ranges ), - include_values_in_response: Some( true ), - ..Default::default() - }; - - match hub - .spreadsheets() - .values_batch_update( req, spreadsheet_id ) - .doit() - .await - { - Ok( ( _, values ) ) => - { - match values.total_updated_cells - { - Some( val ) => Ok( val ), - None => Ok( 0 ), - } - } - Err( error ) => Err( Error::ApiError( error ) ), - } - - // let row_id = pairs - // .columns - // .remove( select_row_by_key ) - // .ok_or_else( || Error::ParseError( format!( "Key '{}' not found in JSON", select_row_by_key ) ) )?; - - // wrap_row() - - - // // Also read about it and change values - // let req = BatchUpdateValuesRequest - // { - // value_input_option: Some( "USER_ENTERED".to_string() ), - // data: Some( value_ranges ), - // include_values_in_response: Some( true ), - // ..Default::default() - // }; - - // match hub - // .spreadsheets() - // .values_batch_update( req, spreadsheet_id ) - // .doit() - // .await - // { - // Ok( ( _, values ) ) => - // { - // match values.total_updated_cells - // { - // Some( val ) => Ok( val ), - // None => Ok( 0 ), - // } - // } - // Err( error ) => Err( Error::ApiError( error ) ), - // } } } diff --git a/module/move/gspread/src/commands/gspread.rs b/module/move/gspread/src/commands/gspread.rs index 59fe8cae55..7150856df1 100644 --- a/module/move/gspread/src/commands/gspread.rs +++ b/module/move/gspread/src/commands/gspread.rs @@ -103,7 +103,8 @@ mod private Command::Cells( cells_command) => { - gspread_cells::command( hub, cells_command ).await; + // hub + gspread_cells::command( cells_command ).await; }, } diff --git a/module/move/gspread/src/commands/gspread_cells.rs b/module/move/gspread/src/commands/gspread_cells.rs index 0d828a8cb9..dd53d80d10 100644 --- a/module/move/gspread/src/commands/gspread_cells.rs +++ b/module/move/gspread/src/commands/gspread_cells.rs @@ -53,7 +53,7 @@ mod private pub async fn command ( - hub : &SheetsType, + // hub : &SheetsType, commands : Commands ) { @@ -73,7 +73,7 @@ mod private match actions::gspread_cells_set::action ( - &hub, + // &hub, select_row_by_key.as_str(), json.as_str(), spreadsheet_id, diff --git a/module/move/gspread/src/debug.rs b/module/move/gspread/src/debug.rs index 8095603737..7f1d303941 100644 --- a/module/move/gspread/src/debug.rs +++ b/module/move/gspread/src/debug.rs @@ -9,16 +9,15 @@ use format_tools:: }; use std::borrow::Cow; -pub mod rows; +pub mod row_wrapper; crate::mod_interface! { exposed use { - rows:: + row_wrapper:: { - RowWrapper, - wrap_row + RowWrapper } }; } diff --git a/module/move/gspread/src/debug/row_wrapper.rs b/module/move/gspread/src/debug/row_wrapper.rs new file mode 100644 index 0000000000..7802773c47 --- /dev/null +++ b/module/move/gspread/src/debug/row_wrapper.rs @@ -0,0 +1,62 @@ +//! +//! Gspread wrapper for outputting data to console +//! +//! It is used for "header" and "rows" commands +//! +use super::*; +use crate::*; +use ser::JsonValue; + + +#[ derive( Debug ) ] +pub struct RowWrapper +{ + pub row: Vec< JsonValue >, + pub max_len: usize +} +impl Clone for RowWrapper +{ + fn clone( &self ) -> Self + { + Self + { + row: self.row.clone(), + max_len: self.max_len.clone() + } + } +} +impl TableWithFields for RowWrapper {} +impl Fields< &'_ str, Option< Cow< '_, str > > > +for RowWrapper +{ + type Key< 'k > = &'k str; + type Val< 'v > = Option< Cow< 'v, str > >; + fn fields( &self ) -> impl IteratorTrait< Item= ( &'_ str, Option > ) > + { + let mut dst = Vec::new(); + + for ( index, value ) in self.row.iter().enumerate() + { + let column_name = format!( "{} ", index ); + let title = Box::leak( column_name.into_boxed_str() ) as &str; + let cleaned: String = value + .to_string() + .chars() + .skip( 1 ) + .take( value.to_string().chars().count() - 2 ) + .collect(); + + dst.push( ( title, Some( Cow::Owned( cleaned ) ) ) ) + } + + //adding empty values for missing cells + for index in self.row.len()..self.max_len + { + let column_name = format!( "Column{}", index ); + let column_name = format!( "{}", index ); + let title = Box::leak( column_name.into_boxed_str() ) as &str; + dst.push( ( title, Some( Cow::Owned( "".to_string() ) ) ) ); + } + dst.into_iter() + } +} \ No newline at end of file diff --git a/module/move/gspread/src/debug/rows.rs b/module/move/gspread/src/debug/rows.rs deleted file mode 100644 index 3b907bbf9c..0000000000 --- a/module/move/gspread/src/debug/rows.rs +++ /dev/null @@ -1,61 +0,0 @@ - - - -mod private -{ - use super::*; - use crate::*; - - use std::borrow::Cow; - use actions::gspread::Result; - use format_tools::TableWithFields; - - /// Struct to keep row and its id. - // #[ derive( Debug, Clone ) ] - // pub struct RowWrapper - // { - // /// Row's id - // pub row_key : usize, - // /// Row wich is represented as an array of typle, where tuple is a pair of key value, where key is a column's name and value is a value of a cell. - // pub row : Vec< ( String, String ) >, - // } - - // impl RowWrapper - // { - // /// Just constructor - // pub fn new( row_key : usize, row : Vec< ( String, String ) > ) -> Self - // { - // Self - // { - // row_key, - // row, - // } - // } - // } - - // impl TableWithFields for RowWrapper {} - // impl Fields< &'_ str, Option< Cow< '_, str > > > - // for RowWrapper - // { - // type Key< 'k > = &'k str; - // type Val< 'v > = Option< Cow< 'v, str > >; - - // fn fields( &self ) -> impl IteratorTrait< Item= ( &'_ str, Option > ) > - // { - // self.row.iter().map( | ( key, value ) | { - // ( key.as_str(), Some( Cow::Borrowed( value.as_str() ) ) ) - // } ) - // } - // } - - -} - - -// crate::mod_interface! -// { -// own use -// { - -// }; -// } \ No newline at end of file