Skip to content

Commit

Permalink
Chore: Cleaning some code in Client and moving query string serializer (
Browse files Browse the repository at this point in the history
#19)

* Update: Cleaning some code

This PR goes through and moves the url param serialization into the
client code to remove duplication in API code.

We also cleaned up a lot of unneeded `Ok(<something>)`s in the codebase.

* fixing linting errror
  • Loading branch information
joshfinnie authored Nov 21, 2023
1 parent 681c6e7 commit 2d1a4d1
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 123 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ hyper-tls = "0.5.0"
reqwest = { version = "0.11.18", features = ["json", "blocking"] }
serde = { version = "1.0.180", features = ["derive"] }
serde_json = "1.0.104"
serde_qs = "0.12"
serde_urlencoded = "0.7.1"
8 changes: 2 additions & 6 deletions src/api/autocomplete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@ impl Autocomplete {
params: AutocompleteParams,
) -> Result<AutocompleteResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self
.client
.get::<AutocompleteResponse>(AUTOCOMPLETE_PATH, &qs)?;

Ok(r)
self.client
.get::<AutocompleteResponse, AutocompleteParams>(AUTOCOMPLETE_PATH, params)
}
}

Expand Down
18 changes: 6 additions & 12 deletions src/api/company.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,25 @@ impl Company {
/// docs: https://docs.peopledatalabs.com/docs/company-enrichment-api
pub fn enrich(&self, params: EnrichCompanyParams) -> Result<CompanyResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self.client.get::<CompanyResponse>(ENRICH_PATH, &qs)?;

Ok(r)
self.client
.get::<CompanyResponse, EnrichCompanyParams>(ENRICH_PATH, params)
}

/// Search gives you access to every record in our full Company dataset,
/// which you can filter and segment using a search query.
/// docs: https://docs.peopledatalabs.com/docs/company-search-api
pub fn search(&self, params: SearchParams) -> Result<SearchCompanyResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self.client.get::<SearchCompanyResponse>(SEARCH_PATH, &qs)?;

Ok(r)
self.client
.get::<SearchCompanyResponse, SearchParams>(SEARCH_PATH, params)
}

/// Clean your company data, so you can better query our person data
/// docs: https://docs.peopledatalabs.com/docs/cleaner-apis-reference
pub fn clean(&self, params: CleanCompanyParams) -> Result<CleanCompanyResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self.client.get::<CleanCompanyResponse>(CLEAN_PATH, &qs)?;

Ok(r)
self.client
.get::<CleanCompanyResponse, CleanCompanyParams>(CLEAN_PATH, params)
}
}

Expand Down
5 changes: 1 addition & 4 deletions src/api/ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ pub struct IP {
impl IP {
pub fn get(&self, params: IPParams) -> Result<IPResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self.client.get::<IPResponse>(PATH, &qs)?;

Ok(r)
self.client.get::<IPResponse, IPParams>(PATH, params)
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/api/jobtitle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ pub struct JobTitle {
impl JobTitle {
pub fn get(&self, params: JobTitleParams) -> Result<JobTitleResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self.client.get::<JobTitleResponse>(PATH, &qs)?;

Ok(r)
self.client
.get::<JobTitleResponse, JobTitleParams>(PATH, params)
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/api/location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ pub struct Location {
impl Location {
pub fn clean(&self, params: CleanLocationParams) -> Result<CleanLocationResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self.client.get::<CleanLocationResponse>(PATH, &qs)?;

Ok(r)
self.client
.get::<CleanLocationResponse, CleanLocationParams>(PATH, params)
}
}

Expand Down
53 changes: 18 additions & 35 deletions src/api/person.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,72 +25,55 @@ pub struct Person {
impl Person {
pub fn enrich(&self, params: EnrichPersonParams) -> Result<EnrichPersonResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
dbg!(&qs);
let r = self
.client
.get::<EnrichPersonResponse>(PERSON_ENRICH_PATH, &qs)?;

Ok(r)
self.client
.get::<EnrichPersonResponse, EnrichPersonParams>(PERSON_ENRICH_PATH, params)
}

pub fn bulk_enrich(
&self,
params: BulkEnrichPersonParams,
) -> Result<Vec<BulkEnrichPersonResponse>, PDLError> {
let json = serde_json::to_value(params).map_err(|_| PDLError::ValidationError)?;
let r = self
.client
.post::<Vec<BulkEnrichPersonResponse>>(PERSON_BULK_ENRICH_PATH, json)?;

Ok(r)
self.client
.post::<Vec<BulkEnrichPersonResponse>, BulkEnrichPersonParams>(
PERSON_BULK_ENRICH_PATH,
params,
)
}

pub fn identify(
&self,
params: IdentifyPersonParams,
) -> Result<IdentifyPersonResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self
.client
.get::<IdentifyPersonResponse>(PERSON_IDENTIFY_PATH, &qs)?;

Ok(r)
self.client
.get::<IdentifyPersonResponse, IdentifyPersonParams>(PERSON_IDENTIFY_PATH, params)
}

pub fn search(&self, params: SearchParams) -> Result<SearchPersonResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self
.client
.get::<SearchPersonResponse>(PERSON_SEARCH_PATH, &qs)?;

Ok(r)
self.client
.get::<SearchPersonResponse, SearchParams>(PERSON_SEARCH_PATH, params)
}

pub fn retrieve(
&self,
params: RetrievePersonParams,
) -> Result<RetrievePersonResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let url = PERSON_RETRIEVE_PATH.to_string() + &params.person_id;
let r = self.client.get::<RetrievePersonResponse>(&url, &qs)?;

Ok(r)
self.client
.get::<RetrievePersonResponse, RetrievePersonParams>(&url, params)
}

pub fn bulk_retrieve(
&self,
params: BulkRetrievePersonParams,
) -> Result<Vec<BulkRetrievePersonResponse>, PDLError> {
let json = serde_json::to_value(params).map_err(|_| PDLError::ValidationError)?;
let r = self
.client
.post::<Vec<BulkRetrievePersonResponse>>(PERSON_BULK_RETRIEVE_PATH, json)?;

Ok(r)
self.client
.post::<Vec<BulkRetrievePersonResponse>, BulkRetrievePersonParams>(
PERSON_BULK_RETRIEVE_PATH,
params,
)
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/api/school.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ pub struct School {
impl School {
pub fn clean(&self, params: CleanSchoolParams) -> Result<CleanSchoolResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self.client.get::<CleanSchoolResponse>(PATH, &qs)?;

Ok(r)
self.client
.get::<CleanSchoolResponse, CleanSchoolParams>(PATH, params)
}
}

Expand Down
5 changes: 1 addition & 4 deletions src/api/skills.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ pub struct Skill {
impl Skill {
pub fn get(&self, params: SkillParams) -> Result<SkillResponse, PDLError> {
params.validate()?;
let qs = serde_qs::to_string(&params).map_err(|_| PDLError::ValidationError)?;
let r = self.client.get::<SkillResponse>(PATH, &qs)?;

Ok(r)
self.client.get::<SkillResponse, SkillParams>(PATH, params)
}
}

Expand Down
96 changes: 47 additions & 49 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use reqwest::blocking as rq;
use reqwest::header;
use reqwest::StatusCode;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::time::Duration;
Expand All @@ -14,6 +18,7 @@ static DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
pub enum PDLError {
NetworkError(reqwest::Error),
HTTPError(StatusCode),
SerializationError,
ValidationError,
}

Expand All @@ -22,7 +27,8 @@ impl Display for PDLError {
match *self {
PDLError::NetworkError(ref e) => e.fmt(f),
PDLError::HTTPError(ref s) => write!(f, "Invalid HTTP status code: {}", s),
PDLError::ValidationError => f.write_str("Invalid Parameters"),
PDLError::SerializationError => f.write_str("Unable to serialize."),
PDLError::ValidationError => f.write_str("Unable to validate."),
}
}
}
Expand Down Expand Up @@ -51,49 +57,48 @@ pub struct PDLClient {
api_key: String,
base_url: String,
api_version: String,
client: reqwest::blocking::Client,
client: rq::Client,
}

impl PDLClient {
/// Make a new People Data Labs client with users API Key and API Version.
pub fn new(key: &str) -> PDLClient {
// Sets the default PDLClient
use reqwest::blocking as rq;
/// Builds client based off of API_KEY and Optional Timeout
fn build_client(api_key: &str, timeout: Option<Duration>) -> rq::Client {
let mut headers = header::HeaderMap::new();
let api_key = header::HeaderValue::from_str(api_key).unwrap();
headers.insert("X-Api-Key", api_key);

let duration = timeout.unwrap_or(DEFAULT_TIMEOUT);

let builder = rq::ClientBuilder::new();
let client = builder
.user_agent(APP_USER_AGENT)
.timeout(DEFAULT_TIMEOUT)
.build()
.unwrap();
rq::Client::builder()
.default_headers(headers)
.user_agent(APP_USER_AGENT)
.timeout(duration)
.build()
.expect("Failed to build reqwest client")
}

impl PDLClient {
/// Make a new People Data Labs client with users API Key and API Version.
pub fn new(api_key: &str) -> Self {
let client = build_client(api_key, None);
PDLClient {
api_key: key.to_string(),
api_key: api_key.to_string(),
base_url: DEFAULT_API_URL.to_string(),
api_version: DEFAULT_API_VERSION.to_string(),
client,
}
}

/// Adds the ability to update the version from the default through chaining.
pub fn version(mut self, version: &str) -> PDLClient {
pub fn version(mut self, version: &str) -> Self {
self.api_version = version.to_string();
self
}

/// Adds the ability to update the default timeout or access sandbox mode
/// through chaining.
pub fn options(mut self, options: PDLCLientOptions) -> PDLClient {
pub fn options(mut self, options: PDLCLientOptions) -> Self {
if options.timeout != DEFAULT_TIMEOUT {
use reqwest::blocking as rq;

let builder = rq::ClientBuilder::new();
let client = builder
.user_agent(APP_USER_AGENT)
.timeout(options.timeout)
.build()
.unwrap();
self.client = client
self.client = build_client(&self.api_key, Some(options.timeout))
}

if options.sandbox {
Expand All @@ -115,64 +120,57 @@ impl PDLClient {

/// Sends a GET method through the PeopleDataLabs API. It takes an endpoint &str and params &str.
/// It returns a generic response or PDLError.
pub fn get<T>(&self, endpoint: &str, params: &str) -> Result<T, PDLError>
pub fn get<T, P>(&self, endpoint: &str, params: P) -> Result<T, PDLError>
where
T: serde::de::DeserializeOwned,
T: DeserializeOwned,
P: Serialize,
{
let query_params =
serde_urlencoded::to_string(params).map_err(|_| PDLError::SerializationError)?;

let uri = format!(
"{}{}{}?api_key={}&{}",
self.base_url, self.api_version, endpoint, self.api_key, params
"{}{}{}?{}",
self.base_url, self.api_version, endpoint, query_params
);

dbg!(&uri);

let resp = self
.client
.get(uri)
.send()
.map_err(PDLError::NetworkError)
.unwrap();
.map_err(PDLError::NetworkError)?;

match resp.status() {
StatusCode::OK => {}
StatusCode::NOT_FOUND => return Err(PDLError::HTTPError(StatusCode::NOT_FOUND)),
other => return Err(PDLError::HTTPError(other)),
}

let r = resp.json::<T>().unwrap();

Ok(r)
resp.json::<T>().map_err(PDLError::NetworkError)
}

/// Sends a POST method through the PeopleDataLabs API. It takes an endpoint &str and params &str.
/// It returns a generic response or PDLError.
pub fn post<T>(&self, endpoint: &str, json: serde_json::Value) -> Result<T, PDLError>
pub fn post<T, P>(&self, endpoint: &str, params: P) -> Result<T, PDLError>
where
T: serde::de::DeserializeOwned + std::fmt::Debug,
T: DeserializeOwned,
P: Serialize,
{
let uri = format!(
"{}{}{}?api_key={}",
self.base_url, self.api_version, endpoint, self.api_key
);
let json = serde_json::to_value(params).map_err(|_| PDLError::ValidationError)?;

dbg!(&uri);
dbg!(&json);
let uri = format!("{}{}{}", self.base_url, self.api_version, endpoint);

let resp = self
.client
.post(uri)
.json(&json)
.send()
.map_err(PDLError::NetworkError)
.unwrap();
.map_err(PDLError::NetworkError)?;

match resp.status() {
StatusCode::OK => {}
other => return Err(PDLError::HTTPError(other)),
}

let r = resp.json::<T>().unwrap();

Ok(r)
resp.json::<T>().map_err(PDLError::NetworkError)
}
}

0 comments on commit 2d1a4d1

Please sign in to comment.