pub struct CkanClient { /* private fields */ }Expand description
HTTP client for interacting with CKAN open data portals.
CKAN (Comprehensive Knowledge Archive Network) is an open-source data management system used by many government open data portals worldwide.
§Examples
use ceres_client::CkanClient;
let client = CkanClient::new("https://dati.gov.it")?;
let dataset_ids = client.list_package_ids().await?;
println!("Found {} datasets", dataset_ids.len());Implementations§
Source§impl CkanClient
impl CkanClient
Sourcepub fn new(base_url_str: &str) -> Result<Self, AppError>
pub fn new(base_url_str: &str) -> Result<Self, AppError>
Creates a new CKAN client for the specified portal.
§Arguments
base_url_str- The base URL of the CKAN portal (e.g., https://dati.gov.it)
§Returns
Returns a configured CkanClient instance.
§Errors
Returns AppError::Generic if the URL is invalid or malformed.
Returns AppError::ClientError if the HTTP client cannot be built.
Sourcepub async fn list_package_ids(&self) -> Result<Vec<String>, AppError>
pub async fn list_package_ids(&self) -> Result<Vec<String>, AppError>
Fetches the complete list of dataset IDs from the CKAN portal.
This method calls the CKAN package_list API endpoint, which returns
all dataset identifiers available in the portal.
§Returns
A vector of dataset ID strings.
§Errors
Returns AppError::ClientError if the HTTP request fails.
Returns AppError::Generic if the CKAN API returns an error.
§Performance Note
TODO(performance): Add pagination for large portals
Large portals can have 100k+ datasets. CKAN supports limit/offset params.
Consider: list_package_ids_paginated(limit: usize, offset: usize)
Or streaming: list_package_ids_stream() -> impl Stream<Item = ...>
Sourcepub async fn show_package(&self, id: &str) -> Result<CkanDataset, AppError>
pub async fn show_package(&self, id: &str) -> Result<CkanDataset, AppError>
Sourcepub async fn search_modified_since(
&self,
since: DateTime<Utc>,
) -> Result<Vec<CkanDataset>, AppError>
pub async fn search_modified_since( &self, since: DateTime<Utc>, ) -> Result<Vec<CkanDataset>, AppError>
Searches for datasets modified since a given timestamp.
Uses CKAN’s package_search API with a metadata_modified filter to fetch
only datasets that have been updated since the last sync. This enables
incremental harvesting with ~99% fewer API calls in steady state.
§Arguments
since- Only return datasets modified after this timestamp
§Returns
A vector of CkanDataset containing all datasets modified since the given time.
Unlike list_package_ids() + show_package(), this returns complete dataset
objects in a single paginated query.
§Errors
Returns AppError::ClientError if the HTTP request fails.
Returns AppError::Generic if the CKAN API returns an error or doesn’t support
the package_search endpoint (some older CKAN instances).
Sourcepub fn into_new_dataset(dataset: CkanDataset, portal_url: &str) -> NewDataset
pub fn into_new_dataset(dataset: CkanDataset, portal_url: &str) -> NewDataset
Converts a CKAN dataset into Ceres’ internal NewDataset model.
This helper method transforms CKAN-specific data structures into the format used by Ceres for database storage.
§Arguments
dataset- The CKAN dataset to convertportal_url- The base URL of the CKAN portal
§Returns
A NewDataset ready to be inserted into the database.
§Examples
use ceres_client::CkanClient;
use ceres_client::ckan::CkanDataset;
let ckan_dataset = CkanDataset {
id: "abc-123".to_string(),
name: "air-quality-data".to_string(),
title: "Air Quality Monitoring".to_string(),
notes: Some("Data from air quality sensors".to_string()),
extras: serde_json::Map::new(),
};
let new_dataset = CkanClient::into_new_dataset(
ckan_dataset,
"https://dati.gov.it"
);
assert_eq!(new_dataset.original_id, "abc-123");
assert_eq!(new_dataset.url, "https://dati.gov.it/dataset/air-quality-data");
assert_eq!(new_dataset.title, "Air Quality Monitoring");Trait Implementations§
Source§impl Clone for CkanClient
impl Clone for CkanClient
Source§fn clone(&self) -> CkanClient
fn clone(&self) -> CkanClient
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl PortalClient for CkanClient
impl PortalClient for CkanClient
Source§type PortalData = CkanDataset
type PortalData = CkanDataset
Source§async fn list_dataset_ids(&self) -> Result<Vec<String>, AppError>
async fn list_dataset_ids(&self) -> Result<Vec<String>, AppError>
Source§async fn get_dataset(&self, id: &str) -> Result<Self::PortalData, AppError>
async fn get_dataset(&self, id: &str) -> Result<Self::PortalData, AppError>
Source§fn into_new_dataset(data: Self::PortalData, portal_url: &str) -> NewDataset
fn into_new_dataset(data: Self::PortalData, portal_url: &str) -> NewDataset
Source§async fn search_modified_since(
&self,
since: DateTime<Utc>,
) -> Result<Vec<Self::PortalData>, AppError>
async fn search_modified_since( &self, since: DateTime<Utc>, ) -> Result<Vec<Self::PortalData>, AppError>
Auto Trait Implementations§
impl Freeze for CkanClient
impl !RefUnwindSafe for CkanClient
impl Send for CkanClient
impl Sync for CkanClient
impl Unpin for CkanClient
impl !UnwindSafe for CkanClient
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more