3 use serde::{Deserialize, Serialize};
4 use std::thread::sleep;
5 use std::time::Duration;
6 use std::{env, process};
9 // Gather and validate our credentials
10 let credentials = ApiCredentials::from_env();
11 match credentials.validate() {
15 eprintln!("Error found in API credentials: {}", issue)
21 // Get all our pages from the BookStack API and list them out
22 let pages = get_all_pages(credentials);
24 println!("ID={}; Name={}", page.id, page.name);
28 // Get all pages from the BookStack instance
29 fn get_all_pages(credentials: ApiCredentials) -> Vec<BookStackPage> {
30 // Create a new vector of pages to contain results
31 let mut results: Vec<BookStackPage> = Vec::new();
33 // Variables to track and perform API endpoint paging
38 // Make API requests while there's still data to retrieve
39 while offset < total {
40 // Delay to respect rate limits
42 sleep(Duration::from_millis(500));
45 let response: BookStackApiListingResult<BookStackPage> =
46 get_page_of_pages(&credentials, page_size, offset);
48 // Update our paging variables
49 total = response.total;
52 // Append the fetched data to our results
53 results.append(&mut response.data.clone());
59 // Get a single listing page of "pages" from the BookStack API
61 credentials: &ApiCredentials,
64 ) -> BookStackApiListingResult<BookStackPage> {
65 // Build our API endpoint to call
66 let endpoint = format!(
67 "{}/api/pages?count={}&offset={}",
68 credentials.url, page_size, offset
71 // Build the authorization header value to authenticate with the BookStack API
72 let auth_header_val = format!(
74 credentials.token_id, credentials.token_secret
77 // Create a client for making API requests
78 let api = reqwest::blocking::Client::new();
80 // Perform the API request
83 .header("Authorization", auth_header_val)
86 // Gather, parse and return our response data
87 let response: BookStackApiListingResult<BookStackPage> = api_result.unwrap().json().unwrap();
91 // A struct to represent a BookStack API listing response
92 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
93 struct BookStackApiListingResult<T> {
98 // A struct to represent a BookStack page provided via an API listing response
99 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
100 struct BookStackPage {
116 // A struct to hold BookStack API details
117 struct ApiCredentials {
120 token_secret: String,
123 impl ApiCredentials {
124 // Construct ApiCredentials by reading from possible environment variables
125 fn from_env() -> ApiCredentials {
126 let mut credentials = ApiCredentials {
127 url: String::from(""),
128 token_id: String::from(""),
129 token_secret: String::from(""),
132 credentials.url = match env::var("BS_URL") {
134 Err(_e) => credentials.url,
137 credentials.token_id = match env::var("BS_TOKEN_ID") {
139 Err(_e) => credentials.token_id,
142 credentials.token_secret = match env::var("BS_TOKEN_SECRET") {
144 Err(_e) => credentials.token_secret,
150 // Validate the current set API credentials
151 fn validate(&self) -> Result<(), Vec<String>> {
152 let mut issues: Vec<String> = Vec::new();
154 if self.url.is_empty() {
155 issues.push("API Base URL not set!".to_string());
158 if self.token_id.is_empty() {
159 issues.push("API Token ID not set!".to_string());
162 if self.token_secret.is_empty() {
163 issues.push("API Token Secret not set!".to_string());
166 if !issues.is_empty() {