-
Notifications
You must be signed in to change notification settings - Fork 5
Contacts API #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Contacts API #49
Changes from 6 commits
8b2cac6
4471f3f
1f7c59b
01e8b32
159c35b
e99c504
b30b772
c76a3e9
bc311cf
46fcb82
b8bc602
f56bc2b
c04ee3c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
require 'mailtrap' | ||
|
||
client = Mailtrap::Client.new(api_key: 'your-api-key') | ||
contact_fields = Mailtrap::ContactFieldsAPI.new 3229, client | ||
|
||
# Set your API credentials as environment variables | ||
# export MAILTRAP_API_KEY='your-api-key' | ||
# export MAILTRAP_ACCOUNT_ID=your-account-id | ||
# | ||
# contact_fields = Mailtrap::ContactFieldsAPI.new | ||
|
||
# Create new contact field | ||
field = contact_fields.create(name: 'Updated name', data_type: 'text', merge_tag: 'updated_name') | ||
|
||
# Get all contact fields | ||
contact_fields.list | ||
|
||
# Update contact field | ||
contact_fields.update(field.id, name: 'Updated name 2', merge_tag: 'updated_name_2') | ||
|
||
# Get contact field | ||
field = contact_fields.get(field.id) | ||
|
||
# Delete contact field | ||
contact_fields.delete(field.id) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
require 'mailtrap' | ||
|
||
client = Mailtrap::Client.new(api_key: 'your-api-key') | ||
contact_list = Mailtrap::ContactListsAPI.new 3229, client | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rename the variable. use plural form for consistency. |
||
contacts = Mailtrap::ContactsAPI.new 3229, client | ||
|
||
# Set your API credentials as environment variables | ||
# export MAILTRAP_API_KEY='your-api-key' | ||
# export MAILTRAP_ACCOUNT_ID=your-account-id | ||
# | ||
# contact_list = Mailtrap::ContactListsAPI.new | ||
# contacts = Mailtrap::ContactsAPI.new | ||
|
||
# Create new contact list | ||
list = contact_list.create(name: 'Test List') | ||
|
||
# Get all contact lists | ||
contact_list.list | ||
|
||
# Update contact list | ||
contact_list.update(list.id, name: 'Test List Updated') | ||
|
||
# Get contact list | ||
list = contact_list.get(list.id) | ||
|
||
# Create new contact | ||
contact = contacts.create(email: 'test@example.com', fields: { first_name: 'John Doe' }, list_ids: [list.id]) | ||
|
||
# Get contact | ||
contact = contacts.get(contact.id) | ||
|
||
# Update contact using id | ||
updated_contact = contacts.update(contact.id, email: 'test2@example.com', fields: { first_name: 'Jane Doe' }) | ||
|
||
# Update contact using email | ||
contacts.update(updated_contact.data.email, email: 'test3@example.com', fields: { first_name: 'Jane Doe' }) | ||
|
||
# Remove contact from lists | ||
contacts.remove_from_lists(contact.id, [list.id]) | ||
|
||
# Add contact to lists | ||
contacts.add_to_lists(contact.id, [list.id]) | ||
|
||
# Delete contact | ||
contacts.delete(contact.id) | ||
|
||
# Delete contact list | ||
contact_list.delete(list.id) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,12 @@ | |
require_relative 'mailtrap/action_mailer' if defined? ActionMailer | ||
require_relative 'mailtrap/mail' | ||
require_relative 'mailtrap/errors' | ||
require_relative 'mailtrap/base_api' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You dont need to require it here. Please require the base api only where it is needed, in api implementations. |
||
require_relative 'mailtrap/version' | ||
require_relative 'mailtrap/email_templates_api' | ||
require_relative 'mailtrap/contacts_api' | ||
require_relative 'mailtrap/contact_lists_api' | ||
require_relative 'mailtrap/contact_fields_api' | ||
|
||
module Mailtrap | ||
# @!macro api_errors | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# frozen_string_literal: true | ||
|
||
module Mailtrap | ||
module BaseAPI | ||
attr_reader :account_id, :client | ||
|
||
def self.included(base) | ||
base.extend(ClassMethods) | ||
end | ||
|
||
module ClassMethods | ||
def supported_options(options) | ||
@supported_options = options | ||
end | ||
|
||
def get_supported_options # rubocop:disable Naming/AccessorMethodName | ||
@supported_options | ||
end | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def response_class(response_class) | ||
@response_class = response_class | ||
end | ||
|
||
def get_response_class # rubocop:disable Naming/AccessorMethodName | ||
@response_class | ||
end | ||
end | ||
|
||
# @param account_id [Integer] The account ID | ||
# @param client [Mailtrap::Client] The client instance | ||
# @raise [ArgumentError] If account_id is nil | ||
def initialize(account_id = ENV.fetch('MAILTRAP_ACCOUNT_ID'), client = Mailtrap::Client.new) | ||
raise ArgumentError, 'account_id is required' if account_id.nil? | ||
Comment on lines
+29
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainVerify account_id parameter type consistency The comment indicates 🏁 Script executed: #!/bin/bash
# Description: Check how account_id is used across the codebase
# Check if account_id is used in string interpolation (which would work with both types)
rg -A 2 'account_id' --type ruby
# Check if there are any type conversions for account_id
ast-grep --pattern 'account_id.to_$_' Length of output: 1980 Ensure consistent Currently,
Pick one approach and update 🤖 Prompt for AI Agents
|
||
|
||
@account_id = account_id | ||
@client = client | ||
end | ||
|
||
private | ||
|
||
def supported_options | ||
self.class.get_supported_options | ||
end | ||
|
||
def response_class | ||
self.class.get_response_class | ||
end | ||
|
||
def validate_options!(options, supported_options) | ||
invalid_options = options.keys - supported_options | ||
return if invalid_options.empty? | ||
|
||
raise ArgumentError, "invalid options are given: #{invalid_options}, supported_options: #{supported_options}" | ||
end | ||
|
||
def build_entity(options, response_class) | ||
response_class.new(options.slice(*response_class.members)) | ||
end | ||
|
||
def base_get(id) | ||
response = client.get("#{base_path}/#{id}") | ||
handle_response(response) | ||
end | ||
|
||
def base_create(options, supported_options_override = supported_options) | ||
validate_options!(options, supported_options_override) | ||
response = client.post(base_path, wrap_request(options)) | ||
handle_response(response) | ||
end | ||
|
||
def base_update(id, options, supported_options_override = supported_options) | ||
validate_options!(options, supported_options_override) | ||
response = client.patch("#{base_path}/#{id}", wrap_request(options)) | ||
handle_response(response) | ||
end | ||
|
||
def base_delete(id) | ||
client.delete("#{base_path}/#{id}") | ||
end | ||
|
||
def base_list | ||
response = client.get(base_path) | ||
response.map { |item| handle_response(item) } | ||
end | ||
|
||
def handle_response(response) | ||
build_entity(response, response_class) | ||
end | ||
|
||
def wrap_request(options) | ||
options | ||
end | ||
|
||
def base_path | ||
raise NotImplementedError, 'base_path must be implemented in the including class' | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# frozen_string_literal: true | ||
|
||
module Mailtrap | ||
# Data Transfer Object for Contact | ||
# @see https://api-docs.mailtrap.io/docs/mailtrap-api-docs/220a54e31e5ca-contact | ||
# @attr_reader id [String] The contact ID | ||
# @attr_reader email [String] The contact's email address | ||
# @attr_reader fields [Hash] Object of fields with merge tags | ||
# @attr_reader list_ids [Array<Integer>] Array of list IDs | ||
# @attr_reader status [String] The contact status (subscribed/unsubscribed) | ||
# @attr_reader created_at [Integer] The creation timestamp | ||
# @attr_reader updated_at [Integer] The last update timestamp | ||
Contact = Struct.new( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @IgorDobryn if the only motivation for resource classes it to achieve strictness, then we can do this: contact_hash = {id: 1}
contact_hash.default_proc = proc { |_, k| raise "unknown key '#{k}'" } Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can work, but rather unexpected. So, I'd prefer to keep separate class |
||
:id, | ||
:email, | ||
:fields, | ||
:list_ids, | ||
:status, | ||
:created_at, | ||
:updated_at, | ||
keyword_init: true | ||
) do | ||
# @return [Hash] The contact attributes as a hash | ||
def to_h | ||
super.compact | ||
end | ||
end | ||
|
||
# Data Transfer Object for Contact Update Response | ||
# @see https://api-docs.mailtrap.io/docs/mailtrap-api-docs/16eab4fff9740-contact-update-response | ||
# @attr_reader action [String] The performed action (created/updated) | ||
# @attr_reader data [Contact, Hash] The contact data | ||
ContactUpdateResponse = Struct.new(:action, :data, keyword_init: true) do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my opinion it creates inconsistency. How about adding |
||
def initialize(*) | ||
super | ||
self.data = Contact.new(data) if data.is_a?(Hash) | ||
end | ||
|
||
# @return [Hash] The response attributes as a hash | ||
def to_h | ||
super.compact | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# frozen_string_literal: true | ||
|
||
module Mailtrap | ||
# Data Transfer Object for Contact Field | ||
# @see https://api-docs.mailtrap.io/docs/mailtrap-api-docs/33efe96c91dcc-get-all-contact-fields | ||
# @attr_reader id [Integer] The contact field ID | ||
# @attr_reader name [String] The name of the contact field (max 80 characters) | ||
# @attr_reader data_type [String] The data type of the field | ||
# Allowed values: text, integer, float, boolean, date | ||
# @attr_reader merge_tag [String] Personalize your campaigns by adding a merge tag. | ||
# This field will be replaced with unique contact details for each recipient (max 80 characters) | ||
ContactField = Struct.new(:id, :name, :data_type, :merge_tag, keyword_init: true) do | ||
# @return [Hash] The contact field attributes as a hash | ||
def to_h | ||
super.compact | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative 'contact_field' | ||
|
||
module Mailtrap | ||
class ContactFieldsAPI | ||
include BaseAPI | ||
|
||
supported_options %i[name data_type merge_tag] | ||
|
||
response_class ContactField | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how about:
That way you can avoid There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same applies to |
||
|
||
# Retrieves a specific contact field | ||
# @param field_id [Integer] The contact field identifier | ||
# @return [ContactField] Contact field object | ||
# @!macro api_errors | ||
def get(field_id) | ||
base_get(field_id) | ||
end | ||
|
||
# Creates a new contact field | ||
# @param [Hash] options The parameters to create | ||
# @option options [String] :name The contact field name | ||
# @option options [String] :data_type The data type of the field | ||
# @option options [String] :merge_tag The merge tag of the field | ||
# @return [ContactField] Created contact field object | ||
# @!macro api_errors | ||
# @raise [ArgumentError] If invalid options are provided | ||
def create(options) | ||
base_create(options) | ||
end | ||
|
||
# Updates an existing contact field | ||
# @param field_id [Integer] The contact field ID | ||
# @param [Hash] options The parameters to update | ||
# @option options [String] :name The contact field name | ||
# @option options [String] :merge_tag The merge tag of the field | ||
# @return [ContactField] Updated contact field object | ||
# @!macro api_errors | ||
# @raise [ArgumentError] If invalid options are provided | ||
def update(field_id, options) | ||
base_update(field_id, options, %i[name merge_tag]) | ||
end | ||
|
||
# Deletes a contact field | ||
# @param field_id [Integer] The contact field ID | ||
# @return nil | ||
# @!macro api_errors | ||
def delete(field_id) | ||
base_delete(field_id) | ||
end | ||
|
||
# Lists all contact fields for the account | ||
# @return [Array<ContactField>] Array of contact field objects | ||
# @!macro api_errors | ||
def list | ||
base_list | ||
end | ||
|
||
private | ||
|
||
def base_path | ||
"/api/accounts/#{account_id}/contacts/fields" | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# frozen_string_literal: true | ||
|
||
module Mailtrap | ||
# Data Transfer Object for Contact List | ||
# @see https://api-docs.mailtrap.io/docs/mailtrap-api-docs/6ec7a37234af2-contact-list | ||
# @attr_reader id [Integer] The contact list ID | ||
# @attr_reader name [String] The name of the contact list | ||
ContactList = Struct.new(:id, :name, keyword_init: true) do | ||
# @return [Hash] The contact list attributes as a hash | ||
def to_h | ||
super.compact | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative 'contact_list' | ||
|
||
module Mailtrap | ||
class ContactListsAPI | ||
include BaseAPI | ||
|
||
supported_options %i[name] | ||
|
||
response_class ContactList | ||
|
||
# Retrieves a specific contact list | ||
# @param list_id [Integer] The contact list identifier | ||
# @return [ContactList] Contact list object | ||
# @!macro api_errors | ||
def get(list_id) | ||
base_get(list_id) | ||
end | ||
|
||
# Creates a new contact list | ||
# @param [Hash] options The parameters to create | ||
# @option options [String] :name The contact list name | ||
# @return [ContactList] Created contact list object | ||
# @!macro api_errors | ||
# @raise [ArgumentError] If invalid options are provided | ||
def create(options) | ||
base_create(options) | ||
end | ||
|
||
# Updates an existing contact list | ||
# @param list_id [Integer] The contact list ID | ||
# @param [Hash] options The parameters to update | ||
# @option options [String] :name The contact list name | ||
# @return [ContactList] Updated contact list object | ||
# @!macro api_errors | ||
# @raise [ArgumentError] If invalid options are provided | ||
def update(list_id, options) | ||
base_update(list_id, options) | ||
end | ||
|
||
# Deletes a contact list | ||
# @param list_id [Integer] The contact list ID | ||
# @return nil | ||
# @!macro api_errors | ||
def delete(list_id) | ||
base_delete(list_id) | ||
end | ||
|
||
# Lists all contact lists for the account | ||
# @return [Array<ContactList>] Array of contact list objects | ||
# @!macro api_errors | ||
def list | ||
base_list | ||
end | ||
|
||
private | ||
|
||
def base_path | ||
"/api/accounts/#{account_id}/contacts/lists" | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you decide to create a separate example file for fields but not for lists?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Contacts and Contacs list would be hard to demonstrate an example without each other, as sometimes we need to have a contact_list
while fields are not depends on it, so it can be as separate file.