diff --git a/.gitignore b/.gitignore index 8e55166..1433f52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,21 @@ **/target **/build **/.vscode -Cargo.lock + .DS_Store + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3b14147 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + +## 2.0.0 +- Released on 2024//: The package was originally made by Pascal CHENEVAS from . The Carbone team is now maintaining the SDK. This version brings all missing functions to interact with the Carbone API. +- Added function `getStatus`: It return the current status and the version of the API as `String`. +- Added error `HttpError`: It return the status code and a error message. +- Modified for the `generate_report`: Optimization of api calls when there is error 404. +- Modified for the `render_data`: When there is an error in the request, the function returns the status code and an error message. +- Modified for the `new`: The value `api_token` is optional. +- Added units tests. + +### v1.0.0 +- Released on \ No newline at end of file diff --git a/README.md b/README.md index ab98d83..6658c87 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ sequenceDiagram # Installation -TODO +carbone-sdk-rust = "x.x" # Render a new report @@ -42,38 +42,363 @@ use carbone_sdk_rust::template::TemplateId; use carbone_sdk_rust::errors::CarboneError; +use std::fs::File; +use std::io::Write + #[tokio::main] async fn main() -> Result<(), CarboneError> { - let token = match env::var("CARBONE_TOKEN") { - Ok(v) => v, - Err(e) => panic!("{}", e.to_string()) - }; - + let token = "Token"; + let config: Config = Default::default(); - let api_token = ApiJsonToken::new(token)?; + let api_token = ApiJsonToken::new(token.to_string())?; let json_data_value = String::from(r#" - "data" : { - "firstname" : "John", - "lastname" : "Wick" - }, - "convertTo" : "odt" + { + "data" : { + "firstname" : "John", + "lastname" : "Wick" + }, + "convertTo" : "odt" + } "#); let json_data = JsonData::new(json_data_value)?; - let template_id = TemplateId::new("0545253258577a632a99065f0572720225f5165cc43db9515e9cef0e17b40114".to_string())?; + let template_id = TemplateId::new("YourTemplateId".to_string())?; let carbone = Carbone::new(&config, &api_token)?; - let _report_content = carbone.generate_report_with_template_id(template_id, json_data).await?; + let report_content = match carbone.generate_report_with_template_id(template_id, json_data).await { + Ok(v) => v, + Err(e) => panic!("{}", e.to_string()) + }; + + let mut output_file = File::create("report.pdf").expect("Failed to create file"); + + if let Err(e) = output_file.write_all(&report_content) { + eprintln!("Failed to write to file: {:?}", e); + } Ok(()) } ``` +## Rust SDK API + +### Table of content + +- SDK functions: + - [CarboneSDK Constructor](#carbone-sdk-constructor) + - [Generate and Download a Document](#generate-and-download-document) + - [Generate a Document Only](#generate-document-only) + - [Download a Document Only](#download-document-only) + - [Add a Template](#add-template) + - [Delete a Template](#delete-template) + - [Get a Template](#get-template) + - [Set Carbone URL](#set-carbone-url) + - [Get API status](#get-api-status) + - [Set API Config](#set-api-version) +- [Build commands](#build-commands) +- [Test commands](#test-commands) +- [Contributing](#-contributing) + +### Carbone SDK Constructor + +**Definition** + +```rust +let config: Config; +``` + +**Example** + +Example of a new SDK instance for **Carbone Cloud**: +Get your API key on your Carbone account: https://account.carbone.io/. +```rust +// For Carbone Cloud, provide your API Access Token as first argument: +let token = "Token"; +let config: Config = Default::default(); +let api_token = ApiJsonToken::new(token.to_string())?; +let carbone = Carbone::new(&config, Some(&api_token))?; +``` + +Example of a new SDK instance for **Carbone On-premise** or **Carbone On-AWS**: +```rust +// Define the URL of your Carbone On-premise Server or AWS EC2 URL: +let config: Config = Config::new("ON_PREMISE_URL".to_string(), "api_time_out_in_sec_in_u64", ApiVersion::new("4".to_string()).expect("REASON")).expect("REASON"); +let carbone = Carbone::new(&config, None)?; +``` + +Constructor to create a new instance of CarboneSDK. +The access token can be pass as an argument or by the environment variable "CARBONE_TOKEN". +Get your API key on your Carbone account: https://account.carbone.io/. +To set a new environment variable, use the command: +```bash +$ export CARBONE_TOKEN=your-secret-token +``` +Check if it is set by running: +```bash +$ printenv | grep "CARBONE_TOKEN" +``` + +### Generate and Download a Document + +```rust +pub async fn generate_report( &self, template_name: String, template_data: Vec, json_data: JsonData, payload: Option<&str>, salt: Option<&str>); +``` + +or + +```rust +pub async fn pub async fn generate_report_with_template_id( &self, template_id: TemplateId, json_data: JsonData); +``` + +The render function generates a document using a specified template and data. It takes two parameters: +* json_data: A stringified JSON containing the data to populate the template. +* template_data: The content of the file in `Vec`. +* template_data: A template ID. + +**Function Behavior** + +2. Template ID as Second Argument: + - If a template ID is provided, the function calls [render_data](#generate-document-only) to generate the report. It then calls [get_report](#download-document-only) to retrieve the generated report. + - If the template ID does not exist, an error is returned. + +**Example** + +```rust +let file_name = "name_file.extention"; +let file_path = format!("your/path/{}", file_name); +let file_content = fs::read(file_path)?; + +let json_data_value = String::from(r#" + { + "data" : { + "firstname" : "John", + "lastname" : "Wick" + }, + "convertTo" : "odt" + } + "#); + +let json_data = JsonData::new(json_data_value)?; + +let content = match carbone.generate_report(file_name.to_string(), file_content, json_data, None, None).await { + Ok(v) => v, + Err(e) => panic!("{}", e.to_string()) + }; + +``` + +or + +```rust +let template_id = TemplateId::new("template_id".to_string())?; +let json_data = String::from(r#" + { + "data" : { + "firstname" : "John", + "lastname" : "Wick" + }, + "convertTo" : "odt" + } + "#); + +let json_data = JsonData::new(json_data_value)?; + +let content = match carbone.generate_report_with_template_id( template_id, filte_content, json_data).await { + Ok(v) => v, + Err(e) => panic!("{}", e.to_string()) + }; +``` + + +### Upload Template + +```rust +pub async fn upload_template(&self,file_name: &str,file_content: Vec,salt: Option<&str>); +``` + +Add a template as file-content `Vec` and the function return the template ID as `String`. + +**Example** + +```rust + +let template_name = "template.odt".to_string(); +let template_path = format!("src/{}", template_name); +let template_data = fs::read(template_path.to_owned())?; + +let template_id = match carbone.upload_template(template_name, template_data, None).await { + Ok(v) => v, + Err(e) => panic!("{}", e.to_string()) + }; +``` + +### Delete Template + +```rust +pub async fn delete_template(&self, template_id: TemplateId); +``` + +Delete a template by providing a template ID as `template_id`, and it returns whether the request succeeded as a `Boolean`. + +**Example** + +```rust +let template_id = TemplateId::new("template_id".to_string())?; + +let boolean = match carbone.delete_template(template_id).await { + Ok(v) => v, + Err(e) => panic!("{}", e.to_string()) + }; +``` + +### Generate Document only + +The generate_report function takes a template ID as `String`, and the JSON data-set as `JsonData`. +It return a `renderId`, you can pass this `renderId` at [get_report](#download-document-only) for download the document. + +```rust +pub async fn render_data( &self, template_id: TemplateId, json_data: JsonData); +``` + +**Example** + +```rust + +let template_id = TemplateId::new("template_id".to_string())?; + +let json_data = String::from(r#" + { + "data" : { + "firstname" : "John", + "lastname" : "Wick" + }, + "convertTo" : "odt" + } + "#); + +let json_data = JsonData::new(json_data_value)?; + +let render_id = match carbone.render_data(template_id, json_data).await { + Ok(v) => v, + Err(e) => panic!("{}", e.to_string()) + }; + + +``` + +### Download Document Only + +**Definition** +```rust +pub async fn get_report(&self, render_id: &RenderId); +``` + +**Example** + +```rust + +let render_id = RenderId::new("render_id".to_string())?; + +let content = match carbone.get_report(&render_id).await { + Ok(v) => v, + Err(e) => panic!("{}", e.to_string()) + }; + +``` + +## Get Template + +**Definition** + +```rust +pub async fn download_template(&self, template_id: &TemplateId); +``` + +Provide a template ID as `String` and it returns the file as `Bytes`. + +**Example** + +```rust +let template_id = TemplateId::new("template_id".to_string())?; + +let content = match carbone.download_template(&template_id).await { + Ok(v) => v, + Err(e) => panic!("{}", e.to_string()) + }; +``` + +### Get API Status + +**Definition** + +```rust + +pub async fn get_status(&self); + +``` + +The function requests the Carbone API to get the current status and version as `String`. + +**Example** + +```rust +let status = match carbone.get_status().await { + Ok(v) => v, + Err(e) => panic!("{}", e.to_string()) + }; +``` + +### Set API Config + +**Definition** + +```rust +pub fn new(api_url: String, api_timeout: u64, api_version: ApiVersion) +``` + +Set the API URL for Carbone On-premise or Carbone On-AWS. + +Specify the version of the Carbone CLoud API you want to request as second argument of the constructor. +By default, all requested are made to the Carbone API version `4`. + +**Example** + +```rust +let config: Config = Config::new("ON_PREMISE_URL".to_string(), "api_time_out_in_sec_in_u64", ApiVersion::new("Version".to_string()).expect("REASON")).expect("REASON"); +``` +## Build commands + +At the root of the SDK repository run: +```sh +cargo build +``` + +In another Rust project, you can load the local build of the SDK, in the Cargo.toml: +```toml + +carbone-sdk-rust = {path = "your/local/path"} +``` +Finally, compile your Rust project with the SDK: +```sh +cargo run +``` + +## Test commands + +Execute unit tests: +```sh +cargo test +``` +Execute unit tests with coverage: +```sh +cargo tarpaulin +``` + # References [Carbone.io](https://carbone.io) a report generator. diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index 82d0ec3..0000000 Binary files a/src/.DS_Store and /dev/null differ diff --git a/src/carbone.rs b/src/carbone.rs index 33bfb58..eb89485 100644 --- a/src/carbone.rs +++ b/src/carbone.rs @@ -10,6 +10,7 @@ use reqwest::Client; use reqwest::ClientBuilder; use reqwest::StatusCode; + use crate::carbone_response::APIResponse; use crate::config::Config; use crate::errors::*; @@ -26,24 +27,26 @@ pub struct Carbone<'a> { } impl<'a> Carbone<'a> { - pub fn new(config: &'a Config, api_token: &'a ApiJsonToken) -> Result { + pub fn new(config: &'a Config, api_token: Option<&'a ApiJsonToken>) -> Result { let mut headers = header::HeaderMap::new(); headers.insert( "carbone-version", HeaderValue::from_str(config.api_version.as_str()).unwrap(), ); + if api_token != None + { + let bearer = format!("Bearer {}", api_token.expect("REASON").as_str()); - let bearer = format!("Bearer {}", api_token.as_str()); - - let mut auth_value = header::HeaderValue::from_str(bearer.as_str()).unwrap(); - auth_value.set_sensitive(true); + let mut auth_value = header::HeaderValue::from_str(bearer.as_str()).unwrap(); + auth_value.set_sensitive(true); - headers.insert(header::AUTHORIZATION, auth_value); + headers.insert(header::AUTHORIZATION, auth_value); + } - let http_client = ClientBuilder::new() - .default_headers(headers) - .timeout(Duration::from_secs(config.api_timeout)) - .build()?; + let http_client = ClientBuilder::new() + .default_headers(headers) + .timeout(Duration::from_secs(config.api_timeout)) + .build()?; Ok(Self { config, @@ -52,41 +55,6 @@ impl<'a> Carbone<'a> { } // Delete a template from the Carbone Service. - /// - /// - /// # Example - /// - /// ```no_run - /// use std::env; - /// - /// use carbone_sdk_rust::config::Config; - /// use carbone_sdk_rust::carbone::Carbone; - /// use carbone_sdk_rust::types::ApiJsonToken; - /// use carbone_sdk_rust::template::TemplateId; - /// use carbone_sdk_rust::errors::CarboneError; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), CarboneError> { - /// - /// let token = match env::var("CARBONE_TOKEN") { - /// Ok(v) => v, - /// Err(e) => panic!("{}", e.to_string()) - /// }; - /// - /// let config: Config = Default::default(); - /// - /// let api_token = ApiJsonToken::new(token)?; - /// - /// let template_id = TemplateId::new("0545253258577a632a99065f0572720225f5165cc43db9515e9cef0e17b40114".to_string())?; - /// - /// let carbone = Carbone::new(&config, &api_token)?; - /// let is_deleted = carbone.delete_template(template_id).await.unwrap(); - /// - /// assert_eq!(is_deleted, true); - /// - /// Ok(()) - /// } - /// ``` pub async fn delete_template(&self, template_id: TemplateId) -> Result { let url = format!("{}/template/{}", self.config.api_url, template_id.as_str()); @@ -102,43 +70,6 @@ impl<'a> Carbone<'a> { } // Download a template from the Carbone Service. - /// - /// - /// # Example - /// - /// ```no_run - /// use std::env; - /// - /// use carbone_sdk_rust::config::Config; - /// use carbone_sdk_rust::carbone::Carbone; - /// use carbone_sdk_rust::types::ApiJsonToken; - /// use carbone_sdk_rust::template::TemplateId; - /// use carbone_sdk_rust::errors::CarboneError; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), CarboneError> { - /// - /// let token = match env::var("CARBONE_TOKEN") { - /// Ok(v) => v, - /// Err(e) => panic!("{}", e.to_string()) - /// }; - /// - /// let config: Config = Default::default(); - /// - /// let api_token = ApiJsonToken::new(token)?; - /// - /// let template_file = String::from("template.odt"); - /// - /// let template_id = TemplateId::new("0545253258577a632a99065f0572720225f5165cc43db9515e9cef0e17b40114".to_string())?; - /// let carbone = Carbone::new(&config, &api_token)?; - /// - /// let template_content = carbone.download_template(&template_id).await.unwrap(); - /// - /// assert_eq!(template_content.is_empty(), false); - /// - /// Ok(()) - /// } - /// ``` pub async fn download_template(&self, template_id: &TemplateId) -> Result { let url = format!("{}/template/{}", self.config.api_url, template_id.as_str()); @@ -153,53 +84,6 @@ impl<'a> Carbone<'a> { } /// Generate a report. - /// - /// - /// # Example - /// - /// ```no_run - /// use std::env; - /// - /// use carbone_sdk_rust::config::Config; - /// use carbone_sdk_rust::render::*; - /// use carbone_sdk_rust::carbone::Carbone; - /// use carbone_sdk_rust::types::{ApiJsonToken, JsonData}; - /// use carbone_sdk_rust::template::{TemplateFile,TemplateId}; - /// - /// use carbone_sdk_rust::errors::CarboneError; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), CarboneError> { - /// - /// let token = match env::var("CARBONE_TOKEN") { - /// Ok(v) => v, - /// Err(e) => panic!("{}", e.to_string()) - /// }; - /// - /// let config: Config = Default::default(); - /// - /// let api_token = &ApiJsonToken::new(token)?; - /// - /// let carbone = Carbone::new(&config, api_token)?; - /// - /// let json_data_value = String::from(r#" - /// "data" : { - /// "firstname" : "John", - /// "lastname" : "Wick" - /// }, - /// "convertTo" : "odt" - /// "#); - /// - /// let json_data = JsonData::new(json_data_value)?; - /// - /// let template_data: Vec = Vec::new(); // content of the template - /// let report_content = carbone.generate_report("template.odt".to_string(), template_data, json_data, None, None).await.unwrap(); - /// - /// assert_eq!(report_content.is_empty(), false); - /// - /// Ok(()) - /// } - /// ``` pub async fn generate_report( &self, template_name: String, @@ -210,62 +94,59 @@ impl<'a> Carbone<'a> { ) -> Result { let template_id_generated = TemplateId::from_bytes(template_data.to_owned(), payload)?; - - let result = self.download_template(&template_id_generated).await; - - let template_id = if result.is_err() { - self.upload_template(template_name.as_str(), template_data, salt).await? - } else { - template_id_generated + let mut template_id = template_id_generated; + let render_id; + + match self.render_data(template_id, json_data.clone()).await { + Ok(id) => { + render_id = Some(id); + } + Err(e) => match e { + CarboneError::HttpError { status_code, error_message } => { + if status_code == reqwest::StatusCode::NOT_FOUND { + template_id = self.upload_template(template_name.as_str(), template_data, salt).await?; + render_id = Some(self.render_data(template_id, json_data).await?); + } else { + return Err(CarboneError::HttpError { status_code, error_message }); + } + }, + CarboneError::Error(error_message) => { + return Err(CarboneError::Error(error_message)); + }, + _ => { + return Err(e); + } + } }; - - let render_id = self.render_data(template_id, json_data).await?; - let report_content = self.get_report(&render_id).await?; - + + let report_content = self.get_report(&render_id.unwrap()).await?; + Ok(report_content) } + /// Get a new report. - /// - /// - /// # Example - /// - /// ```no_run - /// use std::env; - /// - /// use carbone_sdk_rust::config::Config; - /// use carbone_sdk_rust::render::RenderId; - /// use carbone_sdk_rust::carbone::Carbone; - /// use carbone_sdk_rust::types::ApiJsonToken; - /// use carbone_sdk_rust::errors::CarboneError; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), CarboneError> { - /// - /// let token = match env::var("CARBONE_TOKEN") { - /// Ok(v) => v, - /// Err(e) => panic!("{}", e.to_string()) - /// }; - /// - /// let config: Config = Default::default(); - /// - /// let api_token = ApiJsonToken::new(token)?; - /// - /// let carbone = Carbone::new(&config, &api_token)?; - /// - /// let render_id = &RenderId::new("MTAuMjAuMjEuMTAgICAg01E98H4R7PMC2H6XSE5Z6J8XYQ.pdf".to_string())?; - /// let report_content = carbone.get_report(render_id).await.unwrap(); - /// - /// assert_eq!(report_content.is_empty(), false); - /// - /// Ok(()) - /// } - /// ``` pub async fn get_report(&self, render_id: &RenderId) -> Result { let url = format!("{}/render/{}", self.config.api_url, render_id.as_str()); let response = self.http_client.get(url).send().await?; + // let mut report_name = None; + + // if let Some(content_disposition) = response.headers().get("content-disposition") { + // if let Ok(disposition) = content_disposition.to_str() { + // let split_content_disposition: Vec<&str> = disposition.split('=').collect(); + + // if split_content_disposition.len() == 2 { + // let mut name = split_content_disposition[1].to_string(); + // if name.starts_with('"') && name.ends_with('"') { + // name = name[1..name.len() - 1].to_string(); + // } + // report_name = Some(name); + // } + // } + // } + if response.status() == StatusCode::OK { Ok(response.bytes().await?) } else { @@ -275,53 +156,6 @@ impl<'a> Carbone<'a> { } /// Generate a report with a template_id given. - /// - /// - /// # Example - /// - /// ```no_run - /// use std::env; - /// - /// use carbone_sdk_rust::config::Config; - /// use carbone_sdk_rust::render::*; - /// use carbone_sdk_rust::types::JsonData; - /// use carbone_sdk_rust::carbone::Carbone; - /// use carbone_sdk_rust::types::ApiJsonToken; - /// use carbone_sdk_rust::template::TemplateId; - /// - /// use carbone_sdk_rust::errors::CarboneError; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), CarboneError> { - /// - /// let token = match env::var("CARBONE_TOKEN") { - /// Ok(v) => v, - /// Err(e) => panic!("{}", e.to_string()) - /// }; - /// - /// let config: Config = Default::default(); - /// - /// let api_token = &ApiJsonToken::new(token)?; - /// - /// let template_id = TemplateId::new("0545253258577a632a99065f0572720225f5165cc43db9515e9cef0e17b40114".to_string())?; - /// let carbone = Carbone::new(&config, &api_token)?; - /// - /// let json_data_value = String::from(r#" - /// "data" : { - /// "firstname" : "John", - /// "lastname" : "Wick" - /// }, - /// "convertTo" : "odt" - /// "#); - /// - /// let json_data = JsonData::new(json_data_value)?; - /// let report_content = carbone.generate_report_with_template_id(template_id, json_data).await.unwrap(); - /// - /// assert_eq!(report_content.is_empty(), false); - /// - /// Ok(()) - /// } - /// ``` pub async fn generate_report_with_template_id( &self, template_id: TemplateId, @@ -334,51 +168,6 @@ impl<'a> Carbone<'a> { } /// Render data with a given template_id. - /// - /// - /// # Example - /// - /// ```no_run - /// use std::env; - /// - /// use carbone_sdk_rust::carbone::Carbone; - /// use carbone_sdk_rust::config::Config; - /// use carbone_sdk_rust::template::TemplateId; - /// use carbone_sdk_rust::errors::CarboneError; - /// use carbone_sdk_rust::types::{ApiJsonToken, JsonData}; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), CarboneError> { - /// - /// let token = match env::var("CARBONE_TOKEN") { - /// Ok(v) => v, - /// Err(e) => panic!("{}", e.to_string()) - /// }; - /// - /// let config: Config = Default::default(); - /// let api_token = ApiJsonToken::new(token)?; - /// - /// let template_id = TemplateId::new("foiejwoi21e093ru3209jf2093j".to_string())?; - /// - /// let carbone = Carbone::new(&config, &api_token)?; - /// - /// let json_data_value = String::from(r#" - /// "data" : { - /// "firstname" : "John", - /// "lastname" : "Wick" - /// }, - /// "convertTo" : "odt" - /// "#); - /// - /// let json_data = JsonData::new(json_data_value)?; - /// - /// let render_id = carbone.render_data(template_id, json_data).await.unwrap(); - /// - /// assert_eq!(render_id.as_str().is_empty(), false); - /// - /// Ok(()) - /// } - /// ``` pub async fn render_data( &self, template_id: TemplateId, @@ -394,6 +183,15 @@ impl<'a> Carbone<'a> { .send() .await?; + if !response.status().is_success() { + let status_code = response.status(); + let json = response.json::().await?; + return Err(CarboneError::HttpError { + status_code, + error_message: json.error.unwrap_or_else(|| "Unknown error".to_string()), + }); + } + let json = response.json::().await?; if json.success { @@ -404,44 +202,6 @@ impl<'a> Carbone<'a> { } /// Upload a template to the Carbone Service. - /// - /// - /// # Example - /// - /// ```no_run - /// use std::env; - /// use std::fs; - /// - /// use carbone_sdk_rust::config::Config; - /// use carbone_sdk_rust::carbone::Carbone; - /// use carbone_sdk_rust::types::ApiJsonToken; - /// use carbone_sdk_rust::template::TemplateFile; - /// use carbone_sdk_rust::errors::CarboneError; - /// - /// #[tokio::main] - /// async fn main() -> Result<(), CarboneError> { - /// - /// let token = match env::var("CARBONE_TOKEN") { - /// Ok(v) => v, - /// Err(e) => panic!("{}", e.to_string()) - /// }; - /// - /// let config: Config = Default::default(); - /// - /// let api_token = ApiJsonToken::new(token)?; - /// - /// let file_name = "template.odt"; - /// let file_path = format!("tests/data/{}", file_name); - /// let filte_content = fs::read(file_path)?; - /// - /// let carbone = Carbone::new(&config, &api_token)?; - /// let template_id = carbone.upload_template(file_name, filte_content, None).await.unwrap(); - /// - /// assert_eq!(template_id.as_str().is_empty(), false); - /// - /// Ok(()) - /// } - /// ``` pub async fn upload_template( &self, file_name: &str, @@ -464,7 +224,6 @@ impl<'a> Carbone<'a> { None => return Err(CarboneError::Error("Failed to fetch file name".to_string())), }; - println!("file_name = {}", file_name); let ext = file_path .extension() @@ -479,8 +238,6 @@ impl<'a> Carbone<'a> { let url = format!("{}/template", self.config.api_url); - println!("url = {}", url); - println!("form = {:?}", form); let response = self.http_client.post(url).multipart(form).send().await?; @@ -492,4 +249,20 @@ impl<'a> Carbone<'a> { Err(CarboneError::Error(json.error.unwrap())) } } + + + pub async fn get_status(&self) -> Result + { + let url = format!("{}/status", self.config.api_url); + + let response = self.http_client.get(url).send().await?; + + if response.status() == StatusCode::OK { + let body = response.text().await?; + Ok(body) + } else { + let json = response.json::().await?; + Err(CarboneError::Error(json.error.unwrap())) + } + } } diff --git a/src/config.rs b/src/config.rs index c34dec4..f17b241 100644 --- a/src/config.rs +++ b/src/config.rs @@ -23,26 +23,6 @@ pub struct Config { impl Config { /// Create a new Configuraiton. - /// - /// This function will create new Config. - /// - /// # Example - /// - /// ```no_run - /// - /// use carbone_sdk_rust::config::Config; - /// use carbone_sdk_rust::errors::CarboneError; - /// use carbone_sdk_rust::types::ApiVersion; - /// - /// fn main() -> Result<(), CarboneError> { - /// let api_version: ApiVersion = ApiVersion::new("4".to_string())?; - /// let config = Config::new( - /// "http://127.0.0.1:57780".to_string(), - /// 4, - /// api_version)?; - /// Ok(()) - /// } - /// ``` pub fn new(api_url: String, api_timeout: u64, api_version: ApiVersion) -> Result { let config = Self { api_url, @@ -55,22 +35,6 @@ impl Config { } /// Load a Configuraiton from a file. - /// - /// This function will create new Config struct with, - /// the values from the file. - /// - /// # Example - /// - /// ```no_run - /// - /// use carbone_sdk_rust::config::Config; - /// use carbone_sdk_rust::errors::CarboneError; - /// - /// fn main() -> Result<(), CarboneError> { - /// let config = Config::from_file("tests/config.test.json")?; - /// Ok(()) - /// } - /// ``` pub fn from_file(path: &str) -> Result { let file_content = fs::read_to_string(path).or(Err(CarboneError::FileNotFound(path.to_string())))?; @@ -81,26 +45,6 @@ impl Config { } /// Load a Default Configuraiton. -/// -/// This function will create new Config struct the with, -/// the default values. -/// -/// # Example -/// -/// ```no_run -/// -/// use carbone_sdk_rust::config::Config; -/// use carbone_sdk_rust::errors::CarboneError; -/// -/// fn main() -> Result<(), CarboneError> { -/// -/// let config: Config = Default::default(); -/// -/// assert_eq!(config.api_url, "https://api.carbone.io".to_string()); -/// -/// Ok(()) -/// } -/// ``` impl Default for Config { fn default() -> Self { Self { diff --git a/src/errors/mod.rs b/src/errors/mod.rs index 8638d7c..1c89b85 100644 --- a/src/errors/mod.rs +++ b/src/errors/mod.rs @@ -29,6 +29,11 @@ pub enum CarboneError { RequestBodyNotWellFormedJsonError, #[error("Carbone SDK {0:?} ParseError {1:?}")] ParseError(String, String), + #[error("Carbone SDK HttpError: {status_code:?} - {error_message}")] + HttpError { + status_code: reqwest::StatusCode, + error_message: String, + }, } impl From for CarboneError { diff --git a/src/types.rs b/src/types.rs index b2070bb..1de527f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -4,6 +4,8 @@ use crate::errors::CarboneError; pub type Result = std::result::Result; +// pub type Result<(T,U)> = std::result::Result<(T,U), CarboneError>; + #[derive(Debug, Clone, PartialEq, Eq)] pub struct ApiJsonToken(String); diff --git a/tests/.DS_Store b/tests/.DS_Store deleted file mode 100644 index 58a0f29..0000000 Binary files a/tests/.DS_Store and /dev/null differ diff --git a/tests/carbone_test.rs b/tests/carbone_test.rs index 8e40d88..3b2810a 100644 --- a/tests/carbone_test.rs +++ b/tests/carbone_test.rs @@ -8,7 +8,7 @@ use carbone_sdk_rust::carbone_response::APIResponse; use carbone_sdk_rust::errors::CarboneError; use carbone_sdk_rust::render::*; use carbone_sdk_rust::types::JsonData; - +use reqwest::StatusCode; mod helper; use helper::Helper; @@ -50,7 +50,7 @@ mod tests { let api_token = &helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let is_deleted = carbone.delete_template(template_id).await.unwrap(); mock_server.assert(); @@ -72,7 +72,7 @@ mod tests { "0545253258577a632a99065f0572720225f5165cc43db9515e9cef0e17b40114".to_string(), )?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let result = carbone.delete_template(template_id).await; assert!(result.is_err()); @@ -110,7 +110,7 @@ mod tests { let api_token = helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let result = carbone.delete_template(template_id).await; let expected_error = CarboneError::Error(error_msg); @@ -146,7 +146,7 @@ mod tests { let api_token = helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let template_content = carbone.download_template(&template_id).await.unwrap(); @@ -169,7 +169,7 @@ mod tests { "0545253258577a632a99065f0572720225f5165cc43db9515e9cef0e17b40114".to_string(), )?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let result = carbone.download_template(&template_id).await; @@ -208,7 +208,7 @@ mod tests { let api_token = helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let result = carbone.download_template(&template_id).await; @@ -232,7 +232,7 @@ mod tests { let config = helper.create_config_for_mock_server(Some(&server))?; let api_token = helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let report_data = fs::read_to_string("tests/data/report_data.json")?; @@ -289,7 +289,7 @@ mod tests { let config = helper.create_config_for_mock_server(Some(&server))?; let api_token = helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let report_data = fs::read_to_string("tests/data/report_data.json")?; @@ -309,15 +309,106 @@ mod tests { let expected_content = fs::read(file_path)?; - let mock_template_response = server.mock(|when, then| { + let mock_render_response = server.mock(|when, then| { + when.method("POST") + .path(format!("/render/{}", template_id.as_str())); + then.status(200).json_body(json!({ + "success": true, + "data": { + "renderId": render_id.as_str(), + "inputFileExtension": "odt" + } + })); + }); + + let mock_get_report_response = server.mock(|when, then| { when.method("GET") - .path(format!("/template/{}", template_id.as_str())); - then.status(200).body_from_file(template_file.path_as_str()); + .path(format!("/render/{}", render_id.as_str())); + then.status(200).body(&expected_content); }); - let mock_render_response = server.mock(|when, then| { + let result = carbone + .generate_report(template_name, template_data, json_data, None, None) + .await + .unwrap(); + + mock_render_response.assert(); + mock_get_report_response.assert(); + + assert_eq!(result, expected_content); + + Ok(()) + } + + #[tokio::test] + async fn test_generate_report_unupload_template() -> Result<(), CarboneError> + { + // Start a lightweight mock server. + let server = MockServer::start(); + + let helper = Helper::new(); + + let config = helper.create_config_for_mock_server(Some(&server))?; + let api_token = helper.create_api_token()?; + + let carbone = Carbone::new(&config, Some(&api_token))?; + + let report_data = fs::read_to_string("tests/data/report_data.json")?; + + let template_name = "template2.odt".to_string(); + let template_path = format!("tests/data/{}", template_name); + let template_data = fs::read(template_path.to_owned())?; + + let template_file = TemplateFile::new(template_path, Some(template_data.to_owned()))?; + let template_id = template_file.generate_id(None)?; + + let template_id_expected = TemplateId::new( + "0545253258577a632a99065f0572720225f5165cc43db9515e9cef0e17b40114".to_string(), + )?; + + let data = APIResponseData { + template_id: Some(template_id_expected.clone()), + render_id: None, + template_file_extension: None, + }; + + let body = APIResponse { + success: true, + data: Some(data), + error: None, + code: None, + }; + + let json_data = JsonData::new(report_data)?; + + let render_id_value = "MTAuMjAuMjEuNDAgICAgBY4OM11wQg11ekv6_R0n0wcmVwb3J0.pdf".to_string(); + let render_id = &RenderId::new(&render_id_value)?; + + let file_path = "tests/data/report.pdf"; + + let expected_content = fs::read(file_path)?; + + let mock_render_response_false = server.mock(|when, then| { when.method("POST") .path(format!("/render/{}", template_id.as_str())); + then.status(404) + .json_body(json!({ + "success": false, + "error": "template no found" + })); + }); + + let mock_upload_template = server.mock(|when, then| { + when.method("POST") + .path(format!("/template")); + then.status(200) + .header("content-type", "application/json") + .json_body_obj(&body); + }); + + let mock_render_response = server.mock(|when, then| { + when.method("POST") + .path(format!("/render/{}", template_id_expected.as_str())); then.status(200).json_body(json!({ "success": true, "data": { @@ -338,7 +429,6 @@ mod tests { .await .unwrap(); - mock_template_response.assert(); mock_render_response.assert(); mock_get_report_response.assert(); @@ -357,7 +447,7 @@ mod tests { let config = helper.create_config_for_mock_server(Some(&server))?; let api_token = helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let render_id_value = "844318fe97904fb0897d4b0a47fbe9bbd1ce5c9624ae694545cbc1877f581d86.pdf"; @@ -387,7 +477,7 @@ mod tests { let config = Config::new("http://bad_url".to_string(), 1, api_version)?; let api_token = helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let render_id_value = "844318fe97904fb0897d4b0a47fbe9bbd1ce5c9624ae694545cbc1877f581d86.pdf"; @@ -410,7 +500,7 @@ mod tests { let config = helper.create_config_for_mock_server(Some(&server))?; let api_token = helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let render_id_value = "unknown_render_id.pdf"; let render_id = &RenderId::new(render_id_value.to_string())?; @@ -466,7 +556,7 @@ mod tests { let api_token = helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let json_data = String::from( r#" @@ -481,9 +571,10 @@ mod tests { let json_data = JsonData::new(json_data)?; let result = carbone.render_data(template_id, json_data).await; - let expected_error = CarboneError::Error( - "Invalid or undefined TemplateId or RenderId in the URL".to_string(), - ); + let expected_error = CarboneError::HttpError { + status_code: StatusCode::BAD_REQUEST, + error_message: "Invalid or undefined TemplateId or RenderId in the URL".to_string(), + }; mock_server.assert(); assert!(result.is_err()); @@ -502,7 +593,7 @@ mod tests { let config = Config::new("http://bad_url".to_string(), 1, api_version)?; let api_token = helper.create_api_token()?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let json_data = String::from( r#" @@ -561,7 +652,7 @@ mod tests { let file_path = format!("tests/data/{}", file_name); let filte_content = fs::read(file_path)?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let template_id = carbone .upload_template(file_name, filte_content, None) .await @@ -613,7 +704,7 @@ mod tests { let file_path = format!("tests/data/{}", file_name); let filte_content = fs::read(file_path)?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let template_id = carbone .upload_template(file_name, filte_content, Some("salt1234")) .await @@ -657,7 +748,7 @@ mod tests { let file_path = format!("tests/data/{}", file_name); let filte_content = fs::read(file_path)?; - let carbone = Carbone::new(&config, &api_token)?; + let carbone = Carbone::new(&config, Some(&api_token))?; let result = carbone .upload_template(file_name, filte_content, None) .await; @@ -671,4 +762,31 @@ mod tests { Ok(()) } + + #[tokio::test] + async fn test_get_status() -> Result<(), CarboneError> { + let body : String = "{\"success\":true,\"code\":200,\"message\":\"OK\",\"version\":\"4.22.11\"}".to_string(); + let server = MockServer::start(); + + // Create a mock on the server. + let mock_server = server.mock(|when, then| { + when.method("GET") + .path(format!("/status")); + then.status(200).body(body.clone()); + }); + + let helper = Helper::new(); + let config = helper.create_config_for_mock_server(Some(&server))?; + + let api_token = helper.create_api_token()?; + + let carbone = Carbone::new(&config, Some(&api_token))?; + + let response = carbone.get_status().await.unwrap(); + + mock_server.assert(); + + assert_eq!(body, response); + Ok(()) + } } diff --git a/tests/data/template2.odt b/tests/data/template2.odt new file mode 100644 index 0000000..0ae1143 Binary files /dev/null and b/tests/data/template2.odt differ diff --git a/tests/data/~$mplate2.odt b/tests/data/~$mplate2.odt new file mode 100644 index 0000000..105dfb7 Binary files /dev/null and b/tests/data/~$mplate2.odt differ