Skip to content

Commit 2fb0700

Browse files
authored
Merge pull request #72 from i7an/contact-imports-api
Contact Imports API
2 parents a2d734c + 618e7e3 commit 2fb0700

File tree

8 files changed

+459
-76
lines changed

8 files changed

+459
-76
lines changed

examples/contacts_api.rb

Lines changed: 100 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
contact_lists = Mailtrap::ContactListsAPI.new 3229, client
55
contacts = Mailtrap::ContactsAPI.new 3229, client
66
contact_fields = Mailtrap::ContactFieldsAPI.new 3229, client
7+
contact_imports = Mailtrap::ContactImportsAPI.new 3229, client
78

89
# Set your API credentials as environment variables
910
# export MAILTRAP_API_KEY='your-api-key'
@@ -12,83 +13,84 @@
1213
# contact_lists = Mailtrap::ContactListsAPI.new
1314
# contacts = Mailtrap::ContactsAPI.new
1415
# contact_fields = Mailtrap::ContactFieldsAPI.new
16+
# contact_imports = Mailtrap::ContactImportsAPI.new
1517

1618
# Create new contact list
1719
list = contact_lists.create(name: 'Test List')
18-
# => ContactList.new(id: 1, name: 'Test List')
20+
# => #<struct Mailtrap::ContactList id=1, name="Test List">
1921

2022
# Get all contact lists
2123
contact_lists.list
22-
# => [ContactList.new(id: 1, name: 'Test List')]
24+
# => [#<struct Mailtrap::ContactList id=1, name="Test List">]
2325

2426
# Update contact list
2527
contact_lists.update(list.id, name: 'Test List Updated')
26-
# => ContactList.new(id: 1, name: 'Test List Updated')
28+
# => #<struct Mailtrap::ContactList id=1, name="Test List Updated">
2729

2830
# Get contact list
2931
list = contact_lists.get(list.id)
30-
# => ContactList.new(id: 1, name: 'Test List Updated')
32+
# => #<struct Mailtrap::ContactList id=1, name="Test List Updated">
33+
34+
# Delete contact list
35+
contact_lists.delete(list.id)
3136

3237
# Create new contact field
3338
field = contact_fields.create(name: 'Nickname', data_type: 'text', merge_tag: 'nickname')
34-
# => ContactField.new(id: 1, name: 'Nickname', data_type: 'text', merge_tag: 'nickname')
39+
# => #<struct Mailtrap::ContactField id=1, name="Nickname", data_type="text", merge_tag="nickname">
3540

3641
# Get all contact fields
3742
contact_fields.list
38-
# => [ContactField.new(id: 1, name: 'Nickname', data_type: 'text', merge_tag: 'nickname')]
43+
# => [#<struct Mailtrap::ContactField id=1, name="Nickname", data_type="text", merge_tag="nickname">]
3944

4045
# Update contact field
4146
contact_fields.update(field.id, name: 'Nickname 2', merge_tag: 'nickname')
42-
# => ContactField.new(id: 1, name: 'Nickname 2', data_type: 'text', merge_tag: 'nickname')
47+
# => #<struct Mailtrap::ContactField id=1, name="Nickname 2", data_type="text", merge_tag="nickname">
4348

4449
# Get contact field
4550
field = contact_fields.get(field.id)
46-
# => ContactField.new(id: 1, name: 'Nickname 2', data_type: 'text', merge_tag: 'nickname')
51+
# => #<struct Mailtrap::ContactField id=1, name="Nickname 2", data_type="text", merge_tag="nickname">
4752

4853
# Create new contact with all possible fields
4954
contact = contacts.create(
5055
5156
fields: { field.merge_tag => 'John Doe' },
5257
list_ids: [list.id]
5358
)
54-
# => Contact.new(
55-
# id: 1,
56-
# email: '[email protected]',
57-
# fields: { 'nickname' => 'John Doe' },
58-
# list_ids: [1],
59-
# status: 'subscribed',
60-
# created_at: 1721212345,
61-
# updated_at: 1721212345
62-
# )
59+
# => #<struct Mailtrap::Contact
60+
# id=1,
61+
62+
# fields={ "nickname" => "John Doe" },
63+
# list_ids=[1],
64+
# status="subscribed",
65+
# created_at=1721212345,
66+
# updated_at=1721212345>
6367
contact.newly_created? # => true
6468

6569
# Get contact
6670
contact = contacts.get(contact.id)
67-
# => Contact.new(
68-
# id: 1,
69-
# email: '[email protected]',
70-
# fields: { 'nickname' => 'John Doe' },
71-
# list_ids: [1],
72-
# status: 'subscribed',
73-
# created_at: 1721212345,
74-
# updated_at: 1721212345
75-
# )
71+
# => #<struct Mailtrap::Contact
72+
# id=1,
73+
74+
# fields={ "nickname" => "John Doe" },
75+
# list_ids=[1],
76+
# status="subscribed",
77+
# created_at=1721212345,
78+
# updated_at=1721212345>
7679

7780
# Update contact using id
7881
updated_contact = contacts.upsert(
7982
contact.id,
8083
8184
fields: { field.merge_tag => 'Jane Doe' }
8285
)
83-
# => Contact.new(
84-
# id: 1,
85-
# email: '[email protected]',
86-
# fields: { 'nickname' => 'Jane Doe' },
87-
# list_ids: [1],
88-
# status: 'subscribed',
89-
# created_at: 1721212345,
90-
# updated_at: 1721212350
91-
# )
86+
# => #<struct Mailtrap::Contact
87+
# id=1,
88+
89+
# fields={ "nickname" => "Jane Doe" },
90+
# list_ids=[1],
91+
# status="subscribed",
92+
# created_at=1721212345,
93+
# updated_at=1721212350>
9294
updated_contact.newly_created? # => false
9395

9496
# Update contact using email
@@ -97,46 +99,79 @@
9799
98100
fields: { field.merge_tag => 'Jane Doe' }
99101
)
100-
# => Contact.new(
101-
# id: 1,
102-
# email: '[email protected]',
103-
# fields: { 'nickname' => 'Jane Doe' },
104-
# list_ids: [1],
105-
# status: 'subscribed',
106-
# created_at: 1721212345,
107-
# updated_at: 1721212355
108-
# )
102+
# => #<struct Mailtrap::Contact
103+
# id=1,
104+
105+
# fields={ "nickname" => "Jane Doe" },
106+
# list_ids=[1],
107+
# status="subscribed",
108+
# created_at=1721212345,
109+
# updated_at=1721212355>
109110
updated_contact.newly_created? # => false
110111

111112
# Remove contact from lists
112113
contacts.remove_from_lists(contact.id, [list.id])
113-
# => Contact.new(
114-
# id: 1,
115-
# email: '[email protected]',
116-
# fields: { 'nickname' => 'Jane Doe' },
117-
# list_ids: [],
118-
# status: 'subscribed',
119-
# created_at: 1721212345,
120-
# updated_at: 1721212360
121-
# )
114+
# => #<struct Mailtrap::Contact
115+
# id=1,
116+
117+
# fields={ "nickname" => "Jane Doe" },
118+
# list_ids=[],
119+
# status="subscribed",
120+
# created_at=1721212345,
121+
# updated_at=1721212360>
122122

123123
# Add contact to lists
124124
contacts.add_to_lists(contact.id, [list.id])
125-
# => Contact.new(
126-
# id: 1,
127-
# email: '[email protected]',
128-
# fields: { 'nickname' => 'Jane Doe' },
129-
# list_ids: [1],
130-
# status: 'subscribed',
131-
# created_at: 1721212345,
132-
# updated_at: 1721212365
133-
# )
125+
# => #<struct Mailtrap::Contact
126+
# id=1,
127+
128+
# fields={ "nickname" => "Jane Doe" },
129+
# list_ids=[1],
130+
# status="subscribed",
131+
# created_at=1721212345,
132+
# updated_at=1721212365>
134133

135134
# Delete contact
136135
contacts.delete(contact.id)
137136

138-
# Delete contact list
139-
contact_lists.delete(list.id)
140-
141137
# Delete contact field
142138
contact_fields.delete(field.id)
139+
140+
# Create a new contact import using ContactsImportRequest builder
141+
import_request = Mailtrap::ContactsImportRequest.new.tap do |req|
142+
req.upsert(email: '[email protected]', fields: { first_name: 'John' })
143+
.add_to_lists(email: '[email protected]', list_ids: [1])
144+
.remove_from_lists(email: '[email protected]', list_ids: [2])
145+
146+
req.upsert(email: '[email protected]', fields: { first_name: 'Jane' })
147+
.add_to_lists(email: '[email protected]', list_ids: [1])
148+
.remove_from_lists(email: '[email protected]', list_ids: [2])
149+
end
150+
151+
# Execute the import
152+
contact_import = contact_imports.create(import_request)
153+
# => #<struct Mailtrap::ContactImport
154+
# id=1,
155+
# status="created",
156+
# created_contacts_count=nil,
157+
# updated_contacts_count=nil,
158+
# contacts_over_limit_count=nil>
159+
160+
# Wait for the import to complete (if needed)
161+
162+
# Get the import status
163+
contact_imports.get(contact_import.id)
164+
# => #<struct Mailtrap::ContactImport
165+
# id=1,
166+
# status="finished",
167+
# created_contacts_count=2,
168+
# updated_contacts_count=0,
169+
# contacts_over_limit_count=0>
170+
171+
# Import using plain hash
172+
contact_imports.create(
173+
[
174+
{ email: '[email protected]', fields: { first_name: 'John' }, list_ids_included: [1], list_ids_excluded: [2] },
175+
{ email: '[email protected]', fields: { first_name: 'Jane' }, list_ids_included: [1], list_ids_excluded: [2] }
176+
]
177+
)

lib/mailtrap.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
require_relative 'mailtrap/contacts_api'
99
require_relative 'mailtrap/contact_lists_api'
1010
require_relative 'mailtrap/contact_fields_api'
11+
require_relative 'mailtrap/contact_imports_api'
1112

1213
module Mailtrap
1314
# @!macro api_errors

lib/mailtrap/contact_import.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# frozen_string_literal: true
2+
3+
module Mailtrap
4+
# Data Transfer Object for Contact Import
5+
# @attr_reader id [Integer] The contact import ID
6+
# @attr_reader status [String] The status of the import (created, started, finished, failed)
7+
# @attr_reader created_contacts_count [Integer, nil] Number of contacts created in this import
8+
# @attr_reader updated_contacts_count [Integer, nil] Number of contacts updated in this import
9+
# @attr_reader contacts_over_limit_count [Integer, nil] Number of contacts over the allowed limit
10+
ContactImport = Struct.new(
11+
:id,
12+
:status,
13+
:created_contacts_count,
14+
:updated_contacts_count,
15+
:contacts_over_limit_count,
16+
keyword_init: true
17+
) do
18+
# @return [Hash] The contact attributes as a hash
19+
def to_h
20+
super.compact
21+
end
22+
end
23+
end
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# frozen_string_literal: true
2+
3+
require_relative 'contact_import'
4+
require_relative 'contacts_import_request'
5+
6+
module Mailtrap
7+
class ContactImportsAPI
8+
include BaseAPI
9+
10+
self.supported_options = %i[email fields list_ids_included list_ids_excluded]
11+
12+
self.response_class = ContactImport
13+
14+
# Retrieves a specific contact import
15+
# @param import_id [Integer] The contact import identifier
16+
# @return [ContactImport] Contact import object
17+
# @!macro api_errors
18+
def get(import_id)
19+
base_get(import_id)
20+
end
21+
22+
# Create contacts import
23+
#
24+
# @example Using Mailtrap::ContactsImportRequest
25+
# import_request = Mailtrap::ContactsImportRequest.new.tap do |req|
26+
# req.upsert(email: '[email protected]', fields: { first_name: 'Jane' })
27+
# .add_to_lists(email: '[email protected]', list_ids: [1])
28+
# .remove_from_lists(email: '[email protected]', list_ids: [2])
29+
# req.upsert(email: '[email protected]', fields: { first_name: 'John' })
30+
# .add_to_lists(email: '[email protected]', list_ids: [1])
31+
# .remove_from_lists(email: '[email protected]', list_ids: [2])
32+
# end
33+
# contact_imports.create(import_request)
34+
#
35+
# @example Using plain hash
36+
# contact_imports.create([
37+
# {email: '[email protected]', fields: { first_name: 'John' }, list_ids_included: [1], list_ids_excluded: [2]},
38+
# {email: '[email protected]', fields: { first_name: 'Jane' }, list_ids_included: [1], list_ids_excluded: [2]}
39+
# ])
40+
#
41+
# @param contacts [Mailtrap::ContactsImportRequest, Array<Hash>] The contacts import request
42+
#
43+
# @return [ContactImport] Created contact import object
44+
# @!macro api_errors
45+
# @raise [ArgumentError] If invalid options are provided
46+
def create(contacts)
47+
contact_data = contacts.to_a.each do |contact|
48+
validate_options!(contact, supported_options)
49+
end
50+
response = client.post(base_path, contacts: contact_data)
51+
handle_response(response)
52+
end
53+
alias start create
54+
55+
private
56+
57+
def base_path
58+
"/api/accounts/#{account_id}/contacts/imports"
59+
end
60+
end
61+
end
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# frozen_string_literal: true
2+
3+
module Mailtrap
4+
# A builder class for creating contact import requests
5+
# Allows you to build a collection of contacts with their associated fields and list memberships
6+
class ContactsImportRequest
7+
def initialize
8+
@data = Hash.new do |h, k|
9+
h[k] = { email: k, fields: {}, list_ids_included: [], list_ids_excluded: [] }
10+
end
11+
end
12+
13+
# Creates or updates a contact with the provided email and fields
14+
# @param email [String] The contact's email address
15+
# @param fields [Hash] Contact fields in the format: field_merge_tag => String, Integer, Float, Boolean, or
16+
# ISO-8601 date string (yyyy-mm-dd)
17+
# @return [ContactsImportRequest] Returns self for method chaining
18+
def upsert(email:, fields: {})
19+
validate_email!(email)
20+
21+
@data[email][:fields].merge!(fields)
22+
23+
self
24+
end
25+
26+
# Adds a contact to the specified lists
27+
# @param email [String] The contact's email address
28+
# @param list_ids [Array<Integer>] Array of list IDs to add the contact to
29+
# @return [ContactsImportRequest] Returns self for method chaining
30+
def add_to_lists(email:, list_ids:)
31+
validate_email!(email)
32+
33+
append_list_ids email:, list_ids:, key: :list_ids_included
34+
35+
self
36+
end
37+
38+
# Removes a contact from the specified lists
39+
# @param email [String] The contact's email address
40+
# @param list_ids [Array<Integer>] Array of list IDs to remove the contact from
41+
# @return [ContactsImportRequest] Returns self for method chaining
42+
def remove_from_lists(email:, list_ids:)
43+
validate_email!(email)
44+
45+
append_list_ids email:, list_ids:, key: :list_ids_excluded
46+
47+
self
48+
end
49+
50+
# @return [Array<Hash>] Array of contact objects ready for import
51+
def to_a
52+
@data.values
53+
end
54+
55+
private
56+
57+
def validate_email!(email)
58+
raise ArgumentError, 'email must be present' if email.nil? || email.empty?
59+
end
60+
61+
def append_list_ids(email:, list_ids:, key:)
62+
raise ArgumentError, 'list_ids must be present' if list_ids.empty?
63+
64+
@data[email][key] |= list_ids
65+
end
66+
end
67+
end

0 commit comments

Comments
 (0)