diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3d2ac0b..10f3091 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0" + ".": "0.2.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index ddb1fe8..98dd734 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 183 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5d75bbdcac3a0b0a8b798c0b4ef0dacfbc64ad4a20605489dc46dda7c8d1d8d8.yml -openapi_spec_hash: bb242cfd8cc43354412164c2dceae89c -config_hash: 261e1b852ca7f8364e4a283a3edd350a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e6e18b26cd123fcd2dcaf3554352c9aa1aa890768d7928fa117382f0b4ca89aa.yml +openapi_spec_hash: 6bc9a1491dec8d8157477abeb4aa8f3c +config_hash: f6a05e2edf2cd1af3f762b8ac759b9a7 diff --git a/CHANGELOG.md b/CHANGELOG.md index be543db..8457837 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 0.2.0 (2026-09-20) + +Full Changelog: [v0.1.0...v0.2.0](https://github.com/zavudev/sdk-csharp/compare/v0.1.0...v0.2.0) + +### Features + +* **api:** api update ([5fb8697](https://github.com/zavudev/sdk-csharp/commit/5fb8697f9affa1b561efce67b6be0f56d13677fb)) +* **api:** api update ([da0a2c0](https://github.com/zavudev/sdk-csharp/commit/da0a2c07b9299dcaa0c1ae7c078e5642ca679531)) +* **api:** api update ([d73876e](https://github.com/zavudev/sdk-csharp/commit/d73876e64f5cf89766a28baddb7979576157c928)) +* **api:** api update ([2c961fc](https://github.com/zavudev/sdk-csharp/commit/2c961fc5c02d0de95ec3c4dbed98333f097818f8)) +* **api:** api update ([3e405cc](https://github.com/zavudev/sdk-csharp/commit/3e405cc8b4b01a0e4ac758f7f2d45b9a81d4f80c)) +* **api:** api update ([0d869f1](https://github.com/zavudev/sdk-csharp/commit/0d869f1ea9fd1fc2049483bfd76d6ce8803df627)) +* **api:** api update ([415d31d](https://github.com/zavudev/sdk-csharp/commit/415d31dbddf1211b0ce0fe66310f5c1e453c2de4)) +* **api:** api update ([5456b2c](https://github.com/zavudev/sdk-csharp/commit/5456b2c8998185dff2f91bf699cc506f32c02864)) +* **api:** api update ([01367bb](https://github.com/zavudev/sdk-csharp/commit/01367bbf6af82761f861601a214216e503fdd481)) +* **api:** api update ([710ab9f](https://github.com/zavudev/sdk-csharp/commit/710ab9f1f328f1aeef304b058650140f0f0285db)) +* **api:** api update ([c1e8938](https://github.com/zavudev/sdk-csharp/commit/c1e89384e72a0a05e2a94819a2e8656872a804e2)) + ## 0.1.0 (2026-09-08) Full Changelog: [v0.0.2...v0.1.0](https://github.com/zavudev/sdk-csharp/compare/v0.0.2...v0.1.0) diff --git a/src/Zavudev.Tests/Models/Addresses/AddressCreateParamsTest.cs b/src/Zavudev.Tests/Models/Addresses/AddressCreateParamsTest.cs index a67d4b7..50d3c19 100644 --- a/src/Zavudev.Tests/Models/Addresses/AddressCreateParamsTest.cs +++ b/src/Zavudev.Tests/Models/Addresses/AddressCreateParamsTest.cs @@ -11,35 +11,35 @@ public void FieldRoundtrip_Works() var parameters = new AddressCreateParams { CountryCode = "DE", + FirstName = "John", + LastName = "Doe", Locality = "Berlin", PostalCode = "10115", StreetAddress = "123 Main St", AdministrativeArea = "administrativeArea", BusinessName = "businessName", ExtendedAddress = "extendedAddress", - FirstName = "John", - LastName = "Doe", }; string expectedCountryCode = "DE"; + string expectedFirstName = "John"; + string expectedLastName = "Doe"; string expectedLocality = "Berlin"; string expectedPostalCode = "10115"; string expectedStreetAddress = "123 Main St"; string expectedAdministrativeArea = "administrativeArea"; string expectedBusinessName = "businessName"; string expectedExtendedAddress = "extendedAddress"; - string expectedFirstName = "John"; - string expectedLastName = "Doe"; Assert.Equal(expectedCountryCode, parameters.CountryCode); + Assert.Equal(expectedFirstName, parameters.FirstName); + Assert.Equal(expectedLastName, parameters.LastName); Assert.Equal(expectedLocality, parameters.Locality); Assert.Equal(expectedPostalCode, parameters.PostalCode); Assert.Equal(expectedStreetAddress, parameters.StreetAddress); Assert.Equal(expectedAdministrativeArea, parameters.AdministrativeArea); Assert.Equal(expectedBusinessName, parameters.BusinessName); Assert.Equal(expectedExtendedAddress, parameters.ExtendedAddress); - Assert.Equal(expectedFirstName, parameters.FirstName); - Assert.Equal(expectedLastName, parameters.LastName); } [Fact] @@ -48,6 +48,8 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() var parameters = new AddressCreateParams { CountryCode = "DE", + FirstName = "John", + LastName = "Doe", Locality = "Berlin", PostalCode = "10115", StreetAddress = "123 Main St", @@ -59,10 +61,6 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() Assert.False(parameters.RawBodyData.ContainsKey("businessName")); Assert.Null(parameters.ExtendedAddress); Assert.False(parameters.RawBodyData.ContainsKey("extendedAddress")); - Assert.Null(parameters.FirstName); - Assert.False(parameters.RawBodyData.ContainsKey("firstName")); - Assert.Null(parameters.LastName); - Assert.False(parameters.RawBodyData.ContainsKey("lastName")); } [Fact] @@ -71,6 +69,8 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() var parameters = new AddressCreateParams { CountryCode = "DE", + FirstName = "John", + LastName = "Doe", Locality = "Berlin", PostalCode = "10115", StreetAddress = "123 Main St", @@ -79,8 +79,6 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() AdministrativeArea = null, BusinessName = null, ExtendedAddress = null, - FirstName = null, - LastName = null, }; Assert.Null(parameters.AdministrativeArea); @@ -89,10 +87,6 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() Assert.False(parameters.RawBodyData.ContainsKey("businessName")); Assert.Null(parameters.ExtendedAddress); Assert.False(parameters.RawBodyData.ContainsKey("extendedAddress")); - Assert.Null(parameters.FirstName); - Assert.False(parameters.RawBodyData.ContainsKey("firstName")); - Assert.Null(parameters.LastName); - Assert.False(parameters.RawBodyData.ContainsKey("lastName")); } [Fact] @@ -101,6 +95,8 @@ public void Url_Works() AddressCreateParams parameters = new() { CountryCode = "DE", + FirstName = "John", + LastName = "Doe", Locality = "Berlin", PostalCode = "10115", StreetAddress = "123 Main St", @@ -117,14 +113,14 @@ public void CopyConstructor_Works() var parameters = new AddressCreateParams { CountryCode = "DE", + FirstName = "John", + LastName = "Doe", Locality = "Berlin", PostalCode = "10115", StreetAddress = "123 Main St", AdministrativeArea = "administrativeArea", BusinessName = "businessName", ExtendedAddress = "extendedAddress", - FirstName = "John", - LastName = "Doe", }; AddressCreateParams copied = new(parameters); diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastCancelResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastCancelResponseTest.cs index 869a838..c70fd91 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastCancelResponseTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastCancelResponseTest.cs @@ -60,6 +60,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -107,6 +108,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -348,6 +353,7 @@ public void Validation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -409,6 +415,7 @@ public void CopyConstructor_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastContactStatusTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastContactStatusTest.cs index 2be479f..4fa5d86 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastContactStatusTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastContactStatusTest.cs @@ -11,6 +11,7 @@ public class BroadcastContactStatusTest : TestBase [InlineData(BroadcastContactStatus.Pending)] [InlineData(BroadcastContactStatus.Queued)] [InlineData(BroadcastContactStatus.Sending)] + [InlineData(BroadcastContactStatus.Sent)] [InlineData(BroadcastContactStatus.Delivered)] [InlineData(BroadcastContactStatus.Failed)] [InlineData(BroadcastContactStatus.Skipped)] @@ -37,6 +38,7 @@ public void InvalidEnumValidationThrows_Works() [InlineData(BroadcastContactStatus.Pending)] [InlineData(BroadcastContactStatus.Queued)] [InlineData(BroadcastContactStatus.Sending)] + [InlineData(BroadcastContactStatus.Sent)] [InlineData(BroadcastContactStatus.Delivered)] [InlineData(BroadcastContactStatus.Failed)] [InlineData(BroadcastContactStatus.Skipped)] diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastCreateResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastCreateResponseTest.cs index dece232..543a267 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastCreateResponseTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastCreateResponseTest.cs @@ -60,6 +60,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -107,6 +108,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -348,6 +353,7 @@ public void Validation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -409,6 +415,7 @@ public void CopyConstructor_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastEscalateReviewResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastEscalateReviewResponseTest.cs index 43818e2..ad2c2c8 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastEscalateReviewResponseTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastEscalateReviewResponseTest.cs @@ -60,6 +60,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -107,6 +108,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -348,6 +353,7 @@ public void Validation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -409,6 +415,7 @@ public void CopyConstructor_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastListPageResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastListPageResponseTest.cs index 32d238b..6bb4b43 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastListPageResponseTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastListPageResponseTest.cs @@ -65,6 +65,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -122,6 +123,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -194,6 +196,7 @@ public void SerializationRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -268,6 +271,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -332,6 +336,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -404,6 +409,7 @@ public void Validation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -472,6 +478,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -540,6 +547,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -607,6 +615,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -677,6 +686,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -746,6 +756,7 @@ public void CopyConstructor_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastProgressTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastProgressTest.cs index f235fd8..5d0c669 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastProgressTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastProgressTest.cs @@ -25,6 +25,7 @@ public void FieldRoundtrip_Works() EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), EstimatedCost = 0, ReservedAmount = 0, + Sent = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), }; @@ -43,6 +44,7 @@ public void FieldRoundtrip_Works() ); double expectedEstimatedCost = 0; double expectedReservedAmount = 0; + long expectedSent = 0; DateTimeOffset expectedStartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); Assert.Equal(expectedBroadcastID, model.BroadcastID); @@ -58,6 +60,7 @@ public void FieldRoundtrip_Works() Assert.Equal(expectedEstimatedCompletionAt, model.EstimatedCompletionAt); Assert.Equal(expectedEstimatedCost, model.EstimatedCost); Assert.Equal(expectedReservedAmount, model.ReservedAmount); + Assert.Equal(expectedSent, model.Sent); Assert.Equal(expectedStartedAt, model.StartedAt); } @@ -79,6 +82,7 @@ public void SerializationRoundtrip_Works() EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), EstimatedCost = 0, ReservedAmount = 0, + Sent = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), }; @@ -109,6 +113,7 @@ public void FieldRoundtripThroughSerialization_Works() EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), EstimatedCost = 0, ReservedAmount = 0, + Sent = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), }; @@ -134,6 +139,7 @@ public void FieldRoundtripThroughSerialization_Works() ); double expectedEstimatedCost = 0; double expectedReservedAmount = 0; + long expectedSent = 0; DateTimeOffset expectedStartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); Assert.Equal(expectedBroadcastID, deserialized.BroadcastID); @@ -149,6 +155,7 @@ public void FieldRoundtripThroughSerialization_Works() Assert.Equal(expectedEstimatedCompletionAt, deserialized.EstimatedCompletionAt); Assert.Equal(expectedEstimatedCost, deserialized.EstimatedCost); Assert.Equal(expectedReservedAmount, deserialized.ReservedAmount); + Assert.Equal(expectedSent, deserialized.Sent); Assert.Equal(expectedStartedAt, deserialized.StartedAt); } @@ -170,6 +177,7 @@ public void Validation_Works() EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), EstimatedCost = 0, ReservedAmount = 0, + Sent = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), }; @@ -197,6 +205,8 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() Assert.Null(model.EstimatedCompletionAt); Assert.False(model.RawData.ContainsKey("estimatedCompletionAt")); + Assert.Null(model.Sent); + Assert.False(model.RawData.ContainsKey("sent")); Assert.Null(model.StartedAt); Assert.False(model.RawData.ContainsKey("startedAt")); } @@ -243,11 +253,14 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() // Null should be interpreted as omitted for these properties EstimatedCompletionAt = null, + Sent = null, StartedAt = null, }; Assert.Null(model.EstimatedCompletionAt); Assert.False(model.RawData.ContainsKey("estimatedCompletionAt")); + Assert.Null(model.Sent); + Assert.False(model.RawData.ContainsKey("sent")); Assert.Null(model.StartedAt); Assert.False(model.RawData.ContainsKey("startedAt")); } @@ -272,6 +285,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() // Null should be interpreted as omitted for these properties EstimatedCompletionAt = null, + Sent = null, StartedAt = null, }; @@ -293,6 +307,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() Status = BroadcastStatus.Draft, Total = 0, EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Sent = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), }; @@ -319,6 +334,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works() Status = BroadcastStatus.Draft, Total = 0, EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Sent = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), }; @@ -340,6 +356,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() Status = BroadcastStatus.Draft, Total = 0, EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Sent = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ActualCost = null, @@ -370,6 +387,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() Status = BroadcastStatus.Draft, Total = 0, EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), + Sent = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ActualCost = null, @@ -398,6 +416,7 @@ public void CopyConstructor_Works() EstimatedCompletionAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), EstimatedCost = 0, ReservedAmount = 0, + Sent = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), }; diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRescheduleResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRescheduleResponseTest.cs index 63e1af8..d0cb5f9 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRescheduleResponseTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRescheduleResponseTest.cs @@ -60,6 +60,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -107,6 +108,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -348,6 +353,7 @@ public void Validation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -409,6 +415,7 @@ public void CopyConstructor_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetrieveResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetrieveResponseTest.cs index 63b229b..ac3cb45 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetrieveResponseTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetrieveResponseTest.cs @@ -60,6 +60,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -107,6 +108,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -348,6 +353,7 @@ public void Validation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -409,6 +415,7 @@ public void CopyConstructor_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetryReviewResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetryReviewResponseTest.cs index f66ae30..5626558 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetryReviewResponseTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastRetryReviewResponseTest.cs @@ -60,6 +60,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -107,6 +108,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -348,6 +353,7 @@ public void Validation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -409,6 +415,7 @@ public void CopyConstructor_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastSendResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastSendResponseTest.cs index 42b8408..afb19cc 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastSendResponseTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastSendResponseTest.cs @@ -60,6 +60,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -107,6 +108,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -348,6 +353,7 @@ public void Validation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -409,6 +415,7 @@ public void CopyConstructor_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastTest.cs index 64ad99a..2fe364d 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastTest.cs @@ -52,6 +52,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -96,6 +97,7 @@ public void FieldRoundtrip_Works() DateTimeOffset expectedScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); string expectedSenderID = "senderId"; long expectedSendingCount = 0; + long expectedSentCount = 0; DateTimeOffset expectedStartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); string expectedText = "text"; DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); @@ -129,6 +131,7 @@ public void FieldRoundtrip_Works() Assert.Equal(expectedScheduledAt, model.ScheduledAt); Assert.Equal(expectedSenderID, model.SenderID); Assert.Equal(expectedSendingCount, model.SendingCount); + Assert.Equal(expectedSentCount, model.SentCount); Assert.Equal(expectedStartedAt, model.StartedAt); Assert.Equal(expectedText, model.Text); Assert.Equal(expectedUpdatedAt, model.UpdatedAt); @@ -178,6 +181,7 @@ public void SerializationRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -233,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -284,6 +289,7 @@ public void FieldRoundtripThroughSerialization_Works() DateTimeOffset expectedScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); string expectedSenderID = "senderId"; long expectedSendingCount = 0; + long expectedSentCount = 0; DateTimeOffset expectedStartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); string expectedText = "text"; DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); @@ -317,6 +323,7 @@ public void FieldRoundtripThroughSerialization_Works() Assert.Equal(expectedScheduledAt, deserialized.ScheduledAt); Assert.Equal(expectedSenderID, deserialized.SenderID); Assert.Equal(expectedSendingCount, deserialized.SendingCount); + Assert.Equal(expectedSentCount, deserialized.SentCount); Assert.Equal(expectedStartedAt, deserialized.StartedAt); Assert.Equal(expectedText, deserialized.Text); Assert.Equal(expectedUpdatedAt, deserialized.UpdatedAt); @@ -366,6 +373,7 @@ public void Validation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -420,6 +428,8 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() Assert.False(model.RawData.ContainsKey("senderId")); Assert.Null(model.SendingCount); Assert.False(model.RawData.ContainsKey("sendingCount")); + Assert.Null(model.SentCount); + Assert.False(model.RawData.ContainsKey("sentCount")); Assert.Null(model.StartedAt); Assert.False(model.RawData.ContainsKey("startedAt")); Assert.Null(model.Text); @@ -493,6 +503,7 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() ScheduledAt = null, SenderID = null, SendingCount = null, + SentCount = null, StartedAt = null, Text = null, UpdatedAt = null, @@ -518,6 +529,8 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() Assert.False(model.RawData.ContainsKey("senderId")); Assert.Null(model.SendingCount); Assert.False(model.RawData.ContainsKey("sendingCount")); + Assert.Null(model.SentCount); + Assert.False(model.RawData.ContainsKey("sentCount")); Assert.Null(model.StartedAt); Assert.False(model.RawData.ContainsKey("startedAt")); Assert.Null(model.Text); @@ -562,6 +575,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() ScheduledAt = null, SenderID = null, SendingCount = null, + SentCount = null, StartedAt = null, Text = null, UpdatedAt = null, @@ -602,6 +616,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -651,6 +666,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -691,6 +707,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -746,6 +763,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -804,6 +822,7 @@ public void CopyConstructor_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Broadcasts/BroadcastUpdateResponseTest.cs b/src/Zavudev.Tests/Models/Broadcasts/BroadcastUpdateResponseTest.cs index 6fb4c09..f635c5c 100644 --- a/src/Zavudev.Tests/Models/Broadcasts/BroadcastUpdateResponseTest.cs +++ b/src/Zavudev.Tests/Models/Broadcasts/BroadcastUpdateResponseTest.cs @@ -60,6 +60,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -107,6 +108,7 @@ public void FieldRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -167,6 +169,7 @@ public void SerializationRoundtrip_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -234,6 +237,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -288,6 +292,7 @@ public void FieldRoundtripThroughSerialization_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -348,6 +353,7 @@ public void Validation_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -409,6 +415,7 @@ public void CopyConstructor_Works() ScheduledAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), SenderID = "senderId", SendingCount = 0, + SentCount = 0, StartedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), Text = "text", UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandCreateResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandCreateResponseTest.cs index 43c2c6d..c1f5bb6 100644 --- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandCreateResponseTest.cs +++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandCreateResponseTest.cs @@ -34,6 +34,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -65,6 +66,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -103,6 +105,7 @@ public void SerializationRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -148,6 +151,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -186,6 +190,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -224,6 +229,7 @@ public void Validation_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -263,6 +269,7 @@ public void CopyConstructor_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandListPageResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandListPageResponseTest.cs index a156a87..7aeee6a 100644 --- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandListPageResponseTest.cs +++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandListPageResponseTest.cs @@ -37,6 +37,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -72,6 +73,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -119,6 +121,7 @@ public void SerializationRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -168,6 +171,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -210,6 +214,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -257,6 +262,7 @@ public void Validation_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -300,6 +306,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -343,6 +350,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -385,6 +393,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -430,6 +439,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -474,6 +484,7 @@ public void CopyConstructor_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandRetrieveResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandRetrieveResponseTest.cs index d9f55f4..77a7e8e 100644 --- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandRetrieveResponseTest.cs +++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandRetrieveResponseTest.cs @@ -34,6 +34,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -65,6 +66,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -103,6 +105,7 @@ public void SerializationRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -148,6 +151,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -186,6 +190,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -224,6 +229,7 @@ public void Validation_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -263,6 +269,7 @@ public void CopyConstructor_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSubmitResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSubmitResponseTest.cs index 8be86d2..9bc2a8c 100644 --- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSubmitResponseTest.cs +++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSubmitResponseTest.cs @@ -34,6 +34,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -65,6 +66,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -103,6 +105,7 @@ public void SerializationRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -148,6 +151,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -186,6 +190,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -224,6 +229,7 @@ public void Validation_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -263,6 +269,7 @@ public void CopyConstructor_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSyncStatusResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSyncStatusResponseTest.cs index 0352ea2..85f6922 100644 --- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSyncStatusResponseTest.cs +++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandSyncStatusResponseTest.cs @@ -34,6 +34,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -65,6 +66,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -103,6 +105,7 @@ public void SerializationRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -148,6 +151,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -186,6 +190,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -224,6 +229,7 @@ public void Validation_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -263,6 +269,7 @@ public void CopyConstructor_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandUpdateResponseTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandUpdateResponseTest.cs index 5ecd2d8..c663036 100644 --- a/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandUpdateResponseTest.cs +++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/BrandUpdateResponseTest.cs @@ -34,6 +34,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -65,6 +66,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -103,6 +105,7 @@ public void SerializationRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -148,6 +151,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -186,6 +190,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -224,6 +229,7 @@ public void Validation_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -263,6 +269,7 @@ public void CopyConstructor_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", diff --git a/src/Zavudev.Tests/Models/Number10dlc/Brands/TenDlcBrandTest.cs b/src/Zavudev.Tests/Models/Number10dlc/Brands/TenDlcBrandTest.cs index 85a89e7..819ec60 100644 --- a/src/Zavudev.Tests/Models/Number10dlc/Brands/TenDlcBrandTest.cs +++ b/src/Zavudev.Tests/Models/Number10dlc/Brands/TenDlcBrandTest.cs @@ -33,6 +33,7 @@ public void FieldRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -62,6 +63,7 @@ public void FieldRoundtrip_Works() string expectedEin = "12-3456789"; string expectedFailureReason = "failureReason"; string expectedFirstName = "firstName"; + string expectedIdentityStatus = "VERIFIED"; string expectedLastName = "lastName"; string expectedStockExchange = "stockExchange"; string expectedStockSymbol = "stockSymbol"; @@ -89,6 +91,7 @@ public void FieldRoundtrip_Works() Assert.Equal(expectedEin, model.Ein); Assert.Equal(expectedFailureReason, model.FailureReason); Assert.Equal(expectedFirstName, model.FirstName); + Assert.Equal(expectedIdentityStatus, model.IdentityStatus); Assert.Equal(expectedLastName, model.LastName); Assert.Equal(expectedStockExchange, model.StockExchange); Assert.Equal(expectedStockSymbol, model.StockSymbol); @@ -122,6 +125,7 @@ public void SerializationRoundtrip_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -164,6 +168,7 @@ public void FieldRoundtripThroughSerialization_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -200,6 +205,7 @@ public void FieldRoundtripThroughSerialization_Works() string expectedEin = "12-3456789"; string expectedFailureReason = "failureReason"; string expectedFirstName = "firstName"; + string expectedIdentityStatus = "VERIFIED"; string expectedLastName = "lastName"; string expectedStockExchange = "stockExchange"; string expectedStockSymbol = "stockSymbol"; @@ -227,6 +233,7 @@ public void FieldRoundtripThroughSerialization_Works() Assert.Equal(expectedEin, deserialized.Ein); Assert.Equal(expectedFailureReason, deserialized.FailureReason); Assert.Equal(expectedFirstName, deserialized.FirstName); + Assert.Equal(expectedIdentityStatus, deserialized.IdentityStatus); Assert.Equal(expectedLastName, deserialized.LastName); Assert.Equal(expectedStockExchange, deserialized.StockExchange); Assert.Equal(expectedStockSymbol, deserialized.StockSymbol); @@ -260,6 +267,7 @@ public void Validation_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -304,6 +312,8 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() Assert.False(model.RawData.ContainsKey("failureReason")); Assert.Null(model.FirstName); Assert.False(model.RawData.ContainsKey("firstName")); + Assert.Null(model.IdentityStatus); + Assert.False(model.RawData.ContainsKey("identityStatus")); Assert.Null(model.LastName); Assert.False(model.RawData.ContainsKey("lastName")); Assert.Null(model.StockExchange); @@ -368,6 +378,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() Ein = null, FailureReason = null, FirstName = null, + IdentityStatus = null, LastName = null, StockExchange = null, StockSymbol = null, @@ -388,6 +399,8 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() Assert.True(model.RawData.ContainsKey("failureReason")); Assert.Null(model.FirstName); Assert.True(model.RawData.ContainsKey("firstName")); + Assert.Null(model.IdentityStatus); + Assert.True(model.RawData.ContainsKey("identityStatus")); Assert.Null(model.LastName); Assert.True(model.RawData.ContainsKey("lastName")); Assert.Null(model.StockExchange); @@ -428,6 +441,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() Ein = null, FailureReason = null, FirstName = null, + IdentityStatus = null, LastName = null, StockExchange = null, StockSymbol = null, @@ -464,6 +478,7 @@ public void CopyConstructor_Works() Ein = "12-3456789", FailureReason = "failureReason", FirstName = "firstName", + IdentityStatus = "VERIFIED", LastName = "lastName", StockExchange = "stockExchange", StockSymbol = "stockSymbol", @@ -548,7 +563,9 @@ public class StatusTest : TestBase [InlineData(Status.Draft)] [InlineData(Status.Pending)] [InlineData(Status.Verified)] + [InlineData(Status.Unverified)] [InlineData(Status.Rejected)] + [InlineData(Status.Failed)] public void Validation_Works(Status rawValue) { // force implicit conversion because Theory can't do that for us @@ -572,7 +589,9 @@ public void InvalidEnumValidationThrows_Works() [InlineData(Status.Draft)] [InlineData(Status.Pending)] [InlineData(Status.Verified)] + [InlineData(Status.Unverified)] [InlineData(Status.Rejected)] + [InlineData(Status.Failed)] public void SerializationRoundtrip_Works(Status rawValue) { // force implicit conversion because Theory can't do that for us diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/OwnedPhoneNumberTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/OwnedPhoneNumberTest.cs index 1ae8af4..161645a 100644 --- a/src/Zavudev.Tests/Models/PhoneNumbers/OwnedPhoneNumberTest.cs +++ b/src/Zavudev.Tests/Models/PhoneNumbers/OwnedPhoneNumberTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text.Json; using Zavudev.Core; +using Zavudev.Exceptions; using Zavudev.Models.PhoneNumbers; namespace Zavudev.Tests.Models.PhoneNumbers; @@ -24,6 +25,7 @@ public void FieldRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -42,6 +44,7 @@ public void FieldRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }; + ApiEnum expectedRegulatoryStatus = RegulatoryStatus.Approved; ApiEnum expectedStatus = PhoneNumberStatus.Active; string expectedName = "name"; DateTimeOffset expectedNextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); @@ -57,6 +60,7 @@ public void FieldRoundtrip_Works() Assert.Equal(expectedCreatedAt, model.CreatedAt); Assert.Equal(expectedPhoneNumber, model.PhoneNumber); Assert.Equal(expectedPricing, model.Pricing); + Assert.Equal(expectedRegulatoryStatus, model.RegulatoryStatus); Assert.Equal(expectedStatus, model.Status); Assert.Equal(expectedName, model.Name); Assert.Equal(expectedNextRenewalDate, model.NextRenewalDate); @@ -80,6 +84,7 @@ public void SerializationRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -112,6 +117,7 @@ public void FieldRoundtripThroughSerialization_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -137,6 +143,7 @@ public void FieldRoundtripThroughSerialization_Works() MonthlyPrice = 0, UpfrontCost = 0, }; + ApiEnum expectedRegulatoryStatus = RegulatoryStatus.Approved; ApiEnum expectedStatus = PhoneNumberStatus.Active; string expectedName = "name"; DateTimeOffset expectedNextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); @@ -152,6 +159,7 @@ public void FieldRoundtripThroughSerialization_Works() Assert.Equal(expectedCreatedAt, deserialized.CreatedAt); Assert.Equal(expectedPhoneNumber, deserialized.PhoneNumber); Assert.Equal(expectedPricing, deserialized.Pricing); + Assert.Equal(expectedRegulatoryStatus, deserialized.RegulatoryStatus); Assert.Equal(expectedStatus, deserialized.Status); Assert.Equal(expectedName, deserialized.Name); Assert.Equal(expectedNextRenewalDate, deserialized.NextRenewalDate); @@ -175,6 +183,7 @@ public void Validation_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -201,6 +210,7 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, }; @@ -230,6 +240,7 @@ public void OptionalNonNullablePropertiesUnsetValidation_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, }; @@ -252,6 +263,7 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, // Null should be interpreted as omitted for these properties @@ -287,6 +299,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, // Null should be interpreted as omitted for these properties @@ -315,6 +328,7 @@ public void CopyConstructor_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -327,3 +341,63 @@ public void CopyConstructor_Works() Assert.Equal(model, copied); } } + +public class RegulatoryStatusTest : TestBase +{ + [Theory] + [InlineData(RegulatoryStatus.Approved)] + [InlineData(RegulatoryStatus.PendingReview)] + [InlineData(RegulatoryStatus.Rejected)] + public void Validation_Works(RegulatoryStatus rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(RegulatoryStatus.Approved)] + [InlineData(RegulatoryStatus.PendingReview)] + [InlineData(RegulatoryStatus.Rejected)] + public void SerializationRoundtrip_Works(RegulatoryStatus rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberListPageResponseTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberListPageResponseTest.cs index 553206c..95c363c 100644 --- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberListPageResponseTest.cs +++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberListPageResponseTest.cs @@ -28,6 +28,7 @@ public void FieldRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -53,6 +54,7 @@ public void FieldRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -90,6 +92,7 @@ public void SerializationRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -129,6 +132,7 @@ public void FieldRoundtripThroughSerialization_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -161,6 +165,7 @@ public void FieldRoundtripThroughSerialization_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -198,6 +203,7 @@ public void Validation_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -231,6 +237,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -264,6 +271,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -296,6 +304,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -331,6 +340,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -365,6 +375,7 @@ public void CopyConstructor_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseParamsTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseParamsTest.cs index 467b80f..297d65d 100644 --- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseParamsTest.cs +++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseParamsTest.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Text.Json; +using Zavudev.Core; using Zavudev.Models.PhoneNumbers; namespace Zavudev.Tests.Models.PhoneNumbers; @@ -12,13 +15,38 @@ public void FieldRoundtrip_Works() { PhoneNumber = "+15551234567", Name = "Primary Line", + RegulatoryRequirements = + [ + new() + { + FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc", + RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10", + }, + ], + Type = PhoneNumberType.Local, }; string expectedPhoneNumber = "+15551234567"; string expectedName = "Primary Line"; + List expectedRegulatoryRequirements = + [ + new() + { + FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc", + RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10", + }, + ]; + ApiEnum expectedType = PhoneNumberType.Local; Assert.Equal(expectedPhoneNumber, parameters.PhoneNumber); Assert.Equal(expectedName, parameters.Name); + Assert.NotNull(parameters.RegulatoryRequirements); + Assert.Equal(expectedRegulatoryRequirements.Count, parameters.RegulatoryRequirements.Count); + for (int i = 0; i < expectedRegulatoryRequirements.Count; i++) + { + Assert.Equal(expectedRegulatoryRequirements[i], parameters.RegulatoryRequirements[i]); + } + Assert.Equal(expectedType, parameters.Type); } [Fact] @@ -28,6 +56,10 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() Assert.Null(parameters.Name); Assert.False(parameters.RawBodyData.ContainsKey("name")); + Assert.Null(parameters.RegulatoryRequirements); + Assert.False(parameters.RawBodyData.ContainsKey("regulatoryRequirements")); + Assert.Null(parameters.Type); + Assert.False(parameters.RawBodyData.ContainsKey("type")); } [Fact] @@ -39,10 +71,16 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() // Null should be interpreted as omitted for these properties Name = null, + RegulatoryRequirements = null, + Type = null, }; Assert.Null(parameters.Name); Assert.False(parameters.RawBodyData.ContainsKey("name")); + Assert.Null(parameters.RegulatoryRequirements); + Assert.False(parameters.RawBodyData.ContainsKey("regulatoryRequirements")); + Assert.Null(parameters.Type); + Assert.False(parameters.RawBodyData.ContainsKey("type")); } [Fact] @@ -62,6 +100,15 @@ public void CopyConstructor_Works() { PhoneNumber = "+15551234567", Name = "Primary Line", + RegulatoryRequirements = + [ + new() + { + FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc", + RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10", + }, + ], + Type = PhoneNumberType.Local, }; PhoneNumberPurchaseParams copied = new(parameters); @@ -69,3 +116,89 @@ public void CopyConstructor_Works() Assert.Equal(parameters, copied); } } + +public class RegulatoryRequirementTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new RegulatoryRequirement + { + FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc", + RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10", + }; + + string expectedFieldValue = "jd7x2k3m4n5p6q7r8s9t0abc"; + string expectedRequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10"; + + Assert.Equal(expectedFieldValue, model.FieldValue); + Assert.Equal(expectedRequirementType, model.RequirementType); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new RegulatoryRequirement + { + FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc", + RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new RegulatoryRequirement + { + FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc", + RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedFieldValue = "jd7x2k3m4n5p6q7r8s9t0abc"; + string expectedRequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10"; + + Assert.Equal(expectedFieldValue, deserialized.FieldValue); + Assert.Equal(expectedRequirementType, deserialized.RequirementType); + } + + [Fact] + public void Validation_Works() + { + var model = new RegulatoryRequirement + { + FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc", + RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10", + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new RegulatoryRequirement + { + FieldValue = "jd7x2k3m4n5p6q7r8s9t0abc", + RequirementType = "8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10", + }; + + RegulatoryRequirement copied = new(model); + + Assert.Equal(model, copied); + } +} diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseResponseTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseResponseTest.cs index 3b78216..f37a293 100644 --- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseResponseTest.cs +++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberPurchaseResponseTest.cs @@ -25,6 +25,7 @@ public void FieldRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -46,6 +47,7 @@ public void FieldRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -74,6 +76,7 @@ public void SerializationRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -109,6 +112,7 @@ public void FieldRoundtripThroughSerialization_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -137,6 +141,7 @@ public void FieldRoundtripThroughSerialization_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -165,6 +170,7 @@ public void Validation_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -194,6 +200,7 @@ public void CopyConstructor_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRequirementsParamsTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRequirementsParamsTest.cs index 1f4a908..922e0c5 100644 --- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRequirementsParamsTest.cs +++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRequirementsParamsTest.cs @@ -12,21 +12,28 @@ public void FieldRoundtrip_Works() var parameters = new PhoneNumberRequirementsParams { CountryCode = "xx", + PhoneNumber = "phoneNumber", Type = PhoneNumberType.Local, }; string expectedCountryCode = "xx"; + string expectedPhoneNumber = "phoneNumber"; ApiEnum expectedType = PhoneNumberType.Local; Assert.Equal(expectedCountryCode, parameters.CountryCode); + Assert.Equal(expectedPhoneNumber, parameters.PhoneNumber); Assert.Equal(expectedType, parameters.Type); } [Fact] public void OptionalNonNullableParamsUnsetAreNotSet_Works() { - var parameters = new PhoneNumberRequirementsParams { CountryCode = "xx" }; + var parameters = new PhoneNumberRequirementsParams { }; + Assert.Null(parameters.CountryCode); + Assert.False(parameters.RawQueryData.ContainsKey("countryCode")); + Assert.Null(parameters.PhoneNumber); + Assert.False(parameters.RawQueryData.ContainsKey("phoneNumber")); Assert.Null(parameters.Type); Assert.False(parameters.RawQueryData.ContainsKey("type")); } @@ -36,12 +43,16 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() { var parameters = new PhoneNumberRequirementsParams { - CountryCode = "xx", - // Null should be interpreted as omitted for these properties + CountryCode = null, + PhoneNumber = null, Type = null, }; + Assert.Null(parameters.CountryCode); + Assert.False(parameters.RawQueryData.ContainsKey("countryCode")); + Assert.Null(parameters.PhoneNumber); + Assert.False(parameters.RawQueryData.ContainsKey("phoneNumber")); Assert.Null(parameters.Type); Assert.False(parameters.RawQueryData.ContainsKey("type")); } @@ -52,6 +63,7 @@ public void Url_Works() PhoneNumberRequirementsParams parameters = new() { CountryCode = "xx", + PhoneNumber = "phoneNumber", Type = PhoneNumberType.Local, }; @@ -60,7 +72,7 @@ public void Url_Works() Assert.True( TestBase.UrisEqual( new Uri( - "https://api.zavu.dev/v1/phone-numbers/requirements?countryCode=xx&type=local" + "https://api.zavu.dev/v1/phone-numbers/requirements?countryCode=xx&phoneNumber=phoneNumber&type=local" ), url ) @@ -73,6 +85,7 @@ public void CopyConstructor_Works() var parameters = new PhoneNumberRequirementsParams { CountryCode = "xx", + PhoneNumber = "phoneNumber", Type = PhoneNumberType.Local, }; diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRetrieveResponseTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRetrieveResponseTest.cs index 3e564c9..09d4c92 100644 --- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRetrieveResponseTest.cs +++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberRetrieveResponseTest.cs @@ -25,6 +25,7 @@ public void FieldRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -46,6 +47,7 @@ public void FieldRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -74,6 +76,7 @@ public void SerializationRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -109,6 +112,7 @@ public void FieldRoundtripThroughSerialization_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -137,6 +141,7 @@ public void FieldRoundtripThroughSerialization_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -165,6 +170,7 @@ public void Validation_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -194,6 +200,7 @@ public void CopyConstructor_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberStatusTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberStatusTest.cs index 3b192ae..c9d5823 100644 --- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberStatusTest.cs +++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberStatusTest.cs @@ -11,6 +11,8 @@ public class PhoneNumberStatusTest : TestBase [InlineData(PhoneNumberStatus.Active)] [InlineData(PhoneNumberStatus.Suspended)] [InlineData(PhoneNumberStatus.Pending)] + [InlineData(PhoneNumberStatus.Releasing)] + [InlineData(PhoneNumberStatus.Released)] public void Validation_Works(PhoneNumberStatus rawValue) { // force implicit conversion because Theory can't do that for us @@ -34,6 +36,8 @@ public void InvalidEnumValidationThrows_Works() [InlineData(PhoneNumberStatus.Active)] [InlineData(PhoneNumberStatus.Suspended)] [InlineData(PhoneNumberStatus.Pending)] + [InlineData(PhoneNumberStatus.Releasing)] + [InlineData(PhoneNumberStatus.Released)] public void SerializationRoundtrip_Works(PhoneNumberStatus rawValue) { // force implicit conversion because Theory can't do that for us diff --git a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberUpdateResponseTest.cs b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberUpdateResponseTest.cs index 3bcc4a5..803ea6d 100644 --- a/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberUpdateResponseTest.cs +++ b/src/Zavudev.Tests/Models/PhoneNumbers/PhoneNumberUpdateResponseTest.cs @@ -25,6 +25,7 @@ public void FieldRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -46,6 +47,7 @@ public void FieldRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -74,6 +76,7 @@ public void SerializationRoundtrip_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -109,6 +112,7 @@ public void FieldRoundtripThroughSerialization_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -137,6 +141,7 @@ public void FieldRoundtripThroughSerialization_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -165,6 +170,7 @@ public void Validation_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -194,6 +200,7 @@ public void CopyConstructor_Works() MonthlyPrice = 0, UpfrontCost = 0, }, + RegulatoryStatus = RegulatoryStatus.Approved, Status = PhoneNumberStatus.Active, Name = "name", NextRenewalDate = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/AgentFlowTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/AgentFlowTest.cs index ee49f90..7fada41 100644 --- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/AgentFlowTest.cs +++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/AgentFlowTest.cs @@ -35,7 +35,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -64,7 +64,7 @@ public void FieldRoundtrip_Works() Flows::FlowTrigger expectedTrigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }; DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); @@ -113,7 +113,7 @@ public void SerializationRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -156,7 +156,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -192,7 +192,7 @@ public void FieldRoundtripThroughSerialization_Works() Flows::FlowTrigger expectedTrigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }; DateTimeOffset expectedUpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); @@ -241,7 +241,7 @@ public void Validation_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -278,7 +278,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -315,7 +315,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -351,7 +351,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -390,7 +390,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -428,7 +428,7 @@ public void CopyConstructor_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateParamsTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateParamsTest.cs index 5660eea..7bb43d9 100644 --- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateParamsTest.cs +++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateParamsTest.cs @@ -41,7 +41,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["info", "pricing", "demo"], }, Description = "Capture lead information", @@ -78,7 +78,7 @@ public void FieldRoundtrip_Works() Flows::FlowTrigger expectedTrigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["info", "pricing", "demo"], }; string expectedDescription = "Capture lead information"; @@ -132,7 +132,7 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["info", "pricing", "demo"], }, }; @@ -179,7 +179,7 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["info", "pricing", "demo"], }, @@ -231,7 +231,7 @@ public void Url_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["info", "pricing", "demo"], }, }; @@ -277,7 +277,7 @@ public void CopyConstructor_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["info", "pricing", "demo"], }, Description = "Capture lead information", diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateResponseTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateResponseTest.cs index d2363df..ceccad3 100644 --- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateResponseTest.cs +++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowCreateResponseTest.cs @@ -37,7 +37,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -69,7 +69,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -108,7 +108,7 @@ public void SerializationRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -154,7 +154,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -193,7 +193,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -232,7 +232,7 @@ public void Validation_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -272,7 +272,7 @@ public void CopyConstructor_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowDuplicateResponseTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowDuplicateResponseTest.cs index ecbcadb..3bc398a 100644 --- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowDuplicateResponseTest.cs +++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowDuplicateResponseTest.cs @@ -37,7 +37,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -69,7 +69,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -108,7 +108,7 @@ public void SerializationRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -154,7 +154,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -193,7 +193,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -232,7 +232,7 @@ public void Validation_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -272,7 +272,7 @@ public void CopyConstructor_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowListPageResponseTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowListPageResponseTest.cs index b3ca95a..9bd772c 100644 --- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowListPageResponseTest.cs +++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowListPageResponseTest.cs @@ -39,7 +39,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -75,7 +75,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -123,7 +123,7 @@ public void SerializationRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -173,7 +173,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -216,7 +216,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -264,7 +264,7 @@ public void Validation_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -308,7 +308,7 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -352,7 +352,7 @@ public void OptionalNullablePropertiesUnsetValidation_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -395,7 +395,7 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -441,7 +441,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -486,7 +486,7 @@ public void CopyConstructor_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowRetrieveResponseTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowRetrieveResponseTest.cs index dc9a030..db20921 100644 --- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowRetrieveResponseTest.cs +++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowRetrieveResponseTest.cs @@ -37,7 +37,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -69,7 +69,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -108,7 +108,7 @@ public void SerializationRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -154,7 +154,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -193,7 +193,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -232,7 +232,7 @@ public void Validation_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -272,7 +272,7 @@ public void CopyConstructor_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowTriggerTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowTriggerTest.cs index 17d4d3b..b5c3397 100644 --- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowTriggerTest.cs +++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowTriggerTest.cs @@ -14,12 +14,12 @@ public void FieldRoundtrip_Works() var model = new FlowTrigger { Type = FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }; ApiEnum expectedType = FlowTriggerType.Keyword; - string expectedIntent = "intent"; + string expectedIntent = "quiere saber precios o cotizar"; List expectedKeywords = ["string"]; Assert.Equal(expectedType, model.Type); @@ -38,7 +38,7 @@ public void SerializationRoundtrip_Works() var model = new FlowTrigger { Type = FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }; @@ -57,7 +57,7 @@ public void FieldRoundtripThroughSerialization_Works() var model = new FlowTrigger { Type = FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }; @@ -69,7 +69,7 @@ public void FieldRoundtripThroughSerialization_Works() Assert.NotNull(deserialized); ApiEnum expectedType = FlowTriggerType.Keyword; - string expectedIntent = "intent"; + string expectedIntent = "quiere saber precios o cotizar"; List expectedKeywords = ["string"]; Assert.Equal(expectedType, deserialized.Type); @@ -88,7 +88,7 @@ public void Validation_Works() var model = new FlowTrigger { Type = FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }; @@ -153,7 +153,7 @@ public void CopyConstructor_Works() var model = new FlowTrigger { Type = FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }; diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateParamsTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateParamsTest.cs index 8e0008f..336bf5d 100644 --- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateParamsTest.cs +++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateParamsTest.cs @@ -34,7 +34,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, }; @@ -61,7 +61,7 @@ public void FieldRoundtrip_Works() Flows::FlowTrigger expectedTrigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }; @@ -172,7 +172,7 @@ public void CopyConstructor_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, }; diff --git a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateResponseTest.cs b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateResponseTest.cs index e430296..6f22241 100644 --- a/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateResponseTest.cs +++ b/src/Zavudev.Tests/Models/Senders/Agent/Flows/FlowUpdateResponseTest.cs @@ -37,7 +37,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -69,7 +69,7 @@ public void FieldRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -108,7 +108,7 @@ public void SerializationRoundtrip_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -154,7 +154,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -193,7 +193,7 @@ public void FieldRoundtripThroughSerialization_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -232,7 +232,7 @@ public void Validation_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), @@ -272,7 +272,7 @@ public void CopyConstructor_Works() Trigger = new() { Type = Flows::FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["string"], }, UpdatedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), diff --git a/src/Zavudev.Tests/Services/AddressServiceTest.cs b/src/Zavudev.Tests/Services/AddressServiceTest.cs index 6ac263d..78aeb47 100644 --- a/src/Zavudev.Tests/Services/AddressServiceTest.cs +++ b/src/Zavudev.Tests/Services/AddressServiceTest.cs @@ -11,6 +11,8 @@ public async Task Create_Works() new() { CountryCode = "DE", + FirstName = "John", + LastName = "Doe", Locality = "Berlin", PostalCode = "10115", StreetAddress = "123 Main St", diff --git a/src/Zavudev.Tests/Services/PhoneNumberServiceTest.cs b/src/Zavudev.Tests/Services/PhoneNumberServiceTest.cs index 00de373..1775a58 100644 --- a/src/Zavudev.Tests/Services/PhoneNumberServiceTest.cs +++ b/src/Zavudev.Tests/Services/PhoneNumberServiceTest.cs @@ -60,7 +60,7 @@ await this.client.PhoneNumbers.Release( public async Task Requirements_Works() { var response = await this.client.PhoneNumbers.Requirements( - new() { CountryCode = "xx" }, + new(), TestContext.Current.CancellationToken ); response.Validate(); diff --git a/src/Zavudev.Tests/Services/Senders/Agent/FlowServiceTest.cs b/src/Zavudev.Tests/Services/Senders/Agent/FlowServiceTest.cs index b7e0a77..a6f6bf2 100644 --- a/src/Zavudev.Tests/Services/Senders/Agent/FlowServiceTest.cs +++ b/src/Zavudev.Tests/Services/Senders/Agent/FlowServiceTest.cs @@ -42,7 +42,7 @@ public async Task Create_Works() Trigger = new() { Type = FlowTriggerType.Keyword, - Intent = "intent", + Intent = "quiere saber precios o cotizar", Keywords = ["info", "pricing", "demo"], }, }, diff --git a/src/Zavudev/Core/ModelBase.cs b/src/Zavudev/Core/ModelBase.cs index 40d3e4f..1db4b4c 100644 --- a/src/Zavudev/Core/ModelBase.cs +++ b/src/Zavudev/Core/ModelBase.cs @@ -94,6 +94,7 @@ protected ModelBase(ModelBase modelBase) new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), + new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), diff --git a/src/Zavudev/Models/Addresses/AddressCreateParams.cs b/src/Zavudev/Models/Addresses/AddressCreateParams.cs index efa1bfc..4f3d881 100644 --- a/src/Zavudev/Models/Addresses/AddressCreateParams.cs +++ b/src/Zavudev/Models/Addresses/AddressCreateParams.cs @@ -10,8 +10,8 @@ namespace Zavudev.Models.Addresses; /// -/// Create a regulatory address for phone number purchases. Some countries require -/// a verified address before phone numbers can be activated. +/// Create a regulatory address, to use as the value of an `address` requirement when +/// buying a phone number. It is registered for review when it is created, with status `pending`. /// /// NOTE: Do not inherit from this type outside the SDK unless you're okay with /// breaking changes in non-major versions. We may add new methods in the future that @@ -35,6 +35,32 @@ public required string CountryCode init { this._rawBodyData.Set("countryCode", value); } } + /// + /// First name of the person the address is registered to. + /// + public required string FirstName + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("firstName"); + } + init { this._rawBodyData.Set("firstName", value); } + } + + /// + /// Last name of the person the address is registered to. + /// + public required string LastName + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNotNullClass("lastName"); + } + init { this._rawBodyData.Set("lastName", value); } + } + public required string Locality { get @@ -83,6 +109,10 @@ public string? AdministrativeArea } } + /// + /// Business name, when the address belongs to a business. Defaults to the person's + /// full name. + /// public string? BusinessName { get @@ -119,42 +149,6 @@ public string? ExtendedAddress } } - public string? FirstName - { - get - { - this._rawBodyData.Freeze(); - return this._rawBodyData.GetNullableClass("firstName"); - } - init - { - if (value == null) - { - return; - } - - this._rawBodyData.Set("firstName", value); - } - } - - public string? LastName - { - get - { - this._rawBodyData.Freeze(); - return this._rawBodyData.GetNullableClass("lastName"); - } - init - { - if (value == null) - { - return; - } - - this._rawBodyData.Set("lastName", value); - } - } - public AddressCreateParams() { } #pragma warning disable CS8618 diff --git a/src/Zavudev/Models/Addresses/AddressDeleteParams.cs b/src/Zavudev/Models/Addresses/AddressDeleteParams.cs index 3e6a742..7c943fc 100644 --- a/src/Zavudev/Models/Addresses/AddressDeleteParams.cs +++ b/src/Zavudev/Models/Addresses/AddressDeleteParams.cs @@ -9,7 +9,9 @@ namespace Zavudev.Models.Addresses; /// -/// Delete a regulatory address. Cannot delete addresses that are in use. +/// Delete a regulatory address from this project. Any address can be deleted, whatever +/// its status. Phone numbers already purchased with it are not affected, and neither +/// is information already submitted for later purchases in its country. /// /// NOTE: Do not inherit from this type outside the SDK unless you're okay with /// breaking changes in non-major versions. We may add new methods in the future that diff --git a/src/Zavudev/Models/Broadcasts/Broadcast.cs b/src/Zavudev/Models/Broadcasts/Broadcast.cs index fe4f9f7..bc41d52 100644 --- a/src/Zavudev/Models/Broadcasts/Broadcast.cs +++ b/src/Zavudev/Models/Broadcasts/Broadcast.cs @@ -148,6 +148,9 @@ public BroadcastContent? Content } } + /// + /// Recipients with confirmed delivery to the device. + /// public long? DeliveredCount { get @@ -347,6 +350,28 @@ public long? SendingCount } } + /// + /// Recipients whose message the provider accepted, without a confirmed delivery + /// yet. Channels that never report delivery keep their recipients here. + /// + public long? SentCount + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("sentCount"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("sentCount", value); + } + } + public DateTimeOffset? StartedAt { get @@ -426,6 +451,7 @@ public override void Validate() _ = this.ScheduledAt; _ = this.SenderID; _ = this.SendingCount; + _ = this.SentCount; _ = this.StartedAt; _ = this.Text; _ = this.UpdatedAt; diff --git a/src/Zavudev/Models/Broadcasts/BroadcastContact.cs b/src/Zavudev/Models/Broadcasts/BroadcastContact.cs index 9a129ca..6aa240d 100644 --- a/src/Zavudev/Models/Broadcasts/BroadcastContact.cs +++ b/src/Zavudev/Models/Broadcasts/BroadcastContact.cs @@ -54,6 +54,15 @@ public required ApiEnum RecipientType /// /// Status of a contact within a broadcast. + /// + /// - `pending`, `queued`, `sending`: not handed to the provider yet. - + /// `sent`: accepted by the provider; delivery is not confirmed yet. Channels + /// that never report delivery leave the recipient here. - `delivered`: the channel + /// confirmed delivery to the device. A WhatsApp read receipt also counts as + /// delivered. - `failed`: not delivered. A recipient can move from `sent` or + /// `delivered` to `failed` when the provider reports a failure late. - `skipped`: + /// not sent, because the recipient opted out of the channel or the broadcast + /// was cancelled before reaching it. /// public required ApiEnum Status { diff --git a/src/Zavudev/Models/Broadcasts/BroadcastContactStatus.cs b/src/Zavudev/Models/Broadcasts/BroadcastContactStatus.cs index 3012798..5ea97e6 100644 --- a/src/Zavudev/Models/Broadcasts/BroadcastContactStatus.cs +++ b/src/Zavudev/Models/Broadcasts/BroadcastContactStatus.cs @@ -7,6 +7,14 @@ namespace Zavudev.Models.Broadcasts; /// /// Status of a contact within a broadcast. +/// +/// - `pending`, `queued`, `sending`: not handed to the provider yet. - `sent`: +/// accepted by the provider; delivery is not confirmed yet. Channels that never +/// report delivery leave the recipient here. - `delivered`: the channel confirmed +/// delivery to the device. A WhatsApp read receipt also counts as delivered. - `failed`: +/// not delivered. A recipient can move from `sent` or `delivered` to `failed` when +/// the provider reports a failure late. - `skipped`: not sent, because the recipient +/// opted out of the channel or the broadcast was cancelled before reaching it. /// [JsonConverter(typeof(BroadcastContactStatusConverter))] public enum BroadcastContactStatus @@ -14,6 +22,7 @@ public enum BroadcastContactStatus Pending, Queued, Sending, + Sent, Delivered, Failed, Skipped, @@ -32,6 +41,7 @@ JsonSerializerOptions options "pending" => BroadcastContactStatus.Pending, "queued" => BroadcastContactStatus.Queued, "sending" => BroadcastContactStatus.Sending, + "sent" => BroadcastContactStatus.Sent, "delivered" => BroadcastContactStatus.Delivered, "failed" => BroadcastContactStatus.Failed, "skipped" => BroadcastContactStatus.Skipped, @@ -52,6 +62,7 @@ JsonSerializerOptions options BroadcastContactStatus.Pending => "pending", BroadcastContactStatus.Queued => "queued", BroadcastContactStatus.Sending => "sending", + BroadcastContactStatus.Sent => "sent", BroadcastContactStatus.Delivered => "delivered", BroadcastContactStatus.Failed => "failed", BroadcastContactStatus.Skipped => "skipped", diff --git a/src/Zavudev/Models/Broadcasts/BroadcastProgress.cs b/src/Zavudev/Models/Broadcasts/BroadcastProgress.cs index 8bb96fb..b876037 100644 --- a/src/Zavudev/Models/Broadcasts/BroadcastProgress.cs +++ b/src/Zavudev/Models/Broadcasts/BroadcastProgress.cs @@ -22,7 +22,7 @@ public required string BroadcastID } /// - /// Successfully delivered. + /// Confirmed delivered to the device. /// public required long Delivered { @@ -182,6 +182,27 @@ public double? ReservedAmount init { this._rawData.Set("reservedAmount", value); } } + /// + /// Accepted by the provider, delivery not confirmed yet. + /// + public long? Sent + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("sent"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("sent", value); + } + } + public DateTimeOffset? StartedAt { get @@ -216,6 +237,7 @@ public override void Validate() _ = this.EstimatedCompletionAt; _ = this.EstimatedCost; _ = this.ReservedAmount; + _ = this.Sent; _ = this.StartedAt; } diff --git a/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs b/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs index d36ffc5..50fca67 100644 --- a/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs +++ b/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs @@ -12,16 +12,11 @@ namespace Zavudev.Models.Broadcasts; /// /// Start sending the broadcast immediately or schedule for later. /// -/// **The account must be past the unverified level to send, except on WhatsApp.** -/// An account that has verified nothing is refused with `403` and code `kyc_required` -/// on every channel other than `whatsapp`. Any one of these lifts it: identity verification -/// (KYC), a saved payment method, a settled deposit, or a paid plan. Business verification -/// (KYB) is not required to broadcast; it gates 10DLC registration only. A `whatsapp` -/// broadcast is exempt: it can only be built on a template, and Meta vets the business -/// and the content when it approves that template, so an unapproved template is -/// refused instead. `smart` is not exempt, since it can route a contact to SMS or -/// email. Drafts can be created, edited and kept without any check. Every send path -/// (dashboard, API and CLI) enforces the same rule. +/// **Sending a broadcast needs no account verification.** Identity and business +/// verification raise daily ceilings; neither is a permission to broadcast, on any +/// channel. What stands in front of a broadcast is the content review below, and +/// it applies to every send path — dashboard, API and CLI. Drafts can be created, +/// edited and kept freely. /// /// **Daily ceilings apply per recipient.** Each message a broadcast sends /// counts against the channel's daily ceiling (see `POST /v1/messages`). Once the @@ -31,9 +26,15 @@ namespace Zavudev.Models.Broadcasts; /// **Review depends on the channel, and cannot be bypassed.** A draft is submitted /// to automated content review here; it does not go straight out. A WhatsApp broadcast /// built on a Meta-approved template skips review (Meta already vetted the content) -/// and begins sending. An email broadcast sends as soon as the automated review -/// passes. Every other channel moves to `pending_admin_review` and waits for a person. -/// If the review rejects it, use PATCH to edit the content then call POST /retry-review. +/// and begins sending. An email broadcast sends as soon as the review passes it, +/// unless the review asks for a person. Every other channel moves to `pending_admin_review` +/// and waits for a person. A broadcast the review refuses lands on `rejected`: use +/// PATCH to edit the content then call POST /retry-review, or escalate it for a +/// manual review. +/// +/// A broadcast is read once, on its own text, rather than per recipient. While +/// an account's sending is suspended its recipients fail individually with `errorCode` +/// `SENDING_SUSPENDED`; see `POST /v1/messages`. /// /// Calling this on a broadcast that is already `approved` or `scheduled` sends /// or reschedules it directly, since it has already been reviewed. Reserves the diff --git a/src/Zavudev/Models/Broadcasts/Contacts/ContactListParams.cs b/src/Zavudev/Models/Broadcasts/Contacts/ContactListParams.cs index a4a4155..0d906b2 100644 --- a/src/Zavudev/Models/Broadcasts/Contacts/ContactListParams.cs +++ b/src/Zavudev/Models/Broadcasts/Contacts/ContactListParams.cs @@ -57,6 +57,15 @@ public long? Limit /// /// Status of a contact within a broadcast. + /// + /// - `pending`, `queued`, `sending`: not handed to the provider yet. - + /// `sent`: accepted by the provider; delivery is not confirmed yet. Channels + /// that never report delivery leave the recipient here. - `delivered`: the channel + /// confirmed delivery to the device. A WhatsApp read receipt also counts as + /// delivered. - `failed`: not delivered. A recipient can move from `sent` or + /// `delivered` to `failed` when the provider reports a failure late. - `skipped`: + /// not sent, because the recipient opted out of the channel or the broadcast + /// was cancelled before reaching it. /// public ApiEnum? Status { diff --git a/src/Zavudev/Models/Calls/CallCreateParams.cs b/src/Zavudev/Models/Calls/CallCreateParams.cs index 41288c9..9d8b2a3 100644 --- a/src/Zavudev/Models/Calls/CallCreateParams.cs +++ b/src/Zavudev/Models/Calls/CallCreateParams.cs @@ -16,16 +16,17 @@ namespace Zavudev.Models.Calls; /// interruption handling). /// /// **Requirements:** - The Voice Agents feature must be enabled for your team -/// (otherwise `403`). - An account that has verified nothing may only call the phone -/// numbers the project has verified (`403` with code `destination_not_verified`, -/// and `details.verifiedNumbers` lists them), and at most 5 calls a day (`429` with -/// code `daily_limit_exceeded`). A number is verified from the dashboard's Sandbox -/// screen by sending the pre-filled WhatsApp message from that phone; the same verification -/// covers SMS and calls. Verify your identity, add a payment method, settle a deposit -/// or subscribe to call any destination. That raises the ceiling to 50 calls a day -/// on Free; paid plans have no daily call ceiling. Full reference: https://docs.zavu.dev/concepts/sending-limits -/// - The sender's agent must have `voice.enabled` set to `true`. - Not available -/// with test-mode API keys. +/// (otherwise `403`). - An account may call any destination from its first minute, +/// within the daily ceiling: 5 calls a day for an account that has verified nothing, +/// 50 a day on Free once it has verified its identity, added a payment method, settled +/// a deposit or subscribed. Paid plans have no daily call ceiling. Over it, `429` +/// with code `daily_limit_exceeded`. Full reference: https://docs.zavu.dev/concepts/sending-limits +/// - The call is read by Zavu's automated risk review before it is dialed, the way +/// a message is (see `POST /v1/messages`). A call is never held for a person: one +/// the review stops is failed rather than placed late. Repeated refusals suspend +/// the account's calling, answered with `403` and code `sending_suspended`. - The +/// sender's agent must have `voice.enabled` set to `true`. - Not available with +/// test-mode API keys. /// /// **Billing:** Voice calls are billed per minute of connected time plus telephony, /// deducted from your prepaid balance. A short-duration estimate is reserved when diff --git a/src/Zavudev/Models/Messages/MessageSendParams.cs b/src/Zavudev/Models/Messages/MessageSendParams.cs index cc83de7..a3ef7f2 100644 --- a/src/Zavudev/Models/Messages/MessageSendParams.cs +++ b/src/Zavudev/Models/Messages/MessageSendParams.cs @@ -23,9 +23,16 @@ namespace Zavudev.Models.Messages; /// messages to initiate conversations outside the window /// /// **Plan allowances and email billing:** - WhatsApp, Telegram, Instagram and -/// Messenger share an allowance of 2,000 messages per month on Free. Over it, sends -/// return 429 with code `a2p_limit_exceeded` and upgrade details; the counter resets -/// on the 1st of each month. Paid plans have no message caps - Email is billed from +/// Messenger share an allowance of 2,000 messages per month on Free. **It counts +/// messages in both directions**: a message a contact sends you consumes one unit +/// exactly as a message you send them does, so a project that has sent 300 and received +/// 1,700 has used the whole allowance. Messages you send from the WhatsApp Business +/// App on your own phone under coexistence are mirrored into your inbox but never +/// counted, and neither are failed sends. Over the allowance, sends return 429 with +/// code `a2p_limit_exceeded` and upgrade details, **and inbound messages on those +/// channels are refused as well**: not stored, not shown in the inbox, and no `message.inbound` +/// webhook, and not delivered later when the month resets. The counter resets on +/// the 1st of each month. Paid plans have no message caps - Email is billed from /// your prepaid balance in 1,000-message blocks: $0.40 per 1,000 transactional emails, /// $0.80 per 1,000 marketing (broadcast) emails. A block is charged when your monthly /// count crosses each 1,000 boundary, and at zero balance email sends return 402 @@ -34,30 +41,44 @@ namespace Zavudev.Models.Messages; /// email quotas instead - SMS and voice are billed per message from your balance /// on every plan /// -/// **Account verification and daily limits:** - A brand-new account can send -/// on every channel immediately, but `sms`, `sms_oneway` and `voice` reach only -/// the phone numbers the project has verified. Sending elsewhere returns `403` with -/// code `destination_not_verified`; `details.verifiedNumbers` lists the numbers -/// that are reachable. A number is verified from the dashboard's Sandbox screen: -/// generate a code and send the pre-filled WhatsApp message from that phone to Zavu's -/// sandbox number. One verification covers WhatsApp, SMS and calls, up to 5 numbers -/// per project. To send to any destination, do any one of these: verify your identity, -/// add a payment method, settle a deposit, or subscribe to a paid plan. Business -/// verification (KYB) is never required to send - Daily ceilings apply per channel -/// group and rise with verification. An account that has verified nothing: 25/day -/// across `sms` + `sms_oneway`, 5/day for `voice`, 100/day across WhatsApp, Telegram, -/// Instagram and Messenger combined. Past that floor: 200/day for SMS, or 10,000/day -/// once identity or business verification is approved (or a higher limit agreed -/// for your account); 50/day voice and 250/day conversational on Free. **Paid plans -/// have no voice or conversational daily ceiling.** Over a ceiling, sends return -/// `429` with code `daily_limit_exceeded` and `details.limit`; the count resets at -/// 00:00 UTC - The daily ceiling never reduces the monthly allowance: 100/day on -/// the conversational group still reaches the 2,000 monthly A2P messages Free includes -/// - Email needs no account verification here: a sender with a verified domain sends +/// **Daily limits:** - An account sends on every channel from its first minute, +/// to any destination. Verification is not a permission to send: identity verification +/// and business verification (KYB) raise the ceilings below and nothing else asks +/// for them here. KYB is still required to register a 10DLC brand and campaign, which +/// every US and Canadian (+1) SMS destination needs — a carrier rule, answered separately +/// with `403 ten_dlc_required` - Daily ceilings apply per channel group and rise +/// with verification. An account that has verified nothing: 25/day across `sms` + +/// `sms_oneway`, 5/day for `voice`, 100/day across WhatsApp, Telegram, Instagram +/// and Messenger combined. Past that floor: 200/day for SMS, or 10,000/day once identity +/// or business verification is approved (or a higher limit agreed for your account); +/// 50/day voice and 250/day conversational on Free. **Paid plans have no voice or +/// conversational daily ceiling.** Over a ceiling, sends return `429` with code `daily_limit_exceeded` +/// and `details.limit`; the count resets at 00:00 UTC - The daily ceiling never reduces +/// the monthly allowance: 100/day on the conversational group still reaches the 2,000 +/// monthly A2P messages Free includes - Email: a sender with a verified domain sends /// from day one, within the plan quota (100/day and 3,000/month on Free). Over the -/// daily quota it returns `429` with code `daily_limit_exceeded`. Email broadcasts -/// are the exception: they need the account past the unverified level, see `POST -/// /v1/broadcasts/{broadcastId}/send` - Full reference: https://docs.zavu.dev/concepts/sending-limits +/// daily quota it returns `429` with code `daily_limit_exceeded` - Full reference: https://docs.zavu.dev/concepts/sending-limits +/// +/// **Risk review:** Every outbound `sms`, `sms_oneway`, `email` and `voice` +/// message is read before it is sent — the content, and how this account has been +/// sending. What is checked is the message, not who you are. - A message can be **held** +/// for a short review. It stays `queued` while it waits: no new status exists for +/// this, and `MessageStatus` is unchanged. When it is approved it sends normally. +/// - A message that is not approved moves to `failed` and fires `message.failed`. +/// `errorCode` says which happened: `RISK_REJECTED` (a reviewer refused it), `RISK_REVIEW_EXPIRED` +/// (the review window closed first — it is a couple of hours, because a code that +/// arrives late is worse than one that does not arrive), or `RISK_BLOCKED` (refused +/// outright, without a hold). An SMS that fails this way is not charged; the prepaid +/// amount is returned. - A call is never held. `POST /v1/calls` fails a call the +/// review stops rather than placing it hours late. - A message whose content cannot +/// be read — the check is briefly unavailable — is held rather than sent. An account +/// with an approved business verification is unaffected, and so is one that has +/// verified something, already sends real traffic, and has a clean recent record. +/// - Repeated refusals suspend an account's sending. While it is suspended every +/// send is refused with `403` and code `sending_suspended`, `details.dashboardUrl` +/// points at support, and a message already queued fails with `errorCode` `SENDING_SUSPENDED`. +/// - A broadcast is read once, on the broadcast itself, rather than per recipient +/// — see `POST /v1/broadcasts/{broadcastId}/send`. /// /// **Email recipient pre-flight:** Email messages are validated automatically /// before dispatch. Sends that would be a guaranteed hard bounce are failed instead diff --git a/src/Zavudev/Models/Number10dlc/Brands/TenDlcBrand.cs b/src/Zavudev/Models/Number10dlc/Brands/TenDlcBrand.cs index c01be15..d596f2f 100644 --- a/src/Zavudev/Models/Number10dlc/Brands/TenDlcBrand.cs +++ b/src/Zavudev/Models/Number10dlc/Brands/TenDlcBrand.cs @@ -128,6 +128,15 @@ public required string State /// /// Status of a 10DLC brand registration. + /// + /// - `draft`: created, not yet submitted to the carrier. - `pending`: submitted, + /// awaiting the carrier's answer. - `verified`: the carrier registered the brand + /// AND verified the business behind it. - `unverified`: the carrier registered + /// the brand but did not verify the business — the registration exists, the identity + /// check did not pass or has not been resolved. Campaigns are allowed, with lower + /// daily limits. Read `identityStatus` for the carrier's own wording. - `rejected`: + /// refused by the carrier. - `failed`: the registration never reached the carrier; + /// the fee is refunded. /// public required ApiEnum Status { @@ -244,6 +253,22 @@ public string? FirstName init { this._rawData.Set("firstName", value); } } + /// + /// The carrier's raw identity verdict on the business, as the carrier spells + /// it (`VERIFIED`, `VETTED_VERIFIED`, `SELF_DECLARED`, `UNVERIFIED`). Null while + /// the identity has not been resolved — which is not the same as verified, and + /// is why such a brand reports `status: unverified`. + /// + public string? IdentityStatus + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("identityStatus"); + } + init { this._rawData.Set("identityStatus", value); } + } + public string? LastName { get @@ -327,6 +352,7 @@ public override void Validate() _ = this.Ein; _ = this.FailureReason; _ = this.FirstName; + _ = this.IdentityStatus; _ = this.LastName; _ = this.StockExchange; _ = this.StockSymbol; @@ -428,6 +454,15 @@ JsonSerializerOptions options /// /// Status of a 10DLC brand registration. +/// +/// - `draft`: created, not yet submitted to the carrier. - `pending`: submitted, +/// awaiting the carrier's answer. - `verified`: the carrier registered the brand +/// AND verified the business behind it. - `unverified`: the carrier registered the +/// brand but did not verify the business — the registration exists, the identity +/// check did not pass or has not been resolved. Campaigns are allowed, with lower +/// daily limits. Read `identityStatus` for the carrier's own wording. - `rejected`: +/// refused by the carrier. - `failed`: the registration never reached the carrier; +/// the fee is refunded. /// [JsonConverter(typeof(StatusConverter))] public enum Status @@ -435,7 +470,9 @@ public enum Status Draft, Pending, Verified, + Unverified, Rejected, + Failed, } sealed class StatusConverter : JsonConverter @@ -451,7 +488,9 @@ JsonSerializerOptions options "draft" => Status.Draft, "pending" => Status.Pending, "verified" => Status.Verified, + "unverified" => Status.Unverified, "rejected" => Status.Rejected, + "failed" => Status.Failed, _ => (Status)(-1), }; } @@ -465,7 +504,9 @@ public override void Write(Utf8JsonWriter writer, Status value, JsonSerializerOp Status.Draft => "draft", Status.Pending => "pending", Status.Verified => "verified", + Status.Unverified => "unverified", Status.Rejected => "rejected", + Status.Failed => "failed", _ => throw new ZavudevInvalidDataException( string.Format("Invalid value '{0}' in {1}", value, nameof(value)) ), diff --git a/src/Zavudev/Models/PhoneNumbers/OwnedPhoneNumber.cs b/src/Zavudev/Models/PhoneNumbers/OwnedPhoneNumber.cs index b8ba0eb..79679eb 100644 --- a/src/Zavudev/Models/PhoneNumbers/OwnedPhoneNumber.cs +++ b/src/Zavudev/Models/PhoneNumbers/OwnedPhoneNumber.cs @@ -6,6 +6,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using Zavudev.Core; +using Zavudev.Exceptions; namespace Zavudev.Models.PhoneNumbers; @@ -68,6 +69,39 @@ public required OwnedPhoneNumberPricing Pricing init { this._rawData.Set("pricing", value); } } + /// + /// Regulatory review state. Numbers that need no review are `approved` immediately. + /// A number bought with regulatory information is owned and billed from purchase + /// and starts `pending_review`; it cannot send messages or place calls until + /// this is `approved`. The state is re-checked every 6 hours: poll `GET /v1/phone-numbers/{phoneNumberId}` + /// to follow it. + /// + /// Assign it to a sender with `PATCH /v1/phone-numbers/{phoneNumberId}` + /// (`senderId`) before or after approval. A number assigned while under review + /// is recorded and connected to that sender when it is approved; the connection + /// is retried until it succeeds. A sender created over the API is set up for + /// SMS as part of the assignment. `rejected` means review refused the information: + /// the number cannot be assigned to a sender. A number that stays `pending_review` + /// may be waiting on information the API cannot supply; contact support. + /// + public required ApiEnum RegulatoryStatus + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass>( + "regulatoryStatus" + ); + } + init { this._rawData.Set("regulatoryStatus", value); } + } + + /// + /// Billing state of an owned number, separate from `regulatoryStatus`. `pending` + /// is legacy and is not written to numbers today. The SDKs carry `active`, `suspended` + /// and `pending` only; `releasing` and `released` are returned by the REST API + /// until their next release. + /// public required ApiEnum Status { get @@ -164,6 +198,7 @@ public override void Validate() _ = this.CreatedAt; _ = this.PhoneNumber; this.Pricing.Validate(); + this.RegulatoryStatus.Validate(); this.Status.Validate(); _ = this.Name; _ = this.NextRenewalDate; @@ -207,3 +242,65 @@ class OwnedPhoneNumberFromRaw : IFromRawJson public OwnedPhoneNumber FromRawUnchecked(IReadOnlyDictionary rawData) => OwnedPhoneNumber.FromRawUnchecked(rawData); } + +/// +/// Regulatory review state. Numbers that need no review are `approved` immediately. +/// A number bought with regulatory information is owned and billed from purchase +/// and starts `pending_review`; it cannot send messages or place calls until this +/// is `approved`. The state is re-checked every 6 hours: poll `GET /v1/phone-numbers/{phoneNumberId}` +/// to follow it. +/// +/// Assign it to a sender with `PATCH /v1/phone-numbers/{phoneNumberId}` (`senderId`) +/// before or after approval. A number assigned while under review is recorded and +/// connected to that sender when it is approved; the connection is retried until +/// it succeeds. A sender created over the API is set up for SMS as part of the assignment. +/// `rejected` means review refused the information: the number cannot be assigned +/// to a sender. A number that stays `pending_review` may be waiting on information +/// the API cannot supply; contact support. +/// +[JsonConverter(typeof(RegulatoryStatusConverter))] +public enum RegulatoryStatus +{ + Approved, + PendingReview, + Rejected, +} + +sealed class RegulatoryStatusConverter : JsonConverter +{ + public override RegulatoryStatus Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "approved" => RegulatoryStatus.Approved, + "pending_review" => RegulatoryStatus.PendingReview, + "rejected" => RegulatoryStatus.Rejected, + _ => (RegulatoryStatus)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + RegulatoryStatus value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + RegulatoryStatus.Approved => "approved", + RegulatoryStatus.PendingReview => "pending_review", + RegulatoryStatus.Rejected => "rejected", + _ => throw new ZavudevInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} diff --git a/src/Zavudev/Models/PhoneNumbers/PhoneNumberPricing.cs b/src/Zavudev/Models/PhoneNumbers/PhoneNumberPricing.cs index e935103..1e1ddbd 100644 --- a/src/Zavudev/Models/PhoneNumbers/PhoneNumberPricing.cs +++ b/src/Zavudev/Models/PhoneNumbers/PhoneNumberPricing.cs @@ -11,9 +11,9 @@ namespace Zavudev.Models.PhoneNumbers; public sealed record class PhoneNumberPricing : JsonModel { /// - /// Whether this number qualifies as the plan-included US number on paid plans. - /// The benefit is one per account: it is never offered again once claimed, not - /// even after the number is released. + /// Whether this number qualifies as the plan-included number: a US or Canadian + /// number (a +1 number) costing $20 a month or less. The benefit is one per account: + /// it is never offered again once claimed, not even after the number is released. /// public bool? IsFreeEligible { diff --git a/src/Zavudev/Models/PhoneNumbers/PhoneNumberPurchaseParams.cs b/src/Zavudev/Models/PhoneNumbers/PhoneNumberPurchaseParams.cs index a0e5286..422f0d4 100644 --- a/src/Zavudev/Models/PhoneNumbers/PhoneNumberPurchaseParams.cs +++ b/src/Zavudev/Models/PhoneNumbers/PhoneNumberPurchaseParams.cs @@ -1,21 +1,51 @@ using System; using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Net.Http; using System.Text; using System.Text.Json; +using System.Text.Json.Serialization; using Zavudev.Core; namespace Zavudev.Models.PhoneNumbers; /// /// Purchase an available phone number. Requires a paid plan: the Free plan cannot -/// purchase phone numbers and receives `402` with code `paid_plan_required`. Paid -/// plans include one US number at no charge. The included number is one per account -/// and is granted once: claiming it spends the benefit for good, so releasing that -/// number does not make another one free, and numbers the account already bought -/// do not consume it. +/// purchase phone numbers and receives `402` with code `paid_plan_required`. +/// +/// **The included number.** A paid plan includes one number at no charge, +/// once per account: it must be a US or Canadian number (a +1 number) costing $20 +/// a month or less. `isFreeEligible` in `GET /v1/phone-numbers/available` marks the +/// numbers that qualify. Claiming it spends the benefit for good, across every team +/// the account owner owns, so releasing that number does not make another one free. +/// +/// **Numbers with regulatory requirements.** Which numbers need regulatory +/// information is decided per number, not by a fixed country list. The purchase looks +/// the requirements up for the exact number before charging anything: +/// +/// 1. `GET /v1/phone-numbers/requirements?phoneNumber=...`. If `items` is +/// empty, buy normally. 2. Create what it asks for: addresses with `POST /v1/addresses`, +/// documents with `POST /v1/documents`. 3. Purchase with `type` and `regulatoryRequirements`. +/// The number is bought and billed at once with `regulatoryStatus: pending_review`. +/// 4. Poll `GET /v1/phone-numbers/{phoneNumberId}` until `regulatoryStatus` is `approved`. +/// Assign it to a sender before or after approval; it starts carrying messages once approved. +/// +/// **Reuse.** Information you submitted is kept for your project, per country +/// and `type`, and a later purchase there may omit `regulatoryRequirements`. Reuse +/// only happens when what is kept still covers every requirement of the new number +/// and every address and document in it belongs to the project. Otherwise, or when +/// nothing is kept, the purchase returns `400 regulatory_compliance_required` with +/// the missing requirements in `details`. +/// +/// Invalid values (a missing, unknown or repeated requirement id, an address +/// or document from another project, or one rejected in review) return `400 invalid_request`. +/// If an address or document cannot be registered for review, the purchase returns +/// `400 invalid_request` naming the requirement. If the requirements cannot be looked +/// up, the purchase returns `502 requirements_unavailable`, except for US and Canadian +/// numbers, which are sold as numbers without requirements. None of these errors +/// charge anything. /// /// NOTE: Do not inherit from this type outside the SDK unless you're okay with /// breaking changes in non-major versions. We may add new methods in the future that @@ -63,6 +93,64 @@ public string? Name } } + /// + /// Regulatory information, for numbers whose requirements list is not empty. + /// Get the list with `GET /v1/phone-numbers/requirements?phoneNumber=...` and + /// send one entry per requirement id, except `action` requirements, which take + /// no value. Every required id must be present, once, and no unknown id may + /// be sent; otherwise the purchase is refused with `400 invalid_request` before + /// anything is charged. + /// + /// The information is kept for your project under the number's country + /// and `type`. A later purchase there may omit this field if what is kept still + /// covers that number's requirements. Omit it for numbers without requirements. + /// + public IReadOnlyList? RegulatoryRequirements + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableStruct>( + "regulatoryRequirements" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "regulatoryRequirements", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + + /// + /// Type of phone number. `mobile` is stocked in countries where no geographic + /// (`local`) or non-geographic (`national`) inventory exists, and in several + /// markets it is the only type that can receive SMS. + /// + public ApiEnum? Type + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass>("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("type", value); + } + } + public PhoneNumberPurchaseParams() { } #pragma warning disable CS8618 @@ -172,3 +260,81 @@ public override int GetHashCode() return 0; } } + +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class RegulatoryRequirement : JsonModel +{ + /// + /// Depends on the requirement's `type`: the text itself for `textual`; for `address`, + /// the `id` of an address created in this project with `POST /v1/addresses`; + /// for `document`, the `id` of a document created with `POST /v1/documents`. + /// An address or document from another project, or one rejected in review, is refused. + /// + public required string FieldValue + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("fieldValue"); + } + init { this._rawData.Set("fieldValue", value); } + } + + /// + /// A `requirementTypes[].id` from `GET /v1/phone-numbers/requirements`. Each + /// id may appear only once. + /// + public required string RequirementType + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("requirementType"); + } + init { this._rawData.Set("requirementType", value); } + } + + /// + public override void Validate() + { + _ = this.FieldValue; + _ = this.RequirementType; + } + + public RegulatoryRequirement() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public RegulatoryRequirement(RegulatoryRequirement regulatoryRequirement) + : base(regulatoryRequirement) { } +#pragma warning restore CS8618 + + public RegulatoryRequirement(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + RegulatoryRequirement(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static RegulatoryRequirement FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class RegulatoryRequirementFromRaw : IFromRawJson +{ + /// + public RegulatoryRequirement FromRawUnchecked( + IReadOnlyDictionary rawData + ) => RegulatoryRequirement.FromRawUnchecked(rawData); +} diff --git a/src/Zavudev/Models/PhoneNumbers/PhoneNumberRequirementsParams.cs b/src/Zavudev/Models/PhoneNumbers/PhoneNumberRequirementsParams.cs index bc2229f..29338c5 100644 --- a/src/Zavudev/Models/PhoneNumbers/PhoneNumberRequirementsParams.cs +++ b/src/Zavudev/Models/PhoneNumbers/PhoneNumberRequirementsParams.cs @@ -9,9 +9,20 @@ namespace Zavudev.Models.PhoneNumbers; /// -/// Get regulatory requirements for purchasing phone numbers in a specific country. -/// Some countries require additional documentation (addresses, identity documents) -/// before phone numbers can be activated. +/// Get the regulatory information needed to buy a phone number, for one specific +/// number or for a country and number type. Prefer `phoneNumber`: the response is +/// then exactly the list the purchase of that number validates against. Pass each +/// `requirementTypes[].id` back as `requirementType` in `regulatoryRequirements` +/// on `POST /v1/phone-numbers`. +/// +/// For `phoneNumber`, the requirements of that exact number are returned. +/// When they cannot be resolved for the number itself, the list for its country +/// and `type` is returned instead, and the purchase uses the same list. An empty +/// `items` array means the number needs no regulatory information. If the requirements +/// cannot be retrieved at all, the response is `502 requirements_unavailable`, never +/// an empty list. +/// +/// URL-encode the `+` of `phoneNumber` as `%2B`. An unencoded `+` is also accepted. /// /// NOTE: Do not inherit from this type outside the SDK unless you're okay with /// breaking changes in non-major versions. We may add new methods in the future that @@ -20,20 +31,53 @@ namespace Zavudev.Models.PhoneNumbers; public record class PhoneNumberRequirementsParams : ParamsBase { /// - /// Two-letter ISO country code. + /// Two-letter ISO country code. Required unless `phoneNumber` is given. /// - public required string CountryCode + public string? CountryCode { get { this._rawQueryData.Freeze(); - return this._rawQueryData.GetNotNullClass("countryCode"); + return this._rawQueryData.GetNullableClass("countryCode"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("countryCode", value); + } + } + + /// + /// E.164 number from `GET /v1/phone-numbers/available`, with `+` encoded as `%2B`. + /// Returns the requirements the purchase of that number checks. Takes precedence + /// over `countryCode`. + /// + public string? PhoneNumber + { + get + { + this._rawQueryData.Freeze(); + return this._rawQueryData.GetNullableClass("phoneNumber"); + } + init + { + if (value == null) + { + return; + } + + this._rawQueryData.Set("phoneNumber", value); } - init { this._rawQueryData.Set("countryCode", value); } } /// - /// Type of phone number (local, mobile, tollFree). + /// Type of phone number (local, national, mobile, tollFree). Defaults to `local`. + /// With `phoneNumber`, used only when the number's own requirements cannot be + /// resolved and the country list is returned. /// public ApiEnum? Type { diff --git a/src/Zavudev/Models/PhoneNumbers/PhoneNumberStatus.cs b/src/Zavudev/Models/PhoneNumbers/PhoneNumberStatus.cs index 824fad7..7637ada 100644 --- a/src/Zavudev/Models/PhoneNumbers/PhoneNumberStatus.cs +++ b/src/Zavudev/Models/PhoneNumbers/PhoneNumberStatus.cs @@ -5,12 +5,20 @@ namespace Zavudev.Models.PhoneNumbers; +/// +/// Billing state of an owned number, separate from `regulatoryStatus`. `pending` +/// is legacy and is not written to numbers today. The SDKs carry `active`, `suspended` +/// and `pending` only; `releasing` and `released` are returned by the REST API until +/// their next release. +/// [JsonConverter(typeof(PhoneNumberStatusConverter))] public enum PhoneNumberStatus { Active, Suspended, Pending, + Releasing, + Released, } sealed class PhoneNumberStatusConverter : JsonConverter @@ -26,6 +34,8 @@ JsonSerializerOptions options "active" => PhoneNumberStatus.Active, "suspended" => PhoneNumberStatus.Suspended, "pending" => PhoneNumberStatus.Pending, + "releasing" => PhoneNumberStatus.Releasing, + "released" => PhoneNumberStatus.Released, _ => (PhoneNumberStatus)(-1), }; } @@ -43,6 +53,8 @@ JsonSerializerOptions options PhoneNumberStatus.Active => "active", PhoneNumberStatus.Suspended => "suspended", PhoneNumberStatus.Pending => "pending", + PhoneNumberStatus.Releasing => "releasing", + PhoneNumberStatus.Released => "released", _ => throw new ZavudevInvalidDataException( string.Format("Invalid value '{0}' in {1}", value, nameof(value)) ), diff --git a/src/Zavudev/Models/PhoneNumbers/PhoneNumberUpdateParams.cs b/src/Zavudev/Models/PhoneNumbers/PhoneNumberUpdateParams.cs index 5ba796c..8ce36d9 100644 --- a/src/Zavudev/Models/PhoneNumbers/PhoneNumberUpdateParams.cs +++ b/src/Zavudev/Models/PhoneNumbers/PhoneNumberUpdateParams.cs @@ -40,7 +40,9 @@ public string? Name } /// - /// Sender ID to assign the phone number to. Set to null to unassign. + /// Sender ID to assign the phone number to. Set to null to unassign. A number + /// under regulatory review is recorded now and connected to the sender when approved; + /// a rejected number is refused. /// public string? SenderID { diff --git a/src/Zavudev/Models/PhoneNumbers/Requirement.cs b/src/Zavudev/Models/PhoneNumbers/Requirement.cs index 39cbd2a..cf598a6 100644 --- a/src/Zavudev/Models/PhoneNumbers/Requirement.cs +++ b/src/Zavudev/Models/PhoneNumbers/Requirement.cs @@ -9,7 +9,9 @@ namespace Zavudev.Models.PhoneNumbers; /// -/// A group of requirements for a specific country/phone type combination. +/// The requirements for ordering a number: for a country and number type, or for +/// one specific number when requested with `phoneNumber` (then `id` is that phone +/// number and `countryCode` is taken from it). /// [JsonConverter(typeof(JsonModelConverter))] public sealed record class Requirement : JsonModel diff --git a/src/Zavudev/Models/PhoneNumbers/RequirementType.cs b/src/Zavudev/Models/PhoneNumbers/RequirementType.cs index c997e99..6db9129 100644 --- a/src/Zavudev/Models/PhoneNumbers/RequirementType.cs +++ b/src/Zavudev/Models/PhoneNumbers/RequirementType.cs @@ -13,6 +13,9 @@ namespace Zavudev.Models.PhoneNumbers; [JsonConverter(typeof(JsonModelConverter))] public sealed record class RequirementType : JsonModel { + /// + /// Send this as `requirementType` in `regulatoryRequirements` when purchasing. + /// public required string ID { get diff --git a/src/Zavudev/Models/Senders/Agent/Flows/FlowTrigger.cs b/src/Zavudev/Models/Senders/Agent/Flows/FlowTrigger.cs index 9e46f7c..83e7385 100644 --- a/src/Zavudev/Models/Senders/Agent/Flows/FlowTrigger.cs +++ b/src/Zavudev/Models/Senders/Agent/Flows/FlowTrigger.cs @@ -14,7 +14,13 @@ namespace Zavudev.Models.Senders.Agent.Flows; public sealed record class FlowTrigger : JsonModel { /// - /// Type of trigger for a flow. + /// What starts a flow. + /// + /// - `keyword`: the message contains one of the words listed in `keywords`. + /// Plain substring matching, so a word inside another word still counts. - `intent`: + /// the message MEANS what `intent` describes, whatever words it uses. - `always`: + /// any message starts it. - `manual`: reserved. Nothing starts a `manual` flow + /// today — it is accepted and stored, and no message or endpoint runs it. /// public required ApiEnum Type { @@ -27,7 +33,17 @@ public required ApiEnum Type } /// - /// Intent that triggers the flow (for intent type). + /// One plain sentence describing what the contact wants, for `intent` triggers. + /// Any language. + /// + /// The message is judged for meaning, not for words, so "kiero saber el + /// presio" starts a flow whose intent is "quiere saber precios o cotizar", and + /// "no quiero info de precios" starts nothing. + /// + /// A `keyword` or `always` flow with a higher `priority` is matched first + /// and wins. At most 12 intent flows are considered per message, highest priority + /// first. When the classification is unavailable or uncertain, the message is + /// handled as if no intent matched, so a flow never starts on a guess. /// public string? Intent { @@ -48,7 +64,9 @@ public string? Intent } /// - /// Keywords that trigger the flow (for keyword type). + /// Words that start the flow, for `keyword` triggers. Matched as substrings, + /// case-insensitively, against the whole message: a flow on `info` also starts + /// on "no quiero info". Use `intent` when that matters. /// public IReadOnlyList? Keywords { @@ -122,7 +140,13 @@ public FlowTrigger FromRawUnchecked(IReadOnlyDictionary raw } /// -/// Type of trigger for a flow. +/// What starts a flow. +/// +/// - `keyword`: the message contains one of the words listed in `keywords`. +/// Plain substring matching, so a word inside another word still counts. - `intent`: +/// the message MEANS what `intent` describes, whatever words it uses. - `always`: +/// any message starts it. - `manual`: reserved. Nothing starts a `manual` flow today +/// — it is accepted and stored, and no message or endpoint runs it. /// [JsonConverter(typeof(FlowTriggerTypeConverter))] public enum FlowTriggerType diff --git a/src/Zavudev/Models/Senders/Sender.cs b/src/Zavudev/Models/Senders/Sender.cs index d6f9706..c452bc8 100644 --- a/src/Zavudev/Models/Senders/Sender.cs +++ b/src/Zavudev/Models/Senders/Sender.cs @@ -46,10 +46,12 @@ public required string PhoneNumber } /// - /// Channels this sender can actually send on right now, computed from its configuration. + /// Channels this sender can actually send on right now: configured AND activated. /// Empty means the sender cannot send or receive anything yet: a phoneNumber - /// alone does not enable SMS or voice. Check this rather than inferring capability - /// from phoneNumber or emailAddress. + /// alone does not enable SMS or voice, and a connected account that is not activated + /// is left out, because every send on it is refused. Check this rather than inferring + /// capability from phoneNumber or emailAddress, and turn a connected channel + /// on with `POST /v1/senders/{senderId}/channels/{channel}/activate`. /// public IReadOnlyList? Channels { diff --git a/src/Zavudev/Models/Senders/SenderCreateParams.cs b/src/Zavudev/Models/Senders/SenderCreateParams.cs index 28dee3d..8d67ab3 100644 --- a/src/Zavudev/Models/Senders/SenderCreateParams.cs +++ b/src/Zavudev/Models/Senders/SenderCreateParams.cs @@ -104,8 +104,10 @@ public string? EmailFromName } /// - /// Enable inbound email receiving on this sender. Requires a verified MX record - /// on the domain; ignored otherwise. + /// Enable inbound email receiving on this sender. Requires a verified inbound + /// MX record on the domain; the request is ignored otherwise. Read `emailReceivingEnabled` + /// back off the response to see whether it was applied — it comes back `false` + /// when the MX has not verified. /// public bool? EmailReceivingEnabled { @@ -176,8 +178,10 @@ public bool? EnableVoice /// Phone number in E.164 format, and it must be a number your project already /// owns (see `GET /v1/phone-numbers`). The number is routed to the sender as /// part of this call, which is what turns the SMS channel on. Passing a number - /// the project does not own, or one already attached to another sender, returns - /// 400 rather than creating a sender that cannot send. Omit for an email-only sender. + /// the project does not own, one already attached to another sender, or one + /// rejected in regulatory review returns 400 rather than creating a sender that + /// cannot send. A number still under review is attached and starts carrying + /// messages when it is approved. Omit for an email-only sender. /// public string? PhoneNumber { diff --git a/src/Zavudev/Models/Senders/SenderUpdateParams.cs b/src/Zavudev/Models/Senders/SenderUpdateParams.cs index 646c795..a448603 100644 --- a/src/Zavudev/Models/Senders/SenderUpdateParams.cs +++ b/src/Zavudev/Models/Senders/SenderUpdateParams.cs @@ -118,7 +118,10 @@ public string? EmailFromName } /// - /// Enable or disable inbound email receiving for this sender. + /// Enable or disable inbound email receiving for this sender. Enabling requires + /// a verified inbound MX record on the domain; the request is ignored otherwise, + /// and `emailReceivingEnabled` comes back `false` on the response. Disabling + /// always applies. /// public bool? EmailReceivingEnabled { diff --git a/src/Zavudev/Models/Senders/WebhookEvent.cs b/src/Zavudev/Models/Senders/WebhookEvent.cs index f3103b5..119d6a2 100644 --- a/src/Zavudev/Models/Senders/WebhookEvent.cs +++ b/src/Zavudev/Models/Senders/WebhookEvent.cs @@ -41,15 +41,26 @@ namespace Zavudev.Models.Senders; /// contact. `data` carries `conversationId` (the inbox thread id — deep-link with /// `https://dashboard.zavu.dev/{locale}/inbox?conv={conversationId}`), the `phoneNumber` /// or `email` key, `channel`, `firstMessageId`, `firstMessageText`, and `profileName`. -/// - `template.status_changed`: WhatsApp template approval status changed +/// - `template.status_changed`: WhatsApp template approval status changed. `data` +/// carries `templateId`, `name`, `previousStatus`, `currentStatus`, `rejectionReason`, +/// and `category` — the category Meta currently bills the template under. Meta can +/// recategorize a template (typically `UTILITY` to `MARKETING`) at approval or long +/// afterwards, which changes what each message costs; `category` is how that reaches +/// you. A recategorization with no status change is delivered as this same event, +/// so compare `category` against what you hold rather than only reacting to `currentStatus`. /// -/// **Partner events:** - `invitation.status_changed`: A partner invitation -/// status changed (pending, in_progress, completed, cancelled, failed). `data` carries -/// `invitationId`, `clientName`, `clientEmail`, `connectionType` (`whatsapp_waba` -/// or `messenger`), `previousStatus`, and `currentStatus`. On `completed` it also -/// carries `senderId` and `connectedAccount` (`channel`, `id`, `name`) — the WhatsApp -/// number or Facebook Page that was linked. On `failed` it carries `failureReason`; -/// the invitation link stays usable, so a client can retry it. +/// **Partner events:** - `invitation.status_changed`: A partner invitation's +/// stored status changed: to `in_progress`, `completed`, `failed`, `cancelled`, or +/// back to `pending` when it is resent from the dashboard. A change to the same status +/// sends nothing, and expiry is not a stored change, so no event is sent when an +/// invitation expires. Delivered to the project webhook (`POST /v1/invitations/webhook`) +/// of the project that created the invitation; a parent project does not receive +/// its sub-accounts' events. `data` carries `invitationId`, `clientName`, `clientEmail`, +/// `connectionType` (`whatsapp_waba` or `messenger`), `previousStatus`, and `currentStatus`. +/// On `completed` it also carries `senderId`, `connectedAccount` (`channel`, `id`, +/// `name`) — the WhatsApp number or Facebook Page that was linked — and, for WhatsApp, +/// `wabaAccountId`. On `failed` it carries `failureReason`; the invitation link +/// stays usable, so a client can retry it. /// /// **Voice Agent events:** For every voice event, `data` carries `callId`, /// `direction`, `from`, `to`, `status`, `durationSeconds`, `endReason`, and `transcriptAvailable`. diff --git a/src/Zavudev/Models/Templates/TemplateSyncParams.cs b/src/Zavudev/Models/Templates/TemplateSyncParams.cs index c8ac37e..5c7d338 100644 --- a/src/Zavudev/Models/Templates/TemplateSyncParams.cs +++ b/src/Zavudev/Models/Templates/TemplateSyncParams.cs @@ -10,10 +10,18 @@ namespace Zavudev.Models.Templates; /// -/// Reconcile this project's templates against WhatsApp. Two things happen per connected -/// WhatsApp Business Account: templates that exist on Meta but not in Zavu are imported -/// (or linked to an existing template with the same name), and the approval status -/// of the templates Zavu already knows about is refreshed from Meta. +/// Reconcile this project's templates against WhatsApp. Three things happen per +/// connected WhatsApp Business Account: templates that exist on Meta but not in +/// Zavu are imported (or linked to an existing template with the same name), the +/// approval status of the templates Zavu already knows about is refreshed from Meta, +/// and their **category** is refreshed from Meta. +/// +/// The category matters because it is what each message is billed under, and +/// Meta reassigns it on its own — commonly `UTILITY` to `MARKETING`, on a template +/// that is already approved and whose status therefore never moves. A template whose +/// category changed but whose status did not is still counted in `updated`. This +/// is the way to repair templates whose category drifted before you started listening +/// for `template.status_changed`. /// /// This is what to call when a template was created outside Zavu — in Meta /// Business Manager, or by another tool — or when a `template.status_changed` webhook diff --git a/src/Zavudev/Models/Templates/TemplateSyncResponse.cs b/src/Zavudev/Models/Templates/TemplateSyncResponse.cs index 63b698a..b230954 100644 --- a/src/Zavudev/Models/Templates/TemplateSyncResponse.cs +++ b/src/Zavudev/Models/Templates/TemplateSyncResponse.cs @@ -86,7 +86,8 @@ public required long Skipped } /// - /// Templates whose approval status changed to match Meta. + /// Templates brought back in line with Meta — approval status, category, or + /// both. A template whose status and category both moved is counted once. /// public required long Updated { diff --git a/src/Zavudev/Services/IAddressService.cs b/src/Zavudev/Services/IAddressService.cs index 07f6d5a..27c4619 100644 --- a/src/Zavudev/Services/IAddressService.cs +++ b/src/Zavudev/Services/IAddressService.cs @@ -27,8 +27,9 @@ public interface IAddressService IAddressService WithOptions(Func modifier); /// - /// Create a regulatory address for phone number purchases. Some countries require a - /// verified address before phone numbers can be activated. + /// Create a regulatory address, to use as the value of an `address` requirement + /// when buying a phone number. It is registered for review when it is created, with + /// status `pending`. /// Task Create( AddressCreateParams parameters, @@ -59,7 +60,9 @@ Task List( ); /// - /// Delete a regulatory address. Cannot delete addresses that are in use. + /// Delete a regulatory address from this project. Any address can be deleted, + /// whatever its status. Phone numbers already purchased with it are not affected, + /// and neither is information already submitted for later purchases in its country. /// Task Delete(AddressDeleteParams parameters, CancellationToken cancellationToken = default); diff --git a/src/Zavudev/Services/IBroadcastService.cs b/src/Zavudev/Services/IBroadcastService.cs index ac4ad51..4e3e43d 100644 --- a/src/Zavudev/Services/IBroadcastService.cs +++ b/src/Zavudev/Services/IBroadcastService.cs @@ -170,17 +170,11 @@ Task RetryReview( /// /// Start sending the broadcast immediately or schedule for later. /// - /// **The account must be past the unverified level to send, except on - /// WhatsApp.** An account that has verified nothing is refused with `403` and code - /// `kyc_required` on every channel other than `whatsapp`. Any one of these lifts - /// it: identity verification (KYC), a saved payment method, a settled deposit, or a - /// paid plan. Business verification (KYB) is not required to broadcast; it gates - /// 10DLC registration only. A `whatsapp` broadcast is exempt: it can only be built - /// on a template, and Meta vets the business and the content when it approves that - /// template, so an unapproved template is refused instead. `smart` is not exempt, - /// since it can route a contact to SMS or email. Drafts can be created, edited and - /// kept without any check. Every send path (dashboard, API and CLI) enforces the - /// same rule. + /// **Sending a broadcast needs no account verification.** Identity and + /// business verification raise daily ceilings; neither is a permission to + /// broadcast, on any channel. What stands in front of a broadcast is the content + /// review below, and it applies to every send path — dashboard, API and CLI. Drafts + /// can be created, edited and kept freely. /// /// **Daily ceilings apply per recipient.** Each message a broadcast sends /// counts against the channel's daily ceiling (see `POST /v1/messages`). Once the @@ -191,9 +185,14 @@ Task RetryReview( /// submitted to automated content review here; it does not go straight out. A /// WhatsApp broadcast built on a Meta-approved template skips review (Meta already /// vetted the content) and begins sending. An email broadcast sends as soon as the - /// automated review passes. Every other channel moves to `pending_admin_review` and - /// waits for a person. If the review rejects it, use PATCH to edit the content then - /// call POST /retry-review. + /// review passes it, unless the review asks for a person. Every other channel moves + /// to `pending_admin_review` and waits for a person. A broadcast the review refuses + /// lands on `rejected`: use PATCH to edit the content then call POST /retry-review, + /// or escalate it for a manual review. + /// + /// A broadcast is read once, on its own text, rather than per recipient. + /// While an account's sending is suspended its recipients fail individually with + /// `errorCode` `SENDING_SUSPENDED`; see `POST /v1/messages`. /// /// Calling this on a broadcast that is already `approved` or `scheduled` /// sends or reschedules it directly, since it has already been reviewed. Reserves diff --git a/src/Zavudev/Services/ICallService.cs b/src/Zavudev/Services/ICallService.cs index a9db150..03107f9 100644 --- a/src/Zavudev/Services/ICallService.cs +++ b/src/Zavudev/Services/ICallService.cs @@ -33,16 +33,16 @@ public interface ICallService /// real-time interruption handling). /// /// **Requirements:** - The Voice Agents feature must be enabled for your team - /// (otherwise `403`). - An account that has verified nothing may only call the - /// phone numbers the project has verified (`403` with code - /// `destination_not_verified`, and `details.verifiedNumbers` lists them), and at - /// most 5 calls a day (`429` with code `daily_limit_exceeded`). A number is - /// verified from the dashboard's Sandbox screen by sending the pre-filled WhatsApp - /// message from that phone; the same verification covers SMS and calls. Verify your - /// identity, add a payment method, settle a deposit or subscribe to call any - /// destination. That raises the ceiling to 50 calls a day on Free; paid plans have - /// no daily call ceiling. Full reference: - /// https://docs.zavu.dev/concepts/sending-limits - The sender's agent must have + /// (otherwise `403`). - An account may call any destination from its first minute, + /// within the daily ceiling: 5 calls a day for an account that has verified + /// nothing, 50 a day on Free once it has verified its identity, added a payment + /// method, settled a deposit or subscribed. Paid plans have no daily call ceiling. + /// Over it, `429` with code `daily_limit_exceeded`. Full reference: + /// https://docs.zavu.dev/concepts/sending-limits - The call is read by Zavu's + /// automated risk review before it is dialed, the way a message is (see `POST + /// /v1/messages`). A call is never held for a person: one the review stops is + /// failed rather than placed late. Repeated refusals suspend the account's calling, + /// answered with `403` and code `sending_suspended`. - The sender's agent must have /// `voice.enabled` set to `true`. - Not available with test-mode API keys. /// /// **Billing:** Voice calls are billed per minute of connected time plus diff --git a/src/Zavudev/Services/IMessageService.cs b/src/Zavudev/Services/IMessageService.cs index bebcd35..8e55022 100644 --- a/src/Zavudev/Services/IMessageService.cs +++ b/src/Zavudev/Services/IMessageService.cs @@ -97,42 +97,65 @@ Task React( /// template messages to initiate conversations outside the window /// /// **Plan allowances and email billing:** - WhatsApp, Telegram, Instagram and - /// Messenger share an allowance of 2,000 messages per month on Free. Over it, sends - /// return 429 with code `a2p_limit_exceeded` and upgrade details; the counter - /// resets on the 1st of each month. Paid plans have no message caps - Email is - /// billed from your prepaid balance in 1,000-message blocks: $0.40 per 1,000 - /// transactional emails, $0.80 per 1,000 marketing (broadcast) emails. A block is - /// charged when your monthly count crosses each 1,000 boundary, and at zero balance - /// email sends return 402 with code `insufficient_balance`. Free teams start with - /// $2 of credit and additionally cap at 3,000 emails/month and 100/day. Teams on - /// earlier plans keep their original email quotas instead - SMS and voice are - /// billed per message from your balance on every plan + /// Messenger share an allowance of 2,000 messages per month on Free. **It counts + /// messages in both directions**: a message a contact sends you consumes one unit + /// exactly as a message you send them does, so a project that has sent 300 and + /// received 1,700 has used the whole allowance. Messages you send from the WhatsApp + /// Business App on your own phone under coexistence are mirrored into your inbox + /// but never counted, and neither are failed sends. Over the allowance, sends + /// return 429 with code `a2p_limit_exceeded` and upgrade details, **and inbound + /// messages on those channels are refused as well**: not stored, not shown in the + /// inbox, and no `message.inbound` webhook, and not delivered later when the month + /// resets. The counter resets on the 1st of each month. Paid plans have no message + /// caps - Email is billed from your prepaid balance in 1,000-message blocks: $0.40 + /// per 1,000 transactional emails, $0.80 per 1,000 marketing (broadcast) emails. A + /// block is charged when your monthly count crosses each 1,000 boundary, and at + /// zero balance email sends return 402 with code `insufficient_balance`. Free teams + /// start with $2 of credit and additionally cap at 3,000 emails/month and 100/day. + /// Teams on earlier plans keep their original email quotas instead - SMS and voice + /// are billed per message from your balance on every plan /// - /// **Account verification and daily limits:** - A brand-new account can send - /// on every channel immediately, but `sms`, `sms_oneway` and `voice` reach only the - /// phone numbers the project has verified. Sending elsewhere returns `403` with - /// code `destination_not_verified`; `details.verifiedNumbers` lists the numbers - /// that are reachable. A number is verified from the dashboard's Sandbox screen: - /// generate a code and send the pre-filled WhatsApp message from that phone to - /// Zavu's sandbox number. One verification covers WhatsApp, SMS and calls, up to 5 - /// numbers per project. To send to any destination, do any one of these: verify - /// your identity, add a payment method, settle a deposit, or subscribe to a paid - /// plan. Business verification (KYB) is never required to send - Daily ceilings - /// apply per channel group and rise with verification. An account that has verified - /// nothing: 25/day across `sms` + `sms_oneway`, 5/day for `voice`, 100/day across - /// WhatsApp, Telegram, Instagram and Messenger combined. Past that floor: 200/day - /// for SMS, or 10,000/day once identity or business verification is approved (or a - /// higher limit agreed for your account); 50/day voice and 250/day conversational - /// on Free. **Paid plans have no voice or conversational daily ceiling.** Over a - /// ceiling, sends return `429` with code `daily_limit_exceeded` and - /// `details.limit`; the count resets at 00:00 UTC - The daily ceiling never reduces - /// the monthly allowance: 100/day on the conversational group still reaches the - /// 2,000 monthly A2P messages Free includes - Email needs no account verification - /// here: a sender with a verified domain sends from day one, within the plan quota - /// (100/day and 3,000/month on Free). Over the daily quota it returns `429` with - /// code `daily_limit_exceeded`. Email broadcasts are the exception: they need the - /// account past the unverified level, see `POST /v1/broadcasts/{broadcastId}/send` - /// - Full reference: https://docs.zavu.dev/concepts/sending-limits + /// **Daily limits:** - An account sends on every channel from its first + /// minute, to any destination. Verification is not a permission to send: identity + /// verification and business verification (KYB) raise the ceilings below and + /// nothing else asks for them here. KYB is still required to register a 10DLC brand + /// and campaign, which every US and Canadian (+1) SMS destination needs — a carrier + /// rule, answered separately with `403 ten_dlc_required` - Daily ceilings apply per + /// channel group and rise with verification. An account that has verified nothing: + /// 25/day across `sms` + `sms_oneway`, 5/day for `voice`, 100/day across WhatsApp, + /// Telegram, Instagram and Messenger combined. Past that floor: 200/day for SMS, or + /// 10,000/day once identity or business verification is approved (or a higher limit + /// agreed for your account); 50/day voice and 250/day conversational on Free. + /// **Paid plans have no voice or conversational daily ceiling.** Over a ceiling, + /// sends return `429` with code `daily_limit_exceeded` and `details.limit`; the + /// count resets at 00:00 UTC - The daily ceiling never reduces the monthly + /// allowance: 100/day on the conversational group still reaches the 2,000 monthly + /// A2P messages Free includes - Email: a sender with a verified domain sends from + /// day one, within the plan quota (100/day and 3,000/month on Free). Over the daily + /// quota it returns `429` with code `daily_limit_exceeded` - Full reference: + /// https://docs.zavu.dev/concepts/sending-limits + /// + /// **Risk review:** Every outbound `sms`, `sms_oneway`, `email` and `voice` + /// message is read before it is sent — the content, and how this account has been + /// sending. What is checked is the message, not who you are. - A message can be + /// **held** for a short review. It stays `queued` while it waits: no new status + /// exists for this, and `MessageStatus` is unchanged. When it is approved it sends + /// normally. - A message that is not approved moves to `failed` and fires + /// `message.failed`. `errorCode` says which happened: `RISK_REJECTED` (a reviewer + /// refused it), `RISK_REVIEW_EXPIRED` (the review window closed first — it is a + /// couple of hours, because a code that arrives late is worse than one that does + /// not arrive), or `RISK_BLOCKED` (refused outright, without a hold). An SMS that + /// fails this way is not charged; the prepaid amount is returned. - A call is never + /// held. `POST /v1/calls` fails a call the review stops rather than placing it + /// hours late. - A message whose content cannot be read — the check is briefly + /// unavailable — is held rather than sent. An account with an approved business + /// verification is unaffected, and so is one that has verified something, already + /// sends real traffic, and has a clean recent record. - Repeated refusals suspend + /// an account's sending. While it is suspended every send is refused with `403` and + /// code `sending_suspended`, `details.dashboardUrl` points at support, and a + /// message already queued fails with `errorCode` `SENDING_SUSPENDED`. - A broadcast + /// is read once, on the broadcast itself, rather than per recipient — see `POST + /// /v1/broadcasts/{broadcastId}/send`. /// /// **Email recipient pre-flight:** Email messages are validated automatically /// before dispatch. Sends that would be a guaranteed hard bounce are failed instead diff --git a/src/Zavudev/Services/IPhoneNumberService.cs b/src/Zavudev/Services/IPhoneNumberService.cs index 31f4435..bd113e1 100644 --- a/src/Zavudev/Services/IPhoneNumberService.cs +++ b/src/Zavudev/Services/IPhoneNumberService.cs @@ -66,11 +66,41 @@ Task List( /// /// Purchase an available phone number. Requires a paid plan: the Free plan cannot - /// purchase phone numbers and receives `402` with code `paid_plan_required`. Paid - /// plans include one US number at no charge. The included number is one per account - /// and is granted once: claiming it spends the benefit for good, so releasing that - /// number does not make another one free, and numbers the account already bought do - /// not consume it. + /// purchase phone numbers and receives `402` with code `paid_plan_required`. + /// + /// **The included number.** A paid plan includes one number at no charge, + /// once per account: it must be a US or Canadian number (a +1 number) costing $20 a + /// month or less. `isFreeEligible` in `GET /v1/phone-numbers/available` marks the + /// numbers that qualify. Claiming it spends the benefit for good, across every team + /// the account owner owns, so releasing that number does not make another one free. + /// + /// **Numbers with regulatory requirements.** Which numbers need regulatory + /// information is decided per number, not by a fixed country list. The purchase + /// looks the requirements up for the exact number before charging anything: + /// + /// 1. `GET /v1/phone-numbers/requirements?phoneNumber=...`. If `items` is + /// empty, buy normally. 2. Create what it asks for: addresses with `POST + /// /v1/addresses`, documents with `POST /v1/documents`. 3. Purchase with `type` and + /// `regulatoryRequirements`. The number is bought and billed at once with + /// `regulatoryStatus: pending_review`. 4. Poll `GET + /// /v1/phone-numbers/{phoneNumberId}` until `regulatoryStatus` is `approved`. + /// Assign it to a sender before or after approval; it starts carrying messages once + /// approved. + /// + /// **Reuse.** Information you submitted is kept for your project, per country + /// and `type`, and a later purchase there may omit `regulatoryRequirements`. Reuse + /// only happens when what is kept still covers every requirement of the new number + /// and every address and document in it belongs to the project. Otherwise, or when + /// nothing is kept, the purchase returns `400 regulatory_compliance_required` with + /// the missing requirements in `details`. + /// + /// Invalid values (a missing, unknown or repeated requirement id, an address + /// or document from another project, or one rejected in review) return `400 + /// invalid_request`. If an address or document cannot be registered for review, the + /// purchase returns `400 invalid_request` naming the requirement. If the + /// requirements cannot be looked up, the purchase returns `502 + /// requirements_unavailable`, except for US and Canadian numbers, which are sold as + /// numbers without requirements. None of these errors charge anything. /// Task Purchase( PhoneNumberPurchaseParams parameters, @@ -93,12 +123,24 @@ Task Release( ); /// - /// Get regulatory requirements for purchasing phone numbers in a specific country. - /// Some countries require additional documentation (addresses, identity documents) - /// before phone numbers can be activated. + /// Get the regulatory information needed to buy a phone number, for one specific + /// number or for a country and number type. Prefer `phoneNumber`: the response is + /// then exactly the list the purchase of that number validates against. Pass each + /// `requirementTypes[].id` back as `requirementType` in `regulatoryRequirements` on + /// `POST /v1/phone-numbers`. + /// + /// For `phoneNumber`, the requirements of that exact number are returned. + /// When they cannot be resolved for the number itself, the list for its country and + /// `type` is returned instead, and the purchase uses the same list. An empty + /// `items` array means the number needs no regulatory information. If the + /// requirements cannot be retrieved at all, the response is `502 + /// requirements_unavailable`, never an empty list. + /// + /// URL-encode the `+` of `phoneNumber` as `%2B`. An unencoded `+` is also + /// accepted. /// Task Requirements( - PhoneNumberRequirementsParams parameters, + PhoneNumberRequirementsParams? parameters = null, CancellationToken cancellationToken = default ); @@ -192,10 +234,10 @@ Task Release( /// /// Returns a raw HTTP response for get /v1/phone-numbers/requirements, but is otherwise the - /// same as . + /// same as . /// Task> Requirements( - PhoneNumberRequirementsParams parameters, + PhoneNumberRequirementsParams? parameters = null, CancellationToken cancellationToken = default ); diff --git a/src/Zavudev/Services/ITemplateService.cs b/src/Zavudev/Services/ITemplateService.cs index fc71bb7..7764c4f 100644 --- a/src/Zavudev/Services/ITemplateService.cs +++ b/src/Zavudev/Services/ITemplateService.cs @@ -88,11 +88,18 @@ Task