├── .github
├── pull_request_template.md
└── workflows
│ ├── ci-phpunit.yml
│ └── ci-psalm.yaml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE.txt
├── README.md
├── UPGRADE-2.0.md
├── UPGRADE-3.0.md
├── composer.json
├── examples
├── general
│ ├── accounts.php
│ ├── contacts.php
│ ├── permissions.php
│ └── users.php
├── sending
│ └── emails.php
└── testing
│ ├── attachments.php
│ ├── emails.php
│ ├── inboxes.php
│ ├── messages.php
│ └── projects.php
├── psalm.xml
├── src
├── AbstractMailtrapClient.php
├── Api
│ ├── AbstractApi.php
│ ├── AbstractEmails.php
│ ├── BulkSending
│ │ ├── BulkSendingInterface.php
│ │ └── Emails.php
│ ├── EmailsSendApiInterface.php
│ ├── General
│ │ ├── Account.php
│ │ ├── Contact.php
│ │ ├── GeneralInterface.php
│ │ ├── Permission.php
│ │ └── User.php
│ ├── Sandbox
│ │ ├── Attachment.php
│ │ ├── Emails.php
│ │ ├── Inbox.php
│ │ ├── Message.php
│ │ ├── Project.php
│ │ └── SandboxInterface.php
│ └── Sending
│ │ ├── Emails.php
│ │ └── SendingInterface.php
├── Bridge
│ ├── Laravel
│ │ ├── MailtrapSdkProvider.php
│ │ ├── README.md
│ │ └── config
│ │ │ └── mailtrap-sdk.php
│ ├── Symfony
│ │ └── README.md
│ └── Transport
│ │ ├── MailtrapSdkTransport.php
│ │ └── MailtrapSdkTransportFactory.php
├── Config.php
├── ConfigInterface.php
├── DTO
│ └── Request
│ │ ├── Contact
│ │ ├── ContactInterface.php
│ │ ├── CreateContact.php
│ │ └── UpdateContact.php
│ │ ├── Inbox.php
│ │ ├── Permission
│ │ ├── CreateOrUpdatePermission.php
│ │ ├── DestroyPermission.php
│ │ ├── PermissionInterface.php
│ │ └── Permissions.php
│ │ └── RequestInterface.php
├── EmailHeader
│ ├── CategoryHeader.php
│ ├── CustomHeaderInterface.php
│ ├── CustomVariableHeader.php
│ └── Template
│ │ ├── TemplateUuidHeader.php
│ │ └── TemplateVariableHeader.php
├── EmailsSendMailtrapClientInterface.php
├── Exception
│ ├── BadMethodCallException.php
│ ├── HttpClientException.php
│ ├── HttpException.php
│ ├── HttpServerException.php
│ ├── InvalidArgumentException.php
│ ├── InvalidTypeException.php
│ ├── LogicException.php
│ ├── MailtrapExceptionInterface.php
│ ├── RuntimeException.php
│ └── Transport
│ │ └── UnsupportedHostException.php
├── Helper
│ └── ResponseHelper.php
├── HttpClient
│ ├── HttpClientBuilder.php
│ └── HttpClientBuilderInterface.php
├── MailtrapBulkSendingClient.php
├── MailtrapClient.php
├── MailtrapClientInterface.php
├── MailtrapGeneralClient.php
├── MailtrapSandboxClient.php
├── MailtrapSendingClient.php
└── Mime
│ └── MailtrapEmail.php
└── tests
├── Api
├── AbstractEmailsTest.php
├── BulkSending
│ └── BulkEmailsTest.php
├── General
│ ├── AccountTest.php
│ ├── ContactTest.php
│ ├── PermissionTest.php
│ └── UserTest.php
├── Sandbox
│ ├── AttachmentTest.php
│ ├── EmailsTest.php
│ ├── InboxTest.php
│ ├── MessageTest.php
│ └── ProjectTest.php
└── Sending
│ └── EmailsTest.php
├── Bridge
└── Transport
│ ├── MailtrapSdkTransportFactoryTest.php
│ ├── MailtrapSdkTransportTest.php
│ └── TransportFactoryTestCase.php
├── MailtrapBulkSendingClientTest.php
├── MailtrapClientTestCase.php
├── MailtrapGeneralClientTest.php
├── MailtrapSandboxClientTest.php
├── MailtrapSendingClientTest.php
└── MailtrapTestCase.php
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Motivation
2 |
3 |
4 | ## Changes
5 |
6 | - Change 1
7 | - Change 2
8 |
9 | ## How to test
10 |
11 | - [ ] Test 1
12 | - [ ] Test 2
13 |
14 | ## Images and GIFs
15 |
16 | | Before | After |
17 | |--------|--------|
18 | | link1 | link2 |
19 | | link3 | link4 |
20 | | link5 | link6 |
21 | | link7 | link8 |
22 | | link9 | link10 |
23 |
--------------------------------------------------------------------------------
/.github/workflows/ci-phpunit.yml:
--------------------------------------------------------------------------------
1 | name: Unit Tests
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches:
8 | - 'release/**'
9 | - 'feature/**'
10 | - 'main'
11 | schedule:
12 | - cron: '0 0 * * *' # Runs every day at midnight UTC
13 |
14 | jobs:
15 | phpunit-tests:
16 | name: PHP ${{ matrix.php-version }} Latest
17 | runs-on: ${{ matrix.os }}
18 | strategy:
19 | matrix:
20 | os: [ ubuntu-latest ]
21 | php-version: [ '8.0','8.1','8.2','8.3','8.4' ]
22 |
23 | steps:
24 | - name: Checkout
25 | uses: actions/checkout@v3
26 |
27 | - name: Setup PHP
28 | id: setup-php
29 | uses: shivammathur/setup-php@v2
30 | with:
31 | php-version: ${{ matrix.php-version }}
32 | extensions: mbstring, intl, curl
33 |
34 | - name: Print PHP version
35 | run: echo ${{ steps.setup-php.outputs.php-version }}
36 |
37 | - name: Get composer cache directory
38 | id: composer-cache
39 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
40 |
41 | - name: Cache dependencies
42 | uses: actions/cache@v3
43 | with:
44 | path: ${{ steps.composer-cache.outputs.dir }}
45 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
46 | restore-keys: ${{ runner.os }}-composer
47 |
48 | - name: Validate composer.json
49 | run: composer validate
50 |
51 | - name: Install Composer dependencies
52 | run: composer install --prefer-dist --no-progress
53 |
54 | - name: Run phpunit tests
55 | run: composer test
56 |
57 | symfony:
58 | name: Symfony ${{ matrix.symfony }} LTS and PHP ${{ matrix.php-version }}
59 | runs-on: ubuntu-latest
60 | strategy:
61 | matrix:
62 | include:
63 | - symfony: '6'
64 | php-version: '8.2'
65 | - symfony: '7'
66 | php-version: '8.3'
67 | - symfony: '7'
68 | php-version: '8.4'
69 |
70 | steps:
71 | - name: Checkout
72 | uses: actions/checkout@v3
73 |
74 | - name: Setup PHP
75 | uses: shivammathur/setup-php@v2
76 | with:
77 | php-version: ${{ matrix.php-version }}
78 | tools: composer:v2
79 | coverage: none
80 |
81 | - name: Install dependencies
82 | run: |
83 | composer config --no-plugins allow-plugins.symfony/flex true
84 | composer require --no-update --no-interaction --no-progress symfony/flex
85 | composer config extra.symfony.require ${{ matrix.symfony}}
86 | composer update --prefer-dist --no-interaction --prefer-stable --prefer-lowest --no-progress
87 |
88 | - name: Execute tests
89 | run: composer test
90 |
91 | laravel:
92 | name: Laravel ${{ matrix.laravel }} LTS and PHP ${{ matrix.php-version }}
93 | runs-on: ubuntu-latest
94 | strategy:
95 | matrix:
96 | include:
97 | - laravel: '9'
98 | php-version: '8.1'
99 | - laravel: '10'
100 | php-version: '8.2'
101 | - laravel: '11'
102 | php-version: '8.3'
103 | - laravel: '11'
104 | php-version: '8.4'
105 |
106 | steps:
107 | - name: Checkout
108 | uses: actions/checkout@v3
109 |
110 | - name: Setup PHP
111 | uses: shivammathur/setup-php@v2
112 | with:
113 | php-version: ${{ matrix.php-version }}
114 | tools: composer:v2
115 | coverage: none
116 |
117 | - name: Install dependencies
118 | run: |
119 | composer config --no-plugins allow-plugins.laravel/installer true
120 | composer require --no-update --no-interaction --no-progress laravel/framework:${{ matrix.laravel }}
121 | composer update --prefer-dist --no-interaction --prefer-stable --prefer-lowest --no-progress
122 |
123 | - name: Execute tests
124 | run: composer test
125 |
--------------------------------------------------------------------------------
/.github/workflows/ci-psalm.yaml:
--------------------------------------------------------------------------------
1 | name: Psalm Analyze
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches:
8 | - 'release/**'
9 | - 'feature/**'
10 | - 'main'
11 |
12 | jobs:
13 | psalm-analyze:
14 | runs-on: ${{ matrix.os }}
15 | strategy:
16 | matrix:
17 | os: [ ubuntu-latest ]
18 | php-versions: [ '8.0','8.1','8.2','8.3','8.4' ]
19 |
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v3
23 |
24 | - name: Setup PHP
25 | id: setup-php
26 | uses: shivammathur/setup-php@v2
27 | with:
28 | php-version: ${{ matrix.php-versions }}
29 | extensions: mbstring, intl, curl
30 |
31 | - name: Print PHP version
32 | run: echo ${{ steps.setup-php.outputs.php-version }}
33 |
34 | - name: Get composer cache directory
35 | id: composer-cache
36 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
37 |
38 | - name: Cache dependencies
39 | uses: actions/cache@v3
40 | with:
41 | path: ${{ steps.composer-cache.outputs.dir }}
42 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
43 | restore-keys: ${{ runner.os }}-composer
44 |
45 | - name: Install Composer dependencies
46 | run: composer install --prefer-dist --no-progress
47 |
48 | - name: Run Psalm analyze
49 | run: vendor/bin/psalm
50 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [3.1.0] - 2025-05-27
2 | - Add Contacts API functionality
3 |
4 | ## [3.0.0] - 2025-05-15
5 |
6 | - [BC BREAK] Change Symfony&Laravel integration naming from `mailtrap+api` to `mailtrap+sdk` ([reason](https://symfony.com/packages/MailtrapMailer))
7 | - Rollback Psalm library because now they support PHP 8.4
8 |
9 | ## [2.1.0] - 2025-01-28
10 |
11 | - Use psr/http-factory instead of php-http/message-factory
12 | - Remove a Psalm library from dependencies which can break installation on PHP 8.4
13 |
14 | ## [2.0.4] - 2025-01-22
15 |
16 | - Add name prefix into custom EmailHeaders to avoid conflicts with reserved names Symfony\Component\Mime\Header\Headers::HEADER_CLASS_MAP
17 |
18 | ## [2.0.3] - 2024-11-01
19 |
20 | - Add more template examples in Laravel/Symfony docs
21 |
22 | ## [2.0.2] - 2024-10-04
23 |
24 | - Remove an expected message from the `testUnsupportedSchemeException` method ([reason](https://github.com/symfony/mailer/commit/a098a3fe7f42a30235b862162090900cbf787ff6))
25 |
26 |
27 | ## [2.0.1] - 2024-08-16
28 |
29 | - Support mixed types in template_variables (array, string, int, float, bool)
30 |
31 | ## [2.0.0] - 2024-06-12
32 | - [BC BREAK] PHP 7.4 will no longer be supported (PHP 8.0+).
33 | - [BC BREAK] Rebuild `Emails` layers to use the `inboxId` at the client level ([examples](examples/testing/emails.php))
34 | - [BC BREAK] Rebuild SANDBOX `Projects` & `Messages` & `Attachments` & `Inboxes` layers ([examples](examples/testing))
35 | - [BC BREAK] Rebuild GENERAL `Accounts` & `Permissions` & `Users` layers ([examples](examples/general))
36 | - Added a short method to get the Email layer depending on config params `MailtrapClient::initSendingEmails()`
37 | - Added MailtrapEmail wrapper (MIME) for easy add category, custom variables, template uuid, etc.
38 |
39 | ## [1.9.0] - 2024-05-06
40 |
41 | - Support templates in testing
42 | - Refactoring of examples
43 | - sandbox -> [testing](examples/testing)
44 | - bulkSending -> [sending](examples/sending)
45 |
46 | ## [1.8.1] - 2024-04-25
47 |
48 | - Use real value for template headers (should not be encoded as it is not a real header)
49 |
50 | ## [1.8.0] - 2024-04-19
51 |
52 | - Support new functionality [Bulk Stream](https://help.mailtrap.io/article/113-sending-streams)
53 |
54 | ## [1.7.4] - 2024-03-20
55 |
56 | - Add PHP 8.3 support (GitHub Actions)
57 | - Support new Symfony packages v7 (mime, http-client, etc)
58 |
59 | ## [1.7.3] - 2024-01-30
60 |
61 | - Use Psr18ClientDiscovery instead of deprecated HttpClientDiscovery
62 |
63 | ## [1.7.0] - 2023-05-16
64 |
65 | - Support sandbox message endpoints. Examples [here](examples/sandbox/messages.php)
66 |
67 |
68 | ## [1.6.0] - 2023-05-05
69 |
70 | - Support sandbox attachment endpoints. Examples [here](examples/sandbox/attachments.php)
71 |
72 | ## [1.5.0] - 2023-05-04
73 |
74 | - Support sandbox inbox endpoints. Examples [here](examples/sandbox/inboxes.php)
75 |
76 |
77 | ## [1.4.0] - 2023-04-20
78 |
79 | - Support general permission endpoints. Examples [here](examples/general/permissions.php)
80 |
81 | ## [1.3.0] - 2023-04-13
82 |
83 | - Support sandbox project endpoints. Examples [here](examples/sandbox/projects.php)
84 |
85 | ## [1.2.0] - 2023-04-10
86 |
87 | - Support general account users endpoints. Examples [here](examples/general/users.php)
88 |
89 | ## [1.1.0] - 2023-04-07
90 |
91 | - Breaking changes:
92 | - move `accounts()` functions from the `sandbox & sending` layers to the `general`
93 | - Add the new `general` layer to mailtrapClient
94 |
95 | ## [1.0.0] - 2023-03-28
96 |
97 | - The initial release of the official mailtrap.io PHP client
98 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8 |
9 | ## Our Standards
10 |
11 | Examples of behavior that contributes to a positive environment for our community include:
12 |
13 | * Demonstrating empathy and kindness toward other people
14 | * Being respectful of differing opinions, viewpoints, and experiences
15 | * Giving and gracefully accepting constructive feedback
16 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17 | * Focusing on what is best not just for us as individuals, but for the overall community
18 |
19 | Examples of unacceptable behavior include:
20 |
21 | * The use of sexualized language or imagery, and sexual attention or advances of any kind
22 | * Trolling, insulting or derogatory comments, and personal or political attacks
23 | * Public or private harassment
24 | * Publishing others' private information, such as a physical or email address, without their explicit permission
25 | * Other conduct which could reasonably be considered inappropriate in a professional setting
26 |
27 | ## Enforcement Responsibilities
28 |
29 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
32 |
33 | ## Scope
34 |
35 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
36 |
37 | ## Enforcement
38 |
39 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at support@mailtrap.io. All complaints will be reviewed and investigated promptly and fairly.
40 |
41 | All community leaders are obligated to respect the privacy and security of the reporter of any incident.
42 |
43 | ## Enforcement Guidelines
44 |
45 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
46 |
47 | ### 1. Correction
48 |
49 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
50 |
51 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
52 |
53 | ### 2. Warning
54 |
55 | **Community Impact**: A violation through a single incident or series of actions.
56 |
57 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
58 |
59 | ### 3. Temporary Ban
60 |
61 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
62 |
63 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
64 |
65 | ### 4. Permanent Ban
66 |
67 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
68 |
69 | **Consequence**: A permanent ban from any sort of public interaction within the community.
70 |
71 | ## Attribution
72 |
73 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
74 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
75 |
76 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
77 |
78 | [homepage]: https://www.contributor-covenant.org
79 |
80 | For answers to common questions about this code of conduct, see the FAQ at
81 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
82 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2023 Railsware Products Studio LLC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Official Mailtrap PHP client
2 | ===============
3 | 
4 | 
5 |
6 | [](https://packagist.org/packages/railsware/mailtrap-php)
7 | [](https://packagist.org/packages/railsware/mailtrap-php)
8 | [](https://packagist.org/packages/railsware/mailtrap-php)
9 |
10 |
11 | ## Installation
12 | You can install the package via [composer](http://getcomposer.org/)
13 |
14 | The Mailtrap API Client is not hard coupled to Guzzle, React, Zend, Symfony HTTP or any other library that sends
15 | HTTP messages. Instead, it uses the [PSR-18](https://www.php-fig.org/psr/psr-18/) client abstraction.
16 |
17 | This will give you the flexibility to choose what [HTTP client](https://docs.php-http.org/en/latest/clients.html) you want to use.
18 |
19 | If you just want to get started quickly you should run one of the following command (depends on which HTTP client you want to use):
20 | ```bash
21 | # With symfony http client (recommend)
22 | composer require railsware/mailtrap-php symfony/http-client nyholm/psr7
23 |
24 | # Or with guzzle http client
25 | composer require railsware/mailtrap-php guzzlehttp/guzzle php-http/guzzle7-adapter
26 | ```
27 |
28 | ## Usage
29 | You should use Composer autoloader in your application to automatically load your dependencies.
30 |
31 | Here's how to send a message using the SDK:
32 |
33 | ```php
34 | from(new Address('example@your-domain-here.com', 'Mailtrap Test'))
52 | ->replyTo(new Address('reply@your-domain-here.com'))
53 | ->to(new Address('email@example.com', 'Jon'))
54 | ->priority(Email::PRIORITY_HIGH)
55 | ->cc('mailtrapqa@example.com')
56 | ->addCc('staging@example.com')
57 | ->bcc('mailtrapdev@example.com')
58 | ->subject('Best practices of building HTML emails')
59 | ->text('Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap’s Guide on How to Build HTML Email is live on our blog')
60 | ->html(
61 | '
62 |
63 |
Hey
64 | Learn the best practices of building HTML emails and play with ready-to-go templates.
65 | Mailtrap’s Guide on How to Build HTML Email is live on our blog
66 |
67 |
68 | '
69 | )
70 | ->embed(fopen('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg', 'r'), 'logo', 'image/svg+xml')
71 | ->category('Integration Test')
72 | ->customVariables([
73 | 'user_id' => '45982',
74 | 'batch_id' => 'PSJ-12'
75 | ])
76 | ;
77 |
78 | // Custom email headers (optional)
79 | $email->getHeaders()
80 | ->addTextHeader('X-Message-Source', 'domain.com')
81 | ->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client')) // the same as addTextHeader
82 | ;
83 |
84 | try {
85 | $response = $mailtrap->send($email);
86 |
87 | var_dump(ResponseHelper::toArray($response)); // body (array)
88 | } catch (Exception $e) {
89 | echo 'Caught exception: ', $e->getMessage(), "\n";
90 | }
91 |
92 |
93 | // OR -> Mailtrap BULK SENDING client (real)
94 | try {
95 | $mailtrapBulkSending = MailtrapClient::initSendingEmails(
96 | apiKey: getenv('MAILTRAP_API_KEY'), # your API token from here https://mailtrap.io/api-tokens
97 | isBulk: true # Bulk sending (@see https://help.mailtrap.io/article/113-sending-streams)
98 | );
99 |
100 | $response = $mailtrapBulkSending->send($email);
101 |
102 | var_dump(ResponseHelper::toArray($response)); // body (array)
103 | } catch (Exception $e) {
104 | echo 'Caught exception: ', $e->getMessage(), "\n";
105 | }
106 |
107 | // OR -> Mailtrap Testing client (sandbox)
108 | try {
109 | $mailtrapTesting = MailtrapClient::initSendingEmails(
110 | apiKey: getenv('MAILTRAP_API_KEY'), # your API token from here https://mailtrap.io/api-tokens
111 | isSandbox: true, # Sandbox sending (@see https://help.mailtrap.io/article/109-getting-started-with-mailtrap-email-testing)
112 | inboxId: getenv('MAILTRAP_INBOX_ID') # required param for sandbox sending
113 | );
114 |
115 | $response = $mailtrapTesting->send($email);
116 |
117 | var_dump(ResponseHelper::toArray($response)); // body (array)
118 | } catch (Exception $e) {
119 | echo 'Caught exception: ', $e->getMessage(), "\n";
120 | }
121 |
122 | ```
123 |
124 | ### All usage examples
125 |
126 | You can find more examples [here](examples).
127 | * [General examples](examples/general)
128 | * [Testing examples](examples/testing)
129 | * [Sending examples](examples/sending)
130 |
131 |
132 | ## Framework integration
133 |
134 | If you are using a framework you might consider these composer packages to make the framework integration easier.
135 |
136 | * [Symfony](src/Bridge/Symfony)
137 | * [Laravel](src/Bridge/Laravel)
138 |
139 | ## Contributing
140 |
141 | Bug reports and pull requests are welcome on [GitHub](https://github.com/railsware/mailtrap-php). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](CODE_OF_CONDUCT.md).
142 |
143 | ## License
144 |
145 | The package is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
146 |
147 | ## Code of Conduct
148 |
149 | Everyone interacting in the Mailtrap project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
150 |
--------------------------------------------------------------------------------
/UPGRADE-2.0.md:
--------------------------------------------------------------------------------
1 | UPGRADE FROM 1.x to 2.0
2 | =======================
3 |
4 | ### Email Layers
5 | * Added a short method to get one of the Email layers (Sending/Bulk/Sandbox) depending on config params `MailtrapClient::initSendingEmails()`
6 | * string $apiKey
7 | * bool $isBulk = false
8 | * bool $isSandbox = false
9 | * int $inboxId = null
10 | * **BC BREAK**: In Sandbox layer `inboxId` should be passed at the client level.
11 |
12 | __Before__:
13 | ```php
14 | $mailtrap = new MailtrapClient(new Config(getenv('MAILTRAP_API_KEY')));
15 |
16 | $response = $mailtrap
17 | ->sandbox()
18 | ->emails()
19 | ->send($email, 1000001); # <--- inboxId here
20 | ```
21 | __After__:
22 | ```php
23 | # short method using `initSendingEmails`
24 | $mailtrap = MailtrapClient::initSendingEmails(
25 | apiKey: getenv('MAILTRAP_API_KEY'), #your API token from here https://mailtrap.io/api-tokens
26 | isSandbox: true, # required param for sandbox sending
27 | inboxId: getenv('MAILTRAP_INBOX_ID') # <--- inboxId here
28 | );
29 |
30 | # or using the client directly (old variant)
31 | $mailtrap = (new MailtrapClient(new Config(getenv('MAILTRAP_API_KEY'))))
32 | ->sandbox()
33 | ->emails(getenv('MAILTRAP_INBOX_ID')); # <--- inboxId here
34 |
35 | $response = $mailtrap->send($email);
36 | ```
37 |
38 | ### General API
39 | * **BC BREAK**: Rebuild `Accounts` & `Permissions` & `Users` layers ([examples](examples/general))
40 |
41 | __Before__:
42 | ```php
43 | $mailtrap = new MailtrapClient(new Config(getenv('MAILTRAP_API_KEY'))); # no changes here
44 |
45 | $response = $mailtrap
46 | ->general()
47 | ->permissions()
48 | ->getResources(getenv('MAILTRAP_ACCOUNT_ID')); # <--- accountId here
49 |
50 | $response = $mailtrap
51 | ->general()
52 | ->users()
53 | ->getList(getenv('MAILTRAP_ACCOUNT_ID')); # <--- accountId here
54 | ```
55 | __After__:
56 | ```php
57 | // all permissions endpoints
58 | $response = $mailtrap
59 | ->general()
60 | ->permissions(getenv('MAILTRAP_ACCOUNT_ID')) # <--- accountId here
61 | ->getResources();
62 |
63 | // all users endpoints
64 | $response = $mailtrap
65 | ->general()
66 | ->users(getenv('MAILTRAP_ACCOUNT_ID')) # <--- accountId here
67 | ->getList();
68 | ```
69 |
70 | ### Sandbox API
71 | * **BC BREAK**: Rebuild `Projects` & `Messages` & `Attachments` & `Inboxes` layers ([examples](examples/testing))
72 |
73 | __Before__:
74 | ```php
75 | $mailtrap = new MailtrapClient(new Config(getenv('MAILTRAP_API_KEY'))); # no changes here
76 |
77 | $response = $mailtrap
78 | ->sandbox()
79 | ->inboxes()
80 | ->getList(getenv('MAILTRAP_ACCOUNT_ID')); # <--- accountId here
81 | ```
82 | __After__:
83 | ```php
84 | // all sandbox(testing) endpoints: projects, messages, attachments, inboxes
85 | $response = $mailtrap
86 | ->sandbox()
87 | ->inboxes(getenv('MAILTRAP_ACCOUNT_ID')) # <--- accountId here
88 | ->getList();
89 | ```
90 |
91 | ### Email Template class
92 | * Added `MailtrapEmail` wrapper (MIME) for easy use category, custom variables, template uuid, etc.
93 |
94 | __Before__:
95 | ```php
96 | $email = (new Email())
97 | ->from(new Address('example@YOUR-DOMAIN-HERE.com', 'Mailtrap Test')) // <--- you should use your domain here that you installed in the mailtrap.io admin area (otherwise you will get 401)
98 | ->replyTo(new Address('reply@YOUR-DOMAIN-HERE.com'))
99 | ->to(new Address('example@gmail.com', 'Jon'))
100 | ;
101 |
102 | // Template UUID and Variables
103 | $email->getHeaders()
104 | ->add(new TemplateUuidHeader('bfa432fd-0000-0000-0000-8493da283a69'))
105 | ->add(new TemplateVariableHeader('user_name', 'Jon Bush'))
106 | ->add(new TemplateVariableHeader('next_step_link', 'https://mailtrap.io/'))
107 | ->add(new TemplateVariableHeader('get_started_link', 'https://mailtrap.io/'))
108 | ->add(new TemplateVariableHeader('onboarding_video_link', 'some_video_link'))
109 | ;
110 | ```
111 |
112 | __After__:
113 | ```php
114 | use Mailtrap\Mime\MailtrapEmail;
115 |
116 | $email = (new MailtrapEmail()) # <--- new MIME class with template support
117 | ->from(new Address('example@YOUR-DOMAIN-HERE.com', 'Mailtrap Test')) // <--- you should use your domain here that you installed in the mailtrap.io admin area (otherwise you will get 401)
118 | ->replyTo(new Address('reply@YOUR-DOMAIN-HERE.com'))
119 | ->to(new Address('example@gmail.com', 'Jon'))
120 | ->templateUuid('bfa432fd-0000-0000-0000-8493da283a69')
121 | ->templateVariables([
122 | 'user_name' => 'Jon Bush',
123 | 'next_step_link' => 'https://mailtrap.io/',
124 | 'get_started_link' => 'https://mailtrap.io/',
125 | 'onboarding_video_link' => 'some_video_link'
126 | ])
127 | ;
128 | ```
129 |
--------------------------------------------------------------------------------
/UPGRADE-3.0.md:
--------------------------------------------------------------------------------
1 | UPGRADE FROM 2.x to 3.0
2 | =======================
3 |
4 | Version 3.0 introduces **breaking changes** for `Laravel` and `Symfony` frameworks.
5 | This guide helps you upgrade your application accordingly.
6 |
7 |
8 | ### Laravel Integration
9 |
10 | 1. Change mailtrap transport inside your `config/mail.php` file.
11 |
12 | __Before__:
13 | ```php
14 | [
23 |
24 | // start mailtrap transport
25 | 'mailtrap' => [
26 | 'transport' => 'mailtrap'
27 | ],
28 | // end mailtrap transport
29 |
30 | ]
31 | ];
32 | ```
33 | __After__:
34 | ```php
35 | [
44 |
45 | // start mailtrap transport
46 | 'mailtrap-sdk' => [
47 | 'transport' => 'mailtrap-sdk'
48 | ],
49 | // end mailtrap transport
50 |
51 | ]
52 | ];
53 | ```
54 |
55 | 2. Set `mailtrap-sdk` transport as a default instead `mailtrap` inside your `.env` file.
56 |
57 | ```bash
58 | MAIL_MAILER="mailtrap-sdk"
59 | ```
60 |
61 | 3. Rename mailtrap config file and variables
62 |
63 | __Before__:
64 | ```php
65 | # /config/mailtrap.php
66 |
67 | [
71 | 'host' => env('MAILTRAP_HOST', 'send.api.mailtrap.io'),
72 | 'apiKey' => env('MAILTRAP_API_KEY'),
73 | 'inboxId' => env('MAILTRAP_INBOX_ID'),
74 | ],
75 | ];
76 | ```
77 | __After__:
78 | ```php
79 | # /config/mailtrap-sdk.php
80 |
81 | [
85 | 'host' => env('MAILTRAP_HOST', 'send.api.mailtrap.io'),
86 | 'apiKey' => env('MAILTRAP_API_KEY'),
87 | 'inboxId' => env('MAILTRAP_INBOX_ID'),
88 | ],
89 | ];
90 | ```
91 |
92 | ### Symfony Integration
93 | 1. Change configuration inside your `config/services.yaml` file
94 |
95 | __Before__:
96 | ```yaml
97 | ...
98 | # add more service definitions when explicit configuration is needed
99 | # please note that last definitions always *replace* previous ones
100 |
101 | Mailtrap\Bridge\Transport\MailtrapTransportFactory:
102 | tags:
103 | - { name: 'mailer.transport_factory' }
104 | ```
105 | __After__:
106 | ```yaml
107 | ...
108 | # add more service definitions when explicit configuration is needed
109 | # please note that last definitions always *replace* previous ones
110 |
111 | Mailtrap\Bridge\Transport\MailtrapSdkTransportFactory:
112 | tags:
113 | - { name: 'mailer.transport_factory' }
114 | ```
115 |
116 | 2. Change MAILER_DSN variable inside your `.env`
117 |
118 | __Before__:
119 | ```bash
120 | MAILER_DSN=mailtrap://YOUR_API_KEY_HERE@default
121 | # or
122 | MAILER_DSN=mailtrap+api://YOUR_API_KEY_HERE@send.api.mailtrap.io
123 | ```
124 | __After__:
125 | ```bash
126 | MAILER_DSN=mailtrap+sdk://YOUR_API_KEY_HERE@default
127 | # or
128 | MAILER_DSN=mailtrap+sdk://YOUR_API_KEY_HERE@send.api.mailtrap.io
129 | ```
130 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "railsware/mailtrap-php",
3 | "description": "The Mailtrap SDK provides methods for all API functions.",
4 | "keywords": ["mailtrap", "mailtrap-io", "php", "mail", "email", "sdk", "plugin", "symfony", "laravel"],
5 | "homepage": "https://github.com/railsware/mailtrap-php",
6 | "license": "MIT",
7 | "require": {
8 | "php": "^8.0",
9 | "ext-curl": "*",
10 | "ext-json": "*",
11 | "psr/http-message": "^1.0 || ^2.0",
12 | "psr/http-client-implementation": "^1.0",
13 | "php-http/client-common": "^2.0",
14 | "php-http/httplug": "^2.0",
15 | "php-http/discovery": "^1.0",
16 | "symfony/mime": "^6.0|^7.0",
17 | "egulias/email-validator": "^2.1.10|^3.1|^4",
18 | "psr/http-factory": "^1.1"
19 | },
20 | "require-dev": {
21 | "symfony/http-client": "^6.0|^7.0",
22 | "symfony/mailer": "^6.0|^7.0",
23 | "phpunit/phpunit": "^9",
24 | "nyholm/psr7": "^1.5",
25 | "vimeo/psalm": "^5.0|^6.0"
26 | },
27 | "suggest": {
28 | "nyholm/psr7": "PSR-7 message implementation",
29 | "symfony/http-client": "HTTP client"
30 | },
31 | "scripts": {
32 | "test": "vendor/bin/phpunit"
33 | },
34 | "autoload": {
35 | "psr-4": {
36 | "Mailtrap\\": "src/"
37 | }
38 | },
39 | "autoload-dev": {
40 | "psr-4": {
41 | "Mailtrap\\Tests\\": "tests/"
42 | }
43 | },
44 | "extra": {
45 | "laravel": {
46 | "providers": [
47 | "Mailtrap\\Bridge\\Laravel\\MailtrapSdkProvider"
48 | ]
49 | }
50 | },
51 | "minimum-stability": "dev",
52 | "config": {
53 | "allow-plugins": {
54 | "php-http/discovery": true
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/examples/general/accounts.php:
--------------------------------------------------------------------------------
1 | accounts();
11 |
12 | /**
13 | * Get a list of your Mailtrap accounts.
14 | *
15 | * GET https://mailtrap.io/api/accounts
16 | */
17 | try {
18 | $response = $generalAccounts->getList();
19 |
20 | var_dump(ResponseHelper::toArray($response)); // body (array)
21 | } catch (Exception $e) {
22 | echo 'Caught exception: ', $e->getMessage(), "\n";
23 | }
24 |
--------------------------------------------------------------------------------
/examples/general/contacts.php:
--------------------------------------------------------------------------------
1 | contacts($accountId);
14 |
15 | /**
16 | * Get all Contact Lists.
17 | *
18 | * GET https://mailtrap.io/api/accounts/{account_id}/contacts/lists
19 | */
20 | try {
21 | $response = $contacts->getAllContactLists();
22 |
23 | // print the response body (array)
24 | var_dump(ResponseHelper::toArray($response));
25 | } catch (Exception $e) {
26 | echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
27 | }
28 |
29 |
30 | /**
31 | * Create a new Contact
32 | *
33 | * POST https://mailtrap.io/api/accounts/{account_id}/contacts
34 | */
35 | try {
36 | $response = $contacts->createContact(
37 | CreateContact::init(
38 | 'john.smith@example.com',
39 | ['first_name' => 'John', 'last_name' => 'Smith'], // Fields
40 | [1, 2] // List IDs to which the contact will be added
41 | )
42 | );
43 |
44 | // print the response body (array)
45 | var_dump(ResponseHelper::toArray($response));
46 | } catch (Exception $e) {
47 | echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
48 | }
49 |
50 |
51 | /**
52 | * Update contact by ID or Email.
53 | *
54 | * PATCH https://mailtrap.io/api/accounts/{account_id}/contacts/{id_or_email}
55 | */
56 | try {
57 | // Update contact by ID
58 | $response = $contacts->updateContactById(
59 | '019706a8-0000-0000-0000-4f26816b467a',
60 | UpdateContact::init(
61 | 'john.smith@example.com',
62 | ['first_name' => 'John', 'last_name' => 'Smith'], // Fields
63 | [3], // List IDs to which the contact will be added
64 | [1, 2], // List IDs from which the contact will be removed
65 | true // Unsubscribe the contact
66 | )
67 | );
68 |
69 | // OR update contact by email
70 | $response = $contacts->updateContactByEmail(
71 | 'john.smith@example.com',
72 | UpdateContact::init(
73 | 'john.smith@example.com',
74 | ['first_name' => 'John', 'last_name' => 'Smith'], // Fields
75 | [3], // List IDs to which the contact will be added
76 | [1, 2], // List IDs from which the contact will be removed
77 | true // Unsubscribe the contact
78 | )
79 | );
80 |
81 | // print the response body (array)
82 | var_dump(ResponseHelper::toArray($response));
83 | } catch (Exception $e) {
84 | echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
85 | }
86 |
87 |
88 | /**
89 | * Delete contact
90 | *
91 | * Delete https://mailtrap.io/api/accounts/{account_id}/contacts/{id_or_email}
92 | */
93 | try {
94 | // Delete contact by ID
95 | $response = $contacts->deleteContactById('019706a8-0000-0000-0000-4f26816b467a');
96 |
97 | // OR delete contact by email
98 | $response = $contacts->deleteContactByEmail('john.smith@example.com');
99 |
100 | // print the response body (array)
101 | var_dump(ResponseHelper::toArray($response));
102 | } catch (Exception $e) {
103 | echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
104 | }
105 |
--------------------------------------------------------------------------------
/examples/general/permissions.php:
--------------------------------------------------------------------------------
1 | permissions($accountId);
16 |
17 | /**
18 | * Get resources
19 | *
20 | * GET https://mailtrap.io/api/accounts/{account_id}/permissions/resources
21 | */
22 | try {
23 | $response = $generalPermissions->getResources();
24 |
25 | // print the response body (array)
26 | var_dump(ResponseHelper::toArray($response));
27 | } catch (Exception $e) {
28 | echo 'Caught exception: ', $e->getMessage(), "\n";
29 | }
30 |
31 |
32 | /**
33 | * Manage user or token permissions
34 | *
35 | * If you send a combination of resource_type and resource_id that already exists, the permission is updated.
36 | * If the combination doesn’t exist, the permission is created.
37 | *
38 | * PUT https://mailtrap.io/api/accounts/{account_id}/account_accesses/{account_access_id}/permissions/bulk
39 | */
40 | try {
41 | $accountAccessId = getenv('MAILTRAP_ACCOUNT_ACCESS_ID');
42 |
43 | // resource IDs
44 | $projectResourceId = getenv('MAILTRAP_NEW_PROJECT_RESOURCE_ID');
45 | $inboxResourceId = getenv('MAILTRAP_INBOX_RESOURCE_ID');
46 | $destroyProjectResourceId = getenv('MAILTRAP_OLD_PROJECT_RESOURCE_ID');
47 |
48 | $permissions = new Permissions(
49 | new CreateOrUpdatePermission($projectResourceId, PermissionInterface::TYPE_PROJECT, 10), // viewer = 10
50 | new CreateOrUpdatePermission($inboxResourceId, PermissionInterface::TYPE_INBOX, 100), // admin = 100
51 | new DestroyPermission($destroyProjectResourceId, PermissionInterface::TYPE_PROJECT),
52 | );
53 |
54 | $response = $generalPermissions->update($accountAccessId, $permissions);
55 |
56 | // print the response body (array)
57 | var_dump(ResponseHelper::toArray($response));
58 | } catch (Exception $e) {
59 | echo 'Caught exception: ', $e->getMessage(), "\n";
60 | }
61 |
--------------------------------------------------------------------------------
/examples/general/users.php:
--------------------------------------------------------------------------------
1 | users($accountId);
12 |
13 | /**
14 | * List all users in account
15 | *
16 | * GET https://mailtrap.io/api/accounts/{account_id}/account_accesses
17 | */
18 | try {
19 | $response = $generalUsers->getList();
20 |
21 | // OR with query parameters (not required)
22 | $inboxIds = [2000005, 2000006];
23 | $projectIds = [1005001];
24 | $response = $generalUsers->getList($inboxIds, $projectIds);
25 |
26 | // print the response body (array)
27 | var_dump(ResponseHelper::toArray($response));
28 | } catch (Exception $e) {
29 | echo 'Caught exception: ', $e->getMessage(), "\n";
30 | }
31 |
32 | /**
33 | * Remove user from the account
34 | *
35 | * DELETE https://mailtrap.io/api/accounts/{account_id}/account_accesses/{account_access_id}
36 | */
37 | try {
38 | $accountAccessId = 10000009;
39 |
40 | $response = $generalUsers->delete($accountAccessId);
41 |
42 | // print the response body (array)
43 | var_dump(ResponseHelper::toArray($response));
44 | } catch (Exception $e) {
45 | echo 'Caught exception: ', $e->getMessage(), "\n";
46 | }
47 |
--------------------------------------------------------------------------------
/examples/sending/emails.php:
--------------------------------------------------------------------------------
1 | from(new Address('example@YOUR-DOMAIN-HERE.com', 'Mailtrap Test')) // <--- you should use your domain here that you installed in the mailtrap.io admin area (otherwise you will get 401)
30 | ->replyTo(new Address('reply@YOUR-DOMAIN-HERE.com'))
31 | ->to(new Address('email@example.com', 'Jon'))
32 | ->priority(Email::PRIORITY_HIGH)
33 | ->cc('mailtrapqa@example.com')
34 | ->addCc('staging@example.com')
35 | ->bcc('mailtrapdev@example.com')
36 | ->subject('Best practices of building HTML emails')
37 | ->text('Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap’s Guide on How to Build HTML Email is live on our blog')
38 | ->html(
39 | '
40 |
41 |
Hey
42 | Learn the best practices of building HTML emails and play with ready-to-go templates.
43 | Mailtrap’s Guide on How to Build HTML Email is live on our blog
44 |
45 |
46 | '
47 | )
48 | ->embed(fopen('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg', 'r'), 'logo', 'image/svg+xml')
49 | ->attachFromPath('README.md')
50 | ->customVariables([
51 | 'user_id' => '45982',
52 | 'batch_id' => 'PSJ-12'
53 | ])
54 | ->category('Integration Test')
55 | ;
56 |
57 | // Custom email headers (optional)
58 | $email->getHeaders()
59 | ->addTextHeader('X-Message-Source', 'test.com')
60 | ->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client'))
61 | ;
62 |
63 | $response = $mailtrap->send($email);
64 |
65 | var_dump(ResponseHelper::toArray($response)); // body (array)
66 | } catch (Exception $e) {
67 | echo 'Caught exception: ', $e->getMessage(), "\n";
68 | }
69 |
70 |
71 | /**
72 | * Email Sending WITH TEMPLATE
73 | *
74 | * WARNING! If template is provided then subject, text, html, category and other params are forbidden.
75 | *
76 | * UUID of email template. Subject, text and html will be generated from template using optional template_variables.
77 | * Optional template variables that will be used to generate actual subject, text and html from email template
78 | */
79 | try {
80 | $mailtrap = MailtrapClient::initSendingEmails(
81 | apiKey: getenv('MAILTRAP_API_KEY') #your API token from here https://mailtrap.io/api-tokens
82 | );
83 |
84 | $email = (new MailtrapEmail())
85 | ->from(new Address('example@YOUR-DOMAIN-HERE.com', 'Mailtrap Test')) // <--- you should use your domain here that you installed in the mailtrap.io admin area (otherwise you will get 401)
86 | ->replyTo(new Address('reply@YOUR-DOMAIN-HERE.com'))
87 | ->to(new Address('example@gmail.com', 'Jon'))
88 | ->templateUuid('bfa432fd-0000-0000-0000-8493da283a69')
89 | ->templateVariables([
90 | 'user_name' => 'Jon Bush',
91 | 'next_step_link' => 'https://mailtrap.io/',
92 | 'get_started_link' => 'https://mailtrap.io/',
93 | 'onboarding_video_link' => 'some_video_link',
94 | 'company' => [
95 | 'name' => 'Best Company',
96 | 'address' => 'Its Address',
97 | ],
98 | 'products' => [
99 | [
100 | 'name' => 'Product 1',
101 | 'price' => 100,
102 | ],
103 | [
104 | 'name' => 'Product 2',
105 | 'price' => 200,
106 | ],
107 | ],
108 | 'isBool' => true,
109 | 'int' => 123
110 | ])
111 | ;
112 |
113 | $response = $mailtrap->send($email);
114 |
115 | var_dump(ResponseHelper::toArray($response)); // body (array)
116 | } catch (Exception $e) {
117 | echo 'Caught exception: ', $e->getMessage(), "\n";
118 | }
119 |
120 |
121 | /**********************************************************************************************************************
122 | ******************************************* EMAIL BULK SENDING *******************************************************
123 | **********************************************************************************************************************
124 | */
125 |
126 | /**
127 | * Email Bulk Sending API
128 | *
129 | * POST https://bulk.api.mailtrap.io/api/send
130 | */
131 | try {
132 | $bulkMailtrap = MailtrapClient::initSendingEmails(
133 | apiKey: getenv('MAILTRAP_API_KEY'), #your API token from here https://mailtrap.io/api-tokens
134 | isBulk: true # Bulk sending (@see https://help.mailtrap.io/article/113-sending-streams)
135 | );
136 |
137 | $email = (new MailtrapEmail())
138 | ->from(new Address('example@YOUR-DOMAIN-HERE.com', 'Mailtrap Test')) // <--- you should use your domain here that you installed in the mailtrap.io admin area (otherwise you will get 401)
139 | ->replyTo(new Address('reply@YOUR-DOMAIN-HERE.com'))
140 | ->to(new Address('email@example.com', 'Jon'))
141 | ->priority(Email::PRIORITY_HIGH)
142 | ->cc('mailtrapqa@example.com')
143 | ->addCc('staging@example.com')
144 | ->bcc('mailtrapdev@example.com')
145 | ->subject('Best practices of building HTML emails')
146 | ->text('Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap’s Guide on How to Build HTML Email is live on our blog')
147 | ->html(
148 | '
149 |
150 |
Hey
151 | Learn the best practices of building HTML emails and play with ready-to-go templates.
152 | Mailtrap’s Guide on How to Build HTML Email is live on our blog
153 |
154 |
155 | '
156 | )
157 | ->embed(fopen('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg', 'r'), 'logo', 'image/svg+xml')
158 | ->attachFromPath('README.md')
159 | ->customVariables([
160 | 'user_id' => '45982',
161 | 'batch_id' => 'PSJ-12'
162 | ])
163 | ->category('Integration Test')
164 | ;
165 |
166 | // Custom email headers (optional)
167 | $email->getHeaders()
168 | ->addTextHeader('X-Message-Source', 'test.com')
169 | ->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client'))
170 | ;
171 |
172 | $response = $bulkMailtrap->send($email);
173 |
174 | var_dump(ResponseHelper::toArray($response)); // body (array)
175 | } catch (Exception $e) {
176 | echo 'Caught exception: ', $e->getMessage(), "\n";
177 | }
178 |
179 |
180 | /**
181 | * Email Bulk Sending WITH TEMPLATE
182 | *
183 | * WARNING! If a template is provided, then subject, text, html, category and other params are forbidden.
184 | *
185 | * UUID of email template. Subject, text and html will be generated from template using optional template_variables.
186 | * Optional template variables that will be used to generate actual subject, text and html from email template
187 | */
188 | try {
189 | $bulkMailtrap = MailtrapClient::initSendingEmails(
190 | apiKey: getenv('MAILTRAP_API_KEY'), #your API token from here https://mailtrap.io/api-tokens
191 | isBulk: true # Bulk sending (@see https://help.mailtrap.io/article/113-sending-streams)
192 | );
193 |
194 | $email = (new MailtrapEmail())
195 | ->from(new Address('example@YOUR-DOMAIN-HERE.com', 'Mailtrap Test')) // <--- you should use your domain here that you installed in the mailtrap.io admin area (otherwise you will get 401)
196 | ->replyTo(new Address('reply@YOUR-DOMAIN-HERE.com'))
197 | ->to(new Address('example@gmail.com', 'Jon'))
198 | ->templateUuid('bfa432fd-0000-0000-0000-8493da283a69')
199 | ->templateVariables([
200 | 'user_name' => 'Jon Bush',
201 | 'next_step_link' => 'https://mailtrap.io/',
202 | 'get_started_link' => 'https://mailtrap.io/',
203 | 'onboarding_video_link' => 'some_video_link',
204 | 'company' => [
205 | 'name' => 'Best Company',
206 | 'address' => 'Its Address',
207 | ],
208 | 'products' => [
209 | [
210 | 'name' => 'Product 1',
211 | 'price' => 100,
212 | ],
213 | [
214 | 'name' => 'Product 2',
215 | 'price' => 200,
216 | ],
217 | ],
218 | 'isBool' => true,
219 | 'int' => 123
220 | ])
221 | ;
222 |
223 | $response = $bulkMailtrap->send($email);
224 |
225 | var_dump(ResponseHelper::toArray($response)); // body (array)
226 | } catch (Exception $e) {
227 | echo 'Caught exception: ', $e->getMessage(), "\n";
228 | }
229 |
--------------------------------------------------------------------------------
/examples/testing/attachments.php:
--------------------------------------------------------------------------------
1 | attachments($accountId, $inboxId); #required parameters are accountId amd inboxId
14 |
15 |
16 | /**
17 | * Get attachments
18 | *
19 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/attachments
20 | */
21 | try {
22 | $messageId = getenv('MAILTRAP_MESSAGE_ID');
23 | $attachmentType = 'inline'; # optional (null|string)
24 |
25 | $response = $sandboxAttachments->getMessageAttachments($messageId, $attachmentType);
26 |
27 | // print the response body (array)
28 | var_dump(ResponseHelper::toArray($response));
29 | } catch (Exception $e) {
30 | echo 'Caught exception: ', $e->getMessage(), "\n";
31 | }
32 |
33 | /**
34 | * Get single attachment
35 | *
36 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/attachments/{attachment_id}
37 | */
38 | try {
39 | $messageId = getenv('MAILTRAP_MESSAGE_ID');
40 | $attachmentId = getenv('MAILTRAP_MESSAGE_ATTACHMENT_ID');
41 |
42 | $response = $sandboxAttachments->getMessageAttachment($messageId, $attachmentId);
43 |
44 | // print the response body (array)
45 | var_dump(ResponseHelper::toArray($response));
46 | } catch (Exception $e) {
47 | echo 'Caught exception: ', $e->getMessage(), "\n";
48 | }
49 |
--------------------------------------------------------------------------------
/examples/testing/emails.php:
--------------------------------------------------------------------------------
1 | from(new Address('mailtrap@example.com', 'Mailtrap Test')) // <--- you should use the domain which is linked to template UUID (otherwise you will get 401)
31 | ->replyTo(new Address('reply@example.com'))
32 | ->to(new Address('email@example.com', 'Jon'))
33 | ->cc('mailtrapqa@example.com')
34 | ->addCc('staging@example.com')
35 | ->bcc('mailtrapdev@example.com')
36 | ->subject('Best practices of building HTML emails')
37 | ->text('Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap’s Guide on How to Build HTML Email is live on our blog')
38 | ->html(
39 | '
40 |
41 |
Hey
42 | Learn the best practices of building HTML emails and play with ready-to-go templates.
43 | Mailtrap’s Guide on How to Build HTML Email is live on our blog
44 |
45 |
46 | '
47 | )
48 | ->embed(fopen('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg', 'r'), 'logo', 'image/svg+xml')
49 | ->attachFromPath('README.md')
50 | ->customVariables([
51 | 'user_id' => '45982',
52 | 'batch_id' => 'PSJ-12'
53 | ])
54 | ->category('Integration Test')
55 | ;
56 |
57 | // Custom email headers (optional)
58 | $email->getHeaders()
59 | ->addTextHeader('X-Message-Source', 'test.com')
60 | ->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client'))
61 | ;
62 |
63 | $response = $mailtrap->send($email);
64 |
65 | // print all possible information from the response
66 | var_dump($response->getHeaders()); //headers (array)
67 | var_dump($response->getStatusCode()); //status code (int)
68 | var_dump(ResponseHelper::toArray($response)); // body (array)
69 | } catch (Exception $e) {
70 | echo 'Caught exception: ', $e->getMessage(), "\n";
71 | }
72 |
73 |
74 | /**
75 | * Test Email WITH TEMPLATE
76 | *
77 | * WARNING! If template is provided then subject, text, html, category and other params are forbidden.
78 | *
79 | * UUID of email template. Subject, text and html will be generated from template using optional template_variables.
80 | * Optional template variables that will be used to generate actual subject, text and html from email template
81 | */
82 | try {
83 | $mailtrap = MailtrapClient::initSendingEmails(
84 | apiKey: getenv('MAILTRAP_API_KEY'), #your API token from here https://mailtrap.io/api-tokens
85 | isSandbox: true, # Sandbox sending (@see https://help.mailtrap.io/article/109-getting-started-with-mailtrap-email-testing)
86 | inboxId: getenv('MAILTRAP_INBOX_ID') # required param for sandbox sending
87 | );
88 |
89 | $email = (new MailtrapEmail())
90 | ->from(new Address('example@YOUR-DOMAIN-HERE.com', 'Mailtrap Test')) // <--- you should use the domain which is linked to template UUID (otherwise you will get 401)
91 | ->replyTo(new Address('reply@YOUR-DOMAIN-HERE.com'))
92 | ->to(new Address('example@gmail.com', 'Jon'))
93 | ->templateUuid('bfa432fd-0000-0000-0000-8493da283a69')
94 | ->templateVariables([
95 | 'user_name' => 'Jon Bush',
96 | 'next_step_link' => 'https://mailtrap.io/',
97 | 'get_started_link' => 'https://mailtrap.io/',
98 | 'onboarding_video_link' => 'some_video_link',
99 | 'company' => [
100 | 'name' => 'Best Company',
101 | 'address' => 'Its Address',
102 | ],
103 | 'products' => [
104 | [
105 | 'name' => 'Product 1',
106 | 'price' => 100,
107 | ],
108 | [
109 | 'name' => 'Product 2',
110 | 'price' => 200,
111 | ],
112 | ],
113 | 'isBool' => true,
114 | 'int' => 123
115 | ])
116 | ;
117 |
118 | $response = $mailtrap->send($email);
119 |
120 | // print all possible information from the response
121 | var_dump($response->getHeaders()); //headers (array)
122 | var_dump($response->getStatusCode()); //status code (int)
123 | var_dump(ResponseHelper::toArray($response)); // body (array)
124 | } catch (Exception $e) {
125 | echo 'Caught exception: ', $e->getMessage(), "\n";
126 | }
127 |
--------------------------------------------------------------------------------
/examples/testing/inboxes.php:
--------------------------------------------------------------------------------
1 | inboxes($accountId); #required parameter is accountId
14 |
15 | /**
16 | * Get a list of inboxes.
17 | *
18 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes
19 | */
20 | try {
21 | $response = $sandboxInboxes->getList();
22 |
23 | // print the response body (array)
24 | var_dump(ResponseHelper::toArray($response));
25 | } catch (Exception $e) {
26 | echo 'Caught exception: ', $e->getMessage(), "\n";
27 | }
28 |
29 |
30 | /**
31 | * Create an inbox
32 | *
33 | * POST https://mailtrap.io/api/accounts/{account_id}/projects/{project_id}/inboxes
34 | */
35 | try {
36 | $projectId = getenv('MAILTRAP_PROJECT_ID');
37 | $inboxName = 'First inbox';
38 |
39 | $response = $sandboxInboxes->create($projectId, $inboxName);
40 |
41 | // print the response body (array)
42 | var_dump(ResponseHelper::toArray($response));
43 | } catch (Exception $e) {
44 | echo 'Caught exception: ', $e->getMessage(), "\n";
45 | }
46 |
47 |
48 | /**
49 | * Get inbox attributes
50 | *
51 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}
52 | */
53 | try {
54 | $inboxId = getenv('MAILTRAP_INBOX_ID');
55 |
56 | $response = $sandboxInboxes->getInboxAttributes($inboxId);
57 |
58 | // print the response body (array)
59 | var_dump(ResponseHelper::toArray($response));
60 | } catch (Exception $e) {
61 | echo 'Caught exception: ', $e->getMessage(), "\n";
62 | }
63 |
64 |
65 | /**
66 | * Delete an inbox
67 | *
68 | * DELETE https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}
69 | */
70 | try {
71 | $inboxId = getenv('MAILTRAP_INBOX_ID');
72 |
73 | $response = $sandboxInboxes->delete($inboxId);
74 |
75 | // print the response body (array)
76 | var_dump(ResponseHelper::toArray($response));
77 | } catch (Exception $e) {
78 | echo 'Caught exception: ', $e->getMessage(), "\n";
79 | }
80 |
81 |
82 | /**
83 | * Reset email address
84 | *
85 | * PATCH https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/reset_email_username
86 | */
87 | try {
88 | $inboxId = getenv('MAILTRAP_INBOX_ID');
89 |
90 | $response = $sandboxInboxes->resetEmailAddress($inboxId);
91 |
92 | // print the response body (array)
93 | var_dump(ResponseHelper::toArray($response));
94 | } catch (Exception $e) {
95 | echo 'Caught exception: ', $e->getMessage(), "\n";
96 | }
97 |
98 |
99 | /**
100 | * Enable/Disable email address
101 | *
102 | * PATCH https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/toggle_email_username
103 | */
104 | try {
105 | $inboxId = getenv('MAILTRAP_INBOX_ID');
106 |
107 | $response = $sandboxInboxes->toggleEmailAddress($inboxId);
108 |
109 | // print the response body (array)
110 | var_dump(ResponseHelper::toArray($response));
111 | } catch (Exception $e) {
112 | echo 'Caught exception: ', $e->getMessage(), "\n";
113 | }
114 |
115 |
116 | /**
117 | * Reset credentials
118 | *
119 | * PATCH https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/reset_credentials
120 | */
121 | try {
122 | $inboxId = getenv('MAILTRAP_INBOX_ID');
123 |
124 | $response = $sandboxInboxes->resetSmtpCredentials($inboxId);
125 |
126 | // print the response body (array)
127 | var_dump(ResponseHelper::toArray($response));
128 | } catch (Exception $e) {
129 | echo 'Caught exception: ', $e->getMessage(), "\n";
130 | }
131 |
132 |
133 | /**
134 | * Mark as read
135 | *
136 | * PATCH https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/all_read
137 | */
138 | try {
139 | $inboxId = getenv('MAILTRAP_INBOX_ID');
140 |
141 | $response = $sandboxInboxes->markAsRead($inboxId);
142 |
143 | // print the response body (array)
144 | var_dump(ResponseHelper::toArray($response));
145 | } catch (Exception $e) {
146 | echo 'Caught exception: ', $e->getMessage(), "\n";
147 | }
148 |
149 |
150 | /**
151 | * Clean inbox
152 | *
153 | * PATCH https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/clean
154 | */
155 | try {
156 | $inboxId = getenv('MAILTRAP_INBOX_ID');
157 |
158 | $response = $sandboxInboxes->clean($inboxId);
159 |
160 | // print the response body (array)
161 | var_dump(ResponseHelper::toArray($response));
162 | } catch (Exception $e) {
163 | echo 'Caught exception: ', $e->getMessage(), "\n";
164 | }
165 |
166 |
167 | /**
168 | * Update an inbox
169 | *
170 | * PATCH https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}
171 | */
172 | try {
173 | $inboxId = getenv('MAILTRAP_INBOX_ID');
174 | $newInboxName = 'New inbox name';
175 | $newEmailUsername = 'new-email-username';
176 |
177 | $response = $sandboxInboxes->update(
178 | $inboxId,
179 | new Inbox($newInboxName, $newEmailUsername)
180 | );
181 |
182 | // print the response body (array)
183 | var_dump(ResponseHelper::toArray($response));
184 | } catch (Exception $e) {
185 | echo 'Caught exception: ', $e->getMessage(), "\n";
186 | }
187 |
--------------------------------------------------------------------------------
/examples/testing/messages.php:
--------------------------------------------------------------------------------
1 | messages($accountId, $inboxId); #required parameters are accountId and inboxId
15 |
16 | /**
17 | * Get messages
18 | * Note: if you want to get all messages you need to use "page" param (by default will return only 30 messages per page)
19 | *
20 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages
21 | */
22 | try {
23 | // not required parameters
24 | $page = 1; // by default 30 messages per page
25 | $search = 'hello'; // it works like case insensitive pattern matching by subject, to_email, to_name
26 | $lastMessageId = 3000000003; // get emails, where primary key is less then this param (does not work with page param)
27 |
28 | $response = $sandboxMessages->getList();
29 |
30 | // print the response body (array)
31 | var_dump(ResponseHelper::toArray($response));
32 | } catch (Exception $e) {
33 | echo 'Caught exception: ', $e->getMessage(), "\n";
34 | }
35 |
36 |
37 | /**
38 | * Show email message
39 | *
40 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}
41 | */
42 | try {
43 | $messageId = getenv('MAILTRAP_INBOX_MESSAGE_ID');
44 |
45 | $response = $sandboxMessages->getById($messageId);
46 |
47 | // print the response body (array)
48 | var_dump(ResponseHelper::toArray($response));
49 | } catch (Exception $e) {
50 | echo 'Caught exception: ', $e->getMessage(), "\n";
51 | }
52 |
53 |
54 | /**
55 | * Get message source
56 | *
57 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/body.htmlsource
58 | */
59 | try {
60 | $messageId = getenv('MAILTRAP_INBOX_MESSAGE_ID');
61 |
62 | $response = $sandboxMessages->getSource($messageId);
63 |
64 | // print the response body (string)
65 | var_dump(ResponseHelper::toString($response));
66 | } catch (Exception $e) {
67 | echo 'Caught exception: ', $e->getMessage(), "\n";
68 | }
69 |
70 |
71 | /**
72 | * Get message as .eml
73 | *
74 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/body.eml
75 | */
76 | try {
77 | $messageId = getenv('MAILTRAP_INBOX_MESSAGE_ID');
78 |
79 | $response = $sandboxMessages->getEml($messageId);
80 |
81 | // print the response body (string)
82 | var_dump(ResponseHelper::toString($response));
83 | } catch (Exception $e) {
84 | echo 'Caught exception: ', $e->getMessage(), "\n";
85 | }
86 |
87 |
88 | /**
89 | * Get HTML message
90 | *
91 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/body.html
92 | */
93 | try {
94 | $messageId = getenv('MAILTRAP_INBOX_MESSAGE_ID');
95 |
96 | $response = $sandboxMessages->getHtml($messageId);
97 |
98 | // print the response body (string)
99 | var_dump(ResponseHelper::toString($response));
100 | } catch (Exception $e) {
101 | echo 'Caught exception: ', $e->getMessage(), "\n";
102 | }
103 |
104 |
105 | /**
106 | * Get raw message
107 | *
108 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/body.raw
109 | */
110 | try {
111 | $messageId = getenv('MAILTRAP_INBOX_MESSAGE_ID');
112 |
113 | $response = $sandboxMessages->getRaw($messageId);
114 |
115 | // print the response body (string)
116 | var_dump(ResponseHelper::toString($response));
117 | } catch (Exception $e) {
118 | echo 'Caught exception: ', $e->getMessage(), "\n";
119 | }
120 |
121 |
122 | /**
123 | * Get text message
124 | *
125 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/body.txt
126 | */
127 | try {
128 | $messageId = getenv('MAILTRAP_INBOX_MESSAGE_ID');
129 |
130 | $response = $sandboxMessages->getText($messageId);
131 |
132 | // print the response body (string)
133 | var_dump(ResponseHelper::toString($response));
134 | } catch (Exception $e) {
135 | echo 'Caught exception: ', $e->getMessage(), "\n";
136 | }
137 |
138 |
139 | /**
140 | * Get message HTML analysis
141 | *
142 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/analyze
143 | */
144 | try {
145 | $messageId = getenv('MAILTRAP_INBOX_MESSAGE_ID');
146 |
147 | $response = $sandboxMessages->getHtmlAnalysis($messageId);
148 |
149 | // print the response body (array)
150 | var_dump(ResponseHelper::toArray($response));
151 | } catch (Exception $e) {
152 | echo 'Caught exception: ', $e->getMessage(), "\n";
153 | }
154 |
155 |
156 | /**
157 | * Get message spam score
158 | *
159 | * GET https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/spam_report
160 | */
161 | try {
162 | $messageId = getenv('MAILTRAP_INBOX_MESSAGE_ID');
163 |
164 | $response = $sandboxMessages->getSpamScore($messageId);
165 |
166 | // print the response body (array)
167 | var_dump(ResponseHelper::toArray($response));
168 | } catch (Exception $e) {
169 | echo 'Caught exception: ', $e->getMessage(), "\n";
170 | }
171 |
172 |
173 | /**
174 | * Update message (mark as read)
175 | *
176 | * PATCH https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}
177 | */
178 | try {
179 | $messageId = getenv('MAILTRAP_INBOX_MESSAGE_ID');
180 |
181 | $response = $sandboxMessages->markAsRead($messageId);
182 |
183 | // print the response body (array)
184 | var_dump(ResponseHelper::toArray($response));
185 | } catch (Exception $e) {
186 | echo 'Caught exception: ', $e->getMessage(), "\n";
187 | }
188 |
189 |
190 | /**
191 | * Delete message
192 | *
193 | * DELETE https://mailtrap.io/api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}
194 | */
195 | try {
196 | $messageId = getenv('MAILTRAP_INBOX_MESSAGE_ID');
197 |
198 | $response = $sandboxMessages->delete($messageId);
199 |
200 | // print the response body (array)
201 | var_dump(ResponseHelper::toArray($response));
202 | } catch (Exception $e) {
203 | echo 'Caught exception: ', $e->getMessage(), "\n";
204 | }
205 |
--------------------------------------------------------------------------------
/examples/testing/projects.php:
--------------------------------------------------------------------------------
1 | projects($accountId); #required parameter is accountId
13 |
14 | /**
15 | * List projects and their inboxes to which the API token has access.
16 | *
17 | * GET https://mailtrap.io/api/accounts/{account_id}/projects
18 | */
19 | try {
20 | $response = $sandboxProjects->getList();
21 |
22 | // print the response body (array)
23 | var_dump(ResponseHelper::toArray($response));
24 | } catch (Exception $e) {
25 | echo 'Caught exception: ', $e->getMessage(), "\n";
26 | }
27 |
28 |
29 | /**
30 | * Get the project and its inboxes.
31 | *
32 | * GET https://mailtrap.io/api/accounts/{account_id}/projects/{project_id}
33 | */
34 | try {
35 | $projectId = getenv('MAILTRAP_PROJECT_ID');
36 |
37 | $response = $sandboxProjects->getById($projectId);
38 |
39 | // print the response body (array)
40 | var_dump(ResponseHelper::toArray($response));
41 | } catch (Exception $e) {
42 | echo 'Caught exception: ', $e->getMessage(), "\n";
43 | }
44 |
45 | /**
46 | * Create project
47 | * The project name is min 2 characters and max 100 characters long.
48 | *
49 | * POST https://mailtrap.io/api/accounts/{account_id}/projects
50 | */
51 | try {
52 | $projectName = 'Some project name';
53 |
54 | $response = $sandboxProjects->create($projectName);
55 |
56 | // print the response body (array)
57 | var_dump(ResponseHelper::toArray($response));
58 | } catch (Exception $e) {
59 | echo 'Caught exception: ', $e->getMessage(), "\n";
60 | }
61 |
62 |
63 | /**
64 | * Update project
65 | * The project name is min 2 characters and max 100 characters long.
66 | *
67 | * PATCH https://mailtrap.io/api/accounts/{account_id}/projects/{project_id}
68 | */
69 | try {
70 | $projectId = getenv('MAILTRAP_PROJECT_ID');
71 | $newProjectName = 'New project name';
72 |
73 | $response = $sandboxProjects->updateName($projectId, $newProjectName);
74 |
75 | // print the response body (array)
76 | var_dump(ResponseHelper::toArray($response));
77 | } catch (Exception $e) {
78 | echo 'Caught exception: ', $e->getMessage(), "\n";
79 | }
80 |
81 |
82 | /**
83 | * Delete project and its inboxes.
84 | *
85 | * DELETE https://mailtrap.io/api/accounts/{account_id}/projects/{project_id}
86 | */
87 | try {
88 | $projectId = getenv('MAILTRAP_PROJECT_ID');
89 |
90 | $response = $sandboxProjects->delete($projectId);
91 |
92 | // print the response body (array)
93 | var_dump(ResponseHelper::toArray($response));
94 | } catch (Exception $e) {
95 | echo 'Caught exception: ', $e->getMessage(), "\n";
96 | }
97 |
98 |
99 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/AbstractMailtrapClient.php:
--------------------------------------------------------------------------------
1 | initByName($name, $arguments);
23 | } catch (InvalidArgumentException) {
24 | throw new BadMethodCallException(sprintf('%s -> undefined method called: "%s"', static::class, $name));
25 | }
26 | }
27 |
28 | public function getConfig(): ConfigInterface
29 | {
30 | return $this->config;
31 | }
32 |
33 | private function getClassByName(string $name): ?string
34 | {
35 | /** @psalm-suppress UndefinedConstant */
36 | return !empty(static::API_MAPPING[$name]) ? static::API_MAPPING[$name] : null;
37 | }
38 |
39 | private function initByName(string $name, $arguments)
40 | {
41 | $className = $this->getClassByName($name);
42 | if (null === $className) {
43 | throw new InvalidArgumentException(sprintf('%s -> undefined api instance called: "%s"', static::class, $name));
44 | }
45 |
46 | /** @psalm-suppress LessSpecificReturnStatement */
47 | return new $className($this->getConfig(), ...$arguments);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Api/AbstractApi.php:
--------------------------------------------------------------------------------
1 | config = $config;
31 | $this->httpClient = $this->config->getHttpClientBuilder()->getHttpClient();
32 | }
33 |
34 | protected function httpGet(string $path, array $parameters = [], array $requestHeaders = []): ResponseInterface
35 | {
36 | if (count($parameters) > 0) {
37 | $path .= '?' . $this->normalizeArrayParams($parameters);
38 | }
39 |
40 | return $this->httpClient->get($this->addDefaultScheme($path), $requestHeaders);
41 | }
42 |
43 | protected function httpPost(string $path, array $requestHeaders = [], ?array $body = null): ResponseInterface
44 | {
45 | return $this->httpClient->post(
46 | $this->addDefaultScheme($path),
47 | $requestHeaders,
48 | !empty($body) ? $this->jsonEncode($body) : null
49 | );
50 | }
51 |
52 | protected function httpPut(string $path, array $requestHeaders = [], ?array $body = null): ResponseInterface
53 | {
54 | return $this->httpClient->put(
55 | $this->addDefaultScheme($path),
56 | $requestHeaders,
57 | !empty($body) ? $this->jsonEncode($body) : null
58 | );
59 | }
60 |
61 | protected function httpPatch(string $path, array $requestHeaders = [], ?array $body = null): ResponseInterface
62 | {
63 | return $this->httpClient->patch(
64 | $this->addDefaultScheme($path),
65 | $requestHeaders,
66 | !empty($body) ? $this->jsonEncode($body) : null
67 | );
68 | }
69 |
70 | protected function httpDelete(string $path, array $requestHeaders = [], ?array $body = null): ResponseInterface
71 | {
72 | return $this->httpClient->delete(
73 | $this->addDefaultScheme($path),
74 | $requestHeaders,
75 | !empty($body) ? $this->jsonEncode($body) : null
76 | );
77 | }
78 |
79 | protected function getHost(): string
80 | {
81 | return $this->config->getHost() ?: self::DEFAULT_HOST;
82 | }
83 |
84 | protected function handleResponse(ResponseInterface $response): ResponseInterface
85 | {
86 | if (!$this->config->isResponseThrowOnError()) {
87 | return $response;
88 | }
89 |
90 | $statusCode = $response->getStatusCode();
91 | switch (true) {
92 | case $statusCode >= 200 && $statusCode < 300:
93 | // Everything fine
94 | break;
95 | case $statusCode >= 400 && $statusCode < 500:
96 | throw HttpClientException::createFromResponse($response);
97 | case $statusCode >= 500:
98 | throw new HttpServerException(
99 | sprintf('Internal Server Error. HTTP response code ("%d") received from the API server. Retry later or contact support.', $statusCode),
100 | $statusCode
101 | );
102 | default:
103 | throw new HttpException(
104 | sprintf('An unexpected error occurred. HTTP response code ("%d") received.', $statusCode),
105 | $statusCode
106 | );
107 |
108 | }
109 |
110 | return $response;
111 | }
112 |
113 | /**
114 | * @param mixed $value
115 | * @param int|null $flags
116 | * @param int $maxDepth
117 | *
118 | * @return string
119 | */
120 | private function jsonEncode(mixed $value, ?int $flags = null, int $maxDepth = 512): string
121 | {
122 | $flags ??= \JSON_HEX_TAG | \JSON_HEX_APOS | \JSON_HEX_AMP | \JSON_HEX_QUOT | \JSON_PRESERVE_ZERO_FRACTION;
123 |
124 | try {
125 | $value = json_encode($value, $flags | \JSON_THROW_ON_ERROR, $maxDepth);
126 | } catch (\JsonException $e) {
127 | throw new InvalidArgumentException('Invalid value for "json" option: ' . $e->getMessage());
128 | }
129 |
130 | return $value;
131 | }
132 |
133 | private function addDefaultScheme(string $path): string
134 | {
135 | return empty(parse_url($path, PHP_URL_SCHEME)) ? 'https://' . $path : $path;
136 | }
137 |
138 | /**
139 | * Mailtrap API doesn't support array numeric values in GET params like that - inbox_ids[0]=1001&inbox_ids[1]=2002
140 | * that's why we need to do some normalization to use without numbers inbox_ids[]=1001&inbox_ids[]=2002
141 | *
142 | * @param array $parameters
143 | *
144 | * @return string
145 | */
146 | private function normalizeArrayParams(array $parameters): string
147 | {
148 | return preg_replace('/%5B\d+%5D/imU', '%5B%5D', http_build_query($parameters, '', '&'));
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/Api/AbstractEmails.php:
--------------------------------------------------------------------------------
1 | $this->getStringifierAddress($this->getSender($email->getHeaders())),
26 | 'to' => array_map([$this, 'getStringifierAddress'], $this->getRecipients($email->getHeaders(), $email)),
27 | ];
28 |
29 | if (null !== $email->getSubject()) {
30 | $payload['subject'] = $email->getSubject();
31 | }
32 |
33 | if (null !== $email->getTextBody()) {
34 | $payload['text'] = $email->getTextBody();
35 | }
36 |
37 | if (null !== $email->getHtmlBody()) {
38 | $payload['html'] = $email->getHtmlBody();
39 | }
40 |
41 | if ($ccEmails = array_map([$this, 'getStringifierAddress'], $email->getCc())) {
42 | $payload['cc'] = $ccEmails;
43 | }
44 |
45 | if ($bccEmails = array_map([$this, 'getStringifierAddress'], $email->getBcc())) {
46 | $payload['bcc'] = $bccEmails;
47 | }
48 |
49 | if ($email->getAttachments()) {
50 | $payload['attachments'] = $this->getAttachments($email);
51 | }
52 |
53 | $headersToBypass = ['received', 'from', 'to', 'cc', 'bcc', 'subject', 'content-type'];
54 | foreach ($email->getHeaders()->all() as $name => $header) {
55 | if (in_array($name, $headersToBypass, true)) {
56 | continue;
57 | }
58 |
59 | switch(true) {
60 | case $header instanceof CustomVariableHeader:
61 | $payload[CustomVariableHeader::VAR_NAME][$header->getNameWithoutPrefix()] = $header->getValue();
62 | break;
63 | case $header instanceof TemplateVariableHeader:
64 | $payload[TemplateVariableHeader::VAR_NAME][$header->getNameWithoutPrefix()] = $header->getValue();
65 | break;
66 | case $header instanceof CategoryHeader:
67 | if (!empty($payload[CategoryHeader::VAR_NAME])) {
68 | throw new RuntimeException(
69 | sprintf('Too many "%s" instances present in the email headers. Mailtrap does not accept more than 1 category in the email.', CategoryHeader::class)
70 | );
71 | }
72 |
73 | $payload[CategoryHeader::VAR_NAME] = $header->getValue();
74 | break;
75 | case $header instanceof TemplateUuidHeader:
76 | if (!empty($payload[TemplateUuidHeader::VAR_NAME])) {
77 | throw new RuntimeException(
78 | sprintf('Too many "%s" instances present in the email headers. Mailtrap does not accept more than 1 template UUID in the email.', TemplateUuidHeader::class)
79 | );
80 | }
81 |
82 | $payload[TemplateUuidHeader::VAR_NAME] = $header->getValue();
83 | break;
84 | default:
85 | $payload['headers'][$header->getName()] = $header->getBodyAsString();
86 | }
87 | }
88 |
89 | return $payload;
90 | }
91 |
92 | private function getAttachments(Email $email): array
93 | {
94 | $attachments = [];
95 | foreach ($email->getAttachments() as $attachment) {
96 | $headers = $attachment->getPreparedHeaders();
97 | $filename = $headers->getHeaderParameter('Content-Disposition', 'filename');
98 | $disposition = $headers->getHeaderBody('Content-Disposition');
99 |
100 | $att = [
101 | 'content' => str_replace("\r\n", '', $attachment->bodyToString()),
102 | 'type' => $headers->get('Content-Type')->getBody(),
103 | 'filename' => $filename,
104 | 'disposition' => $disposition,
105 | ];
106 |
107 | if ('inline' === $disposition) {
108 | $att['content_id'] = $filename;
109 | }
110 |
111 | $attachments[] = $att;
112 | }
113 |
114 | return $attachments;
115 | }
116 |
117 | private function getStringifierAddress(Address $address): array
118 | {
119 | $res = ['email' => $address->getAddress()];
120 |
121 | if ($address->getName()) {
122 | $res['name'] = $address->getName();
123 | }
124 |
125 | return $res;
126 | }
127 |
128 | private function getSender(Headers $headers): Address
129 | {
130 | if ($sender = $headers->get('Sender')) {
131 | return $sender->getAddress();
132 | }
133 | if ($return = $headers->get('Return-Path')) {
134 | return $return->getAddress();
135 | }
136 | if ($from = $headers->get('From')) {
137 | return $from->getAddresses()[0];
138 | }
139 |
140 | throw new LogicException('Unable to determine the sender of the message.');
141 | }
142 |
143 | /**
144 | * @param Headers $headers
145 | * @param Email $email
146 | *
147 | * @return Address[]
148 | */
149 | private function getRecipients(Headers $headers, Email $email): array
150 | {
151 | $recipients = [];
152 | foreach (['to', 'cc', 'bcc'] as $name) {
153 | foreach ($headers->all($name) as $header) {
154 | foreach ($header->getAddresses() as $address) {
155 | $recipients[] = $address;
156 | }
157 | }
158 | }
159 |
160 | return array_filter(
161 | $recipients,
162 | static fn (Address $address) => false === in_array($address, array_merge($email->getCc(), $email->getBcc()), true)
163 | );
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/Api/BulkSending/BulkSendingInterface.php:
--------------------------------------------------------------------------------
1 | handleResponse(
19 | $this->httpPost($this->getHost() . '/api/send', [], $this->getPayload($email))
20 | );
21 | }
22 |
23 | protected function getHost(): string
24 | {
25 | return $this->config->getHost() ?: self::SENDMAIL_BULK_HOST;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Api/EmailsSendApiInterface.php:
--------------------------------------------------------------------------------
1 | handleResponse(
23 | $this->httpGet($this->getHost() . '/api/accounts')
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Api/General/Contact.php:
--------------------------------------------------------------------------------
1 | handleResponse(
31 | $this->httpGet($this->getBasePath() . '/lists')
32 | );
33 | }
34 |
35 | /**
36 | * Create a new Contact.
37 | *
38 | * @param CreateContact $contact
39 | * @return ResponseInterface
40 | */
41 | public function createContact(CreateContact $contact): ResponseInterface
42 | {
43 | return $this->handleResponse(
44 | $this->httpPost(path: $this->getBasePath(), body: ['contact' => $contact->toArray()])
45 | );
46 | }
47 |
48 | /**
49 | * Update an existing Contact by ID (UUID).
50 | *
51 | * @param string $contactId
52 | * @param UpdateContact $contact
53 | * @return ResponseInterface
54 | */
55 | public function updateContactById(string $contactId, UpdateContact $contact): ResponseInterface
56 | {
57 | return $this->updateContact($contactId, $contact);
58 | }
59 |
60 | /**
61 | * Update an existing Contact by Email.
62 | *
63 | * @param string $email
64 | * @param UpdateContact $contact
65 | * @return ResponseInterface
66 | */
67 | public function updateContactByEmail(string $email, UpdateContact $contact): ResponseInterface
68 | {
69 | return $this->updateContact($email, $contact);
70 | }
71 |
72 | /**
73 | * Delete a Contact by ID (UUID).
74 | *
75 | * @param string $contactId
76 | * @return ResponseInterface
77 | */
78 | public function deleteContactById(string $contactId): ResponseInterface
79 | {
80 | return $this->deleteContact($contactId);
81 | }
82 |
83 | /**
84 | * Delete a Contact by Email.
85 | *
86 | * @param string $email
87 | * @return ResponseInterface
88 | */
89 | public function deleteContactByEmail(string $email): ResponseInterface
90 | {
91 | return $this->deleteContact($email);
92 | }
93 |
94 | public function getAccountId(): int
95 | {
96 | return $this->accountId;
97 | }
98 |
99 | /**
100 | * Update an existing Contact.
101 | *
102 | * @param string $contactIdOrEmail
103 | * @param UpdateContact $contact
104 | * @return ResponseInterface
105 | */
106 | private function updateContact(string $contactIdOrEmail, UpdateContact $contact): ResponseInterface
107 | {
108 | return $this->handleResponse(
109 | $this->httpPut(
110 | path: $this->getBasePath() . '/' . urlencode($contactIdOrEmail),
111 | body: ['contact' => $contact->toArray()]
112 | )
113 | );
114 | }
115 |
116 | /**
117 | * Delete a Contact by ID or Email.
118 | *
119 | * @param string $idOrEmail
120 | * @return ResponseInterface
121 | */
122 | private function deleteContact(string $idOrEmail): ResponseInterface
123 | {
124 | return $this->handleResponse(
125 | $this->httpDelete($this->getBasePath() . '/' . urlencode($idOrEmail))
126 | );
127 | }
128 |
129 | private function getBasePath(): string
130 | {
131 | return sprintf('%s/api/accounts/%s/contacts', $this->getHost(), $this->getAccountId());
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/Api/General/GeneralInterface.php:
--------------------------------------------------------------------------------
1 | handleResponse($this->httpGet(
31 | sprintf('%s/api/accounts/%s/permissions/resources', $this->getHost(), $this->getAccountId())
32 | ));
33 | }
34 |
35 | /**
36 | * Manage user or token permissions.
37 | * If you send a combination of resource_type and resource_id that already exists, the permission is updated.
38 | * If the combination doesn’t exist, the permission is created.
39 | *
40 | * @param int $accountAccessId
41 | * @param Permissions $permissions
42 | *
43 | * @return ResponseInterface
44 | */
45 | public function update(int $accountAccessId, Permissions $permissions): ResponseInterface
46 | {
47 | return $this->handleResponse($this->httpPut(
48 | sprintf('%s/api/accounts/%s/account_accesses/%s/permissions/bulk', $this->getHost(), $this->getAccountId(), $accountAccessId),
49 | [],
50 | ['permissions' => $this->getPayload($permissions)]
51 | ));
52 | }
53 |
54 | public function getAccountId(): int
55 | {
56 | return $this->accountId;
57 | }
58 |
59 | private function getPayload(Permissions $permissions): array
60 | {
61 | $payload = [];
62 | foreach ($permissions->getAll() as $permission) {
63 | $payload[] = $permission->toArray();
64 | }
65 |
66 | if (count($payload) === 0) {
67 | throw new RuntimeException('At least one "permission" object should be added to manage user or token');
68 | }
69 |
70 | return $payload;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Api/General/User.php:
--------------------------------------------------------------------------------
1 | 0) {
34 | $parameters['inbox_ids'] = $inboxIds;
35 | }
36 |
37 | if (count($projectIds) > 0) {
38 | $parameters['project_ids'] = $projectIds;
39 | }
40 |
41 | return $this->handleResponse($this->httpGet(
42 | sprintf('%s/api/accounts/%s/account_accesses', $this->getHost(), $this->getAccountId()),
43 | $parameters
44 | ));
45 | }
46 |
47 | /**
48 | * Remove user by their ID. You need to be an account admin/owner for this endpoint to work.
49 | *
50 | * @param int $accountAccessId
51 | *
52 | * @return ResponseInterface
53 | */
54 | public function delete(int $accountAccessId): ResponseInterface
55 | {
56 | return $this->handleResponse($this->httpDelete(
57 | sprintf('%s/api/accounts/%s/account_accesses/%s', $this->getHost(), $this->getAccountId(), $accountAccessId)
58 | ));
59 | }
60 |
61 | public function getAccountId(): int
62 | {
63 | return $this->accountId;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Api/Sandbox/Attachment.php:
--------------------------------------------------------------------------------
1 | $attachmentType
40 | ];
41 | }
42 |
43 | return $this->handleResponse($this->httpGet(
44 | sprintf(
45 | '%s/api/accounts/%s/inboxes/%s/messages/%s/attachments',
46 | $this->getHost(),
47 | $this->getAccountId(),
48 | $this->getInboxId(),
49 | $messageId
50 | ),
51 | $parameters
52 | ));
53 | }
54 |
55 | /**
56 | * Get message single attachment by id.
57 | *
58 | * @param int $messageId
59 | * @param int $attachmentId
60 | *
61 | * @return ResponseInterface
62 | */
63 | public function getMessageAttachment(int $messageId, int $attachmentId): ResponseInterface
64 | {
65 | return $this->handleResponse($this->httpGet(sprintf(
66 | '%s/api/accounts/%s/inboxes/%s/messages/%s/attachments/%s',
67 | $this->getHost(),
68 | $this->getAccountId(),
69 | $this->getInboxId(),
70 | $messageId,
71 | $attachmentId
72 | )));
73 | }
74 |
75 | public function getAccountId(): int
76 | {
77 | return $this->accountId;
78 | }
79 |
80 | public function getInboxId(): int
81 | {
82 | return $this->inboxId;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/Api/Sandbox/Emails.php:
--------------------------------------------------------------------------------
1 | handleResponse(
25 | $this->httpPost(sprintf('%s/api/send/%s', $this->getHost(), $this->getInboxId()), [], $this->getPayload($email))
26 | );
27 | }
28 |
29 | protected function getHost(): string
30 | {
31 | return $this->config->getHost() ?: self::SENDMAIL_SANDBOX_HOST;
32 | }
33 |
34 | public function getInboxId(): int
35 | {
36 | return $this->inboxId;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Api/Sandbox/Inbox.php:
--------------------------------------------------------------------------------
1 | handleResponse($this->httpGet(
31 | sprintf('%s/api/accounts/%s/inboxes', $this->getHost(), $this->getAccountId())
32 | ));
33 | }
34 |
35 | /**
36 | * Get inbox attributes by inbox id. See the list of attributes in the example
37 | *
38 | * @param int $inboxId
39 | *
40 | * @return ResponseInterface
41 | */
42 | public function getInboxAttributes(int $inboxId): ResponseInterface
43 | {
44 | return $this->handleResponse($this->httpGet(
45 | sprintf('%s/api/accounts/%s/inboxes/%s', $this->getHost(), $this->getAccountId(), $inboxId)
46 | ));
47 | }
48 |
49 | /**
50 | * Create an inbox in a project.
51 | *
52 | * @param int $projectId
53 | * @param string $inboxName
54 | *
55 | * @return ResponseInterface
56 | */
57 | public function create(int $projectId, string $inboxName): ResponseInterface
58 | {
59 | return $this->handleResponse(
60 | $this->httpPost(
61 | sprintf('%s/api/accounts/%s/projects/%s/inboxes', $this->getHost(), $this->getAccountId(), $projectId),
62 | [],
63 | ['inbox' => ['name' => $inboxName]]
64 | )
65 | );
66 | }
67 |
68 | /**
69 | * Delete an inbox with all its emails.
70 | *
71 | * @param int $inboxId
72 | *
73 | * @return ResponseInterface
74 | */
75 | public function delete(int $inboxId): ResponseInterface
76 | {
77 | return $this->handleResponse($this->httpDelete(
78 | sprintf('%s/api/accounts/%s/inboxes/%s', $this->getHost(), $this->getAccountId(), $inboxId)
79 | ));
80 | }
81 |
82 | /**
83 | * Update inbox name and/or inbox email username.
84 | *
85 | * @param int $inboxId
86 | * @param InboxRequest $updateInbox
87 | *
88 | * @return ResponseInterface
89 | */
90 | public function update(int $inboxId, InboxRequest $updateInbox): ResponseInterface
91 | {
92 | return $this->handleResponse($this->httpPatch(
93 | sprintf('%s/api/accounts/%s/inboxes/%s', $this->getHost(), $this->getAccountId(), $inboxId),
94 | [],
95 | ['inbox' => $this->getUpdatePayload($updateInbox)]
96 | ));
97 | }
98 |
99 | /**
100 | * Delete all messages (emails) from inbox.
101 | *
102 | * @param int $inboxId
103 | *
104 | * @return ResponseInterface
105 | */
106 | public function clean(int $inboxId): ResponseInterface
107 | {
108 | return $this->handleResponse($this->httpPatch(
109 | sprintf('%s/api/accounts/%s/inboxes/%s/clean', $this->getHost(), $this->getAccountId(), $inboxId)
110 | ));
111 | }
112 |
113 | /**
114 | * Mark all messages in the inbox as read.
115 | *
116 | * @param int $inboxId
117 | *
118 | * @return ResponseInterface
119 | */
120 | public function markAsRead(int $inboxId): ResponseInterface
121 | {
122 | return $this->handleResponse($this->httpPatch(
123 | sprintf('%s/api/accounts/%s/inboxes/%s/all_read', $this->getHost(), $this->getAccountId(), $inboxId)
124 | ));
125 | }
126 |
127 | /**
128 | * Reset SMTP credentials of the inbox.
129 | *
130 | * @param int $inboxId
131 | *
132 | * @return ResponseInterface
133 | */
134 | public function resetSmtpCredentials(int $inboxId): ResponseInterface
135 | {
136 | return $this->handleResponse($this->httpPatch(
137 | sprintf('%s/api/accounts/%s/inboxes/%s/reset_credentials', $this->getHost(), $this->getAccountId(), $inboxId)
138 | ));
139 | }
140 |
141 | /**
142 | * Turn the email address of the inbox on/off.
143 | *
144 | * @param int $inboxId
145 | *
146 | * @return ResponseInterface
147 | */
148 | public function toggleEmailAddress(int $inboxId): ResponseInterface
149 | {
150 | return $this->handleResponse($this->httpPatch(
151 | sprintf('%s/api/accounts/%s/inboxes/%s/toggle_email_username', $this->getHost(), $this->getAccountId(), $inboxId)
152 | ));
153 | }
154 |
155 | /**
156 | * Reset username of email address per inbox.
157 | *
158 | * @param int $inboxId
159 | *
160 | * @return ResponseInterface
161 | */
162 | public function resetEmailAddress(int $inboxId): ResponseInterface
163 | {
164 | return $this->handleResponse($this->httpPatch(
165 | sprintf('%s/api/accounts/%s/inboxes/%s/reset_email_username', $this->getHost(), $this->getAccountId(), $inboxId)
166 | ));
167 | }
168 |
169 | private function getUpdatePayload(InboxRequest $updateInbox): array
170 | {
171 | $result = $updateInbox->toArray();
172 | if (empty($result)) {
173 | throw new RuntimeException('At least one inbox parameter should be populated ("name" or "email_username")');
174 | }
175 |
176 | return $result;
177 | }
178 |
179 | public function getAccountId(): int
180 | {
181 | return $this->accountId;
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/Api/Sandbox/Message.php:
--------------------------------------------------------------------------------
1 | handleResponse($this->httpGet(
58 | sprintf('%s/api/accounts/%s/inboxes/%s/messages', $this->getHost(), $this->getAccountId(), $this->getInboxId()),
59 | $parameters
60 | ));
61 | }
62 |
63 | /**
64 | * Get email message by ID.
65 | *
66 | * @param int $messageId
67 | *
68 | * @return ResponseInterface
69 | */
70 | public function getById(int $messageId): ResponseInterface
71 | {
72 | return $this->handleResponse($this->httpGet(
73 | sprintf('%s/api/accounts/%s/inboxes/%s/messages/%s', $this->getHost(), $this->getAccountId(), $this->getInboxId(), $messageId)
74 | ));
75 | }
76 |
77 | /**
78 | * Get a brief spam report by message ID.
79 | *
80 | * @param int $messageId
81 | *
82 | * @return ResponseInterface
83 | */
84 | public function getSpamScore(int $messageId): ResponseInterface
85 | {
86 | return $this->handleResponse($this->httpGet(sprintf(
87 | '%s/api/accounts/%s/inboxes/%s/messages/%s/spam_report',
88 | $this->getHost(),
89 | $this->getAccountId(),
90 | $this->getInboxId(),
91 | $messageId
92 | )));
93 | }
94 |
95 | /**
96 | * Get a brief HTML report by message ID.
97 | *
98 | * @param int $messageId
99 | *
100 | * @return ResponseInterface
101 | */
102 | public function getHtmlAnalysis(int $messageId): ResponseInterface
103 | {
104 | return $this->handleResponse($this->httpGet(sprintf(
105 | '%s/api/accounts/%s/inboxes/%s/messages/%s/analyze',
106 | $this->getHost(),
107 | $this->getAccountId(),
108 | $this->getInboxId(),
109 | $messageId
110 | )));
111 | }
112 |
113 | /**
114 | * Get text email body, if it exists.
115 | *
116 | * @param int $messageId
117 | *
118 | * @return ResponseInterface
119 | */
120 | public function getText(int $messageId): ResponseInterface
121 | {
122 | return $this->handleResponse($this->httpGet(sprintf(
123 | '%s/api/accounts/%s/inboxes/%s/messages/%s/body.txt',
124 | $this->getHost(),
125 | $this->getAccountId(),
126 | $this->getInboxId(),
127 | $messageId
128 | )));
129 | }
130 |
131 | /**
132 | * Get raw email body.
133 | *
134 | * @param int $messageId
135 | *
136 | * @return ResponseInterface
137 | */
138 | public function getRaw(int $messageId): ResponseInterface
139 | {
140 | return $this->handleResponse($this->httpGet(sprintf(
141 | '%s/api/accounts/%s/inboxes/%s/messages/%s/body.raw',
142 | $this->getHost(),
143 | $this->getAccountId(),
144 | $this->getInboxId(),
145 | $messageId
146 | )));
147 | }
148 |
149 | /**
150 | * Get formatted HTML email body. Not applicable for plain text emails.
151 | *
152 | * @param int $messageId
153 | *
154 | * @return ResponseInterface
155 | */
156 | public function getHtml(int $messageId): ResponseInterface
157 | {
158 | return $this->handleResponse($this->httpGet(sprintf(
159 | '%s/api/accounts/%s/inboxes/%s/messages/%s/body.html',
160 | $this->getHost(),
161 | $this->getAccountId(),
162 | $this->getInboxId(),
163 | $messageId
164 | )));
165 | }
166 |
167 | /**
168 | * Get email message in .eml format.
169 | *
170 | * @param int $messageId
171 | *
172 | * @return ResponseInterface
173 | */
174 | public function getEml( int $messageId): ResponseInterface
175 | {
176 | return $this->handleResponse($this->httpGet(sprintf(
177 | '%s/api/accounts/%s/inboxes/%s/messages/%s/body.eml',
178 | $this->getHost(),
179 | $this->getAccountId(),
180 | $this->getInboxId(),
181 | $messageId
182 | )));
183 | }
184 |
185 | /**
186 | * Get HTML source of email.
187 | *
188 | * @param int $messageId
189 | *
190 | * @return ResponseInterface
191 | */
192 | public function getSource(int $messageId): ResponseInterface
193 | {
194 | return $this->handleResponse($this->httpGet(sprintf(
195 | '%s/api/accounts/%s/inboxes/%s/messages/%s/body.htmlsource',
196 | $this->getHost(),
197 | $this->getAccountId(),
198 | $this->getInboxId(),
199 | $messageId
200 | )));
201 | }
202 |
203 | /**
204 | * Update message attributes (right now only the is_read attribute is available for modification).
205 | *
206 | * @param int $messageId
207 | * @param bool $isRead
208 | *
209 | * @return ResponseInterface
210 | */
211 | public function markAsRead(int $messageId, bool $isRead = true): ResponseInterface
212 | {
213 | return $this->handleResponse($this->httpPatch(
214 | sprintf('%s/api/accounts/%s/inboxes/%s/messages/%s', $this->getHost(), $this->getAccountId(), $this->getInboxId(), $messageId),
215 | [],
216 | [
217 | 'message' => [
218 | 'is_read' => $isRead
219 | ]
220 | ]
221 | ));
222 | }
223 |
224 | /**
225 | * Delete message from inbox.
226 | *
227 | * @param int $messageId
228 | *
229 | * @return ResponseInterface
230 | */
231 | public function delete(int $messageId): ResponseInterface
232 | {
233 | return $this->handleResponse($this->httpDelete(
234 | sprintf('%s/api/accounts/%s/inboxes/%s/messages/%s', $this->getHost(), $this->getAccountId(), $this->getInboxId(), $messageId)
235 | ));
236 | }
237 |
238 | public function getAccountId(): int
239 | {
240 | return $this->accountId;
241 | }
242 |
243 | public function getInboxId(): int
244 | {
245 | return $this->inboxId;
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/src/Api/Sandbox/Project.php:
--------------------------------------------------------------------------------
1 | handleResponse($this->httpGet(
29 | sprintf('%s/api/accounts/%s/projects', $this->getHost(), $this->getAccountId())
30 | ));
31 | }
32 |
33 | /**
34 | * Get the project and its inboxes.
35 | *
36 | * @param int $projectId
37 | *
38 | * @return ResponseInterface
39 | */
40 | public function getById(int $projectId): ResponseInterface
41 | {
42 | return $this->handleResponse($this->httpGet(
43 | sprintf('%s/api/accounts/%s/projects/%s', $this->getHost(), $this->getAccountId(), $projectId)
44 | ));
45 | }
46 |
47 | /**
48 | * Create a project
49 | *
50 | * @param string $projectName
51 | *
52 | * @return ResponseInterface
53 | */
54 | public function create(string $projectName): ResponseInterface
55 | {
56 | return $this->handleResponse($this->httpPost(
57 | sprintf('%s/api/accounts/%s/projects', $this->getHost(), $this->getAccountId()),
58 | [],
59 | ['project' => ['name' => $projectName]]
60 | ));
61 | }
62 |
63 | /**
64 | * Delete project and its inboxes.
65 | *
66 | * @param int $projectId
67 | *
68 | * @return ResponseInterface
69 | */
70 | public function delete(int $projectId): ResponseInterface
71 | {
72 | return $this->handleResponse($this->httpDelete(
73 | sprintf('%s/api/accounts/%s/projects/%s', $this->getHost(), $this->getAccountId(), $projectId)
74 | ));
75 | }
76 |
77 | /**
78 | * Update project name.
79 | *
80 | * @param int $projectId
81 | * @param string $projectName
82 | *
83 | * @return ResponseInterface
84 | */
85 | public function updateName(int $projectId, string $projectName): ResponseInterface
86 | {
87 | return $this->handleResponse($this->httpPatch(
88 | sprintf('%s/api/accounts/%s/projects/%s', $this->getHost(), $this->getAccountId(), $projectId),
89 | [],
90 | ['project' => ['name' => $projectName]]
91 | ));
92 | }
93 |
94 | public function getAccountId(): int
95 | {
96 | return $this->accountId;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/Api/Sandbox/SandboxInterface.php:
--------------------------------------------------------------------------------
1 | handleResponse(
19 | $this->httpPost($this->getHost() . '/api/send', [], $this->getPayload($email))
20 | );
21 | }
22 |
23 | protected function getHost(): string
24 | {
25 | return $this->config->getHost() ?: self::SENDMAIL_TRANSACTIONAL_HOST;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Api/Sending/SendingInterface.php:
--------------------------------------------------------------------------------
1 | mergeConfigFrom(__DIR__ . '/config/mailtrap-sdk.php', 'services');
23 | }
24 |
25 | /**
26 | * Bootstrap services.
27 | */
28 | public function boot(): void
29 | {
30 | // https://laravel.com/docs/9.x/upgrade#symfony-mailer
31 | if ((int) $this->app->version() >= 9) {
32 | Mail::extend('mailtrap-sdk', function () {
33 | return (new MailtrapSdkTransportFactory)->create(
34 | new Dsn(
35 | 'mailtrap+sdk',
36 | config('services.mailtrap-sdk.host'),
37 | config('services.mailtrap-sdk.apiKey'),
38 | null,
39 | null,
40 | config('services.mailtrap-sdk', [])
41 | )
42 | );
43 | });
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Bridge/Laravel/config/mailtrap-sdk.php:
--------------------------------------------------------------------------------
1 | [
5 | 'host' => env('MAILTRAP_HOST', 'send.api.mailtrap.io'),
6 | 'apiKey' => env('MAILTRAP_API_KEY'),
7 | 'inboxId' => env('MAILTRAP_INBOX_ID'),
8 | ],
9 | ];
10 |
--------------------------------------------------------------------------------
/src/Bridge/Symfony/README.md:
--------------------------------------------------------------------------------
1 | Mailtrap bridge for Symfony framework [SDK]
2 | ===============
3 |
4 | Provides full mailtrap.io integration for Symfony Mailer.
5 |
6 | ## Installation
7 | If you just want to get started quickly with the SDK library, you should run the following command:
8 | ```bash
9 | composer require railsware/mailtrap-php symfony/http-client nyholm/psr7 symfony/mailer
10 | ```
11 |
12 | ## Usage
13 |
14 | Add MailtrapTransport into your `config/services.yaml` file
15 | ```yaml
16 | ...
17 | # add more service definitions when explicit configuration is needed
18 | # please note that last definitions always *replace* previous ones
19 |
20 | Mailtrap\Bridge\Transport\MailtrapSdkTransportFactory:
21 | tags:
22 | - { name: 'mailer.transport_factory' }
23 | ```
24 |
25 | ### Sending
26 | Add or change MAILER_DSN variable inside your `.env` file. Also, you need to change the `YOUR_API_KEY_HERE` placeholder.
27 | ```bash
28 | MAILER_DSN=mailtrap+sdk://YOUR_API_KEY_HERE@default
29 | # or
30 | MAILER_DSN=mailtrap+sdk://YOUR_API_KEY_HERE@send.api.mailtrap.io
31 | ```
32 |
33 | ### Bulk Sending
34 | Add or change MAILER_DSN variable inside your `.env` file. Also, you need to change the `YOUR_API_KEY_HERE` placeholder.
35 |
36 | More info about bulk sending -> https://help.mailtrap.io/article/113-sending-streams
37 | ```bash
38 | MAILER_DSN=mailtrap+sdk://YOUR_API_KEY_HERE@bulk.api.mailtrap.io
39 | ```
40 |
41 | ### Sandbox
42 | Add or change MAILER_DSN variable inside your `.env` file. Also, you need to change the `YOUR_API_KEY_HERE` placeholder and put correct `inboxId`.
43 |
44 | More info sandbox -> https://help.mailtrap.io/article/109-getting-started-with-mailtrap-email-testing
45 | ```bash
46 | MAILER_DSN=mailtrap+sdk://YOUR_API_KEY_HERE@sandbox.api.mailtrap.io?inboxId=1000001
47 | ```
48 |
49 | ### Send you first email
50 |
51 | #### CLI command (the mailer:test command was introduced only in Symfony 6.2)
52 | ```bash
53 | php bin/console mailer:test to@example.com
54 | ```
55 |
56 | #### Controller (base example)
57 |
58 | ```php
59 | transport = $transport;
79 | }
80 |
81 | /**
82 | * @Route(name="send-email", path="/send-email", methods={"GET"})
83 | *
84 | * @return JsonResponse
85 | */
86 | public function sendEmail(): JsonResponse
87 | {
88 | $message = (new MailtrapEmail())
89 | ->from('from@xample.com')
90 | ->to('to@xample.com')
91 | ->cc('cc@example.com')
92 | ->bcc('bcc@example.com')
93 | ->replyTo('fabien@example.com')
94 | ->priority(Email::PRIORITY_HIGH)
95 | ->subject('Test email')
96 | ->text('text')
97 | ->category('category')
98 | ->customVariables([
99 | 'var1' => 'value1',
100 | 'var2' => 'value2'
101 | ])
102 | ;
103 |
104 | $response = $this->transport->send($message);
105 |
106 | return JsonResponse::create(['messageId' => $response->getMessageId()]);
107 | }
108 |
109 | /**
110 | * WARNING! To send using Mailtrap Email Template, you should use the native library and its methods,
111 | * as mail transport validation does not allow you to send emails without ‘html’ or ‘text’
112 | *
113 | * @Route(name="send-template-email", path="/send-template-email", methods={"GET"})
114 | *
115 | * @return JsonResponse
116 | */
117 | public function sendTemplateEmail(): JsonResponse
118 | {
119 | $email = (new MailtrapEmail())
120 | ->from(new Address('example@YOUR-DOMAIN-HERE.com', 'Mailtrap Test')) // <--- you should use your domain here that you installed in the mailtrap.io admin area (otherwise you will get 401)
121 | ->replyTo(new Address('reply@YOUR-DOMAIN-HERE.com'))
122 | ->to(new Address('example@gmail.com', 'Jon'))
123 | // when using a template, you should not set a subject, text, HTML, category
124 | // otherwise there will be a validation error from the API side
125 | ->templateUuid('bfa432fd-0000-0000-0000-8493da283a69')
126 | ->templateVariables([
127 | 'user_name' => 'Jon Bush',
128 | 'next_step_link' => 'https://mailtrap.io/',
129 | 'get_started_link' => 'https://mailtrap.io/',
130 | 'onboarding_video_link' => 'some_video_link',
131 | 'company' => [
132 | 'name' => 'Best Company',
133 | 'address' => 'Its Address',
134 | ],
135 | 'products' => [
136 | [
137 | 'name' => 'Product 1',
138 | 'price' => 100,
139 | ],
140 | [
141 | 'name' => 'Product 2',
142 | 'price' => 200,
143 | ],
144 | ],
145 | 'isBool' => true,
146 | 'int' => 123
147 | ])
148 | ;
149 |
150 | $response = MailtrapClient::initSendingEmails(
151 | apiKey: env('MAILTRAP_API_KEY') // your API token from here https://mailtrap.io/api-tokens
152 | )->send($email);
153 |
154 | return JsonResponse::create(ResponseHelper::toArray($response));
155 | }
156 | }
157 | ```
158 |
159 | ## Resources
160 |
161 | * [Symfony mailer documentation](https://symfony.com/doc/current/mailer.html)
162 |
163 | ### Notes
164 | > If you are looking for a quicker setup and don’t need advanced features like templates or custom variables,
165 | > you can also use the [MailtrapMailer Symfony package](https://symfony.com/packages/MailtrapMailer), which provides a simple bridge for sending emails
166 | > through Mailtrap using standard Symfony Mailer configuration.
167 |
--------------------------------------------------------------------------------
/src/Bridge/Transport/MailtrapSdkTransport.php:
--------------------------------------------------------------------------------
1 | getEndpoint());
38 | }
39 |
40 | protected function doSend(SentMessage $message): void
41 | {
42 | try {
43 | $email = MessageConverter::toEmail($message->getOriginalMessage());
44 | $envelope = $message->getEnvelope();
45 |
46 | // overrides from the envelope
47 | $email->from($envelope->getSender());
48 | $envelopeRecipients = $this->getEnvelopeRecipients($email, $envelope);
49 | if (!empty($envelopeRecipients)) {
50 | foreach ($envelopeRecipients as $envelopeRecipient) {
51 | $email->addTo($envelopeRecipient);
52 | }
53 | }
54 |
55 | $response = $this->emailsSendApiLayer->send($email);
56 |
57 | $body = ResponseHelper::toArray($response);
58 | $message->setMessageId(implode(',', $body['message_ids']));
59 | } catch (\Exception $e) {
60 | throw new RuntimeException(
61 | sprintf('Unable to send a message with the "%s" transport: ', __CLASS__) . $e->getMessage(), 0, $e
62 | );
63 | }
64 | }
65 |
66 | private function getEndpoint(): string
67 | {
68 | $inboxId = null;
69 | if ($this->emailsSendApiLayer instanceof SandboxEmails) {
70 | $inboxId = $this->emailsSendApiLayer->getInboxId();
71 | }
72 |
73 | return $this->config->getHost() . (null === $inboxId ? '' : '?inboxId=' . $inboxId);
74 | }
75 |
76 | private function getEnvelopeRecipients(Email $email, Envelope $envelope): array
77 | {
78 | return array_filter(
79 | $envelope->getRecipients(),
80 | static fn (Address $address) => false === in_array($address, array_merge($email->getTo(), $email->getCc(), $email->getBcc()), true)
81 | );
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/Bridge/Transport/MailtrapSdkTransportFactory.php:
--------------------------------------------------------------------------------
1 | getScheme(), $this->getSupportedSchemes())) {
28 | throw new UnsupportedSchemeException($dsn, 'mailtrap+sdk', $this->getSupportedSchemes());
29 | }
30 |
31 | $inboxId = !empty($dsn->getOption('inboxId')) ? (int) $dsn->getOption('inboxId') : null;
32 | $config = (new Config($this->getUser($dsn)))
33 | ->setHost('default' === $dsn->getHost() ? AbstractApi::SENDMAIL_TRANSACTIONAL_HOST : $dsn->getHost())
34 | ->setHttpClient(null === $this->client ? null : new Psr18Client($this->client))
35 | ;
36 |
37 | $emailsSendMailtrapClient = $this->getEmailsSendMailTrapClient($config);
38 | if ($emailsSendMailtrapClient instanceof MailtrapSandboxClient) {
39 | if (null === $inboxId) {
40 | throw new RuntimeException(
41 | 'You cannot send an email to a sandbox with an empty "inboxId" parameter. Example -> "MAILER_DSN=mailtrap+sdk://APIKEY@sandbox.api.mailtrap.io?inboxId=1234"'
42 | );
43 | }
44 |
45 | $emailsSendApiLayer = $emailsSendMailtrapClient->emails($inboxId);
46 | } else {
47 | $emailsSendApiLayer = $emailsSendMailtrapClient->emails();
48 | }
49 |
50 | return new MailtrapSdkTransport($emailsSendApiLayer, $config, $this->dispatcher, $this->logger);
51 | }
52 |
53 | protected function getSupportedSchemes(): array
54 | {
55 | return ['mailtrap+sdk'];
56 | }
57 |
58 | private function getEmailsSendMailTrapClient(Config $config): EmailsSendMailtrapClientInterface
59 | {
60 | $layer = $this->determineLayerNameByHost($config->getHost());
61 |
62 | return (new MailtrapClient($config))->{$layer}();
63 | }
64 |
65 | private function determineLayerNameByHost(string $host): string
66 | {
67 | $hostLayers = [
68 | AbstractApi::SENDMAIL_TRANSACTIONAL_HOST => MailtrapClient::LAYER_TRANSACTIONAL_SENDING,
69 | AbstractApi::SENDMAIL_BULK_HOST => MailtrapClient::LAYER_BULK_SENDING,
70 | AbstractApi::SENDMAIL_SANDBOX_HOST => MailtrapClient::LAYER_SANDBOX,
71 | ];
72 |
73 | foreach ($hostLayers as $hostKey => $layer) {
74 | if (stripos($host, $hostKey) !== false) {
75 | return $layer;
76 | }
77 | }
78 |
79 | throw new UnsupportedHostException(
80 | sprintf(
81 | 'The "%s" host is not supported. Only these are available: %s',
82 | $host,
83 | implode(
84 | ', ',
85 | [
86 | AbstractApi::SENDMAIL_TRANSACTIONAL_HOST,
87 | AbstractApi::SENDMAIL_BULK_HOST,
88 | AbstractApi::SENDMAIL_SANDBOX_HOST
89 | ]
90 | ))
91 | );
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/Config.php:
--------------------------------------------------------------------------------
1 | apiToken;
32 | }
33 |
34 | public function getHost(): ?string
35 | {
36 | return $this->host;
37 | }
38 |
39 | public function setHost(?string $host): self
40 | {
41 | $this->host = $host;
42 |
43 | return $this;
44 | }
45 |
46 | public function getHttpClientBuilder(): HttpClientBuilderInterface
47 | {
48 | if (null === $this->httpClientBuilder) {
49 | $this->httpClientBuilder = new HttpClientBuilder(
50 | $this->apiToken,
51 | $this->httpClient,
52 | $this->requestFactory,
53 | $this->streamFactory
54 | );
55 | }
56 |
57 | return $this->httpClientBuilder;
58 | }
59 |
60 | public function setHttpClientBuilder(?HttpClientBuilderInterface $httpClientBuilder): self
61 | {
62 | $this->httpClientBuilder = $httpClientBuilder;
63 |
64 | return $this;
65 | }
66 |
67 | public function setHttpClient(?ClientInterface $httpClient): self
68 | {
69 | $this->httpClient = $httpClient;
70 |
71 | return $this;
72 | }
73 |
74 | public function setRequestFactory(?RequestFactoryInterface $requestFactory): self
75 | {
76 | $this->requestFactory = $requestFactory;
77 |
78 | return $this;
79 | }
80 |
81 | public function setStreamFactory(?StreamFactoryInterface $streamFactory): self
82 | {
83 | $this->streamFactory = $streamFactory;
84 |
85 | return $this;
86 | }
87 |
88 | public function isResponseThrowOnError(): bool
89 | {
90 | return $this->responseThrowOnError;
91 | }
92 |
93 | /**
94 | * Throw an HttpException if the response returns a status other than 20x (default: true)
95 | * otherwise a response will be returned
96 | *
97 | * @param bool $responseThrowOnError
98 | *
99 | * @return $this
100 | */
101 | public function setResponseThrowOnError(bool $responseThrowOnError): self
102 | {
103 | $this->responseThrowOnError = $responseThrowOnError;
104 |
105 | return $this;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/ConfigInterface.php:
--------------------------------------------------------------------------------
1 | email;
27 | }
28 |
29 | public function getFields(): array
30 | {
31 | return $this->fields;
32 | }
33 |
34 | public function getListIds(): array
35 | {
36 | return $this->listIds;
37 | }
38 |
39 | public function toArray(): array
40 | {
41 | return [
42 | 'email' => $this->getEmail(),
43 | 'fields' => $this->getFields(),
44 | 'list_ids' => $this->getListIds(),
45 | ];
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/DTO/Request/Contact/UpdateContact.php:
--------------------------------------------------------------------------------
1 | email;
34 | }
35 |
36 | public function getFields(): array
37 | {
38 | return $this->fields;
39 | }
40 |
41 | public function getListIdsIncluded(): array
42 | {
43 | return $this->listIdsIncluded;
44 | }
45 |
46 | public function getListIdsExcluded(): array
47 | {
48 | return $this->listIdsExcluded;
49 | }
50 |
51 | public function getUnsubscribed(): ?bool
52 | {
53 | return $this->unsubscribed;
54 | }
55 |
56 | public function toArray(): array
57 | {
58 | return array_filter(
59 | [
60 | 'email' => $this->getEmail(),
61 | 'fields' => $this->getFields(),
62 | 'list_ids_included' => $this->getListIdsIncluded(),
63 | 'list_ids_excluded' => $this->getListIdsExcluded(),
64 | 'unsubscribed' => $this->getUnsubscribed(),
65 | ],
66 | fn($value) => $value !== null
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/DTO/Request/Inbox.php:
--------------------------------------------------------------------------------
1 | name;
22 | }
23 |
24 | /**
25 | * @return string|null
26 | */
27 | public function getEmailUsername(): ?string
28 | {
29 | return $this->emailUsername;
30 | }
31 |
32 | public function toArray(): array
33 | {
34 | $array = [];
35 |
36 | if (!empty($this->getName())) {
37 | $array['name'] = $this->getName();
38 | }
39 |
40 | if (!empty($this->getEmailUsername())) {
41 | $array['email_username'] = $this->getEmailUsername();
42 | }
43 |
44 | return $array;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/DTO/Request/Permission/CreateOrUpdatePermission.php:
--------------------------------------------------------------------------------
1 | resourceId = (string) $resourceId;
24 | $this->resourceType = $resourceType;
25 | $this->accessLevel = (string) $accessLevel;
26 | }
27 |
28 | public function getResourceId(): string
29 | {
30 | return $this->resourceId;
31 | }
32 |
33 | public function getResourceType(): string
34 | {
35 | return $this->resourceType;
36 | }
37 |
38 | public function getAccessLevel(): string
39 | {
40 | return $this->accessLevel;
41 | }
42 |
43 | public function toArray(): array
44 | {
45 | return [
46 | 'resource_id' => $this->getResourceId(),
47 | 'resource_type' => $this->getResourceType(),
48 | 'access_level' => $this->getAccessLevel(),
49 | ];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/DTO/Request/Permission/DestroyPermission.php:
--------------------------------------------------------------------------------
1 | resourceId = (string) $resourceId;
22 | $this->resourceType = $resourceType;
23 | }
24 |
25 | public function getResourceId(): string
26 | {
27 | return $this->resourceId;
28 | }
29 |
30 | public function getResourceType(): string
31 | {
32 | return $this->resourceType;
33 | }
34 |
35 | public function toArray(): array
36 | {
37 | return [
38 | 'resource_id' => $this->getResourceId(),
39 | 'resource_type' => $this->getResourceType(),
40 | '_destroy' => true,
41 | ];
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/DTO/Request/Permission/PermissionInterface.php:
--------------------------------------------------------------------------------
1 | add($permission);
21 | }
22 | }
23 |
24 | public function add(PermissionInterface $permission): Permissions
25 | {
26 | $this->permissions[] = $permission;
27 |
28 | return $this;
29 | }
30 |
31 | /**
32 | * @return PermissionInterface[]
33 | */
34 | public function getAll(): array
35 | {
36 | return $this->permissions;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/DTO/Request/RequestInterface.php:
--------------------------------------------------------------------------------
1 | setValue($value);
25 | }
26 |
27 | /**
28 | * @param string $body
29 | */
30 | public function setBody($body): void
31 | {
32 | $this->setValue($body);
33 | }
34 |
35 | /**
36 | * @psalm-suppress MethodSignatureMismatch
37 | */
38 | public function getBody(): string
39 | {
40 | return $this->getValue();
41 | }
42 |
43 | /**
44 | * Get the (unencoded) value of this header.
45 | */
46 | public function getValue(): string
47 | {
48 | return $this->value;
49 | }
50 |
51 | /**
52 | * Set the (unencoded) value of this header.
53 | */
54 | public function setValue(string $value): void
55 | {
56 | $this->value = $value;
57 | }
58 |
59 | /**
60 | * Get the value of this header prepared for rendering.
61 | */
62 | public function getBodyAsString(): string
63 | {
64 | return $this->encodeWords($this, $this->value);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/EmailHeader/CustomHeaderInterface.php:
--------------------------------------------------------------------------------
1 | setValue($value);
27 | }
28 |
29 | /**
30 | * @param string $body
31 | */
32 | public function setBody($body): void
33 | {
34 | $this->setValue($body);
35 | }
36 |
37 | /**
38 | * @psalm-suppress MethodSignatureMismatch
39 | */
40 | public function getBody(): string
41 | {
42 | return $this->getValue();
43 | }
44 |
45 | /**
46 | * Get the (unencoded) value of this header.
47 | */
48 | public function getValue(): string
49 | {
50 | return $this->value;
51 | }
52 |
53 | /**
54 | * Set the (unencoded) value of this header.
55 | */
56 | public function setValue(string $value): void
57 | {
58 | $this->value = $value;
59 | }
60 |
61 | public function getNameWithoutPrefix(): string
62 | {
63 | return substr($this->getName(), strlen(self::NAME_PREFIX));
64 | }
65 |
66 | /**
67 | * Get the value of this header prepared for rendering.
68 | */
69 | public function getBodyAsString(): string
70 | {
71 | return $this->encodeWords($this, $this->value);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/EmailHeader/Template/TemplateUuidHeader.php:
--------------------------------------------------------------------------------
1 | setValue($value);
25 | }
26 |
27 | /**
28 | * @param string $body
29 | */
30 | public function setBody($body): void
31 | {
32 | $this->setValue($body);
33 | }
34 |
35 | /**
36 | * @psalm-suppress MethodSignatureMismatch
37 | */
38 | public function getBody(): string
39 | {
40 | return $this->getValue();
41 | }
42 |
43 | /**
44 | * Get the (unencoded) value of this header.
45 | */
46 | public function getValue(): string
47 | {
48 | return $this->value;
49 | }
50 |
51 | /**
52 | * Set the (unencoded) value of this header.
53 | */
54 | public function setValue(string $value): void
55 | {
56 | $this->value = $value;
57 | }
58 |
59 | /**
60 | * Get the value of this header prepared for rendering.
61 | */
62 | public function getBodyAsString(): string
63 | {
64 | return $this->encodeWords($this, $this->value);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/EmailHeader/Template/TemplateVariableHeader.php:
--------------------------------------------------------------------------------
1 | setValue($value);
29 | }
30 |
31 | public function setBody(mixed $body): void
32 | {
33 | $this->setValue($body);
34 | }
35 |
36 | /**
37 | * @psalm-suppress MethodSignatureMismatch
38 | */
39 | public function getBody(): mixed
40 | {
41 | return $this->getValue();
42 | }
43 |
44 | /**
45 | * Get the (unencoded) value of this header.
46 | */
47 | public function getValue(): mixed
48 | {
49 | return $this->value;
50 | }
51 |
52 | /**
53 | * Set the (unencoded) value of this header.
54 | */
55 | public function setValue(mixed $value): void
56 | {
57 | $this->value = $value;
58 | }
59 |
60 | public function getNameWithoutPrefix(): string
61 | {
62 | return substr($this->getName(), strlen(self::NAME_PREFIX));
63 | }
64 |
65 | public function getBodyAsString(): string
66 | {
67 | throw new RuntimeException(__METHOD__ . ' method is not supported for this type of header');
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/EmailsSendMailtrapClientInterface.php:
--------------------------------------------------------------------------------
1 | 'Bad request. Fix errors listed in response before retrying.',
18 | 401 => 'Unauthorized. Make sure you are sending correct credentials with the request before retrying.',
19 | 403 => 'Forbidden. Make sure domain verification process is completed or check your permissions.',
20 | 404 => 'The requested entity has not been found.',
21 | ];
22 |
23 | public static function createFromResponse(ResponseInterface $response): HttpClientException
24 | {
25 | $errorMsg = '';
26 | $statusCode = $response->getStatusCode();
27 |
28 | try {
29 | $body = ResponseHelper::toArray($response);
30 | } catch (JsonException|InvalidTypeException) {
31 | $body['error'] = $response->getBody()->__toString();
32 | }
33 |
34 | if (isset(self::ERROR_PREFIXES[$statusCode])) {
35 | $errorMsg .= self::ERROR_PREFIXES[$statusCode] . ' ';
36 | }
37 |
38 | $errorMsg .= trim('Errors: ' . self::getErrorMsg(!empty($body['errors']) ? $body['errors'] : $body['error']));
39 |
40 | return new self (
41 | !empty($errorMsg)
42 | ? $errorMsg
43 | : sprintf('HTTP response code ("%d") received from the API server (no error info)', $statusCode),
44 | $statusCode
45 | );
46 | }
47 |
48 | /**
49 | * It can be different structure of errors in the response...
50 | *
51 | * Examples:
52 | * {"errors": ["'to' address is required", "'subject' is required"]} 400 errorS (array)
53 | * {"error": "Incorrect API token"} 401 error (string)
54 | * {"errors": "Access forbidden"} 403 errorS (string)
55 | * {"error": "Not found"} 404 error (string)
56 | * {"errors": {"name":["is too short (minimum is 2 characters)"]}} 422 errorS (array with key name)
57 | *
58 | *
59 | * @param array|string $errors
60 | *
61 | * @return string
62 | */
63 | public static function getErrorMsg(array|string $errors): string
64 | {
65 | $errorMsg = '';
66 | if (is_array($errors)) {
67 | foreach ($errors as $key => $value) {
68 | if (is_string($key)) {
69 | // add name of field
70 | $errorMsg .= $key . ' -> ';
71 | }
72 |
73 | $errorMsg .= self::getErrorMsg($value);
74 | }
75 | } else {
76 | $errorMsg .= $errors . '. ';
77 | }
78 |
79 | return $errorMsg;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Exception/HttpException.php:
--------------------------------------------------------------------------------
1 | getHeaderLine('Content-Type'), 'application/json')) {
22 | // This can happen when the URL structure changes and the response returns a 404 HTML page. (rare case)
23 | throw new InvalidTypeException(sprintf(
24 | 'Invalid content type in response. "%s" type expected, but received "%s"',
25 | 'application/json',
26 | $response->getHeaderLine('Content-Type')
27 | ));
28 | }
29 |
30 | return json_decode($response->getBody()->__toString(), true, 512, JSON_THROW_ON_ERROR);
31 | }
32 |
33 | public static function toString(ResponseInterface $response): string
34 | {
35 | return $response->getBody()->__toString();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/HttpClient/HttpClientBuilder.php:
--------------------------------------------------------------------------------
1 | httpClient = $httpClient ?? Psr18ClientDiscovery::find();
35 | $this->requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory();
36 | $this->streamFactory = $streamFactory ?? Psr17FactoryDiscovery::findStreamFactory();
37 | }
38 |
39 | public function getHttpClient(): ClientInterface
40 | {
41 | if (null === $this->pluginClient) {
42 | $plugins = [
43 | new Plugin\HeaderDefaultsPlugin([
44 | 'User-Agent' => 'mailtrap-php (https://github.com/railsware/mailtrap-php)',
45 | 'Content-Type' => 'application/json',
46 | ]),
47 | new Plugin\AuthenticationPlugin(
48 | new Bearer($this->apiToken)
49 | )
50 | ];
51 | $this->pluginClient = new HttpMethodsClient(
52 | (new PluginClientFactory())->createClient($this->httpClient, $plugins),
53 | $this->requestFactory,
54 | $this->streamFactory
55 | );
56 | }
57 |
58 | return $this->pluginClient;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/HttpClient/HttpClientBuilderInterface.php:
--------------------------------------------------------------------------------
1 | Api\BulkSending\Emails::class,
16 | ];
17 | }
18 |
--------------------------------------------------------------------------------
/src/MailtrapClient.php:
--------------------------------------------------------------------------------
1 | MailtrapGeneralClient::class,
29 | self::LAYER_SANDBOX => MailtrapSandboxClient::class,
30 | self::LAYER_TRANSACTIONAL_SENDING => MailtrapSendingClient::class,
31 | self::LAYER_BULK_SENDING => MailtrapBulkSendingClient::class,
32 | ];
33 |
34 | public static function initSendingEmails(
35 | string $apiKey,
36 | bool $isBulk = false,
37 | bool $isSandbox = false,
38 | ?int $inboxId = null,
39 | ): EmailsSendApiInterface {
40 | $client = new self(new Config($apiKey));
41 |
42 | if ($isBulk && $isSandbox) {
43 | throw new InvalidArgumentException('Bulk mode is not applicable for sandbox API');
44 | }
45 |
46 | if ($isSandbox) {
47 | return $client->sandbox()->emails($inboxId);
48 | }
49 |
50 | if ($isBulk) {
51 | return $client->bulkSending()->emails();
52 | }
53 |
54 | return $client->sending()->emails();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/MailtrapClientInterface.php:
--------------------------------------------------------------------------------
1 | Api\General\Account::class,
19 | 'users' => Api\General\User::class,
20 | 'permissions' => Api\General\Permission::class,
21 | 'contacts' => Api\General\Contact::class,
22 | ];
23 | }
24 |
--------------------------------------------------------------------------------
/src/MailtrapSandboxClient.php:
--------------------------------------------------------------------------------
1 | Api\Sandbox\Emails::class,
20 | 'projects' => Api\Sandbox\Project::class,
21 | 'inboxes' => Api\Sandbox\Inbox::class,
22 | 'attachments' => Api\Sandbox\Attachment::class,
23 | 'messages' => Api\Sandbox\Message::class,
24 | ];
25 | }
26 |
--------------------------------------------------------------------------------
/src/MailtrapSendingClient.php:
--------------------------------------------------------------------------------
1 | Api\Sending\Emails::class,
16 | ];
17 | }
18 |
--------------------------------------------------------------------------------
/src/Mime/MailtrapEmail.php:
--------------------------------------------------------------------------------
1 | getHeaders()->has(TemplateUuidHeader::VAR_NAME)) {
24 | $this->getHeaders()->remove(TemplateUuidHeader::VAR_NAME);
25 | }
26 |
27 | $this->getHeaders()->add(new TemplateUuidHeader($templateUuid));
28 |
29 | return $this;
30 | }
31 |
32 | public function templateVariable(string $name, mixed $value): self
33 | {
34 | $this->getHeaders()->add(new TemplateVariableHeader($name, $value));
35 |
36 | return $this;
37 | }
38 |
39 | public function templateVariables(array $variables): self
40 | {
41 | foreach ($variables as $name => $value) {
42 | $this->templateVariable($name, $value);
43 | }
44 |
45 | return $this;
46 | }
47 |
48 | public function category(string $category): self
49 | {
50 | // Only one category is allowed
51 | if ($this->getHeaders()->has(CategoryHeader::VAR_NAME)) {
52 | $this->getHeaders()->remove(CategoryHeader::VAR_NAME);
53 | }
54 |
55 | $this->getHeaders()->add(new CategoryHeader($category));
56 |
57 | return $this;
58 | }
59 |
60 | public function customVariable(string $name, string $value): self
61 | {
62 | $this->getHeaders()->add(new CustomVariableHeader($name, $value));
63 |
64 | return $this;
65 | }
66 |
67 | public function customVariables(array $variables): self
68 | {
69 | foreach ($variables as $name => $value) {
70 | $this->customVariable($name, $value);
71 | }
72 |
73 | return $this;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tests/Api/BulkSending/BulkEmailsTest.php:
--------------------------------------------------------------------------------
1 | email = $this->getMockBuilder(Emails::class)
23 | ->onlyMethods(['httpPost'])
24 | ->setConstructorArgs([$this->getConfigMock()])
25 | ->getMock()
26 | ;
27 | }
28 |
29 | protected function tearDown(): void
30 | {
31 | $this->email = null;
32 |
33 | parent::tearDown();
34 | }
35 |
36 | protected function getHost(): string
37 | {
38 | return AbstractApi::SENDMAIL_BULK_HOST;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/Api/General/AccountTest.php:
--------------------------------------------------------------------------------
1 | account = $this->getMockBuilder(Account::class)
31 | ->onlyMethods(['httpGet'])
32 | ->setConstructorArgs([$this->getConfigMock()])
33 | ->getMock()
34 | ;
35 | }
36 |
37 | protected function tearDown(): void
38 | {
39 | $this->account = null;
40 |
41 | parent::tearDown();
42 | }
43 |
44 | public function testValidGetAll(): void
45 | {
46 | $expectedData = [
47 | [
48 | "id" => 26730,
49 | "name" => "James",
50 | "access_levels" => [
51 | 100
52 | ]
53 | ],
54 | [
55 | "id" => 26731,
56 | "name" => "John",
57 | "access_levels" => [
58 | 1000
59 | ]
60 | ]
61 | ];
62 |
63 | $this->account->expects($this->once())
64 | ->method('httpGet')
65 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts')
66 | ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($expectedData)));
67 |
68 | $response = $this->account->getList();
69 | $responseData = ResponseHelper::toArray($response);
70 |
71 | $this->assertInstanceOf(Response::class, $response);
72 | $this->assertCount(2, $responseData);
73 | $this->assertArrayHasKey('access_levels', array_shift($responseData));
74 | }
75 |
76 | public function testInvalidGetAll(): void
77 | {
78 | $expectedData = ['error' => 'Incorrect API token'];
79 |
80 | $this->account->expects($this->once())
81 | ->method('httpGet')
82 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts')
83 | ->willReturn(new Response(401, ['Content-Type' => 'application/json'], json_encode($expectedData)));
84 |
85 | $this->expectException(HttpClientException::class);
86 | $this->expectExceptionMessage(
87 | 'Unauthorized. Make sure you are sending correct credentials with the request before retrying. Errors: Incorrect API token.'
88 | );
89 |
90 | $this->account->getList();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/tests/Api/General/PermissionTest.php:
--------------------------------------------------------------------------------
1 | permission = $this->getMockBuilder(Permission::class)
34 | ->onlyMethods(['httpGet', 'httpPut'])
35 | ->setConstructorArgs([$this->getConfigMock(), self::FAKE_ACCOUNT_ID])
36 | ->getMock()
37 | ;
38 | }
39 |
40 | protected function tearDown(): void
41 | {
42 | $this->permission = null;
43 |
44 | parent::tearDown();
45 | }
46 |
47 | public function testValidGetResources(): void
48 | {
49 | $this->permission->expects($this->once())
50 | ->method('httpGet')
51 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/permissions/resources')
52 | ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedData())));
53 |
54 | $response = $this->permission->getResources();
55 | $responseData = ResponseHelper::toArray($response);
56 |
57 | $this->assertInstanceOf(Response::class, $response);
58 | $this->assertCount(2, $responseData);
59 | $this->assertArrayHasKey('resources', array_shift($responseData));
60 | }
61 |
62 | public function test401InvalidGetResources(): void
63 | {
64 | $this->permission->expects($this->once())
65 | ->method('httpGet')
66 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/permissions/resources')
67 | ->willReturn(new Response(401, ['Content-Type' => 'application/json'], json_encode(['error' => 'Incorrect API token'])));
68 |
69 | $this->expectException(HttpClientException::class);
70 | $this->expectExceptionMessage(
71 | 'Unauthorized. Make sure you are sending correct credentials with the request before retrying. Errors: Incorrect API token.'
72 | );
73 |
74 | $this->permission->getResources();
75 | }
76 |
77 | public function test403InvalidGetResources(): void
78 | {
79 | $this->permission->expects($this->once())
80 | ->method('httpGet')
81 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/permissions/resources')
82 | ->willReturn(new Response(403, ['Content-Type' => 'application/json'], json_encode(['errors' => 'Access forbidden'])));
83 |
84 | $this->expectException(HttpClientException::class);
85 | $this->expectExceptionMessage(
86 | 'Forbidden. Make sure domain verification process is completed or check your permissions. Errors: Access forbidden.'
87 | );
88 |
89 | $this->permission->getResources();
90 | }
91 |
92 | /**
93 | * @dataProvider validUpdateDataProvider
94 | */
95 | public function testValidUpdate($permissions): void
96 | {
97 | $this->permission->expects($this->once())
98 | ->method('httpPut')
99 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/account_accesses/' . self::FAKE_ACCOUNT_ACCESS_ID . '/permissions/bulk')
100 | ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode(['message' => 'Permissions have been updated!'])));
101 |
102 | $response = $this->permission->update(self::FAKE_ACCOUNT_ACCESS_ID, $permissions);
103 | $responseData = ResponseHelper::toArray($response);
104 |
105 | $this->assertInstanceOf(Response::class, $response);
106 | $this->assertArrayHasKey('message', $responseData);
107 | $this->assertEquals('Permissions have been updated!', $responseData['message']);
108 | }
109 |
110 | public function testInvalidUpdate(): void
111 | {
112 | $this->expectException(RuntimeException::class);
113 | $this->expectExceptionMessage(
114 | 'At least one "permission" object should be added to manage user or token'
115 | );
116 |
117 | $emptyPermissions = new Permissions();
118 | $this->permission->update(self::FAKE_ACCOUNT_ACCESS_ID, $emptyPermissions);
119 | }
120 |
121 | /**
122 | * @dataProvider validUpdateDataProvider
123 | */
124 | public function testValidGetPayload($permissions, $expectedResult): void
125 | {
126 | $method = new \ReflectionMethod(Permission::class, 'getPayload');
127 | $method->setAccessible(true);
128 | $payload = $method->invoke(new Permission($this->getConfigMock(), self::FAKE_ACCOUNT_ID), $permissions);
129 |
130 | $this->assertEquals($expectedResult, $payload);
131 | }
132 |
133 | public function validUpdateDataProvider(): iterable
134 | {
135 | // create/update
136 | yield [
137 | new Permissions(
138 | new CreateOrUpdatePermission(1000001, PermissionInterface::TYPE_PROJECT, 10),
139 | ),
140 | [
141 | [
142 | "resource_id" => "1000001",
143 | "resource_type" => "project",
144 | "access_level" => "10"
145 | ]
146 | ]
147 | ];
148 |
149 | // destroy
150 | yield [
151 | new Permissions(
152 | new DestroyPermission(1000009, PermissionInterface::TYPE_PROJECT),
153 | ),
154 | [
155 | [
156 | "resource_id" => "1000009",
157 | "resource_type" => "project",
158 | "_destroy" => true
159 | ]
160 | ]
161 | ];
162 |
163 | // create/update and destroy together
164 | yield [
165 | new Permissions(
166 | new CreateOrUpdatePermission(1000001, PermissionInterface::TYPE_PROJECT, 10),
167 | new CreateOrUpdatePermission(2000002, PermissionInterface::TYPE_INBOX, 100),
168 | new DestroyPermission(1000009, PermissionInterface::TYPE_PROJECT),
169 | ),
170 | [
171 | [
172 | "resource_id" => "1000001",
173 | "resource_type" => "project",
174 | "access_level" => "10"
175 | ],
176 | [
177 | "resource_id" => "2000002",
178 | "resource_type" => "inbox",
179 | "access_level" => "100"
180 | ],
181 | [
182 | "resource_id" => "1000009",
183 | "resource_type" => "project",
184 | "_destroy" => true
185 | ]
186 | ]
187 | ];
188 | }
189 |
190 | private function getExpectedData()
191 | {
192 | return [
193 | [
194 | "id" => 4001,
195 | "name" => "My First Project",
196 | "type" => "project",
197 | "access_level" => 1,
198 | "resources" => [
199 | [
200 | "id" => 3816,
201 | "name" => "My First Inbox",
202 | "type" => "inbox",
203 | "access_level" => 100,
204 | "resources" => [
205 | ]
206 | ]
207 | ]
208 | ],
209 | [
210 | "id" => 4002,
211 | "name" => "My Second Project",
212 | "type" => "project",
213 | "access_level" => 1,
214 | "resources" => [
215 | [
216 | "id" => 3820,
217 | "name" => "My Second Inbox",
218 | "type" => "inbox",
219 | "access_level" => 100,
220 | "resources" => [
221 | ]
222 | ]
223 | ]
224 | ]
225 | ];
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/tests/Api/General/UserTest.php:
--------------------------------------------------------------------------------
1 | user = $this->getMockBuilder(User::class)
32 | ->onlyMethods(['httpGet', 'httpDelete'])
33 | ->setConstructorArgs([$this->getConfigMock(), self::FAKE_ACCOUNT_ID])
34 | ->getMock()
35 | ;
36 | }
37 |
38 | protected function tearDown(): void
39 | {
40 | $this->user = null;
41 |
42 | parent::tearDown();
43 | }
44 |
45 | public function testValidGetListWithoutFilters(): void
46 | {
47 | $this->user->expects($this->once())
48 | ->method('httpGet')
49 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/account_accesses')
50 | ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedData())));
51 |
52 | $response = $this->user->getList();
53 | $responseData = ResponseHelper::toArray($response);
54 |
55 | $this->assertInstanceOf(Response::class, $response);
56 | $this->assertCount(3, $responseData);
57 | $this->assertArrayHasKey('specifier_type', array_shift($responseData));
58 | }
59 |
60 | public function testValidGetListWithFilters(): void
61 | {
62 | $inboxIds = [2090015, 2157025];
63 | $projectIds = [1515592];
64 | $expectedResult = $this->getExpectedData();
65 |
66 | $this->user->expects($this->once())
67 | ->method('httpGet')
68 | ->with(
69 | AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/account_accesses',
70 | ['inbox_ids' => $inboxIds, 'project_ids' => $projectIds]
71 | )
72 | ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode(array_slice($expectedResult, 1))));
73 |
74 | $response = $this->user->getList($inboxIds, $projectIds);
75 | $responseData = ResponseHelper::toArray($response);
76 |
77 | $this->assertInstanceOf(Response::class, $response);
78 | $this->assertCount(2, $responseData);
79 | $this->assertArrayHasKey('specifier_type', array_shift($responseData));
80 | }
81 |
82 | public function test401InvalidGetList(): void
83 | {
84 | $this->user->expects($this->once())
85 | ->method('httpGet')
86 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/account_accesses')
87 | ->willReturn(new Response(401, ['Content-Type' => 'application/json'], json_encode(['error' => 'Incorrect API token'])));
88 |
89 | $this->expectException(HttpClientException::class);
90 | $this->expectExceptionMessage(
91 | 'Unauthorized. Make sure you are sending correct credentials with the request before retrying. Errors: Incorrect API token.'
92 | );
93 |
94 | $this->user->getList();
95 | }
96 |
97 | public function test403InvalidGetList(): void
98 | {
99 | $this->user->expects($this->once())
100 | ->method('httpGet')
101 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/account_accesses')
102 | ->willReturn(new Response(403, ['Content-Type' => 'application/json'], json_encode(['errors' => 'Access forbidden'])));
103 |
104 | $this->expectException(HttpClientException::class);
105 | $this->expectExceptionMessage(
106 | 'Forbidden. Make sure domain verification process is completed or check your permissions. Errors: Access forbidden.'
107 | );
108 |
109 | $this->user->getList();
110 | }
111 |
112 | public function testValidRemove(): void
113 | {
114 | $this->user->expects($this->once())
115 | ->method('httpDelete')
116 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/account_accesses/' . self::FAKE_ACCOUNT_ACCESS_ID)
117 | ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode(['id' => self::FAKE_ACCOUNT_ACCESS_ID])));
118 |
119 | $response = $this->user->delete(self::FAKE_ACCOUNT_ACCESS_ID);
120 | $responseData = ResponseHelper::toArray($response);
121 |
122 | $this->assertInstanceOf(Response::class, $response);
123 | $this->assertArrayHasKey('id', $responseData);
124 | $this->assertEquals(self::FAKE_ACCOUNT_ACCESS_ID, $responseData['id']);
125 | }
126 |
127 | public function test401InvalidRemove(): void
128 | {
129 | $this->user->expects($this->once())
130 | ->method('httpDelete')
131 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/account_accesses/' . self::FAKE_ACCOUNT_ACCESS_ID)
132 | ->willReturn(new Response(401, ['Content-Type' => 'application/json'], json_encode(['error' => 'Incorrect API token'])));
133 |
134 | $this->expectException(HttpClientException::class);
135 | $this->expectExceptionMessage(
136 | 'Unauthorized. Make sure you are sending correct credentials with the request before retrying. Errors: Incorrect API token.'
137 | );
138 |
139 | $this->user->delete(self::FAKE_ACCOUNT_ACCESS_ID);
140 | }
141 |
142 | public function test403InvalidRemove(): void
143 | {
144 | $this->user->expects($this->once())
145 | ->method('httpDelete')
146 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/account_accesses/' . self::FAKE_ACCOUNT_ACCESS_ID)
147 | ->willReturn(new Response(403, ['Content-Type' => 'application/json'], json_encode(['error' => 'Access forbidden'])));
148 |
149 | $this->expectException(HttpClientException::class);
150 | $this->expectExceptionMessage(
151 | 'Forbidden. Make sure domain verification process is completed or check your permissions. Errors: Access forbidden.'
152 | );
153 |
154 | $this->user->delete(self::FAKE_ACCOUNT_ACCESS_ID);
155 | }
156 |
157 | public function test404InvalidRemove(): void
158 | {
159 | $this->user->expects($this->once())
160 | ->method('httpDelete')
161 | ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/account_accesses/' . self::FAKE_ACCOUNT_ACCESS_ID)
162 | ->willReturn(new Response(404, ['Content-Type' => 'application/json'], json_encode(['error' => 'Not Found'])));
163 |
164 | $this->expectException(HttpClientException::class);
165 | $this->expectExceptionMessage(
166 | 'The requested entity has not been found. Errors: Not Found.'
167 | );
168 |
169 | $this->user->delete(self::FAKE_ACCOUNT_ACCESS_ID);
170 | }
171 |
172 | private function getExpectedData(): array
173 | {
174 | return [
175 | [
176 | "id" => 4773,
177 | "specifier_type" => "User",
178 | "resources" => [
179 | [
180 | "resource_type" => "account",
181 | "resource_id" => 3229,
182 | "access_level" => 1000
183 | ]
184 | ],
185 | "specifier" => [
186 | "id" => 2077,
187 | "email" => "james@mailtrap.io",
188 | "name" => "James"
189 | ],
190 | "permissions" => [
191 | "can_read" => false,
192 | "can_update" => false,
193 | "can_destroy" => false,
194 | "can_leave" => false
195 | ]
196 | ],
197 | [
198 | "id" => 4775,
199 | "specifier_type" => "User",
200 | "resources" => [
201 | [
202 | "resource_type" => "account",
203 | "resource_id" => 3229,
204 | "access_level" => 100
205 | ]
206 | ],
207 | "specifier" => [
208 | "id" => 3230,
209 | "email" => "john@mailtrap.io",
210 | "name" => "John"
211 | ],
212 | "permissions" => [
213 | "can_read" => false,
214 | "can_update" => true,
215 | "can_destroy" => true,
216 | "can_leave" => false
217 | ]
218 | ],
219 | [
220 | "id" => 4776,
221 | "specifier_type" => "Invite",
222 | "resources" => [
223 | [
224 | "resource_type" => "project",
225 | "resource_id" => 3938,
226 | "access_level" => 10
227 | ],
228 | [
229 | "resource_type" => "inbox",
230 | "resource_id" => 3757,
231 | "access_level" => 100
232 | ]
233 | ],
234 | "specifier" => [
235 | "id" => 64,
236 | "email" => "mary@mailtrap.io"
237 | ],
238 | "permissions" => [
239 | "can_read" => false,
240 | "can_update" => true,
241 | "can_destroy" => true,
242 | "can_leave" => false
243 | ]
244 | ]
245 | ];
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/tests/Api/Sandbox/AttachmentTest.php:
--------------------------------------------------------------------------------
1 | attachment = $this->getMockBuilder(Attachment::class)
29 | ->onlyMethods(['httpGet'])
30 | ->setConstructorArgs([$this->getConfigMock(), self::FAKE_ACCOUNT_ID, self::FAKE_INBOX_ID])
31 | ->getMock()
32 | ;
33 | }
34 |
35 | protected function tearDown(): void
36 | {
37 | $this->attachment = null;
38 |
39 | parent::tearDown();
40 | }
41 |
42 | public function testGetMessageAttachments(): void
43 | {
44 | $attachmentType = 'inline';
45 | $this->attachment->expects($this->once())
46 | ->method('httpGet')
47 | ->with(
48 | sprintf(
49 | '%s/api/accounts/%s/inboxes/%s/messages/%s/attachments',
50 | AbstractApi::DEFAULT_HOST,
51 | self::FAKE_ACCOUNT_ID,
52 | self::FAKE_INBOX_ID,
53 | self::FAKE_MESSAGE_ID,
54 | ),
55 | [
56 | 'attachment_type' => $attachmentType
57 | ]
58 | )
59 | ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedData())));
60 |
61 | $response = $this->attachment->getMessageAttachments(
62 | self::FAKE_MESSAGE_ID,
63 | $attachmentType
64 | );
65 | $responseData = ResponseHelper::toArray($response);
66 |
67 | $this->assertInstanceOf(Response::class, $response);
68 | $this->assertCount(1, $responseData);
69 | $this->assertArrayHasKey('filename', array_shift($responseData));
70 | }
71 |
72 | public function testGetMessageAttachment(): void
73 | {
74 | $this->attachment->expects($this->once())
75 | ->method('httpGet')
76 | ->with(sprintf(
77 | '%s/api/accounts/%s/inboxes/%s/messages/%s/attachments/%s',
78 | AbstractApi::DEFAULT_HOST,
79 | self::FAKE_ACCOUNT_ID,
80 | self::FAKE_INBOX_ID,
81 | self::FAKE_MESSAGE_ID,
82 | self::FAKE_MESSAGE_ATTACHMENT_ID
83 | ))
84 | ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedData())));
85 |
86 | $response = $this->attachment->getMessageAttachment(
87 | self::FAKE_MESSAGE_ID,
88 | self::FAKE_MESSAGE_ATTACHMENT_ID
89 | );
90 | $responseData = ResponseHelper::toArray($response);
91 |
92 | $this->assertInstanceOf(Response::class, $response);
93 | $this->assertCount(1, $responseData);
94 | $this->assertArrayHasKey('filename', array_shift($responseData));
95 | }
96 |
97 | private function getExpectedData(): array
98 | {
99 | return [
100 | [
101 | "id" => self::FAKE_MESSAGE_ATTACHMENT_ID,
102 | "message_id" => self::FAKE_MESSAGE_ID,
103 | "filename" => "test.csv",
104 | "attachment_type" => "inline",
105 | "content_type" => "plain/text",
106 | "content_id" => null,
107 | "transfer_encoding" => null,
108 | "attachment_size" => 0,
109 | "created_at" => "2022-06-02T19:25:54.827Z",
110 | "updated_at" => "2022-06-02T19:25:54.827Z",
111 | "attachment_human_size" => "0 Bytes",
112 | "download_path" => "/api/accounts/3831/inboxes/4394/messages/457/attachments/67/download"
113 | ]
114 | ];
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/tests/Api/Sending/EmailsTest.php:
--------------------------------------------------------------------------------
1 | email = $this->getMockBuilder(Emails::class)
23 | ->onlyMethods(['httpPost'])
24 | ->setConstructorArgs([$this->getConfigMock()])
25 | ->getMock()
26 | ;
27 | }
28 |
29 | protected function tearDown(): void
30 | {
31 | $this->email = null;
32 |
33 | parent::tearDown();
34 | }
35 |
36 | protected function getHost(): string
37 | {
38 | return AbstractApi::SENDMAIL_TRANSACTIONAL_HOST;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/Bridge/Transport/MailtrapSdkTransportFactoryTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped(
27 | 'The "MailtrapSdkTransportFactoryTest" tests skipped, because "symfony/mailer" package is not installed.'
28 | );
29 | }
30 |
31 | parent::setUp();
32 | }
33 |
34 | public function getFactory(): TransportFactoryInterface
35 | {
36 | return new MailtrapSdkTransportFactory($this->getDispatcher(), $this->getClient(), $this->getLogger());
37 | }
38 |
39 | public function supportsProvider(): iterable
40 | {
41 | yield [
42 | new Dsn('mailtrap+sdk', 'default'),
43 | true,
44 | ];
45 | }
46 |
47 | public function createProvider(): iterable
48 | {
49 | $dispatcher = $this->getDispatcher();
50 | $logger = $this->getLogger();
51 | $psrClient = new Psr18Client($this->getClient());
52 | $sendConfig = (new Config(self::USER))
53 | ->setHttpClient($psrClient)
54 | ->setHost(AbstractApi::SENDMAIL_TRANSACTIONAL_HOST);
55 | $sandboxConfig = (new Config(self::USER))
56 | ->setHttpClient($psrClient)
57 | ->setHost(AbstractApi::SENDMAIL_SANDBOX_HOST);
58 | $bulkConfig = (new Config(self::USER))
59 | ->setHttpClient($psrClient)
60 | ->setHost(AbstractApi::SENDMAIL_BULK_HOST);
61 | $inboxId = 1234;
62 |
63 | yield [
64 | new Dsn('mailtrap+sdk', 'default', self::USER),
65 | new MailtrapSdkTransport(
66 | (new MailtrapClient($sendConfig))->sending()->emails(),
67 | $sendConfig,
68 | $dispatcher,
69 | $logger
70 | ),
71 | ];
72 |
73 | yield [
74 | new Dsn('mailtrap+sdk', AbstractApi::SENDMAIL_TRANSACTIONAL_HOST, self::USER),
75 | new MailtrapSdkTransport(
76 | (new MailtrapClient($sendConfig))->sending()->emails(),
77 | $sendConfig,
78 | $dispatcher,
79 | $logger
80 | ),
81 | ];
82 |
83 | // sandbox
84 | yield [
85 | new Dsn('mailtrap+sdk', AbstractApi::SENDMAIL_SANDBOX_HOST, self::USER, null, null, ['inboxId' => 1234]),
86 | new MailtrapSdkTransport(
87 | (new MailtrapClient($sandboxConfig))->sandbox()->emails($inboxId),
88 | $sandboxConfig,
89 | $dispatcher,
90 | $logger
91 | ),
92 | ];
93 |
94 | // bulk sending
95 | yield [
96 | new Dsn('mailtrap+sdk', AbstractApi::SENDMAIL_BULK_HOST, self::USER),
97 | new MailtrapSdkTransport(
98 | (new MailtrapClient($bulkConfig))->bulkSending()->emails(),
99 | $bulkConfig,
100 | $dispatcher,
101 | $logger
102 | ),
103 | ];
104 | }
105 |
106 | public function unsupportedSchemeProvider(): iterable
107 | {
108 | yield [
109 | new Dsn('mailtrap+foo', 'mailtrap', self::USER)
110 | ];
111 | }
112 |
113 | public function incompleteDsnProvider(): iterable
114 | {
115 | yield [new Dsn('mailtrap+sdk', 'default')];
116 | }
117 |
118 | public function unsupportedHostsProvider(): iterable
119 | {
120 | yield [new Dsn('mailtrap+sdk', 'invalid_url.api.mailtrap.io', self::USER)];
121 | yield [new Dsn('mailtrap+sdk', 'mailtrap.io', self::USER)];
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/tests/Bridge/Transport/MailtrapSdkTransportTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped(
25 | 'The "MailtrapSdkTransportTest" tests skipped, because "symfony/mailer" package is not installed.'
26 | );
27 | }
28 |
29 | parent::setUp();
30 | }
31 |
32 | /**
33 | * @dataProvider getTransportData
34 | */
35 | public function testToString(MailtrapSdkTransport $transport, string $expected): void
36 | {
37 | $this->assertSame($expected, (string) $transport);
38 | }
39 |
40 | public static function getTransportData(): array
41 | {
42 | $sendConfig = (new Config('key'))->setHost(AbstractApi::SENDMAIL_TRANSACTIONAL_HOST);
43 | $sandboxConfig = (new Config('key'))->setHost(AbstractApi::SENDMAIL_SANDBOX_HOST);
44 | $bulkConfig = (new Config('key'))->setHost(AbstractApi::SENDMAIL_BULK_HOST);
45 | $inboxId = 1234;
46 |
47 | return [
48 | [
49 | new MailtrapSdkTransport(
50 | (new MailtrapSendingClient($sendConfig))->emails(),
51 | $sendConfig
52 | ),
53 | sprintf('mailtrap+sdk://%s', AbstractApi::SENDMAIL_TRANSACTIONAL_HOST),
54 | ],
55 | [
56 | new MailtrapSdkTransport(
57 | (new MailtrapSandboxClient($sandboxConfig))->emails($inboxId),
58 | $sandboxConfig
59 | ),
60 | sprintf('mailtrap+sdk://%s?inboxId=%s', AbstractApi::SENDMAIL_SANDBOX_HOST, $inboxId),
61 | ],
62 | [
63 | new MailtrapSdkTransport(
64 | (new MailtrapBulkSendingClient($bulkConfig))->emails(),
65 | $bulkConfig
66 | ),
67 | sprintf('mailtrap+sdk://%s', AbstractApi::SENDMAIL_BULK_HOST),
68 | ],
69 | ];
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/Bridge/Transport/TransportFactoryTestCase.php:
--------------------------------------------------------------------------------
1 | getFactory();
56 |
57 | $this->assertSame($supports, $factory->supports($dsn));
58 | }
59 |
60 | /**
61 | * @dataProvider createProvider
62 | */
63 | public function testCreate(Dsn $dsn, TransportInterface $transport): void
64 | {
65 | $factory = $this->getFactory();
66 |
67 | $this->assertEquals($transport, $factory->create($dsn));
68 |
69 | if ('default' === $dsn->getHost()) {
70 | $this->assertStringMatchesFormat('mailtrap+sdk://' . AbstractApi::SENDMAIL_TRANSACTIONAL_HOST, (string) $transport);
71 | } else {
72 | $this->assertStringStartsWith('mailtrap+sdk://' . $dsn->getHost(), (string) $transport);
73 | }
74 | }
75 |
76 | /**
77 | * @dataProvider unsupportedSchemeProvider
78 | */
79 | public function testUnsupportedSchemeException(Dsn $dsn, string $message = null): void
80 | {
81 | $factory = $this->getFactory();
82 |
83 | $this->expectException(UnsupportedSchemeException::class);
84 | if (null !== $message) {
85 | $this->expectExceptionMessage($message);
86 | }
87 |
88 | $factory->create($dsn);
89 | }
90 |
91 | /**
92 | * @dataProvider unsupportedHostsProvider
93 | */
94 | public function testUnsupportedHostsException(Dsn $dsn): void
95 | {
96 | $factory = $this->getFactory();
97 |
98 | $this->expectException(UnsupportedHostException::class);
99 |
100 | $factory->create($dsn);
101 | }
102 |
103 | /**
104 | * @dataProvider incompleteDsnProvider
105 | */
106 | public function testIncompleteDsnException(Dsn $dsn): void
107 | {
108 | $factory = $this->getFactory();
109 |
110 | $this->expectException(IncompleteDsnException::class);
111 | $factory->create($dsn);
112 | }
113 |
114 | protected function getDispatcher(): EventDispatcherInterface
115 | {
116 | return $this->dispatcher ??= $this->createMock(EventDispatcherInterface::class);
117 | }
118 |
119 | protected function getClient(): HttpClientInterface
120 | {
121 | return $this->client ??= $this->createMock(HttpClientInterface::class);
122 | }
123 |
124 | protected function getLogger(): LoggerInterface
125 | {
126 | return $this->logger ??= $this->createMock(LoggerInterface::class);
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/tests/MailtrapBulkSendingClientTest.php:
--------------------------------------------------------------------------------
1 | getConfigMock())];
34 | }
35 | }
36 |
37 | public function testValidInitBulkSendingEmails(): void
38 | {
39 | $this->assertInstanceOf(
40 | BulkSendingEmails::class,
41 | MailtrapClient::initSendingEmails(apiKey: self::DEFAULT_API_KEY, isBulk: true)
42 | );
43 | }
44 |
45 | public function testInValidInitBulkSendingEmails(): void
46 | {
47 | $this->expectException(InvalidArgumentException::class);
48 |
49 | MailtrapClient::initSendingEmails(apiKey: self::DEFAULT_API_KEY, isBulk: true, isSandbox: true);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/MailtrapClientTestCase.php:
--------------------------------------------------------------------------------
1 | getMailtrapClientClassName();
29 | $this->mailtrapClientLayer = new $className($this->createMock(ConfigInterface::class));
30 | }
31 |
32 | protected function tearDown(): void
33 | {
34 | $this->mailtrapClientLayer = null;
35 |
36 | parent::tearDown();
37 | }
38 |
39 | /**
40 | * @dataProvider invalidApiClassNameProvider
41 | */
42 | public function testInvalidApiClassName($name): void
43 | {
44 | $this->expectExceptionObject(
45 | new BadMethodCallException(
46 | sprintf('%s -> undefined method called: "%s"', $this->getMailtrapClientClassName(), $name)
47 | )
48 | );
49 |
50 | $this->mailtrapClientLayer->{$name}();
51 | }
52 |
53 | /**
54 | * @dataProvider mapInstancesProvider
55 | */
56 | public function testMapInstance($instance): void
57 | {
58 | $this->assertInstanceOf($this->getLayerInterfaceClassName(), $instance);
59 | }
60 |
61 | public function invalidApiClassNameProvider(): array
62 | {
63 | return [['fakeclass1'], ['fakeclass2']];
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/tests/MailtrapGeneralClientTest.php:
--------------------------------------------------------------------------------
1 | $item) {
30 | yield match ($key) {
31 | 'permissions', 'users', 'contacts' => [new $item($this->getConfigMock(), self::FAKE_ACCOUNT_ID)],
32 | default => [new $item($this->getConfigMock())],
33 | };
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/tests/MailtrapSandboxClientTest.php:
--------------------------------------------------------------------------------
1 | $item) {
32 | yield match ($key) {
33 | 'emails' => [new $item($this->getConfigMock(), self::FAKE_INBOX_ID)],
34 | 'projects', 'inboxes' => [new $item($this->getConfigMock(), self::FAKE_ACCOUNT_ID)],
35 | 'messages', 'attachments' => [new $item($this->getConfigMock(), self::FAKE_ACCOUNT_ID, self::FAKE_INBOX_ID)],
36 | default => [new $item($this->getConfigMock())],
37 | };
38 | }
39 | }
40 |
41 | public function testValidInitSandboxSendingEmails(): void
42 | {
43 | $this->assertInstanceOf(
44 | SandboxSendingEmails::class,
45 | MailtrapClient::initSendingEmails(apiKey: self::DEFAULT_API_KEY, isSandbox: true, inboxId: self::FAKE_INBOX_ID)
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/MailtrapSendingClientTest.php:
--------------------------------------------------------------------------------
1 | getConfigMock())];
33 | }
34 | }
35 |
36 | public function testValidInitTransactionSendingEmails(): void
37 | {
38 | $this->assertInstanceOf(
39 | TransactionSendingEmails::class,
40 | MailtrapClient::initSendingEmails(apiKey: self::DEFAULT_API_KEY)
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tests/MailtrapTestCase.php:
--------------------------------------------------------------------------------
1 | getMockBuilder(ClientInterface::class)
28 | ->onlyMethods(['sendRequest'])
29 | ->getMock();
30 |
31 | $client
32 | ->expects($this->any())
33 | ->method('sendRequest');
34 |
35 | return $client;
36 | }
37 |
38 | protected function getConfigMock(): ConfigInterface
39 | {
40 | $config = $this->getMockBuilder(ConfigInterface::class)
41 | ->onlyMethods(['getHttpClientBuilder', 'getApiToken', 'getHost', 'isResponseThrowOnError'])
42 | ->getMock();
43 |
44 | $config
45 | ->method('getHttpClientBuilder')
46 | ->willReturn($this->getHttpClientBuilderMock());
47 |
48 | $config
49 | ->method('getApiToken')
50 | ->willReturn(self::DEFAULT_API_KEY);
51 |
52 | $config
53 | ->method('isResponseThrowOnError')
54 | ->willReturn(true);
55 |
56 | return $config;
57 | }
58 |
59 | protected function getHttpClientBuilderMock(): HttpClientBuilderInterface
60 | {
61 | $builder = $this->getMockBuilder(HttpClientBuilderInterface::class)
62 | ->onlyMethods(['getHttpClient'])
63 | ->disableOriginalConstructor()
64 | ->getMock();
65 |
66 | $builder
67 | ->method('getHttpClient')
68 | ->willReturn($this->getHttpClientMock());
69 |
70 | return $builder;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------