diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1b77f50..6538ca9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.7.0" + ".": "0.8.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 7a3566b..74f9874 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 77 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-8bebbc269d62102c587a3a2ed99f065e0336599697b55d21e30080aa9c160672.yml -openapi_spec_hash: 1e701bc39fb0673ddef95c579da4a54b -config_hash: 866b537cf536cf8cb91b44e437fa812d +configured_endpoints: 129 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-77943605e8ccf7c6c70bfe2fcd14e41b45f018747af93fbd3b54c92187fc6d18.yml +openapi_spec_hash: d831e4cd9855111c1f9a1ee04a508564 +config_hash: 280cf643b641e9d1b21852c7e2926d54 diff --git a/CHANGELOG.md b/CHANGELOG.md index cafc679..c3c2260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 0.8.0 (2026-04-28) + +Full Changelog: [v0.7.0...v0.8.0](https://github.com/zavudev/sdk-php/compare/v0.7.0...v0.8.0) + +### Features + +* **api:** api update ([d0c5123](https://github.com/zavudev/sdk-php/commit/d0c5123b8267fefac5956b82b713bb5a0f5462de)) +* **api:** api update ([808bd81](https://github.com/zavudev/sdk-php/commit/808bd812e9eaa946fa56a19611efa8c76f410cb7)) +* **api:** api update ([4eb815e](https://github.com/zavudev/sdk-php/commit/4eb815e9b0b43449c32e234f9412d943561fa9f1)) +* **api:** api update ([fcb8c55](https://github.com/zavudev/sdk-php/commit/fcb8c557a21f6400fef4ca2b7c334ae69769d35b)) +* **api:** api update ([ba76c2d](https://github.com/zavudev/sdk-php/commit/ba76c2dc26ee2da1c46fcb506f18d6981216c0be)) +* **api:** api update ([7722e7c](https://github.com/zavudev/sdk-php/commit/7722e7c02702cc6d81e96d0a30aad73366cca343)) +* **api:** manual updates ([17eb562](https://github.com/zavudev/sdk-php/commit/17eb56286fe646c46e0f403a08be4412bd32b61b)) + + +### Bug Fixes + +* **client:** resolve serialization issue with unions and enums ([9dd15a4](https://github.com/zavudev/sdk-php/commit/9dd15a4da36f4b3ed629e120aebf31de677361dc)) +* populate enum-typed properties with enum instances ([30f86a8](https://github.com/zavudev/sdk-php/commit/30f86a82d26eba9c82c0c18c3f9cf2bd4abc23f1)) + ## 0.7.0 (2026-04-14) Full Changelog: [v0.6.0...v0.7.0](https://github.com/zavudev/sdk-php/compare/v0.6.0...v0.7.0) diff --git a/src/Balance/BalanceGetResponse.php b/src/Balance/BalanceGetResponse.php new file mode 100644 index 0000000..e10921e --- /dev/null +++ b/src/Balance/BalanceGetResponse.php @@ -0,0 +1,147 @@ + */ + use SdkModel; + + /** + * Team balance in cents. All charges are billed to the parent team. + */ + #[Required] + public int $balance; + + #[Required] + public string $currency; + + /** + * Spending cap in cents (only for sub-accounts). + */ + #[Optional(nullable: true)] + public ?int $creditLimit; + + /** + * Whether this API key belongs to a sub-account. + */ + #[Optional] + public ?bool $isSubAccount; + + /** + * Total amount spent by this sub-account in cents (only for sub-accounts). + */ + #[Optional(nullable: true)] + public ?int $totalSpent; + + /** + * `new BalanceGetResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BalanceGetResponse::with(balance: ..., currency: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BalanceGetResponse)->withBalance(...)->withCurrency(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with( + int $balance, + string $currency, + ?int $creditLimit = null, + ?bool $isSubAccount = null, + ?int $totalSpent = null, + ): self { + $self = new self; + + $self['balance'] = $balance; + $self['currency'] = $currency; + + null !== $creditLimit && $self['creditLimit'] = $creditLimit; + null !== $isSubAccount && $self['isSubAccount'] = $isSubAccount; + null !== $totalSpent && $self['totalSpent'] = $totalSpent; + + return $self; + } + + /** + * Team balance in cents. All charges are billed to the parent team. + */ + public function withBalance(int $balance): self + { + $self = clone $this; + $self['balance'] = $balance; + + return $self; + } + + public function withCurrency(string $currency): self + { + $self = clone $this; + $self['currency'] = $currency; + + return $self; + } + + /** + * Spending cap in cents (only for sub-accounts). + */ + public function withCreditLimit(?int $creditLimit): self + { + $self = clone $this; + $self['creditLimit'] = $creditLimit; + + return $self; + } + + /** + * Whether this API key belongs to a sub-account. + */ + public function withIsSubAccount(bool $isSubAccount): self + { + $self = clone $this; + $self['isSubAccount'] = $isSubAccount; + + return $self; + } + + /** + * Total amount spent by this sub-account in cents (only for sub-accounts). + */ + public function withTotalSpent(?int $totalSpent): self + { + $self = clone $this; + $self['totalSpent'] = $totalSpent; + + return $self; + } +} diff --git a/src/Broadcasts/BroadcastContact.php b/src/Broadcasts/BroadcastContact.php index 8d7ad2a..0552a8e 100644 --- a/src/Broadcasts/BroadcastContact.php +++ b/src/Broadcasts/BroadcastContact.php @@ -22,6 +22,7 @@ * errorMessage?: string|null, * messageID?: string|null, * processedAt?: \DateTimeInterface|null, + * templateButtonVariables?: array|null, * templateVariables?: array|null, * } */ @@ -69,6 +70,10 @@ final class BroadcastContact implements BaseModel #[Optional] public ?\DateTimeInterface $processedAt; + /** @var array|null $templateButtonVariables */ + #[Optional(map: 'string')] + public ?array $templateButtonVariables; + /** @var array|null $templateVariables */ #[Optional(map: 'string')] public ?array $templateVariables; @@ -106,6 +111,7 @@ public function __construct() * * @param RecipientType|value-of $recipientType * @param BroadcastContactStatus|value-of $status + * @param array|null $templateButtonVariables * @param array|null $templateVariables */ public static function with( @@ -119,6 +125,7 @@ public static function with( ?string $errorMessage = null, ?string $messageID = null, ?\DateTimeInterface $processedAt = null, + ?array $templateButtonVariables = null, ?array $templateVariables = null, ): self { $self = new self; @@ -134,6 +141,7 @@ public static function with( null !== $errorMessage && $self['errorMessage'] = $errorMessage; null !== $messageID && $self['messageID'] = $messageID; null !== $processedAt && $self['processedAt'] = $processedAt; + null !== $templateButtonVariables && $self['templateButtonVariables'] = $templateButtonVariables; null !== $templateVariables && $self['templateVariables'] = $templateVariables; return $self; @@ -230,6 +238,18 @@ public function withProcessedAt(\DateTimeInterface $processedAt): self return $self; } + /** + * @param array $templateButtonVariables + */ + public function withTemplateButtonVariables( + array $templateButtonVariables + ): self { + $self = clone $this; + $self['templateButtonVariables'] = $templateButtonVariables; + + return $self; + } + /** * @param array $templateVariables */ diff --git a/src/Broadcasts/BroadcastContent.php b/src/Broadcasts/BroadcastContent.php index cc829e2..8443b98 100644 --- a/src/Broadcasts/BroadcastContent.php +++ b/src/Broadcasts/BroadcastContent.php @@ -16,6 +16,7 @@ * mediaID?: string|null, * mediaURL?: string|null, * mimeType?: string|null, + * templateButtonVariables?: array|null, * templateID?: string|null, * templateVariables?: array|null, * } @@ -49,6 +50,14 @@ final class BroadcastContent implements BaseModel #[Optional] public ?string $mimeType; + /** + * Default button variables for dynamic URL/OTP buttons. Keys are the button index (0, 1, 2). Per-contact values override these. + * + * @var array|null $templateButtonVariables + */ + #[Optional(map: 'string')] + public ?array $templateButtonVariables; + /** * Template ID for template messages. */ @@ -56,7 +65,7 @@ final class BroadcastContent implements BaseModel public ?string $templateID; /** - * Default template variables (can be overridden per contact). + * Default body variables (can be overridden per contact). Keys are positions (1, 2, ...). * * @var array|null $templateVariables */ @@ -73,6 +82,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * + * @param array|null $templateButtonVariables * @param array|null $templateVariables */ public static function with( @@ -80,6 +90,7 @@ public static function with( ?string $mediaID = null, ?string $mediaURL = null, ?string $mimeType = null, + ?array $templateButtonVariables = null, ?string $templateID = null, ?array $templateVariables = null, ): self { @@ -89,6 +100,7 @@ public static function with( null !== $mediaID && $self['mediaID'] = $mediaID; null !== $mediaURL && $self['mediaURL'] = $mediaURL; null !== $mimeType && $self['mimeType'] = $mimeType; + null !== $templateButtonVariables && $self['templateButtonVariables'] = $templateButtonVariables; null !== $templateID && $self['templateID'] = $templateID; null !== $templateVariables && $self['templateVariables'] = $templateVariables; @@ -139,6 +151,20 @@ public function withMimeType(string $mimeType): self return $self; } + /** + * Default button variables for dynamic URL/OTP buttons. Keys are the button index (0, 1, 2). Per-contact values override these. + * + * @param array $templateButtonVariables + */ + public function withTemplateButtonVariables( + array $templateButtonVariables + ): self { + $self = clone $this; + $self['templateButtonVariables'] = $templateButtonVariables; + + return $self; + } + /** * Template ID for template messages. */ @@ -151,7 +177,7 @@ public function withTemplateID(string $templateID): self } /** - * Default template variables (can be overridden per contact). + * Default body variables (can be overridden per contact). Keys are positions (1, 2, ...). * * @param array $templateVariables */ diff --git a/src/Broadcasts/BroadcastEscalateReviewResponse.php b/src/Broadcasts/BroadcastEscalateReviewResponse.php new file mode 100644 index 0000000..9eb3b7d --- /dev/null +++ b/src/Broadcasts/BroadcastEscalateReviewResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public Broadcast $broadcast; + + /** + * `new BroadcastEscalateReviewResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BroadcastEscalateReviewResponse::with(broadcast: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BroadcastEscalateReviewResponse)->withBroadcast(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Broadcast|BroadcastShape $broadcast + */ + public static function with(Broadcast|array $broadcast): self + { + $self = new self; + + $self['broadcast'] = $broadcast; + + return $self; + } + + /** + * @param Broadcast|BroadcastShape $broadcast + */ + public function withBroadcast(Broadcast|array $broadcast): self + { + $self = clone $this; + $self['broadcast'] = $broadcast; + + return $self; + } +} diff --git a/src/Broadcasts/BroadcastRetryReviewResponse.php b/src/Broadcasts/BroadcastRetryReviewResponse.php new file mode 100644 index 0000000..e54235c --- /dev/null +++ b/src/Broadcasts/BroadcastRetryReviewResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public Broadcast $broadcast; + + /** + * `new BroadcastRetryReviewResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BroadcastRetryReviewResponse::with(broadcast: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BroadcastRetryReviewResponse)->withBroadcast(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Broadcast|BroadcastShape $broadcast + */ + public static function with(Broadcast|array $broadcast): self + { + $self = new self; + + $self['broadcast'] = $broadcast; + + return $self; + } + + /** + * @param Broadcast|BroadcastShape $broadcast + */ + public function withBroadcast(Broadcast|array $broadcast): self + { + $self = clone $this; + $self['broadcast'] = $broadcast; + + return $self; + } +} diff --git a/src/Broadcasts/Contacts/ContactAddParams/Contact.php b/src/Broadcasts/Contacts/ContactAddParams/Contact.php index 6de3e9c..3b78edb 100644 --- a/src/Broadcasts/Contacts/ContactAddParams/Contact.php +++ b/src/Broadcasts/Contacts/ContactAddParams/Contact.php @@ -11,7 +11,9 @@ /** * @phpstan-type ContactShape = array{ - * recipient: string, templateVariables?: array|null + * recipient: string, + * templateButtonVariables?: array|null, + * templateVariables?: array|null, * } */ final class Contact implements BaseModel @@ -26,7 +28,15 @@ final class Contact implements BaseModel public string $recipient; /** - * Per-contact template variables to personalize the message. + * Per-contact button variables for dynamic URL/OTP buttons. Keys are the button index (0, 1, 2). + * + * @var array|null $templateButtonVariables + */ + #[Optional(map: 'string')] + public ?array $templateButtonVariables; + + /** + * Per-contact body variables. Keys are positions (1, 2, ...) matching the order placeholders appear in the template body. * * @var array|null $templateVariables */ @@ -57,16 +67,19 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * + * @param array|null $templateButtonVariables * @param array|null $templateVariables */ public static function with( string $recipient, - ?array $templateVariables = null + ?array $templateButtonVariables = null, + ?array $templateVariables = null, ): self { $self = new self; $self['recipient'] = $recipient; + null !== $templateButtonVariables && $self['templateButtonVariables'] = $templateButtonVariables; null !== $templateVariables && $self['templateVariables'] = $templateVariables; return $self; @@ -84,7 +97,21 @@ public function withRecipient(string $recipient): self } /** - * Per-contact template variables to personalize the message. + * Per-contact button variables for dynamic URL/OTP buttons. Keys are the button index (0, 1, 2). + * + * @param array $templateButtonVariables + */ + public function withTemplateButtonVariables( + array $templateButtonVariables + ): self { + $self = clone $this; + $self['templateButtonVariables'] = $templateButtonVariables; + + return $self; + } + + /** + * Per-contact body variables. Keys are positions (1, 2, ...) matching the order placeholders appear in the template body. * * @param array $templateVariables */ diff --git a/src/Client.php b/src/Client.php index fd9aaa7..c7e0706 100644 --- a/src/Client.php +++ b/src/Client.php @@ -9,14 +9,22 @@ use Zavudev\Core\BaseClient; use Zavudev\Core\Util; use Zavudev\Services\AddressesService; +use Zavudev\Services\BalanceService; use Zavudev\Services\BroadcastsService; use Zavudev\Services\ContactsService; +use Zavudev\Services\ExportsService; use Zavudev\Services\IntrospectService; +use Zavudev\Services\InvitationsService; use Zavudev\Services\MessagesService; +use Zavudev\Services\Number10dlcService; use Zavudev\Services\PhoneNumbersService; +use Zavudev\Services\PlanService; use Zavudev\Services\RegulatoryDocumentsService; use Zavudev\Services\SendersService; +use Zavudev\Services\SubAccountsService; use Zavudev\Services\TemplatesService; +use Zavudev\Services\URLsService; +use Zavudev\Services\UsageService; /** * @phpstan-import-type NormalizedRequest from \Zavudev\Core\BaseClient @@ -71,6 +79,46 @@ class Client extends BaseClient */ public RegulatoryDocumentsService $regulatoryDocuments; + /** + * @api + */ + public InvitationsService $invitations; + + /** + * @api + */ + public ExportsService $exports; + + /** + * @api + */ + public URLsService $urls; + + /** + * @api + */ + public BalanceService $balance; + + /** + * @api + */ + public PlanService $plan; + + /** + * @api + */ + public UsageService $usage; + + /** + * @api + */ + public SubAccountsService $subAccounts; + + /** + * @api + */ + public Number10dlcService $number10dlc; + /** * @param RequestOpts|null $requestOptions */ @@ -118,6 +166,14 @@ public function __construct( $this->phoneNumbers = new PhoneNumbersService($this); $this->addresses = new AddressesService($this); $this->regulatoryDocuments = new RegulatoryDocumentsService($this); + $this->invitations = new InvitationsService($this); + $this->exports = new ExportsService($this); + $this->urls = new URLsService($this); + $this->balance = new BalanceService($this); + $this->plan = new PlanService($this); + $this->usage = new UsageService($this); + $this->subAccounts = new SubAccountsService($this); + $this->number10dlc = new Number10dlcService($this); } /** @return array */ diff --git a/src/Contacts/Channels/ChannelAddParams.php b/src/Contacts/Channels/ChannelAddParams.php new file mode 100644 index 0000000..87d8ca7 --- /dev/null +++ b/src/Contacts/Channels/ChannelAddParams.php @@ -0,0 +1,166 @@ +, + * identifier: string, + * countryCode?: string|null, + * isPrimary?: bool|null, + * label?: string|null, + * } + */ +final class ChannelAddParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + /** + * Channel type. + * + * @var value-of $channel + */ + #[Required(enum: Channel::class)] + public string $channel; + + /** + * Channel identifier (phone number in E.164 format or email address). + */ + #[Required] + public string $identifier; + + /** + * ISO country code for phone numbers. + */ + #[Optional] + public ?string $countryCode; + + /** + * Whether this should be the primary channel for its type. + */ + #[Optional] + public ?bool $isPrimary; + + /** + * Optional label for the channel. + */ + #[Optional] + public ?string $label; + + /** + * `new ChannelAddParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ChannelAddParams::with(channel: ..., identifier: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ChannelAddParams)->withChannel(...)->withIdentifier(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Channel|value-of $channel + */ + public static function with( + Channel|string $channel, + string $identifier, + ?string $countryCode = null, + ?bool $isPrimary = null, + ?string $label = null, + ): self { + $self = new self; + + $self['channel'] = $channel; + $self['identifier'] = $identifier; + + null !== $countryCode && $self['countryCode'] = $countryCode; + null !== $isPrimary && $self['isPrimary'] = $isPrimary; + null !== $label && $self['label'] = $label; + + return $self; + } + + /** + * Channel type. + * + * @param Channel|value-of $channel + */ + public function withChannel(Channel|string $channel): self + { + $self = clone $this; + $self['channel'] = $channel; + + return $self; + } + + /** + * Channel identifier (phone number in E.164 format or email address). + */ + public function withIdentifier(string $identifier): self + { + $self = clone $this; + $self['identifier'] = $identifier; + + return $self; + } + + /** + * ISO country code for phone numbers. + */ + public function withCountryCode(string $countryCode): self + { + $self = clone $this; + $self['countryCode'] = $countryCode; + + return $self; + } + + /** + * Whether this should be the primary channel for its type. + */ + public function withIsPrimary(bool $isPrimary): self + { + $self = clone $this; + $self['isPrimary'] = $isPrimary; + + return $self; + } + + /** + * Optional label for the channel. + */ + public function withLabel(string $label): self + { + $self = clone $this; + $self['label'] = $label; + + return $self; + } +} diff --git a/src/Contacts/Channels/ChannelAddParams/Channel.php b/src/Contacts/Channels/ChannelAddParams/Channel.php new file mode 100644 index 0000000..0d023eb --- /dev/null +++ b/src/Contacts/Channels/ChannelAddParams/Channel.php @@ -0,0 +1,21 @@ + */ + use SdkModel; + + /** + * A communication channel for a contact. + */ + #[Required] + public ContactChannel $channel; + + /** + * `new ChannelAddResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ChannelAddResponse::with(channel: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ChannelAddResponse)->withChannel(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param ContactChannel|ContactChannelShape $channel + */ + public static function with(ContactChannel|array $channel): self + { + $self = new self; + + $self['channel'] = $channel; + + return $self; + } + + /** + * A communication channel for a contact. + * + * @param ContactChannel|ContactChannelShape $channel + */ + public function withChannel(ContactChannel|array $channel): self + { + $self = clone $this; + $self['channel'] = $channel; + + return $self; + } +} diff --git a/src/Contacts/Channels/ChannelRemoveParams.php b/src/Contacts/Channels/ChannelRemoveParams.php new file mode 100644 index 0000000..3cdf7de --- /dev/null +++ b/src/Contacts/Channels/ChannelRemoveParams.php @@ -0,0 +1,68 @@ + */ + use SdkModel; + use SdkParams; + + #[Required] + public string $contactID; + + /** + * `new ChannelRemoveParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ChannelRemoveParams::with(contactID: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ChannelRemoveParams)->withContactID(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with(string $contactID): self + { + $self = new self; + + $self['contactID'] = $contactID; + + return $self; + } + + public function withContactID(string $contactID): self + { + $self = clone $this; + $self['contactID'] = $contactID; + + return $self; + } +} diff --git a/src/Contacts/Channels/ChannelSetPrimaryParams.php b/src/Contacts/Channels/ChannelSetPrimaryParams.php new file mode 100644 index 0000000..c3c1049 --- /dev/null +++ b/src/Contacts/Channels/ChannelSetPrimaryParams.php @@ -0,0 +1,68 @@ + */ + use SdkModel; + use SdkParams; + + #[Required] + public string $contactID; + + /** + * `new ChannelSetPrimaryParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ChannelSetPrimaryParams::with(contactID: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ChannelSetPrimaryParams)->withContactID(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with(string $contactID): self + { + $self = new self; + + $self['contactID'] = $contactID; + + return $self; + } + + public function withContactID(string $contactID): self + { + $self = clone $this; + $self['contactID'] = $contactID; + + return $self; + } +} diff --git a/src/Contacts/Channels/ChannelSetPrimaryResponse.php b/src/Contacts/Channels/ChannelSetPrimaryResponse.php new file mode 100644 index 0000000..a9f43a0 --- /dev/null +++ b/src/Contacts/Channels/ChannelSetPrimaryResponse.php @@ -0,0 +1,77 @@ + */ + use SdkModel; + + /** + * A communication channel for a contact. + */ + #[Required] + public ContactChannel $channel; + + /** + * `new ChannelSetPrimaryResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ChannelSetPrimaryResponse::with(channel: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ChannelSetPrimaryResponse)->withChannel(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param ContactChannel|ContactChannelShape $channel + */ + public static function with(ContactChannel|array $channel): self + { + $self = new self; + + $self['channel'] = $channel; + + return $self; + } + + /** + * A communication channel for a contact. + * + * @param ContactChannel|ContactChannelShape $channel + */ + public function withChannel(ContactChannel|array $channel): self + { + $self = clone $this; + $self['channel'] = $channel; + + return $self; + } +} diff --git a/src/Contacts/Channels/ChannelUpdateParams.php b/src/Contacts/Channels/ChannelUpdateParams.php new file mode 100644 index 0000000..a49ec92 --- /dev/null +++ b/src/Contacts/Channels/ChannelUpdateParams.php @@ -0,0 +1,133 @@ +|null, + * verified?: bool|null, + * } + */ +final class ChannelUpdateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + #[Required] + public string $contactID; + + /** + * Optional label for the channel. Set to null to clear. + */ + #[Optional(nullable: true)] + public ?string $label; + + /** @var array|null $metadata */ + #[Optional(map: 'string')] + public ?array $metadata; + + /** + * Whether the channel is verified. + */ + #[Optional] + public ?bool $verified; + + /** + * `new ChannelUpdateParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ChannelUpdateParams::with(contactID: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ChannelUpdateParams)->withContactID(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param array|null $metadata + */ + public static function with( + string $contactID, + ?string $label = null, + ?array $metadata = null, + ?bool $verified = null, + ): self { + $self = new self; + + $self['contactID'] = $contactID; + + null !== $label && $self['label'] = $label; + null !== $metadata && $self['metadata'] = $metadata; + null !== $verified && $self['verified'] = $verified; + + return $self; + } + + public function withContactID(string $contactID): self + { + $self = clone $this; + $self['contactID'] = $contactID; + + return $self; + } + + /** + * Optional label for the channel. Set to null to clear. + */ + public function withLabel(?string $label): self + { + $self = clone $this; + $self['label'] = $label; + + return $self; + } + + /** + * @param array $metadata + */ + public function withMetadata(array $metadata): self + { + $self = clone $this; + $self['metadata'] = $metadata; + + return $self; + } + + /** + * Whether the channel is verified. + */ + public function withVerified(bool $verified): self + { + $self = clone $this; + $self['verified'] = $verified; + + return $self; + } +} diff --git a/src/Contacts/Channels/ChannelUpdateResponse.php b/src/Contacts/Channels/ChannelUpdateResponse.php new file mode 100644 index 0000000..a75c9d9 --- /dev/null +++ b/src/Contacts/Channels/ChannelUpdateResponse.php @@ -0,0 +1,77 @@ + */ + use SdkModel; + + /** + * A communication channel for a contact. + */ + #[Required] + public ContactChannel $channel; + + /** + * `new ChannelUpdateResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ChannelUpdateResponse::with(channel: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ChannelUpdateResponse)->withChannel(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param ContactChannel|ContactChannelShape $channel + */ + public static function with(ContactChannel|array $channel): self + { + $self = new self; + + $self['channel'] = $channel; + + return $self; + } + + /** + * A communication channel for a contact. + * + * @param ContactChannel|ContactChannelShape $channel + */ + public function withChannel(ContactChannel|array $channel): self + { + $self = clone $this; + $self['channel'] = $channel; + + return $self; + } +} diff --git a/src/Contacts/ContactCreateParams.php b/src/Contacts/ContactCreateParams.php new file mode 100644 index 0000000..503e657 --- /dev/null +++ b/src/Contacts/ContactCreateParams.php @@ -0,0 +1,133 @@ +, + * displayName?: string|null, + * metadata?: array|null, + * } + */ +final class ContactCreateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + /** + * Communication channels for the contact. + * + * @var list $channels + */ + #[Required(list: Channel::class)] + public array $channels; + + /** + * Display name for the contact. + */ + #[Optional] + public ?string $displayName; + + /** + * Arbitrary metadata to associate with the contact. + * + * @var array|null $metadata + */ + #[Optional(map: 'string')] + public ?array $metadata; + + /** + * `new ContactCreateParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ContactCreateParams::with(channels: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ContactCreateParams)->withChannels(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $channels + * @param array|null $metadata + */ + public static function with( + array $channels, + ?string $displayName = null, + ?array $metadata = null + ): self { + $self = new self; + + $self['channels'] = $channels; + + null !== $displayName && $self['displayName'] = $displayName; + null !== $metadata && $self['metadata'] = $metadata; + + return $self; + } + + /** + * Communication channels for the contact. + * + * @param list $channels + */ + public function withChannels(array $channels): self + { + $self = clone $this; + $self['channels'] = $channels; + + return $self; + } + + /** + * Display name for the contact. + */ + public function withDisplayName(string $displayName): self + { + $self = clone $this; + $self['displayName'] = $displayName; + + return $self; + } + + /** + * Arbitrary metadata to associate with the contact. + * + * @param array $metadata + */ + public function withMetadata(array $metadata): self + { + $self = clone $this; + $self['metadata'] = $metadata; + + return $self; + } +} diff --git a/src/Contacts/ContactCreateParams/Channel1.php b/src/Contacts/ContactCreateParams/Channel1.php new file mode 100644 index 0000000..7867a25 --- /dev/null +++ b/src/Contacts/ContactCreateParams/Channel1.php @@ -0,0 +1,162 @@ +, + * identifier: string, + * countryCode?: string|null, + * isPrimary?: bool|null, + * label?: string|null, + * } + */ +final class Channel1 implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * Channel type. + * + * @var value-of $channel + */ + #[Required(enum: Channel::class)] + public string $channel; + + /** + * Channel identifier (phone number in E.164 format or email address). + */ + #[Required] + public string $identifier; + + /** + * ISO country code for phone numbers. + */ + #[Optional] + public ?string $countryCode; + + /** + * Whether this should be the primary channel for its type. + */ + #[Optional] + public ?bool $isPrimary; + + /** + * Optional label for the channel. + */ + #[Optional] + public ?string $label; + + /** + * `new Channel1()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * Channel1::with(channel: ..., identifier: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new Channel1)->withChannel(...)->withIdentifier(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Channel|value-of $channel + */ + public static function with( + Channel|string $channel, + string $identifier, + ?string $countryCode = null, + ?bool $isPrimary = null, + ?string $label = null, + ): self { + $self = new self; + + $self['channel'] = $channel; + $self['identifier'] = $identifier; + + null !== $countryCode && $self['countryCode'] = $countryCode; + null !== $isPrimary && $self['isPrimary'] = $isPrimary; + null !== $label && $self['label'] = $label; + + return $self; + } + + /** + * Channel type. + * + * @param Channel|value-of $channel + */ + public function withChannel(Channel|string $channel): self + { + $self = clone $this; + $self['channel'] = $channel; + + return $self; + } + + /** + * Channel identifier (phone number in E.164 format or email address). + */ + public function withIdentifier(string $identifier): self + { + $self = clone $this; + $self['identifier'] = $identifier; + + return $self; + } + + /** + * ISO country code for phone numbers. + */ + public function withCountryCode(string $countryCode): self + { + $self = clone $this; + $self['countryCode'] = $countryCode; + + return $self; + } + + /** + * Whether this should be the primary channel for its type. + */ + public function withIsPrimary(bool $isPrimary): self + { + $self = clone $this; + $self['isPrimary'] = $isPrimary; + + return $self; + } + + /** + * Optional label for the channel. + */ + public function withLabel(string $label): self + { + $self = clone $this; + $self['label'] = $label; + + return $self; + } +} diff --git a/src/Contacts/ContactCreateParams/Channel1/Channel.php b/src/Contacts/ContactCreateParams/Channel1/Channel.php new file mode 100644 index 0000000..f2315f1 --- /dev/null +++ b/src/Contacts/ContactCreateParams/Channel1/Channel.php @@ -0,0 +1,21 @@ + */ + use SdkModel; + use SdkParams; + + /** + * ID of the contact to merge into the target contact. The source contact will be marked as merged. + */ + #[Required('sourceContactId')] + public string $sourceContactID; + + /** + * `new ContactMergeParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ContactMergeParams::with(sourceContactID: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ContactMergeParams)->withSourceContactID(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with(string $sourceContactID): self + { + $self = new self; + + $self['sourceContactID'] = $sourceContactID; + + return $self; + } + + /** + * ID of the contact to merge into the target contact. The source contact will be marked as merged. + */ + public function withSourceContactID(string $sourceContactID): self + { + $self = clone $this; + $self['sourceContactID'] = $sourceContactID; + + return $self; + } +} diff --git a/src/Core/Attributes/Required.php b/src/Core/Attributes/Required.php index a383c0a..e0c144b 100644 --- a/src/Core/Attributes/Required.php +++ b/src/Core/Attributes/Required.php @@ -25,9 +25,6 @@ class Required public readonly bool $nullable; - /** @var array */ - private static array $enumConverters = []; - /** * @param class-string|Converter|string|null $type * @param class-string<\BackedEnum>|Converter|null $enum @@ -52,7 +49,7 @@ public function __construct( $type ??= new MapOf($map); } if (null !== $enum) { - $type ??= $enum instanceof Converter ? $enum : self::enumConverter($enum); + $type ??= $enum instanceof Converter ? $enum : EnumOf::fromBackedEnum($enum); } $this->apiName = $apiName; @@ -60,16 +57,4 @@ public function __construct( $this->optional = false; $this->nullable = $nullable; } - - /** @property class-string<\BackedEnum> $enum */ - private static function enumConverter(string $enum): Converter - { - if (!isset(self::$enumConverters[$enum])) { - // @phpstan-ignore-next-line argument.type - $converter = new EnumOf(array_column($enum::cases(), column_key: 'value')); - self::$enumConverters[$enum] = $converter; - } - - return self::$enumConverters[$enum]; - } } diff --git a/src/Core/Conversion.php b/src/Core/Conversion.php index 488c790..ba1af39 100644 --- a/src/Core/Conversion.php +++ b/src/Core/Conversion.php @@ -8,6 +8,7 @@ use Zavudev\Core\Conversion\Contracts\Converter; use Zavudev\Core\Conversion\Contracts\ConverterSource; use Zavudev\Core\Conversion\DumpState; +use Zavudev\Core\Conversion\EnumOf; /** * @internal @@ -65,6 +66,13 @@ public static function coerce(Converter|ConverterSource|string $target, mixed $v return $target->coerce($value, state: $state); } + // BackedEnum class-name targets: wrap in EnumOf so enum values are scored + // against the enum's cases. Without this, tryConvert's default case scores + // any class-name target as `no`, even when the value is a valid enum member. + if (is_a($target, class: \BackedEnum::class, allow_string: true)) { + return EnumOf::fromBackedEnum($target)->coerce($value, state: $state); + } + return self::tryConvert($target, value: $value, state: $state); } @@ -78,6 +86,13 @@ public static function dump(Converter|ConverterSource|string $target, mixed $val return $target::converter()->dump($value, state: $state); } + // BackedEnum class-name targets: wrap in EnumOf so enum values are scored + // against the enum's cases. Without this, tryConvert's default case scores + // any class-name target as `no`, even when the value is a valid enum member. + if (is_a($target, class: \BackedEnum::class, allow_string: true)) { + return EnumOf::fromBackedEnum($target)->dump($value, state: $state); + } + self::tryConvert($target, value: $value, state: $state); return self::dump_unknown($value, state: $state); diff --git a/src/Core/Conversion/EnumOf.php b/src/Core/Conversion/EnumOf.php index 970bdf1..ed46cb1 100644 --- a/src/Core/Conversion/EnumOf.php +++ b/src/Core/Conversion/EnumOf.php @@ -14,11 +14,17 @@ final class EnumOf implements Converter { private readonly string $type; + /** @var array, self> */ + private static array $cache = []; + /** * @param list $members + * @param class-string<\BackedEnum>|null $class */ - public function __construct(private readonly array $members) - { + public function __construct( + private readonly array $members, + private readonly ?string $class = null, + ) { $type = 'NULL'; foreach ($this->members as $member) { $type = gettype($member); @@ -26,10 +32,28 @@ public function __construct(private readonly array $members) $this->type = $type; } + /** @param class-string<\BackedEnum> $enum */ + public static function fromBackedEnum(string $enum): self + { + // @phpstan-ignore-next-line argument.type + return self::$cache[$enum] ??= new self( + array_column($enum::cases(), column_key: 'value'), + class: $enum, + ); + } + public function coerce(mixed $value, CoerceState $state): mixed { $this->tally($value, state: $state); + if ($value instanceof \BackedEnum) { + return $value; + } + + if (null !== $this->class && (is_int($value) || is_string($value))) { + return ($this->class)::tryFrom($value) ?? $value; + } + return $value; } @@ -42,9 +66,10 @@ public function dump(mixed $value, DumpState $state): mixed private function tally(mixed $value, CoerceState|DumpState $state): void { - if (in_array($value, haystack: $this->members, strict: true)) { + $needle = $value instanceof \BackedEnum ? $value->value : $value; + if (in_array($needle, haystack: $this->members, strict: true)) { ++$state->yes; - } elseif ($this->type === gettype($value)) { + } elseif ($this->type === gettype($needle)) { ++$state->maybe; } else { ++$state->no; diff --git a/src/Exports/DataExport.php b/src/Exports/DataExport.php new file mode 100644 index 0000000..872aaf8 --- /dev/null +++ b/src/Exports/DataExport.php @@ -0,0 +1,257 @@ +>, + * expiresAt: \DateTimeInterface, + * status: Status|value-of, + * completedAt?: \DateTimeInterface|null, + * dateFrom?: \DateTimeInterface|null, + * dateTo?: \DateTimeInterface|null, + * downloadURL?: string|null, + * errorMessage?: string|null, + * fileSize?: int|null, + * } + */ +final class DataExport implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + #[Required] + public string $id; + + #[Required] + public \DateTimeInterface $createdAt; + + /** @var list> $dataTypes */ + #[Required(list: DataType::class)] + public array $dataTypes; + + /** + * When the export download link expires (24 hours after creation). + */ + #[Required] + public \DateTimeInterface $expiresAt; + + /** + * Status of a data export job. + * + * @var value-of $status + */ + #[Required(enum: Status::class)] + public string $status; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $completedAt; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $dateFrom; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $dateTo; + + /** + * URL to download the export file. Only available when status is 'completed'. + */ + #[Optional('downloadUrl', nullable: true)] + public ?string $downloadURL; + + /** + * Error message if the export failed. + */ + #[Optional(nullable: true)] + public ?string $errorMessage; + + /** + * Size of the export file in bytes. + */ + #[Optional(nullable: true)] + public ?int $fileSize; + + /** + * `new DataExport()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * DataExport::with( + * id: ..., createdAt: ..., dataTypes: ..., expiresAt: ..., status: ... + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new DataExport) + * ->withID(...) + * ->withCreatedAt(...) + * ->withDataTypes(...) + * ->withExpiresAt(...) + * ->withStatus(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list> $dataTypes + * @param Status|value-of $status + */ + public static function with( + string $id, + \DateTimeInterface $createdAt, + array $dataTypes, + \DateTimeInterface $expiresAt, + Status|string $status, + ?\DateTimeInterface $completedAt = null, + ?\DateTimeInterface $dateFrom = null, + ?\DateTimeInterface $dateTo = null, + ?string $downloadURL = null, + ?string $errorMessage = null, + ?int $fileSize = null, + ): self { + $self = new self; + + $self['id'] = $id; + $self['createdAt'] = $createdAt; + $self['dataTypes'] = $dataTypes; + $self['expiresAt'] = $expiresAt; + $self['status'] = $status; + + null !== $completedAt && $self['completedAt'] = $completedAt; + null !== $dateFrom && $self['dateFrom'] = $dateFrom; + null !== $dateTo && $self['dateTo'] = $dateTo; + null !== $downloadURL && $self['downloadURL'] = $downloadURL; + null !== $errorMessage && $self['errorMessage'] = $errorMessage; + null !== $fileSize && $self['fileSize'] = $fileSize; + + return $self; + } + + public function withID(string $id): self + { + $self = clone $this; + $self['id'] = $id; + + return $self; + } + + public function withCreatedAt(\DateTimeInterface $createdAt): self + { + $self = clone $this; + $self['createdAt'] = $createdAt; + + return $self; + } + + /** + * @param list> $dataTypes + */ + public function withDataTypes(array $dataTypes): self + { + $self = clone $this; + $self['dataTypes'] = $dataTypes; + + return $self; + } + + /** + * When the export download link expires (24 hours after creation). + */ + public function withExpiresAt(\DateTimeInterface $expiresAt): self + { + $self = clone $this; + $self['expiresAt'] = $expiresAt; + + return $self; + } + + /** + * Status of a data export job. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } + + public function withCompletedAt(?\DateTimeInterface $completedAt): self + { + $self = clone $this; + $self['completedAt'] = $completedAt; + + return $self; + } + + public function withDateFrom(?\DateTimeInterface $dateFrom): self + { + $self = clone $this; + $self['dateFrom'] = $dateFrom; + + return $self; + } + + public function withDateTo(?\DateTimeInterface $dateTo): self + { + $self = clone $this; + $self['dateTo'] = $dateTo; + + return $self; + } + + /** + * URL to download the export file. Only available when status is 'completed'. + */ + public function withDownloadURL(?string $downloadURL): self + { + $self = clone $this; + $self['downloadURL'] = $downloadURL; + + return $self; + } + + /** + * Error message if the export failed. + */ + public function withErrorMessage(?string $errorMessage): self + { + $self = clone $this; + $self['errorMessage'] = $errorMessage; + + return $self; + } + + /** + * Size of the export file in bytes. + */ + public function withFileSize(?int $fileSize): self + { + $self = clone $this; + $self['fileSize'] = $fileSize; + + return $self; + } +} diff --git a/src/Exports/DataExport/DataType.php b/src/Exports/DataExport/DataType.php new file mode 100644 index 0000000..3858d8d --- /dev/null +++ b/src/Exports/DataExport/DataType.php @@ -0,0 +1,21 @@ +>, + * dateFrom?: \DateTimeInterface|null, + * dateTo?: \DateTimeInterface|null, + * } + */ +final class ExportCreateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + /** + * List of data types to include in the export. + * + * @var list> $dataTypes + */ + #[Required(list: DataType::class)] + public array $dataTypes; + + /** + * Start date for data to export (inclusive). + */ + #[Optional] + public ?\DateTimeInterface $dateFrom; + + /** + * End date for data to export (inclusive). + */ + #[Optional] + public ?\DateTimeInterface $dateTo; + + /** + * `new ExportCreateParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ExportCreateParams::with(dataTypes: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ExportCreateParams)->withDataTypes(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list> $dataTypes + */ + public static function with( + array $dataTypes, + ?\DateTimeInterface $dateFrom = null, + ?\DateTimeInterface $dateTo = null, + ): self { + $self = new self; + + $self['dataTypes'] = $dataTypes; + + null !== $dateFrom && $self['dateFrom'] = $dateFrom; + null !== $dateTo && $self['dateTo'] = $dateTo; + + return $self; + } + + /** + * List of data types to include in the export. + * + * @param list> $dataTypes + */ + public function withDataTypes(array $dataTypes): self + { + $self = clone $this; + $self['dataTypes'] = $dataTypes; + + return $self; + } + + /** + * Start date for data to export (inclusive). + */ + public function withDateFrom(\DateTimeInterface $dateFrom): self + { + $self = clone $this; + $self['dateFrom'] = $dateFrom; + + return $self; + } + + /** + * End date for data to export (inclusive). + */ + public function withDateTo(\DateTimeInterface $dateTo): self + { + $self = clone $this; + $self['dateTo'] = $dateTo; + + return $self; + } +} diff --git a/src/Exports/ExportCreateParams/DataType.php b/src/Exports/ExportCreateParams/DataType.php new file mode 100644 index 0000000..021ef80 --- /dev/null +++ b/src/Exports/ExportCreateParams/DataType.php @@ -0,0 +1,21 @@ + */ + use SdkModel; + + #[Required] + public DataExport $export; + + /** + * `new ExportGetResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ExportGetResponse::with(export: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ExportGetResponse)->withExport(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param DataExport|DataExportShape $export + */ + public static function with(DataExport|array $export): self + { + $self = new self; + + $self['export'] = $export; + + return $self; + } + + /** + * @param DataExport|DataExportShape $export + */ + public function withExport(DataExport|array $export): self + { + $self = clone $this; + $self['export'] = $export; + + return $self; + } +} diff --git a/src/Exports/ExportListParams.php b/src/Exports/ExportListParams.php new file mode 100644 index 0000000..1325805 --- /dev/null +++ b/src/Exports/ExportListParams.php @@ -0,0 +1,96 @@ + + * } + */ +final class ExportListParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + #[Optional] + public ?string $cursor; + + #[Optional] + public ?int $limit; + + /** + * Status of a data export job. + * + * @var value-of|null $status + */ + #[Optional(enum: Status::class)] + public ?string $status; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Status|value-of|null $status + */ + public static function with( + ?string $cursor = null, + ?int $limit = null, + Status|string|null $status = null + ): self { + $self = new self; + + null !== $cursor && $self['cursor'] = $cursor; + null !== $limit && $self['limit'] = $limit; + null !== $status && $self['status'] = $status; + + return $self; + } + + public function withCursor(string $cursor): self + { + $self = clone $this; + $self['cursor'] = $cursor; + + return $self; + } + + public function withLimit(int $limit): self + { + $self = clone $this; + $self['limit'] = $limit; + + return $self; + } + + /** + * Status of a data export job. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } +} diff --git a/src/Exports/ExportListParams/Status.php b/src/Exports/ExportListParams/Status.php new file mode 100644 index 0000000..93bc546 --- /dev/null +++ b/src/Exports/ExportListParams/Status.php @@ -0,0 +1,19 @@ + */ + use SdkModel; + + #[Required] + public DataExport $export; + + /** + * `new ExportNewResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * ExportNewResponse::with(export: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new ExportNewResponse)->withExport(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param DataExport|DataExportShape $export + */ + public static function with(DataExport|array $export): self + { + $self = new self; + + $self['export'] = $export; + + return $self; + } + + /** + * @param DataExport|DataExportShape $export + */ + public function withExport(DataExport|array $export): self + { + $self = clone $this; + $self['export'] = $export; + + return $self; + } +} diff --git a/src/Invitations/Invitation.php b/src/Invitations/Invitation.php new file mode 100644 index 0000000..3583abc --- /dev/null +++ b/src/Invitations/Invitation.php @@ -0,0 +1,315 @@ +, + * updatedAt: \DateTimeInterface, + * url: string, + * clientEmail?: string|null, + * clientName?: string|null, + * clientPhone?: string|null, + * completedAt?: \DateTimeInterface|null, + * phoneNumberID?: string|null, + * senderID?: string|null, + * startedAt?: \DateTimeInterface|null, + * viewedAt?: \DateTimeInterface|null, + * } + */ +final class Invitation implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + #[Required] + public string $id; + + /** + * Unique invitation token. + */ + #[Required] + public string $token; + + #[Required] + public \DateTimeInterface $createdAt; + + #[Required] + public \DateTimeInterface $expiresAt; + + /** + * Current status of the partner invitation. + * + * @var value-of $status + */ + #[Required(enum: Status::class)] + public string $status; + + #[Required] + public \DateTimeInterface $updatedAt; + + /** + * Full URL to share with the client. + */ + #[Required] + public string $url; + + #[Optional(nullable: true)] + public ?string $clientEmail; + + #[Optional(nullable: true)] + public ?string $clientName; + + #[Optional(nullable: true)] + public ?string $clientPhone; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $completedAt; + + /** + * ID of a pre-assigned Zavu phone number for WhatsApp registration. + */ + #[Optional('phoneNumberId', nullable: true)] + public ?string $phoneNumberID; + + /** + * ID of the sender created when invitation is completed. + */ + #[Optional('senderId', nullable: true)] + public ?string $senderID; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $startedAt; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $viewedAt; + + /** + * `new Invitation()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * Invitation::with( + * id: ..., + * token: ..., + * createdAt: ..., + * expiresAt: ..., + * status: ..., + * updatedAt: ..., + * url: ..., + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new Invitation) + * ->withID(...) + * ->withToken(...) + * ->withCreatedAt(...) + * ->withExpiresAt(...) + * ->withStatus(...) + * ->withUpdatedAt(...) + * ->withURL(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Status|value-of $status + */ + public static function with( + string $id, + string $token, + \DateTimeInterface $createdAt, + \DateTimeInterface $expiresAt, + Status|string $status, + \DateTimeInterface $updatedAt, + string $url, + ?string $clientEmail = null, + ?string $clientName = null, + ?string $clientPhone = null, + ?\DateTimeInterface $completedAt = null, + ?string $phoneNumberID = null, + ?string $senderID = null, + ?\DateTimeInterface $startedAt = null, + ?\DateTimeInterface $viewedAt = null, + ): self { + $self = new self; + + $self['id'] = $id; + $self['token'] = $token; + $self['createdAt'] = $createdAt; + $self['expiresAt'] = $expiresAt; + $self['status'] = $status; + $self['updatedAt'] = $updatedAt; + $self['url'] = $url; + + null !== $clientEmail && $self['clientEmail'] = $clientEmail; + null !== $clientName && $self['clientName'] = $clientName; + null !== $clientPhone && $self['clientPhone'] = $clientPhone; + null !== $completedAt && $self['completedAt'] = $completedAt; + null !== $phoneNumberID && $self['phoneNumberID'] = $phoneNumberID; + null !== $senderID && $self['senderID'] = $senderID; + null !== $startedAt && $self['startedAt'] = $startedAt; + null !== $viewedAt && $self['viewedAt'] = $viewedAt; + + return $self; + } + + public function withID(string $id): self + { + $self = clone $this; + $self['id'] = $id; + + return $self; + } + + /** + * Unique invitation token. + */ + public function withToken(string $token): self + { + $self = clone $this; + $self['token'] = $token; + + return $self; + } + + public function withCreatedAt(\DateTimeInterface $createdAt): self + { + $self = clone $this; + $self['createdAt'] = $createdAt; + + return $self; + } + + public function withExpiresAt(\DateTimeInterface $expiresAt): self + { + $self = clone $this; + $self['expiresAt'] = $expiresAt; + + return $self; + } + + /** + * Current status of the partner invitation. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } + + public function withUpdatedAt(\DateTimeInterface $updatedAt): self + { + $self = clone $this; + $self['updatedAt'] = $updatedAt; + + return $self; + } + + /** + * Full URL to share with the client. + */ + public function withURL(string $url): self + { + $self = clone $this; + $self['url'] = $url; + + return $self; + } + + public function withClientEmail(?string $clientEmail): self + { + $self = clone $this; + $self['clientEmail'] = $clientEmail; + + return $self; + } + + public function withClientName(?string $clientName): self + { + $self = clone $this; + $self['clientName'] = $clientName; + + return $self; + } + + public function withClientPhone(?string $clientPhone): self + { + $self = clone $this; + $self['clientPhone'] = $clientPhone; + + return $self; + } + + public function withCompletedAt(?\DateTimeInterface $completedAt): self + { + $self = clone $this; + $self['completedAt'] = $completedAt; + + return $self; + } + + /** + * ID of a pre-assigned Zavu phone number for WhatsApp registration. + */ + public function withPhoneNumberID(?string $phoneNumberID): self + { + $self = clone $this; + $self['phoneNumberID'] = $phoneNumberID; + + return $self; + } + + /** + * ID of the sender created when invitation is completed. + */ + public function withSenderID(?string $senderID): self + { + $self = clone $this; + $self['senderID'] = $senderID; + + return $self; + } + + public function withStartedAt(?\DateTimeInterface $startedAt): self + { + $self = clone $this; + $self['startedAt'] = $startedAt; + + return $self; + } + + public function withViewedAt(?\DateTimeInterface $viewedAt): self + { + $self = clone $this; + $self['viewedAt'] = $viewedAt; + + return $self; + } +} diff --git a/src/Invitations/Invitation/Status.php b/src/Invitations/Invitation/Status.php new file mode 100644 index 0000000..3e71c6a --- /dev/null +++ b/src/Invitations/Invitation/Status.php @@ -0,0 +1,21 @@ + */ + use SdkModel; + + #[Required] + public Invitation $invitation; + + /** + * `new InvitationCancelResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * InvitationCancelResponse::with(invitation: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new InvitationCancelResponse)->withInvitation(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Invitation|InvitationShape $invitation + */ + public static function with(Invitation|array $invitation): self + { + $self = new self; + + $self['invitation'] = $invitation; + + return $self; + } + + /** + * @param Invitation|InvitationShape $invitation + */ + public function withInvitation(Invitation|array $invitation): self + { + $self = clone $this; + $self['invitation'] = $invitation; + + return $self; + } +} diff --git a/src/Invitations/InvitationCreateParams.php b/src/Invitations/InvitationCreateParams.php new file mode 100644 index 0000000..ed581bf --- /dev/null +++ b/src/Invitations/InvitationCreateParams.php @@ -0,0 +1,170 @@ +|null, + * clientEmail?: string|null, + * clientName?: string|null, + * clientPhone?: string|null, + * expiresInDays?: int|null, + * phoneNumberID?: string|null, + * } + */ +final class InvitationCreateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + /** + * ISO country codes for allowed phone numbers. + * + * @var list|null $allowedPhoneCountries + */ + #[Optional(list: 'string')] + public ?array $allowedPhoneCountries; + + /** + * Email of the client being invited. + */ + #[Optional] + public ?string $clientEmail; + + /** + * Name of the client being invited. + */ + #[Optional] + public ?string $clientName; + + /** + * Phone number of the client in E.164 format. + */ + #[Optional] + public ?string $clientPhone; + + /** + * Number of days until the invitation expires. + */ + #[Optional] + public ?int $expiresInDays; + + /** + * ID of a Zavu phone number to pre-assign for WhatsApp registration. If provided, the client will use this number instead of their own. + */ + #[Optional('phoneNumberId')] + public ?string $phoneNumberID; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list|null $allowedPhoneCountries + */ + public static function with( + ?array $allowedPhoneCountries = null, + ?string $clientEmail = null, + ?string $clientName = null, + ?string $clientPhone = null, + ?int $expiresInDays = null, + ?string $phoneNumberID = null, + ): self { + $self = new self; + + null !== $allowedPhoneCountries && $self['allowedPhoneCountries'] = $allowedPhoneCountries; + null !== $clientEmail && $self['clientEmail'] = $clientEmail; + null !== $clientName && $self['clientName'] = $clientName; + null !== $clientPhone && $self['clientPhone'] = $clientPhone; + null !== $expiresInDays && $self['expiresInDays'] = $expiresInDays; + null !== $phoneNumberID && $self['phoneNumberID'] = $phoneNumberID; + + return $self; + } + + /** + * ISO country codes for allowed phone numbers. + * + * @param list $allowedPhoneCountries + */ + public function withAllowedPhoneCountries( + array $allowedPhoneCountries + ): self { + $self = clone $this; + $self['allowedPhoneCountries'] = $allowedPhoneCountries; + + return $self; + } + + /** + * Email of the client being invited. + */ + public function withClientEmail(string $clientEmail): self + { + $self = clone $this; + $self['clientEmail'] = $clientEmail; + + return $self; + } + + /** + * Name of the client being invited. + */ + public function withClientName(string $clientName): self + { + $self = clone $this; + $self['clientName'] = $clientName; + + return $self; + } + + /** + * Phone number of the client in E.164 format. + */ + public function withClientPhone(string $clientPhone): self + { + $self = clone $this; + $self['clientPhone'] = $clientPhone; + + return $self; + } + + /** + * Number of days until the invitation expires. + */ + public function withExpiresInDays(int $expiresInDays): self + { + $self = clone $this; + $self['expiresInDays'] = $expiresInDays; + + return $self; + } + + /** + * ID of a Zavu phone number to pre-assign for WhatsApp registration. If provided, the client will use this number instead of their own. + */ + public function withPhoneNumberID(string $phoneNumberID): self + { + $self = clone $this; + $self['phoneNumberID'] = $phoneNumberID; + + return $self; + } +} diff --git a/src/Invitations/InvitationGetResponse.php b/src/Invitations/InvitationGetResponse.php new file mode 100644 index 0000000..3f1c1e7 --- /dev/null +++ b/src/Invitations/InvitationGetResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public Invitation $invitation; + + /** + * `new InvitationGetResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * InvitationGetResponse::with(invitation: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new InvitationGetResponse)->withInvitation(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Invitation|InvitationShape $invitation + */ + public static function with(Invitation|array $invitation): self + { + $self = new self; + + $self['invitation'] = $invitation; + + return $self; + } + + /** + * @param Invitation|InvitationShape $invitation + */ + public function withInvitation(Invitation|array $invitation): self + { + $self = clone $this; + $self['invitation'] = $invitation; + + return $self; + } +} diff --git a/src/Invitations/InvitationListParams.php b/src/Invitations/InvitationListParams.php new file mode 100644 index 0000000..723aa34 --- /dev/null +++ b/src/Invitations/InvitationListParams.php @@ -0,0 +1,96 @@ + + * } + */ +final class InvitationListParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + #[Optional] + public ?string $cursor; + + #[Optional] + public ?int $limit; + + /** + * Current status of the partner invitation. + * + * @var value-of|null $status + */ + #[Optional(enum: Status::class)] + public ?string $status; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Status|value-of|null $status + */ + public static function with( + ?string $cursor = null, + ?int $limit = null, + Status|string|null $status = null + ): self { + $self = new self; + + null !== $cursor && $self['cursor'] = $cursor; + null !== $limit && $self['limit'] = $limit; + null !== $status && $self['status'] = $status; + + return $self; + } + + public function withCursor(string $cursor): self + { + $self = clone $this; + $self['cursor'] = $cursor; + + return $self; + } + + public function withLimit(int $limit): self + { + $self = clone $this; + $self['limit'] = $limit; + + return $self; + } + + /** + * Current status of the partner invitation. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } +} diff --git a/src/Invitations/InvitationListParams/Status.php b/src/Invitations/InvitationListParams/Status.php new file mode 100644 index 0000000..a385a27 --- /dev/null +++ b/src/Invitations/InvitationListParams/Status.php @@ -0,0 +1,21 @@ + */ + use SdkModel; + + #[Required] + public Invitation $invitation; + + /** + * `new InvitationNewResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * InvitationNewResponse::with(invitation: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new InvitationNewResponse)->withInvitation(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Invitation|InvitationShape $invitation + */ + public static function with(Invitation|array $invitation): self + { + $self = new self; + + $self['invitation'] = $invitation; + + return $self; + } + + /** + * @param Invitation|InvitationShape $invitation + */ + public function withInvitation(Invitation|array $invitation): self + { + $self = clone $this; + $self['invitation'] = $invitation; + + return $self; + } +} diff --git a/src/Messages/MessageContent.php b/src/Messages/MessageContent.php index 504892b..dcddf2f 100644 --- a/src/Messages/MessageContent.php +++ b/src/Messages/MessageContent.php @@ -9,6 +9,7 @@ use Zavudev\Core\Contracts\BaseModel; use Zavudev\Messages\MessageContent\Button; use Zavudev\Messages\MessageContent\Contact; +use Zavudev\Messages\MessageContent\CtaHeaderType; use Zavudev\Messages\MessageContent\Section; /** @@ -21,8 +22,14 @@ * @phpstan-type MessageContentShape = array{ * buttons?: list|null, * contacts?: list|null, + * ctaDisplayText?: string|null, + * ctaHeaderMediaURL?: string|null, + * ctaHeaderText?: string|null, + * ctaHeaderType?: null|CtaHeaderType|value-of, + * ctaURL?: string|null, * emoji?: string|null, * filename?: string|null, + * footerText?: string|null, * latitude?: float|null, * listButton?: string|null, * locationAddress?: string|null, @@ -33,6 +40,7 @@ * mimeType?: string|null, * reactToMessageID?: string|null, * sections?: list|null, + * templateButtonVariables?: array|null, * templateID?: string|null, * templateVariables?: array|null, * } @@ -58,6 +66,38 @@ final class MessageContent implements BaseModel #[Optional(list: Contact::class)] public ?array $contacts; + /** + * Button label for cta_url messages. + */ + #[Optional] + public ?string $ctaDisplayText; + + /** + * Public HTTPS URL of the header media when ctaHeaderType is 'image', 'video', or 'document'. WhatsApp fetches this URL — it must be publicly reachable and return the declared content type. + */ + #[Optional('ctaHeaderMediaUrl')] + public ?string $ctaHeaderMediaURL; + + /** + * Header text when ctaHeaderType is 'text'. + */ + #[Optional] + public ?string $ctaHeaderText; + + /** + * Optional header type for cta_url messages. + * + * @var value-of|null $ctaHeaderType + */ + #[Optional(enum: CtaHeaderType::class)] + public ?string $ctaHeaderType; + + /** + * Destination URL opened in the device's default browser when the button is tapped. Used with messageType=cta_url. WhatsApp requires HTTPS in production. + */ + #[Optional('ctaUrl')] + public ?string $ctaURL; + /** * Emoji for reaction messages. */ @@ -70,6 +110,12 @@ final class MessageContent implements BaseModel #[Optional] public ?string $filename; + /** + * Optional footer text for cta_url messages. + */ + #[Optional] + public ?string $footerText; + /** * Latitude for location messages. */ @@ -132,6 +178,20 @@ final class MessageContent implements BaseModel #[Optional(list: Section::class)] public ?array $sections; + /** + * Variables for dynamic button placeholders (URL buttons and OTP buttons). Keys are the button index (0, 1, 2) in the template's `buttons` array — not the placeholder name. Values substitute the `{{1}}` placeholder inside that button's URL. + * + * **WhatsApp constraints:** + * - URL buttons only accept `{{1}}` — positional, numeric, no whitespace, no name. Named placeholders like `{{token}}` are stored as literal URL text by Meta and cannot be substituted. + * - At most one placeholder per URL button. + * - A template may have at most three buttons. + * - Static URL buttons (no placeholder) and `quick_reply` buttons are not included here. + * + * @var array|null $templateButtonVariables + */ + #[Optional(map: 'string')] + public ?array $templateButtonVariables; + /** * Template ID for template messages. */ @@ -139,7 +199,7 @@ final class MessageContent implements BaseModel public ?string $templateID; /** - * Variables for template rendering. Keys are variable positions (1, 2, 3...). + * Variables for body placeholders. Keys are positions (1, 2, 3, ...) matching the order placeholders appear in the template body. * * @var array|null $templateVariables */ @@ -158,14 +218,22 @@ public function __construct() * * @param list|null $buttons * @param list|null $contacts + * @param CtaHeaderType|value-of|null $ctaHeaderType * @param list|null $sections + * @param array|null $templateButtonVariables * @param array|null $templateVariables */ public static function with( ?array $buttons = null, ?array $contacts = null, + ?string $ctaDisplayText = null, + ?string $ctaHeaderMediaURL = null, + ?string $ctaHeaderText = null, + CtaHeaderType|string|null $ctaHeaderType = null, + ?string $ctaURL = null, ?string $emoji = null, ?string $filename = null, + ?string $footerText = null, ?float $latitude = null, ?string $listButton = null, ?string $locationAddress = null, @@ -176,6 +244,7 @@ public static function with( ?string $mimeType = null, ?string $reactToMessageID = null, ?array $sections = null, + ?array $templateButtonVariables = null, ?string $templateID = null, ?array $templateVariables = null, ): self { @@ -183,8 +252,14 @@ public static function with( null !== $buttons && $self['buttons'] = $buttons; null !== $contacts && $self['contacts'] = $contacts; + null !== $ctaDisplayText && $self['ctaDisplayText'] = $ctaDisplayText; + null !== $ctaHeaderMediaURL && $self['ctaHeaderMediaURL'] = $ctaHeaderMediaURL; + null !== $ctaHeaderText && $self['ctaHeaderText'] = $ctaHeaderText; + null !== $ctaHeaderType && $self['ctaHeaderType'] = $ctaHeaderType; + null !== $ctaURL && $self['ctaURL'] = $ctaURL; null !== $emoji && $self['emoji'] = $emoji; null !== $filename && $self['filename'] = $filename; + null !== $footerText && $self['footerText'] = $footerText; null !== $latitude && $self['latitude'] = $latitude; null !== $listButton && $self['listButton'] = $listButton; null !== $locationAddress && $self['locationAddress'] = $locationAddress; @@ -195,6 +270,7 @@ public static function with( null !== $mimeType && $self['mimeType'] = $mimeType; null !== $reactToMessageID && $self['reactToMessageID'] = $reactToMessageID; null !== $sections && $self['sections'] = $sections; + null !== $templateButtonVariables && $self['templateButtonVariables'] = $templateButtonVariables; null !== $templateID && $self['templateID'] = $templateID; null !== $templateVariables && $self['templateVariables'] = $templateVariables; @@ -227,6 +303,63 @@ public function withContacts(array $contacts): self return $self; } + /** + * Button label for cta_url messages. + */ + public function withCtaDisplayText(string $ctaDisplayText): self + { + $self = clone $this; + $self['ctaDisplayText'] = $ctaDisplayText; + + return $self; + } + + /** + * Public HTTPS URL of the header media when ctaHeaderType is 'image', 'video', or 'document'. WhatsApp fetches this URL — it must be publicly reachable and return the declared content type. + */ + public function withCtaHeaderMediaURL(string $ctaHeaderMediaURL): self + { + $self = clone $this; + $self['ctaHeaderMediaURL'] = $ctaHeaderMediaURL; + + return $self; + } + + /** + * Header text when ctaHeaderType is 'text'. + */ + public function withCtaHeaderText(string $ctaHeaderText): self + { + $self = clone $this; + $self['ctaHeaderText'] = $ctaHeaderText; + + return $self; + } + + /** + * Optional header type for cta_url messages. + * + * @param CtaHeaderType|value-of $ctaHeaderType + */ + public function withCtaHeaderType(CtaHeaderType|string $ctaHeaderType): self + { + $self = clone $this; + $self['ctaHeaderType'] = $ctaHeaderType; + + return $self; + } + + /** + * Destination URL opened in the device's default browser when the button is tapped. Used with messageType=cta_url. WhatsApp requires HTTPS in production. + */ + public function withCtaURL(string $ctaURL): self + { + $self = clone $this; + $self['ctaURL'] = $ctaURL; + + return $self; + } + /** * Emoji for reaction messages. */ @@ -249,6 +382,17 @@ public function withFilename(string $filename): self return $self; } + /** + * Optional footer text for cta_url messages. + */ + public function withFooterText(string $footerText): self + { + $self = clone $this; + $self['footerText'] = $footerText; + + return $self; + } + /** * Latitude for location messages. */ @@ -361,6 +505,26 @@ public function withSections(array $sections): self return $self; } + /** + * Variables for dynamic button placeholders (URL buttons and OTP buttons). Keys are the button index (0, 1, 2) in the template's `buttons` array — not the placeholder name. Values substitute the `{{1}}` placeholder inside that button's URL. + * + * **WhatsApp constraints:** + * - URL buttons only accept `{{1}}` — positional, numeric, no whitespace, no name. Named placeholders like `{{token}}` are stored as literal URL text by Meta and cannot be substituted. + * - At most one placeholder per URL button. + * - A template may have at most three buttons. + * - Static URL buttons (no placeholder) and `quick_reply` buttons are not included here. + * + * @param array $templateButtonVariables + */ + public function withTemplateButtonVariables( + array $templateButtonVariables + ): self { + $self = clone $this; + $self['templateButtonVariables'] = $templateButtonVariables; + + return $self; + } + /** * Template ID for template messages. */ @@ -373,7 +537,7 @@ public function withTemplateID(string $templateID): self } /** - * Variables for template rendering. Keys are variable positions (1, 2, 3...). + * Variables for body placeholders. Keys are positions (1, 2, 3, ...) matching the order placeholders appear in the template body. * * @param array $templateVariables */ diff --git a/src/Messages/MessageContent/CtaHeaderType.php b/src/Messages/MessageContent/CtaHeaderType.php new file mode 100644 index 0000000..f438ce3 --- /dev/null +++ b/src/Messages/MessageContent/CtaHeaderType.php @@ -0,0 +1,19 @@ +, + * phone: string, + * postalCode: string, + * state: string, + * street: string, + * vertical: string, + * companyName?: string|null, + * ein?: string|null, + * firstName?: string|null, + * lastName?: string|null, + * stockExchange?: string|null, + * stockSymbol?: string|null, + * website?: string|null, + * } + */ +final class BrandCreateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + #[Required] + public string $city; + + /** + * Two-letter ISO country code. + */ + #[Required] + public string $country; + + /** + * Display name of the brand. + */ + #[Required] + public string $displayName; + + #[Required] + public string $email; + + /** + * Business entity type for 10DLC brand registration. + * + * @var value-of $entityType + */ + #[Required(enum: EntityType::class)] + public string $entityType; + + /** + * Contact phone in E.164 format. + */ + #[Required] + public string $phone; + + #[Required] + public string $postalCode; + + #[Required] + public string $state; + + #[Required] + public string $street; + + /** + * Industry vertical. + */ + #[Required] + public string $vertical; + + /** + * Legal company name. + */ + #[Optional] + public ?string $companyName; + + /** + * Employer Identification Number (format: XX-XXXXXXX). + */ + #[Optional] + public ?string $ein; + + #[Optional] + public ?string $firstName; + + #[Optional] + public ?string $lastName; + + #[Optional] + public ?string $stockExchange; + + #[Optional] + public ?string $stockSymbol; + + #[Optional] + public ?string $website; + + /** + * `new BrandCreateParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BrandCreateParams::with( + * city: ..., + * country: ..., + * displayName: ..., + * email: ..., + * entityType: ..., + * phone: ..., + * postalCode: ..., + * state: ..., + * street: ..., + * vertical: ..., + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BrandCreateParams) + * ->withCity(...) + * ->withCountry(...) + * ->withDisplayName(...) + * ->withEmail(...) + * ->withEntityType(...) + * ->withPhone(...) + * ->withPostalCode(...) + * ->withState(...) + * ->withStreet(...) + * ->withVertical(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param EntityType|value-of $entityType + */ + public static function with( + string $city, + string $country, + string $displayName, + string $email, + EntityType|string $entityType, + string $phone, + string $postalCode, + string $state, + string $street, + string $vertical, + ?string $companyName = null, + ?string $ein = null, + ?string $firstName = null, + ?string $lastName = null, + ?string $stockExchange = null, + ?string $stockSymbol = null, + ?string $website = null, + ): self { + $self = new self; + + $self['city'] = $city; + $self['country'] = $country; + $self['displayName'] = $displayName; + $self['email'] = $email; + $self['entityType'] = $entityType; + $self['phone'] = $phone; + $self['postalCode'] = $postalCode; + $self['state'] = $state; + $self['street'] = $street; + $self['vertical'] = $vertical; + + null !== $companyName && $self['companyName'] = $companyName; + null !== $ein && $self['ein'] = $ein; + null !== $firstName && $self['firstName'] = $firstName; + null !== $lastName && $self['lastName'] = $lastName; + null !== $stockExchange && $self['stockExchange'] = $stockExchange; + null !== $stockSymbol && $self['stockSymbol'] = $stockSymbol; + null !== $website && $self['website'] = $website; + + return $self; + } + + public function withCity(string $city): self + { + $self = clone $this; + $self['city'] = $city; + + return $self; + } + + /** + * Two-letter ISO country code. + */ + public function withCountry(string $country): self + { + $self = clone $this; + $self['country'] = $country; + + return $self; + } + + /** + * Display name of the brand. + */ + public function withDisplayName(string $displayName): self + { + $self = clone $this; + $self['displayName'] = $displayName; + + return $self; + } + + public function withEmail(string $email): self + { + $self = clone $this; + $self['email'] = $email; + + return $self; + } + + /** + * Business entity type for 10DLC brand registration. + * + * @param EntityType|value-of $entityType + */ + public function withEntityType(EntityType|string $entityType): self + { + $self = clone $this; + $self['entityType'] = $entityType; + + return $self; + } + + /** + * Contact phone in E.164 format. + */ + public function withPhone(string $phone): self + { + $self = clone $this; + $self['phone'] = $phone; + + return $self; + } + + public function withPostalCode(string $postalCode): self + { + $self = clone $this; + $self['postalCode'] = $postalCode; + + return $self; + } + + public function withState(string $state): self + { + $self = clone $this; + $self['state'] = $state; + + return $self; + } + + public function withStreet(string $street): self + { + $self = clone $this; + $self['street'] = $street; + + return $self; + } + + /** + * Industry vertical. + */ + public function withVertical(string $vertical): self + { + $self = clone $this; + $self['vertical'] = $vertical; + + return $self; + } + + /** + * Legal company name. + */ + public function withCompanyName(string $companyName): self + { + $self = clone $this; + $self['companyName'] = $companyName; + + return $self; + } + + /** + * Employer Identification Number (format: XX-XXXXXXX). + */ + public function withEin(string $ein): self + { + $self = clone $this; + $self['ein'] = $ein; + + return $self; + } + + public function withFirstName(string $firstName): self + { + $self = clone $this; + $self['firstName'] = $firstName; + + return $self; + } + + public function withLastName(string $lastName): self + { + $self = clone $this; + $self['lastName'] = $lastName; + + return $self; + } + + public function withStockExchange(string $stockExchange): self + { + $self = clone $this; + $self['stockExchange'] = $stockExchange; + + return $self; + } + + public function withStockSymbol(string $stockSymbol): self + { + $self = clone $this; + $self['stockSymbol'] = $stockSymbol; + + return $self; + } + + public function withWebsite(string $website): self + { + $self = clone $this; + $self['website'] = $website; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/BrandCreateParams/EntityType.php b/src/Number10dlc/Brands/BrandCreateParams/EntityType.php new file mode 100644 index 0000000..a654344 --- /dev/null +++ b/src/Number10dlc/Brands/BrandCreateParams/EntityType.php @@ -0,0 +1,21 @@ + */ + use SdkModel; + + #[Required] + public TenDlcBrand $brand; + + /** + * `new BrandGetResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BrandGetResponse::with(brand: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BrandGetResponse)->withBrand(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcBrand|TenDlcBrandShape $brand + */ + public static function with(TenDlcBrand|array $brand): self + { + $self = new self; + + $self['brand'] = $brand; + + return $self; + } + + /** + * @param TenDlcBrand|TenDlcBrandShape $brand + */ + public function withBrand(TenDlcBrand|array $brand): self + { + $self = clone $this; + $self['brand'] = $brand; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/BrandListParams.php b/src/Number10dlc/Brands/BrandListParams.php new file mode 100644 index 0000000..52b3e9f --- /dev/null +++ b/src/Number10dlc/Brands/BrandListParams.php @@ -0,0 +1,68 @@ + */ + use SdkModel; + use SdkParams; + + #[Optional] + public ?string $cursor; + + #[Optional] + public ?int $limit; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with(?string $cursor = null, ?int $limit = null): self + { + $self = new self; + + null !== $cursor && $self['cursor'] = $cursor; + null !== $limit && $self['limit'] = $limit; + + return $self; + } + + public function withCursor(string $cursor): self + { + $self = clone $this; + $self['cursor'] = $cursor; + + return $self; + } + + public function withLimit(int $limit): self + { + $self = clone $this; + $self['limit'] = $limit; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/BrandListUseCasesResponse.php b/src/Number10dlc/Brands/BrandListUseCasesResponse.php new file mode 100644 index 0000000..916ceda --- /dev/null +++ b/src/Number10dlc/Brands/BrandListUseCasesResponse.php @@ -0,0 +1,73 @@ + + * } + */ +final class BrandListUseCasesResponse implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** @var list $useCases */ + #[Required(list: UseCase::class)] + public array $useCases; + + /** + * `new BrandListUseCasesResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BrandListUseCasesResponse::with(useCases: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BrandListUseCasesResponse)->withUseCases(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $useCases + */ + public static function with(array $useCases): self + { + $self = new self; + + $self['useCases'] = $useCases; + + return $self; + } + + /** + * @param list $useCases + */ + public function withUseCases(array $useCases): self + { + $self = clone $this; + $self['useCases'] = $useCases; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/BrandListUseCasesResponse/UseCase.php b/src/Number10dlc/Brands/BrandListUseCasesResponse/UseCase.php new file mode 100644 index 0000000..7d50474 --- /dev/null +++ b/src/Number10dlc/Brands/BrandListUseCasesResponse/UseCase.php @@ -0,0 +1,64 @@ + */ + use SdkModel; + + #[Optional] + public ?string $description; + + #[Optional] + public ?string $name; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with( + ?string $description = null, + ?string $name = null + ): self { + $self = new self; + + null !== $description && $self['description'] = $description; + null !== $name && $self['name'] = $name; + + return $self; + } + + public function withDescription(string $description): self + { + $self = clone $this; + $self['description'] = $description; + + return $self; + } + + public function withName(string $name): self + { + $self = clone $this; + $self['name'] = $name; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/BrandNewResponse.php b/src/Number10dlc/Brands/BrandNewResponse.php new file mode 100644 index 0000000..e867072 --- /dev/null +++ b/src/Number10dlc/Brands/BrandNewResponse.php @@ -0,0 +1,69 @@ + */ + use SdkModel; + + #[Required] + public TenDlcBrand $brand; + + /** + * `new BrandNewResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BrandNewResponse::with(brand: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BrandNewResponse)->withBrand(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcBrand|TenDlcBrandShape $brand + */ + public static function with(TenDlcBrand|array $brand): self + { + $self = new self; + + $self['brand'] = $brand; + + return $self; + } + + /** + * @param TenDlcBrand|TenDlcBrandShape $brand + */ + public function withBrand(TenDlcBrand|array $brand): self + { + $self = clone $this; + $self['brand'] = $brand; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/BrandSubmitResponse.php b/src/Number10dlc/Brands/BrandSubmitResponse.php new file mode 100644 index 0000000..2a72c3a --- /dev/null +++ b/src/Number10dlc/Brands/BrandSubmitResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public TenDlcBrand $brand; + + /** + * `new BrandSubmitResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BrandSubmitResponse::with(brand: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BrandSubmitResponse)->withBrand(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcBrand|TenDlcBrandShape $brand + */ + public static function with(TenDlcBrand|array $brand): self + { + $self = new self; + + $self['brand'] = $brand; + + return $self; + } + + /** + * @param TenDlcBrand|TenDlcBrandShape $brand + */ + public function withBrand(TenDlcBrand|array $brand): self + { + $self = clone $this; + $self['brand'] = $brand; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/BrandSyncStatusResponse.php b/src/Number10dlc/Brands/BrandSyncStatusResponse.php new file mode 100644 index 0000000..5ea806a --- /dev/null +++ b/src/Number10dlc/Brands/BrandSyncStatusResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public TenDlcBrand $brand; + + /** + * `new BrandSyncStatusResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BrandSyncStatusResponse::with(brand: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BrandSyncStatusResponse)->withBrand(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcBrand|TenDlcBrandShape $brand + */ + public static function with(TenDlcBrand|array $brand): self + { + $self = new self; + + $self['brand'] = $brand; + + return $self; + } + + /** + * @param TenDlcBrand|TenDlcBrandShape $brand + */ + public function withBrand(TenDlcBrand|array $brand): self + { + $self = clone $this; + $self['brand'] = $brand; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/BrandUpdateParams.php b/src/Number10dlc/Brands/BrandUpdateParams.php new file mode 100644 index 0000000..4023899 --- /dev/null +++ b/src/Number10dlc/Brands/BrandUpdateParams.php @@ -0,0 +1,294 @@ +, + * firstName?: string|null, + * lastName?: string|null, + * phone?: string|null, + * postalCode?: string|null, + * state?: string|null, + * stockExchange?: string|null, + * stockSymbol?: string|null, + * street?: string|null, + * vertical?: string|null, + * website?: string|null, + * } + */ +final class BrandUpdateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + #[Optional] + public ?string $city; + + #[Optional] + public ?string $companyName; + + #[Optional] + public ?string $country; + + #[Optional] + public ?string $displayName; + + #[Optional] + public ?string $ein; + + #[Optional] + public ?string $email; + + /** + * Business entity type for 10DLC brand registration. + * + * @var value-of|null $entityType + */ + #[Optional(enum: EntityType::class)] + public ?string $entityType; + + #[Optional] + public ?string $firstName; + + #[Optional] + public ?string $lastName; + + #[Optional] + public ?string $phone; + + #[Optional] + public ?string $postalCode; + + #[Optional] + public ?string $state; + + #[Optional] + public ?string $stockExchange; + + #[Optional] + public ?string $stockSymbol; + + #[Optional] + public ?string $street; + + #[Optional] + public ?string $vertical; + + #[Optional] + public ?string $website; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param EntityType|value-of|null $entityType + */ + public static function with( + ?string $city = null, + ?string $companyName = null, + ?string $country = null, + ?string $displayName = null, + ?string $ein = null, + ?string $email = null, + EntityType|string|null $entityType = null, + ?string $firstName = null, + ?string $lastName = null, + ?string $phone = null, + ?string $postalCode = null, + ?string $state = null, + ?string $stockExchange = null, + ?string $stockSymbol = null, + ?string $street = null, + ?string $vertical = null, + ?string $website = null, + ): self { + $self = new self; + + null !== $city && $self['city'] = $city; + null !== $companyName && $self['companyName'] = $companyName; + null !== $country && $self['country'] = $country; + null !== $displayName && $self['displayName'] = $displayName; + null !== $ein && $self['ein'] = $ein; + null !== $email && $self['email'] = $email; + null !== $entityType && $self['entityType'] = $entityType; + null !== $firstName && $self['firstName'] = $firstName; + null !== $lastName && $self['lastName'] = $lastName; + null !== $phone && $self['phone'] = $phone; + null !== $postalCode && $self['postalCode'] = $postalCode; + null !== $state && $self['state'] = $state; + null !== $stockExchange && $self['stockExchange'] = $stockExchange; + null !== $stockSymbol && $self['stockSymbol'] = $stockSymbol; + null !== $street && $self['street'] = $street; + null !== $vertical && $self['vertical'] = $vertical; + null !== $website && $self['website'] = $website; + + return $self; + } + + public function withCity(string $city): self + { + $self = clone $this; + $self['city'] = $city; + + return $self; + } + + public function withCompanyName(string $companyName): self + { + $self = clone $this; + $self['companyName'] = $companyName; + + return $self; + } + + public function withCountry(string $country): self + { + $self = clone $this; + $self['country'] = $country; + + return $self; + } + + public function withDisplayName(string $displayName): self + { + $self = clone $this; + $self['displayName'] = $displayName; + + return $self; + } + + public function withEin(string $ein): self + { + $self = clone $this; + $self['ein'] = $ein; + + return $self; + } + + public function withEmail(string $email): self + { + $self = clone $this; + $self['email'] = $email; + + return $self; + } + + /** + * Business entity type for 10DLC brand registration. + * + * @param EntityType|value-of $entityType + */ + public function withEntityType(EntityType|string $entityType): self + { + $self = clone $this; + $self['entityType'] = $entityType; + + return $self; + } + + public function withFirstName(string $firstName): self + { + $self = clone $this; + $self['firstName'] = $firstName; + + return $self; + } + + public function withLastName(string $lastName): self + { + $self = clone $this; + $self['lastName'] = $lastName; + + return $self; + } + + public function withPhone(string $phone): self + { + $self = clone $this; + $self['phone'] = $phone; + + return $self; + } + + public function withPostalCode(string $postalCode): self + { + $self = clone $this; + $self['postalCode'] = $postalCode; + + return $self; + } + + public function withState(string $state): self + { + $self = clone $this; + $self['state'] = $state; + + return $self; + } + + public function withStockExchange(string $stockExchange): self + { + $self = clone $this; + $self['stockExchange'] = $stockExchange; + + return $self; + } + + public function withStockSymbol(string $stockSymbol): self + { + $self = clone $this; + $self['stockSymbol'] = $stockSymbol; + + return $self; + } + + public function withStreet(string $street): self + { + $self = clone $this; + $self['street'] = $street; + + return $self; + } + + public function withVertical(string $vertical): self + { + $self = clone $this; + $self['vertical'] = $vertical; + + return $self; + } + + public function withWebsite(string $website): self + { + $self = clone $this; + $self['website'] = $website; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/BrandUpdateParams/EntityType.php b/src/Number10dlc/Brands/BrandUpdateParams/EntityType.php new file mode 100644 index 0000000..22c51cb --- /dev/null +++ b/src/Number10dlc/Brands/BrandUpdateParams/EntityType.php @@ -0,0 +1,21 @@ + */ + use SdkModel; + + #[Required] + public TenDlcBrand $brand; + + /** + * `new BrandUpdateResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * BrandUpdateResponse::with(brand: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new BrandUpdateResponse)->withBrand(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcBrand|TenDlcBrandShape $brand + */ + public static function with(TenDlcBrand|array $brand): self + { + $self = new self; + + $self['brand'] = $brand; + + return $self; + } + + /** + * @param TenDlcBrand|TenDlcBrandShape $brand + */ + public function withBrand(TenDlcBrand|array $brand): self + { + $self = clone $this; + $self['brand'] = $brand; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/TenDlcBrand.php b/src/Number10dlc/Brands/TenDlcBrand.php new file mode 100644 index 0000000..c847489 --- /dev/null +++ b/src/Number10dlc/Brands/TenDlcBrand.php @@ -0,0 +1,519 @@ +, + * phone: string, + * postalCode: string, + * state: string, + * status: Status|value-of, + * street: string, + * updatedAt: \DateTimeInterface, + * vertical: string, + * brandRelationship?: string|null, + * brandScore?: int|null, + * companyName?: string|null, + * ein?: string|null, + * failureReason?: string|null, + * firstName?: string|null, + * lastName?: string|null, + * stockExchange?: string|null, + * stockSymbol?: string|null, + * submittedAt?: \DateTimeInterface|null, + * verifiedAt?: \DateTimeInterface|null, + * website?: string|null, + * } + */ +final class TenDlcBrand implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + #[Required] + public string $id; + + #[Required] + public string $city; + + /** + * Two-letter ISO country code. + */ + #[Required] + public string $country; + + #[Required] + public \DateTimeInterface $createdAt; + + /** + * Display name of the brand. + */ + #[Required] + public string $displayName; + + #[Required] + public string $email; + + /** + * Business entity type for 10DLC brand registration. + * + * @var value-of $entityType + */ + #[Required(enum: EntityType::class)] + public string $entityType; + + /** + * Contact phone number in E.164 format. + */ + #[Required] + public string $phone; + + #[Required] + public string $postalCode; + + #[Required] + public string $state; + + /** + * Status of a 10DLC brand registration. + * + * @var value-of $status + */ + #[Required(enum: Status::class)] + public string $status; + + #[Required] + public string $street; + + #[Required] + public \DateTimeInterface $updatedAt; + + /** + * Industry vertical. + */ + #[Required] + public string $vertical; + + #[Optional(nullable: true)] + public ?string $brandRelationship; + + /** + * Trust score assigned by TCR after vetting. + */ + #[Optional(nullable: true)] + public ?int $brandScore; + + /** + * Legal company name. + */ + #[Optional(nullable: true)] + public ?string $companyName; + + /** + * Employer Identification Number (EIN). + */ + #[Optional(nullable: true)] + public ?string $ein; + + /** + * Reason for rejection, if applicable. + */ + #[Optional(nullable: true)] + public ?string $failureReason; + + #[Optional(nullable: true)] + public ?string $firstName; + + #[Optional(nullable: true)] + public ?string $lastName; + + #[Optional(nullable: true)] + public ?string $stockExchange; + + #[Optional(nullable: true)] + public ?string $stockSymbol; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $submittedAt; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $verifiedAt; + + #[Optional(nullable: true)] + public ?string $website; + + /** + * `new TenDlcBrand()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * TenDlcBrand::with( + * id: ..., + * city: ..., + * country: ..., + * createdAt: ..., + * displayName: ..., + * email: ..., + * entityType: ..., + * phone: ..., + * postalCode: ..., + * state: ..., + * status: ..., + * street: ..., + * updatedAt: ..., + * vertical: ..., + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new TenDlcBrand) + * ->withID(...) + * ->withCity(...) + * ->withCountry(...) + * ->withCreatedAt(...) + * ->withDisplayName(...) + * ->withEmail(...) + * ->withEntityType(...) + * ->withPhone(...) + * ->withPostalCode(...) + * ->withState(...) + * ->withStatus(...) + * ->withStreet(...) + * ->withUpdatedAt(...) + * ->withVertical(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param EntityType|value-of $entityType + * @param Status|value-of $status + */ + public static function with( + string $id, + string $city, + string $country, + \DateTimeInterface $createdAt, + string $displayName, + string $email, + EntityType|string $entityType, + string $phone, + string $postalCode, + string $state, + Status|string $status, + string $street, + \DateTimeInterface $updatedAt, + string $vertical, + ?string $brandRelationship = null, + ?int $brandScore = null, + ?string $companyName = null, + ?string $ein = null, + ?string $failureReason = null, + ?string $firstName = null, + ?string $lastName = null, + ?string $stockExchange = null, + ?string $stockSymbol = null, + ?\DateTimeInterface $submittedAt = null, + ?\DateTimeInterface $verifiedAt = null, + ?string $website = null, + ): self { + $self = new self; + + $self['id'] = $id; + $self['city'] = $city; + $self['country'] = $country; + $self['createdAt'] = $createdAt; + $self['displayName'] = $displayName; + $self['email'] = $email; + $self['entityType'] = $entityType; + $self['phone'] = $phone; + $self['postalCode'] = $postalCode; + $self['state'] = $state; + $self['status'] = $status; + $self['street'] = $street; + $self['updatedAt'] = $updatedAt; + $self['vertical'] = $vertical; + + null !== $brandRelationship && $self['brandRelationship'] = $brandRelationship; + null !== $brandScore && $self['brandScore'] = $brandScore; + null !== $companyName && $self['companyName'] = $companyName; + null !== $ein && $self['ein'] = $ein; + null !== $failureReason && $self['failureReason'] = $failureReason; + null !== $firstName && $self['firstName'] = $firstName; + null !== $lastName && $self['lastName'] = $lastName; + null !== $stockExchange && $self['stockExchange'] = $stockExchange; + null !== $stockSymbol && $self['stockSymbol'] = $stockSymbol; + null !== $submittedAt && $self['submittedAt'] = $submittedAt; + null !== $verifiedAt && $self['verifiedAt'] = $verifiedAt; + null !== $website && $self['website'] = $website; + + return $self; + } + + public function withID(string $id): self + { + $self = clone $this; + $self['id'] = $id; + + return $self; + } + + public function withCity(string $city): self + { + $self = clone $this; + $self['city'] = $city; + + return $self; + } + + /** + * Two-letter ISO country code. + */ + public function withCountry(string $country): self + { + $self = clone $this; + $self['country'] = $country; + + return $self; + } + + public function withCreatedAt(\DateTimeInterface $createdAt): self + { + $self = clone $this; + $self['createdAt'] = $createdAt; + + return $self; + } + + /** + * Display name of the brand. + */ + public function withDisplayName(string $displayName): self + { + $self = clone $this; + $self['displayName'] = $displayName; + + return $self; + } + + public function withEmail(string $email): self + { + $self = clone $this; + $self['email'] = $email; + + return $self; + } + + /** + * Business entity type for 10DLC brand registration. + * + * @param EntityType|value-of $entityType + */ + public function withEntityType(EntityType|string $entityType): self + { + $self = clone $this; + $self['entityType'] = $entityType; + + return $self; + } + + /** + * Contact phone number in E.164 format. + */ + public function withPhone(string $phone): self + { + $self = clone $this; + $self['phone'] = $phone; + + return $self; + } + + public function withPostalCode(string $postalCode): self + { + $self = clone $this; + $self['postalCode'] = $postalCode; + + return $self; + } + + public function withState(string $state): self + { + $self = clone $this; + $self['state'] = $state; + + return $self; + } + + /** + * Status of a 10DLC brand registration. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } + + public function withStreet(string $street): self + { + $self = clone $this; + $self['street'] = $street; + + return $self; + } + + public function withUpdatedAt(\DateTimeInterface $updatedAt): self + { + $self = clone $this; + $self['updatedAt'] = $updatedAt; + + return $self; + } + + /** + * Industry vertical. + */ + public function withVertical(string $vertical): self + { + $self = clone $this; + $self['vertical'] = $vertical; + + return $self; + } + + public function withBrandRelationship(?string $brandRelationship): self + { + $self = clone $this; + $self['brandRelationship'] = $brandRelationship; + + return $self; + } + + /** + * Trust score assigned by TCR after vetting. + */ + public function withBrandScore(?int $brandScore): self + { + $self = clone $this; + $self['brandScore'] = $brandScore; + + return $self; + } + + /** + * Legal company name. + */ + public function withCompanyName(?string $companyName): self + { + $self = clone $this; + $self['companyName'] = $companyName; + + return $self; + } + + /** + * Employer Identification Number (EIN). + */ + public function withEin(?string $ein): self + { + $self = clone $this; + $self['ein'] = $ein; + + return $self; + } + + /** + * Reason for rejection, if applicable. + */ + public function withFailureReason(?string $failureReason): self + { + $self = clone $this; + $self['failureReason'] = $failureReason; + + return $self; + } + + public function withFirstName(?string $firstName): self + { + $self = clone $this; + $self['firstName'] = $firstName; + + return $self; + } + + public function withLastName(?string $lastName): self + { + $self = clone $this; + $self['lastName'] = $lastName; + + return $self; + } + + public function withStockExchange(?string $stockExchange): self + { + $self = clone $this; + $self['stockExchange'] = $stockExchange; + + return $self; + } + + public function withStockSymbol(?string $stockSymbol): self + { + $self = clone $this; + $self['stockSymbol'] = $stockSymbol; + + return $self; + } + + public function withSubmittedAt(?\DateTimeInterface $submittedAt): self + { + $self = clone $this; + $self['submittedAt'] = $submittedAt; + + return $self; + } + + public function withVerifiedAt(?\DateTimeInterface $verifiedAt): self + { + $self = clone $this; + $self['verifiedAt'] = $verifiedAt; + + return $self; + } + + public function withWebsite(?string $website): self + { + $self = clone $this; + $self['website'] = $website; + + return $self; + } +} diff --git a/src/Number10dlc/Brands/TenDlcBrand/EntityType.php b/src/Number10dlc/Brands/TenDlcBrand/EntityType.php new file mode 100644 index 0000000..d7220b7 --- /dev/null +++ b/src/Number10dlc/Brands/TenDlcBrand/EntityType.php @@ -0,0 +1,21 @@ +, + * subscriberHelp: bool, + * subscriberOptIn: bool, + * subscriberOptOut: bool, + * useCase: string, + * helpMessage?: string|null, + * messageFlow?: string|null, + * optInKeywords?: list|null, + * optOutKeywords?: list|null, + * subUseCases?: list|null, + * } + */ +final class CampaignCreateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + #[Required] + public bool $affiliateMarketing; + + #[Required] + public bool $ageGated; + + /** + * ID of the brand to create this campaign under. + */ + #[Required('brandId')] + public string $brandID; + + #[Required] + public string $description; + + #[Required] + public bool $directLending; + + #[Required] + public bool $embeddedLink; + + #[Required] + public bool $embeddedPhone; + + #[Required] + public string $name; + + #[Required] + public bool $numberPooling; + + /** @var list $sampleMessages */ + #[Required(list: 'string')] + public array $sampleMessages; + + #[Required] + public bool $subscriberHelp; + + #[Required] + public bool $subscriberOptIn; + + #[Required] + public bool $subscriberOptOut; + + /** + * Campaign use case (e.g., ACCOUNT_NOTIFICATION, MARKETING, 2FA). + */ + #[Required] + public string $useCase; + + #[Optional] + public ?string $helpMessage; + + #[Optional] + public ?string $messageFlow; + + /** @var list|null $optInKeywords */ + #[Optional(list: 'string')] + public ?array $optInKeywords; + + /** @var list|null $optOutKeywords */ + #[Optional(list: 'string')] + public ?array $optOutKeywords; + + /** @var list|null $subUseCases */ + #[Optional(list: 'string')] + public ?array $subUseCases; + + /** + * `new CampaignCreateParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * CampaignCreateParams::with( + * affiliateMarketing: ..., + * ageGated: ..., + * brandID: ..., + * description: ..., + * directLending: ..., + * embeddedLink: ..., + * embeddedPhone: ..., + * name: ..., + * numberPooling: ..., + * sampleMessages: ..., + * subscriberHelp: ..., + * subscriberOptIn: ..., + * subscriberOptOut: ..., + * useCase: ..., + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new CampaignCreateParams) + * ->withAffiliateMarketing(...) + * ->withAgeGated(...) + * ->withBrandID(...) + * ->withDescription(...) + * ->withDirectLending(...) + * ->withEmbeddedLink(...) + * ->withEmbeddedPhone(...) + * ->withName(...) + * ->withNumberPooling(...) + * ->withSampleMessages(...) + * ->withSubscriberHelp(...) + * ->withSubscriberOptIn(...) + * ->withSubscriberOptOut(...) + * ->withUseCase(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $sampleMessages + * @param list|null $optInKeywords + * @param list|null $optOutKeywords + * @param list|null $subUseCases + */ + public static function with( + bool $affiliateMarketing, + bool $ageGated, + string $brandID, + string $description, + bool $directLending, + bool $embeddedLink, + bool $embeddedPhone, + string $name, + bool $numberPooling, + array $sampleMessages, + bool $subscriberHelp, + bool $subscriberOptIn, + bool $subscriberOptOut, + string $useCase, + ?string $helpMessage = null, + ?string $messageFlow = null, + ?array $optInKeywords = null, + ?array $optOutKeywords = null, + ?array $subUseCases = null, + ): self { + $self = new self; + + $self['affiliateMarketing'] = $affiliateMarketing; + $self['ageGated'] = $ageGated; + $self['brandID'] = $brandID; + $self['description'] = $description; + $self['directLending'] = $directLending; + $self['embeddedLink'] = $embeddedLink; + $self['embeddedPhone'] = $embeddedPhone; + $self['name'] = $name; + $self['numberPooling'] = $numberPooling; + $self['sampleMessages'] = $sampleMessages; + $self['subscriberHelp'] = $subscriberHelp; + $self['subscriberOptIn'] = $subscriberOptIn; + $self['subscriberOptOut'] = $subscriberOptOut; + $self['useCase'] = $useCase; + + null !== $helpMessage && $self['helpMessage'] = $helpMessage; + null !== $messageFlow && $self['messageFlow'] = $messageFlow; + null !== $optInKeywords && $self['optInKeywords'] = $optInKeywords; + null !== $optOutKeywords && $self['optOutKeywords'] = $optOutKeywords; + null !== $subUseCases && $self['subUseCases'] = $subUseCases; + + return $self; + } + + public function withAffiliateMarketing(bool $affiliateMarketing): self + { + $self = clone $this; + $self['affiliateMarketing'] = $affiliateMarketing; + + return $self; + } + + public function withAgeGated(bool $ageGated): self + { + $self = clone $this; + $self['ageGated'] = $ageGated; + + return $self; + } + + /** + * ID of the brand to create this campaign under. + */ + public function withBrandID(string $brandID): self + { + $self = clone $this; + $self['brandID'] = $brandID; + + return $self; + } + + public function withDescription(string $description): self + { + $self = clone $this; + $self['description'] = $description; + + return $self; + } + + public function withDirectLending(bool $directLending): self + { + $self = clone $this; + $self['directLending'] = $directLending; + + return $self; + } + + public function withEmbeddedLink(bool $embeddedLink): self + { + $self = clone $this; + $self['embeddedLink'] = $embeddedLink; + + return $self; + } + + public function withEmbeddedPhone(bool $embeddedPhone): self + { + $self = clone $this; + $self['embeddedPhone'] = $embeddedPhone; + + return $self; + } + + public function withName(string $name): self + { + $self = clone $this; + $self['name'] = $name; + + return $self; + } + + public function withNumberPooling(bool $numberPooling): self + { + $self = clone $this; + $self['numberPooling'] = $numberPooling; + + return $self; + } + + /** + * @param list $sampleMessages + */ + public function withSampleMessages(array $sampleMessages): self + { + $self = clone $this; + $self['sampleMessages'] = $sampleMessages; + + return $self; + } + + public function withSubscriberHelp(bool $subscriberHelp): self + { + $self = clone $this; + $self['subscriberHelp'] = $subscriberHelp; + + return $self; + } + + public function withSubscriberOptIn(bool $subscriberOptIn): self + { + $self = clone $this; + $self['subscriberOptIn'] = $subscriberOptIn; + + return $self; + } + + public function withSubscriberOptOut(bool $subscriberOptOut): self + { + $self = clone $this; + $self['subscriberOptOut'] = $subscriberOptOut; + + return $self; + } + + /** + * Campaign use case (e.g., ACCOUNT_NOTIFICATION, MARKETING, 2FA). + */ + public function withUseCase(string $useCase): self + { + $self = clone $this; + $self['useCase'] = $useCase; + + return $self; + } + + public function withHelpMessage(string $helpMessage): self + { + $self = clone $this; + $self['helpMessage'] = $helpMessage; + + return $self; + } + + public function withMessageFlow(string $messageFlow): self + { + $self = clone $this; + $self['messageFlow'] = $messageFlow; + + return $self; + } + + /** + * @param list $optInKeywords + */ + public function withOptInKeywords(array $optInKeywords): self + { + $self = clone $this; + $self['optInKeywords'] = $optInKeywords; + + return $self; + } + + /** + * @param list $optOutKeywords + */ + public function withOptOutKeywords(array $optOutKeywords): self + { + $self = clone $this; + $self['optOutKeywords'] = $optOutKeywords; + + return $self; + } + + /** + * @param list $subUseCases + */ + public function withSubUseCases(array $subUseCases): self + { + $self = clone $this; + $self['subUseCases'] = $subUseCases; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/CampaignGetResponse.php b/src/Number10dlc/Campaigns/CampaignGetResponse.php new file mode 100644 index 0000000..d90dc3f --- /dev/null +++ b/src/Number10dlc/Campaigns/CampaignGetResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public TenDlcCampaign $campaign; + + /** + * `new CampaignGetResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * CampaignGetResponse::with(campaign: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new CampaignGetResponse)->withCampaign(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcCampaign|TenDlcCampaignShape $campaign + */ + public static function with(TenDlcCampaign|array $campaign): self + { + $self = new self; + + $self['campaign'] = $campaign; + + return $self; + } + + /** + * @param TenDlcCampaign|TenDlcCampaignShape $campaign + */ + public function withCampaign(TenDlcCampaign|array $campaign): self + { + $self = clone $this; + $self['campaign'] = $campaign; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/CampaignListParams.php b/src/Number10dlc/Campaigns/CampaignListParams.php new file mode 100644 index 0000000..81b9fae --- /dev/null +++ b/src/Number10dlc/Campaigns/CampaignListParams.php @@ -0,0 +1,89 @@ + */ + use SdkModel; + use SdkParams; + + /** + * Filter campaigns by brand ID. + */ + #[Optional] + public ?string $brandID; + + #[Optional] + public ?string $cursor; + + #[Optional] + public ?int $limit; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with( + ?string $brandID = null, + ?string $cursor = null, + ?int $limit = null + ): self { + $self = new self; + + null !== $brandID && $self['brandID'] = $brandID; + null !== $cursor && $self['cursor'] = $cursor; + null !== $limit && $self['limit'] = $limit; + + return $self; + } + + /** + * Filter campaigns by brand ID. + */ + public function withBrandID(string $brandID): self + { + $self = clone $this; + $self['brandID'] = $brandID; + + return $self; + } + + public function withCursor(string $cursor): self + { + $self = clone $this; + $self['cursor'] = $cursor; + + return $self; + } + + public function withLimit(int $limit): self + { + $self = clone $this; + $self['limit'] = $limit; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/CampaignNewResponse.php b/src/Number10dlc/Campaigns/CampaignNewResponse.php new file mode 100644 index 0000000..3ab1e9c --- /dev/null +++ b/src/Number10dlc/Campaigns/CampaignNewResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public TenDlcCampaign $campaign; + + /** + * `new CampaignNewResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * CampaignNewResponse::with(campaign: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new CampaignNewResponse)->withCampaign(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcCampaign|TenDlcCampaignShape $campaign + */ + public static function with(TenDlcCampaign|array $campaign): self + { + $self = new self; + + $self['campaign'] = $campaign; + + return $self; + } + + /** + * @param TenDlcCampaign|TenDlcCampaignShape $campaign + */ + public function withCampaign(TenDlcCampaign|array $campaign): self + { + $self = clone $this; + $self['campaign'] = $campaign; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/CampaignSubmitResponse.php b/src/Number10dlc/Campaigns/CampaignSubmitResponse.php new file mode 100644 index 0000000..d90f7f5 --- /dev/null +++ b/src/Number10dlc/Campaigns/CampaignSubmitResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public TenDlcCampaign $campaign; + + /** + * `new CampaignSubmitResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * CampaignSubmitResponse::with(campaign: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new CampaignSubmitResponse)->withCampaign(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcCampaign|TenDlcCampaignShape $campaign + */ + public static function with(TenDlcCampaign|array $campaign): self + { + $self = new self; + + $self['campaign'] = $campaign; + + return $self; + } + + /** + * @param TenDlcCampaign|TenDlcCampaignShape $campaign + */ + public function withCampaign(TenDlcCampaign|array $campaign): self + { + $self = clone $this; + $self['campaign'] = $campaign; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/CampaignSyncStatusResponse.php b/src/Number10dlc/Campaigns/CampaignSyncStatusResponse.php new file mode 100644 index 0000000..8ddf74f --- /dev/null +++ b/src/Number10dlc/Campaigns/CampaignSyncStatusResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public TenDlcCampaign $campaign; + + /** + * `new CampaignSyncStatusResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * CampaignSyncStatusResponse::with(campaign: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new CampaignSyncStatusResponse)->withCampaign(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcCampaign|TenDlcCampaignShape $campaign + */ + public static function with(TenDlcCampaign|array $campaign): self + { + $self = new self; + + $self['campaign'] = $campaign; + + return $self; + } + + /** + * @param TenDlcCampaign|TenDlcCampaignShape $campaign + */ + public function withCampaign(TenDlcCampaign|array $campaign): self + { + $self = clone $this; + $self['campaign'] = $campaign; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/CampaignUpdateParams.php b/src/Number10dlc/Campaigns/CampaignUpdateParams.php new file mode 100644 index 0000000..c0a7e27 --- /dev/null +++ b/src/Number10dlc/Campaigns/CampaignUpdateParams.php @@ -0,0 +1,157 @@ +|null, + * optOutKeywords?: list|null, + * sampleMessages?: list|null, + * } + */ +final class CampaignUpdateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + #[Optional] + public ?string $description; + + #[Optional] + public ?string $helpMessage; + + #[Optional] + public ?string $messageFlow; + + #[Optional] + public ?string $name; + + /** @var list|null $optInKeywords */ + #[Optional(list: 'string')] + public ?array $optInKeywords; + + /** @var list|null $optOutKeywords */ + #[Optional(list: 'string')] + public ?array $optOutKeywords; + + /** @var list|null $sampleMessages */ + #[Optional(list: 'string')] + public ?array $sampleMessages; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list|null $optInKeywords + * @param list|null $optOutKeywords + * @param list|null $sampleMessages + */ + public static function with( + ?string $description = null, + ?string $helpMessage = null, + ?string $messageFlow = null, + ?string $name = null, + ?array $optInKeywords = null, + ?array $optOutKeywords = null, + ?array $sampleMessages = null, + ): self { + $self = new self; + + null !== $description && $self['description'] = $description; + null !== $helpMessage && $self['helpMessage'] = $helpMessage; + null !== $messageFlow && $self['messageFlow'] = $messageFlow; + null !== $name && $self['name'] = $name; + null !== $optInKeywords && $self['optInKeywords'] = $optInKeywords; + null !== $optOutKeywords && $self['optOutKeywords'] = $optOutKeywords; + null !== $sampleMessages && $self['sampleMessages'] = $sampleMessages; + + return $self; + } + + public function withDescription(string $description): self + { + $self = clone $this; + $self['description'] = $description; + + return $self; + } + + public function withHelpMessage(string $helpMessage): self + { + $self = clone $this; + $self['helpMessage'] = $helpMessage; + + return $self; + } + + public function withMessageFlow(string $messageFlow): self + { + $self = clone $this; + $self['messageFlow'] = $messageFlow; + + return $self; + } + + public function withName(string $name): self + { + $self = clone $this; + $self['name'] = $name; + + return $self; + } + + /** + * @param list $optInKeywords + */ + public function withOptInKeywords(array $optInKeywords): self + { + $self = clone $this; + $self['optInKeywords'] = $optInKeywords; + + return $self; + } + + /** + * @param list $optOutKeywords + */ + public function withOptOutKeywords(array $optOutKeywords): self + { + $self = clone $this; + $self['optOutKeywords'] = $optOutKeywords; + + return $self; + } + + /** + * @param list $sampleMessages + */ + public function withSampleMessages(array $sampleMessages): self + { + $self = clone $this; + $self['sampleMessages'] = $sampleMessages; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/CampaignUpdateResponse.php b/src/Number10dlc/Campaigns/CampaignUpdateResponse.php new file mode 100644 index 0000000..21c8a24 --- /dev/null +++ b/src/Number10dlc/Campaigns/CampaignUpdateResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public TenDlcCampaign $campaign; + + /** + * `new CampaignUpdateResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * CampaignUpdateResponse::with(campaign: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new CampaignUpdateResponse)->withCampaign(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcCampaign|TenDlcCampaignShape $campaign + */ + public static function with(TenDlcCampaign|array $campaign): self + { + $self = new self; + + $self['campaign'] = $campaign; + + return $self; + } + + /** + * @param TenDlcCampaign|TenDlcCampaignShape $campaign + */ + public function withCampaign(TenDlcCampaign|array $campaign): self + { + $self = clone $this; + $self['campaign'] = $campaign; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberAssignParams.php b/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberAssignParams.php new file mode 100644 index 0000000..0b30490 --- /dev/null +++ b/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberAssignParams.php @@ -0,0 +1,74 @@ + */ + use SdkModel; + use SdkParams; + + /** + * ID of the phone number to assign. + */ + #[Required('phoneNumberId')] + public string $phoneNumberID; + + /** + * `new PhoneNumberAssignParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * PhoneNumberAssignParams::with(phoneNumberID: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new PhoneNumberAssignParams)->withPhoneNumberID(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with(string $phoneNumberID): self + { + $self = new self; + + $self['phoneNumberID'] = $phoneNumberID; + + return $self; + } + + /** + * ID of the phone number to assign. + */ + public function withPhoneNumberID(string $phoneNumberID): self + { + $self = clone $this; + $self['phoneNumberID'] = $phoneNumberID; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberAssignResponse.php b/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberAssignResponse.php new file mode 100644 index 0000000..12cadd9 --- /dev/null +++ b/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberAssignResponse.php @@ -0,0 +1,73 @@ + */ + use SdkModel; + + #[Required] + public TenDlcPhoneNumberAssignment $assignment; + + /** + * `new PhoneNumberAssignResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * PhoneNumberAssignResponse::with(assignment: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new PhoneNumberAssignResponse)->withAssignment(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param TenDlcPhoneNumberAssignment|TenDlcPhoneNumberAssignmentShape $assignment + */ + public static function with( + TenDlcPhoneNumberAssignment|array $assignment + ): self { + $self = new self; + + $self['assignment'] = $assignment; + + return $self; + } + + /** + * @param TenDlcPhoneNumberAssignment|TenDlcPhoneNumberAssignmentShape $assignment + */ + public function withAssignment( + TenDlcPhoneNumberAssignment|array $assignment + ): self { + $self = clone $this; + $self['assignment'] = $assignment; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberListResponse.php b/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberListResponse.php new file mode 100644 index 0000000..bb0c4cd --- /dev/null +++ b/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberListResponse.php @@ -0,0 +1,87 @@ +, + * nextCursor?: string|null, + * } + */ +final class PhoneNumberListResponse implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** @var list $items */ + #[Required(list: TenDlcPhoneNumberAssignment::class)] + public array $items; + + #[Optional(nullable: true)] + public ?string $nextCursor; + + /** + * `new PhoneNumberListResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * PhoneNumberListResponse::with(items: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new PhoneNumberListResponse)->withItems(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $items + */ + public static function with(array $items, ?string $nextCursor = null): self + { + $self = new self; + + $self['items'] = $items; + + null !== $nextCursor && $self['nextCursor'] = $nextCursor; + + return $self; + } + + /** + * @param list $items + */ + public function withItems(array $items): self + { + $self = clone $this; + $self['items'] = $items; + + return $self; + } + + public function withNextCursor(?string $nextCursor): self + { + $self = clone $this; + $self['nextCursor'] = $nextCursor; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberUnassignParams.php b/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberUnassignParams.php new file mode 100644 index 0000000..82e0b44 --- /dev/null +++ b/src/Number10dlc/Campaigns/PhoneNumbers/PhoneNumberUnassignParams.php @@ -0,0 +1,68 @@ + */ + use SdkModel; + use SdkParams; + + #[Required] + public string $campaignID; + + /** + * `new PhoneNumberUnassignParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * PhoneNumberUnassignParams::with(campaignID: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new PhoneNumberUnassignParams)->withCampaignID(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with(string $campaignID): self + { + $self = new self; + + $self['campaignID'] = $campaignID; + + return $self; + } + + public function withCampaignID(string $campaignID): self + { + $self = clone $this; + $self['campaignID'] = $campaignID; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/PhoneNumbers/TenDlcPhoneNumberAssignment.php b/src/Number10dlc/Campaigns/PhoneNumbers/TenDlcPhoneNumberAssignment.php new file mode 100644 index 0000000..b010b1b --- /dev/null +++ b/src/Number10dlc/Campaigns/PhoneNumbers/TenDlcPhoneNumberAssignment.php @@ -0,0 +1,191 @@ +, + * updatedAt: \DateTimeInterface, + * assignedAt?: \DateTimeInterface|null, + * failureReason?: string|null, + * } + */ +final class TenDlcPhoneNumberAssignment implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + #[Required] + public string $id; + + #[Required('campaignId')] + public string $campaignID; + + #[Required] + public \DateTimeInterface $createdAt; + + #[Required('phoneNumberId')] + public string $phoneNumberID; + + /** + * Assignment status. + * + * @var value-of $status + */ + #[Required(enum: Status::class)] + public string $status; + + #[Required] + public \DateTimeInterface $updatedAt; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $assignedAt; + + #[Optional(nullable: true)] + public ?string $failureReason; + + /** + * `new TenDlcPhoneNumberAssignment()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * TenDlcPhoneNumberAssignment::with( + * id: ..., + * campaignID: ..., + * createdAt: ..., + * phoneNumberID: ..., + * status: ..., + * updatedAt: ..., + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new TenDlcPhoneNumberAssignment) + * ->withID(...) + * ->withCampaignID(...) + * ->withCreatedAt(...) + * ->withPhoneNumberID(...) + * ->withStatus(...) + * ->withUpdatedAt(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Status|value-of $status + */ + public static function with( + string $id, + string $campaignID, + \DateTimeInterface $createdAt, + string $phoneNumberID, + Status|string $status, + \DateTimeInterface $updatedAt, + ?\DateTimeInterface $assignedAt = null, + ?string $failureReason = null, + ): self { + $self = new self; + + $self['id'] = $id; + $self['campaignID'] = $campaignID; + $self['createdAt'] = $createdAt; + $self['phoneNumberID'] = $phoneNumberID; + $self['status'] = $status; + $self['updatedAt'] = $updatedAt; + + null !== $assignedAt && $self['assignedAt'] = $assignedAt; + null !== $failureReason && $self['failureReason'] = $failureReason; + + return $self; + } + + public function withID(string $id): self + { + $self = clone $this; + $self['id'] = $id; + + return $self; + } + + public function withCampaignID(string $campaignID): self + { + $self = clone $this; + $self['campaignID'] = $campaignID; + + return $self; + } + + public function withCreatedAt(\DateTimeInterface $createdAt): self + { + $self = clone $this; + $self['createdAt'] = $createdAt; + + return $self; + } + + public function withPhoneNumberID(string $phoneNumberID): self + { + $self = clone $this; + $self['phoneNumberID'] = $phoneNumberID; + + return $self; + } + + /** + * Assignment status. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } + + public function withUpdatedAt(\DateTimeInterface $updatedAt): self + { + $self = clone $this; + $self['updatedAt'] = $updatedAt; + + return $self; + } + + public function withAssignedAt(?\DateTimeInterface $assignedAt): self + { + $self = clone $this; + $self['assignedAt'] = $assignedAt; + + return $self; + } + + public function withFailureReason(?string $failureReason): self + { + $self = clone $this; + $self['failureReason'] = $failureReason; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/PhoneNumbers/TenDlcPhoneNumberAssignment/Status.php b/src/Number10dlc/Campaigns/PhoneNumbers/TenDlcPhoneNumberAssignment/Status.php new file mode 100644 index 0000000..7617f71 --- /dev/null +++ b/src/Number10dlc/Campaigns/PhoneNumbers/TenDlcPhoneNumberAssignment/Status.php @@ -0,0 +1,17 @@ +, + * status: Status|value-of, + * subscriberHelp: bool, + * subscriberOptIn: bool, + * subscriberOptOut: bool, + * updatedAt: \DateTimeInterface, + * useCase: string, + * approvedAt?: \DateTimeInterface|null, + * dailyLimit?: int|null, + * failureReason?: string|null, + * helpMessage?: string|null, + * messageFlow?: string|null, + * monthlyFeeCents?: int|null, + * optInKeywords?: list|null, + * optOutKeywords?: list|null, + * registrationCostCents?: int|null, + * submittedAt?: \DateTimeInterface|null, + * subUseCases?: list|null, + * } + */ +final class TenDlcCampaign implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + #[Required] + public string $id; + + #[Required] + public bool $affiliateMarketing; + + #[Required] + public bool $ageGated; + + /** + * ID of the brand this campaign belongs to. + */ + #[Required('brandId')] + public string $brandID; + + #[Required] + public \DateTimeInterface $createdAt; + + /** + * Description of the messaging campaign. + */ + #[Required] + public string $description; + + #[Required] + public bool $directLending; + + #[Required] + public bool $embeddedLink; + + #[Required] + public bool $embeddedPhone; + + #[Required] + public string $name; + + #[Required] + public bool $numberPooling; + + /** + * Sample messages representative of campaign content. + * + * @var list $sampleMessages + */ + #[Required(list: 'string')] + public array $sampleMessages; + + /** + * Status of a 10DLC campaign registration. + * + * @var value-of $status + */ + #[Required(enum: Status::class)] + public string $status; + + #[Required] + public bool $subscriberHelp; + + #[Required] + public bool $subscriberOptIn; + + #[Required] + public bool $subscriberOptOut; + + #[Required] + public \DateTimeInterface $updatedAt; + + /** + * Campaign use case type. + */ + #[Required] + public string $useCase; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $approvedAt; + + /** + * Daily message limit based on brand trust score. + */ + #[Optional(nullable: true)] + public ?int $dailyLimit; + + #[Optional(nullable: true)] + public ?string $failureReason; + + #[Optional(nullable: true)] + public ?string $helpMessage; + + #[Optional(nullable: true)] + public ?string $messageFlow; + + /** + * Recurring monthly fee in cents. + */ + #[Optional(nullable: true)] + public ?int $monthlyFeeCents; + + /** @var list|null $optInKeywords */ + #[Optional(list: 'string', nullable: true)] + public ?array $optInKeywords; + + /** @var list|null $optOutKeywords */ + #[Optional(list: 'string', nullable: true)] + public ?array $optOutKeywords; + + /** + * One-time registration cost in cents. + */ + #[Optional(nullable: true)] + public ?int $registrationCostCents; + + #[Optional(nullable: true)] + public ?\DateTimeInterface $submittedAt; + + /** @var list|null $subUseCases */ + #[Optional(list: 'string', nullable: true)] + public ?array $subUseCases; + + /** + * `new TenDlcCampaign()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * TenDlcCampaign::with( + * id: ..., + * affiliateMarketing: ..., + * ageGated: ..., + * brandID: ..., + * createdAt: ..., + * description: ..., + * directLending: ..., + * embeddedLink: ..., + * embeddedPhone: ..., + * name: ..., + * numberPooling: ..., + * sampleMessages: ..., + * status: ..., + * subscriberHelp: ..., + * subscriberOptIn: ..., + * subscriberOptOut: ..., + * updatedAt: ..., + * useCase: ..., + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new TenDlcCampaign) + * ->withID(...) + * ->withAffiliateMarketing(...) + * ->withAgeGated(...) + * ->withBrandID(...) + * ->withCreatedAt(...) + * ->withDescription(...) + * ->withDirectLending(...) + * ->withEmbeddedLink(...) + * ->withEmbeddedPhone(...) + * ->withName(...) + * ->withNumberPooling(...) + * ->withSampleMessages(...) + * ->withStatus(...) + * ->withSubscriberHelp(...) + * ->withSubscriberOptIn(...) + * ->withSubscriberOptOut(...) + * ->withUpdatedAt(...) + * ->withUseCase(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $sampleMessages + * @param Status|value-of $status + * @param list|null $optInKeywords + * @param list|null $optOutKeywords + * @param list|null $subUseCases + */ + public static function with( + string $id, + bool $affiliateMarketing, + bool $ageGated, + string $brandID, + \DateTimeInterface $createdAt, + string $description, + bool $directLending, + bool $embeddedLink, + bool $embeddedPhone, + string $name, + bool $numberPooling, + array $sampleMessages, + Status|string $status, + bool $subscriberHelp, + bool $subscriberOptIn, + bool $subscriberOptOut, + \DateTimeInterface $updatedAt, + string $useCase, + ?\DateTimeInterface $approvedAt = null, + ?int $dailyLimit = null, + ?string $failureReason = null, + ?string $helpMessage = null, + ?string $messageFlow = null, + ?int $monthlyFeeCents = null, + ?array $optInKeywords = null, + ?array $optOutKeywords = null, + ?int $registrationCostCents = null, + ?\DateTimeInterface $submittedAt = null, + ?array $subUseCases = null, + ): self { + $self = new self; + + $self['id'] = $id; + $self['affiliateMarketing'] = $affiliateMarketing; + $self['ageGated'] = $ageGated; + $self['brandID'] = $brandID; + $self['createdAt'] = $createdAt; + $self['description'] = $description; + $self['directLending'] = $directLending; + $self['embeddedLink'] = $embeddedLink; + $self['embeddedPhone'] = $embeddedPhone; + $self['name'] = $name; + $self['numberPooling'] = $numberPooling; + $self['sampleMessages'] = $sampleMessages; + $self['status'] = $status; + $self['subscriberHelp'] = $subscriberHelp; + $self['subscriberOptIn'] = $subscriberOptIn; + $self['subscriberOptOut'] = $subscriberOptOut; + $self['updatedAt'] = $updatedAt; + $self['useCase'] = $useCase; + + null !== $approvedAt && $self['approvedAt'] = $approvedAt; + null !== $dailyLimit && $self['dailyLimit'] = $dailyLimit; + null !== $failureReason && $self['failureReason'] = $failureReason; + null !== $helpMessage && $self['helpMessage'] = $helpMessage; + null !== $messageFlow && $self['messageFlow'] = $messageFlow; + null !== $monthlyFeeCents && $self['monthlyFeeCents'] = $monthlyFeeCents; + null !== $optInKeywords && $self['optInKeywords'] = $optInKeywords; + null !== $optOutKeywords && $self['optOutKeywords'] = $optOutKeywords; + null !== $registrationCostCents && $self['registrationCostCents'] = $registrationCostCents; + null !== $submittedAt && $self['submittedAt'] = $submittedAt; + null !== $subUseCases && $self['subUseCases'] = $subUseCases; + + return $self; + } + + public function withID(string $id): self + { + $self = clone $this; + $self['id'] = $id; + + return $self; + } + + public function withAffiliateMarketing(bool $affiliateMarketing): self + { + $self = clone $this; + $self['affiliateMarketing'] = $affiliateMarketing; + + return $self; + } + + public function withAgeGated(bool $ageGated): self + { + $self = clone $this; + $self['ageGated'] = $ageGated; + + return $self; + } + + /** + * ID of the brand this campaign belongs to. + */ + public function withBrandID(string $brandID): self + { + $self = clone $this; + $self['brandID'] = $brandID; + + return $self; + } + + public function withCreatedAt(\DateTimeInterface $createdAt): self + { + $self = clone $this; + $self['createdAt'] = $createdAt; + + return $self; + } + + /** + * Description of the messaging campaign. + */ + public function withDescription(string $description): self + { + $self = clone $this; + $self['description'] = $description; + + return $self; + } + + public function withDirectLending(bool $directLending): self + { + $self = clone $this; + $self['directLending'] = $directLending; + + return $self; + } + + public function withEmbeddedLink(bool $embeddedLink): self + { + $self = clone $this; + $self['embeddedLink'] = $embeddedLink; + + return $self; + } + + public function withEmbeddedPhone(bool $embeddedPhone): self + { + $self = clone $this; + $self['embeddedPhone'] = $embeddedPhone; + + return $self; + } + + public function withName(string $name): self + { + $self = clone $this; + $self['name'] = $name; + + return $self; + } + + public function withNumberPooling(bool $numberPooling): self + { + $self = clone $this; + $self['numberPooling'] = $numberPooling; + + return $self; + } + + /** + * Sample messages representative of campaign content. + * + * @param list $sampleMessages + */ + public function withSampleMessages(array $sampleMessages): self + { + $self = clone $this; + $self['sampleMessages'] = $sampleMessages; + + return $self; + } + + /** + * Status of a 10DLC campaign registration. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } + + public function withSubscriberHelp(bool $subscriberHelp): self + { + $self = clone $this; + $self['subscriberHelp'] = $subscriberHelp; + + return $self; + } + + public function withSubscriberOptIn(bool $subscriberOptIn): self + { + $self = clone $this; + $self['subscriberOptIn'] = $subscriberOptIn; + + return $self; + } + + public function withSubscriberOptOut(bool $subscriberOptOut): self + { + $self = clone $this; + $self['subscriberOptOut'] = $subscriberOptOut; + + return $self; + } + + public function withUpdatedAt(\DateTimeInterface $updatedAt): self + { + $self = clone $this; + $self['updatedAt'] = $updatedAt; + + return $self; + } + + /** + * Campaign use case type. + */ + public function withUseCase(string $useCase): self + { + $self = clone $this; + $self['useCase'] = $useCase; + + return $self; + } + + public function withApprovedAt(?\DateTimeInterface $approvedAt): self + { + $self = clone $this; + $self['approvedAt'] = $approvedAt; + + return $self; + } + + /** + * Daily message limit based on brand trust score. + */ + public function withDailyLimit(?int $dailyLimit): self + { + $self = clone $this; + $self['dailyLimit'] = $dailyLimit; + + return $self; + } + + public function withFailureReason(?string $failureReason): self + { + $self = clone $this; + $self['failureReason'] = $failureReason; + + return $self; + } + + public function withHelpMessage(?string $helpMessage): self + { + $self = clone $this; + $self['helpMessage'] = $helpMessage; + + return $self; + } + + public function withMessageFlow(?string $messageFlow): self + { + $self = clone $this; + $self['messageFlow'] = $messageFlow; + + return $self; + } + + /** + * Recurring monthly fee in cents. + */ + public function withMonthlyFeeCents(?int $monthlyFeeCents): self + { + $self = clone $this; + $self['monthlyFeeCents'] = $monthlyFeeCents; + + return $self; + } + + /** + * @param list|null $optInKeywords + */ + public function withOptInKeywords(?array $optInKeywords): self + { + $self = clone $this; + $self['optInKeywords'] = $optInKeywords; + + return $self; + } + + /** + * @param list|null $optOutKeywords + */ + public function withOptOutKeywords(?array $optOutKeywords): self + { + $self = clone $this; + $self['optOutKeywords'] = $optOutKeywords; + + return $self; + } + + /** + * One-time registration cost in cents. + */ + public function withRegistrationCostCents(?int $registrationCostCents): self + { + $self = clone $this; + $self['registrationCostCents'] = $registrationCostCents; + + return $self; + } + + public function withSubmittedAt(?\DateTimeInterface $submittedAt): self + { + $self = clone $this; + $self['submittedAt'] = $submittedAt; + + return $self; + } + + /** + * @param list|null $subUseCases + */ + public function withSubUseCases(?array $subUseCases): self + { + $self = clone $this; + $self['subUseCases'] = $subUseCases; + + return $self; + } +} diff --git a/src/Number10dlc/Campaigns/TenDlcCampaign/Status.php b/src/Number10dlc/Campaigns/TenDlcCampaign/Status.php new file mode 100644 index 0000000..2424fd1 --- /dev/null +++ b/src/Number10dlc/Campaigns/TenDlcCampaign/Status.php @@ -0,0 +1,19 @@ +, + * status: Status|value-of, + * tier: Tier|value-of, + * cancelAtPeriodEnd?: bool|null, + * currentPeriodEnd?: \DateTimeInterface|null, + * currentPeriodStart?: \DateTimeInterface|null, + * limits?: null|Limits|LimitsShape, + * } + */ +final class PlanGetResponse implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** @var value-of $billingInterval */ + #[Required(enum: BillingInterval::class)] + public string $billingInterval; + + /** @var value-of $status */ + #[Required(enum: Status::class)] + public string $status; + + /** + * Current subscription tier. + * + * @var value-of $tier + */ + #[Required(enum: Tier::class)] + public string $tier; + + #[Optional] + public ?bool $cancelAtPeriodEnd; + + #[Optional] + public ?\DateTimeInterface $currentPeriodEnd; + + #[Optional] + public ?\DateTimeInterface $currentPeriodStart; + + #[Optional] + public ?Limits $limits; + + /** + * `new PlanGetResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * PlanGetResponse::with(billingInterval: ..., status: ..., tier: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new PlanGetResponse)->withBillingInterval(...)->withStatus(...)->withTier(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param BillingInterval|value-of $billingInterval + * @param Status|value-of $status + * @param Tier|value-of $tier + * @param Limits|LimitsShape|null $limits + */ + public static function with( + BillingInterval|string $billingInterval, + Status|string $status, + Tier|string $tier, + ?bool $cancelAtPeriodEnd = null, + ?\DateTimeInterface $currentPeriodEnd = null, + ?\DateTimeInterface $currentPeriodStart = null, + Limits|array|null $limits = null, + ): self { + $self = new self; + + $self['billingInterval'] = $billingInterval; + $self['status'] = $status; + $self['tier'] = $tier; + + null !== $cancelAtPeriodEnd && $self['cancelAtPeriodEnd'] = $cancelAtPeriodEnd; + null !== $currentPeriodEnd && $self['currentPeriodEnd'] = $currentPeriodEnd; + null !== $currentPeriodStart && $self['currentPeriodStart'] = $currentPeriodStart; + null !== $limits && $self['limits'] = $limits; + + return $self; + } + + /** + * @param BillingInterval|value-of $billingInterval + */ + public function withBillingInterval( + BillingInterval|string $billingInterval + ): self { + $self = clone $this; + $self['billingInterval'] = $billingInterval; + + return $self; + } + + /** + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } + + /** + * Current subscription tier. + * + * @param Tier|value-of $tier + */ + public function withTier(Tier|string $tier): self + { + $self = clone $this; + $self['tier'] = $tier; + + return $self; + } + + public function withCancelAtPeriodEnd(bool $cancelAtPeriodEnd): self + { + $self = clone $this; + $self['cancelAtPeriodEnd'] = $cancelAtPeriodEnd; + + return $self; + } + + public function withCurrentPeriodEnd( + \DateTimeInterface $currentPeriodEnd + ): self { + $self = clone $this; + $self['currentPeriodEnd'] = $currentPeriodEnd; + + return $self; + } + + public function withCurrentPeriodStart( + \DateTimeInterface $currentPeriodStart + ): self { + $self = clone $this; + $self['currentPeriodStart'] = $currentPeriodStart; + + return $self; + } + + /** + * @param Limits|LimitsShape $limits + */ + public function withLimits(Limits|array $limits): self + { + $self = clone $this; + $self['limits'] = $limits; + + return $self; + } +} diff --git a/src/Plan/PlanGetResponse/BillingInterval.php b/src/Plan/PlanGetResponse/BillingInterval.php new file mode 100644 index 0000000..cab5ec6 --- /dev/null +++ b/src/Plan/PlanGetResponse/BillingInterval.php @@ -0,0 +1,12 @@ + */ + use SdkModel; + + #[Optional] + public ?bool $broadcasts; + + /** + * Monthly email limit. + */ + #[Optional] + public ?int $emails; + + /** + * Monthly A2P message limit. + */ + #[Optional] + public ?int $messagesA2P; + + #[Optional] + public ?int $phoneNumbers; + + #[Optional] + public ?int $senders; + + #[Optional] + public ?bool $subAccounts; + + #[Optional] + public ?int $wabaConnections; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with( + ?bool $broadcasts = null, + ?int $emails = null, + ?int $messagesA2P = null, + ?int $phoneNumbers = null, + ?int $senders = null, + ?bool $subAccounts = null, + ?int $wabaConnections = null, + ): self { + $self = new self; + + null !== $broadcasts && $self['broadcasts'] = $broadcasts; + null !== $emails && $self['emails'] = $emails; + null !== $messagesA2P && $self['messagesA2P'] = $messagesA2P; + null !== $phoneNumbers && $self['phoneNumbers'] = $phoneNumbers; + null !== $senders && $self['senders'] = $senders; + null !== $subAccounts && $self['subAccounts'] = $subAccounts; + null !== $wabaConnections && $self['wabaConnections'] = $wabaConnections; + + return $self; + } + + public function withBroadcasts(bool $broadcasts): self + { + $self = clone $this; + $self['broadcasts'] = $broadcasts; + + return $self; + } + + /** + * Monthly email limit. + */ + public function withEmails(int $emails): self + { + $self = clone $this; + $self['emails'] = $emails; + + return $self; + } + + /** + * Monthly A2P message limit. + */ + public function withMessagesA2P(int $messagesA2P): self + { + $self = clone $this; + $self['messagesA2P'] = $messagesA2P; + + return $self; + } + + public function withPhoneNumbers(int $phoneNumbers): self + { + $self = clone $this; + $self['phoneNumbers'] = $phoneNumbers; + + return $self; + } + + public function withSenders(int $senders): self + { + $self = clone $this; + $self['senders'] = $senders; + + return $self; + } + + public function withSubAccounts(bool $subAccounts): self + { + $self = clone $this; + $self['subAccounts'] = $subAccounts; + + return $self; + } + + public function withWabaConnections(int $wabaConnections): self + { + $self = clone $this; + $self['wabaConnections'] = $wabaConnections; + + return $self; + } +} diff --git a/src/Plan/PlanGetResponse/Status.php b/src/Plan/PlanGetResponse/Status.php new file mode 100644 index 0000000..3338b41 --- /dev/null +++ b/src/Plan/PlanGetResponse/Status.php @@ -0,0 +1,16 @@ +, + * requestedAt?: \DateTimeInterface|null, + * } + */ +final class WhatsAppSyncContacts implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * Whether contacts sync can be initiated. + */ + #[Required] + public bool $canSync; + + /** + * Status of WhatsApp contacts sync. + * + * @var value-of $status + */ + #[Required(enum: Status::class)] + public string $status; + + /** + * When the sync was last requested. + */ + #[Optional(nullable: true)] + public ?\DateTimeInterface $requestedAt; + + /** + * `new WhatsAppSyncContacts()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * WhatsAppSyncContacts::with(canSync: ..., status: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new WhatsAppSyncContacts)->withCanSync(...)->withStatus(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Status|value-of $status + */ + public static function with( + bool $canSync, + Status|string $status, + ?\DateTimeInterface $requestedAt = null, + ): self { + $self = new self; + + $self['canSync'] = $canSync; + $self['status'] = $status; + + null !== $requestedAt && $self['requestedAt'] = $requestedAt; + + return $self; + } + + /** + * Whether contacts sync can be initiated. + */ + public function withCanSync(bool $canSync): self + { + $self = clone $this; + $self['canSync'] = $canSync; + + return $self; + } + + /** + * Status of WhatsApp contacts sync. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } + + /** + * When the sync was last requested. + */ + public function withRequestedAt(?\DateTimeInterface $requestedAt): self + { + $self = clone $this; + $self['requestedAt'] = $requestedAt; + + return $self; + } +} diff --git a/src/Senders/WhatsappSync/WhatsAppSyncContacts/Status.php b/src/Senders/WhatsappSync/WhatsAppSyncContacts/Status.php new file mode 100644 index 0000000..74ebcf9 --- /dev/null +++ b/src/Senders/WhatsappSync/WhatsAppSyncContacts/Status.php @@ -0,0 +1,19 @@ +, + * completedAt?: \DateTimeInterface|null, + * requestedAt?: \DateTimeInterface|null, + * } + */ +final class WhatsAppSyncHistory implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * Whether history sync can be initiated. + */ + #[Required] + public bool $canSync; + + /** + * Status of WhatsApp message history sync. + * + * @var value-of $status + */ + #[Required(enum: Status::class)] + public string $status; + + /** + * When the sync was completed. + */ + #[Optional(nullable: true)] + public ?\DateTimeInterface $completedAt; + + /** + * When the sync was last requested. + */ + #[Optional(nullable: true)] + public ?\DateTimeInterface $requestedAt; + + /** + * `new WhatsAppSyncHistory()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * WhatsAppSyncHistory::with(canSync: ..., status: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new WhatsAppSyncHistory)->withCanSync(...)->withStatus(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Status|value-of $status + */ + public static function with( + bool $canSync, + Status|string $status, + ?\DateTimeInterface $completedAt = null, + ?\DateTimeInterface $requestedAt = null, + ): self { + $self = new self; + + $self['canSync'] = $canSync; + $self['status'] = $status; + + null !== $completedAt && $self['completedAt'] = $completedAt; + null !== $requestedAt && $self['requestedAt'] = $requestedAt; + + return $self; + } + + /** + * Whether history sync can be initiated. + */ + public function withCanSync(bool $canSync): self + { + $self = clone $this; + $self['canSync'] = $canSync; + + return $self; + } + + /** + * Status of WhatsApp message history sync. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } + + /** + * When the sync was completed. + */ + public function withCompletedAt(?\DateTimeInterface $completedAt): self + { + $self = clone $this; + $self['completedAt'] = $completedAt; + + return $self; + } + + /** + * When the sync was last requested. + */ + public function withRequestedAt(?\DateTimeInterface $requestedAt): self + { + $self = clone $this; + $self['requestedAt'] = $requestedAt; + + return $self; + } +} diff --git a/src/Senders/WhatsappSync/WhatsAppSyncHistory/Status.php b/src/Senders/WhatsappSync/WhatsAppSyncHistory/Status.php new file mode 100644 index 0000000..5ff081e --- /dev/null +++ b/src/Senders/WhatsappSync/WhatsAppSyncHistory/Status.php @@ -0,0 +1,21 @@ +, + * } + */ +final class WhatsAppSyncStatus implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * Contacts sync status details. + */ + #[Required] + public WhatsAppSyncContacts $contacts; + + /** + * History sync status details. + */ + #[Required] + public WhatsAppSyncHistory $history; + + /** + * Whether the account is in coexistence mode. + */ + #[Required] + public bool $isCoexistence; + + /** + * WhatsApp account status. + * + * @var value-of $status + */ + #[Required(enum: Status::class)] + public string $status; + + /** + * `new WhatsAppSyncStatus()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * WhatsAppSyncStatus::with( + * contacts: ..., history: ..., isCoexistence: ..., status: ... + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new WhatsAppSyncStatus) + * ->withContacts(...) + * ->withHistory(...) + * ->withIsCoexistence(...) + * ->withStatus(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param WhatsAppSyncContacts|WhatsAppSyncContactsShape $contacts + * @param WhatsAppSyncHistory|WhatsAppSyncHistoryShape $history + * @param Status|value-of $status + */ + public static function with( + WhatsAppSyncContacts|array $contacts, + WhatsAppSyncHistory|array $history, + bool $isCoexistence, + Status|string $status, + ): self { + $self = new self; + + $self['contacts'] = $contacts; + $self['history'] = $history; + $self['isCoexistence'] = $isCoexistence; + $self['status'] = $status; + + return $self; + } + + /** + * Contacts sync status details. + * + * @param WhatsAppSyncContacts|WhatsAppSyncContactsShape $contacts + */ + public function withContacts(WhatsAppSyncContacts|array $contacts): self + { + $self = clone $this; + $self['contacts'] = $contacts; + + return $self; + } + + /** + * History sync status details. + * + * @param WhatsAppSyncHistory|WhatsAppSyncHistoryShape $history + */ + public function withHistory(WhatsAppSyncHistory|array $history): self + { + $self = clone $this; + $self['history'] = $history; + + return $self; + } + + /** + * Whether the account is in coexistence mode. + */ + public function withIsCoexistence(bool $isCoexistence): self + { + $self = clone $this; + $self['isCoexistence'] = $isCoexistence; + + return $self; + } + + /** + * WhatsApp account status. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } +} diff --git a/src/Senders/WhatsappSync/WhatsAppSyncStatus/Status.php b/src/Senders/WhatsappSync/WhatsAppSyncStatus/Status.php new file mode 100644 index 0000000..9f4788a --- /dev/null +++ b/src/Senders/WhatsappSync/WhatsAppSyncStatus/Status.php @@ -0,0 +1,21 @@ + */ + use SdkModel; + + /** + * WhatsApp coexistence sync status. + */ + #[Required] + public WhatsAppSyncStatus $sync; + + /** + * `new WhatsappSyncGetResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * WhatsappSyncGetResponse::with(sync: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new WhatsappSyncGetResponse)->withSync(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param WhatsAppSyncStatus|WhatsAppSyncStatusShape $sync + */ + public static function with(WhatsAppSyncStatus|array $sync): self + { + $self = new self; + + $self['sync'] = $sync; + + return $self; + } + + /** + * WhatsApp coexistence sync status. + * + * @param WhatsAppSyncStatus|WhatsAppSyncStatusShape $sync + */ + public function withSync(WhatsAppSyncStatus|array $sync): self + { + $self = clone $this; + $self['sync'] = $sync; + + return $self; + } +} diff --git a/src/Senders/WhatsappSync/WhatsappSyncStartContactsSyncResponse.php b/src/Senders/WhatsappSync/WhatsappSyncStartContactsSyncResponse.php new file mode 100644 index 0000000..2f1b16b --- /dev/null +++ b/src/Senders/WhatsappSync/WhatsappSyncStartContactsSyncResponse.php @@ -0,0 +1,96 @@ + */ + use SdkModel; + + /** + * Success message. + */ + #[Required] + public string $message; + + /** + * WhatsApp coexistence sync status. + */ + #[Required] + public WhatsAppSyncStatus $sync; + + /** + * `new WhatsappSyncStartContactsSyncResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * WhatsappSyncStartContactsSyncResponse::with(message: ..., sync: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new WhatsappSyncStartContactsSyncResponse)->withMessage(...)->withSync(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param WhatsAppSyncStatus|WhatsAppSyncStatusShape $sync + */ + public static function with( + string $message, + WhatsAppSyncStatus|array $sync + ): self { + $self = new self; + + $self['message'] = $message; + $self['sync'] = $sync; + + return $self; + } + + /** + * Success message. + */ + public function withMessage(string $message): self + { + $self = clone $this; + $self['message'] = $message; + + return $self; + } + + /** + * WhatsApp coexistence sync status. + * + * @param WhatsAppSyncStatus|WhatsAppSyncStatusShape $sync + */ + public function withSync(WhatsAppSyncStatus|array $sync): self + { + $self = clone $this; + $self['sync'] = $sync; + + return $self; + } +} diff --git a/src/Senders/WhatsappSync/WhatsappSyncStartHistorySyncResponse.php b/src/Senders/WhatsappSync/WhatsappSyncStartHistorySyncResponse.php new file mode 100644 index 0000000..dcd94e6 --- /dev/null +++ b/src/Senders/WhatsappSync/WhatsappSyncStartHistorySyncResponse.php @@ -0,0 +1,96 @@ + */ + use SdkModel; + + /** + * Success message. + */ + #[Required] + public string $message; + + /** + * WhatsApp coexistence sync status. + */ + #[Required] + public WhatsAppSyncStatus $sync; + + /** + * `new WhatsappSyncStartHistorySyncResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * WhatsappSyncStartHistorySyncResponse::with(message: ..., sync: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new WhatsappSyncStartHistorySyncResponse)->withMessage(...)->withSync(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param WhatsAppSyncStatus|WhatsAppSyncStatusShape $sync + */ + public static function with( + string $message, + WhatsAppSyncStatus|array $sync + ): self { + $self = new self; + + $self['message'] = $message; + $self['sync'] = $sync; + + return $self; + } + + /** + * Success message. + */ + public function withMessage(string $message): self + { + $self = clone $this; + $self['message'] = $message; + + return $self; + } + + /** + * WhatsApp coexistence sync status. + * + * @param WhatsAppSyncStatus|WhatsAppSyncStatusShape $sync + */ + public function withSync(WhatsAppSyncStatus|array $sync): self + { + $self = clone $this; + $self['sync'] = $sync; + + return $self; + } +} diff --git a/src/ServiceContracts/BalanceContract.php b/src/ServiceContracts/BalanceContract.php new file mode 100644 index 0000000..13bf63e --- /dev/null +++ b/src/ServiceContracts/BalanceContract.php @@ -0,0 +1,26 @@ + + * + * @throws APIException + */ + public function retrieve( + RequestOptions|array|null $requestOptions = null + ): BaseResponse; +} diff --git a/src/ServiceContracts/BroadcastsContract.php b/src/ServiceContracts/BroadcastsContract.php index 67a996f..082cb24 100644 --- a/src/ServiceContracts/BroadcastsContract.php +++ b/src/ServiceContracts/BroadcastsContract.php @@ -8,11 +8,13 @@ use Zavudev\Broadcasts\BroadcastCancelResponse; use Zavudev\Broadcasts\BroadcastChannel; use Zavudev\Broadcasts\BroadcastContent; +use Zavudev\Broadcasts\BroadcastEscalateReviewResponse; use Zavudev\Broadcasts\BroadcastGetResponse; use Zavudev\Broadcasts\BroadcastMessageType; use Zavudev\Broadcasts\BroadcastNewResponse; use Zavudev\Broadcasts\BroadcastProgress; use Zavudev\Broadcasts\BroadcastRescheduleResponse; +use Zavudev\Broadcasts\BroadcastRetryReviewResponse; use Zavudev\Broadcasts\BroadcastSendResponse; use Zavudev\Broadcasts\BroadcastStatus; use Zavudev\Broadcasts\BroadcastUpdateResponse; @@ -132,6 +134,18 @@ public function cancel( RequestOptions|array|null $requestOptions = null ): BroadcastCancelResponse; + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function escalateReview( + string $broadcastID, + RequestOptions|array|null $requestOptions = null + ): BroadcastEscalateReviewResponse; + /** * @api * @@ -158,6 +172,18 @@ public function reschedule( RequestOptions|array|null $requestOptions = null, ): BroadcastRescheduleResponse; + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retryReview( + string $broadcastID, + RequestOptions|array|null $requestOptions = null + ): BroadcastRetryReviewResponse; + /** * @api * diff --git a/src/ServiceContracts/BroadcastsRawContract.php b/src/ServiceContracts/BroadcastsRawContract.php index d533259..b05e57e 100644 --- a/src/ServiceContracts/BroadcastsRawContract.php +++ b/src/ServiceContracts/BroadcastsRawContract.php @@ -7,12 +7,14 @@ use Zavudev\Broadcasts\Broadcast; use Zavudev\Broadcasts\BroadcastCancelResponse; use Zavudev\Broadcasts\BroadcastCreateParams; +use Zavudev\Broadcasts\BroadcastEscalateReviewResponse; use Zavudev\Broadcasts\BroadcastGetResponse; use Zavudev\Broadcasts\BroadcastListParams; use Zavudev\Broadcasts\BroadcastNewResponse; use Zavudev\Broadcasts\BroadcastProgress; use Zavudev\Broadcasts\BroadcastRescheduleParams; use Zavudev\Broadcasts\BroadcastRescheduleResponse; +use Zavudev\Broadcasts\BroadcastRetryReviewResponse; use Zavudev\Broadcasts\BroadcastSendParams; use Zavudev\Broadcasts\BroadcastSendResponse; use Zavudev\Broadcasts\BroadcastUpdateParams; @@ -115,6 +117,20 @@ public function cancel( RequestOptions|array|null $requestOptions = null ): BaseResponse; + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function escalateReview( + string $broadcastID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + /** * @api * @@ -145,6 +161,20 @@ public function reschedule( RequestOptions|array|null $requestOptions = null, ): BaseResponse; + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retryReview( + string $broadcastID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + /** * @api * diff --git a/src/ServiceContracts/Contacts/ChannelsContract.php b/src/ServiceContracts/Contacts/ChannelsContract.php new file mode 100644 index 0000000..ad1ceb9 --- /dev/null +++ b/src/ServiceContracts/Contacts/ChannelsContract.php @@ -0,0 +1,89 @@ + $metadata Body param + * @param bool $verified body param: Whether the channel is verified + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function update( + string $channelID, + string $contactID, + ?string $label = null, + ?array $metadata = null, + ?bool $verified = null, + RequestOptions|array|null $requestOptions = null, + ): ChannelUpdateResponse; + + /** + * @api + * + * @param Channel|value-of $channel channel type + * @param string $identifier Channel identifier (phone number in E.164 format or email address). + * @param string $countryCode ISO country code for phone numbers + * @param bool $isPrimary whether this should be the primary channel for its type + * @param string $label optional label for the channel + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function add( + string $contactID, + Channel|string $channel, + string $identifier, + ?string $countryCode = null, + bool $isPrimary = false, + ?string $label = null, + RequestOptions|array|null $requestOptions = null, + ): ChannelAddResponse; + + /** + * @api + * + * @param string $channelID channel ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function remove( + string $channelID, + string $contactID, + RequestOptions|array|null $requestOptions = null, + ): mixed; + + /** + * @api + * + * @param string $channelID channel ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function setPrimary( + string $channelID, + string $contactID, + RequestOptions|array|null $requestOptions = null, + ): ChannelSetPrimaryResponse; +} diff --git a/src/ServiceContracts/Contacts/ChannelsRawContract.php b/src/ServiceContracts/Contacts/ChannelsRawContract.php new file mode 100644 index 0000000..4411a7d --- /dev/null +++ b/src/ServiceContracts/Contacts/ChannelsRawContract.php @@ -0,0 +1,89 @@ +|ChannelUpdateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function update( + string $channelID, + array|ChannelUpdateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param array|ChannelAddParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function add( + string $contactID, + array|ChannelAddParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param string $channelID channel ID + * @param array|ChannelRemoveParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function remove( + string $channelID, + array|ChannelRemoveParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param string $channelID channel ID + * @param array|ChannelSetPrimaryParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function setPrimary( + string $channelID, + array|ChannelSetPrimaryParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; +} diff --git a/src/ServiceContracts/ContactsContract.php b/src/ServiceContracts/ContactsContract.php index 163a975..382cc0b 100644 --- a/src/ServiceContracts/ContactsContract.php +++ b/src/ServiceContracts/ContactsContract.php @@ -5,16 +5,35 @@ namespace Zavudev\ServiceContracts; use Zavudev\Contacts\Contact; +use Zavudev\Contacts\ContactCreateParams\Channel1 as Channel; use Zavudev\Contacts\ContactUpdateParams\DefaultChannel; use Zavudev\Core\Exceptions\APIException; use Zavudev\Cursor; use Zavudev\RequestOptions; /** + * @phpstan-import-type Channel1Shape from \Zavudev\Contacts\ContactCreateParams\Channel1 * @phpstan-import-type RequestOpts from \Zavudev\RequestOptions */ interface ContactsContract { + /** + * @api + * + * @param list $channels communication channels for the contact + * @param string $displayName display name for the contact + * @param array $metadata arbitrary metadata to associate with the contact + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + array $channels, + ?string $displayName = null, + ?array $metadata = null, + RequestOptions|array|null $requestOptions = null, + ): Contact; + /** * @api * @@ -59,6 +78,32 @@ public function list( RequestOptions|array|null $requestOptions = null, ): Cursor; + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function dismissMergeSuggestion( + string $contactID, + RequestOptions|array|null $requestOptions = null + ): mixed; + + /** + * @api + * + * @param string $sourceContactID ID of the contact to merge into the target contact. The source contact will be marked as merged. + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function merge( + string $contactID, + string $sourceContactID, + RequestOptions|array|null $requestOptions = null, + ): Contact; + /** * @api * diff --git a/src/ServiceContracts/ContactsRawContract.php b/src/ServiceContracts/ContactsRawContract.php index 0a3d861..21d4595 100644 --- a/src/ServiceContracts/ContactsRawContract.php +++ b/src/ServiceContracts/ContactsRawContract.php @@ -5,7 +5,9 @@ namespace Zavudev\ServiceContracts; use Zavudev\Contacts\Contact; +use Zavudev\Contacts\ContactCreateParams; use Zavudev\Contacts\ContactListParams; +use Zavudev\Contacts\ContactMergeParams; use Zavudev\Contacts\ContactUpdateParams; use Zavudev\Core\Contracts\BaseResponse; use Zavudev\Core\Exceptions\APIException; @@ -17,6 +19,21 @@ */ interface ContactsRawContract { + /** + * @api + * + * @param array|ContactCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|ContactCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + /** * @api * @@ -62,6 +79,36 @@ public function list( RequestOptions|array|null $requestOptions = null, ): BaseResponse; + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function dismissMergeSuggestion( + string $contactID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param array|ContactMergeParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function merge( + string $contactID, + array|ContactMergeParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + /** * @api * diff --git a/src/ServiceContracts/ExportsContract.php b/src/ServiceContracts/ExportsContract.php new file mode 100644 index 0000000..674349e --- /dev/null +++ b/src/ServiceContracts/ExportsContract.php @@ -0,0 +1,66 @@ +> $dataTypes list of data types to include in the export + * @param \DateTimeInterface $dateFrom start date for data to export (inclusive) + * @param \DateTimeInterface $dateTo end date for data to export (inclusive) + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + array $dataTypes, + ?\DateTimeInterface $dateFrom = null, + ?\DateTimeInterface $dateTo = null, + RequestOptions|array|null $requestOptions = null, + ): ExportNewResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $exportID, + RequestOptions|array|null $requestOptions = null + ): ExportGetResponse; + + /** + * @api + * + * @param Status|value-of $status status of a data export job + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function list( + ?string $cursor = null, + int $limit = 50, + Status|string|null $status = null, + RequestOptions|array|null $requestOptions = null, + ): Cursor; +} diff --git a/src/ServiceContracts/ExportsRawContract.php b/src/ServiceContracts/ExportsRawContract.php new file mode 100644 index 0000000..abbfe12 --- /dev/null +++ b/src/ServiceContracts/ExportsRawContract.php @@ -0,0 +1,65 @@ +|ExportCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|ExportCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieve( + string $exportID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param array|ExportListParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function list( + array|ExportListParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; +} diff --git a/src/ServiceContracts/InvitationsContract.php b/src/ServiceContracts/InvitationsContract.php new file mode 100644 index 0000000..43a76e4 --- /dev/null +++ b/src/ServiceContracts/InvitationsContract.php @@ -0,0 +1,84 @@ + $allowedPhoneCountries ISO country codes for allowed phone numbers + * @param string $clientEmail email of the client being invited + * @param string $clientName name of the client being invited + * @param string $clientPhone Phone number of the client in E.164 format. + * @param int $expiresInDays number of days until the invitation expires + * @param string $phoneNumberID ID of a Zavu phone number to pre-assign for WhatsApp registration. If provided, the client will use this number instead of their own. + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + ?array $allowedPhoneCountries = null, + ?string $clientEmail = null, + ?string $clientName = null, + ?string $clientPhone = null, + int $expiresInDays = 7, + ?string $phoneNumberID = null, + RequestOptions|array|null $requestOptions = null, + ): InvitationNewResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $invitationID, + RequestOptions|array|null $requestOptions = null + ): InvitationGetResponse; + + /** + * @api + * + * @param Status|value-of $status current status of the partner invitation + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function list( + ?string $cursor = null, + int $limit = 50, + Status|string|null $status = null, + RequestOptions|array|null $requestOptions = null, + ): Cursor; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function cancel( + string $invitationID, + RequestOptions|array|null $requestOptions = null + ): InvitationCancelResponse; +} diff --git a/src/ServiceContracts/InvitationsRawContract.php b/src/ServiceContracts/InvitationsRawContract.php new file mode 100644 index 0000000..6983893 --- /dev/null +++ b/src/ServiceContracts/InvitationsRawContract.php @@ -0,0 +1,80 @@ +|InvitationCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|InvitationCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieve( + string $invitationID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param array|InvitationListParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function list( + array|InvitationListParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function cancel( + string $invitationID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; +} diff --git a/src/ServiceContracts/Number10dlc/BrandsContract.php b/src/ServiceContracts/Number10dlc/BrandsContract.php new file mode 100644 index 0000000..11be6b4 --- /dev/null +++ b/src/ServiceContracts/Number10dlc/BrandsContract.php @@ -0,0 +1,167 @@ + $entityType business entity type for 10DLC brand registration + * @param string $phone Contact phone in E.164 format. + * @param string $vertical industry vertical + * @param string $companyName legal company name + * @param string $ein employer Identification Number (format: XX-XXXXXXX) + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + string $city, + string $country, + string $displayName, + string $email, + EntityType|string $entityType, + string $phone, + string $postalCode, + string $state, + string $street, + string $vertical, + ?string $companyName = null, + ?string $ein = null, + ?string $firstName = null, + ?string $lastName = null, + ?string $stockExchange = null, + ?string $stockSymbol = null, + ?string $website = null, + RequestOptions|array|null $requestOptions = null, + ): BrandNewResponse; + + /** + * @api + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BrandGetResponse; + + /** + * @api + * + * @param string $brandID 10DLC brand ID + * @param \Zavudev\Number10dlc\Brands\BrandUpdateParams\EntityType|value-of<\Zavudev\Number10dlc\Brands\BrandUpdateParams\EntityType> $entityType business entity type for 10DLC brand registration + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function update( + string $brandID, + ?string $city = null, + ?string $companyName = null, + ?string $country = null, + ?string $displayName = null, + ?string $ein = null, + ?string $email = null, + \Zavudev\Number10dlc\Brands\BrandUpdateParams\EntityType|string|null $entityType = null, + ?string $firstName = null, + ?string $lastName = null, + ?string $phone = null, + ?string $postalCode = null, + ?string $state = null, + ?string $stockExchange = null, + ?string $stockSymbol = null, + ?string $street = null, + ?string $vertical = null, + ?string $website = null, + RequestOptions|array|null $requestOptions = null, + ): BrandUpdateResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function list( + ?string $cursor = null, + int $limit = 50, + RequestOptions|array|null $requestOptions = null, + ): Cursor; + + /** + * @api + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function delete( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): mixed; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function listUseCases( + RequestOptions|array|null $requestOptions = null + ): BrandListUseCasesResponse; + + /** + * @api + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function submit( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BrandSubmitResponse; + + /** + * @api + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function syncStatus( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BrandSyncStatusResponse; +} diff --git a/src/ServiceContracts/Number10dlc/BrandsRawContract.php b/src/ServiceContracts/Number10dlc/BrandsRawContract.php new file mode 100644 index 0000000..ddbe564 --- /dev/null +++ b/src/ServiceContracts/Number10dlc/BrandsRawContract.php @@ -0,0 +1,146 @@ +|BrandCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|BrandCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieve( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param string $brandID 10DLC brand ID + * @param array|BrandUpdateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function update( + string $brandID, + array|BrandUpdateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param array|BrandListParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function list( + array|BrandListParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function delete( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function listUseCases( + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function submit( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function syncStatus( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; +} diff --git a/src/ServiceContracts/Number10dlc/Campaigns/PhoneNumbersContract.php b/src/ServiceContracts/Number10dlc/Campaigns/PhoneNumbersContract.php new file mode 100644 index 0000000..ea9ce68 --- /dev/null +++ b/src/ServiceContracts/Number10dlc/Campaigns/PhoneNumbersContract.php @@ -0,0 +1,59 @@ + + * + * @throws APIException + */ + public function list( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param array|PhoneNumberAssignParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function assign( + string $campaignID, + array|PhoneNumberAssignParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param string $assignmentID phone number assignment ID + * @param array|PhoneNumberUnassignParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function unassign( + string $assignmentID, + array|PhoneNumberUnassignParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; +} diff --git a/src/ServiceContracts/Number10dlc/CampaignsContract.php b/src/ServiceContracts/Number10dlc/CampaignsContract.php new file mode 100644 index 0000000..cb36e86 --- /dev/null +++ b/src/ServiceContracts/Number10dlc/CampaignsContract.php @@ -0,0 +1,149 @@ + $sampleMessages + * @param string $useCase Campaign use case (e.g., ACCOUNT_NOTIFICATION, MARKETING, 2FA). + * @param list $optInKeywords + * @param list $optOutKeywords + * @param list $subUseCases + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + bool $affiliateMarketing, + bool $ageGated, + string $brandID, + string $description, + bool $directLending, + bool $embeddedLink, + bool $embeddedPhone, + string $name, + bool $numberPooling, + array $sampleMessages, + bool $subscriberHelp, + bool $subscriberOptIn, + bool $subscriberOptOut, + string $useCase, + ?string $helpMessage = null, + ?string $messageFlow = null, + ?array $optInKeywords = null, + ?array $optOutKeywords = null, + ?array $subUseCases = null, + RequestOptions|array|null $requestOptions = null, + ): CampaignNewResponse; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): CampaignGetResponse; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param list $optInKeywords + * @param list $optOutKeywords + * @param list $sampleMessages + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function update( + string $campaignID, + ?string $description = null, + ?string $helpMessage = null, + ?string $messageFlow = null, + ?string $name = null, + ?array $optInKeywords = null, + ?array $optOutKeywords = null, + ?array $sampleMessages = null, + RequestOptions|array|null $requestOptions = null, + ): CampaignUpdateResponse; + + /** + * @api + * + * @param string $brandID filter campaigns by brand ID + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function list( + ?string $brandID = null, + ?string $cursor = null, + int $limit = 50, + RequestOptions|array|null $requestOptions = null, + ): Cursor; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function delete( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): mixed; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function submit( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): CampaignSubmitResponse; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function syncStatus( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): CampaignSyncStatusResponse; +} diff --git a/src/ServiceContracts/Number10dlc/CampaignsRawContract.php b/src/ServiceContracts/Number10dlc/CampaignsRawContract.php new file mode 100644 index 0000000..83b752e --- /dev/null +++ b/src/ServiceContracts/Number10dlc/CampaignsRawContract.php @@ -0,0 +1,132 @@ +|CampaignCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|CampaignCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieve( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param array|CampaignUpdateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function update( + string $campaignID, + array|CampaignUpdateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param array|CampaignListParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function list( + array|CampaignListParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function delete( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function submit( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function syncStatus( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; +} diff --git a/src/ServiceContracts/Number10dlcContract.php b/src/ServiceContracts/Number10dlcContract.php new file mode 100644 index 0000000..d46f61a --- /dev/null +++ b/src/ServiceContracts/Number10dlcContract.php @@ -0,0 +1,7 @@ + + * + * @throws APIException + */ + public function retrieve( + RequestOptions|array|null $requestOptions = null + ): BaseResponse; +} diff --git a/src/ServiceContracts/Senders/WhatsappSyncContract.php b/src/ServiceContracts/Senders/WhatsappSyncContract.php new file mode 100644 index 0000000..d313ef2 --- /dev/null +++ b/src/ServiceContracts/Senders/WhatsappSyncContract.php @@ -0,0 +1,53 @@ + + * + * @throws APIException + */ + public function retrieve( + string $senderID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function startContactsSync( + string $senderID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function startHistorySync( + string $senderID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; +} diff --git a/src/ServiceContracts/SubAccounts/APIKeysContract.php b/src/ServiceContracts/SubAccounts/APIKeysContract.php new file mode 100644 index 0000000..6072f98 --- /dev/null +++ b/src/ServiceContracts/SubAccounts/APIKeysContract.php @@ -0,0 +1,63 @@ + $environment + * @param list $permissions + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + string $id, + string $name, + Environment|string $environment = 'live', + ?array $permissions = null, + RequestOptions|array|null $requestOptions = null, + ): APIKeyNewResponse; + + /** + * @api + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function list( + string $id, + RequestOptions|array|null $requestOptions = null + ): APIKeyListResponse; + + /** + * @api + * + * @param string $keyID API key ID + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function revoke( + string $keyID, + string $id, + RequestOptions|array|null $requestOptions = null, + ): mixed; +} diff --git a/src/ServiceContracts/SubAccounts/APIKeysRawContract.php b/src/ServiceContracts/SubAccounts/APIKeysRawContract.php new file mode 100644 index 0000000..b02cead --- /dev/null +++ b/src/ServiceContracts/SubAccounts/APIKeysRawContract.php @@ -0,0 +1,68 @@ +|APIKeyCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + string $id, + array|APIKeyCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function list( + string $id, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param string $keyID API key ID + * @param array|APIKeyRevokeParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function revoke( + string $keyID, + array|APIKeyRevokeParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; +} diff --git a/src/ServiceContracts/SubAccountsContract.php b/src/ServiceContracts/SubAccountsContract.php new file mode 100644 index 0000000..2d57f24 --- /dev/null +++ b/src/ServiceContracts/SubAccountsContract.php @@ -0,0 +1,115 @@ + $metadata + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + string $name, + ?int $creditLimit = null, + ?string $externalID = null, + ?array $metadata = null, + RequestOptions|array|null $requestOptions = null, + ): SubAccountNewResponse; + + /** + * @api + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $id, + RequestOptions|array|null $requestOptions = null + ): SubAccountGetResponse; + + /** + * @api + * + * @param string $id sub-account ID + * @param array $metadata + * @param Status|value-of $status + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function update( + string $id, + ?int $creditLimit = null, + ?string $externalID = null, + ?array $metadata = null, + ?string $name = null, + Status|string|null $status = null, + RequestOptions|array|null $requestOptions = null, + ): SubAccountUpdateResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function list( + ?string $cursor = null, + int $limit = 50, + RequestOptions|array|null $requestOptions = null, + ): Cursor; + + /** + * @api + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function deactivate( + string $id, + RequestOptions|array|null $requestOptions = null + ): SubAccountDeactivateResponse; + + /** + * @api + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function getBalance( + string $id, + RequestOptions|array|null $requestOptions = null + ): SubAccountGetBalanceResponse; +} diff --git a/src/ServiceContracts/SubAccountsRawContract.php b/src/ServiceContracts/SubAccountsRawContract.php new file mode 100644 index 0000000..e794a39 --- /dev/null +++ b/src/ServiceContracts/SubAccountsRawContract.php @@ -0,0 +1,117 @@ +|SubAccountCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|SubAccountCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieve( + string $id, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param string $id sub-account ID + * @param array|SubAccountUpdateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function update( + string $id, + array|SubAccountUpdateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param array|SubAccountListParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function list( + array|SubAccountListParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function deactivate( + string $id, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function getBalance( + string $id, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; +} diff --git a/src/ServiceContracts/URLsContract.php b/src/ServiceContracts/URLsContract.php new file mode 100644 index 0000000..c789042 --- /dev/null +++ b/src/ServiceContracts/URLsContract.php @@ -0,0 +1,61 @@ + $status filter by verification status + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function listVerified( + ?string $cursor = null, + int $limit = 50, + Status|string|null $status = null, + RequestOptions|array|null $requestOptions = null, + ): Cursor; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieveDetails( + string $urlID, + RequestOptions|array|null $requestOptions = null + ): URLGetDetailsResponse; + + /** + * @api + * + * @param string $url the URL to submit for verification + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function submitForVerification( + string $url, + RequestOptions|array|null $requestOptions = null + ): URLSubmitForVerificationResponse; +} diff --git a/src/ServiceContracts/URLsRawContract.php b/src/ServiceContracts/URLsRawContract.php new file mode 100644 index 0000000..7ae8ca9 --- /dev/null +++ b/src/ServiceContracts/URLsRawContract.php @@ -0,0 +1,65 @@ +|URLListVerifiedParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function listVerified( + array|URLListVerifiedParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; + + /** + * @api + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieveDetails( + string $urlID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse; + + /** + * @api + * + * @param array|URLSubmitForVerificationParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function submitForVerification( + array|URLSubmitForVerificationParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse; +} diff --git a/src/ServiceContracts/UsageContract.php b/src/ServiceContracts/UsageContract.php new file mode 100644 index 0000000..dc7a820 --- /dev/null +++ b/src/ServiceContracts/UsageContract.php @@ -0,0 +1,26 @@ + + * + * @throws APIException + */ + public function retrieve( + RequestOptions|array|null $requestOptions = null + ): BaseResponse; +} diff --git a/src/Services/BalanceRawService.php b/src/Services/BalanceRawService.php new file mode 100644 index 0000000..eda728b --- /dev/null +++ b/src/Services/BalanceRawService.php @@ -0,0 +1,47 @@ + + * + * @throws APIException + */ + public function retrieve( + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: 'v1/balance', + options: $requestOptions, + convert: BalanceGetResponse::class, + ); + } +} diff --git a/src/Services/BalanceService.php b/src/Services/BalanceService.php new file mode 100644 index 0000000..f238080 --- /dev/null +++ b/src/Services/BalanceService.php @@ -0,0 +1,48 @@ +raw = new BalanceRawService($client); + } + + /** + * @api + * + * Get balance for the API key's team. If the API key belongs to a sub-account, also includes the sub-account's total spending and credit limit. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + RequestOptions|array|null $requestOptions = null + ): BalanceGetResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retrieve(requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/BroadcastsRawService.php b/src/Services/BroadcastsRawService.php index edc6131..42e3f26 100644 --- a/src/Services/BroadcastsRawService.php +++ b/src/Services/BroadcastsRawService.php @@ -9,6 +9,7 @@ use Zavudev\Broadcasts\BroadcastChannel; use Zavudev\Broadcasts\BroadcastContent; use Zavudev\Broadcasts\BroadcastCreateParams; +use Zavudev\Broadcasts\BroadcastEscalateReviewResponse; use Zavudev\Broadcasts\BroadcastGetResponse; use Zavudev\Broadcasts\BroadcastListParams; use Zavudev\Broadcasts\BroadcastMessageType; @@ -16,6 +17,7 @@ use Zavudev\Broadcasts\BroadcastProgress; use Zavudev\Broadcasts\BroadcastRescheduleParams; use Zavudev\Broadcasts\BroadcastRescheduleResponse; +use Zavudev\Broadcasts\BroadcastRetryReviewResponse; use Zavudev\Broadcasts\BroadcastSendParams; use Zavudev\Broadcasts\BroadcastSendResponse; use Zavudev\Broadcasts\BroadcastStatus; @@ -228,6 +230,30 @@ public function cancel( ); } + /** + * @api + * + * Request manual review by the Zavu team for a rejected broadcast. Use this after automated review rejection if you believe the content is legitimate. + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function escalateReview( + string $broadcastID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/broadcasts/%1$s/escalate', $broadcastID], + options: $requestOptions, + convert: BroadcastEscalateReviewResponse::class, + ); + } + /** * @api * @@ -284,6 +310,30 @@ public function reschedule( ); } + /** + * @api + * + * Resubmit a rejected broadcast for AI review after editing content. Maximum 3 review attempts allowed per broadcast. + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retryReview( + string $broadcastID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/broadcasts/%1$s/retry-review', $broadcastID], + options: $requestOptions, + convert: BroadcastRetryReviewResponse::class, + ); + } + /** * @api * diff --git a/src/Services/BroadcastsService.php b/src/Services/BroadcastsService.php index 2f0b51f..bafeeda 100644 --- a/src/Services/BroadcastsService.php +++ b/src/Services/BroadcastsService.php @@ -8,11 +8,13 @@ use Zavudev\Broadcasts\BroadcastCancelResponse; use Zavudev\Broadcasts\BroadcastChannel; use Zavudev\Broadcasts\BroadcastContent; +use Zavudev\Broadcasts\BroadcastEscalateReviewResponse; use Zavudev\Broadcasts\BroadcastGetResponse; use Zavudev\Broadcasts\BroadcastMessageType; use Zavudev\Broadcasts\BroadcastNewResponse; use Zavudev\Broadcasts\BroadcastProgress; use Zavudev\Broadcasts\BroadcastRescheduleResponse; +use Zavudev\Broadcasts\BroadcastRetryReviewResponse; use Zavudev\Broadcasts\BroadcastSendResponse; use Zavudev\Broadcasts\BroadcastStatus; use Zavudev\Broadcasts\BroadcastUpdateResponse; @@ -228,6 +230,25 @@ public function cancel( return $response->parse(); } + /** + * @api + * + * Request manual review by the Zavu team for a rejected broadcast. Use this after automated review rejection if you believe the content is legitimate. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function escalateReview( + string $broadcastID, + RequestOptions|array|null $requestOptions = null + ): BroadcastEscalateReviewResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->escalateReview($broadcastID, requestOptions: $requestOptions); + + return $response->parse(); + } + /** * @api * @@ -270,6 +291,25 @@ public function reschedule( return $response->parse(); } + /** + * @api + * + * Resubmit a rejected broadcast for AI review after editing content. Maximum 3 review attempts allowed per broadcast. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retryReview( + string $broadcastID, + RequestOptions|array|null $requestOptions = null + ): BroadcastRetryReviewResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retryReview($broadcastID, requestOptions: $requestOptions); + + return $response->parse(); + } + /** * @api * diff --git a/src/Services/Contacts/ChannelsRawService.php b/src/Services/Contacts/ChannelsRawService.php new file mode 100644 index 0000000..cf83588 --- /dev/null +++ b/src/Services/Contacts/ChannelsRawService.php @@ -0,0 +1,177 @@ +, + * verified?: bool, + * }|ChannelUpdateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function update( + string $channelID, + array|ChannelUpdateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = ChannelUpdateParams::parseRequest( + $params, + $requestOptions, + ); + $contactID = $parsed['contactID']; + unset($parsed['contactID']); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'patch', + path: ['v1/contacts/%1$s/channels/%2$s', $contactID, $channelID], + body: (object) array_diff_key($parsed, array_flip(['contactID'])), + options: $options, + convert: ChannelUpdateResponse::class, + ); + } + + /** + * @api + * + * Add a new communication channel to an existing contact. + * + * @param array{ + * channel: Channel|value-of, + * identifier: string, + * countryCode?: string, + * isPrimary?: bool, + * label?: string, + * }|ChannelAddParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function add( + string $contactID, + array|ChannelAddParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = ChannelAddParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/contacts/%1$s/channels', $contactID], + body: (object) $parsed, + options: $options, + convert: ChannelAddResponse::class, + ); + } + + /** + * @api + * + * Remove a communication channel from a contact. Cannot remove the last channel. + * + * @param string $channelID channel ID + * @param array{contactID: string}|ChannelRemoveParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function remove( + string $channelID, + array|ChannelRemoveParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = ChannelRemoveParams::parseRequest( + $params, + $requestOptions, + ); + $contactID = $parsed['contactID']; + unset($parsed['contactID']); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'delete', + path: ['v1/contacts/%1$s/channels/%2$s', $contactID, $channelID], + options: $options, + convert: null, + ); + } + + /** + * @api + * + * Set a channel as the primary channel for its type. + * + * @param string $channelID channel ID + * @param array{contactID: string}|ChannelSetPrimaryParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function setPrimary( + string $channelID, + array|ChannelSetPrimaryParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = ChannelSetPrimaryParams::parseRequest( + $params, + $requestOptions, + ); + $contactID = $parsed['contactID']; + unset($parsed['contactID']); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/contacts/%1$s/channels/%2$s/primary', $contactID, $channelID], + options: $options, + convert: ChannelSetPrimaryResponse::class, + ); + } +} diff --git a/src/Services/Contacts/ChannelsService.php b/src/Services/Contacts/ChannelsService.php new file mode 100644 index 0000000..cdaa730 --- /dev/null +++ b/src/Services/Contacts/ChannelsService.php @@ -0,0 +1,156 @@ +raw = new ChannelsRawService($client); + } + + /** + * @api + * + * Update a contact's channel properties. + * + * @param string $channelID path param: Channel ID + * @param string $contactID Path param + * @param string|null $label Body param: Optional label for the channel. Set to null to clear. + * @param array $metadata Body param + * @param bool $verified body param: Whether the channel is verified + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function update( + string $channelID, + string $contactID, + ?string $label = null, + ?array $metadata = null, + ?bool $verified = null, + RequestOptions|array|null $requestOptions = null, + ): ChannelUpdateResponse { + $params = Util::removeNulls( + [ + 'contactID' => $contactID, + 'label' => $label, + 'metadata' => $metadata, + 'verified' => $verified, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->update($channelID, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Add a new communication channel to an existing contact. + * + * @param Channel|value-of $channel channel type + * @param string $identifier Channel identifier (phone number in E.164 format or email address). + * @param string $countryCode ISO country code for phone numbers + * @param bool $isPrimary whether this should be the primary channel for its type + * @param string $label optional label for the channel + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function add( + string $contactID, + Channel|string $channel, + string $identifier, + ?string $countryCode = null, + bool $isPrimary = false, + ?string $label = null, + RequestOptions|array|null $requestOptions = null, + ): ChannelAddResponse { + $params = Util::removeNulls( + [ + 'channel' => $channel, + 'identifier' => $identifier, + 'countryCode' => $countryCode, + 'isPrimary' => $isPrimary, + 'label' => $label, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->add($contactID, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Remove a communication channel from a contact. Cannot remove the last channel. + * + * @param string $channelID channel ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function remove( + string $channelID, + string $contactID, + RequestOptions|array|null $requestOptions = null, + ): mixed { + $params = Util::removeNulls(['contactID' => $contactID]); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->remove($channelID, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Set a channel as the primary channel for its type. + * + * @param string $channelID channel ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function setPrimary( + string $channelID, + string $contactID, + RequestOptions|array|null $requestOptions = null, + ): ChannelSetPrimaryResponse { + $params = Util::removeNulls(['contactID' => $contactID]); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->setPrimary($channelID, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/ContactsRawService.php b/src/Services/ContactsRawService.php index 8848df5..c72097a 100644 --- a/src/Services/ContactsRawService.php +++ b/src/Services/ContactsRawService.php @@ -6,7 +6,10 @@ use Zavudev\Client; use Zavudev\Contacts\Contact; +use Zavudev\Contacts\ContactCreateParams; +use Zavudev\Contacts\ContactCreateParams\Channel1 as Channel; use Zavudev\Contacts\ContactListParams; +use Zavudev\Contacts\ContactMergeParams; use Zavudev\Contacts\ContactUpdateParams; use Zavudev\Contacts\ContactUpdateParams\DefaultChannel; use Zavudev\Core\Contracts\BaseResponse; @@ -16,6 +19,7 @@ use Zavudev\ServiceContracts\ContactsRawContract; /** + * @phpstan-import-type Channel1Shape from \Zavudev\Contacts\ContactCreateParams\Channel1 * @phpstan-import-type RequestOpts from \Zavudev\RequestOptions */ final class ContactsRawService implements ContactsRawContract @@ -26,6 +30,41 @@ final class ContactsRawService implements ContactsRawContract */ public function __construct(private Client $client) {} + /** + * @api + * + * Create a new contact with one or more communication channels. + * + * @param array{ + * channels: list, + * displayName?: string, + * metadata?: array, + * }|ContactCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|ContactCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = ContactCreateParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: 'v1/contacts', + body: (object) $parsed, + options: $options, + convert: Contact::class, + ); + } + /** * @api * @@ -119,6 +158,62 @@ public function list( ); } + /** + * @api + * + * Dismiss the merge suggestion for a contact. + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function dismissMergeSuggestion( + string $contactID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'delete', + path: ['v1/contacts/%1$s/merge-suggestion', $contactID], + options: $requestOptions, + convert: null, + ); + } + + /** + * @api + * + * Merge a source contact into this contact. All channels from the source contact will be moved to the target contact, and the source contact will be marked as merged. + * + * @param array{sourceContactID: string}|ContactMergeParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function merge( + string $contactID, + array|ContactMergeParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = ContactMergeParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/contacts/%1$s/merge', $contactID], + body: (object) $parsed, + options: $options, + convert: Contact::class, + ); + } + /** * @api * diff --git a/src/Services/ContactsService.php b/src/Services/ContactsService.php index a8852e0..a97dbc9 100644 --- a/src/Services/ContactsService.php +++ b/src/Services/ContactsService.php @@ -6,14 +6,17 @@ use Zavudev\Client; use Zavudev\Contacts\Contact; +use Zavudev\Contacts\ContactCreateParams\Channel1 as Channel; use Zavudev\Contacts\ContactUpdateParams\DefaultChannel; use Zavudev\Core\Exceptions\APIException; use Zavudev\Core\Util; use Zavudev\Cursor; use Zavudev\RequestOptions; use Zavudev\ServiceContracts\ContactsContract; +use Zavudev\Services\Contacts\ChannelsService; /** + * @phpstan-import-type Channel1Shape from \Zavudev\Contacts\ContactCreateParams\Channel1 * @phpstan-import-type RequestOpts from \Zavudev\RequestOptions */ final class ContactsService implements ContactsContract @@ -23,12 +26,50 @@ final class ContactsService implements ContactsContract */ public ContactsRawService $raw; + /** + * @api + */ + public ChannelsService $channels; + /** * @internal */ public function __construct(private Client $client) { $this->raw = new ContactsRawService($client); + $this->channels = new ChannelsService($client); + } + + /** + * @api + * + * Create a new contact with one or more communication channels. + * + * @param list $channels communication channels for the contact + * @param string $displayName display name for the contact + * @param array $metadata arbitrary metadata to associate with the contact + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + array $channels, + ?string $displayName = null, + ?array $metadata = null, + RequestOptions|array|null $requestOptions = null, + ): Contact { + $params = Util::removeNulls( + [ + 'channels' => $channels, + 'displayName' => $displayName, + 'metadata' => $metadata, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->create(params: $params, requestOptions: $requestOptions); + + return $response->parse(); } /** @@ -104,6 +145,48 @@ public function list( return $response->parse(); } + /** + * @api + * + * Dismiss the merge suggestion for a contact. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function dismissMergeSuggestion( + string $contactID, + RequestOptions|array|null $requestOptions = null + ): mixed { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->dismissMergeSuggestion($contactID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Merge a source contact into this contact. All channels from the source contact will be moved to the target contact, and the source contact will be marked as merged. + * + * @param string $sourceContactID ID of the contact to merge into the target contact. The source contact will be marked as merged. + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function merge( + string $contactID, + string $sourceContactID, + RequestOptions|array|null $requestOptions = null, + ): Contact { + $params = Util::removeNulls(['sourceContactID' => $sourceContactID]); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->merge($contactID, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + /** * @api * diff --git a/src/Services/ExportsRawService.php b/src/Services/ExportsRawService.php new file mode 100644 index 0000000..d1b4245 --- /dev/null +++ b/src/Services/ExportsRawService.php @@ -0,0 +1,124 @@ +>, + * dateFrom?: \DateTimeInterface, + * dateTo?: \DateTimeInterface, + * }|ExportCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|ExportCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = ExportCreateParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: 'v1/exports', + body: (object) $parsed, + options: $options, + convert: ExportNewResponse::class, + ); + } + + /** + * @api + * + * Get details of a specific data export, including download URL when completed. + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieve( + string $exportID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: ['v1/exports/%1$s', $exportID], + options: $requestOptions, + convert: ExportGetResponse::class, + ); + } + + /** + * @api + * + * List data exports for this project. + * + * @param array{ + * cursor?: string, limit?: int, status?: Status|value-of + * }|ExportListParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function list( + array|ExportListParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = ExportListParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: 'v1/exports', + query: $parsed, + options: $options, + convert: DataExport::class, + page: Cursor::class, + ); + } +} diff --git a/src/Services/ExportsService.php b/src/Services/ExportsService.php new file mode 100644 index 0000000..5cedfb2 --- /dev/null +++ b/src/Services/ExportsService.php @@ -0,0 +1,111 @@ +raw = new ExportsRawService($client); + } + + /** + * @api + * + * Create a new data export job. The export will be processed asynchronously and the download URL will be available when status is 'completed'. Export links expire after 24 hours. + * + * @param list> $dataTypes list of data types to include in the export + * @param \DateTimeInterface $dateFrom start date for data to export (inclusive) + * @param \DateTimeInterface $dateTo end date for data to export (inclusive) + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + array $dataTypes, + ?\DateTimeInterface $dateFrom = null, + ?\DateTimeInterface $dateTo = null, + RequestOptions|array|null $requestOptions = null, + ): ExportNewResponse { + $params = Util::removeNulls( + ['dataTypes' => $dataTypes, 'dateFrom' => $dateFrom, 'dateTo' => $dateTo] + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->create(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Get details of a specific data export, including download URL when completed. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $exportID, + RequestOptions|array|null $requestOptions = null + ): ExportGetResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retrieve($exportID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * List data exports for this project. + * + * @param Status|value-of $status status of a data export job + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function list( + ?string $cursor = null, + int $limit = 50, + Status|string|null $status = null, + RequestOptions|array|null $requestOptions = null, + ): Cursor { + $params = Util::removeNulls( + ['cursor' => $cursor, 'limit' => $limit, 'status' => $status] + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->list(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/InvitationsRawService.php b/src/Services/InvitationsRawService.php new file mode 100644 index 0000000..f45d4af --- /dev/null +++ b/src/Services/InvitationsRawService.php @@ -0,0 +1,151 @@ +, + * clientEmail?: string, + * clientName?: string, + * clientPhone?: string, + * expiresInDays?: int, + * phoneNumberID?: string, + * }|InvitationCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|InvitationCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = InvitationCreateParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: 'v1/invitations', + body: (object) $parsed, + options: $options, + convert: InvitationNewResponse::class, + ); + } + + /** + * @api + * + * Get invitation + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieve( + string $invitationID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: ['v1/invitations/%1$s', $invitationID], + options: $requestOptions, + convert: InvitationGetResponse::class, + ); + } + + /** + * @api + * + * List partner invitations for this project. + * + * @param array{ + * cursor?: string, limit?: int, status?: Status|value-of + * }|InvitationListParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function list( + array|InvitationListParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = InvitationListParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: 'v1/invitations', + query: $parsed, + options: $options, + convert: Invitation::class, + page: Cursor::class, + ); + } + + /** + * @api + * + * Cancel an active invitation. The client will no longer be able to use the invitation link. + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function cancel( + string $invitationID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/invitations/%1$s/cancel', $invitationID], + options: $requestOptions, + convert: InvitationCancelResponse::class, + ); + } +} diff --git a/src/Services/InvitationsService.php b/src/Services/InvitationsService.php new file mode 100644 index 0000000..f76ec93 --- /dev/null +++ b/src/Services/InvitationsService.php @@ -0,0 +1,143 @@ +raw = new InvitationsRawService($client); + } + + /** + * @api + * + * Create a partner invitation link for a client to connect their WhatsApp Business account. The client will complete Meta's embedded signup flow and the resulting sender will be created in your project. + * + * @param list $allowedPhoneCountries ISO country codes for allowed phone numbers + * @param string $clientEmail email of the client being invited + * @param string $clientName name of the client being invited + * @param string $clientPhone Phone number of the client in E.164 format. + * @param int $expiresInDays number of days until the invitation expires + * @param string $phoneNumberID ID of a Zavu phone number to pre-assign for WhatsApp registration. If provided, the client will use this number instead of their own. + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + ?array $allowedPhoneCountries = null, + ?string $clientEmail = null, + ?string $clientName = null, + ?string $clientPhone = null, + int $expiresInDays = 7, + ?string $phoneNumberID = null, + RequestOptions|array|null $requestOptions = null, + ): InvitationNewResponse { + $params = Util::removeNulls( + [ + 'allowedPhoneCountries' => $allowedPhoneCountries, + 'clientEmail' => $clientEmail, + 'clientName' => $clientName, + 'clientPhone' => $clientPhone, + 'expiresInDays' => $expiresInDays, + 'phoneNumberID' => $phoneNumberID, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->create(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Get invitation + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $invitationID, + RequestOptions|array|null $requestOptions = null + ): InvitationGetResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retrieve($invitationID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * List partner invitations for this project. + * + * @param Status|value-of $status current status of the partner invitation + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function list( + ?string $cursor = null, + int $limit = 50, + Status|string|null $status = null, + RequestOptions|array|null $requestOptions = null, + ): Cursor { + $params = Util::removeNulls( + ['cursor' => $cursor, 'limit' => $limit, 'status' => $status] + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->list(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Cancel an active invitation. The client will no longer be able to use the invitation link. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function cancel( + string $invitationID, + RequestOptions|array|null $requestOptions = null + ): InvitationCancelResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->cancel($invitationID, requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/Number10dlc/BrandsRawService.php b/src/Services/Number10dlc/BrandsRawService.php new file mode 100644 index 0000000..0e5e244 --- /dev/null +++ b/src/Services/Number10dlc/BrandsRawService.php @@ -0,0 +1,290 @@ +, + * phone: string, + * postalCode: string, + * state: string, + * street: string, + * vertical: string, + * companyName?: string, + * ein?: string, + * firstName?: string, + * lastName?: string, + * stockExchange?: string, + * stockSymbol?: string, + * website?: string, + * }|BrandCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|BrandCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = BrandCreateParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: 'v1/10dlc/brands', + body: (object) $parsed, + options: $options, + convert: BrandNewResponse::class, + ); + } + + /** + * @api + * + * Get 10DLC brand + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieve( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: ['v1/10dlc/brands/%1$s', $brandID], + options: $requestOptions, + convert: BrandGetResponse::class, + ); + } + + /** + * @api + * + * Update a 10DLC brand in draft status. Cannot update after submission. + * + * @param string $brandID 10DLC brand ID + * @param array{ + * city?: string, + * companyName?: string, + * country?: string, + * displayName?: string, + * ein?: string, + * email?: string, + * entityType?: BrandUpdateParams\EntityType|value-of, + * firstName?: string, + * lastName?: string, + * phone?: string, + * postalCode?: string, + * state?: string, + * stockExchange?: string, + * stockSymbol?: string, + * street?: string, + * vertical?: string, + * website?: string, + * }|BrandUpdateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function update( + string $brandID, + array|BrandUpdateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = BrandUpdateParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'patch', + path: ['v1/10dlc/brands/%1$s', $brandID], + body: (object) $parsed, + options: $options, + convert: BrandUpdateResponse::class, + ); + } + + /** + * @api + * + * List 10DLC brand registrations for this project. + * + * @param array{cursor?: string, limit?: int}|BrandListParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function list( + array|BrandListParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = BrandListParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: 'v1/10dlc/brands', + query: $parsed, + options: $options, + convert: TenDlcBrand::class, + page: Cursor::class, + ); + } + + /** + * @api + * + * Delete 10DLC brand + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function delete( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'delete', + path: ['v1/10dlc/brands/%1$s', $brandID], + options: $requestOptions, + convert: null, + ); + } + + /** + * @api + * + * List available use cases for 10DLC campaign registration. + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function listUseCases( + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: 'v1/10dlc/brands/use-cases', + options: $requestOptions, + convert: BrandListUseCasesResponse::class, + ); + } + + /** + * @api + * + * Submit a draft brand to The Campaign Registry (TCR) for vetting. The brand must be in draft status. A $35 registration fee is charged from your balance. + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function submit( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/10dlc/brands/%1$s/submit', $brandID], + options: $requestOptions, + convert: BrandSubmitResponse::class, + ); + } + + /** + * @api + * + * Sync the brand status with the registration provider. Use this to check for approval updates after submission. + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function syncStatus( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/10dlc/brands/%1$s/sync', $brandID], + options: $requestOptions, + convert: BrandSyncStatusResponse::class, + ); + } +} diff --git a/src/Services/Number10dlc/BrandsService.php b/src/Services/Number10dlc/BrandsService.php new file mode 100644 index 0000000..5350194 --- /dev/null +++ b/src/Services/Number10dlc/BrandsService.php @@ -0,0 +1,285 @@ +raw = new BrandsRawService($client); + } + + /** + * @api + * + * Create a 10DLC brand registration. The brand starts in draft status. Submit it for review using the submit endpoint. + * + * @param string $country two-letter ISO country code + * @param string $displayName display name of the brand + * @param EntityType|value-of $entityType business entity type for 10DLC brand registration + * @param string $phone Contact phone in E.164 format. + * @param string $vertical industry vertical + * @param string $companyName legal company name + * @param string $ein employer Identification Number (format: XX-XXXXXXX) + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + string $city, + string $country, + string $displayName, + string $email, + EntityType|string $entityType, + string $phone, + string $postalCode, + string $state, + string $street, + string $vertical, + ?string $companyName = null, + ?string $ein = null, + ?string $firstName = null, + ?string $lastName = null, + ?string $stockExchange = null, + ?string $stockSymbol = null, + ?string $website = null, + RequestOptions|array|null $requestOptions = null, + ): BrandNewResponse { + $params = Util::removeNulls( + [ + 'city' => $city, + 'country' => $country, + 'displayName' => $displayName, + 'email' => $email, + 'entityType' => $entityType, + 'phone' => $phone, + 'postalCode' => $postalCode, + 'state' => $state, + 'street' => $street, + 'vertical' => $vertical, + 'companyName' => $companyName, + 'ein' => $ein, + 'firstName' => $firstName, + 'lastName' => $lastName, + 'stockExchange' => $stockExchange, + 'stockSymbol' => $stockSymbol, + 'website' => $website, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->create(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Get 10DLC brand + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BrandGetResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retrieve($brandID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Update a 10DLC brand in draft status. Cannot update after submission. + * + * @param string $brandID 10DLC brand ID + * @param \Zavudev\Number10dlc\Brands\BrandUpdateParams\EntityType|value-of<\Zavudev\Number10dlc\Brands\BrandUpdateParams\EntityType> $entityType business entity type for 10DLC brand registration + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function update( + string $brandID, + ?string $city = null, + ?string $companyName = null, + ?string $country = null, + ?string $displayName = null, + ?string $ein = null, + ?string $email = null, + \Zavudev\Number10dlc\Brands\BrandUpdateParams\EntityType|string|null $entityType = null, + ?string $firstName = null, + ?string $lastName = null, + ?string $phone = null, + ?string $postalCode = null, + ?string $state = null, + ?string $stockExchange = null, + ?string $stockSymbol = null, + ?string $street = null, + ?string $vertical = null, + ?string $website = null, + RequestOptions|array|null $requestOptions = null, + ): BrandUpdateResponse { + $params = Util::removeNulls( + [ + 'city' => $city, + 'companyName' => $companyName, + 'country' => $country, + 'displayName' => $displayName, + 'ein' => $ein, + 'email' => $email, + 'entityType' => $entityType, + 'firstName' => $firstName, + 'lastName' => $lastName, + 'phone' => $phone, + 'postalCode' => $postalCode, + 'state' => $state, + 'stockExchange' => $stockExchange, + 'stockSymbol' => $stockSymbol, + 'street' => $street, + 'vertical' => $vertical, + 'website' => $website, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->update($brandID, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * List 10DLC brand registrations for this project. + * + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function list( + ?string $cursor = null, + int $limit = 50, + RequestOptions|array|null $requestOptions = null, + ): Cursor { + $params = Util::removeNulls(['cursor' => $cursor, 'limit' => $limit]); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->list(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Delete 10DLC brand + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function delete( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): mixed { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->delete($brandID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * List available use cases for 10DLC campaign registration. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function listUseCases( + RequestOptions|array|null $requestOptions = null + ): BrandListUseCasesResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->listUseCases(requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Submit a draft brand to The Campaign Registry (TCR) for vetting. The brand must be in draft status. A $35 registration fee is charged from your balance. + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function submit( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BrandSubmitResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->submit($brandID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Sync the brand status with the registration provider. Use this to check for approval updates after submission. + * + * @param string $brandID 10DLC brand ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function syncStatus( + string $brandID, + RequestOptions|array|null $requestOptions = null + ): BrandSyncStatusResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->syncStatus($brandID, requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/Number10dlc/Campaigns/PhoneNumbersRawService.php b/src/Services/Number10dlc/Campaigns/PhoneNumbersRawService.php new file mode 100644 index 0000000..63fdcc5 --- /dev/null +++ b/src/Services/Number10dlc/Campaigns/PhoneNumbersRawService.php @@ -0,0 +1,121 @@ + + * + * @throws APIException + */ + public function list( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: ['v1/10dlc/campaigns/%1$s/phone-numbers', $campaignID], + options: $requestOptions, + convert: PhoneNumberListResponse::class, + ); + } + + /** + * @api + * + * Assign a US phone number to an approved 10DLC campaign. The campaign must be in approved status. + * + * @param string $campaignID 10DLC campaign ID + * @param array{phoneNumberID: string}|PhoneNumberAssignParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function assign( + string $campaignID, + array|PhoneNumberAssignParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = PhoneNumberAssignParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/10dlc/campaigns/%1$s/phone-numbers', $campaignID], + body: (object) $parsed, + options: $options, + convert: PhoneNumberAssignResponse::class, + ); + } + + /** + * @api + * + * Remove a phone number assignment from a 10DLC campaign. + * + * @param string $assignmentID phone number assignment ID + * @param array{campaignID: string}|PhoneNumberUnassignParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function unassign( + string $assignmentID, + array|PhoneNumberUnassignParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = PhoneNumberUnassignParams::parseRequest( + $params, + $requestOptions, + ); + $campaignID = $parsed['campaignID']; + unset($parsed['campaignID']); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'delete', + path: [ + 'v1/10dlc/campaigns/%1$s/phone-numbers/%2$s', $campaignID, $assignmentID, + ], + options: $options, + convert: null, + ); + } +} diff --git a/src/Services/Number10dlc/Campaigns/PhoneNumbersService.php b/src/Services/Number10dlc/Campaigns/PhoneNumbersService.php new file mode 100644 index 0000000..b75b61c --- /dev/null +++ b/src/Services/Number10dlc/Campaigns/PhoneNumbersService.php @@ -0,0 +1,100 @@ +raw = new PhoneNumbersRawService($client); + } + + /** + * @api + * + * List phone numbers assigned to a 10DLC campaign. + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function list( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): PhoneNumberListResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->list($campaignID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Assign a US phone number to an approved 10DLC campaign. The campaign must be in approved status. + * + * @param string $campaignID 10DLC campaign ID + * @param string $phoneNumberID ID of the phone number to assign + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function assign( + string $campaignID, + string $phoneNumberID, + RequestOptions|array|null $requestOptions = null, + ): PhoneNumberAssignResponse { + $params = Util::removeNulls(['phoneNumberID' => $phoneNumberID]); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->assign($campaignID, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Remove a phone number assignment from a 10DLC campaign. + * + * @param string $assignmentID phone number assignment ID + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function unassign( + string $assignmentID, + string $campaignID, + RequestOptions|array|null $requestOptions = null, + ): mixed { + $params = Util::removeNulls(['campaignID' => $campaignID]); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->unassign($assignmentID, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/Number10dlc/CampaignsRawService.php b/src/Services/Number10dlc/CampaignsRawService.php new file mode 100644 index 0000000..e0fae39 --- /dev/null +++ b/src/Services/Number10dlc/CampaignsRawService.php @@ -0,0 +1,260 @@ +, + * subscriberHelp: bool, + * subscriberOptIn: bool, + * subscriberOptOut: bool, + * useCase: string, + * helpMessage?: string, + * messageFlow?: string, + * optInKeywords?: list, + * optOutKeywords?: list, + * subUseCases?: list, + * }|CampaignCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|CampaignCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = CampaignCreateParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: 'v1/10dlc/campaigns', + body: (object) $parsed, + options: $options, + convert: CampaignNewResponse::class, + ); + } + + /** + * @api + * + * Get 10DLC campaign + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieve( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: ['v1/10dlc/campaigns/%1$s', $campaignID], + options: $requestOptions, + convert: CampaignGetResponse::class, + ); + } + + /** + * @api + * + * Update a 10DLC campaign in draft status. Cannot update after submission. + * + * @param string $campaignID 10DLC campaign ID + * @param array{ + * description?: string, + * helpMessage?: string, + * messageFlow?: string, + * name?: string, + * optInKeywords?: list, + * optOutKeywords?: list, + * sampleMessages?: list, + * }|CampaignUpdateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function update( + string $campaignID, + array|CampaignUpdateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = CampaignUpdateParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'patch', + path: ['v1/10dlc/campaigns/%1$s', $campaignID], + body: (object) $parsed, + options: $options, + convert: CampaignUpdateResponse::class, + ); + } + + /** + * @api + * + * List 10DLC campaign registrations for this project. + * + * @param array{ + * brandID?: string, cursor?: string, limit?: int + * }|CampaignListParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function list( + array|CampaignListParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = CampaignListParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: 'v1/10dlc/campaigns', + query: Util::array_transform_keys($parsed, ['brandID' => 'brandId']), + options: $options, + convert: TenDlcCampaign::class, + page: Cursor::class, + ); + } + + /** + * @api + * + * Delete 10DLC campaign + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function delete( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'delete', + path: ['v1/10dlc/campaigns/%1$s', $campaignID], + options: $requestOptions, + convert: null, + ); + } + + /** + * @api + * + * Submit a draft campaign for carrier review. The campaign must be in draft status and its brand must be verified. + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function submit( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/10dlc/campaigns/%1$s/submit', $campaignID], + options: $requestOptions, + convert: CampaignSubmitResponse::class, + ); + } + + /** + * @api + * + * Sync the campaign status with the registration provider. Use this to check for approval updates after submission. + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function syncStatus( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/10dlc/campaigns/%1$s/sync', $campaignID], + options: $requestOptions, + convert: CampaignSyncStatusResponse::class, + ); + } +} diff --git a/src/Services/Number10dlc/CampaignsService.php b/src/Services/Number10dlc/CampaignsService.php new file mode 100644 index 0000000..83aa046 --- /dev/null +++ b/src/Services/Number10dlc/CampaignsService.php @@ -0,0 +1,261 @@ +raw = new CampaignsRawService($client); + $this->phoneNumbers = new PhoneNumbersService($client); + } + + /** + * @api + * + * Create a 10DLC campaign under an existing brand. The campaign starts in draft status. Submit it for carrier review using the submit endpoint. + * + * @param string $brandID ID of the brand to create this campaign under + * @param list $sampleMessages + * @param string $useCase Campaign use case (e.g., ACCOUNT_NOTIFICATION, MARKETING, 2FA). + * @param list $optInKeywords + * @param list $optOutKeywords + * @param list $subUseCases + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + bool $affiliateMarketing, + bool $ageGated, + string $brandID, + string $description, + bool $directLending, + bool $embeddedLink, + bool $embeddedPhone, + string $name, + bool $numberPooling, + array $sampleMessages, + bool $subscriberHelp, + bool $subscriberOptIn, + bool $subscriberOptOut, + string $useCase, + ?string $helpMessage = null, + ?string $messageFlow = null, + ?array $optInKeywords = null, + ?array $optOutKeywords = null, + ?array $subUseCases = null, + RequestOptions|array|null $requestOptions = null, + ): CampaignNewResponse { + $params = Util::removeNulls( + [ + 'affiliateMarketing' => $affiliateMarketing, + 'ageGated' => $ageGated, + 'brandID' => $brandID, + 'description' => $description, + 'directLending' => $directLending, + 'embeddedLink' => $embeddedLink, + 'embeddedPhone' => $embeddedPhone, + 'name' => $name, + 'numberPooling' => $numberPooling, + 'sampleMessages' => $sampleMessages, + 'subscriberHelp' => $subscriberHelp, + 'subscriberOptIn' => $subscriberOptIn, + 'subscriberOptOut' => $subscriberOptOut, + 'useCase' => $useCase, + 'helpMessage' => $helpMessage, + 'messageFlow' => $messageFlow, + 'optInKeywords' => $optInKeywords, + 'optOutKeywords' => $optOutKeywords, + 'subUseCases' => $subUseCases, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->create(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Get 10DLC campaign + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): CampaignGetResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retrieve($campaignID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Update a 10DLC campaign in draft status. Cannot update after submission. + * + * @param string $campaignID 10DLC campaign ID + * @param list $optInKeywords + * @param list $optOutKeywords + * @param list $sampleMessages + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function update( + string $campaignID, + ?string $description = null, + ?string $helpMessage = null, + ?string $messageFlow = null, + ?string $name = null, + ?array $optInKeywords = null, + ?array $optOutKeywords = null, + ?array $sampleMessages = null, + RequestOptions|array|null $requestOptions = null, + ): CampaignUpdateResponse { + $params = Util::removeNulls( + [ + 'description' => $description, + 'helpMessage' => $helpMessage, + 'messageFlow' => $messageFlow, + 'name' => $name, + 'optInKeywords' => $optInKeywords, + 'optOutKeywords' => $optOutKeywords, + 'sampleMessages' => $sampleMessages, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->update($campaignID, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * List 10DLC campaign registrations for this project. + * + * @param string $brandID filter campaigns by brand ID + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function list( + ?string $brandID = null, + ?string $cursor = null, + int $limit = 50, + RequestOptions|array|null $requestOptions = null, + ): Cursor { + $params = Util::removeNulls( + ['brandID' => $brandID, 'cursor' => $cursor, 'limit' => $limit] + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->list(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Delete 10DLC campaign + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function delete( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): mixed { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->delete($campaignID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Submit a draft campaign for carrier review. The campaign must be in draft status and its brand must be verified. + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function submit( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): CampaignSubmitResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->submit($campaignID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Sync the campaign status with the registration provider. Use this to check for approval updates after submission. + * + * @param string $campaignID 10DLC campaign ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function syncStatus( + string $campaignID, + RequestOptions|array|null $requestOptions = null + ): CampaignSyncStatusResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->syncStatus($campaignID, requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/Number10dlcRawService.php b/src/Services/Number10dlcRawService.php new file mode 100644 index 0000000..7e22619 --- /dev/null +++ b/src/Services/Number10dlcRawService.php @@ -0,0 +1,17 @@ +raw = new Number10dlcRawService($client); + $this->brands = new BrandsService($client); + $this->campaigns = new CampaignsService($client); + } +} diff --git a/src/Services/PlanRawService.php b/src/Services/PlanRawService.php new file mode 100644 index 0000000..add2d97 --- /dev/null +++ b/src/Services/PlanRawService.php @@ -0,0 +1,47 @@ + + * + * @throws APIException + */ + public function retrieve( + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: 'v1/plan', + options: $requestOptions, + convert: PlanGetResponse::class, + ); + } +} diff --git a/src/Services/PlanService.php b/src/Services/PlanService.php new file mode 100644 index 0000000..eb7bcb5 --- /dev/null +++ b/src/Services/PlanService.php @@ -0,0 +1,48 @@ +raw = new PlanRawService($client); + } + + /** + * @api + * + * Get the current subscription plan for the API key's team, including tier, billing interval, and period dates. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + RequestOptions|array|null $requestOptions = null + ): PlanGetResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retrieve(requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/Senders/WhatsappSyncRawService.php b/src/Services/Senders/WhatsappSyncRawService.php new file mode 100644 index 0000000..21822e2 --- /dev/null +++ b/src/Services/Senders/WhatsappSyncRawService.php @@ -0,0 +1,98 @@ + + * + * @throws APIException + */ + public function retrieve( + string $senderID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: ['v1/senders/%1$s/whatsapp-sync', $senderID], + options: $requestOptions, + convert: WhatsappSyncGetResponse::class, + ); + } + + /** + * @api + * + * Initiate contact names sync from the WhatsApp Business App. This imports contact names stored in the app to Zavu. Only available for coexistence accounts with active status. + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function startContactsSync( + string $senderID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/senders/%1$s/whatsapp-sync/contacts', $senderID], + options: $requestOptions, + convert: WhatsappSyncStartContactsSyncResponse::class, + ); + } + + /** + * @api + * + * Initiate message history sync from the WhatsApp Business App. This sends a request to the account owner to approve sharing their conversation history. Only available for coexistence accounts with active status. + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function startHistorySync( + string $senderID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/senders/%1$s/whatsapp-sync/history', $senderID], + options: $requestOptions, + convert: WhatsappSyncStartHistorySyncResponse::class, + ); + } +} diff --git a/src/Services/Senders/WhatsappSyncService.php b/src/Services/Senders/WhatsappSyncService.php new file mode 100644 index 0000000..21d9003 --- /dev/null +++ b/src/Services/Senders/WhatsappSyncService.php @@ -0,0 +1,89 @@ +raw = new WhatsappSyncRawService($client); + } + + /** + * @api + * + * Get the current sync status for a sender's WhatsApp coexistence account. Only available for senders connected in coexistence mode (WhatsApp Business App + Cloud API). + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $senderID, + RequestOptions|array|null $requestOptions = null + ): WhatsappSyncGetResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retrieve($senderID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Initiate contact names sync from the WhatsApp Business App. This imports contact names stored in the app to Zavu. Only available for coexistence accounts with active status. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function startContactsSync( + string $senderID, + RequestOptions|array|null $requestOptions = null + ): WhatsappSyncStartContactsSyncResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->startContactsSync($senderID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Initiate message history sync from the WhatsApp Business App. This sends a request to the account owner to approve sharing their conversation history. Only available for coexistence accounts with active status. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function startHistorySync( + string $senderID, + RequestOptions|array|null $requestOptions = null + ): WhatsappSyncStartHistorySyncResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->startHistorySync($senderID, requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/SendersService.php b/src/Services/SendersService.php index 709678a..afa1be0 100644 --- a/src/Services/SendersService.php +++ b/src/Services/SendersService.php @@ -19,6 +19,7 @@ use Zavudev\Senders\WhatsappBusinessProfileVertical; use Zavudev\ServiceContracts\SendersContract; use Zavudev\Services\Senders\AgentService; +use Zavudev\Services\Senders\WhatsappSyncService; /** * @phpstan-import-type RequestOpts from \Zavudev\RequestOptions @@ -35,6 +36,11 @@ final class SendersService implements SendersContract */ public AgentService $agent; + /** + * @api + */ + public WhatsappSyncService $whatsappSync; + /** * @internal */ @@ -42,6 +48,7 @@ public function __construct(private Client $client) { $this->raw = new SendersRawService($client); $this->agent = new AgentService($client); + $this->whatsappSync = new WhatsappSyncService($client); } /** diff --git a/src/Services/SubAccounts/APIKeysRawService.php b/src/Services/SubAccounts/APIKeysRawService.php new file mode 100644 index 0000000..7f568ba --- /dev/null +++ b/src/Services/SubAccounts/APIKeysRawService.php @@ -0,0 +1,124 @@ +, + * permissions?: list, + * }|APIKeyCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + string $id, + array|APIKeyCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = APIKeyCreateParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: ['v1/sub-accounts/%1$s/api-keys', $id], + body: (object) $parsed, + options: $options, + convert: APIKeyNewResponse::class, + ); + } + + /** + * @api + * + * List sub-account API keys. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function list( + string $id, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: ['v1/sub-accounts/%1$s/api-keys', $id], + options: $requestOptions, + convert: APIKeyListResponse::class, + ); + } + + /** + * @api + * + * Revoke sub-account API key. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $keyID API key ID + * @param array{id: string}|APIKeyRevokeParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function revoke( + string $keyID, + array|APIKeyRevokeParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = APIKeyRevokeParams::parseRequest( + $params, + $requestOptions, + ); + $id = $parsed['id']; + unset($parsed['id']); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'delete', + path: ['v1/sub-accounts/%1$s/api-keys/%2$s', $id, $keyID], + options: $options, + convert: null, + ); + } +} diff --git a/src/Services/SubAccounts/APIKeysService.php b/src/Services/SubAccounts/APIKeysService.php new file mode 100644 index 0000000..81a4538 --- /dev/null +++ b/src/Services/SubAccounts/APIKeysService.php @@ -0,0 +1,110 @@ +raw = new APIKeysRawService($client); + } + + /** + * @api + * + * Create sub-account API key. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param Environment|value-of $environment + * @param list $permissions + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + string $id, + string $name, + Environment|string $environment = 'live', + ?array $permissions = null, + RequestOptions|array|null $requestOptions = null, + ): APIKeyNewResponse { + $params = Util::removeNulls( + [ + 'name' => $name, + 'environment' => $environment, + 'permissions' => $permissions, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->create($id, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * List sub-account API keys. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function list( + string $id, + RequestOptions|array|null $requestOptions = null + ): APIKeyListResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->list($id, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Revoke sub-account API key. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $keyID API key ID + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function revoke( + string $keyID, + string $id, + RequestOptions|array|null $requestOptions = null + ): mixed { + $params = Util::removeNulls(['id' => $id]); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->revoke($keyID, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/SubAccountsRawService.php b/src/Services/SubAccountsRawService.php new file mode 100644 index 0000000..b99cefc --- /dev/null +++ b/src/Services/SubAccountsRawService.php @@ -0,0 +1,216 @@ +, + * }|SubAccountCreateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function create( + array|SubAccountCreateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = SubAccountCreateParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: 'v1/sub-accounts', + body: (object) $parsed, + options: $options, + convert: SubAccountNewResponse::class, + ); + } + + /** + * @api + * + * Get sub-account. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieve( + string $id, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: ['v1/sub-accounts/%1$s', $id], + options: $requestOptions, + convert: SubAccountGetResponse::class, + ); + } + + /** + * @api + * + * Update sub-account. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param array{ + * creditLimit?: int|null, + * externalID?: string, + * metadata?: array, + * name?: string, + * status?: Status|value-of, + * }|SubAccountUpdateParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function update( + string $id, + array|SubAccountUpdateParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = SubAccountUpdateParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'patch', + path: ['v1/sub-accounts/%1$s', $id], + body: (object) $parsed, + options: $options, + convert: SubAccountUpdateResponse::class, + ); + } + + /** + * @api + * + * List sub-accounts for this team. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param array{cursor?: string, limit?: int}|SubAccountListParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function list( + array|SubAccountListParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = SubAccountListParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: 'v1/sub-accounts', + query: $parsed, + options: $options, + convert: SubAccount::class, + page: Cursor::class, + ); + } + + /** + * @api + * + * Deactivate a sub-account. Remaining balance is returned to the parent team and all API keys are revoked. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function deactivate( + string $id, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'delete', + path: ['v1/sub-accounts/%1$s', $id], + options: $requestOptions, + convert: SubAccountDeactivateResponse::class, + ); + } + + /** + * @api + * + * Get spending information for a sub-account. Returns the parent team's balance, the sub-account's total spending, and its credit limit (spending cap). Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function getBalance( + string $id, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: ['v1/sub-accounts/%1$s/balance', $id], + options: $requestOptions, + convert: SubAccountGetBalanceResponse::class, + ); + } +} diff --git a/src/Services/SubAccountsService.php b/src/Services/SubAccountsService.php new file mode 100644 index 0000000..f33d3de --- /dev/null +++ b/src/Services/SubAccountsService.php @@ -0,0 +1,201 @@ +raw = new SubAccountsRawService($client); + $this->apiKeys = new APIKeysService($client); + } + + /** + * @api + * + * Create a new sub-account (project) with its own API key. All charges are billed to the parent team's balance. Use creditLimit to set a spending cap. The sub-account's API key is returned only in the creation response. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $name name of the sub-account + * @param int $creditLimit Spending cap in cents. When reached, messages from this sub-account will be blocked. Omit or set to 0 for no limit. + * @param string $externalID external reference ID for your own tracking + * @param array $metadata + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function create( + string $name, + ?int $creditLimit = null, + ?string $externalID = null, + ?array $metadata = null, + RequestOptions|array|null $requestOptions = null, + ): SubAccountNewResponse { + $params = Util::removeNulls( + [ + 'name' => $name, + 'creditLimit' => $creditLimit, + 'externalID' => $externalID, + 'metadata' => $metadata, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->create(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Get sub-account. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + string $id, + RequestOptions|array|null $requestOptions = null + ): SubAccountGetResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retrieve($id, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Update sub-account. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param array $metadata + * @param Status|value-of $status + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function update( + string $id, + ?int $creditLimit = null, + ?string $externalID = null, + ?array $metadata = null, + ?string $name = null, + Status|string|null $status = null, + RequestOptions|array|null $requestOptions = null, + ): SubAccountUpdateResponse { + $params = Util::removeNulls( + [ + 'creditLimit' => $creditLimit, + 'externalID' => $externalID, + 'metadata' => $metadata, + 'name' => $name, + 'status' => $status, + ], + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->update($id, params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * List sub-accounts for this team. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function list( + ?string $cursor = null, + int $limit = 50, + RequestOptions|array|null $requestOptions = null, + ): Cursor { + $params = Util::removeNulls(['cursor' => $cursor, 'limit' => $limit]); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->list(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Deactivate a sub-account. Remaining balance is returned to the parent team and all API keys are revoked. Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function deactivate( + string $id, + RequestOptions|array|null $requestOptions = null + ): SubAccountDeactivateResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->deactivate($id, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Get spending information for a sub-account. Returns the parent team's balance, the sub-account's total spending, and its credit limit (spending cap). Requires a parent project API key; sub-account API keys receive HTTP 403. + * + * @param string $id sub-account ID + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function getBalance( + string $id, + RequestOptions|array|null $requestOptions = null + ): SubAccountGetBalanceResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->getBalance($id, requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/URLsRawService.php b/src/Services/URLsRawService.php new file mode 100644 index 0000000..84d4f67 --- /dev/null +++ b/src/Services/URLsRawService.php @@ -0,0 +1,121 @@ + + * }|URLListVerifiedParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse> + * + * @throws APIException + */ + public function listVerified( + array|URLListVerifiedParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = URLListVerifiedParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: 'v1/urls', + query: $parsed, + options: $options, + convert: VerifiedURL::class, + page: Cursor::class, + ); + } + + /** + * @api + * + * Get details of a specific verified URL. + * + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function retrieveDetails( + string $urlID, + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: ['v1/urls/%1$s', $urlID], + options: $requestOptions, + convert: URLGetDetailsResponse::class, + ); + } + + /** + * @api + * + * Submit a URL for verification. URLs are automatically checked against Google Web Risk API. Safe URLs are auto-approved, malicious URLs are blocked. URL shorteners (bit.ly, t.co, etc.) are always blocked. + * + * **Important:** All SMS and Email messages containing URLs require those URLs to be verified before the message can be sent. This endpoint allows pre-verification of URLs. + * + * @param array{url: string}|URLSubmitForVerificationParams $params + * @param RequestOpts|null $requestOptions + * + * @return BaseResponse + * + * @throws APIException + */ + public function submitForVerification( + array|URLSubmitForVerificationParams $params, + RequestOptions|array|null $requestOptions = null, + ): BaseResponse { + [$parsed, $options] = URLSubmitForVerificationParams::parseRequest( + $params, + $requestOptions, + ); + + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'post', + path: 'v1/urls', + body: (object) $parsed, + options: $options, + convert: URLSubmitForVerificationResponse::class, + ); + } +} diff --git a/src/Services/URLsService.php b/src/Services/URLsService.php new file mode 100644 index 0000000..9fc3d3c --- /dev/null +++ b/src/Services/URLsService.php @@ -0,0 +1,106 @@ +raw = new URLsRawService($client); + } + + /** + * @api + * + * List URLs that have been verified for this project. + * + * @param Status|value-of $status filter by verification status + * @param RequestOpts|null $requestOptions + * + * @return Cursor + * + * @throws APIException + */ + public function listVerified( + ?string $cursor = null, + int $limit = 50, + Status|string|null $status = null, + RequestOptions|array|null $requestOptions = null, + ): Cursor { + $params = Util::removeNulls( + ['cursor' => $cursor, 'limit' => $limit, 'status' => $status] + ); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->listVerified(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Get details of a specific verified URL. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieveDetails( + string $urlID, + RequestOptions|array|null $requestOptions = null + ): URLGetDetailsResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retrieveDetails($urlID, requestOptions: $requestOptions); + + return $response->parse(); + } + + /** + * @api + * + * Submit a URL for verification. URLs are automatically checked against Google Web Risk API. Safe URLs are auto-approved, malicious URLs are blocked. URL shorteners (bit.ly, t.co, etc.) are always blocked. + * + * **Important:** All SMS and Email messages containing URLs require those URLs to be verified before the message can be sent. This endpoint allows pre-verification of URLs. + * + * @param string $url the URL to submit for verification + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function submitForVerification( + string $url, + RequestOptions|array|null $requestOptions = null + ): URLSubmitForVerificationResponse { + $params = Util::removeNulls(['url' => $url]); + + // @phpstan-ignore-next-line argument.type + $response = $this->raw->submitForVerification(params: $params, requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/Services/UsageRawService.php b/src/Services/UsageRawService.php new file mode 100644 index 0000000..1f2b611 --- /dev/null +++ b/src/Services/UsageRawService.php @@ -0,0 +1,47 @@ + + * + * @throws APIException + */ + public function retrieve( + RequestOptions|array|null $requestOptions = null + ): BaseResponse { + // @phpstan-ignore-next-line return.type + return $this->client->request( + method: 'get', + path: 'v1/usage', + options: $requestOptions, + convert: UsageGetResponse::class, + ); + } +} diff --git a/src/Services/UsageService.php b/src/Services/UsageService.php new file mode 100644 index 0000000..f119ebd --- /dev/null +++ b/src/Services/UsageService.php @@ -0,0 +1,48 @@ +raw = new UsageRawService($client); + } + + /** + * @api + * + * Get the current month's usage counters for A2P messages and emails, along with the tier limits. + * + * @param RequestOpts|null $requestOptions + * + * @throws APIException + */ + public function retrieve( + RequestOptions|array|null $requestOptions = null + ): UsageGetResponse { + // @phpstan-ignore-next-line argument.type + $response = $this->raw->retrieve(requestOptions: $requestOptions); + + return $response->parse(); + } +} diff --git a/src/SubAccounts/APIKeys/APIKeyCreateParams.php b/src/SubAccounts/APIKeys/APIKeyCreateParams.php new file mode 100644 index 0000000..cd33181 --- /dev/null +++ b/src/SubAccounts/APIKeys/APIKeyCreateParams.php @@ -0,0 +1,113 @@ +, + * permissions?: list|null, + * } + */ +final class APIKeyCreateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + #[Required] + public string $name; + + /** @var value-of|null $environment */ + #[Optional(enum: Environment::class)] + public ?string $environment; + + /** @var list|null $permissions */ + #[Optional(list: 'string')] + public ?array $permissions; + + /** + * `new APIKeyCreateParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * APIKeyCreateParams::with(name: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new APIKeyCreateParams)->withName(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Environment|value-of|null $environment + * @param list|null $permissions + */ + public static function with( + string $name, + Environment|string|null $environment = null, + ?array $permissions = null, + ): self { + $self = new self; + + $self['name'] = $name; + + null !== $environment && $self['environment'] = $environment; + null !== $permissions && $self['permissions'] = $permissions; + + return $self; + } + + public function withName(string $name): self + { + $self = clone $this; + $self['name'] = $name; + + return $self; + } + + /** + * @param Environment|value-of $environment + */ + public function withEnvironment(Environment|string $environment): self + { + $self = clone $this; + $self['environment'] = $environment; + + return $self; + } + + /** + * @param list $permissions + */ + public function withPermissions(array $permissions): self + { + $self = clone $this; + $self['permissions'] = $permissions; + + return $self; + } +} diff --git a/src/SubAccounts/APIKeys/APIKeyCreateParams/Environment.php b/src/SubAccounts/APIKeys/APIKeyCreateParams/Environment.php new file mode 100644 index 0000000..5f86da5 --- /dev/null +++ b/src/SubAccounts/APIKeys/APIKeyCreateParams/Environment.php @@ -0,0 +1,12 @@ +} + */ +final class APIKeyListResponse implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** @var list $items */ + #[Required(list: Item::class)] + public array $items; + + /** + * `new APIKeyListResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * APIKeyListResponse::with(items: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new APIKeyListResponse)->withItems(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $items + */ + public static function with(array $items): self + { + $self = new self; + + $self['items'] = $items; + + return $self; + } + + /** + * @param list $items + */ + public function withItems(array $items): self + { + $self = clone $this; + $self['items'] = $items; + + return $self; + } +} diff --git a/src/SubAccounts/APIKeys/APIKeyListResponse/Item.php b/src/SubAccounts/APIKeys/APIKeyListResponse/Item.php new file mode 100644 index 0000000..a83a275 --- /dev/null +++ b/src/SubAccounts/APIKeys/APIKeyListResponse/Item.php @@ -0,0 +1,208 @@ +, + * keyPrefix: string, + * name: string, + * key?: string|null, + * lastUsedAt?: float|null, + * permissions?: list|null, + * revokedAt?: float|null, + * } + */ +final class Item implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + #[Required] + public string $id; + + #[Required] + public float $createdAt; + + /** @var value-of $environment */ + #[Required(enum: Environment::class)] + public string $environment; + + /** + * First characters of the key for identification. + */ + #[Required] + public string $keyPrefix; + + #[Required] + public string $name; + + /** + * Full API key. Only returned on creation. + */ + #[Optional] + public ?string $key; + + #[Optional(nullable: true)] + public ?float $lastUsedAt; + + /** @var list|null $permissions */ + #[Optional(list: 'string')] + public ?array $permissions; + + #[Optional(nullable: true)] + public ?float $revokedAt; + + /** + * `new Item()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * Item::with(id: ..., createdAt: ..., environment: ..., keyPrefix: ..., name: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new Item) + * ->withID(...) + * ->withCreatedAt(...) + * ->withEnvironment(...) + * ->withKeyPrefix(...) + * ->withName(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Environment|value-of $environment + * @param list|null $permissions + */ + public static function with( + string $id, + float $createdAt, + Environment|string $environment, + string $keyPrefix, + string $name, + ?string $key = null, + ?float $lastUsedAt = null, + ?array $permissions = null, + ?float $revokedAt = null, + ): self { + $self = new self; + + $self['id'] = $id; + $self['createdAt'] = $createdAt; + $self['environment'] = $environment; + $self['keyPrefix'] = $keyPrefix; + $self['name'] = $name; + + null !== $key && $self['key'] = $key; + null !== $lastUsedAt && $self['lastUsedAt'] = $lastUsedAt; + null !== $permissions && $self['permissions'] = $permissions; + null !== $revokedAt && $self['revokedAt'] = $revokedAt; + + return $self; + } + + public function withID(string $id): self + { + $self = clone $this; + $self['id'] = $id; + + return $self; + } + + public function withCreatedAt(float $createdAt): self + { + $self = clone $this; + $self['createdAt'] = $createdAt; + + return $self; + } + + /** + * @param Environment|value-of $environment + */ + public function withEnvironment(Environment|string $environment): self + { + $self = clone $this; + $self['environment'] = $environment; + + return $self; + } + + /** + * First characters of the key for identification. + */ + public function withKeyPrefix(string $keyPrefix): self + { + $self = clone $this; + $self['keyPrefix'] = $keyPrefix; + + return $self; + } + + public function withName(string $name): self + { + $self = clone $this; + $self['name'] = $name; + + return $self; + } + + /** + * Full API key. Only returned on creation. + */ + public function withKey(string $key): self + { + $self = clone $this; + $self['key'] = $key; + + return $self; + } + + public function withLastUsedAt(?float $lastUsedAt): self + { + $self = clone $this; + $self['lastUsedAt'] = $lastUsedAt; + + return $self; + } + + /** + * @param list $permissions + */ + public function withPermissions(array $permissions): self + { + $self = clone $this; + $self['permissions'] = $permissions; + + return $self; + } + + public function withRevokedAt(?float $revokedAt): self + { + $self = clone $this; + $self['revokedAt'] = $revokedAt; + + return $self; + } +} diff --git a/src/SubAccounts/APIKeys/APIKeyListResponse/Item/Environment.php b/src/SubAccounts/APIKeys/APIKeyListResponse/Item/Environment.php new file mode 100644 index 0000000..acff3b0 --- /dev/null +++ b/src/SubAccounts/APIKeys/APIKeyListResponse/Item/Environment.php @@ -0,0 +1,12 @@ + */ + use SdkModel; + + #[Required] + public APIKey $apiKey; + + /** + * `new APIKeyNewResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * APIKeyNewResponse::with(apiKey: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new APIKeyNewResponse)->withAPIKey(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param APIKey|APIKeyShape $apiKey + */ + public static function with(APIKey|array $apiKey): self + { + $self = new self; + + $self['apiKey'] = $apiKey; + + return $self; + } + + /** + * @param APIKey|APIKeyShape $apiKey + */ + public function withAPIKey(APIKey|array $apiKey): self + { + $self = clone $this; + $self['apiKey'] = $apiKey; + + return $self; + } +} diff --git a/src/SubAccounts/APIKeys/APIKeyNewResponse/APIKey.php b/src/SubAccounts/APIKeys/APIKeyNewResponse/APIKey.php new file mode 100644 index 0000000..b318f01 --- /dev/null +++ b/src/SubAccounts/APIKeys/APIKeyNewResponse/APIKey.php @@ -0,0 +1,114 @@ +, + * key: string, + * name: string, + * } + */ +final class APIKey implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + #[Required] + public string $id; + + /** @var value-of $environment */ + #[Required(enum: Environment::class)] + public string $environment; + + #[Required] + public string $key; + + #[Required] + public string $name; + + /** + * `new APIKey()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * APIKey::with(id: ..., environment: ..., key: ..., name: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new APIKey)->withID(...)->withEnvironment(...)->withKey(...)->withName(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Environment|value-of $environment + */ + public static function with( + string $id, + Environment|string $environment, + string $key, + string $name + ): self { + $self = new self; + + $self['id'] = $id; + $self['environment'] = $environment; + $self['key'] = $key; + $self['name'] = $name; + + return $self; + } + + public function withID(string $id): self + { + $self = clone $this; + $self['id'] = $id; + + return $self; + } + + /** + * @param Environment|value-of $environment + */ + public function withEnvironment(Environment|string $environment): self + { + $self = clone $this; + $self['environment'] = $environment; + + return $self; + } + + public function withKey(string $key): self + { + $self = clone $this; + $self['key'] = $key; + + return $self; + } + + public function withName(string $name): self + { + $self = clone $this; + $self['name'] = $name; + + return $self; + } +} diff --git a/src/SubAccounts/APIKeys/APIKeyNewResponse/APIKey/Environment.php b/src/SubAccounts/APIKeys/APIKeyNewResponse/APIKey/Environment.php new file mode 100644 index 0000000..97631a8 --- /dev/null +++ b/src/SubAccounts/APIKeys/APIKeyNewResponse/APIKey/Environment.php @@ -0,0 +1,12 @@ + */ + use SdkModel; + use SdkParams; + + #[Required] + public string $id; + + /** + * `new APIKeyRevokeParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * APIKeyRevokeParams::with(id: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new APIKeyRevokeParams)->withID(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with(string $id): self + { + $self = new self; + + $self['id'] = $id; + + return $self; + } + + public function withID(string $id): self + { + $self = clone $this; + $self['id'] = $id; + + return $self; + } +} diff --git a/src/SubAccounts/SubAccount.php b/src/SubAccounts/SubAccount.php new file mode 100644 index 0000000..d072f40 --- /dev/null +++ b/src/SubAccounts/SubAccount.php @@ -0,0 +1,222 @@ +, + * totalSpent: int, + * apiKey?: string|null, + * creditLimit?: int|null, + * externalID?: string|null, + * metadata?: array|null, + * } + */ +final class SubAccount implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + #[Required] + public string $id; + + #[Required] + public \DateTimeInterface $createdAt; + + #[Required] + public string $name; + + /** @var value-of $status */ + #[Required(enum: Status::class)] + public string $status; + + /** + * Total amount spent by this sub-account in cents. + */ + #[Required] + public int $totalSpent; + + /** + * API key for the sub-account. Only returned on creation. + */ + #[Optional] + public ?string $apiKey; + + /** + * Spending cap in cents. When reached, messages from this sub-account will be blocked. + */ + #[Optional(nullable: true)] + public ?int $creditLimit; + + /** + * External reference ID set by the parent account. + */ + #[Optional('externalId', nullable: true)] + public ?string $externalID; + + /** @var array|null $metadata */ + #[Optional(map: 'mixed', nullable: true)] + public ?array $metadata; + + /** + * `new SubAccount()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * SubAccount::with( + * id: ..., createdAt: ..., name: ..., status: ..., totalSpent: ... + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new SubAccount) + * ->withID(...) + * ->withCreatedAt(...) + * ->withName(...) + * ->withStatus(...) + * ->withTotalSpent(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Status|value-of $status + * @param array|null $metadata + */ + public static function with( + string $id, + \DateTimeInterface $createdAt, + string $name, + Status|string $status, + int $totalSpent, + ?string $apiKey = null, + ?int $creditLimit = null, + ?string $externalID = null, + ?array $metadata = null, + ): self { + $self = new self; + + $self['id'] = $id; + $self['createdAt'] = $createdAt; + $self['name'] = $name; + $self['status'] = $status; + $self['totalSpent'] = $totalSpent; + + null !== $apiKey && $self['apiKey'] = $apiKey; + null !== $creditLimit && $self['creditLimit'] = $creditLimit; + null !== $externalID && $self['externalID'] = $externalID; + null !== $metadata && $self['metadata'] = $metadata; + + return $self; + } + + public function withID(string $id): self + { + $self = clone $this; + $self['id'] = $id; + + return $self; + } + + public function withCreatedAt(\DateTimeInterface $createdAt): self + { + $self = clone $this; + $self['createdAt'] = $createdAt; + + return $self; + } + + public function withName(string $name): self + { + $self = clone $this; + $self['name'] = $name; + + return $self; + } + + /** + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } + + /** + * Total amount spent by this sub-account in cents. + */ + public function withTotalSpent(int $totalSpent): self + { + $self = clone $this; + $self['totalSpent'] = $totalSpent; + + return $self; + } + + /** + * API key for the sub-account. Only returned on creation. + */ + public function withAPIKey(string $apiKey): self + { + $self = clone $this; + $self['apiKey'] = $apiKey; + + return $self; + } + + /** + * Spending cap in cents. When reached, messages from this sub-account will be blocked. + */ + public function withCreditLimit(?int $creditLimit): self + { + $self = clone $this; + $self['creditLimit'] = $creditLimit; + + return $self; + } + + /** + * External reference ID set by the parent account. + */ + public function withExternalID(?string $externalID): self + { + $self = clone $this; + $self['externalID'] = $externalID; + + return $self; + } + + /** + * @param array|null $metadata + */ + public function withMetadata(?array $metadata): self + { + $self = clone $this; + $self['metadata'] = $metadata; + + return $self; + } +} diff --git a/src/SubAccounts/SubAccount/Status.php b/src/SubAccounts/SubAccount/Status.php new file mode 100644 index 0000000..9e2e1c5 --- /dev/null +++ b/src/SubAccounts/SubAccount/Status.php @@ -0,0 +1,12 @@ +|null, + * } + */ +final class SubAccountCreateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + /** + * Name of the sub-account. + */ + #[Required] + public string $name; + + /** + * Spending cap in cents. When reached, messages from this sub-account will be blocked. Omit or set to 0 for no limit. + */ + #[Optional] + public ?int $creditLimit; + + /** + * External reference ID for your own tracking. + */ + #[Optional('externalId')] + public ?string $externalID; + + /** @var array|null $metadata */ + #[Optional(map: 'mixed')] + public ?array $metadata; + + /** + * `new SubAccountCreateParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * SubAccountCreateParams::with(name: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new SubAccountCreateParams)->withName(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param array|null $metadata + */ + public static function with( + string $name, + ?int $creditLimit = null, + ?string $externalID = null, + ?array $metadata = null, + ): self { + $self = new self; + + $self['name'] = $name; + + null !== $creditLimit && $self['creditLimit'] = $creditLimit; + null !== $externalID && $self['externalID'] = $externalID; + null !== $metadata && $self['metadata'] = $metadata; + + return $self; + } + + /** + * Name of the sub-account. + */ + public function withName(string $name): self + { + $self = clone $this; + $self['name'] = $name; + + return $self; + } + + /** + * Spending cap in cents. When reached, messages from this sub-account will be blocked. Omit or set to 0 for no limit. + */ + public function withCreditLimit(int $creditLimit): self + { + $self = clone $this; + $self['creditLimit'] = $creditLimit; + + return $self; + } + + /** + * External reference ID for your own tracking. + */ + public function withExternalID(string $externalID): self + { + $self = clone $this; + $self['externalID'] = $externalID; + + return $self; + } + + /** + * @param array $metadata + */ + public function withMetadata(array $metadata): self + { + $self = clone $this; + $self['metadata'] = $metadata; + + return $self; + } +} diff --git a/src/SubAccounts/SubAccountDeactivateResponse.php b/src/SubAccounts/SubAccountDeactivateResponse.php new file mode 100644 index 0000000..c07ff1d --- /dev/null +++ b/src/SubAccounts/SubAccountDeactivateResponse.php @@ -0,0 +1,82 @@ + */ + use SdkModel; + + /** + * Number of API keys revoked. + */ + #[Required] + public int $keysRevoked; + + #[Required] + public string $message; + + /** + * `new SubAccountDeactivateResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * SubAccountDeactivateResponse::with(keysRevoked: ..., message: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new SubAccountDeactivateResponse)->withKeysRevoked(...)->withMessage(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with(int $keysRevoked, string $message): self + { + $self = new self; + + $self['keysRevoked'] = $keysRevoked; + $self['message'] = $message; + + return $self; + } + + /** + * Number of API keys revoked. + */ + public function withKeysRevoked(int $keysRevoked): self + { + $self = clone $this; + $self['keysRevoked'] = $keysRevoked; + + return $self; + } + + public function withMessage(string $message): self + { + $self = clone $this; + $self['message'] = $message; + + return $self; + } +} diff --git a/src/SubAccounts/SubAccountGetBalanceResponse.php b/src/SubAccounts/SubAccountGetBalanceResponse.php new file mode 100644 index 0000000..8f4d253 --- /dev/null +++ b/src/SubAccounts/SubAccountGetBalanceResponse.php @@ -0,0 +1,147 @@ + */ + use SdkModel; + + /** + * Team balance in cents. All charges are billed to the parent team. + */ + #[Required] + public int $balance; + + #[Required] + public string $currency; + + /** + * Spending cap in cents (only for sub-accounts). + */ + #[Optional(nullable: true)] + public ?int $creditLimit; + + /** + * Whether this API key belongs to a sub-account. + */ + #[Optional] + public ?bool $isSubAccount; + + /** + * Total amount spent by this sub-account in cents (only for sub-accounts). + */ + #[Optional(nullable: true)] + public ?int $totalSpent; + + /** + * `new SubAccountGetBalanceResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * SubAccountGetBalanceResponse::with(balance: ..., currency: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new SubAccountGetBalanceResponse)->withBalance(...)->withCurrency(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with( + int $balance, + string $currency, + ?int $creditLimit = null, + ?bool $isSubAccount = null, + ?int $totalSpent = null, + ): self { + $self = new self; + + $self['balance'] = $balance; + $self['currency'] = $currency; + + null !== $creditLimit && $self['creditLimit'] = $creditLimit; + null !== $isSubAccount && $self['isSubAccount'] = $isSubAccount; + null !== $totalSpent && $self['totalSpent'] = $totalSpent; + + return $self; + } + + /** + * Team balance in cents. All charges are billed to the parent team. + */ + public function withBalance(int $balance): self + { + $self = clone $this; + $self['balance'] = $balance; + + return $self; + } + + public function withCurrency(string $currency): self + { + $self = clone $this; + $self['currency'] = $currency; + + return $self; + } + + /** + * Spending cap in cents (only for sub-accounts). + */ + public function withCreditLimit(?int $creditLimit): self + { + $self = clone $this; + $self['creditLimit'] = $creditLimit; + + return $self; + } + + /** + * Whether this API key belongs to a sub-account. + */ + public function withIsSubAccount(bool $isSubAccount): self + { + $self = clone $this; + $self['isSubAccount'] = $isSubAccount; + + return $self; + } + + /** + * Total amount spent by this sub-account in cents (only for sub-accounts). + */ + public function withTotalSpent(?int $totalSpent): self + { + $self = clone $this; + $self['totalSpent'] = $totalSpent; + + return $self; + } +} diff --git a/src/SubAccounts/SubAccountGetResponse.php b/src/SubAccounts/SubAccountGetResponse.php new file mode 100644 index 0000000..1e044ec --- /dev/null +++ b/src/SubAccounts/SubAccountGetResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public SubAccount $subAccount; + + /** + * `new SubAccountGetResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * SubAccountGetResponse::with(subAccount: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new SubAccountGetResponse)->withSubAccount(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param SubAccount|SubAccountShape $subAccount + */ + public static function with(SubAccount|array $subAccount): self + { + $self = new self; + + $self['subAccount'] = $subAccount; + + return $self; + } + + /** + * @param SubAccount|SubAccountShape $subAccount + */ + public function withSubAccount(SubAccount|array $subAccount): self + { + $self = clone $this; + $self['subAccount'] = $subAccount; + + return $self; + } +} diff --git a/src/SubAccounts/SubAccountListParams.php b/src/SubAccounts/SubAccountListParams.php new file mode 100644 index 0000000..736f411 --- /dev/null +++ b/src/SubAccounts/SubAccountListParams.php @@ -0,0 +1,68 @@ + */ + use SdkModel; + use SdkParams; + + #[Optional] + public ?string $cursor; + + #[Optional] + public ?int $limit; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with(?string $cursor = null, ?int $limit = null): self + { + $self = new self; + + null !== $cursor && $self['cursor'] = $cursor; + null !== $limit && $self['limit'] = $limit; + + return $self; + } + + public function withCursor(string $cursor): self + { + $self = clone $this; + $self['cursor'] = $cursor; + + return $self; + } + + public function withLimit(int $limit): self + { + $self = clone $this; + $self['limit'] = $limit; + + return $self; + } +} diff --git a/src/SubAccounts/SubAccountNewResponse.php b/src/SubAccounts/SubAccountNewResponse.php new file mode 100644 index 0000000..b992042 --- /dev/null +++ b/src/SubAccounts/SubAccountNewResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public SubAccount $subAccount; + + /** + * `new SubAccountNewResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * SubAccountNewResponse::with(subAccount: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new SubAccountNewResponse)->withSubAccount(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param SubAccount|SubAccountShape $subAccount + */ + public static function with(SubAccount|array $subAccount): self + { + $self = new self; + + $self['subAccount'] = $subAccount; + + return $self; + } + + /** + * @param SubAccount|SubAccountShape $subAccount + */ + public function withSubAccount(SubAccount|array $subAccount): self + { + $self = clone $this; + $self['subAccount'] = $subAccount; + + return $self; + } +} diff --git a/src/SubAccounts/SubAccountUpdateParams.php b/src/SubAccounts/SubAccountUpdateParams.php new file mode 100644 index 0000000..249a6ac --- /dev/null +++ b/src/SubAccounts/SubAccountUpdateParams.php @@ -0,0 +1,125 @@ +|null, + * name?: string|null, + * status?: null|Status|value-of, + * } + */ +final class SubAccountUpdateParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + #[Optional(nullable: true)] + public ?int $creditLimit; + + #[Optional('externalId')] + public ?string $externalID; + + /** @var array|null $metadata */ + #[Optional(map: 'mixed')] + public ?array $metadata; + + #[Optional] + public ?string $name; + + /** @var value-of|null $status */ + #[Optional(enum: Status::class)] + public ?string $status; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param array|null $metadata + * @param Status|value-of|null $status + */ + public static function with( + ?int $creditLimit = null, + ?string $externalID = null, + ?array $metadata = null, + ?string $name = null, + Status|string|null $status = null, + ): self { + $self = new self; + + null !== $creditLimit && $self['creditLimit'] = $creditLimit; + null !== $externalID && $self['externalID'] = $externalID; + null !== $metadata && $self['metadata'] = $metadata; + null !== $name && $self['name'] = $name; + null !== $status && $self['status'] = $status; + + return $self; + } + + public function withCreditLimit(?int $creditLimit): self + { + $self = clone $this; + $self['creditLimit'] = $creditLimit; + + return $self; + } + + public function withExternalID(string $externalID): self + { + $self = clone $this; + $self['externalID'] = $externalID; + + return $self; + } + + /** + * @param array $metadata + */ + public function withMetadata(array $metadata): self + { + $self = clone $this; + $self['metadata'] = $metadata; + + return $self; + } + + public function withName(string $name): self + { + $self = clone $this; + $self['name'] = $name; + + return $self; + } + + /** + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } +} diff --git a/src/SubAccounts/SubAccountUpdateParams/Status.php b/src/SubAccounts/SubAccountUpdateParams/Status.php new file mode 100644 index 0000000..5ef3566 --- /dev/null +++ b/src/SubAccounts/SubAccountUpdateParams/Status.php @@ -0,0 +1,12 @@ + */ + use SdkModel; + + #[Required] + public SubAccount $subAccount; + + /** + * `new SubAccountUpdateResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * SubAccountUpdateResponse::with(subAccount: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new SubAccountUpdateResponse)->withSubAccount(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param SubAccount|SubAccountShape $subAccount + */ + public static function with(SubAccount|array $subAccount): self + { + $self = new self; + + $self['subAccount'] = $subAccount; + + return $self; + } + + /** + * @param SubAccount|SubAccountShape $subAccount + */ + public function withSubAccount(SubAccount|array $subAccount): self + { + $self = clone $this; + $self['subAccount'] = $subAccount; + + return $self; + } +} diff --git a/src/Templates/Template/Button.php b/src/Templates/Template/Button.php index f5cc3b2..9f7baf9 100644 --- a/src/Templates/Template/Button.php +++ b/src/Templates/Template/Button.php @@ -12,6 +12,7 @@ /** * @phpstan-type ButtonShape = array{ + * example?: string|null, * otpType?: null|OtpType|value-of, * packageName?: string|null, * phoneNumber?: string|null, @@ -26,6 +27,12 @@ final class Button implements BaseModel /** @use SdkModel */ use SdkModel; + /** + * Sample value used to substitute `{{1}}` in the URL when submitting the template to Meta for review. Only present for dynamic URL buttons. + */ + #[Optional] + public ?string $example; + /** * OTP button type. Required when type is 'otp'. * @@ -73,6 +80,7 @@ public function __construct() * @param Type|value-of|null $type */ public static function with( + ?string $example = null, OtpType|string|null $otpType = null, ?string $packageName = null, ?string $phoneNumber = null, @@ -83,6 +91,7 @@ public static function with( ): self { $self = new self; + null !== $example && $self['example'] = $example; null !== $otpType && $self['otpType'] = $otpType; null !== $packageName && $self['packageName'] = $packageName; null !== $phoneNumber && $self['phoneNumber'] = $phoneNumber; @@ -94,6 +103,17 @@ public static function with( return $self; } + /** + * Sample value used to substitute `{{1}}` in the URL when submitting the template to Meta for review. Only present for dynamic URL buttons. + */ + public function withExample(string $example): self + { + $self = clone $this; + $self['example'] = $example; + + return $self; + } + /** * OTP button type. Required when type is 'otp'. * diff --git a/src/Templates/TemplateCreateParams/Button.php b/src/Templates/TemplateCreateParams/Button.php index 5a0efcb..420b9e6 100644 --- a/src/Templates/TemplateCreateParams/Button.php +++ b/src/Templates/TemplateCreateParams/Button.php @@ -15,6 +15,7 @@ * @phpstan-type ButtonShape = array{ * text: string, * type: Type|value-of, + * example?: string|null, * otpType?: null|OtpType|value-of, * packageName?: string|null, * phoneNumber?: string|null, @@ -34,6 +35,12 @@ final class Button implements BaseModel #[Required(enum: Type::class)] public string $type; + /** + * Sample value Meta uses to review templates with a dynamic URL button. Substituted into `{{1}}` of the URL when the template is submitted to Meta. Only meaningful when `url` contains `{{1}}`; ignored for static URLs. + */ + #[Optional] + public ?string $example; + /** * Required when type is 'otp'. COPY_CODE shows copy button, ONE_TAP enables Android autofill. * @@ -57,6 +64,9 @@ final class Button implements BaseModel #[Optional] public ?string $signatureHash; + /** + * Button destination. Use `{{1}}` exactly once for a dynamic URL (e.g. `https://example.com/orders/{{1}}`); WhatsApp only accepts the strict `{{1}}` form. Static URLs must not contain any `{{...}}` placeholder. + */ #[Optional] public ?string $url; @@ -90,6 +100,7 @@ public function __construct() public static function with( string $text, Type|string $type, + ?string $example = null, OtpType|string|null $otpType = null, ?string $packageName = null, ?string $phoneNumber = null, @@ -101,6 +112,7 @@ public static function with( $self['text'] = $text; $self['type'] = $type; + null !== $example && $self['example'] = $example; null !== $otpType && $self['otpType'] = $otpType; null !== $packageName && $self['packageName'] = $packageName; null !== $phoneNumber && $self['phoneNumber'] = $phoneNumber; @@ -129,6 +141,17 @@ public function withType(Type|string $type): self return $self; } + /** + * Sample value Meta uses to review templates with a dynamic URL button. Substituted into `{{1}}` of the URL when the template is submitted to Meta. Only meaningful when `url` contains `{{1}}`; ignored for static URLs. + */ + public function withExample(string $example): self + { + $self = clone $this; + $self['example'] = $example; + + return $self; + } + /** * Required when type is 'otp'. COPY_CODE shows copy button, ONE_TAP enables Android autofill. * @@ -172,6 +195,9 @@ public function withSignatureHash(string $signatureHash): self return $self; } + /** + * Button destination. Use `{{1}}` exactly once for a dynamic URL (e.g. `https://example.com/orders/{{1}}`); WhatsApp only accepts the strict `{{1}}` form. Static URLs must not contain any `{{...}}` placeholder. + */ public function withURL(string $url): self { $self = clone $this; diff --git a/src/URLs/URLGetDetailsResponse.php b/src/URLs/URLGetDetailsResponse.php new file mode 100644 index 0000000..d261c33 --- /dev/null +++ b/src/URLs/URLGetDetailsResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public VerifiedURL $url; + + /** + * `new URLGetDetailsResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * URLGetDetailsResponse::with(url: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new URLGetDetailsResponse)->withURL(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param VerifiedURL|VerifiedURLShape $url + */ + public static function with(VerifiedURL|array $url): self + { + $self = new self; + + $self['url'] = $url; + + return $self; + } + + /** + * @param VerifiedURL|VerifiedURLShape $url + */ + public function withURL(VerifiedURL|array $url): self + { + $self = clone $this; + $self['url'] = $url; + + return $self; + } +} diff --git a/src/URLs/URLListVerifiedParams.php b/src/URLs/URLListVerifiedParams.php new file mode 100644 index 0000000..f08d2fd --- /dev/null +++ b/src/URLs/URLListVerifiedParams.php @@ -0,0 +1,96 @@ + + * } + */ +final class URLListVerifiedParams implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + use SdkParams; + + #[Optional] + public ?string $cursor; + + #[Optional] + public ?int $limit; + + /** + * Filter by verification status. + * + * @var value-of|null $status + */ + #[Optional(enum: Status::class)] + public ?string $status; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Status|value-of|null $status + */ + public static function with( + ?string $cursor = null, + ?int $limit = null, + Status|string|null $status = null + ): self { + $self = new self; + + null !== $cursor && $self['cursor'] = $cursor; + null !== $limit && $self['limit'] = $limit; + null !== $status && $self['status'] = $status; + + return $self; + } + + public function withCursor(string $cursor): self + { + $self = clone $this; + $self['cursor'] = $cursor; + + return $self; + } + + public function withLimit(int $limit): self + { + $self = clone $this; + $self['limit'] = $limit; + + return $self; + } + + /** + * Filter by verification status. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } +} diff --git a/src/URLs/URLListVerifiedParams/Status.php b/src/URLs/URLListVerifiedParams/Status.php new file mode 100644 index 0000000..6254eaf --- /dev/null +++ b/src/URLs/URLListVerifiedParams/Status.php @@ -0,0 +1,19 @@ + */ + use SdkModel; + use SdkParams; + + /** + * The URL to submit for verification. + */ + #[Required] + public string $url; + + /** + * `new URLSubmitForVerificationParams()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * URLSubmitForVerificationParams::with(url: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new URLSubmitForVerificationParams)->withURL(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with(string $url): self + { + $self = new self; + + $self['url'] = $url; + + return $self; + } + + /** + * The URL to submit for verification. + */ + public function withURL(string $url): self + { + $self = clone $this; + $self['url'] = $url; + + return $self; + } +} diff --git a/src/URLs/URLSubmitForVerificationResponse.php b/src/URLs/URLSubmitForVerificationResponse.php new file mode 100644 index 0000000..b6644dd --- /dev/null +++ b/src/URLs/URLSubmitForVerificationResponse.php @@ -0,0 +1,71 @@ + */ + use SdkModel; + + #[Required] + public VerifiedURL $url; + + /** + * `new URLSubmitForVerificationResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * URLSubmitForVerificationResponse::with(url: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new URLSubmitForVerificationResponse)->withURL(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param VerifiedURL|VerifiedURLShape $url + */ + public static function with(VerifiedURL|array $url): self + { + $self = new self; + + $self['url'] = $url; + + return $self; + } + + /** + * @param VerifiedURL|VerifiedURLShape $url + */ + public function withURL(VerifiedURL|array $url): self + { + $self = clone $this; + $self['url'] = $url; + + return $self; + } +} diff --git a/src/URLs/VerifiedURL.php b/src/URLs/VerifiedURL.php new file mode 100644 index 0000000..4494b71 --- /dev/null +++ b/src/URLs/VerifiedURL.php @@ -0,0 +1,193 @@ +, + * url: string, + * approvalType?: null|ApprovalType|value-of, + * updatedAt?: \DateTimeInterface|null, + * } + */ +final class VerifiedURL implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + #[Required] + public string $id; + + #[Required] + public \DateTimeInterface $createdAt; + + /** + * Domain extracted from the URL. + */ + #[Required] + public string $domain; + + /** + * Status of a verified URL. + * + * @var value-of $status + */ + #[Required(enum: Status::class)] + public string $status; + + /** + * The verified URL. + */ + #[Required] + public string $url; + + /** + * How the URL was approved or rejected. + * + * @var value-of|null $approvalType + */ + #[Optional(enum: ApprovalType::class)] + public ?string $approvalType; + + #[Optional] + public ?\DateTimeInterface $updatedAt; + + /** + * `new VerifiedURL()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * VerifiedURL::with(id: ..., createdAt: ..., domain: ..., status: ..., url: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new VerifiedURL) + * ->withID(...) + * ->withCreatedAt(...) + * ->withDomain(...) + * ->withStatus(...) + * ->withURL(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Status|value-of $status + * @param ApprovalType|value-of|null $approvalType + */ + public static function with( + string $id, + \DateTimeInterface $createdAt, + string $domain, + Status|string $status, + string $url, + ApprovalType|string|null $approvalType = null, + ?\DateTimeInterface $updatedAt = null, + ): self { + $self = new self; + + $self['id'] = $id; + $self['createdAt'] = $createdAt; + $self['domain'] = $domain; + $self['status'] = $status; + $self['url'] = $url; + + null !== $approvalType && $self['approvalType'] = $approvalType; + null !== $updatedAt && $self['updatedAt'] = $updatedAt; + + return $self; + } + + public function withID(string $id): self + { + $self = clone $this; + $self['id'] = $id; + + return $self; + } + + public function withCreatedAt(\DateTimeInterface $createdAt): self + { + $self = clone $this; + $self['createdAt'] = $createdAt; + + return $self; + } + + /** + * Domain extracted from the URL. + */ + public function withDomain(string $domain): self + { + $self = clone $this; + $self['domain'] = $domain; + + return $self; + } + + /** + * Status of a verified URL. + * + * @param Status|value-of $status + */ + public function withStatus(Status|string $status): self + { + $self = clone $this; + $self['status'] = $status; + + return $self; + } + + /** + * The verified URL. + */ + public function withURL(string $url): self + { + $self = clone $this; + $self['url'] = $url; + + return $self; + } + + /** + * How the URL was approved or rejected. + * + * @param ApprovalType|value-of $approvalType + */ + public function withApprovalType(ApprovalType|string $approvalType): self + { + $self = clone $this; + $self['approvalType'] = $approvalType; + + return $self; + } + + public function withUpdatedAt(\DateTimeInterface $updatedAt): self + { + $self = clone $this; + $self['updatedAt'] = $updatedAt; + + return $self; + } +} diff --git a/src/URLs/VerifiedURL/ApprovalType.php b/src/URLs/VerifiedURL/ApprovalType.php new file mode 100644 index 0000000..612e094 --- /dev/null +++ b/src/URLs/VerifiedURL/ApprovalType.php @@ -0,0 +1,15 @@ +, + * } + */ +final class UsageGetResponse implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * Emails sent this month. + */ + #[Required] + public int $emailsSent; + + #[Required] + public Limits $limits; + + /** + * A2P messages sent this month (WhatsApp replies + Telegram). + */ + #[Required] + public int $messagesA2P; + + /** + * Current month in YYYY-MM format. + */ + #[Required] + public string $monthKey; + + /** @var value-of $tier */ + #[Required(enum: Tier::class)] + public string $tier; + + /** + * `new UsageGetResponse()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * UsageGetResponse::with( + * emailsSent: ..., limits: ..., messagesA2P: ..., monthKey: ..., tier: ... + * ) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new UsageGetResponse) + * ->withEmailsSent(...) + * ->withLimits(...) + * ->withMessagesA2P(...) + * ->withMonthKey(...) + * ->withTier(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param Limits|LimitsShape $limits + * @param Tier|value-of $tier + */ + public static function with( + int $emailsSent, + Limits|array $limits, + int $messagesA2P, + string $monthKey, + Tier|string $tier, + ): self { + $self = new self; + + $self['emailsSent'] = $emailsSent; + $self['limits'] = $limits; + $self['messagesA2P'] = $messagesA2P; + $self['monthKey'] = $monthKey; + $self['tier'] = $tier; + + return $self; + } + + /** + * Emails sent this month. + */ + public function withEmailsSent(int $emailsSent): self + { + $self = clone $this; + $self['emailsSent'] = $emailsSent; + + return $self; + } + + /** + * @param Limits|LimitsShape $limits + */ + public function withLimits(Limits|array $limits): self + { + $self = clone $this; + $self['limits'] = $limits; + + return $self; + } + + /** + * A2P messages sent this month (WhatsApp replies + Telegram). + */ + public function withMessagesA2P(int $messagesA2P): self + { + $self = clone $this; + $self['messagesA2P'] = $messagesA2P; + + return $self; + } + + /** + * Current month in YYYY-MM format. + */ + public function withMonthKey(string $monthKey): self + { + $self = clone $this; + $self['monthKey'] = $monthKey; + + return $self; + } + + /** + * @param Tier|value-of $tier + */ + public function withTier(Tier|string $tier): self + { + $self = clone $this; + $self['tier'] = $tier; + + return $self; + } +} diff --git a/src/Usage/UsageGetResponse/Limits.php b/src/Usage/UsageGetResponse/Limits.php new file mode 100644 index 0000000..f1d7795 --- /dev/null +++ b/src/Usage/UsageGetResponse/Limits.php @@ -0,0 +1,62 @@ + */ + use SdkModel; + + #[Optional] + public ?int $emails; + + #[Optional] + public ?int $messagesA2P; + + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with( + ?int $emails = null, + ?int $messagesA2P = null + ): self { + $self = new self; + + null !== $emails && $self['emails'] = $emails; + null !== $messagesA2P && $self['messagesA2P'] = $messagesA2P; + + return $self; + } + + public function withEmails(int $emails): self + { + $self = clone $this; + $self['emails'] = $emails; + + return $self; + } + + public function withMessagesA2P(int $messagesA2P): self + { + $self = clone $this; + $self['messagesA2P'] = $messagesA2P; + + return $self; + } +} diff --git a/src/Usage/UsageGetResponse/Tier.php b/src/Usage/UsageGetResponse/Tier.php new file mode 100644 index 0000000..2ef07e3 --- /dev/null +++ b/src/Usage/UsageGetResponse/Tier.php @@ -0,0 +1,16 @@ +> */ + use SdkModel; + + #[Required(enum: TicketPriority::class)] + public TicketPriority $priority; + + /** @var list */ + #[Required(list: TicketPriority::class)] + public array $labels; + + public function __construct() + { + $this->initialize(); + } +} + /** * @internal * @@ -141,4 +165,42 @@ public function testSerializeModelWithExplicitNull(): void json_encode($model) ); } + + #[Test] + public function testScalarEnumCoercesToInstance(): void + { + $model = Ticket::fromArray(['priority' => 'low', 'labels' => []]); + $this->assertSame(TicketPriority::Low, $model->priority); + } + + #[Test] + public function testListOfEnumCoercesElementsToInstances(): void + { + $model = Ticket::fromArray(['priority' => 'low', 'labels' => ['low', 'high']]); + $this->assertCount(2, $model->labels); + $this->assertSame(TicketPriority::Low, $model->labels[0]); + $this->assertSame(TicketPriority::High, $model->labels[1]); + } + + #[Test] + public function testEnumInstancePassesThrough(): void + { + $model = Ticket::fromArray(['priority' => TicketPriority::High, 'labels' => []]); + $this->assertSame(TicketPriority::High, $model->priority); + } + + #[Test] + public function testInvalidEnumScalarFallsBackToData(): void + { + $model = Ticket::fromArray(['priority' => 'urgent', 'labels' => []]); + $this->assertSame('urgent', $model['priority']); + } + + #[Test] + public function testEnumWireFormatStableAcrossConstruction(): void + { + $fromScalar = Ticket::fromArray(['priority' => 'low', 'labels' => ['high']]); + $fromInstance = Ticket::fromArray(['priority' => TicketPriority::Low, 'labels' => [TicketPriority::High]]); + $this->assertSame(json_encode($fromScalar), json_encode($fromInstance)); + } } diff --git a/tests/Services/BalanceTest.php b/tests/Services/BalanceTest.php new file mode 100644 index 0000000..7863276 --- /dev/null +++ b/tests/Services/BalanceTest.php @@ -0,0 +1,43 @@ +client = $client; + } + + #[Test] + public function testRetrieve(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->balance->retrieve(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(BalanceGetResponse::class, $result); + } +} diff --git a/tests/Services/Broadcasts/ContactsTest.php b/tests/Services/Broadcasts/ContactsTest.php index 8a8f09e..bcfe4a3 100644 --- a/tests/Services/Broadcasts/ContactsTest.php +++ b/tests/Services/Broadcasts/ContactsTest.php @@ -78,10 +78,12 @@ public function testAddWithOptionalParams(): void contacts: [ [ 'recipient' => '+14155551234', + 'templateButtonVariables' => ['0' => 'abc-report-token'], 'templateVariables' => ['name' => 'John', 'order_id' => 'ORD-001'], ], [ 'recipient' => '+14155555678', + 'templateButtonVariables' => ['0' => 'abc-report-token'], 'templateVariables' => ['name' => 'Jane', 'order_id' => 'ORD-002'], ], ], diff --git a/tests/Services/BroadcastsTest.php b/tests/Services/BroadcastsTest.php index 2a2dc10..a80786e 100644 --- a/tests/Services/BroadcastsTest.php +++ b/tests/Services/BroadcastsTest.php @@ -9,11 +9,13 @@ use Zavudev\Broadcasts\Broadcast; use Zavudev\Broadcasts\BroadcastCancelResponse; use Zavudev\Broadcasts\BroadcastChannel; +use Zavudev\Broadcasts\BroadcastEscalateReviewResponse; use Zavudev\Broadcasts\BroadcastGetResponse; use Zavudev\Broadcasts\BroadcastMessageType; use Zavudev\Broadcasts\BroadcastNewResponse; use Zavudev\Broadcasts\BroadcastProgress; use Zavudev\Broadcasts\BroadcastRescheduleResponse; +use Zavudev\Broadcasts\BroadcastRetryReviewResponse; use Zavudev\Broadcasts\BroadcastSendResponse; use Zavudev\Broadcasts\BroadcastUpdateResponse; use Zavudev\Client; @@ -69,6 +71,7 @@ public function testCreateWithOptionalParams(): void 'mediaID' => 'mediaId', 'mediaURL' => 'mediaUrl', 'mimeType' => 'mimeType', + 'templateButtonVariables' => ['foo' => 'string'], 'templateID' => 'templateId', 'templateVariables' => ['foo' => 'string'], ], @@ -156,6 +159,19 @@ public function testCancel(): void $this->assertInstanceOf(BroadcastCancelResponse::class, $result); } + #[Test] + public function testEscalateReview(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->broadcasts->escalateReview('broadcastId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(BroadcastEscalateReviewResponse::class, $result); + } + #[Test] public function testProgress(): void { @@ -201,6 +217,19 @@ public function testRescheduleWithOptionalParams(): void $this->assertInstanceOf(BroadcastRescheduleResponse::class, $result); } + #[Test] + public function testRetryReview(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->broadcasts->retryReview('broadcastId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(BroadcastRetryReviewResponse::class, $result); + } + #[Test] public function testSend(): void { diff --git a/tests/Services/Contacts/ChannelsTest.php b/tests/Services/Contacts/ChannelsTest.php new file mode 100644 index 0000000..f47c57c --- /dev/null +++ b/tests/Services/Contacts/ChannelsTest.php @@ -0,0 +1,168 @@ +client = $client; + } + + #[Test] + public function testUpdate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->channels->update( + 'channelId', + contactID: 'contactId' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(ChannelUpdateResponse::class, $result); + } + + #[Test] + public function testUpdateWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->channels->update( + 'channelId', + contactID: 'contactId', + label: 'label', + metadata: ['foo' => 'string'], + verified: true, + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(ChannelUpdateResponse::class, $result); + } + + #[Test] + public function testAdd(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->channels->add( + 'contactId', + channel: 'email', + identifier: 'john.work@company.com' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(ChannelAddResponse::class, $result); + } + + #[Test] + public function testAddWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->channels->add( + 'contactId', + channel: 'email', + identifier: 'john.work@company.com', + countryCode: 'US', + isPrimary: true, + label: 'work', + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(ChannelAddResponse::class, $result); + } + + #[Test] + public function testRemove(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->channels->remove( + 'channelId', + contactID: 'contactId' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertNull($result); + } + + #[Test] + public function testRemoveWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->channels->remove( + 'channelId', + contactID: 'contactId' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertNull($result); + } + + #[Test] + public function testSetPrimary(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->channels->setPrimary( + 'channelId', + contactID: 'contactId' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(ChannelSetPrimaryResponse::class, $result); + } + + #[Test] + public function testSetPrimaryWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->channels->setPrimary( + 'channelId', + contactID: 'contactId' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(ChannelSetPrimaryResponse::class, $result); + } +} diff --git a/tests/Services/ContactsTest.php b/tests/Services/ContactsTest.php index 98dfe01..84811c3 100644 --- a/tests/Services/ContactsTest.php +++ b/tests/Services/ContactsTest.php @@ -29,6 +29,46 @@ protected function setUp(): void $this->client = $client; } + #[Test] + public function testCreate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->create( + channels: [['channel' => 'sms', 'identifier' => '+14155551234']] + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Contact::class, $result); + } + + #[Test] + public function testCreateWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->create( + channels: [ + [ + 'channel' => 'sms', + 'identifier' => '+14155551234', + 'countryCode' => 'US', + 'isPrimary' => true, + 'label' => 'work', + ], + ], + displayName: 'John Doe', + metadata: ['foo' => 'string'], + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Contact::class, $result); + } + #[Test] public function testRetrieve(): void { @@ -73,6 +113,51 @@ public function testList(): void } } + #[Test] + public function testDismissMergeSuggestion(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->dismissMergeSuggestion('contactId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertNull($result); + } + + #[Test] + public function testMerge(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->merge( + 'contactId', + sourceContactID: 'jx7xyz789' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Contact::class, $result); + } + + #[Test] + public function testMergeWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->contacts->merge( + 'contactId', + sourceContactID: 'jx7xyz789' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Contact::class, $result); + } + #[Test] public function testRetrieveByPhone(): void { diff --git a/tests/Services/ExportsTest.php b/tests/Services/ExportsTest.php new file mode 100644 index 0000000..fc19e61 --- /dev/null +++ b/tests/Services/ExportsTest.php @@ -0,0 +1,96 @@ +client = $client; + } + + #[Test] + public function testCreate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->exports->create( + dataTypes: ['messages', 'conversations'] + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(ExportNewResponse::class, $result); + } + + #[Test] + public function testCreateWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->exports->create( + dataTypes: ['messages', 'conversations'], + dateFrom: new \DateTimeImmutable('2024-01-01T00:00:00Z'), + dateTo: new \DateTimeImmutable('2024-12-31T23:59:59Z'), + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(ExportNewResponse::class, $result); + } + + #[Test] + public function testRetrieve(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->exports->retrieve('exportId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(ExportGetResponse::class, $result); + } + + #[Test] + public function testList(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $page = $this->client->exports->list(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Cursor::class, $page); + + if ($item = $page->getItems()[0] ?? null) { + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(DataExport::class, $item); + } + } +} diff --git a/tests/Services/InvitationsTest.php b/tests/Services/InvitationsTest.php new file mode 100644 index 0000000..6b0180a --- /dev/null +++ b/tests/Services/InvitationsTest.php @@ -0,0 +1,91 @@ +client = $client; + } + + #[Test] + public function testCreate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->invitations->create(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(InvitationNewResponse::class, $result); + } + + #[Test] + public function testRetrieve(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->invitations->retrieve('invitationId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(InvitationGetResponse::class, $result); + } + + #[Test] + public function testList(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $page = $this->client->invitations->list(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Cursor::class, $page); + + if ($item = $page->getItems()[0] ?? null) { + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Invitation::class, $item); + } + } + + #[Test] + public function testCancel(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->invitations->cancel('invitationId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(InvitationCancelResponse::class, $result); + } +} diff --git a/tests/Services/MessagesTest.php b/tests/Services/MessagesTest.php index b244a4f..9311e01 100644 --- a/tests/Services/MessagesTest.php +++ b/tests/Services/MessagesTest.php @@ -128,8 +128,14 @@ public function testSendWithOptionalParams(): void content: [ 'buttons' => [['id' => 'id', 'title' => 'title']], 'contacts' => [['name' => 'name', 'phones' => ['string']]], + 'ctaDisplayText' => 'See Dates', + 'ctaHeaderMediaURL' => 'https://example.com', + 'ctaHeaderText' => 'ctaHeaderText', + 'ctaHeaderType' => 'text', + 'ctaURL' => 'https://example.com/schedule', 'emoji' => 'emoji', 'filename' => 'invoice.pdf', + 'footerText' => 'Dates subject to change.', 'latitude' => 0, 'listButton' => 'listButton', 'locationAddress' => 'locationAddress', @@ -147,6 +153,7 @@ public function testSendWithOptionalParams(): void 'title' => 'title', ], ], + 'templateButtonVariables' => ['0' => 'abc-report-token'], 'templateID' => 'templateId', 'templateVariables' => ['1' => 'John', '2' => 'ORD-12345'], ], diff --git a/tests/Services/Number10dlc/BrandsTest.php b/tests/Services/Number10dlc/BrandsTest.php new file mode 100644 index 0000000..4c9e9d8 --- /dev/null +++ b/tests/Services/Number10dlc/BrandsTest.php @@ -0,0 +1,188 @@ +client = $client; + } + + #[Test] + public function testCreate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->brands->create( + city: 'San Francisco', + country: 'US', + displayName: 'Acme Corp', + email: 'compliance@acme.com', + entityType: 'PRIVATE_PROFIT', + phone: '+14155551234', + postalCode: '94102', + state: 'CA', + street: '123 Main St', + vertical: 'Technology', + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(BrandNewResponse::class, $result); + } + + #[Test] + public function testCreateWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->brands->create( + city: 'San Francisco', + country: 'US', + displayName: 'Acme Corp', + email: 'compliance@acme.com', + entityType: 'PRIVATE_PROFIT', + phone: '+14155551234', + postalCode: '94102', + state: 'CA', + street: '123 Main St', + vertical: 'Technology', + companyName: 'Acme Corporation', + ein: '12-3456789', + firstName: 'firstName', + lastName: 'lastName', + stockExchange: 'stockExchange', + stockSymbol: 'stockSymbol', + website: 'https://acme.com', + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(BrandNewResponse::class, $result); + } + + #[Test] + public function testRetrieve(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->brands->retrieve('brandId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(BrandGetResponse::class, $result); + } + + #[Test] + public function testUpdate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->brands->update('brandId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(BrandUpdateResponse::class, $result); + } + + #[Test] + public function testList(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $page = $this->client->number10dlc->brands->list(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Cursor::class, $page); + + if ($item = $page->getItems()[0] ?? null) { + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(TenDlcBrand::class, $item); + } + } + + #[Test] + public function testDelete(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->brands->delete('brandId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertNull($result); + } + + #[Test] + public function testListUseCases(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->brands->listUseCases(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(BrandListUseCasesResponse::class, $result); + } + + #[Test] + public function testSubmit(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->brands->submit('brandId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(BrandSubmitResponse::class, $result); + } + + #[Test] + public function testSyncStatus(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->brands->syncStatus('brandId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(BrandSyncStatusResponse::class, $result); + } +} diff --git a/tests/Services/Number10dlc/Campaigns/PhoneNumbersTest.php b/tests/Services/Number10dlc/Campaigns/PhoneNumbersTest.php new file mode 100644 index 0000000..bb4c027 --- /dev/null +++ b/tests/Services/Number10dlc/Campaigns/PhoneNumbersTest.php @@ -0,0 +1,110 @@ +client = $client; + } + + #[Test] + public function testList(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->phoneNumbers->list( + 'campaignId' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(PhoneNumberListResponse::class, $result); + } + + #[Test] + public function testAssign(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->phoneNumbers->assign( + 'campaignId', + phoneNumberID: 'pn_abc123' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(PhoneNumberAssignResponse::class, $result); + } + + #[Test] + public function testAssignWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->phoneNumbers->assign( + 'campaignId', + phoneNumberID: 'pn_abc123' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(PhoneNumberAssignResponse::class, $result); + } + + #[Test] + public function testUnassign(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->phoneNumbers->unassign( + 'assignmentId', + campaignID: 'campaignId' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertNull($result); + } + + #[Test] + public function testUnassignWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->phoneNumbers->unassign( + 'assignmentId', + campaignID: 'campaignId' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertNull($result); + } +} diff --git a/tests/Services/Number10dlc/CampaignsTest.php b/tests/Services/Number10dlc/CampaignsTest.php new file mode 100644 index 0000000..347eb39 --- /dev/null +++ b/tests/Services/Number10dlc/CampaignsTest.php @@ -0,0 +1,186 @@ +client = $client; + } + + #[Test] + public function testCreate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->create( + affiliateMarketing: false, + ageGated: false, + brandID: 'brand_abc123', + description: 'Send order status updates and shipping notifications to customers who opted in.', + directLending: false, + embeddedLink: true, + embeddedPhone: false, + name: 'Order Notifications', + numberPooling: false, + sampleMessages: [ + 'Hi {{name}}, your order #{{order_id}} has shipped! Track it at {{url}}', + 'Your order #{{order_id}} has been delivered. Thank you for your purchase!', + ], + subscriberHelp: true, + subscriberOptIn: true, + subscriberOptOut: true, + useCase: 'ACCOUNT_NOTIFICATION', + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(CampaignNewResponse::class, $result); + } + + #[Test] + public function testCreateWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->create( + affiliateMarketing: false, + ageGated: false, + brandID: 'brand_abc123', + description: 'Send order status updates and shipping notifications to customers who opted in.', + directLending: false, + embeddedLink: true, + embeddedPhone: false, + name: 'Order Notifications', + numberPooling: false, + sampleMessages: [ + 'Hi {{name}}, your order #{{order_id}} has shipped! Track it at {{url}}', + 'Your order #{{order_id}} has been delivered. Thank you for your purchase!', + ], + subscriberHelp: true, + subscriberOptIn: true, + subscriberOptOut: true, + useCase: 'ACCOUNT_NOTIFICATION', + helpMessage: 'helpMessage', + messageFlow: 'messageFlow', + optInKeywords: ['string'], + optOutKeywords: ['string'], + subUseCases: ['string'], + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(CampaignNewResponse::class, $result); + } + + #[Test] + public function testRetrieve(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->retrieve('campaignId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(CampaignGetResponse::class, $result); + } + + #[Test] + public function testUpdate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->update('campaignId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(CampaignUpdateResponse::class, $result); + } + + #[Test] + public function testList(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $page = $this->client->number10dlc->campaigns->list(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Cursor::class, $page); + + if ($item = $page->getItems()[0] ?? null) { + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(TenDlcCampaign::class, $item); + } + } + + #[Test] + public function testDelete(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->delete('campaignId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertNull($result); + } + + #[Test] + public function testSubmit(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->submit('campaignId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(CampaignSubmitResponse::class, $result); + } + + #[Test] + public function testSyncStatus(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->number10dlc->campaigns->syncStatus('campaignId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(CampaignSyncStatusResponse::class, $result); + } +} diff --git a/tests/Services/PlanTest.php b/tests/Services/PlanTest.php new file mode 100644 index 0000000..f3b7333 --- /dev/null +++ b/tests/Services/PlanTest.php @@ -0,0 +1,43 @@ +client = $client; + } + + #[Test] + public function testRetrieve(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->plan->retrieve(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(PlanGetResponse::class, $result); + } +} diff --git a/tests/Services/Senders/WhatsappSyncTest.php b/tests/Services/Senders/WhatsappSyncTest.php new file mode 100644 index 0000000..39ffbd1 --- /dev/null +++ b/tests/Services/Senders/WhatsappSyncTest.php @@ -0,0 +1,81 @@ +client = $client; + } + + #[Test] + public function testRetrieve(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->senders->whatsappSync->retrieve('senderId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(WhatsappSyncGetResponse::class, $result); + } + + #[Test] + public function testStartContactsSync(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->senders->whatsappSync->startContactsSync( + 'senderId' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf( + WhatsappSyncStartContactsSyncResponse::class, + $result + ); + } + + #[Test] + public function testStartHistorySync(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->senders->whatsappSync->startHistorySync( + 'senderId' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf( + WhatsappSyncStartHistorySyncResponse::class, + $result + ); + } +} diff --git a/tests/Services/SubAccounts/APIKeysTest.php b/tests/Services/SubAccounts/APIKeysTest.php new file mode 100644 index 0000000..df0a7ba --- /dev/null +++ b/tests/Services/SubAccounts/APIKeysTest.php @@ -0,0 +1,104 @@ +client = $client; + } + + #[Test] + public function testCreate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->apiKeys->create( + 'id', + name: 'Production Key' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(APIKeyNewResponse::class, $result); + } + + #[Test] + public function testCreateWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->apiKeys->create( + 'id', + name: 'Production Key', + environment: 'live', + permissions: ['string'] + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(APIKeyNewResponse::class, $result); + } + + #[Test] + public function testList(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->apiKeys->list('id'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(APIKeyListResponse::class, $result); + } + + #[Test] + public function testRevoke(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->apiKeys->revoke('keyId', id: 'id'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertNull($result); + } + + #[Test] + public function testRevokeWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->apiKeys->revoke('keyId', id: 'id'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertNull($result); + } +} diff --git a/tests/Services/SubAccountsTest.php b/tests/Services/SubAccountsTest.php new file mode 100644 index 0000000..104e3f6 --- /dev/null +++ b/tests/Services/SubAccountsTest.php @@ -0,0 +1,137 @@ +client = $client; + } + + #[Test] + public function testCreate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->create(name: 'Client ABC'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(SubAccountNewResponse::class, $result); + } + + #[Test] + public function testCreateWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->create( + name: 'Client ABC', + creditLimit: 0, + externalID: 'externalId', + metadata: ['foo' => 'bar'], + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(SubAccountNewResponse::class, $result); + } + + #[Test] + public function testRetrieve(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->retrieve('id'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(SubAccountGetResponse::class, $result); + } + + #[Test] + public function testUpdate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->update('id'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(SubAccountUpdateResponse::class, $result); + } + + #[Test] + public function testList(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $page = $this->client->subAccounts->list(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Cursor::class, $page); + + if ($item = $page->getItems()[0] ?? null) { + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(SubAccount::class, $item); + } + } + + #[Test] + public function testDeactivate(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->deactivate('id'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(SubAccountDeactivateResponse::class, $result); + } + + #[Test] + public function testGetBalance(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->subAccounts->getBalance('id'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(SubAccountGetBalanceResponse::class, $result); + } +} diff --git a/tests/Services/TemplatesTest.php b/tests/Services/TemplatesTest.php index cb2e5e4..50bb0a7 100644 --- a/tests/Services/TemplatesTest.php +++ b/tests/Services/TemplatesTest.php @@ -63,6 +63,7 @@ public function testCreateWithOptionalParams(): void [ 'text' => 'text', 'type' => 'quick_reply', + 'example' => 'ORD-12345', 'otpType' => 'COPY_CODE', 'packageName' => 'packageName', 'phoneNumber' => 'phoneNumber', diff --git a/tests/Services/URLsTest.php b/tests/Services/URLsTest.php new file mode 100644 index 0000000..98b9606 --- /dev/null +++ b/tests/Services/URLsTest.php @@ -0,0 +1,94 @@ +client = $client; + } + + #[Test] + public function testListVerified(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $page = $this->client->urls->listVerified(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(Cursor::class, $page); + + if ($item = $page->getItems()[0] ?? null) { + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(VerifiedURL::class, $item); + } + } + + #[Test] + public function testRetrieveDetails(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->urls->retrieveDetails('urlId'); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(URLGetDetailsResponse::class, $result); + } + + #[Test] + public function testSubmitForVerification(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->urls->submitForVerification( + url: 'https://example.com/page' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(URLSubmitForVerificationResponse::class, $result); + } + + #[Test] + public function testSubmitForVerificationWithOptionalParams(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->urls->submitForVerification( + url: 'https://example.com/page' + ); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(URLSubmitForVerificationResponse::class, $result); + } +} diff --git a/tests/Services/UsageTest.php b/tests/Services/UsageTest.php new file mode 100644 index 0000000..9bc289f --- /dev/null +++ b/tests/Services/UsageTest.php @@ -0,0 +1,43 @@ +client = $client; + } + + #[Test] + public function testRetrieve(): void + { + if (UnsupportedMockTests::$skip) { + $this->markTestSkipped('Mock server tests are disabled'); + } + + $result = $this->client->usage->retrieve(); + + // @phpstan-ignore-next-line method.alreadyNarrowedType + $this->assertInstanceOf(UsageGetResponse::class, $result); + } +}