From 47de211deff2c722fe0158faedbf35690434fcdc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 02:43:31 +0000 Subject: [PATCH 01/20] feat(api): api update --- .stats.yml | 4 +- .../Functions/FunctionCreateParamsTest.cs | 55 ++++++++++++++++++ .../Functions/FunctionDeployParamsTest.cs | 55 ++++++++++++++++++ .../Functions/FunctionUpdateParamsTest.cs | 55 ++++++++++++++++++ .../Models/Functions/FunctionCreateParams.cs | 56 ++++++++++++++++++- .../Models/Functions/FunctionDeployParams.cs | 56 ++++++++++++++++++- .../Functions/FunctionDeployResponse.cs | 6 ++ .../FunctionGetDeploymentResponse.cs | 6 ++ .../Models/Functions/FunctionUpdateParams.cs | 56 ++++++++++++++++++- 9 files changed, 344 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 754d90b..484237c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-86aec85be1021cea51f9fc0bb95248276c8b85e3ac0fd810490114599ed7cf00.yml -openapi_spec_hash: 6b119a3213e324f3826453deca57f10b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-21db15ab8c7fc4a95f96e62e9ae84d4db9696f41adf62806c9be4043268b0fad.yml +openapi_spec_hash: f5928b2d6fc0d6f10ea63acac9dead60 config_hash: 7fca08d9dae18513f5d7451cea11e34c diff --git a/src/Zavudev.Tests/Models/Functions/FunctionCreateParamsTest.cs b/src/Zavudev.Tests/Models/Functions/FunctionCreateParamsTest.cs index e34773e..8ecbd83 100644 --- a/src/Zavudev.Tests/Models/Functions/FunctionCreateParamsTest.cs +++ b/src/Zavudev.Tests/Models/Functions/FunctionCreateParamsTest.cs @@ -18,6 +18,18 @@ public void FieldRoundtrip_Works() Slug = "order-bot", Dependencies = new Dictionary() { { "openai", "^4.20.0" } }, Description = "Replies to order status questions on WhatsApp.", + Entrypoint = "index.ts", + Files = new Dictionary() + { + { + "index.ts", + "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n" + }, + { + "lib/orders.ts", + "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n" + }, + }, HttpEnabled = true, MemoryMB = MemoryMB.V128, Runtime = Runtime.Nodejs24, @@ -30,6 +42,18 @@ public void FieldRoundtrip_Works() string expectedSlug = "order-bot"; Dictionary expectedDependencies = new() { { "openai", "^4.20.0" } }; string expectedDescription = "Replies to order status questions on WhatsApp."; + string expectedEntrypoint = "index.ts"; + Dictionary expectedFiles = new() + { + { + "index.ts", + "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n" + }, + { + "lib/orders.ts", + "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n" + }, + }; bool expectedHttpEnabled = true; ApiEnum expectedMemoryMB = MemoryMB.V128; ApiEnum expectedRuntime = Runtime.Nodejs24; @@ -48,6 +72,15 @@ public void FieldRoundtrip_Works() Assert.Equal(value, parameters.Dependencies[item.Key]); } Assert.Equal(expectedDescription, parameters.Description); + Assert.Equal(expectedEntrypoint, parameters.Entrypoint); + Assert.NotNull(parameters.Files); + Assert.Equal(expectedFiles.Count, parameters.Files.Count); + foreach (var item in expectedFiles) + { + Assert.True(parameters.Files.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, parameters.Files[item.Key]); + } Assert.Equal(expectedHttpEnabled, parameters.HttpEnabled); Assert.Equal(expectedMemoryMB, parameters.MemoryMB); Assert.Equal(expectedRuntime, parameters.Runtime); @@ -64,6 +97,10 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() Assert.False(parameters.RawBodyData.ContainsKey("dependencies")); Assert.Null(parameters.Description); Assert.False(parameters.RawBodyData.ContainsKey("description")); + Assert.Null(parameters.Entrypoint); + Assert.False(parameters.RawBodyData.ContainsKey("entrypoint")); + Assert.Null(parameters.Files); + Assert.False(parameters.RawBodyData.ContainsKey("files")); Assert.Null(parameters.HttpEnabled); Assert.False(parameters.RawBodyData.ContainsKey("httpEnabled")); Assert.Null(parameters.MemoryMB); @@ -87,6 +124,8 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() // Null should be interpreted as omitted for these properties Dependencies = null, Description = null, + Entrypoint = null, + Files = null, HttpEnabled = null, MemoryMB = null, Runtime = null, @@ -98,6 +137,10 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() Assert.False(parameters.RawBodyData.ContainsKey("dependencies")); Assert.Null(parameters.Description); Assert.False(parameters.RawBodyData.ContainsKey("description")); + Assert.Null(parameters.Entrypoint); + Assert.False(parameters.RawBodyData.ContainsKey("entrypoint")); + Assert.Null(parameters.Files); + Assert.False(parameters.RawBodyData.ContainsKey("files")); Assert.Null(parameters.HttpEnabled); Assert.False(parameters.RawBodyData.ContainsKey("httpEnabled")); Assert.Null(parameters.MemoryMB); @@ -129,6 +172,18 @@ public void CopyConstructor_Works() Slug = "order-bot", Dependencies = new Dictionary() { { "openai", "^4.20.0" } }, Description = "Replies to order status questions on WhatsApp.", + Entrypoint = "index.ts", + Files = new Dictionary() + { + { + "index.ts", + "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n" + }, + { + "lib/orders.ts", + "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n" + }, + }, HttpEnabled = true, MemoryMB = MemoryMB.V128, Runtime = Runtime.Nodejs24, diff --git a/src/Zavudev.Tests/Models/Functions/FunctionDeployParamsTest.cs b/src/Zavudev.Tests/Models/Functions/FunctionDeployParamsTest.cs index f88734c..6fb595b 100644 --- a/src/Zavudev.Tests/Models/Functions/FunctionDeployParamsTest.cs +++ b/src/Zavudev.Tests/Models/Functions/FunctionDeployParamsTest.cs @@ -13,11 +13,35 @@ public void FieldRoundtrip_Works() { FunctionID = "functionId", Dependencies = new Dictionary() { { "foo", "string" } }, + Entrypoint = "index.ts", + Files = new Dictionary() + { + { + "index.ts", + "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n" + }, + { + "lib/orders.ts", + "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n" + }, + }, SourceCode = "sourceCode", }; string expectedFunctionID = "functionId"; Dictionary expectedDependencies = new() { { "foo", "string" } }; + string expectedEntrypoint = "index.ts"; + Dictionary expectedFiles = new() + { + { + "index.ts", + "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n" + }, + { + "lib/orders.ts", + "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n" + }, + }; string expectedSourceCode = "sourceCode"; Assert.Equal(expectedFunctionID, parameters.FunctionID); @@ -29,6 +53,15 @@ public void FieldRoundtrip_Works() Assert.Equal(value, parameters.Dependencies[item.Key]); } + Assert.Equal(expectedEntrypoint, parameters.Entrypoint); + Assert.NotNull(parameters.Files); + Assert.Equal(expectedFiles.Count, parameters.Files.Count); + foreach (var item in expectedFiles) + { + Assert.True(parameters.Files.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, parameters.Files[item.Key]); + } Assert.Equal(expectedSourceCode, parameters.SourceCode); } @@ -39,6 +72,10 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() Assert.Null(parameters.Dependencies); Assert.False(parameters.RawBodyData.ContainsKey("dependencies")); + Assert.Null(parameters.Entrypoint); + Assert.False(parameters.RawBodyData.ContainsKey("entrypoint")); + Assert.Null(parameters.Files); + Assert.False(parameters.RawBodyData.ContainsKey("files")); Assert.Null(parameters.SourceCode); Assert.False(parameters.RawBodyData.ContainsKey("sourceCode")); } @@ -52,11 +89,17 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() // Null should be interpreted as omitted for these properties Dependencies = null, + Entrypoint = null, + Files = null, SourceCode = null, }; Assert.Null(parameters.Dependencies); Assert.False(parameters.RawBodyData.ContainsKey("dependencies")); + Assert.Null(parameters.Entrypoint); + Assert.False(parameters.RawBodyData.ContainsKey("entrypoint")); + Assert.Null(parameters.Files); + Assert.False(parameters.RawBodyData.ContainsKey("files")); Assert.Null(parameters.SourceCode); Assert.False(parameters.RawBodyData.ContainsKey("sourceCode")); } @@ -80,6 +123,18 @@ public void CopyConstructor_Works() { FunctionID = "functionId", Dependencies = new Dictionary() { { "foo", "string" } }, + Entrypoint = "index.ts", + Files = new Dictionary() + { + { + "index.ts", + "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n" + }, + { + "lib/orders.ts", + "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n" + }, + }, SourceCode = "sourceCode", }; diff --git a/src/Zavudev.Tests/Models/Functions/FunctionUpdateParamsTest.cs b/src/Zavudev.Tests/Models/Functions/FunctionUpdateParamsTest.cs index 2a98f54..a41846c 100644 --- a/src/Zavudev.Tests/Models/Functions/FunctionUpdateParamsTest.cs +++ b/src/Zavudev.Tests/Models/Functions/FunctionUpdateParamsTest.cs @@ -13,12 +13,36 @@ public void FieldRoundtrip_Works() { FunctionID = "functionId", Dependencies = new Dictionary() { { "foo", "string" } }, + Entrypoint = "index.ts", + Files = new Dictionary() + { + { + "index.ts", + "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n" + }, + { + "lib/orders.ts", + "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n" + }, + }, HttpEnabled = true, SourceCode = "sourceCode", }; string expectedFunctionID = "functionId"; Dictionary expectedDependencies = new() { { "foo", "string" } }; + string expectedEntrypoint = "index.ts"; + Dictionary expectedFiles = new() + { + { + "index.ts", + "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n" + }, + { + "lib/orders.ts", + "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n" + }, + }; bool expectedHttpEnabled = true; string expectedSourceCode = "sourceCode"; @@ -31,6 +55,15 @@ public void FieldRoundtrip_Works() Assert.Equal(value, parameters.Dependencies[item.Key]); } + Assert.Equal(expectedEntrypoint, parameters.Entrypoint); + Assert.NotNull(parameters.Files); + Assert.Equal(expectedFiles.Count, parameters.Files.Count); + foreach (var item in expectedFiles) + { + Assert.True(parameters.Files.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, parameters.Files[item.Key]); + } Assert.Equal(expectedHttpEnabled, parameters.HttpEnabled); Assert.Equal(expectedSourceCode, parameters.SourceCode); } @@ -42,6 +75,10 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() Assert.Null(parameters.Dependencies); Assert.False(parameters.RawBodyData.ContainsKey("dependencies")); + Assert.Null(parameters.Entrypoint); + Assert.False(parameters.RawBodyData.ContainsKey("entrypoint")); + Assert.Null(parameters.Files); + Assert.False(parameters.RawBodyData.ContainsKey("files")); Assert.Null(parameters.HttpEnabled); Assert.False(parameters.RawBodyData.ContainsKey("httpEnabled")); Assert.Null(parameters.SourceCode); @@ -57,12 +94,18 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() // Null should be interpreted as omitted for these properties Dependencies = null, + Entrypoint = null, + Files = null, HttpEnabled = null, SourceCode = null, }; Assert.Null(parameters.Dependencies); Assert.False(parameters.RawBodyData.ContainsKey("dependencies")); + Assert.Null(parameters.Entrypoint); + Assert.False(parameters.RawBodyData.ContainsKey("entrypoint")); + Assert.Null(parameters.Files); + Assert.False(parameters.RawBodyData.ContainsKey("files")); Assert.Null(parameters.HttpEnabled); Assert.False(parameters.RawBodyData.ContainsKey("httpEnabled")); Assert.Null(parameters.SourceCode); @@ -88,6 +131,18 @@ public void CopyConstructor_Works() { FunctionID = "functionId", Dependencies = new Dictionary() { { "foo", "string" } }, + Entrypoint = "index.ts", + Files = new Dictionary() + { + { + "index.ts", + "import { formatOrder } from './lib/orders';\n\nexport default async function handler(event) {\n return { statusCode: 200, body: formatOrder(event) };\n}\n" + }, + { + "lib/orders.ts", + "export function formatOrder(event) {\n return JSON.stringify(event);\n}\n" + }, + }, HttpEnabled = true, SourceCode = "sourceCode", }; diff --git a/src/Zavudev/Models/Functions/FunctionCreateParams.cs b/src/Zavudev/Models/Functions/FunctionCreateParams.cs index 22d6cdf..4ee277c 100644 --- a/src/Zavudev/Models/Functions/FunctionCreateParams.cs +++ b/src/Zavudev/Models/Functions/FunctionCreateParams.cs @@ -98,6 +98,58 @@ public string? Description } } + /// + /// Which file in `files` is the entry point. Defaults to `index.ts`. + /// + public string? Entrypoint + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("entrypoint"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("entrypoint", value); + } + } + + /// + /// The project's source files, keyed by path relative to the project root (e.g. + /// `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + /// function is built, so a function can be split across as many files as it needs. + /// + /// Paths must be relative and use forward slashes; `..`, `node_modules/` + /// and `package.json` are rejected. npm packages are not uploaded here — declare + /// them under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 + /// bytes for the whole tree. + /// + public IReadOnlyDictionary? Files + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass>("files"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "files", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + /// /// Whether to expose a public HTTPS URL for this function. /// @@ -159,7 +211,9 @@ public ApiEnum? Runtime } /// - /// TypeScript source code for the function entry point (max ~900KB). + /// Shortcut for a single-file function: exactly equivalent to sending `files` + /// with one entry named after `entrypoint` (`index.ts` by default). Fully supported + /// — use whichever fits. If both are sent, `files` wins. /// public string? SourceCode { diff --git a/src/Zavudev/Models/Functions/FunctionDeployParams.cs b/src/Zavudev/Models/Functions/FunctionDeployParams.cs index 750128e..15f281e 100644 --- a/src/Zavudev/Models/Functions/FunctionDeployParams.cs +++ b/src/Zavudev/Models/Functions/FunctionDeployParams.cs @@ -56,7 +56,61 @@ public IReadOnlyDictionary? Dependencies } /// - /// New source code to publish (replaces the draft). + /// Which file in `files` is the entry point. Defaults to `index.ts`. + /// + public string? Entrypoint + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("entrypoint"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("entrypoint", value); + } + } + + /// + /// The project's source files, keyed by path relative to the project root (e.g. + /// `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + /// function is built, so a function can be split across as many files as it needs. + /// + /// Paths must be relative and use forward slashes; `..`, `node_modules/` + /// and `package.json` are rejected. npm packages are not uploaded here — declare + /// them under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 + /// bytes for the whole tree. + /// + public IReadOnlyDictionary? Files + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass>("files"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "files", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// Shortcut for a single-file function: exactly equivalent to sending `files` + /// with one entry named after `entrypoint` (`index.ts` by default). Fully supported + /// — use whichever fits. If both are sent, `files` wins. /// public string? SourceCode { diff --git a/src/Zavudev/Models/Functions/FunctionDeployResponse.cs b/src/Zavudev/Models/Functions/FunctionDeployResponse.cs index ea91a4d..8945f65 100644 --- a/src/Zavudev/Models/Functions/FunctionDeployResponse.cs +++ b/src/Zavudev/Models/Functions/FunctionDeployResponse.cs @@ -132,6 +132,9 @@ public required long Version init { this._rawData.Set("version", value); } } + /// + /// Size of the built bundle in bytes. Null until the build finishes. + /// public long? BundleBytes { get @@ -165,6 +168,9 @@ public string? ErrorMessage init { this._rawData.Set("errorMessage", value); } } + /// + /// Total size of the deployed source tree in bytes. + /// public long? SourceCodeBytes { get diff --git a/src/Zavudev/Models/Functions/FunctionGetDeploymentResponse.cs b/src/Zavudev/Models/Functions/FunctionGetDeploymentResponse.cs index 46b47f5..033ba72 100644 --- a/src/Zavudev/Models/Functions/FunctionGetDeploymentResponse.cs +++ b/src/Zavudev/Models/Functions/FunctionGetDeploymentResponse.cs @@ -145,6 +145,9 @@ public required long Version init { this._rawData.Set("version", value); } } + /// + /// Size of the built bundle in bytes. Null until the build finishes. + /// public long? BundleBytes { get @@ -178,6 +181,9 @@ public string? ErrorMessage init { this._rawData.Set("errorMessage", value); } } + /// + /// Total size of the deployed source tree in bytes. + /// public long? SourceCodeBytes { get diff --git a/src/Zavudev/Models/Functions/FunctionUpdateParams.cs b/src/Zavudev/Models/Functions/FunctionUpdateParams.cs index 46cc800..bc0557e 100644 --- a/src/Zavudev/Models/Functions/FunctionUpdateParams.cs +++ b/src/Zavudev/Models/Functions/FunctionUpdateParams.cs @@ -55,6 +55,58 @@ public IReadOnlyDictionary? Dependencies } } + /// + /// Which file in `files` is the entry point. Defaults to `index.ts`. + /// + public string? Entrypoint + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass("entrypoint"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set("entrypoint", value); + } + } + + /// + /// The project's source files, keyed by path relative to the project root (e.g. + /// `index.ts`, `lib/orders.ts`). Imports between them are resolved when the + /// function is built, so a function can be split across as many files as it needs. + /// + /// Paths must be relative and use forward slashes; `..`, `node_modules/` + /// and `package.json` are rejected. npm packages are not uploaded here — declare + /// them under `dependencies` and Zavu installs them. Limits: 200 files and 900,000 + /// bytes for the whole tree. + /// + public IReadOnlyDictionary? Files + { + get + { + this._rawBodyData.Freeze(); + return this._rawBodyData.GetNullableClass>("files"); + } + init + { + if (value == null) + { + return; + } + + this._rawBodyData.Set?>( + "files", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + /// /// Expose the function on its public HTTPS URL, or take it down. Applies to /// the already-deployed function without redeploying; the URL is returned as `publicUrl`. @@ -78,7 +130,9 @@ public bool? HttpEnabled } /// - /// New source code for the draft (replaces it). + /// Shortcut for a single-file function: exactly equivalent to sending `files` + /// with one entry named after `entrypoint` (`index.ts` by default). Fully supported + /// — use whichever fits. If both are sent, `files` wins. /// public string? SourceCode { From c89fc7b716dc67adaeebc0389d47bee998137f05 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 03:48:05 +0000 Subject: [PATCH 02/20] feat(api): api update --- .stats.yml | 4 ++-- .../Functions/FunctionDeployResponseTest.cs | 22 +++++++++++++++++++ .../FunctionGetDeploymentResponseTest.cs | 22 +++++++++++++++++++ .../Functions/FunctionDeployResponse.cs | 18 +++++++++++++++ .../FunctionGetDeploymentResponse.cs | 18 +++++++++++++++ 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 484237c..0b60fcc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-21db15ab8c7fc4a95f96e62e9ae84d4db9696f41adf62806c9be4043268b0fad.yml -openapi_spec_hash: f5928b2d6fc0d6f10ea63acac9dead60 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e4987140138afb7287e25f31dc8cff94c6b11975e460e0c19d54ec457d009a1b.yml +openapi_spec_hash: d20e1b8d0a9a7a65dc8a21e3a151111e config_hash: 7fca08d9dae18513f5d7451cea11e34c diff --git a/src/Zavudev.Tests/Models/Functions/FunctionDeployResponseTest.cs b/src/Zavudev.Tests/Models/Functions/FunctionDeployResponseTest.cs index bbfef9b..1ad84eb 100644 --- a/src/Zavudev.Tests/Models/Functions/FunctionDeployResponseTest.cs +++ b/src/Zavudev.Tests/Models/Functions/FunctionDeployResponseTest.cs @@ -20,6 +20,7 @@ public void FieldRoundtrip_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -34,6 +35,7 @@ public void FieldRoundtrip_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -55,6 +57,7 @@ public void SerializationRoundtrip_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -83,6 +86,7 @@ public void FieldRoundtripThroughSerialization_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -104,6 +108,7 @@ public void FieldRoundtripThroughSerialization_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -125,6 +130,7 @@ public void Validation_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -147,6 +153,7 @@ public void CopyConstructor_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -172,6 +179,7 @@ public void FieldRoundtrip_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -183,6 +191,7 @@ public void FieldRoundtrip_Works() string expectedFunctionID = "functionId"; ApiEnum expectedStatus = DeploymentStatus.Pending; long expectedVersion = 0; + string expectedBuildLogs = "buildLogs"; long expectedBundleBytes = 0; DateTimeOffset expectedDeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); string expectedErrorMessage = "errorMessage"; @@ -193,6 +202,7 @@ public void FieldRoundtrip_Works() Assert.Equal(expectedFunctionID, model.FunctionID); Assert.Equal(expectedStatus, model.Status); Assert.Equal(expectedVersion, model.Version); + Assert.Equal(expectedBuildLogs, model.BuildLogs); Assert.Equal(expectedBundleBytes, model.BundleBytes); Assert.Equal(expectedDeployedAt, model.DeployedAt); Assert.Equal(expectedErrorMessage, model.ErrorMessage); @@ -209,6 +219,7 @@ public void SerializationRoundtrip_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -234,6 +245,7 @@ public void FieldRoundtripThroughSerialization_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -252,6 +264,7 @@ public void FieldRoundtripThroughSerialization_Works() string expectedFunctionID = "functionId"; ApiEnum expectedStatus = DeploymentStatus.Pending; long expectedVersion = 0; + string expectedBuildLogs = "buildLogs"; long expectedBundleBytes = 0; DateTimeOffset expectedDeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); string expectedErrorMessage = "errorMessage"; @@ -262,6 +275,7 @@ public void FieldRoundtripThroughSerialization_Works() Assert.Equal(expectedFunctionID, deserialized.FunctionID); Assert.Equal(expectedStatus, deserialized.Status); Assert.Equal(expectedVersion, deserialized.Version); + Assert.Equal(expectedBuildLogs, deserialized.BuildLogs); Assert.Equal(expectedBundleBytes, deserialized.BundleBytes); Assert.Equal(expectedDeployedAt, deserialized.DeployedAt); Assert.Equal(expectedErrorMessage, deserialized.ErrorMessage); @@ -278,6 +292,7 @@ public void Validation_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -299,6 +314,8 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() Version = 0, }; + Assert.Null(model.BuildLogs); + Assert.False(model.RawData.ContainsKey("buildLogs")); Assert.Null(model.BundleBytes); Assert.False(model.RawData.ContainsKey("bundleBytes")); Assert.Null(model.DeployedAt); @@ -335,12 +352,15 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = null, BundleBytes = null, DeployedAt = null, ErrorMessage = null, SourceCodeBytes = null, }; + Assert.Null(model.BuildLogs); + Assert.True(model.RawData.ContainsKey("buildLogs")); Assert.Null(model.BundleBytes); Assert.True(model.RawData.ContainsKey("bundleBytes")); Assert.Null(model.DeployedAt); @@ -362,6 +382,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = null, BundleBytes = null, DeployedAt = null, ErrorMessage = null, @@ -381,6 +402,7 @@ public void CopyConstructor_Works() FunctionID = "functionId", Status = DeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", diff --git a/src/Zavudev.Tests/Models/Functions/FunctionGetDeploymentResponseTest.cs b/src/Zavudev.Tests/Models/Functions/FunctionGetDeploymentResponseTest.cs index c281934..e65354b 100644 --- a/src/Zavudev.Tests/Models/Functions/FunctionGetDeploymentResponseTest.cs +++ b/src/Zavudev.Tests/Models/Functions/FunctionGetDeploymentResponseTest.cs @@ -20,6 +20,7 @@ public void FieldRoundtrip_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -34,6 +35,7 @@ public void FieldRoundtrip_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -55,6 +57,7 @@ public void SerializationRoundtrip_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -83,6 +86,7 @@ public void FieldRoundtripThroughSerialization_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -104,6 +108,7 @@ public void FieldRoundtripThroughSerialization_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -125,6 +130,7 @@ public void Validation_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -147,6 +153,7 @@ public void CopyConstructor_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -172,6 +179,7 @@ public void FieldRoundtrip_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -184,6 +192,7 @@ public void FieldRoundtrip_Works() ApiEnum expectedStatus = FunctionGetDeploymentResponseDeploymentStatus.Pending; long expectedVersion = 0; + string expectedBuildLogs = "buildLogs"; long expectedBundleBytes = 0; DateTimeOffset expectedDeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); string expectedErrorMessage = "errorMessage"; @@ -194,6 +203,7 @@ public void FieldRoundtrip_Works() Assert.Equal(expectedFunctionID, model.FunctionID); Assert.Equal(expectedStatus, model.Status); Assert.Equal(expectedVersion, model.Version); + Assert.Equal(expectedBuildLogs, model.BuildLogs); Assert.Equal(expectedBundleBytes, model.BundleBytes); Assert.Equal(expectedDeployedAt, model.DeployedAt); Assert.Equal(expectedErrorMessage, model.ErrorMessage); @@ -210,6 +220,7 @@ public void SerializationRoundtrip_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -235,6 +246,7 @@ public void FieldRoundtripThroughSerialization_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -254,6 +266,7 @@ public void FieldRoundtripThroughSerialization_Works() ApiEnum expectedStatus = FunctionGetDeploymentResponseDeploymentStatus.Pending; long expectedVersion = 0; + string expectedBuildLogs = "buildLogs"; long expectedBundleBytes = 0; DateTimeOffset expectedDeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"); string expectedErrorMessage = "errorMessage"; @@ -264,6 +277,7 @@ public void FieldRoundtripThroughSerialization_Works() Assert.Equal(expectedFunctionID, deserialized.FunctionID); Assert.Equal(expectedStatus, deserialized.Status); Assert.Equal(expectedVersion, deserialized.Version); + Assert.Equal(expectedBuildLogs, deserialized.BuildLogs); Assert.Equal(expectedBundleBytes, deserialized.BundleBytes); Assert.Equal(expectedDeployedAt, deserialized.DeployedAt); Assert.Equal(expectedErrorMessage, deserialized.ErrorMessage); @@ -280,6 +294,7 @@ public void Validation_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", @@ -301,6 +316,8 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() Version = 0, }; + Assert.Null(model.BuildLogs); + Assert.False(model.RawData.ContainsKey("buildLogs")); Assert.Null(model.BundleBytes); Assert.False(model.RawData.ContainsKey("bundleBytes")); Assert.Null(model.DeployedAt); @@ -337,12 +354,15 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = null, BundleBytes = null, DeployedAt = null, ErrorMessage = null, SourceCodeBytes = null, }; + Assert.Null(model.BuildLogs); + Assert.True(model.RawData.ContainsKey("buildLogs")); Assert.Null(model.BundleBytes); Assert.True(model.RawData.ContainsKey("bundleBytes")); Assert.Null(model.DeployedAt); @@ -364,6 +384,7 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = null, BundleBytes = null, DeployedAt = null, ErrorMessage = null, @@ -383,6 +404,7 @@ public void CopyConstructor_Works() FunctionID = "functionId", Status = FunctionGetDeploymentResponseDeploymentStatus.Pending, Version = 0, + BuildLogs = "buildLogs", BundleBytes = 0, DeployedAt = DateTimeOffset.Parse("2019-12-27T18:11:19.117Z"), ErrorMessage = "errorMessage", diff --git a/src/Zavudev/Models/Functions/FunctionDeployResponse.cs b/src/Zavudev/Models/Functions/FunctionDeployResponse.cs index 8945f65..43fa04e 100644 --- a/src/Zavudev/Models/Functions/FunctionDeployResponse.cs +++ b/src/Zavudev/Models/Functions/FunctionDeployResponse.cs @@ -132,6 +132,23 @@ public required long Version init { this._rawData.Set("version", value); } } + /// + /// What the build printed: dependency installation, the bundler's output, and + /// the compiler's message when it failed. Returned when fetching a single deployment, + /// omitted from the list. Read this first when a deploy fails — `errorMessage` + /// is often the outer wrapper's summary, and the line that names the broken + /// import or the syntax error is here. + /// + public string? BuildLogs + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("buildLogs"); + } + init { this._rawData.Set("buildLogs", value); } + } + /// /// Size of the built bundle in bytes. Null until the build finishes. /// @@ -189,6 +206,7 @@ public override void Validate() _ = this.FunctionID; this.Status.Validate(); _ = this.Version; + _ = this.BuildLogs; _ = this.BundleBytes; _ = this.DeployedAt; _ = this.ErrorMessage; diff --git a/src/Zavudev/Models/Functions/FunctionGetDeploymentResponse.cs b/src/Zavudev/Models/Functions/FunctionGetDeploymentResponse.cs index 033ba72..c30503c 100644 --- a/src/Zavudev/Models/Functions/FunctionGetDeploymentResponse.cs +++ b/src/Zavudev/Models/Functions/FunctionGetDeploymentResponse.cs @@ -145,6 +145,23 @@ public required long Version init { this._rawData.Set("version", value); } } + /// + /// What the build printed: dependency installation, the bundler's output, and + /// the compiler's message when it failed. Returned when fetching a single deployment, + /// omitted from the list. Read this first when a deploy fails — `errorMessage` + /// is often the outer wrapper's summary, and the line that names the broken + /// import or the syntax error is here. + /// + public string? BuildLogs + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("buildLogs"); + } + init { this._rawData.Set("buildLogs", value); } + } + /// /// Size of the built bundle in bytes. Null until the build finishes. /// @@ -202,6 +219,7 @@ public override void Validate() _ = this.FunctionID; this.Status.Validate(); _ = this.Version; + _ = this.BuildLogs; _ = this.BundleBytes; _ = this.DeployedAt; _ = this.ErrorMessage; From 4ec33f77284119bda83c2634b766f87ded613997 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 03:55:47 +0000 Subject: [PATCH 03/20] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0b60fcc..edddea6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e4987140138afb7287e25f31dc8cff94c6b11975e460e0c19d54ec457d009a1b.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-f94b2cae6c929ce34c51007444f7105c96b458c2cb9b07be2ce7d434adda4e66.yml openapi_spec_hash: d20e1b8d0a9a7a65dc8a21e3a151111e -config_hash: 7fca08d9dae18513f5d7451cea11e34c +config_hash: 942fad5624cf310889c6161a3b6bf725 From 873dec663c00244a44f06c5956bb7761e15ee34a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 03:56:12 +0000 Subject: [PATCH 04/20] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index edddea6..0b60fcc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-f94b2cae6c929ce34c51007444f7105c96b458c2cb9b07be2ce7d434adda4e66.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e4987140138afb7287e25f31dc8cff94c6b11975e460e0c19d54ec457d009a1b.yml openapi_spec_hash: d20e1b8d0a9a7a65dc8a21e3a151111e -config_hash: 942fad5624cf310889c6161a3b6bf725 +config_hash: 7fca08d9dae18513f5d7451cea11e34c From 075d0c247aee199ad08c180fa65925beb52b0f08 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 23:28:50 +0000 Subject: [PATCH 05/20] feat(api): api update --- .stats.yml | 4 ++-- .../Models/Broadcasts/BroadcastSendParams.cs | 12 ++++++++---- src/Zavudev/Services/IBroadcastService.cs | 15 +++++++++------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0b60fcc..d105c4b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e4987140138afb7287e25f31dc8cff94c6b11975e460e0c19d54ec457d009a1b.yml -openapi_spec_hash: d20e1b8d0a9a7a65dc8a21e3a151111e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-dd62b3849e15304da4087f73928025602737e10a414c87a6c2a5fe7059b9ea37.yml +openapi_spec_hash: 5a3d5cb61a7dd5d556a9f9abd9db01d4 config_hash: 7fca08d9dae18513f5d7451cea11e34c diff --git a/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs b/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs index 7a01695..36a565b 100644 --- a/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs +++ b/src/Zavudev/Models/Broadcasts/BroadcastSendParams.cs @@ -12,10 +12,14 @@ namespace Zavudev.Models.Broadcasts; /// /// Start sending the broadcast immediately or schedule for later. /// -/// **Verification is required to send, and there are two of them.** The team -/// must have completed both identity verification (KYC) and business verification -/// (KYB); passing one is not enough. Drafts can be created, edited and kept without -/// either. Every send path — dashboard, API and CLI alike — enforces both, returning +/// **Verification is required to send, except on WhatsApp.** On every channel +/// other than `whatsapp`, the team must have completed both identity verification +/// (KYC) and business verification (KYB); passing one is not enough. A `whatsapp` +/// broadcast requires neither: 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 — 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 alike — enforces the same rule, returning /// `403` with code `kyc_required` or `kyb_required` for whichever is outstanding. /// /// **Review depends on the channel, and cannot be bypassed.** A draft is submitted diff --git a/src/Zavudev/Services/IBroadcastService.cs b/src/Zavudev/Services/IBroadcastService.cs index 219d0a1..c2cc2e2 100644 --- a/src/Zavudev/Services/IBroadcastService.cs +++ b/src/Zavudev/Services/IBroadcastService.cs @@ -170,12 +170,15 @@ Task RetryReview( /// /// Start sending the broadcast immediately or schedule for later. /// - /// **Verification is required to send, and there are two of them.** The team - /// must have completed both identity verification (KYC) and business verification - /// (KYB); passing one is not enough. Drafts can be created, edited and kept without - /// either. Every send path — dashboard, API and CLI alike — enforces both, - /// returning `403` with code `kyc_required` or `kyb_required` for whichever is - /// outstanding. + /// **Verification is required to send, except on WhatsApp.** On every channel + /// other than `whatsapp`, the team must have completed both identity verification + /// (KYC) and business verification (KYB); passing one is not enough. A `whatsapp` + /// broadcast requires neither: 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 — 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 alike — enforces the same rule, returning + /// `403` with code `kyc_required` or `kyb_required` for whichever is outstanding. /// /// **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 From ab63674d56ece9623457bc26b3e7f7de61dc56af Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 23:51:55 +0000 Subject: [PATCH 06/20] feat(api): api update --- .stats.yml | 4 +- .../Models/Messages/MessageContentTest.cs | 466 ++++++++++++++++++ .../Messages/MessageListPageResponseTest.cs | 143 ++++++ .../Models/Messages/MessageResponseTest.cs | 91 ++++ .../Models/Messages/MessageSendParamsTest.cs | 39 ++ .../Models/Messages/MessageTest.cs | 143 ++++++ src/Zavudev/Core/ModelBase.cs | 2 + src/Zavudev/Models/Messages/MessageContent.cs | 400 +++++++++++++++ src/Zavudev/Models/Senders/WebhookEvent.cs | 8 +- 9 files changed, 1292 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index d105c4b..c8f40a3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-dd62b3849e15304da4087f73928025602737e10a414c87a6c2a5fe7059b9ea37.yml -openapi_spec_hash: 5a3d5cb61a7dd5d556a9f9abd9db01d4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-8ead699d165bc116549ef757ace827bf39add6e0e19afdc1653f0d0282947eae.yml +openapi_spec_hash: 5a35a5dab833f4d1e6deeea795e1abf2 config_hash: 7fca08d9dae18513f5d7451cea11e34c diff --git a/src/Zavudev.Tests/Models/Messages/MessageContentTest.cs b/src/Zavudev.Tests/Models/Messages/MessageContentTest.cs index 64831fb..4199e74 100644 --- a/src/Zavudev.Tests/Models/Messages/MessageContentTest.cs +++ b/src/Zavudev.Tests/Models/Messages/MessageContentTest.cs @@ -32,6 +32,19 @@ public void FieldRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -85,6 +98,19 @@ public void FieldRoundtrip_Works() string expectedMediaUrl = "https://example.com/image.jpg"; string expectedMimeType = "image/jpeg"; string expectedReactToMessageID = "reactToMessageId"; + Referral expectedReferral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }; string expectedReplyToFrom = "replyToFrom"; string expectedReplyToMessageID = "replyToMessageId"; string expectedReplyToMessageType = "replyToMessageType"; @@ -150,6 +176,7 @@ public void FieldRoundtrip_Works() Assert.Equal(expectedMediaUrl, model.MediaUrl); Assert.Equal(expectedMimeType, model.MimeType); Assert.Equal(expectedReactToMessageID, model.ReactToMessageID); + Assert.Equal(expectedReferral, model.Referral); Assert.Equal(expectedReplyToFrom, model.ReplyToFrom); Assert.Equal(expectedReplyToMessageID, model.ReplyToMessageID); Assert.Equal(expectedReplyToMessageType, model.ReplyToMessageType); @@ -212,6 +239,19 @@ public void SerializationRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -279,6 +319,19 @@ public void FieldRoundtripThroughSerialization_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -339,6 +392,19 @@ public void FieldRoundtripThroughSerialization_Works() string expectedMediaUrl = "https://example.com/image.jpg"; string expectedMimeType = "image/jpeg"; string expectedReactToMessageID = "reactToMessageId"; + Referral expectedReferral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }; string expectedReplyToFrom = "replyToFrom"; string expectedReplyToMessageID = "replyToMessageId"; string expectedReplyToMessageType = "replyToMessageType"; @@ -404,6 +470,7 @@ public void FieldRoundtripThroughSerialization_Works() Assert.Equal(expectedMediaUrl, deserialized.MediaUrl); Assert.Equal(expectedMimeType, deserialized.MimeType); Assert.Equal(expectedReactToMessageID, deserialized.ReactToMessageID); + Assert.Equal(expectedReferral, deserialized.Referral); Assert.Equal(expectedReplyToFrom, deserialized.ReplyToFrom); Assert.Equal(expectedReplyToMessageID, deserialized.ReplyToMessageID); Assert.Equal(expectedReplyToMessageType, deserialized.ReplyToMessageType); @@ -472,6 +539,19 @@ public void Validation_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -552,6 +632,8 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() Assert.False(model.RawData.ContainsKey("mimeType")); Assert.Null(model.ReactToMessageID); Assert.False(model.RawData.ContainsKey("reactToMessageId")); + Assert.Null(model.Referral); + Assert.False(model.RawData.ContainsKey("referral")); Assert.Null(model.ReplyToFrom); Assert.False(model.RawData.ContainsKey("replyToFrom")); Assert.Null(model.ReplyToMessageID); @@ -607,6 +689,7 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() MediaUrl = null, MimeType = null, ReactToMessageID = null, + Referral = null, ReplyToFrom = null, ReplyToMessageID = null, ReplyToMessageType = null, @@ -657,6 +740,8 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() Assert.False(model.RawData.ContainsKey("mimeType")); Assert.Null(model.ReactToMessageID); Assert.False(model.RawData.ContainsKey("reactToMessageId")); + Assert.Null(model.Referral); + Assert.False(model.RawData.ContainsKey("referral")); Assert.Null(model.ReplyToFrom); Assert.False(model.RawData.ContainsKey("replyToFrom")); Assert.Null(model.ReplyToMessageID); @@ -704,6 +789,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() MediaUrl = null, MimeType = null, ReactToMessageID = null, + Referral = null, ReplyToFrom = null, ReplyToMessageID = null, ReplyToMessageType = null, @@ -743,6 +829,19 @@ public void CopyConstructor_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -1026,6 +1125,373 @@ public void InvalidEnumSerializationRoundtrip_Works() } } +public class ReferralTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Referral + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }; + + string expectedBody = "body"; + string expectedCtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ"; + string expectedHeadline = "headline"; + string expectedImageUrl = "https://example.com"; + ApiEnum expectedMediaType = MediaType.Image; + string expectedSourceID = "120210000000000000"; + ApiEnum expectedSourceType = SourceType.Ad; + string expectedSourceUrl = "https://example.com"; + string expectedThumbnailUrl = "https://example.com"; + string expectedVideoUrl = "https://example.com"; + + Assert.Equal(expectedBody, model.Body); + Assert.Equal(expectedCtwaClid, model.CtwaClid); + Assert.Equal(expectedHeadline, model.Headline); + Assert.Equal(expectedImageUrl, model.ImageUrl); + Assert.Equal(expectedMediaType, model.MediaType); + Assert.Equal(expectedSourceID, model.SourceID); + Assert.Equal(expectedSourceType, model.SourceType); + Assert.Equal(expectedSourceUrl, model.SourceUrl); + Assert.Equal(expectedThumbnailUrl, model.ThumbnailUrl); + Assert.Equal(expectedVideoUrl, model.VideoUrl); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Referral + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }; + + 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 Referral + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedBody = "body"; + string expectedCtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ"; + string expectedHeadline = "headline"; + string expectedImageUrl = "https://example.com"; + ApiEnum expectedMediaType = MediaType.Image; + string expectedSourceID = "120210000000000000"; + ApiEnum expectedSourceType = SourceType.Ad; + string expectedSourceUrl = "https://example.com"; + string expectedThumbnailUrl = "https://example.com"; + string expectedVideoUrl = "https://example.com"; + + Assert.Equal(expectedBody, deserialized.Body); + Assert.Equal(expectedCtwaClid, deserialized.CtwaClid); + Assert.Equal(expectedHeadline, deserialized.Headline); + Assert.Equal(expectedImageUrl, deserialized.ImageUrl); + Assert.Equal(expectedMediaType, deserialized.MediaType); + Assert.Equal(expectedSourceID, deserialized.SourceID); + Assert.Equal(expectedSourceType, deserialized.SourceType); + Assert.Equal(expectedSourceUrl, deserialized.SourceUrl); + Assert.Equal(expectedThumbnailUrl, deserialized.ThumbnailUrl); + Assert.Equal(expectedVideoUrl, deserialized.VideoUrl); + } + + [Fact] + public void Validation_Works() + { + var model = new Referral + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Referral { }; + + Assert.Null(model.Body); + Assert.False(model.RawData.ContainsKey("body")); + Assert.Null(model.CtwaClid); + Assert.False(model.RawData.ContainsKey("ctwaClid")); + Assert.Null(model.Headline); + Assert.False(model.RawData.ContainsKey("headline")); + Assert.Null(model.ImageUrl); + Assert.False(model.RawData.ContainsKey("imageUrl")); + Assert.Null(model.MediaType); + Assert.False(model.RawData.ContainsKey("mediaType")); + Assert.Null(model.SourceID); + Assert.False(model.RawData.ContainsKey("sourceId")); + Assert.Null(model.SourceType); + Assert.False(model.RawData.ContainsKey("sourceType")); + Assert.Null(model.SourceUrl); + Assert.False(model.RawData.ContainsKey("sourceUrl")); + Assert.Null(model.ThumbnailUrl); + Assert.False(model.RawData.ContainsKey("thumbnailUrl")); + Assert.Null(model.VideoUrl); + Assert.False(model.RawData.ContainsKey("videoUrl")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Referral { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Referral + { + // Null should be interpreted as omitted for these properties + Body = null, + CtwaClid = null, + Headline = null, + ImageUrl = null, + MediaType = null, + SourceID = null, + SourceType = null, + SourceUrl = null, + ThumbnailUrl = null, + VideoUrl = null, + }; + + Assert.Null(model.Body); + Assert.False(model.RawData.ContainsKey("body")); + Assert.Null(model.CtwaClid); + Assert.False(model.RawData.ContainsKey("ctwaClid")); + Assert.Null(model.Headline); + Assert.False(model.RawData.ContainsKey("headline")); + Assert.Null(model.ImageUrl); + Assert.False(model.RawData.ContainsKey("imageUrl")); + Assert.Null(model.MediaType); + Assert.False(model.RawData.ContainsKey("mediaType")); + Assert.Null(model.SourceID); + Assert.False(model.RawData.ContainsKey("sourceId")); + Assert.Null(model.SourceType); + Assert.False(model.RawData.ContainsKey("sourceType")); + Assert.Null(model.SourceUrl); + Assert.False(model.RawData.ContainsKey("sourceUrl")); + Assert.Null(model.ThumbnailUrl); + Assert.False(model.RawData.ContainsKey("thumbnailUrl")); + Assert.Null(model.VideoUrl); + Assert.False(model.RawData.ContainsKey("videoUrl")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Referral + { + // Null should be interpreted as omitted for these properties + Body = null, + CtwaClid = null, + Headline = null, + ImageUrl = null, + MediaType = null, + SourceID = null, + SourceType = null, + SourceUrl = null, + ThumbnailUrl = null, + VideoUrl = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Referral + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }; + + Referral copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class MediaTypeTest : TestBase +{ + [Theory] + [InlineData(MediaType.Image)] + [InlineData(MediaType.Video)] + public void Validation_Works(MediaType 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(MediaType.Image)] + [InlineData(MediaType.Video)] + public void SerializationRoundtrip_Works(MediaType 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); + } +} + +public class SourceTypeTest : TestBase +{ + [Theory] + [InlineData(SourceType.Ad)] + [InlineData(SourceType.Post)] + public void Validation_Works(SourceType 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(SourceType.Ad)] + [InlineData(SourceType.Post)] + public void SerializationRoundtrip_Works(SourceType 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); + } +} + public class SectionTest : TestBase { [Fact] diff --git a/src/Zavudev.Tests/Models/Messages/MessageListPageResponseTest.cs b/src/Zavudev.Tests/Models/Messages/MessageListPageResponseTest.cs index d0f0717..dbdefea 100644 --- a/src/Zavudev.Tests/Models/Messages/MessageListPageResponseTest.cs +++ b/src/Zavudev.Tests/Models/Messages/MessageListPageResponseTest.cs @@ -44,6 +44,19 @@ public void FieldRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -128,6 +141,19 @@ public void FieldRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -224,6 +250,19 @@ public void SerializationRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -322,6 +361,19 @@ public void FieldRoundtripThroughSerialization_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -413,6 +465,19 @@ public void FieldRoundtripThroughSerialization_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -509,6 +574,19 @@ public void Validation_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -601,6 +679,19 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -693,6 +784,19 @@ public void OptionalNullablePropertiesUnsetValidation_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -784,6 +888,19 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -878,6 +995,19 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -971,6 +1101,19 @@ public void CopyConstructor_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", diff --git a/src/Zavudev.Tests/Models/Messages/MessageResponseTest.cs b/src/Zavudev.Tests/Models/Messages/MessageResponseTest.cs index 20ca0ff..7f5a3e5 100644 --- a/src/Zavudev.Tests/Models/Messages/MessageResponseTest.cs +++ b/src/Zavudev.Tests/Models/Messages/MessageResponseTest.cs @@ -42,6 +42,19 @@ public void FieldRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -122,6 +135,19 @@ public void FieldRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -209,6 +235,19 @@ public void SerializationRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -303,6 +342,19 @@ public void FieldRoundtripThroughSerialization_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -390,6 +442,19 @@ public void FieldRoundtripThroughSerialization_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -477,6 +542,19 @@ public void Validation_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -565,6 +643,19 @@ public void CopyConstructor_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", diff --git a/src/Zavudev.Tests/Models/Messages/MessageSendParamsTest.cs b/src/Zavudev.Tests/Models/Messages/MessageSendParamsTest.cs index 9df6d4d..df913d8 100644 --- a/src/Zavudev.Tests/Models/Messages/MessageSendParamsTest.cs +++ b/src/Zavudev.Tests/Models/Messages/MessageSendParamsTest.cs @@ -48,6 +48,19 @@ public void FieldRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -130,6 +143,19 @@ public void FieldRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -346,6 +372,19 @@ public void CopyConstructor_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", diff --git a/src/Zavudev.Tests/Models/Messages/MessageTest.cs b/src/Zavudev.Tests/Models/Messages/MessageTest.cs index b813a6e..98bf82a 100644 --- a/src/Zavudev.Tests/Models/Messages/MessageTest.cs +++ b/src/Zavudev.Tests/Models/Messages/MessageTest.cs @@ -40,6 +40,19 @@ public void FieldRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -117,6 +130,19 @@ public void FieldRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -223,6 +249,19 @@ public void SerializationRoundtrip_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -311,6 +350,19 @@ public void FieldRoundtripThroughSerialization_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -395,6 +447,19 @@ public void FieldRoundtripThroughSerialization_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -501,6 +566,19 @@ public void Validation_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -720,6 +798,19 @@ public void OptionalNullablePropertiesUnsetAreNotSet_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -809,6 +900,19 @@ public void OptionalNullablePropertiesUnsetValidation_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -889,6 +993,19 @@ public void OptionalNullablePropertiesSetToNullAreSetToNull_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -984,6 +1101,19 @@ public void OptionalNullablePropertiesSetToNullValidation_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", @@ -1070,6 +1200,19 @@ public void CopyConstructor_Works() MediaUrl = "https://example.com/image.jpg", MimeType = "image/jpeg", ReactToMessageID = "reactToMessageId", + Referral = new() + { + Body = "body", + CtwaClid = "ARIzZm9vYmFyY3R3YWNsaWQ", + Headline = "headline", + ImageUrl = "https://example.com", + MediaType = MediaType.Image, + SourceID = "120210000000000000", + SourceType = SourceType.Ad, + SourceUrl = "https://example.com", + ThumbnailUrl = "https://example.com", + VideoUrl = "https://example.com", + }, ReplyToFrom = "replyToFrom", ReplyToMessageID = "replyToMessageId", ReplyToMessageType = "replyToMessageType", diff --git a/src/Zavudev/Core/ModelBase.cs b/src/Zavudev/Core/ModelBase.cs index 9e50614..dd64ff4 100644 --- a/src/Zavudev/Core/ModelBase.cs +++ b/src/Zavudev/Core/ModelBase.cs @@ -44,6 +44,8 @@ protected ModelBase(ModelBase modelBase) new FrozenDictionaryConverterFactory(), new ApiEnumConverter(), new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), diff --git a/src/Zavudev/Models/Messages/MessageContent.cs b/src/Zavudev/Models/Messages/MessageContent.cs index 2258d55..3a8cf29 100644 --- a/src/Zavudev/Models/Messages/MessageContent.cs +++ b/src/Zavudev/Models/Messages/MessageContent.cs @@ -424,6 +424,35 @@ public string? ReactToMessageID } } + /// + /// Click-to-WhatsApp (CTWA) ad attribution: where an inbound conversation came from. + /// + /// WhatsApp only. Present on the **first inbound message** of a conversation + /// opened from a Meta ad or post, and on no message after it — so store it when + /// it arrives rather than expecting it again. Organic conversations never carry it. + /// + /// Field names are camelCased to match the rest of this API; Meta sends + /// them as snake_case (`ctwa_clid`, `source_id`, ...). Fields that do not apply + /// are omitted: a `post` source has no click id, and an image ad has no `videoUrl`. + /// + public Referral? Referral + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("referral"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("referral", value); + } + } + /// /// Sender of the quoted message (phone number in E.164 format). /// @@ -701,6 +730,7 @@ public override void Validate() _ = this.MediaUrl; _ = this.MimeType; _ = this.ReactToMessageID; + this.Referral?.Validate(); _ = this.ReplyToFrom; _ = this.ReplyToMessageID; _ = this.ReplyToMessageType; @@ -953,6 +983,376 @@ JsonSerializerOptions options } } +/// +/// Click-to-WhatsApp (CTWA) ad attribution: where an inbound conversation came from. +/// +/// WhatsApp only. Present on the **first inbound message** of a conversation +/// opened from a Meta ad or post, and on no message after it — so store it when it +/// arrives rather than expecting it again. Organic conversations never carry it. +/// +/// Field names are camelCased to match the rest of this API; Meta sends them +/// as snake_case (`ctwa_clid`, `source_id`, ...). Fields that do not apply are omitted: +/// a `post` source has no click id, and an image ad has no `videoUrl`. +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Referral : JsonModel +{ + /// + /// Body copy of the ad or post. + /// + public string? Body + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("body"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("body", value); + } + } + + /// + /// Click-to-WhatsApp click identifier. This is the value Meta's Conversions API + /// needs to credit a conversion back to the ad that produced the conversation. + /// Present on `ad` sources; a `post` source has none. + /// + public string? CtwaClid + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("ctwaClid"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("ctwaClid", value); + } + } + + /// + /// Headline of the ad or post. + /// + public string? Headline + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("headline"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("headline", value); + } + } + + /// + /// Image of the ad. Present when `mediaType` is `image`. + /// + public string? ImageUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("imageUrl"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("imageUrl", value); + } + } + + /// + /// Type of media on the ad, when it had any. + /// + public ApiEnum? MediaType + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("mediaType"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("mediaType", value); + } + } + + /// + /// Identifier of the ad or post that produced the click. + /// + public string? SourceID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("sourceId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("sourceId", value); + } + } + + /// + /// Where the click came from. + /// + public ApiEnum? SourceType + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("sourceType"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("sourceType", value); + } + } + + /// + /// Meta permalink to the ad or post. + /// + public string? SourceUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("sourceUrl"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("sourceUrl", value); + } + } + + /// + /// Thumbnail of the ad media. + /// + public string? ThumbnailUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("thumbnailUrl"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("thumbnailUrl", value); + } + } + + /// + /// Video of the ad. Present when `mediaType` is `video`. + /// + public string? VideoUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("videoUrl"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("videoUrl", value); + } + } + + /// + public override void Validate() + { + _ = this.Body; + _ = this.CtwaClid; + _ = this.Headline; + _ = this.ImageUrl; + this.MediaType?.Validate(); + _ = this.SourceID; + this.SourceType?.Validate(); + _ = this.SourceUrl; + _ = this.ThumbnailUrl; + _ = this.VideoUrl; + } + + public Referral() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Referral(Referral referral) + : base(referral) { } +#pragma warning restore CS8618 + + public Referral(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Referral(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Referral FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ReferralFromRaw : IFromRawJson +{ + /// + public Referral FromRawUnchecked(IReadOnlyDictionary rawData) => + Referral.FromRawUnchecked(rawData); +} + +/// +/// Type of media on the ad, when it had any. +/// +[JsonConverter(typeof(MediaTypeConverter))] +public enum MediaType +{ + Image, + Video, +} + +sealed class MediaTypeConverter : JsonConverter +{ + public override MediaType Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "image" => MediaType.Image, + "video" => MediaType.Video, + _ => (MediaType)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + MediaType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + MediaType.Image => "image", + MediaType.Video => "video", + _ => throw new ZavudevInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Where the click came from. +/// +[JsonConverter(typeof(SourceTypeConverter))] +public enum SourceType +{ + Ad, + Post, +} + +sealed class SourceTypeConverter : JsonConverter +{ + public override SourceType Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "ad" => SourceType.Ad, + "post" => SourceType.Post, + _ => (SourceType)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + SourceType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + SourceType.Ad => "ad", + SourceType.Post => "post", + _ => throw new ZavudevInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + [JsonConverter(typeof(JsonModelConverter))] public sealed record class Section : JsonModel { diff --git a/src/Zavudev/Models/Senders/WebhookEvent.cs b/src/Zavudev/Models/Senders/WebhookEvent.cs index cef13a1..f3103b5 100644 --- a/src/Zavudev/Models/Senders/WebhookEvent.cs +++ b/src/Zavudev/Models/Senders/WebhookEvent.cs @@ -27,8 +27,12 @@ namespace Zavudev.Models.Senders; /// receive time in Unix milliseconds (the moment the channel received the message /// from the contact — WhatsApp, Telegram, Instagram, Messenger; `null` for SMS and /// email). Compare it against the top-level `timestamp` (when Zavu dispatched the -/// webhook) to detect and ignore delayed deliveries. - `message.unsupported`: Received -/// a message type that is not supported +/// webhook) to detect and ignore delayed deliveries. When the conversation was opened +/// from a Click-to-WhatsApp ad or post, `data.referral` carries the ad attribution +/// — including `ctwaClid`, the identifier Meta's Conversions API needs to credit +/// a conversion back to that ad. WhatsApp only, and only on the first message of +/// the thread: it is absent from every later message, so persist it when it arrives. +/// - `message.unsupported`: Received a message type that is not supported /// /// **Broadcast events:** - `broadcast.status_changed`: Broadcast status changed /// (pending_review, approved, rejected, sending, completed, cancelled) From 2b103ddb1eaab89819d650662e802e5322e8d306 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2026 20:34:44 +0000 Subject: [PATCH 07/20] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index c8f40a3..d1cb2f5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-8ead699d165bc116549ef757ace827bf39add6e0e19afdc1653f0d0282947eae.yml -openapi_spec_hash: 5a35a5dab833f4d1e6deeea795e1abf2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-a3e89fc19e2d04b767db8da79617311c572cc2a0273abf1f211c77999c164a71.yml +openapi_spec_hash: 920231951c3213b2a70bdddf6f4922fb config_hash: 7fca08d9dae18513f5d7451cea11e34c From 3b8b30b4f611031121a795e7d256d3675bc5100c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 12:41:44 +0000 Subject: [PATCH 08/20] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index d1cb2f5..968d7bb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-a3e89fc19e2d04b767db8da79617311c572cc2a0273abf1f211c77999c164a71.yml -openapi_spec_hash: 920231951c3213b2a70bdddf6f4922fb +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5f801823f89d92d2c00f2bc33b30aa7b127f21b5f3fb38f9278f06ce1d79b869.yml +openapi_spec_hash: d1baedab2c021ab97b4aba0030180b29 config_hash: 7fca08d9dae18513f5d7451cea11e34c From 1bcce5a7cde22f37b0e236a768a5e681dbae78c6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 13:06:48 +0000 Subject: [PATCH 09/20] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 968d7bb..ec5b810 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-5f801823f89d92d2c00f2bc33b30aa7b127f21b5f3fb38f9278f06ce1d79b869.yml -openapi_spec_hash: d1baedab2c021ab97b4aba0030180b29 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-9acbefe4fc4908c698c1165bed491d415687f0092e9df6ffb968f92e43b11961.yml +openapi_spec_hash: 410cb45b6fa3a8a1f3da3aba50accbdc config_hash: 7fca08d9dae18513f5d7451cea11e34c From 18aecff0d1bd009748ef781eb2261cc219ed6aef Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 13:23:46 +0000 Subject: [PATCH 10/20] feat(api): manual updates --- .stats.yml | 6 +- .../AgentTemplateListParamsTest.cs | 1 + .../AgentTemplateListResponseTest.cs | 347 +++++ .../AgentTemplateRetrieveParamsTest.cs | 39 + .../AgentTemplateRetrieveResponseTest.cs | 523 ++++++++ .../Models/Agents/AgentCreateParamsTest.cs | 677 ++++++++++ .../Models/Agents/AgentCreateResponseTest.cs | 414 ++++++ .../Models/Agents/AgentDeleteParamsTest.cs | 37 + .../Agents/AgentListPageResponseTest.cs | 702 ++++++++++ .../Models/Agents/AgentListParamsTest.cs | 71 + .../Agents/AgentListVoicesParamsTest.cs | 61 + .../Agents/AgentListVoicesResponseTest.cs | 350 +++++ .../Models/Agents/AgentRetrieveParamsTest.cs | 37 + .../Agents/AgentRetrieveResponseTest.cs | 414 ++++++ .../Models/Agents/AgentTestParamsTest.cs | 234 ++++ .../Models/Agents/AgentTestResponseTest.cs | 450 +++++++ .../Models/Agents/AgentUpdateParamsTest.cs | 802 +++++++++++ .../Models/Agents/AgentUpdateResponseTest.cs | 414 ++++++ .../Agents/Senders/SenderConnectParamsTest.cs | 41 + .../Senders/SenderConnectResponseTest.cs | 414 ++++++ .../Senders/SenderDisconnectParamsTest.cs | 44 + .../Models/Calls/CallCreateParamsTest.cs | 115 ++ .../Models/Calls/CallCreateResponseTest.cs | 1190 ++++++++++++++++ .../Models/Calls/CallHangupParamsTest.cs | 39 + .../Models/Calls/CallHangupResponseTest.cs | 1194 +++++++++++++++++ .../Models/Calls/CallListPageResponseTest.cs | 481 +++++++ .../Models/Calls/CallListParamsTest.cs | 236 ++++ .../Models/Calls/CallListResponseTest.cs | 932 +++++++++++++ .../Models/Calls/CallRetrieveParamsTest.cs | 37 + .../Models/Calls/CallRetrieveResponseTest.cs | 1194 +++++++++++++++++ ...onversationListMessagesPageResponseTest.cs | 1175 ++++++++++++++++ .../ConversationListMessagesParamsTest.cs | 92 ++ .../ConversationListPageResponseTest.cs | 482 +++++++ .../ConversationListParamsTest.cs | 188 +++ .../ConversationListResponseTest.cs | 856 ++++++++++++ .../ConversationMarkAsReadParamsTest.cs | 42 + .../ConversationMarkAsReadResponseTest.cs | 1165 ++++++++++++++++ .../ConversationRetrieveParamsTest.cs | 39 + .../ConversationRetrieveResponseTest.cs | 1107 +++++++++++++++ .../EmailDomainCreateParamsTest.cs | 37 + .../EmailDomainCreateResponseTest.cs | 717 ++++++++++ .../EmailDomainDeleteParamsTest.cs | 39 + .../EmailDomains/EmailDomainListParamsTest.cs | 1 + .../EmailDomainListResponseTest.cs | 749 +++++++++++ .../EmailDomainRetrieveParamsTest.cs | 39 + .../EmailDomainRetrieveResponseTest.cs | 726 ++++++++++ .../EmailDomainVerifyParamsTest.cs | 42 + .../EmailDomainVerifyResponseTest.cs | 726 ++++++++++ .../FunctionListDeploymentsParamsTest.cs | 76 ++ .../FunctionListDeploymentsResponseTest.cs | 554 ++++++++ .../FunctionListEventTypesParamsTest.cs | 1 + .../FunctionListEventTypesResponseTest.cs | 91 ++ .../FunctionRollbackDeploymentParamsTest.cs | 56 + .../FunctionRollbackDeploymentResponseTest.cs | 712 ++++++++++ .../GitLink/GitLinkDeployNowParamsTest.cs | 42 + .../GitLink/GitLinkDeployNowResponseTest.cs | 67 + .../GitLink/GitLinkLinkParamsTest.cs | 114 ++ .../GitLink/GitLinkLinkResponseTest.cs | 858 ++++++++++++ .../GitLink/GitLinkRetrieveParamsTest.cs | 42 + .../GitLink/GitLinkRetrieveResponseTest.cs | 852 ++++++++++++ .../GitLink/GitLinkUnlinkParamsTest.cs | 42 + .../GitLink/GitLinkUpdateParamsTest.cs | 120 ++ .../GitLink/GitLinkUpdateResponseTest.cs | 854 ++++++++++++ .../Triggers/TriggerCreateParamsTest.cs | 105 ++ .../Triggers/TriggerCreateResponseTest.cs | 458 +++++++ .../Triggers/TriggerDeleteParamsTest.cs | 39 + .../Triggers/TriggerListParamsTest.cs | 42 + .../Triggers/TriggerListResponseTest.cs | 443 ++++++ .../Triggers/TriggerUpdateParamsTest.cs | 41 + .../Triggers/TriggerUpdateResponseTest.cs | 71 + .../IntrospectValidateEmailParamsTest.cs | 80 ++ .../IntrospectValidateEmailResponseTest.cs | 563 ++++++++ .../MessageListAttachmentsParamsTest.cs | 42 + .../MessageListAttachmentsResponseTest.cs | 317 +++++ .../DocumentRetrieveDocumentParamsTest.cs | 63 + .../DocumentRetrieveDocumentResponseTest.cs | 154 +++ .../DocumentUpdateDocumentParamsTest.cs | 107 ++ .../DocumentUpdateDocumentResponseTest.cs | 154 +++ .../Agent/Tools/ToolListTestRunsParamsTest.cs | 88 ++ .../Tools/ToolListTestRunsResponseTest.cs | 563 ++++++++ .../Webhook/WebhookRotateSecretParamsTest.cs | 46 + .../Telegram/TelegramConnectParamsTest.cs | 41 + .../Telegram/TelegramConnectResponseTest.cs | 264 ++++ .../Telegram/TelegramDisconnectParamsTest.cs | 39 + .../Templates/TemplateSyncParamsTest.cs | 59 + .../Templates/TemplateSyncResponseTest.cs | 136 ++ .../Models/Urls/UrlEscalateParamsTest.cs | 53 + .../Models/Urls/UrlEscalateResponseTest.cs | 155 +++ .../Services/AgentServiceTest.cs | 79 ++ .../Services/AgentTemplateServiceTest.cs | 27 + .../Services/Agents/SenderServiceTest.cs | 27 + src/Zavudev.Tests/Services/CallServiceTest.cs | 45 + .../Services/ConversationServiceTest.cs | 49 + .../Services/EmailDomainServiceTest.cs | 58 + .../Services/FunctionServiceTest.cs | 32 + .../Services/Functions/GitLinkServiceTest.cs | 60 + .../Services/Functions/TriggerServiceTest.cs | 49 + .../Services/IntrospectServiceTest.cs | 10 + .../Services/MessageServiceTest.cs | 11 + .../KnowledgeBases/DocumentServiceTest.cs | 22 + .../Services/Senders/Agent/ToolServiceTest.cs | 11 + .../Senders/Agent/Tools/WebhookServiceTest.cs | 17 + .../Services/Senders/TelegramServiceTest.cs | 27 + .../Services/TemplateServiceTest.cs | 10 + src/Zavudev.Tests/Services/UrlServiceTest.cs | 11 + src/Zavudev/Core/ModelBase.cs | 58 + src/Zavudev/IZavudevClient.cs | 20 + .../AgentTemplates/AgentTemplateListParams.cs | 110 ++ .../AgentTemplateListResponse.cs | 251 ++++ .../AgentTemplateRetrieveParams.cs | 125 ++ .../AgentTemplateRetrieveResponse.cs | 439 ++++++ .../Models/Agents/AgentCreateParams.cs | 819 +++++++++++ .../Models/Agents/AgentCreateResponse.cs | 75 ++ .../Models/Agents/AgentDeleteParams.cs | 121 ++ src/Zavudev/Models/Agents/AgentListPage.cs | 82 ++ .../Models/Agents/AgentListPageResponse.cs | 94 ++ src/Zavudev/Models/Agents/AgentListParams.cs | 146 ++ .../Models/Agents/AgentListVoicesParams.cs | 131 ++ .../Models/Agents/AgentListVoicesResponse.cs | 196 +++ .../Models/Agents/AgentRetrieveParams.cs | 121 ++ .../Models/Agents/AgentRetrieveResponse.cs | 76 ++ src/Zavudev/Models/Agents/AgentTestParams.cs | 349 +++++ .../Models/Agents/AgentTestResponse.cs | 277 ++++ .../Models/Agents/AgentUpdateParams.cs | 876 ++++++++++++ .../Models/Agents/AgentUpdateResponse.cs | 75 ++ .../Agents/Senders/SenderConnectParams.cs | 163 +++ .../Agents/Senders/SenderConnectResponse.cs | 76 ++ .../Agents/Senders/SenderDisconnectParams.cs | 132 ++ src/Zavudev/Models/Calls/CallCreateParams.cs | 274 ++++ .../Models/Calls/CallCreateResponse.cs | 639 +++++++++ src/Zavudev/Models/Calls/CallHangupParams.cs | 123 ++ .../Models/Calls/CallHangupResponse.cs | 666 +++++++++ src/Zavudev/Models/Calls/CallListPage.cs | 82 ++ .../Models/Calls/CallListPageResponse.cs | 93 ++ src/Zavudev/Models/Calls/CallListParams.cs | 306 +++++ src/Zavudev/Models/Calls/CallListResponse.cs | 593 ++++++++ .../Models/Calls/CallRetrieveParams.cs | 122 ++ .../Models/Calls/CallRetrieveResponse.cs | 669 +++++++++ .../ConversationListMessagesPage.cs | 84 ++ .../ConversationListMessagesPageResponse.cs | 102 ++ .../ConversationListMessagesParams.cs | 165 +++ .../Conversations/ConversationListPage.cs | 82 ++ .../ConversationListPageResponse.cs | 97 ++ .../Conversations/ConversationListParams.cs | 291 ++++ .../Conversations/ConversationListResponse.cs | 658 +++++++++ .../ConversationMarkAsReadParams.cs | 123 ++ .../ConversationMarkAsReadResponse.cs | 768 +++++++++++ .../ConversationRetrieveParams.cs | 122 ++ .../ConversationRetrieveResponse.cs | 692 ++++++++++ .../EmailDomains/EmailDomainCreateParams.cs | 150 +++ .../EmailDomains/EmailDomainCreateResponse.cs | 379 ++++++ .../EmailDomains/EmailDomainDeleteParams.cs | 122 ++ .../EmailDomains/EmailDomainListParams.cs | 108 ++ .../EmailDomains/EmailDomainListResponse.cs | 390 ++++++ .../EmailDomains/EmailDomainRetrieveParams.cs | 122 ++ .../EmailDomainRetrieveResponse.cs | 411 ++++++ .../EmailDomains/EmailDomainVerifyParams.cs | 122 ++ .../EmailDomains/EmailDomainVerifyResponse.cs | 411 ++++++ .../FunctionListDeploymentsParams.cs | 143 ++ .../FunctionListDeploymentsResponse.cs | 347 +++++ .../Functions/FunctionListEventTypesParams.cs | 110 ++ .../FunctionListEventTypesResponse.cs | 86 ++ .../FunctionRollbackDeploymentParams.cs | 166 +++ .../FunctionRollbackDeploymentResponse.cs | 371 +++++ .../GitLink/GitLinkDeployNowParams.cs | 124 ++ .../GitLink/GitLinkDeployNowResponse.cs | 74 + .../Functions/GitLink/GitLinkLinkParams.cs | 236 ++++ .../Functions/GitLink/GitLinkLinkResponse.cs | 504 +++++++ .../GitLink/GitLinkRetrieveParams.cs | 122 ++ .../GitLink/GitLinkRetrieveResponse.cs | 489 +++++++ .../Functions/GitLink/GitLinkUnlinkParams.cs | 123 ++ .../Functions/GitLink/GitLinkUpdateParams.cs | 195 +++ .../GitLink/GitLinkUpdateResponse.cs | 507 +++++++ .../Functions/Triggers/TriggerCreateParams.cs | 217 +++ .../Triggers/TriggerCreateResponse.cs | 275 ++++ .../Functions/Triggers/TriggerDeleteParams.cs | 122 ++ .../Functions/Triggers/TriggerListParams.cs | 122 ++ .../Functions/Triggers/TriggerListResponse.cs | 263 ++++ .../Functions/Triggers/TriggerUpdateParams.cs | 158 +++ .../Triggers/TriggerUpdateResponse.cs | 76 ++ .../IntrospectValidateEmailParams.cs | 192 +++ .../IntrospectValidateEmailResponse.cs | 427 ++++++ .../Messages/MessageListAttachmentsParams.cs | 127 ++ .../MessageListAttachmentsResponse.cs | 243 ++++ .../DocumentRetrieveDocumentParams.cs | 147 ++ .../DocumentRetrieveDocumentResponse.cs | 79 ++ .../Documents/DocumentUpdateDocumentParams.cs | 208 +++ .../DocumentUpdateDocumentResponse.cs | 79 ++ .../Agent/Tools/ToolListTestRunsParams.cs | 155 +++ .../Agent/Tools/ToolListTestRunsResponse.cs | 258 ++++ .../Webhook/WebhookRotateSecretParams.cs | 137 ++ .../Senders/Telegram/TelegramConnectParams.cs | 163 +++ .../Telegram/TelegramConnectResponse.cs | 181 +++ .../Telegram/TelegramDisconnectParams.cs | 122 ++ .../Models/Templates/TemplateSyncParams.cs | 176 +++ .../Models/Templates/TemplateSyncResponse.cs | 148 ++ src/Zavudev/Models/Urls/UrlEscalateParams.cs | 162 +++ .../Models/Urls/UrlEscalateResponse.cs | 75 ++ src/Zavudev/Services/AgentService.cs | 446 ++++++ src/Zavudev/Services/AgentTemplateService.cs | 168 +++ src/Zavudev/Services/Agents/ISenderService.cs | 107 ++ src/Zavudev/Services/Agents/SenderService.cs | 167 +++ src/Zavudev/Services/CallService.cs | 273 ++++ src/Zavudev/Services/ConversationService.cs | 342 +++++ src/Zavudev/Services/EmailDomainService.cs | 328 +++++ src/Zavudev/Services/FunctionService.cs | 216 +++ .../Services/Functions/GitLinkService.cs | 378 ++++++ .../Services/Functions/IGitLinkService.cs | 209 +++ .../Services/Functions/ITriggerService.cs | 171 +++ .../Services/Functions/TriggerService.cs | 305 +++++ src/Zavudev/Services/IAgentService.cs | 238 ++++ src/Zavudev/Services/IAgentTemplateService.cs | 95 ++ src/Zavudev/Services/ICallService.cs | 153 +++ src/Zavudev/Services/IConversationService.cs | 161 +++ src/Zavudev/Services/IEmailDomainService.cs | 168 +++ src/Zavudev/Services/IFunctionService.cs | 94 ++ src/Zavudev/Services/IIntrospectService.cs | 26 + src/Zavudev/Services/IMessageService.cs | 36 + src/Zavudev/Services/ISenderService.cs | 14 +- src/Zavudev/Services/ITemplateService.cs | 37 + src/Zavudev/Services/IUrlService.cs | 32 + src/Zavudev/Services/IntrospectService.cs | 42 + src/Zavudev/Services/MessageService.cs | 69 + src/Zavudev/Services/SenderService.cs | 40 +- .../Services/Senders/Agent/IToolService.cs | 38 + .../Agent/KnowledgeBases/DocumentService.cs | 130 ++ .../Agent/KnowledgeBases/IDocumentService.cs | 63 + .../Services/Senders/Agent/ToolService.cs | 81 ++ .../Senders/Agent/Tools/IWebhookService.cs | 76 ++ .../Senders/Agent/Tools/WebhookService.cs | 119 ++ .../Services/Senders/ITelegramService.cs | 106 ++ .../Services/Senders/TelegramService.cs | 171 +++ src/Zavudev/Services/TemplateService.cs | 42 + src/Zavudev/Services/UrlService.cs | 65 + src/Zavudev/ZavudevClient.cs | 70 + 235 files changed, 55809 insertions(+), 21 deletions(-) create mode 100644 src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateListParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateListResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateRetrieveParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateRetrieveResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentCreateParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentCreateResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentDeleteParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentListPageResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentListParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentListVoicesParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentListVoicesResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentRetrieveParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentRetrieveResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentTestParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentTestResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentUpdateParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/AgentUpdateResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/Senders/SenderConnectParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/Senders/SenderConnectResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Agents/Senders/SenderDisconnectParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Calls/CallCreateParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Calls/CallCreateResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Calls/CallHangupParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Calls/CallHangupResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Calls/CallListPageResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Calls/CallListParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Calls/CallListResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Calls/CallRetrieveParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Calls/CallRetrieveResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Conversations/ConversationListMessagesPageResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Conversations/ConversationListMessagesParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Conversations/ConversationListPageResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Conversations/ConversationListParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Conversations/ConversationListResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Conversations/ConversationMarkAsReadParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Conversations/ConversationMarkAsReadResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Conversations/ConversationRetrieveParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Conversations/ConversationRetrieveResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/EmailDomains/EmailDomainCreateParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/EmailDomains/EmailDomainCreateResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/EmailDomains/EmailDomainDeleteParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/EmailDomains/EmailDomainListParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/EmailDomains/EmailDomainListResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/EmailDomains/EmailDomainRetrieveParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/EmailDomains/EmailDomainRetrieveResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/EmailDomains/EmailDomainVerifyParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/EmailDomains/EmailDomainVerifyResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/FunctionListDeploymentsParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/FunctionListDeploymentsResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/FunctionListEventTypesParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/FunctionListEventTypesResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/FunctionRollbackDeploymentParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/FunctionRollbackDeploymentResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/GitLink/GitLinkDeployNowParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/GitLink/GitLinkDeployNowResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/GitLink/GitLinkLinkParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/GitLink/GitLinkLinkResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/GitLink/GitLinkRetrieveParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/GitLink/GitLinkRetrieveResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/GitLink/GitLinkUnlinkParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/GitLink/GitLinkUpdateParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/GitLink/GitLinkUpdateResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/Triggers/TriggerCreateParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/Triggers/TriggerCreateResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/Triggers/TriggerDeleteParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/Triggers/TriggerListParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/Triggers/TriggerListResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/Triggers/TriggerUpdateParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Functions/Triggers/TriggerUpdateResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Introspect/IntrospectValidateEmailParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Introspect/IntrospectValidateEmailResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Messages/MessageListAttachmentsParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Messages/MessageListAttachmentsResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Senders/Agent/KnowledgeBases/Documents/DocumentRetrieveDocumentParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Senders/Agent/KnowledgeBases/Documents/DocumentRetrieveDocumentResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Senders/Agent/KnowledgeBases/Documents/DocumentUpdateDocumentParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Senders/Agent/KnowledgeBases/Documents/DocumentUpdateDocumentResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Senders/Agent/Tools/ToolListTestRunsParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Senders/Agent/Tools/ToolListTestRunsResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Senders/Agent/Tools/Webhook/WebhookRotateSecretParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Senders/Telegram/TelegramConnectParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Senders/Telegram/TelegramConnectResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Senders/Telegram/TelegramDisconnectParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Templates/TemplateSyncParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Templates/TemplateSyncResponseTest.cs create mode 100644 src/Zavudev.Tests/Models/Urls/UrlEscalateParamsTest.cs create mode 100644 src/Zavudev.Tests/Models/Urls/UrlEscalateResponseTest.cs create mode 100644 src/Zavudev.Tests/Services/AgentServiceTest.cs create mode 100644 src/Zavudev.Tests/Services/AgentTemplateServiceTest.cs create mode 100644 src/Zavudev.Tests/Services/Agents/SenderServiceTest.cs create mode 100644 src/Zavudev.Tests/Services/CallServiceTest.cs create mode 100644 src/Zavudev.Tests/Services/ConversationServiceTest.cs create mode 100644 src/Zavudev.Tests/Services/EmailDomainServiceTest.cs create mode 100644 src/Zavudev.Tests/Services/Functions/GitLinkServiceTest.cs create mode 100644 src/Zavudev.Tests/Services/Functions/TriggerServiceTest.cs create mode 100644 src/Zavudev.Tests/Services/Senders/Agent/Tools/WebhookServiceTest.cs create mode 100644 src/Zavudev.Tests/Services/Senders/TelegramServiceTest.cs create mode 100644 src/Zavudev/Models/AgentTemplates/AgentTemplateListParams.cs create mode 100644 src/Zavudev/Models/AgentTemplates/AgentTemplateListResponse.cs create mode 100644 src/Zavudev/Models/AgentTemplates/AgentTemplateRetrieveParams.cs create mode 100644 src/Zavudev/Models/AgentTemplates/AgentTemplateRetrieveResponse.cs create mode 100644 src/Zavudev/Models/Agents/AgentCreateParams.cs create mode 100644 src/Zavudev/Models/Agents/AgentCreateResponse.cs create mode 100644 src/Zavudev/Models/Agents/AgentDeleteParams.cs create mode 100644 src/Zavudev/Models/Agents/AgentListPage.cs create mode 100644 src/Zavudev/Models/Agents/AgentListPageResponse.cs create mode 100644 src/Zavudev/Models/Agents/AgentListParams.cs create mode 100644 src/Zavudev/Models/Agents/AgentListVoicesParams.cs create mode 100644 src/Zavudev/Models/Agents/AgentListVoicesResponse.cs create mode 100644 src/Zavudev/Models/Agents/AgentRetrieveParams.cs create mode 100644 src/Zavudev/Models/Agents/AgentRetrieveResponse.cs create mode 100644 src/Zavudev/Models/Agents/AgentTestParams.cs create mode 100644 src/Zavudev/Models/Agents/AgentTestResponse.cs create mode 100644 src/Zavudev/Models/Agents/AgentUpdateParams.cs create mode 100644 src/Zavudev/Models/Agents/AgentUpdateResponse.cs create mode 100644 src/Zavudev/Models/Agents/Senders/SenderConnectParams.cs create mode 100644 src/Zavudev/Models/Agents/Senders/SenderConnectResponse.cs create mode 100644 src/Zavudev/Models/Agents/Senders/SenderDisconnectParams.cs create mode 100644 src/Zavudev/Models/Calls/CallCreateParams.cs create mode 100644 src/Zavudev/Models/Calls/CallCreateResponse.cs create mode 100644 src/Zavudev/Models/Calls/CallHangupParams.cs create mode 100644 src/Zavudev/Models/Calls/CallHangupResponse.cs create mode 100644 src/Zavudev/Models/Calls/CallListPage.cs create mode 100644 src/Zavudev/Models/Calls/CallListPageResponse.cs create mode 100644 src/Zavudev/Models/Calls/CallListParams.cs create mode 100644 src/Zavudev/Models/Calls/CallListResponse.cs create mode 100644 src/Zavudev/Models/Calls/CallRetrieveParams.cs create mode 100644 src/Zavudev/Models/Calls/CallRetrieveResponse.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationListMessagesPage.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationListMessagesPageResponse.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationListMessagesParams.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationListPage.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationListPageResponse.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationListParams.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationListResponse.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationMarkAsReadParams.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationMarkAsReadResponse.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationRetrieveParams.cs create mode 100644 src/Zavudev/Models/Conversations/ConversationRetrieveResponse.cs create mode 100644 src/Zavudev/Models/EmailDomains/EmailDomainCreateParams.cs create mode 100644 src/Zavudev/Models/EmailDomains/EmailDomainCreateResponse.cs create mode 100644 src/Zavudev/Models/EmailDomains/EmailDomainDeleteParams.cs create mode 100644 src/Zavudev/Models/EmailDomains/EmailDomainListParams.cs create mode 100644 src/Zavudev/Models/EmailDomains/EmailDomainListResponse.cs create mode 100644 src/Zavudev/Models/EmailDomains/EmailDomainRetrieveParams.cs create mode 100644 src/Zavudev/Models/EmailDomains/EmailDomainRetrieveResponse.cs create mode 100644 src/Zavudev/Models/EmailDomains/EmailDomainVerifyParams.cs create mode 100644 src/Zavudev/Models/EmailDomains/EmailDomainVerifyResponse.cs create mode 100644 src/Zavudev/Models/Functions/FunctionListDeploymentsParams.cs create mode 100644 src/Zavudev/Models/Functions/FunctionListDeploymentsResponse.cs create mode 100644 src/Zavudev/Models/Functions/FunctionListEventTypesParams.cs create mode 100644 src/Zavudev/Models/Functions/FunctionListEventTypesResponse.cs create mode 100644 src/Zavudev/Models/Functions/FunctionRollbackDeploymentParams.cs create mode 100644 src/Zavudev/Models/Functions/FunctionRollbackDeploymentResponse.cs create mode 100644 src/Zavudev/Models/Functions/GitLink/GitLinkDeployNowParams.cs create mode 100644 src/Zavudev/Models/Functions/GitLink/GitLinkDeployNowResponse.cs create mode 100644 src/Zavudev/Models/Functions/GitLink/GitLinkLinkParams.cs create mode 100644 src/Zavudev/Models/Functions/GitLink/GitLinkLinkResponse.cs create mode 100644 src/Zavudev/Models/Functions/GitLink/GitLinkRetrieveParams.cs create mode 100644 src/Zavudev/Models/Functions/GitLink/GitLinkRetrieveResponse.cs create mode 100644 src/Zavudev/Models/Functions/GitLink/GitLinkUnlinkParams.cs create mode 100644 src/Zavudev/Models/Functions/GitLink/GitLinkUpdateParams.cs create mode 100644 src/Zavudev/Models/Functions/GitLink/GitLinkUpdateResponse.cs create mode 100644 src/Zavudev/Models/Functions/Triggers/TriggerCreateParams.cs create mode 100644 src/Zavudev/Models/Functions/Triggers/TriggerCreateResponse.cs create mode 100644 src/Zavudev/Models/Functions/Triggers/TriggerDeleteParams.cs create mode 100644 src/Zavudev/Models/Functions/Triggers/TriggerListParams.cs create mode 100644 src/Zavudev/Models/Functions/Triggers/TriggerListResponse.cs create mode 100644 src/Zavudev/Models/Functions/Triggers/TriggerUpdateParams.cs create mode 100644 src/Zavudev/Models/Functions/Triggers/TriggerUpdateResponse.cs create mode 100644 src/Zavudev/Models/Introspect/IntrospectValidateEmailParams.cs create mode 100644 src/Zavudev/Models/Introspect/IntrospectValidateEmailResponse.cs create mode 100644 src/Zavudev/Models/Messages/MessageListAttachmentsParams.cs create mode 100644 src/Zavudev/Models/Messages/MessageListAttachmentsResponse.cs create mode 100644 src/Zavudev/Models/Senders/Agent/KnowledgeBases/Documents/DocumentRetrieveDocumentParams.cs create mode 100644 src/Zavudev/Models/Senders/Agent/KnowledgeBases/Documents/DocumentRetrieveDocumentResponse.cs create mode 100644 src/Zavudev/Models/Senders/Agent/KnowledgeBases/Documents/DocumentUpdateDocumentParams.cs create mode 100644 src/Zavudev/Models/Senders/Agent/KnowledgeBases/Documents/DocumentUpdateDocumentResponse.cs create mode 100644 src/Zavudev/Models/Senders/Agent/Tools/ToolListTestRunsParams.cs create mode 100644 src/Zavudev/Models/Senders/Agent/Tools/ToolListTestRunsResponse.cs create mode 100644 src/Zavudev/Models/Senders/Agent/Tools/Webhook/WebhookRotateSecretParams.cs create mode 100644 src/Zavudev/Models/Senders/Telegram/TelegramConnectParams.cs create mode 100644 src/Zavudev/Models/Senders/Telegram/TelegramConnectResponse.cs create mode 100644 src/Zavudev/Models/Senders/Telegram/TelegramDisconnectParams.cs create mode 100644 src/Zavudev/Models/Templates/TemplateSyncParams.cs create mode 100644 src/Zavudev/Models/Templates/TemplateSyncResponse.cs create mode 100644 src/Zavudev/Models/Urls/UrlEscalateParams.cs create mode 100644 src/Zavudev/Models/Urls/UrlEscalateResponse.cs create mode 100644 src/Zavudev/Services/AgentService.cs create mode 100644 src/Zavudev/Services/AgentTemplateService.cs create mode 100644 src/Zavudev/Services/Agents/ISenderService.cs create mode 100644 src/Zavudev/Services/Agents/SenderService.cs create mode 100644 src/Zavudev/Services/CallService.cs create mode 100644 src/Zavudev/Services/ConversationService.cs create mode 100644 src/Zavudev/Services/EmailDomainService.cs create mode 100644 src/Zavudev/Services/Functions/GitLinkService.cs create mode 100644 src/Zavudev/Services/Functions/IGitLinkService.cs create mode 100644 src/Zavudev/Services/Functions/ITriggerService.cs create mode 100644 src/Zavudev/Services/Functions/TriggerService.cs create mode 100644 src/Zavudev/Services/IAgentService.cs create mode 100644 src/Zavudev/Services/IAgentTemplateService.cs create mode 100644 src/Zavudev/Services/ICallService.cs create mode 100644 src/Zavudev/Services/IConversationService.cs create mode 100644 src/Zavudev/Services/IEmailDomainService.cs create mode 100644 src/Zavudev/Services/Senders/Agent/Tools/IWebhookService.cs create mode 100644 src/Zavudev/Services/Senders/Agent/Tools/WebhookService.cs create mode 100644 src/Zavudev/Services/Senders/ITelegramService.cs create mode 100644 src/Zavudev/Services/Senders/TelegramService.cs diff --git a/.stats.yml b/.stats.yml index ec5b810..906e6fe 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 138 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-9acbefe4fc4908c698c1165bed491d415687f0092e9df6ffb968f92e43b11961.yml +configured_endpoints: 184 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-ba782c22cc1b237327afbf2aeffb453185cec9513d918b5a0fdc7e7233320c9b.yml openapi_spec_hash: 410cb45b6fa3a8a1f3da3aba50accbdc -config_hash: 7fca08d9dae18513f5d7451cea11e34c +config_hash: 261e1b852ca7f8364e4a283a3edd350a diff --git a/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateListParamsTest.cs b/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateListParamsTest.cs new file mode 100644 index 0000000..b6ba8d8 --- /dev/null +++ b/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateListParamsTest.cs @@ -0,0 +1 @@ +namespace Zavudev.Tests.Models.AgentTemplates; diff --git a/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateListResponseTest.cs b/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateListResponseTest.cs new file mode 100644 index 0000000..c28ba8c --- /dev/null +++ b/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateListResponseTest.cs @@ -0,0 +1,347 @@ +using System.Collections.Generic; +using System.Text.Json; +using Zavudev.Core; +using Zavudev.Exceptions; +using Zavudev.Models.AgentTemplates; + +namespace Zavudev.Tests.Models.AgentTemplates; + +public class AgentTemplateListResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AgentTemplateListResponse + { + Items = + [ + new() + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }, + ], + }; + + List expectedItems = + [ + new() + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }, + ]; + + Assert.Equal(expectedItems.Count, model.Items.Count); + for (int i = 0; i < expectedItems.Count; i++) + { + Assert.Equal(expectedItems[i], model.Items[i]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AgentTemplateListResponse + { + Items = + [ + new() + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }, + ], + }; + + 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 AgentTemplateListResponse + { + Items = + [ + new() + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }, + ], + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + List expectedItems = + [ + new() + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }, + ]; + + Assert.Equal(expectedItems.Count, deserialized.Items.Count); + for (int i = 0; i < expectedItems.Count; i++) + { + Assert.Equal(expectedItems[i], deserialized.Items[i]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new AgentTemplateListResponse + { + Items = + [ + new() + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }, + ], + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AgentTemplateListResponse + { + Items = + [ + new() + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }, + ], + }; + + AgentTemplateListResponse copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ItemTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Item + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }; + + string expectedID = "fermi"; + ApiEnum expectedCategory = ItemCategory.Sales; + string expectedName = "name"; + string expectedSummary = "summary"; + long expectedToolCount = 0; + bool expectedVoice = true; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedCategory, model.Category); + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedSummary, model.Summary); + Assert.Equal(expectedToolCount, model.ToolCount); + Assert.Equal(expectedVoice, model.Voice); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Item + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }; + + 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 Item + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + Assert.NotNull(deserialized); + + string expectedID = "fermi"; + ApiEnum expectedCategory = ItemCategory.Sales; + string expectedName = "name"; + string expectedSummary = "summary"; + long expectedToolCount = 0; + bool expectedVoice = true; + + Assert.Equal(expectedID, deserialized.ID); + Assert.Equal(expectedCategory, deserialized.Category); + Assert.Equal(expectedName, deserialized.Name); + Assert.Equal(expectedSummary, deserialized.Summary); + Assert.Equal(expectedToolCount, deserialized.ToolCount); + Assert.Equal(expectedVoice, deserialized.Voice); + } + + [Fact] + public void Validation_Works() + { + var model = new Item + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Item + { + ID = "fermi", + Category = ItemCategory.Sales, + Name = "name", + Summary = "summary", + ToolCount = 0, + Voice = true, + }; + + Item copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ItemCategoryTest : TestBase +{ + [Theory] + [InlineData(ItemCategory.Sales)] + [InlineData(ItemCategory.Support)] + [InlineData(ItemCategory.FrontDesk)] + [InlineData(ItemCategory.Ops)] + public void Validation_Works(ItemCategory 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(ItemCategory.Sales)] + [InlineData(ItemCategory.Support)] + [InlineData(ItemCategory.FrontDesk)] + [InlineData(ItemCategory.Ops)] + public void SerializationRoundtrip_Works(ItemCategory 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/AgentTemplates/AgentTemplateRetrieveParamsTest.cs b/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateRetrieveParamsTest.cs new file mode 100644 index 0000000..9d22f68 --- /dev/null +++ b/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateRetrieveParamsTest.cs @@ -0,0 +1,39 @@ +using System; +using Zavudev.Models.AgentTemplates; + +namespace Zavudev.Tests.Models.AgentTemplates; + +public class AgentTemplateRetrieveParamsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var parameters = new AgentTemplateRetrieveParams { TemplateID = "fermi" }; + + string expectedTemplateID = "fermi"; + + Assert.Equal(expectedTemplateID, parameters.TemplateID); + } + + [Fact] + public void Url_Works() + { + AgentTemplateRetrieveParams parameters = new() { TemplateID = "fermi" }; + + var url = parameters.Url(new() { ApiKey = "My API Key" }); + + Assert.True( + TestBase.UrisEqual(new Uri("https://api.zavu.dev/v1/agent-templates/fermi"), url) + ); + } + + [Fact] + public void CopyConstructor_Works() + { + var parameters = new AgentTemplateRetrieveParams { TemplateID = "fermi" }; + + AgentTemplateRetrieveParams copied = new(parameters); + + Assert.Equal(parameters, copied); + } +} diff --git a/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateRetrieveResponseTest.cs b/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateRetrieveResponseTest.cs new file mode 100644 index 0000000..8522bf9 --- /dev/null +++ b/src/Zavudev.Tests/Models/AgentTemplates/AgentTemplateRetrieveResponseTest.cs @@ -0,0 +1,523 @@ +using System.Collections.Generic; +using System.Text.Json; +using Zavudev.Core; +using Zavudev.Exceptions; +using Zavudev.Models.AgentTemplates; + +namespace Zavudev.Tests.Models.AgentTemplates; + +public class AgentTemplateRetrieveResponseTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AgentTemplateRetrieveResponse + { + Template = new() + { + ID = "fermi", + Category = Category.Sales, + DefaultSlug = "fermi", + Dependencies = new Dictionary() { { "foo", "string" } }, + Files = [new() { Content = "content", Path = "index.ts" }], + Name = "name", + RequiredSecrets = [new() { Hint = "hint", Key = "SENDER_ID" }], + Summary = "summary", + Voice = true, + }, + }; + + Template expectedTemplate = new() + { + ID = "fermi", + Category = Category.Sales, + DefaultSlug = "fermi", + Dependencies = new Dictionary() { { "foo", "string" } }, + Files = [new() { Content = "content", Path = "index.ts" }], + Name = "name", + RequiredSecrets = [new() { Hint = "hint", Key = "SENDER_ID" }], + Summary = "summary", + Voice = true, + }; + + Assert.Equal(expectedTemplate, model.Template); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AgentTemplateRetrieveResponse + { + Template = new() + { + ID = "fermi", + Category = Category.Sales, + DefaultSlug = "fermi", + Dependencies = new Dictionary() { { "foo", "string" } }, + Files = [new() { Content = "content", Path = "index.ts" }], + Name = "name", + RequiredSecrets = [new() { Hint = "hint", Key = "SENDER_ID" }], + Summary = "summary", + Voice = true, + }, + }; + + 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 AgentTemplateRetrieveResponse + { + Template = new() + { + ID = "fermi", + Category = Category.Sales, + DefaultSlug = "fermi", + Dependencies = new Dictionary() { { "foo", "string" } }, + Files = [new() { Content = "content", Path = "index.ts" }], + Name = "name", + RequiredSecrets = [new() { Hint = "hint", Key = "SENDER_ID" }], + Summary = "summary", + Voice = true, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Template expectedTemplate = new() + { + ID = "fermi", + Category = Category.Sales, + DefaultSlug = "fermi", + Dependencies = new Dictionary() { { "foo", "string" } }, + Files = [new() { Content = "content", Path = "index.ts" }], + Name = "name", + RequiredSecrets = [new() { Hint = "hint", Key = "SENDER_ID" }], + Summary = "summary", + Voice = true, + }; + + Assert.Equal(expectedTemplate, deserialized.Template); + } + + [Fact] + public void Validation_Works() + { + var model = new AgentTemplateRetrieveResponse + { + Template = new() + { + ID = "fermi", + Category = Category.Sales, + DefaultSlug = "fermi", + Dependencies = new Dictionary() { { "foo", "string" } }, + Files = [new() { Content = "content", Path = "index.ts" }], + Name = "name", + RequiredSecrets = [new() { Hint = "hint", Key = "SENDER_ID" }], + Summary = "summary", + Voice = true, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AgentTemplateRetrieveResponse + { + Template = new() + { + ID = "fermi", + Category = Category.Sales, + DefaultSlug = "fermi", + Dependencies = new Dictionary() { { "foo", "string" } }, + Files = [new() { Content = "content", Path = "index.ts" }], + Name = "name", + RequiredSecrets = [new() { Hint = "hint", Key = "SENDER_ID" }], + Summary = "summary", + Voice = true, + }, + }; + + AgentTemplateRetrieveResponse copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TemplateTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Template + { + ID = "fermi", + Category = Category.Sales, + DefaultSlug = "fermi", + Dependencies = new Dictionary() { { "foo", "string" } }, + Files = [new() { Content = "content", Path = "index.ts" }], + Name = "name", + RequiredSecrets = [new() { Hint = "hint", Key = "SENDER_ID" }], + Summary = "summary", + Voice = true, + }; + + string expectedID = "fermi"; + ApiEnum expectedCategory = Category.Sales; + string expectedDefaultSlug = "fermi"; + Dictionary expectedDependencies = new() { { "foo", "string" } }; + List expectedFiles = [new() { Content = "content", Path = "index.ts" }]; + string expectedName = "name"; + List expectedRequiredSecrets = [new() { Hint = "hint", Key = "SENDER_ID" }]; + string expectedSummary = "summary"; + bool expectedVoice = true; + + Assert.Equal(expectedID, model.ID); + Assert.Equal(expectedCategory, model.Category); + Assert.Equal(expectedDefaultSlug, model.DefaultSlug); + Assert.Equal(expectedDependencies.Count, model.Dependencies.Count); + foreach (var item in expectedDependencies) + { + Assert.True(model.Dependencies.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Dependencies[item.Key]); + } + Assert.Equal(expectedFiles.Count, model.Files.Count); + for (int i = 0; i < expectedFiles.Count; i++) + { + Assert.Equal(expectedFiles[i], model.Files[i]); + } + Assert.Equal(expectedName, model.Name); + Assert.Equal(expectedRequiredSecrets.Count, model.RequiredSecrets.Count); + for (int i = 0; i < expectedRequiredSecrets.Count; i++) + { + Assert.Equal(expectedRequiredSecrets[i], model.RequiredSecrets[i]); + } + Assert.Equal(expectedSummary, model.Summary); + Assert.Equal(expectedVoice, model.Voice); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Template + { + ID = "fermi", + Category = Category.Sales, + DefaultSlug = "fermi", + Dependencies = new Dictionary() { { "foo", "string" } }, + Files = [new() { Content = "content", Path = "index.ts" }], + Name = "name", + RequiredSecrets = [new() { Hint = "hint", Key = "SENDER_ID" }], + Summary = "summary", + Voice = true, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize