GuilhermeStracini / apiclient-boilerplate-rs

:bulb: :building_construction: A boilerplate API client template for Rust API clients wrappers
https://guilhermestracini.github.io/apiclient-boilerplate-rs/
MIT License
1 stars 1 forks source link

Enhance API client with CRUD operations and error handling #53

Closed guibranco closed 2 weeks ago

guibranco commented 2 weeks ago

Description


Changes walkthrough 📝

Relevant files
Enhancement
integration_tests.rs
Update integration tests for ApiClient usage                         

src/bin/integration_tests.rs
  • Updated the import statement for ApiClient.
  • Adjusted the instantiation of ApiClient to use the correct base URL.
  • +2/-1     
    post.rs
    Refactor post.rs to use ApiClient correctly                           

    src/bin/post.rs
  • Changed the import path for ApiClient.
  • Updated the instantiation of ApiClient with a base URL variable.
  • +4/-3     
    client.rs
    Implement ApiClient with CRUD methods                                       

    src/client.rs
  • Created a new ApiClient struct with methods for API interactions.
  • Implemented methods for CRUD operations on posts.
  • +44/-0   
    error.rs
    Add custom error handling for API operations                         

    src/error.rs
  • Introduced a custom error type ApiError for handling API errors.
  • Implemented conversion from ReqwestError and serde_json::Error.
  • +31/-0   
    lib.rs
    Refactor lib.rs to modular structure                                         

    src/lib.rs
  • Modularized the code by creating separate modules for client, error,
    and models.
  • Removed old ApiClient implementation.
  • +3/-31   
    models.rs
    Define Post model for API interactions                                     

    src/models.rs - Created a new `Post` struct for serialization and deserialization.
    +8/-0     

    Summary by Sourcery

    Refactor the API client into a modular structure with separate modules for client, error handling, and models. Enhance the API client by adding new methods for creating, updating, and deleting posts, and implement a custom error handling mechanism.

    New Features:

    Enhancements:

    Summary by CodeRabbit

    semanticdiff-com[bot] commented 2 weeks ago

    Review changes with SemanticDiff.

    Analyzed 3 of 6 files.

    Overall, the semantic diff is 10% smaller than the GitHub diff.

    Filename Status
    :grey_question: src/client.rs Unsupported file format
    :grey_question: src/error.rs Unsupported file format
    :heavy_check_mark: src/lib.rs 4.34% smaller
    :grey_question: src/models.rs Unsupported file format
    :heavy_check_mark: src/bin/integration_tests.rs 31.84% smaller
    :heavy_check_mark: src/bin/post.rs 41.75% smaller
    korbit-ai[bot] commented 2 weeks ago

    My review is in progress :book: - I will have feedback for you in a few minutes!

    senior-dev-bot[bot] commented 2 weeks ago

    Hi there! :wave: Thanks for opening a PR. It looks like you've already reached the 5 review limit on our Basic Plan for the week. If you still want a review, feel free to upgrade your subscription in the Web App and then reopen the PR

    instapr[bot] commented 2 weeks ago
    **Feedback:**
    
    - The creation of `error.rs`, `models.rs`, and `client.rs` looks good.
    - Nice structuring and separation of concerns in the codebase.
    - Good job on refactoring and moving the code around efficiently.
    pr-code-reviewer[bot] commented 2 weeks ago

    :wave: Hi there!

    Everything looks good!

    Automatically generated with the help of gpt-3.5-turbo. Feedback? Please don't hesitate to drop me an email at webber@takken.io.

    sourcery-ai[bot] commented 2 weeks ago

    Reviewer's Guide by Sourcery

    This pull request refactors the API client implementation, introducing a modular structure, error handling, and additional CRUD operations. The changes improve code organization, enhance error management, and expand the functionality of the API client.

    File-Level Changes

    Change Details Files
    Restructure the project into multiple modules
    • Move ApiClient implementation to a new client.rs file
    • Create a new error.rs file for custom error handling
    • Add a new models.rs file for data structures
    • Update lib.rs to use the new module structure
    src/lib.rs
    src/client.rs
    src/error.rs
    src/models.rs
    Enhance ApiClient implementation with additional CRUD operations
    • Add create_post method for creating new posts
    • Add update_post method for updating existing posts
    • Add delete_post method for deleting posts
    • Modify get_post method to use the new error handling
    src/client.rs
    Implement custom error handling
    • Create ApiError enum to represent different error types
    • Implement From trait for converting between error types
    • Update ApiClient methods to use the new ApiError type
    src/error.rs
    src/client.rs
    Update existing code to use the new module structure
    • Modify post.rs to import ApiClient from the new module
    • Update integration_tests.rs to use the new ApiClient import path
    src/bin/post.rs
    src/bin/integration_tests.rs

    Tips - Trigger a new Sourcery review by commenting `@sourcery-ai review` on the pull request. - Continue your discussion with Sourcery by replying directly to review comments. - You can change your review settings at any time by accessing your [dashboard](https://app.sourcery.ai): - Enable or disable the Sourcery-generated pull request summary or reviewer's guide; - Change the review language; - You can always [contact us](mailto:support@sourcery.ai) if you have any questions or feedback.
    codara-ai-code-review[bot] commented 2 weeks ago

    Potential issues, bugs, and flaws that can introduce unwanted behavior:

    1. src/bin/integration_tests.rs:

      • Adding a line to import ApiClient from apiclient_rust::client can potentially introduce issues if the import path is incorrect.
    2. src/bin/post.rs:

      • There is inconsistent indentation before the match keyword in the main function.
    3. src/client.rs:

      • The ApiClient struct contains the base URL as a field, which may cause issues if the base URL needs to be changed dynamically for each request.
    4. src/error.rs:

      • The error handling is generic and can lose specific context regarding the type of error that occurred.

    Code suggestions and improvements for better exception handling, logic, standardization, and consistency:

    1. src/bin/integration_tests.rs:

      • Import the ApiClient using use apiclient_rust::client::ApiClient; to ensure code clarity and unambiguous imports.
    2. src/bin/post.rs:

      • Ensure consistent indentation by aligning the match keyword with the let api_client statement to enhance code readability.
    3. src/client.rs:

      • Consider passing the base URL as a parameter in the request methods to allow flexibility in changing URLs for different requests without modifying the ApiClient instance.
    4. src/error.rs:

      • Provide more specific error messages by incorporating additional context when handling errors inside the ApiError enum to improve error traceability and debugging.
    coderabbitai[bot] commented 2 weeks ago

    Walkthrough

    The pull request introduces a restructuring of the codebase, specifically focusing on the ApiClient for API interactions. Key changes include the addition of a new ApiClient struct, an ApiError enum for error handling, and a Post struct for data representation. The code also refines the instantiation of ApiClient in test and main files, enhancing readability. The overall architecture is modularized by creating separate modules for client, error, and models, which improves organization and maintainability.

    Changes

    Files Change Summary
    src/bin/integration_tests.rs, src/bin/post.rs Modified ApiClient instantiation and updated import paths for clarity and organization.
    src/client.rs Introduced ApiClient struct with methods for CRUD operations on posts.
    src/error.rs Added ApiError enum for structured error handling during API interactions.
    src/lib.rs Restructured to declare modules for client, error, and models, removing previous implementations.
    src/models.rs Added Post struct for representing blog posts with serialization capabilities.

    Possibly related PRs

    Suggested labels

    size/S, korbit-code-analysis

    Poem

    🐰 In the code where bunnies play,
    New clients hop and errors sway.
    With posts to share and errors neat,
    Our code is now a tasty treat!
    So let us leap with joy and cheer,
    For changes bright and oh so clear! 🌟


    Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

    Share - [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) - [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) - [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) - [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)
    Tips ### Chat There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai): - Review comments: Directly reply to a review comment made by CodeRabbit. Example: - `I pushed a fix in commit .` - `Generate unit testing code for this file.` - `Open a follow-up GitHub issue for this discussion.` - Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples: - `@coderabbitai generate unit testing code for this file.` - `@coderabbitai modularize this function.` - PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples: - `@coderabbitai generate interesting stats about this repository and render them as a table.` - `@coderabbitai show all the console.log statements in this repository.` - `@coderabbitai read src/utils.ts and generate unit testing code.` - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.` - `@coderabbitai help me debug CodeRabbit configuration file.` Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. ### CodeRabbit Commands (Invoked using PR comments) - `@coderabbitai pause` to pause the reviews on a PR. - `@coderabbitai resume` to resume the paused reviews. - `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository. - `@coderabbitai full review` to do a full review from scratch and review all the files again. - `@coderabbitai summary` to regenerate the summary of the PR. - `@coderabbitai resolve` resolve all the CodeRabbit review comments. - `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository. - `@coderabbitai help` to get help. ### Other keywords and placeholders - Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed. - Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description. - Add `@coderabbitai` anywhere in the PR title to generate the title automatically. ### CodeRabbit Configuration File (`.coderabbit.yaml`) - You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository. - Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information. - If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json` ### Documentation and Community - Visit our [Documentation](https://coderabbit.ai/docs) for detailed information on how to use CodeRabbit. - Join our [Discord Community](https://discord.com/invite/GsXnASn26c) to get help, request features, and share feedback. - Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.
    penify-dev[bot] commented 2 weeks ago

    PR Review 🔍

    ⏱️ Estimated effort to review [1-5] 4, because the PR introduces a new structure for API client management, including CRUD operations and error handling. It requires a thorough understanding of the new code and its integration with existing components.
    🧪 Relevant tests Yes
    ⚡ Possible issues Potential Bug: The error handling in `ApiClient` may not cover all edge cases, such as network failures or invalid responses from the API.
    🔒 Security concerns No
    gooroo-dev[bot] commented 2 weeks ago

    Please double check the following review of the pull request:

    Issues counts

    🐞Mistake 🤪Typo 🚨Security 🚀Performance 💪Best Practices 📖Readability ❓Others
    0 0 0 0 1 1 0

    Changes in the diff

    Identified Issues

    ID Type Details Severity Confidence
    1 💪Best Practices client.rs: Missing documentation comments for ApiClient methods. 🟡Low 🟠Medium
    2 📖Readability post.rs: Misaligned indentation in main function. 🟡Low 🟠Medium

    Issue 1: Missing documentation comments for ApiClient methods

    Explanation

    In client.rs, the methods of ApiClient lack documentation comments. Adding comments will improve code maintainability and readability.

    Code to Address the Issue

    impl ApiClient {
        /// Creates a new instance of `ApiClient`.
        ///
        /// # Arguments
        ///
        /// * `base_url` - A string slice that holds the base URL for the API.
        pub fn new(base_url: &str) -> Self {
            ApiClient {
                client: Client::new(),
                base_url: base_url.to_string(),
            }
        }
    
        /// Fetches a post by its ID.
        ///
        /// # Arguments
        ///
        /// * `post_id` - The ID of the post to fetch.
        ///
        /// # Returns
        ///
        /// * `Result<Post, ApiError>` - The fetched post or an error.
        pub async fn get_post(&self, post_id: u32) -> Result<Post, ApiError> {
            let url = format!("{}/posts/{}", self.base_url, post_id);
            let response = self.client.get(&url).send().await?.json::<Post>().await?;
            Ok(response)
        }
    
        /// Creates a new post.
        ///
        /// # Arguments
        ///
        /// * `new_post` - A reference to the post to create.
        ///
        /// # Returns
        ///
        /// * `Result<Post, ApiError>` - The created post or an error.
        pub async fn create_post(&self, new_post: &Post) -> Result<Post, ApiError> {
            let url = format!("{}/posts", self.base_url);
            let response = self.client.post(&url).json(new_post).send().await?.json::<Post>().await?;
            Ok(response)
        }
    
        /// Updates an existing post by its ID.
        ///
        /// # Arguments
        ///
        /// * `post_id` - The ID of the post to update.
        /// * `updated_post` - A reference to the updated post.
        ///
        /// # Returns
        ///
        /// * `Result<Post, ApiError>` - The updated post or an error.
        pub async fn update_post(&self, post_id: u32, updated_post: &Post) -> Result<Post, ApiError> {
            let url = format!("{}/posts/{}", self.base_url, post_id);
            let response = self.client.put(&url).json(updated_post).send().await?.json::<Post>().await?;
            Ok(response)
        }
    
        /// Deletes a post by its ID.
        ///
        /// # Arguments
        ///
        /// * `post_id` - The ID of the post to delete.
        ///
        /// # Returns
        ///
        /// * `Result<(), ApiError>` - An empty result or an error.
        pub async fn delete_post(&self, post_id: u32) -> Result<(), ApiError> {
            let url = format!("{}/posts/{}", self.base_url, post_id);
            self.client.delete(&url).send().await?;
            Ok(())
        }
    }

    Explanation of the Fix

    The fix involves adding documentation comments to each method in the ApiClient implementation. These comments describe the purpose, arguments, and return values of each method, improving code maintainability and readability.

    Issue 2: Misaligned indentation in main function

    Explanation

    In post.rs, the indentation of the match statement in the main function is misaligned, which affects readability.

    Code to Address the Issue

    #[tokio::main]
    async fn main() -> Result<(), reqwest::Error> {
        let base_url = "https://jsonplaceholder.typicode.com";
        let api_client = ApiClient::new(base_url);
    
        match api_client.get_post(1).await {
            Ok(post) => {
                println!("Post: {:?}", post);
            }
            Err(err) => {
                eprintln!("Error: {:?}", err);
            }
        }
    
        Ok(())
    }

    Explanation of the Fix

    The fix involves correcting the indentation of the match statement in the main function to ensure consistent formatting and improve readability.

    Missing Tests for the Incoming Changes

    New Tests

    #[cfg(test)]
    mod tests {
        use super::*;
        use wiremock::matchers::{method, path};
        use wiremock::{Mock, MockServer, ResponseTemplate};
    
        #[tokio::test]
        async fn test_get_post() {
            let mock_server = MockServer::start().await;
            let post = Post {
                title: "Test Title".to_string(),
                body: "Test Body".to_string(),
                userId: 1,
            };
    
            Mock::given(method("GET"))
                .and(path("/posts/1"))
                .respond_with(ResponseTemplate::new(200).set_body_json(&post))
                .mount(&mock_server)
                .await;
    
            let api_client = ApiClient::new(&mock_server.uri());
            let fetched_post = api_client.get_post(1).await.unwrap();
    
            assert_eq!(fetched_post.title, post.title);
            assert_eq!(fetched_post.body, post.body);
            assert_eq!(fetched_post.userId, post.userId);
        }
    
        #[tokio::test]
        async fn test_create_post() {
            let mock_server = MockServer::start().await;
            let post = Post {
                title: "Test Title".to_string(),
                body: "Test Body".to_string(),
                userId: 1,
            };
    
            Mock::given(method("POST"))
                .and(path("/posts"))
                .respond_with(ResponseTemplate::new(201).set_body_json(&post))
                .mount(&mock_server)
                .await;
    
            let api_client = ApiClient::new(&mock_server.uri());
            let created_post = api_client.create_post(&post).await.unwrap();
    
            assert_eq!(created_post.title, post.title);
            assert_eq!(created_post.body, post.body);
            assert_eq!(created_post.userId, post.userId);
        }
    
        #[tokio::test]
        async fn test_update_post() {
            let mock_server = MockServer::start().await;
            let post = Post {
                title: "Updated Title".to_string(),
                body: "Updated Body".to_string(),
                userId: 1,
            };
    
            Mock::given(method("PUT"))
                .and(path("/posts/1"))
                .respond_with(ResponseTemplate::new(200).set_body_json(&post))
                .mount(&mock_server)
                .await;
    
            let api_client = ApiClient::new(&mock_server.uri());
            let updated_post = api_client.update_post(1, &post).await.unwrap();
    
            assert_eq!(updated_post.title, post.title);
            assert_eq!(updated_post.body, post.body);
            assert_eq!(updated_post.userId, post.userId);
        }
    
        #[tokio::test]
        async fn test_delete_post() {
            let mock_server = MockServer::start().await;
    
            Mock::given(method("DELETE"))
                .and(path("/posts/1"))
                .respond_with(ResponseTemplate::new(204))
                .mount(&mock_server)
                .await;
    
            let api_client = ApiClient::new(&mock_server.uri());
            let result = api_client.delete_post(1).await;
    
            assert!(result.is_ok());
        }
    }

    Explanation of the Tests

    These tests cover the basic CRUD operations provided by the ApiClient:

    These tests ensure the correctness of the ApiClient methods and improve the reliability of the code.

    Summon me to re-review when updated! Yours, Gooroo.dev Got thoughts? Don't hesitate to reply or add a reaction.

    penify-dev[bot] commented 2 weeks ago

    PR Code Suggestions ✨

    CategorySuggestion                                                                                                                                    Score
    Error handling
    Improve error handling for API requests in the client methods ___ **Consider handling potential errors when sending requests in the get_post, create_post,
    update_post, and delete_post methods to ensure that the API client can gracefully handle
    failures.** [src/client.rs [21-23]](https://github.com/GuilhermeStracini/apiclient-boilerplate-rs/pull/53/files#diff-7f93c4e263c4e9ec748f804c7fd04a3b2fde86ffd741fb5516d67e1097bae4c1R21-R23) ```diff -let response = self.client.get(&url).send().await?.json::().await?; +let response = self.client.get(&url).send().await.map_err(ApiError::Http)?.json::().await.map_err(ApiError::Serialization)?; ```
    Suggestion importance[1-10]: 9 Why: This suggestion addresses a critical aspect of error handling in API interactions, which is essential for robustness. The proposed change improves the error management significantly.
    9
    Validate the response status in the delete method to confirm successful deletion ___ **Ensure that the delete_post method checks the response status to confirm successful
    deletion before returning.** [src/client.rs [39-41]](https://github.com/GuilhermeStracini/apiclient-boilerplate-rs/pull/53/files#diff-7f93c4e263c4e9ec748f804c7fd04a3b2fde86ffd741fb5516d67e1097bae4c1R39-R41) ```diff -self.client.delete(&url).send().await?; +let response = self.client.delete(&url).send().await?; +if !response.status().is_success() { + return Err(ApiError::Unknown); +} ```
    Suggestion importance[1-10]: 8 Why: Validating the response status is important for ensuring that the deletion was successful, which enhances the reliability of the API client.
    8
    Enhancement
    Enhance the error handling by adding specific error variants to the ApiError enum ___ **Consider adding more specific error variants to the ApiError enum to better categorize
    different types of errors that may occur.** [src/error.rs [5-8]](https://github.com/GuilhermeStracini/apiclient-boilerplate-rs/pull/53/files#diff-97e25e2a0e41c578875856e97b659be2719a65227c104b992e3144efa000c35eR5-R8) ```diff pub enum ApiError { Http(ReqwestError), Serialization(serde_json::Error), + NotFound, + Unauthorized, Unknown, } ```
    Suggestion importance[1-10]: 7 Why: Adding specific error variants can improve error handling and debugging, making the API client more user-friendly and maintainable.
    7
    Validation
    Add validation to the Post struct to ensure only valid data is processed ___ **Ensure that the Post struct includes validation for required fields to prevent incomplete
    data from being processed.** [src/models.rs [3]](https://github.com/GuilhermeStracini/apiclient-boilerplate-rs/pull/53/files#diff-623892a814b2aa624c59933bec54e6f0a8d9af2aea2c395f4b83418de364a3afR3-R3) ```diff #[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] ```
    Suggestion importance[1-10]: 6 Why: Adding validation for required fields is a good practice to ensure data integrity, although it is a minor enhancement compared to the other suggestions.
    6
    github-actions[bot] commented 2 weeks ago

    Infisical secrets check: :white_check_mark: No secrets leaked!

    Scan results:

    12:11PM INF scanning for exposed secrets...
    12:11PM INF 88 commits scanned.
    12:11PM INF scan completed in 67.6ms
    12:11PM INF no leaks found