Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Adding a Fetcher

This guide walks through adding a new data fetcher to redshank-fetchers.

1. Add the module

Create redshank-fetchers/src/fetchers/my_source.rs.

2. Implement the struct

use crate::{client::FetcherClient, domain::FetcherError};
use serde::{Deserialize, Serialize};

/// Fetches data from My Source.
pub struct MySourceFetcher {
    client: FetcherClient,
}

impl MySourceFetcher {
    /// Creates a new fetcher with the given HTTP client.
    #[must_use]
    pub const fn new(client: FetcherClient) -> Self {
        Self { client }
    }

    /// Fetch records matching `query`.
    ///
    /// # Errors
    ///
    /// Returns [`FetcherError`] on HTTP or parse failure.
    pub async fn fetch(&self, query: &str) -> Result<Vec<MySourceRecord>, FetcherError> {
        // ...
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MySourceRecord {
    pub id: String,
    pub name: String,
}

3. Write a test first

#[cfg(test)]
mod tests {
    use super::*;
    use wiremock::{MockServer, Mock, ResponseTemplate};
    use wiremock::matchers::{method, path};

    #[tokio::test]
    async fn test_fetch_returns_records() {
        let server = MockServer::start().await;
        Mock::given(method("GET"))
            .and(path("/api/search"))
            .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
                {"id": "1", "name": "Test Entity"}
            ])))
            .mount(&server)
            .await;

        let client = FetcherClient::with_base_url(server.uri());
        let fetcher = MySourceFetcher::new(client);
        let results = fetcher.fetch("Test Entity").await.unwrap();
        assert_eq!(results.len(), 1);
        assert_eq!(results[0].name, "Test Entity");
    }
}

4. Register the module

In redshank-fetchers/src/fetchers/mod.rs:

pub mod my_source;

5. Add a CLI subcommand

In redshank-cli/src/main.rs, add a variant to the FetchCommand enum and wire it to MySourceFetcher::fetch.

6. Document it

Add an entry to Data Sources Overview and create a page in docs/src/data-sources/.