Skip to content

Commit

Permalink
Use ureq http library (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkon authored Oct 28, 2024
1 parent ed25f61 commit eef0384
Show file tree
Hide file tree
Showing 22 changed files with 526 additions and 833 deletions.
306 changes: 90 additions & 216 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ resolver = "2"
members = [
"api",
"cli",
"spinner_macro",
"utilities",
]

Expand Down
2 changes: 1 addition & 1 deletion api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
attohttpc = { version = "0.28.0", features = ["basic-auth", "json"] }
json_value_merge = ">=2"
regex = "1.10.6"
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
time = { version = "0.3.17", features = ["local-offset", "macros", "serde", "serde-human-readable"] }
ureq = {version = "2.10.1", features = ["json"] }
url = "2.3.1"

[dev-dependencies]
Expand Down
35 changes: 19 additions & 16 deletions api/src/account_info.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use serde::Deserialize;

use super::{Error, Result, Session};
use super::{Error, Result};

pub fn fetch(session: &Session) -> Result<AccountInfo> {
let list: Vec<AccountInfo> = session.get_json("auth_ext/get_account_info")?;
list.into_iter().next().ok_or(Error::UnexpectedPayload)
impl super::Client {
pub fn account_info(&self) -> Result<AccountInfo> {
let list: Vec<AccountInfo> = self.get("auth_ext/get_account_info/").call()?.into_json()?;
list.into_iter().next().ok_or(Error::UnexpectedPayload)
}
}

#[derive(Debug, Deserialize)]
Expand All @@ -15,8 +17,6 @@ pub struct AccountInfo {

#[derive(Debug, Deserialize)]
pub struct AccountInfoRole {
#[serde(rename = "_id")]
pub id: Oid,
pub company: Oid,
}

Expand All @@ -26,25 +26,28 @@ pub struct Oid {
pub id: String,
}

#[derive(Clone, Debug, Deserialize)]
pub struct TimeTrackPolicy {
#[serde(rename = "breakPolicy")]
pub break_policy_id: String,
}

#[cfg(test)]
mod tests {
use super::*;
use crate::Client;

use utilities::mocking;

fn setup() -> (mocking::FakeRippling, Client) {
let server = mocking::FakeRippling::new();
let client = Client::new("access-token".to_owned())
.with_root(url::Url::parse(&server.url()).unwrap())
.with_company_and_role("some-company-id".to_owned(), "some-role-id".to_owned());
(server, client)
}

#[test]
fn it_can_fetch_account_info() {
let mut server = mocking::FakeRippling::new();
let (mut server, client) = setup();
let _m = server
.with_fixture("GET", "/auth_ext/get_account_info", "account_info")
.with_fixture("GET", "/auth_ext/get_account_info/", "account_info")
.create();

let info = fetch(&crate::session::test_session(&server)).unwrap();
let info = client.account_info().unwrap();
assert_eq!(info.role.company.id, "some-company-id");
assert_eq!(info.id, "my-role-id");
}
Expand Down
42 changes: 29 additions & 13 deletions api/src/break_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@ use std::collections::HashMap;

use serde::Deserialize;

use super::session::Session;
use super::{Error, Result};

pub fn active_policy(session: &Session) -> Result<ActivePolicy> {
let mut map: HashMap<String, ActivePolicy> =
session.get_json("time_tracking/api/time_entry_policies/get_active_policy")?;
map.remove(session.role().unwrap()).ok_or(Error::UnexpectedPayload)
}
impl super::Client {
pub fn active_break_policy(&self) -> Result<ActivePolicy> {
let mut map: HashMap<String, ActivePolicy> = self
.get("time_tracking/api/time_entry_policies/get_active_policy")
.call()?
.into_json()?;
map.remove(self.role().as_ref().unwrap())
.ok_or(Error::UnexpectedPayload)
}

pub fn fetch(session: &Session, id: &str) -> Result<BreakPolicy> {
session.get_json(&format!("time_tracking/api/time_entry_break_policies/{id}"))
pub fn break_policy(&self, id: &str) -> Result<BreakPolicy> {
let break_policy: BreakPolicy = self
.get(&format!("time_tracking/api/time_entry_break_policies/{id}"))
.call()?
.into_json()?;
Ok(break_policy)
}
}

#[derive(Clone, Debug, Deserialize)]
Expand Down Expand Up @@ -89,25 +97,33 @@ impl BreakPolicy {
mod tests {
use utilities::mocking;

use super::*;
use crate::Client;

fn setup() -> (mocking::FakeRippling, Client) {
let server = mocking::FakeRippling::new();
let client = Client::new("access-token".to_owned())
.with_root(url::Url::parse(&server.url()).unwrap())
.with_company_and_role("some-company-id".to_owned(), "some-role-id".to_owned());
(server, client)
}

#[test]
fn it_can_fetch_a_break_policy() {
let mut server = mocking::FakeRippling::new();
let (mut server, client) = setup();
let _m = server.mock_break_policy("policy-id");

let policy = fetch(&crate::session::test_session(&server), "policy-id").unwrap();
let policy = client.break_policy("policy-id").unwrap();
let mybreak = policy.manual_break_type().unwrap();
assert_eq!(mybreak.id, "break-id-1");
assert_eq!(mybreak.description, "Lunch Break - Manually clock in/out");
}

#[test]
fn it_can_fetch_active_policy() {
let mut server = mocking::FakeRippling::new();
let (mut server, client) = setup();
let _m = server.mock_active_policy();

let policy = active_policy(&crate::session::test_session(&server)).unwrap();
let policy = client.active_break_policy().unwrap();
assert_eq!(policy.break_policy, "some-break-policy-id");
assert_eq!(policy.time_policy, "some-policy-id");
assert_eq!(policy.role_overrides.role_properties.default_timezone, "Europe/Berlin");
Expand Down
84 changes: 84 additions & 0 deletions api/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::time::Duration;

use crate::default_root;

#[derive(Debug)]
pub struct Client {
company: Option<String>,
role: Option<String>,
root: url::Url,
token: String,
}

/// Getters & instantiation
impl Client {
pub fn new(token: String) -> Self {
Self { company: None, role: None, root: default_root(), token }
}

pub fn role(&self) -> &Option<String> {
&self.role
}

pub fn company(&self) -> &Option<String> {
&self.company
}

pub fn with_company_and_role(&self, company: String, role: String) -> Self {
Self {
company: Some(company),
role: Some(role),
root: self.root.clone(),
token: self.token.clone(),
}
}

/// Used for testing mainly
#[allow(dead_code)]
pub(crate) fn with_root(&self, root: url::Url) -> Self {
Self {
company: self.company.clone(),
role: self.role.clone(),
root,
token: self.token.clone(),
}
}
}

/// Methods for internal use
impl Client {
fn agent(&self) -> ureq::Agent {
ureq::AgentBuilder::new()
.timeout_read(Duration::from_secs(5))
.timeout_write(Duration::from_secs(5))
.build()
}

pub(super) fn get(&self, path: &str) -> ureq::Request {
let mut request = self
.agent()
.get(self.root.join(path).unwrap().as_str())
.set("Authorization", &format!("Bearer {}", self.token));
if let Some(company) = &self.company {
request = request.set("Company", company);
}
if let Some(role) = &self.role {
request = request.set("Role", role);
}
request
}

pub(super) fn post(&self, path: &str) -> ureq::Request {
let mut request = self
.agent()
.post(self.root.join(path).unwrap().as_str())
.set("Authorization", &format!("Bearer {}", self.token));
if let Some(company) = &self.company {
request = request.set("Company", company);
}
if let Some(role) = &self.role {
request = request.set("Role", role);
}
request
}
}
Loading

0 comments on commit eef0384

Please sign in to comment.