Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
7ebac4f
chore: overall update
JoaoFSCruz Apr 28, 2026
316216f
fix: add dmarc monitoring missing parameters
JoaoFSCruz Apr 28, 2026
208b0e5
feat: add template create and update endpoints
JoaoFSCruz Apr 28, 2026
786e135
test: add more test coverage
JoaoFSCruz Apr 30, 2026
4465054
fix: add data provider docblock
JoaoFSCruz Apr 30, 2026
46fa35c
refactor: send_at also accepts string
JoaoFSCruz May 4, 2026
61f9162
fix: add missing ToC entry; fix broken anchor; consistent titles
JoaoFSCruz May 4, 2026
10ba5cc
fix: fix parameter order
JoaoFSCruz May 4, 2026
120af4c
fix: add missing entries
JoaoFSCruz May 4, 2026
adceead
fix: add validation to Template create/update and fix example class name
JoaoFSCruz May 5, 2026
939889d
fix: add page param to Webhook::get(), optional fields to update(), a…
JoaoFSCruz May 5, 2026
c762a83
fix: add TYPE_EMAIL constant and correct COMPARER_NOT_EQUAL typo
JoaoFSCruz May 5, 2026
4dbdd3a
fix: remove non-existent builder method calls from README activity ex…
JoaoFSCruz May 5, 2026
2624158
fix: correct domain_id validation across suppression endpoints
JoaoFSCruz May 5, 2026
e40846b
fix: make domain_id optional on token create, fix update endpoint pat…
JoaoFSCruz May 5, 2026
e0a8823
fix: exclude ghost fields from sender identity PUT requests and fix R…
JoaoFSCruz May 5, 2026
849a1a5
fix: add page and limit pagination params to User::getAll()
JoaoFSCruz May 5, 2026
5abd560
fix: add page param to SmsWebhook::get(), add processed status, and v…
JoaoFSCruz May 5, 2026
1d623d1
fix: replace union types with docblocks for PHP 7.4 compatibility
JoaoFSCruz May 5, 2026
9ad3f4a
test: fix ActivityTest coverage gaps and consistency
JoaoFSCruz May 6, 2026
e91d1e7
test: fix AnalyticsTest coverage gaps
JoaoFSCruz May 6, 2026
37c7720
test: update api quota test
JoaoFSCruz May 6, 2026
f765af9
fix: assert model ids
JoaoFSCruz May 6, 2026
b8148bf
fix: overhaul test suite for correctness and fix source bugs
JoaoFSCruz May 6, 2026
e23f144
fix: cleanup; fix styling
JoaoFSCruz May 6, 2026
9109145
test: remove unecessary tests
JoaoFSCruz May 6, 2026
3aebbbe
fix: add missing request constraint validations and tests for email, …
JoaoFSCruz May 8, 2026
2ad4bdb
fix: add missing template and webhook field constraint validations an…
JoaoFSCruz May 8, 2026
51957f4
fix: add inbound builder enum/cardinality validations and tests, impl…
JoaoFSCruz May 8, 2026
86ddf7c
fix: add missing validations and tests for tokens, sender identities,…
JoaoFSCruz May 8, 2026
b1b5067
fix: add missing email verification field constraint validations and …
JoaoFSCruz May 8, 2026
3e773f9
fix: add missing validation for DMARC and blocklist monitoring endpoints
JoaoFSCruz May 8, 2026
f7b978b
fix: add domain_id to on-hold list endpoints
JoaoFSCruz May 11, 2026
3a52293
test: adjust test for on-hold recipients
JoaoFSCruz May 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
432 changes: 415 additions & 17 deletions README.md

Large diffs are not rendered by default.

18 changes: 15 additions & 3 deletions src/Common/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ class Constants
public const DEFAULT_LIMIT = 25;
public const MIN_LIMIT = 10;
public const MAX_LIMIT = 100;
public const POSSIBLE_EVENT_TYPES = ['queued', 'sent', 'delivered', 'soft_bounced', 'hard_bounced', 'junk', 'opened', 'clicked', 'unsubscribed', 'spam_complaints', 'opened_unique', 'clicked_unique'];
public const POSSIBLE_SMS_STATUSES = ['queued', 'sent', 'delivered', 'failed'];
public const POSSIBLE_EVENT_TYPES = ['queued', 'sent', 'delivered', 'soft_bounced', 'hard_bounced', 'junk', 'opened', 'clicked', 'unsubscribed', 'spam_complaints', 'opened_unique', 'clicked_unique', 'survey_opened', 'survey_submitted', 'deferred'];
public const POSSIBLE_SMS_STATUSES = ['processed', 'queued', 'sent', 'delivered', 'failed'];
public const POSSIBLE_SMS_RECIPIENT_STATUSES = ['active', 'opt_out'];
public const POSSIBLE_GROUP_BY_OPTIONS = ['days', 'weeks', 'months', 'years'];
public const GROUP_BY_DAYS = 'days';
Expand All @@ -33,16 +33,28 @@ class Constants

// Comparison Operators
public const COMPARER_EQUAL = 'equal';
public const COMPARER_NOT_EQUQL = 'not-equal';
public const COMPARER_NOT_EQUAL = 'not-equal';
public const COMPARER_CONTAINS = 'contains';
public const COMPARER_NOT_CONTAINS = 'not-contains';
public const COMPARER_STARTS_WITH = 'starts-with';
public const COMPARER_ENDS_WITH = 'ends-with';
public const COMPARER_NOT_STARTS_WITH = 'not-starts-with';
public const COMPARER_NOT_ENDS_WITH = 'not-ends-with';

public const POSSIBLE_SMS_INBOUND_COMPARERS = [
self::COMPARER_EQUAL,
self::COMPARER_NOT_EQUAL,
self::COMPARER_CONTAINS,
self::COMPARER_NOT_CONTAINS,
self::COMPARER_STARTS_WITH,
self::COMPARER_ENDS_WITH,
self::COMPARER_NOT_STARTS_WITH,
self::COMPARER_NOT_ENDS_WITH,
];

// Forward Types
public const TYPE_WEBHOOK = 'webhook';
public const TYPE_EMAIL = 'email';

// Scheduled messages status
public const STATUS_SCHEDULED = 'scheduled';
Expand Down
8 changes: 8 additions & 0 deletions src/Common/Roles.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,12 @@ class Roles
public const DESIGNER = 'Designer';
public const ACCOUNTANT = 'Accountant';
public const CUSTOM_USER = 'Custom User';

public const ALL_ROLES = [
self::ADMIN,
self::MANAGER,
self::DESIGNER,
self::ACCOUNTANT,
self::CUSTOM_USER,
];
}
2 changes: 1 addition & 1 deletion src/Endpoints/Activity.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function getAll(string $domainId, ActivityParams $activityParams): array
$activityParams->getLimit(),
Constants::MIN_LIMIT,
Constants::MAX_LIMIT,
'Limit is supposed to be between' . Constants::MIN_LIMIT . ' and ' . Constants::MAX_LIMIT . '.'
'Limit is supposed to be between ' . Constants::MIN_LIMIT . ' and ' . Constants::MAX_LIMIT . '.'
)
);
}
Expand Down
1 change: 0 additions & 1 deletion src/Endpoints/Blocklist.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public function create(BlocklistParams $params): array
array_filter([$params->getRecipients(), $params->getPatterns()], fn ($v) => !empty($v)),
'Either recipients or patterns must be provided.'
)
&& Assertion::minLength($params->getDomainId(), 1, 'Domain id is required.')
);

return $this->httpLayer->post(
Expand Down
165 changes: 165 additions & 0 deletions src/Endpoints/BlocklistMonitoring.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<?php

namespace MailerSend\Endpoints;

use Assert\Assertion;
use MailerSend\Common\Constants;
use MailerSend\Helpers\Builder\BlocklistMonitoringParams;
use MailerSend\Helpers\Builder\BlocklistMonitoringUpdateParams;
use MailerSend\Helpers\GeneralHelpers;

class BlocklistMonitoring extends AbstractEndpoint
{
protected string $endpoint = 'blocklist-monitoring';

public const POSSIBLE_SORT_BY = ['name', 'address', 'created_at', 'updated_at', 'blocklisted'];
public const POSSIBLE_ORDER = ['asc', 'desc'];

/**
* @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \MailerSend\Exceptions\MailerSendAssertException
* @throws \JsonException
*/
public function getAll(
?int $page = null,
?int $limit = Constants::DEFAULT_LIMIT,
?string $query = null,
?string $sortBy = null,
?string $order = null
): array {
if ($limit) {
GeneralHelpers::assert(
fn () => Assertion::range(
$limit,
Constants::MIN_LIMIT,
Constants::MAX_LIMIT,
'Limit is supposed to be between ' . Constants::MIN_LIMIT . ' and ' . Constants::MAX_LIMIT . '.'
)
);
}

if ($query) {
GeneralHelpers::assert(
fn () => Assertion::maxLength($query, 255, 'Query may not be greater than 255 characters.')
);
}

if ($page !== null) {
GeneralHelpers::assert(
fn () => Assertion::range($page, 1, PHP_INT_MAX, 'Page must be at least 1.')
);
}

if ($sortBy) {
GeneralHelpers::assert(
fn () => Assertion::inArray(
$sortBy,
self::POSSIBLE_SORT_BY,
'sort_by must be one of: ' . implode(', ', self::POSSIBLE_SORT_BY) . '.'
)
);
}

if ($order) {
GeneralHelpers::assert(
fn () => Assertion::inArray($order, self::POSSIBLE_ORDER, 'order must be asc or desc.')
);
}

return $this->httpLayer->get(
$this->buildUri($this->endpoint, array_filter([
'page' => $page,
'limit' => $limit,
'query' => $query,
'sort_by' => $sortBy,
'order' => $order,
], fn ($v) => $v !== null))
);
}

/**
* @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \MailerSend\Exceptions\MailerSendAssertException
* @throws \JsonException
*/
public function find(string $monitorId): array
{
GeneralHelpers::assert(
fn () => Assertion::minLength($monitorId, 1, 'Monitor id is required.')
);

return $this->httpLayer->get(
$this->buildUri("$this->endpoint/$monitorId")
);
}

/**
* @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \MailerSend\Exceptions\MailerSendAssertException
* @throws \JsonException
*/
public function create(BlocklistMonitoringParams $params): array
{
GeneralHelpers::assert(
fn () => Assertion::minLength($params->getAddress(), 1, 'Address is required.')
);

GeneralHelpers::assert(
fn () => Assertion::maxLength($params->getAddress(), 255, 'Address may not be greater than 255 characters.')
);

if ($params->getNotify() === true) {
if (empty($params->getNotifyEmail()) && empty($params->getNotifyAddress())) {
GeneralHelpers::assert(
fn () => Assertion::notEmpty(null, 'Notify email or notify address is required when notify is enabled.')
);
}
}

return $this->httpLayer->post(
$this->buildUri($this->endpoint),
$params->toArray()
);
}

/**
* @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \MailerSend\Exceptions\MailerSendAssertException
* @throws \JsonException
*/
public function update(string $monitorId, BlocklistMonitoringUpdateParams $params): array
{
GeneralHelpers::assert(
fn () => Assertion::minLength($monitorId, 1, 'Monitor id is required.')
);

if ($params->getNotify() === true) {
if (empty($params->getNotifyEmail()) && empty($params->getNotifyAddress())) {
GeneralHelpers::assert(
fn () => Assertion::notEmpty(null, 'Notify email or notify address is required when notify is enabled.')
);
}
}

return $this->httpLayer->put(
$this->buildUri("$this->endpoint/$monitorId"),
$params->toArray()
);
}

/**
* @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \MailerSend\Exceptions\MailerSendAssertException
* @throws \JsonException
*/
public function delete(string $monitorId): array
{
GeneralHelpers::assert(
fn () => Assertion::minLength($monitorId, 1, 'Monitor id is required.')
);

return $this->httpLayer->delete(
$this->buildUri("$this->endpoint/$monitorId")
);
}
}
10 changes: 10 additions & 0 deletions src/Endpoints/BulkEmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function send(array $bulkParams): array
$recipients_mapped = GeneralHelpers::mapToArray($params->getRecipients(), Recipient::class);
$cc_mapped = GeneralHelpers::mapToArray($params->getCc(), Recipient::class);
$bcc_mapped = GeneralHelpers::mapToArray($params->getBcc(), Recipient::class);
$rcpt_to_mapped = GeneralHelpers::mapToArray($params->getRcptTo(), Recipient::class);
$attachments_mapped = GeneralHelpers::mapToArray($params->getAttachments(), Attachment::class);
$personalization_mapped = GeneralHelpers::mapToArray($params->getPersonalization(), Personalization::class);

Expand All @@ -55,6 +56,15 @@ public function send(array $bulkParams): array
'send_at' => $params->getSendAt(),
'precedence_bulk' => $params->getPrecedenceBulkHeader(),
'in_reply_to' => $params->getInReplyToHeader(),
'settings' => [
'track_clicks' => $params->trackClicks(),
'track_opens' => $params->trackOpens(),
'track_content' => $params->trackContent(),
],
'headers' => $params->getHeaders(),
'references' => $params->getReferencesHeader(),
'list_unsubscribe' => $params->getListUnsubscribe(),
'rcptTo' => $rcpt_to_mapped,
],
fn ($v) => is_array($v) ? array_filter($v, fn ($v) => $v !== null) : $v !== null
);
Expand Down
Loading
Loading