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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/Endpoints/EmailEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* route?: string,
* metadata?: array<string, string>,
* tag?: string|null,
* tags?: list<array{name: string, value: string}>,
* settings?: EmailSettings|null,
* headers?: array<string, string>
* }
Expand Down Expand Up @@ -243,6 +244,18 @@ public function tag(?string $tag): self
return $this;
}

/**
* Set reusable name-value tags for the email.
*
* @param list<array{name: string, value: string}> $tags
*/
public function tags(array $tags): self
{
$this->payload['tags'] = $tags;

return $this;
}

/**
* Set per-email settings.
*
Expand Down
14 changes: 7 additions & 7 deletions src/Types/ApiTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @phpstan-type ApiObject array<string, mixed>
* @phpstan-type CursorPage array{data: list<ApiObject>, path: string|null, per_page: int, next_cursor: string|null, next_page_url: string|null, prev_cursor: string|null, prev_page_url: string|null}
* @phpstan-type MessageStatus 'pending'|'queued'|'suppressed'|'processed'|'delivered'|'opened'|'clicked'|'soft_bounced'|'hard_bounced'|'spam_complaint'|'failed'|'blocked'|'policy_rejected'|'unsubscribed'
* @phpstan-type SendMailRequest array{route?: string, from: string, to: non-empty-list<string>, cc?: list<string>, bcc?: list<string>, reply_to?: list<string>, subject: string, headers?: array<string, string>, metadata?: array<string, string>, tag?: string|null, settings?: array{track_opens?: bool, track_clicks?: bool, tls?: TlsPolicy}|null, html?: string|null, text?: string|null, attachments?: list<array{filename: string, content: string, content_type?: string|null, content_id?: string|null}>}
* @phpstan-type SendMailRequest array{route?: string, from: string, to: non-empty-list<string>, cc?: list<string>, bcc?: list<string>, reply_to?: list<string>, subject: string, headers?: array<string, string>, metadata?: array<string, string>, tag?: string|null, tags?: list<array{name: string, value: string}>, settings?: array{track_opens?: bool, track_clicks?: bool, tls?: TlsPolicy}|null, html?: string|null, text?: string|null, attachments?: list<array{filename: string, content: string, content_type?: string|null, content_id?: string|null}>}
* @phpstan-type SendBatchMailRequest list<SendMailRequest>
* @phpstan-type TlsPolicy 'opportunistic'|'enforced'
* @phpstan-type AttachmentDelivery 'inline'|'url'
Expand All @@ -24,10 +24,10 @@
* @phpstan-type DomainStatus 'verified'|'partially_verified'|'pending_verification'|'failed_verification'
* @phpstan-type InitialRoutes 'both'|'transactional'|'broadcast'
* @phpstan-type MessageAttachmentData array{size: int, filename: string, content_id: string|null, content_type: string}
* @phpstan-type MessageData array{id: string, type: MessageType, status: MessageStatus, status_changed_at: string|null, tag: string|null, from_email: string, from_name: string|null, reply_to: list<string>|null, subject: string|null, to: list<MessageRecipientData>|null, cc: list<MessageRecipientData>|null, bcc: list<MessageRecipientData>|null, attachments: list<MessageAttachmentData>|null, metadata: array<string, string>|null, spam_score?: float|int|null, spam_symbols?: list<SpamSymbol>, route_id: string, created_at: string}
* @phpstan-type MessageEventData array{message_id: string, event: MessageEventType, metadata: array<string, mixed>|null, timestamp: string}
* @phpstan-type MessageData array{id: string, type: MessageType, status: MessageStatus, status_changed_at: string|null, tag: string|null, tags: list<array{name: string, value: string}>, from_email: string, from_name: string|null, reply_to: list<string>|null, subject: string|null, to: list<MessageRecipientData>|null, cc: list<MessageRecipientData>|null, bcc: list<MessageRecipientData>|null, attachments: list<MessageAttachmentData>|null, metadata: array<string, string>|null, spam_score?: float|int|null, spam_symbols?: list<SpamSymbol>, route_id: string, created_at: string}
* @phpstan-type MessageEventData array{message_id: string, event: MessageEventType, tag: string|null, tags: list<array{name: string, value: string}>, metadata: array<string, mixed>|null, timestamp: string}
* @phpstan-type MessageEventType 'queued'|'processed'|'suppressed'|'delivered'|'auto_replied'|'soft_bounced'|'hard_bounced'|'spam_complaint'|'failed'|'blocked'|'policy_rejected'|'unsubscribed'|'opened'|'clicked'|'inbound_received'|'inbound_queued'|'inbound_spam_blocked'|'inbound_processed'|'inbound_retry'
* @phpstan-type MessageListData array{id: string, type: MessageType, status: MessageStatus, spam_score?: float|int|null, from_email: string, from_name: string|null, subject: string|null, to: list<MessageRecipientData>|null, cc: list<MessageRecipientData>|null, bcc: list<MessageRecipientData>|null, reply_to: list<string>|null, tag: string|null, status_changed_at: string|null, created_at: string}
* @phpstan-type MessageListData array{id: string, type: MessageType, status: MessageStatus, spam_score?: float|int|null, from_email: string, from_name: string|null, subject: string|null, to: list<MessageRecipientData>|null, cc: list<MessageRecipientData>|null, bcc: list<MessageRecipientData>|null, reply_to: list<string>|null, tag: string|null, tags: list<array{name: string, value: string}>, status_changed_at: string|null, created_at: string}
* @phpstan-type MessageRecipientData array{email: string, name: string|null}
* @phpstan-type MessageStatsData array{messages_transactional: int, messages_broadcast: int, messages_inbound: int, deliverability: float|int}
* @phpstan-type MessageType 'inbound'|'outbound'
Expand All @@ -38,7 +38,7 @@
* @phpstan-type RbacConflictCode 'stale_resource'|'owner_protected'|'last_owner'|'built_in_role_immutable'|'custom_role_requires_pro'
* @phpstan-type RbacPermission 'team:manage'|'billing:manage'|'security:manage'|'audit:read'|'support:manage'|'members:read'|'members:manage'|'roles:manage'|'team_tokens:read'|'team_tokens:manage'|'team_tokens:rotate'|'team_tokens:revoke'|'projects:create'|'team_suppressions:read'|'team_suppressions:add'|'team_suppressions:remove'|'projects:read'|'projects:manage'|'projects:delete'|'routes:read'|'routes:manage'|'routes:delete'|'domains:read'|'domains:manage'|'domains:delete'|'project_tokens:read'|'project_tokens:manage'|'project_tokens:rotate'|'project_tokens:revoke'|'webhooks:read'|'webhooks:manage'|'webhooks:delete'|'webhooks:rotate_secret'|'stats:read'|'messages:read'|'messages:read_content'|'messages:send'|'suppressions:read'|'suppressions:add'|'suppressions:remove'
* @phpstan-type RecordType 'TXT'|'CNAME'|'MX'
* @phpstan-type RouteData array{id: string, project_id: string, slug: string, name: string, route_type: RouteType, is_default: bool, inbound_address?: string|null, inbound_domain?: string|null, inbound_domain_verified_at?: string|null, inbound_spam_threshold?: float|int|null, attachment_delivery?: AttachmentDelivery, settings?: array{disable_hosted_unsubscribe?: bool, track_opens?: bool, track_clicks?: bool, generate_plaintext_fallback?: bool, suppress_auto_responders?: bool, tls?: TlsPolicy, redact_email_content?: bool, attachment_delivery?: array<string, mixed>}|null, project?: ProjectListData, webhooks_count?: int, suppressed_recipients_count?: int, statistics?: list<RouteStatisticData>, created_at: string, updated_at: string}
* @phpstan-type RouteData array{id: string, project_id: string, slug: string, name: string, route_type: RouteType, is_default: bool, inbound_address?: string|null, inbound_domain?: string|null, inbound_domain_verified_at?: string|null, inbound_spam_threshold?: float|int|null, attachment_delivery?: AttachmentDelivery, settings?: array{disable_hosted_unsubscribe?: bool, track_opens?: bool, track_clicks?: bool, generate_plaintext_fallback?: bool, suppress_auto_responders?: bool, suppress_disposable_recipients?: bool, tls?: TlsPolicy, redact_email_content?: bool, attachment_delivery?: array<string, mixed>}|null, project?: ProjectListData, webhooks_count?: int, suppressed_recipients_count?: int, statistics?: list<RouteStatisticData>, created_at: string, updated_at: string}
* @phpstan-type RouteListData array{id: string, slug: string, name: string, route_type: RouteType, is_default: bool, webhooks_count: int, suppressed_recipients_count: int, created_at: string, updated_at: string}
* @phpstan-type RouteStatisticData array{date: string, sent_count: int, delivered_count: int, opened_count: int, clicked_count: int, hard_bounce_count: int, spam_complaint_count: int, inbound_received_count: int, observed_opened_count?: int|null, human_opened_count?: int|null, privacy_opened_count?: int|null, effective_opened_count: int|null, machine_opened_count: int|null, machine_clicked_count: int|null}
* @phpstan-type RouteType 'transactional'|'broadcast'|'inbound'
Expand Down Expand Up @@ -71,7 +71,7 @@
* @phpstan-type UpdateProjectData array{name?: string|null, smtp_enabled?: bool|null, redact_email_content?: bool|null, default_route_id?: string|null}
* @phpstan-type UpdateRouteData array{name?: string|null, settings?: UpdateRouteSettingsData|null, inbound_settings?: UpdateRouteInboundSettingsData|null}
* @phpstan-type UpdateRouteInboundSettingsData array{inbound_domain?: string|null, inbound_spam_threshold?: float|int|null, attachment_delivery?: AttachmentDelivery|null}
* @phpstan-type UpdateRouteSettingsData array{track_opens?: bool|null, track_clicks?: bool|null, generate_plaintext_fallback?: bool|null, suppress_auto_responders?: bool|null, tls?: TlsPolicy|null, disable_hosted_unsubscribe?: bool|null, redact_email_content?: bool|null}
* @phpstan-type UpdateRouteSettingsData array{track_opens?: bool|null, track_clicks?: bool|null, generate_plaintext_fallback?: bool|null, suppress_auto_responders?: bool|null, suppress_disposable_recipients?: bool|null, tls?: TlsPolicy|null, disable_hosted_unsubscribe?: bool|null, redact_email_content?: bool|null}
* @phpstan-type UpdateTeamData array{name?: string|null}
* @phpstan-type UpdateTeamMemberAssignmentData array{role_id: string, project_access: array{scope: ProjectAccessScope, project_ids?: list<string>}}
* @phpstan-type UpdateWebhookData array{name?: string, url?: string, events?: non-empty-list<WebhookEvent>, enabled?: bool, include_machine_events?: bool}
Expand All @@ -93,7 +93,7 @@
* @phpstan-type BlockedFileTypesResponse array{extensions: list<string>, mime_types: list<string>}
* @phpstan-type MessageListResponse array{data: list<MessageListData>, links: list<string>, meta: array{path: string|null, per_page: int, next_cursor: string|null, next_cursor_url: string|null, prev_cursor: string|null, prev_cursor_url: string|null}}
* @phpstan-type MessageResponse MessageData
* @phpstan-type MessageEventsResponse array{data: list<MessageEventData>, path: string|null, per_page: int, next_cursor: string|null, next_page_url: string|null, prev_cursor: string|null, prev_page_url: string|null}
* @phpstan-type MessageEventsResponse array{data: list<MessageEventData>, links: list<string>, meta: array{path: string|null, per_page: int, next_cursor: string|null, next_cursor_url: string|null, prev_cursor: string|null, prev_cursor_url: string|null}}
* @phpstan-type ProjectListResponse array{data: list<ProjectListData>, path: string|null, per_page: int, next_cursor: string|null, next_page_url: string|null, prev_cursor: string|null, prev_page_url: string|null}
* @phpstan-type CreateProjectResponse array{data: ProjectData, message: string, api_token: string}
* @phpstan-type ProjectResponse ProjectData
Expand Down
22 changes: 22 additions & 0 deletions tests/Endpoints/EmailEndpointTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -495,3 +495,25 @@
->tag('campaign')
->send();
});

test('it handles reusable tags', function () {
$tags = [['name' => 'campaign', 'value' => 'welcome-v2']];

$this->httpClient
->shouldReceive('post')
->once()
->with('/v1/send', [
'from' => 'sender@example.com',
'to' => ['recipient@example.com'],
'subject' => 'Test Subject',
'tags' => $tags,
], [])
->andReturn(['message_id' => '123', 'status' => 'pending']);

$this->endpoint
->from('sender@example.com')
->to('recipient@example.com')
->subject('Test Subject')
->tags($tags)
->send();
});