Generate code to integrate with external APIs
This deliverable provides a foundational, production-ready Python code template designed for robust and secure integration with external RESTful APIs. This code emphasizes best practices in API interaction, including secure authentication, comprehensive error handling, automatic retries, and clear modularity. It serves as a strong starting point that you can adapt and extend for your specific API integration needs.
The goal of this step is to furnish you with a versatile and well-structured API client. This client is built to handle common challenges in API integration, allowing you to focus on the business logic rather than the intricacies of HTTP requests, retries, and error parsing.
Key Features of the Generated Code:
GenericAPIClient class for reusability.Before diving into the code, understanding these core concepts will help you leverage and customize the generated client effectively:
requests in Python) simplifies making HTTP requests.GenericAPIClient (Python)Below is the Python code for a GenericAPIClient class, built using the popular requests library.
Dependencies:
To run this code, you need to install the requests library:
pip install requests
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import os
import json
from typing import Dict, Any, Optional, Union
# --- Custom Exception Classes ---
class APIClientError(Exception):
"""Base exception for API client errors."""
def __init__(self, message: str, status_code: Optional[int] = None, response_data: Optional[Any] = None):
super().__init__(message)
self.status_code = status_code
self.response_data = response_data
class APIAuthenticationError(APIClientError):
"""Raised for 401 Unauthorized or 403 Forbidden errors."""
pass
class APIRateLimitError(APIClientError):
"""Raised for 429 Too Many Requests errors."""
pass
class APIServerError(APIClientError):
"""Raised for 5xx server errors."""
pass
class APIValidationError(APIClientError):
"""Raised for 400 Bad Request or 422 Unprocessable Entity errors."""
pass
# --- Generic API Client Class ---
class GenericAPIClient:
"""
A generic and robust API client for interacting with RESTful services.
Handles authentication, retries with exponential backoff, and comprehensive
error handling for common HTTP status codes.
"""
def __init__(
self,
base_url: str,
api_key: Optional[str] = None,
auth_token: Optional[str] = None,
headers: Optional[Dict[str, str]] = None,
timeout: int = 30,
retries: int = 3,
backoff_factor: float = 0.3,
status_forcelist: tuple = (429, 500, 502, 503, 504),
):
"""
Initializes the GenericAPIClient.
Args:
base_url (str): The base URL of the API (e.g., "https://api.example.com/v1").
api_key (str, optional): An API key for 'x-api-key' or similar header.
If provided, overrides 'Authorization' header if both
api_key and auth_token are given.
auth_token (str, optional): A Bearer token for 'Authorization' header.
headers (Dict[str, str], optional): Additional custom headers to send with requests.
timeout (int): Default timeout for requests in seconds.
retries (int): Number of times to retry failed requests.
backoff_factor (float): Factor for exponential backoff between retries.
status_forcelist (tuple): HTTP status codes that will trigger a retry.
"""
if not base_url:
raise ValueError("base_url cannot be empty.")
self.base_url = base_url.rstrip('/') # Ensure no trailing slash for consistent path joining
self.timeout = timeout
self.session = requests.Session()
self._configure_session_retries(retries, backoff_factor, status_forcelist)
# Set default headers
self.headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
if headers:
self.headers.update(headers)
# Handle authentication
if api_key:
self.headers["X-API-KEY"] = api_key # Common header for API keys
elif auth_token:
self.headers["Authorization"] = f"Bearer {auth_token}"
self.session.headers.update(self.headers)
def _configure_session_retries(self, retries: int, backoff_factor: float, status_forcelist: tuple):
"""Configures the requests session with retry logic."""
retry_strategy = Retry(
total=retries,
read=retries,
connect=retries,
backoff_factor=backoff_factor,
status_forcelist=status_forcelist,
allowed_methods=frozenset(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']), # Retry all common methods
raise_on_status=False # Don't raise on status, let our error handler do it
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)
def _send_request(
self,
method: str,
endpoint: str,
data: Optional[Union[Dict, str]] = None,
params: Optional[Dict[str, Any]] = None,
json_payload: Optional[Dict[str, Any]] = None,
files: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None,
) -> Any:
"""
Sends an HTTP request to the API.
Args:
method (str): The HTTP method (GET, POST, PUT, DELETE).
endpoint (str): The API endpoint (e.g., "/users", "/products/123").
data (Union[Dict, str], optional): Dictionary or string of data to send in the request body.
Used for `application/x-www-form-urlencoded` or raw body.
params (Dict[str, Any], optional): Dictionary of query parameters.
json_payload (Dict[str, Any], optional): Dictionary to be sent as JSON in the request body.
files (Dict[str, Any], optional): Dictionary of files to upload.
headers (Dict[str, str], optional): Additional headers for this specific request.
Returns:
Any: The JSON response from the API, or raw text if not JSON.
Raises:
APIClientError: For various API interaction failures.
"""
url = f"{self.base_url}/{endpoint.lstrip('/')}"
request_headers = self.session.headers.copy()
if headers:
request_headers.update(headers)
try:
response = self.session.request(
method,
url,
params=params,
data=data,
json=json_payload,
files=files,
headers=request_headers,
timeout=self.timeout
)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
# Attempt to parse JSON, fall back to text if not JSON
try:
return response.json()
except json.JSONDecodeError:
return response.text
except requests.exceptions.HTTPError as e:
status_code = e.response.status_code
response_data = None
try:
response_data = e.response.json()
except json.JSONDecodeError:
response_data = e.response.text
message = f"API request failed with status {status_code}: {e.response.reason}"
if response_data and isinstance(response_data, dict) and 'message' in response_data:
message = response_data['message']
if status_code == 400:
raise APIValidationError(message, status_code, response_data) from e
elif status_code == 401 or status_code == 403:
raise APIAuthenticationError(message, status_code, response_data) from e
elif status_code == 404:
raise APIClientError(f"Resource not found: {endpoint}", status_code, response_data) from e
elif status_code == 422: # Unprocessable Entity
raise APIValidationError(message, status_code, response_data) from e
elif status_code == 429:
raise APIRateLimitError(message, status_code, response_data) from e
elif 500 <= status_code < 600:
raise APIServerError(message, status_code, response_data) from e
else:
raise APIClientError(message, status_code, response_data) from e
except requests.exceptions.ConnectionError as e:
raise APIClientError(f"Network connection error: {e}", response_data=str(e)) from e
except requests.exceptions.Timeout as e:
raise APIClientError(f"Request timed out after {self.timeout} seconds: {e}", response_data=str(e)) from e
except requests.exceptions.RequestException as e:
raise APIClientError(f"An unexpected request error occurred: {e}", response_data=str(e)) from e
except Exception as e:
raise APIClientError(f"An unexpected
Project Deliverable: Automated Project Setup via Integrated APIs
This document serves as the comprehensive guide for leveraging the newly developed API integrations to streamline and automate your project creation process. The "API Integration Builder" workflow has successfully established robust connections with key external platforms, enabling a unified and efficient approach to setting up new projects.
This deliverable empowers Project Managers to initiate projects with pre-configured resources, standardizing the setup process, reducing manual effort, and ensuring consistency across all new initiatives.
The following external APIs have been successfully integrated and are now available to automate various aspects of project setup:
* Integration Purpose: Automatic creation of a new project instance, including key metadata (name, description, lead, start/end dates).
* Key Functionality: Project board/space initialization.
* Integration Purpose: Automated repository creation for source code management.
* Key Functionality: Repository naming, initial README file, default branching strategy.
* Integration Purpose: Creation of a dedicated communication channel for the project team.
* Key Functionality: Channel naming, basic introductory message, optional team member invitation.
* Integration Purpose: Automatic creation of a structured folder for project documentation.
* Key Functionality: Pre-defined folder structure (e.g., "Requirements," "Design," "Meeting Notes," "Deliverables"), shared access configuration.
Leveraging these integrations for project creation offers significant advantages:
This section provides a step-by-step guide for Project Managers to utilize the API integrations for automated project creation.
Before initiating a new project, ensure the following:
The automated project creation process follows these steps:
(Example: Navigate to [Your Internal Project Portal URL] or execute python create_project.py)*
To successfully create a project using the integrated APIs, you will typically need to provide the following information:
Used for:* Project Management System, Version Control, Communication Channel, Document Folder.
Used for:* Project Management System (e.g., JIRA key).
Used for:* Project Management System, Version Control (README), Communication Channel (intro).
Used for:* Project Management System (assignee), Communication Channel (initial admin).
Used for:* Project Management System.
Used for:* Project Management System.
Used for:* Communication Channel, Document Management (shared access).
Upon successful execution, you should observe the following:
* A new project dashboard/space named [Project Name] with [Project Lead] assigned.
* Initial project settings (start/end dates, description) configured.
(Verification: Log into Jira/Asana/Monday.com and confirm the project exists.)*
* A new repository named [project-name-slug] (or similar) under the designated organization/group.
* An initial README.md file with the project description.
(Verification: Log into GitHub/GitLab/Bitbucket and confirm the repository's existence and initial content.)*
* A new channel named #project-name-slug (or similar) created.
* An introductory message posted in the channel.
* Invited team members (if specified) added to the channel.
(Verification: Check Slack/Teams for the new channel and its members.)*
* A new top-level folder [Project Name] created in the specified location.
* Pre-defined subfolders (e.g., "Requirements," "Design") within the main folder.
* Shared access permissions configured for specified team members.
(Verification: Navigate to Google Drive/SharePoint and confirm the folder structure and access rights.)*
The current API integrations provide a robust foundation. Future enhancements could include:
[Support Email/Channel].[Feedback Form/System].This concludes the "API Integration Builder" workflow, providing you with a powerful tool for efficient project management. We are confident this automation will significantly enhance your operational efficiency and project consistency.