Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions PR_DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Sage CRM API Client Implementation

## Why
This PR introduces a modern Python client for interacting with Sage CRM's REST API, providing a more maintainable and type-safe way to manage tickets and other CRM entities. The implementation follows Python best practices and includes comprehensive error handling and rate limiting.

## What's New
- Modern Python client for Sage CRM API with type hints (Python 3.8+)
- Comprehensive error handling with custom exceptions
- Built-in rate limiting to prevent API abuse
- Input validation and type checking
- Detailed logging for debugging
- Example usage with environment variables

## Implementation Details

### Core Features
- **Authentication**: Secure Basic Auth with proper credential handling
- **Rate Limiting**: Configurable request throttling (default: 10 requests/second)
- **Error Handling**:
- Custom exceptions for common error cases
- Detailed error messages with context
- Proper HTTP status code handling

### API Methods
- `get_tickets()`: List tickets with optional filtering
- `get_ticket()`: Retrieve a single ticket by ID
- `create_ticket()`: Create new tickets
- `update_ticket()`: Modify existing tickets
- `delete_ticket()`: Remove tickets

### Development Setup
1. Install dependencies:
```bash
pip install requests python-dotenv
```

2. Set up environment variables (`.env` file):
```
SAGE_CRM_URL=https://your-company.crm.sage.com
SAGE_CRM_USERNAME=your_username
SAGE_CRM_PASSWORD=your_password
```

3. Run the example:
```bash
python sage_crm_client.py
```

## Testing
The implementation includes example usage in the `__main__` block that demonstrates:
- Listing open tickets
- Creating a new ticket
- Updating the ticket status
- Error handling for common scenarios

## Security
- Credentials are never logged
- HTTPS is enforced for all API requests
- Rate limiting prevents accidental API abuse

## Future Improvements
- Add support for more Sage CRM entities
- Implement pagination for large result sets
- Add async/await support
- Include unit tests and CI/CD pipeline
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { defineAction } from "@pipedream/types";
import gorgiasOAuth from "../../gorgias_oauth.app.mjs";

export default defineAction({
name: "Get Ticket Message",
description: "Get a specific message from a ticket [See docs here](https://developers.gorgias.com/reference/get-ticket-message)",
key: "gorgias_oauth-get-ticket-message",
version: "0.0.1",
type: "action",
props: {
gorgiasOAuth,
ticketId: {
propDefinition: [
gorgiasOAuth,
"ticketId",
],
},
messageId: {
type: "integer",
label: "Message ID",
description: "The ID of the message to retrieve",
},
},
async run({ $ }) {
const response = await this.gorgiasOAuth.getTicketMessage({
$,
ticketId: this.ticketId,
messageId: this.messageId,
});

// Add summary for user feedback
$.export("$summary", `Successfully retrieved message ${this.messageId} from ticket ${this.ticketId}`);

return response;
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { defineAction } from "@pipedream/types";
import gorgiasOAuth from "../../gorgias_oauth.app.mjs";

export default defineAction({
name: "List Ticket Messages",
description: "List all messages for a specific ticket [See docs here](https://developers.gorgias.com/reference/list-ticket-messages)",
key: "gorgias_oauth-list-ticket-messages",
version: "0.0.1",
type: "action",
props: {
gorgiasOAuth,
ticketId: {
propDefinition: [
gorgiasOAuth,
"ticketId",
],
},
limit: {
type: "integer",
label: "Limit",
description: "Maximum number of messages to return (1-100)",
min: 1,
max: 100,
default: 50,
},
cursor: {
type: "string",
label: "Cursor",
description: "Cursor for pagination (get from the meta.next_cursor of the previous response)",
optional: true,
},
},
async run({ $ }) {
const params = {
limit: this.limit,
};

Check failure on line 37 in components/gorgias_oauth/actions/list-ticket-messages/list-ticket-messages.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Trailing spaces not allowed
if (this.cursor) {
params.cursor = this.cursor;
}

const response = await this.gorgiasOAuth.listTicketMessages({
$,
ticketId: this.ticketId,
params,
});

// Add summary for user feedback
$.export("$summary", `Successfully retrieved ${response.data.length} message${response.data.length === 1 ? '' : 's'}`);

Check failure on line 49 in components/gorgias_oauth/actions/list-ticket-messages/list-ticket-messages.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Strings must use doublequote

Check failure on line 49 in components/gorgias_oauth/actions/list-ticket-messages/list-ticket-messages.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Strings must use doublequote

Check failure on line 49 in components/gorgias_oauth/actions/list-ticket-messages/list-ticket-messages.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Expected newline between consequent and alternate of ternary expression

Check failure on line 49 in components/gorgias_oauth/actions/list-ticket-messages/list-ticket-messages.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Expected newline between test and consequent of ternary expression

// Return the data and pagination info
return {
data: response.data,
meta: response.meta,
};
},
});
31 changes: 31 additions & 0 deletions components/gorgias_oauth/gorgias_oauth.app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,37 @@
},
},
methods: {
/**
* List all messages for a specific ticket
* @param {Object} params - Parameters for the request
* @param {number} params.ticketId - The ID of the ticket
* @param {Object} params.params - Optional query parameters (cursor, limit, etc.)
* @returns {Promise<Object>} - Returns the list of messages and pagination info
*/
listTicketMessages({ $, ticketId, params = {} }) {

Check failure on line 236 in components/gorgias_oauth/gorgias_oauth.app.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Expected a line break before this closing brace

Check failure on line 236 in components/gorgias_oauth/gorgias_oauth.app.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Expected a line break after this opening brace
return this._makeRequest({
$,
path: `/tickets/${ticketId}/messages`,
method: 'get',

Check failure on line 240 in components/gorgias_oauth/gorgias_oauth.app.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Strings must use doublequote
params,
});
},

/**
* Get a specific message by ID
* @param {Object} params - Parameters for the request
* @param {number} params.ticketId - The ID of the ticket
* @param {number} params.messageId - The ID of the message to retrieve
* @returns {Promise<Object>} - Returns the message details
*/
getTicketMessage({ $, ticketId, messageId }) {

Check failure on line 252 in components/gorgias_oauth/gorgias_oauth.app.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Expected a line break before this closing brace

Check failure on line 252 in components/gorgias_oauth/gorgias_oauth.app.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Expected a line break after this opening brace
return this._makeRequest({
$,
path: `/tickets/${ticketId}/messages/${messageId}`,
method: 'get',
});
},

_defaultConfig({
path, method = "get", params = {}, data,
}) {
Expand Down
Loading
Loading