Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Analyze Text #336

Merged
merged 1 commit into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,177 @@ await analyzeClient.Invoking(y => y.AnalyzeUrlCallBack(source, null, analyzeSche
await analyzeClient.DidNotReceive().PostAsync<AnalyzeSchema, SyncResponse>(url, Arg.Any<AnalyzeSchema>());
}

[Test]
public async Task AnalyzeText_Should_Call_PostAsync_Returning_SyncResponse()
{
// Input and Output
var url = AbstractRestClient.GetUri(_options, $"{UriSegments.READ}");
var expectedResponse = new AutoFaker<SyncResponse>().Generate();
var analyzeSchema = new AutoFaker<AnalyzeSchema>().Generate();
analyzeSchema.CallBack = null;
var source = new AutoFaker<TextSource>().Generate();

// Fake Clients
var httpClient = MockHttpClient.CreateHttpClientWithResult(expectedResponse);
var analyzeClient = Substitute.For<AnalyzeClient>(_apiKey, _options, null);

// Mock methods
analyzeClient.When(x => x.PostAsync<TextSource, AnalyzeSchema, SyncResponse>(Arg.Any<string>(), Arg.Any<AnalyzeSchema>(), Arg.Any<TextSource>())).DoNotCallBase();
analyzeClient.PostAsync<TextSource, AnalyzeSchema, SyncResponse>(url, Arg.Any<AnalyzeSchema>(), Arg.Any<TextSource>()).Returns(expectedResponse);

// Act
var result = await analyzeClient.AnalyzeText(source, analyzeSchema);

// Assert
await analyzeClient.Received().PostAsync<TextSource, AnalyzeSchema, SyncResponse>(url, Arg.Any<AnalyzeSchema>(), Arg.Any<TextSource>());

using (new AssertionScope())
{
result.Should().NotBeNull();
result.Should().BeAssignableTo<SyncResponse>();
result.Should().BeEquivalentTo(expectedResponse);
}
}

[Test]
public async Task AnalyzeText_Should_Throw_ArgumentException_If_CallBack_Not_Null()
{
// Input and Output
var url = AbstractRestClient.GetUri(_options, $"{UriSegments.READ}");
var expectedResponse = new AutoFaker<AsyncResponse>().Generate();
var analyzeSchema = new AutoFaker<AnalyzeSchema>().Generate();
var source = new AutoFaker<TextSource>().Generate();

// Fake Clients
var httpClient = MockHttpClient.CreateHttpClientWithResult(expectedResponse);
var analyzeClient = Substitute.For<AnalyzeClient>(_apiKey, _options, null);

// Mock methods
analyzeClient.When(x => x.PostAsync<AnalyzeSchema, AsyncResponse>(Arg.Any<string>(), Arg.Any<AnalyzeSchema>())).DoNotCallBase();
analyzeClient.PostAsync<AnalyzeSchema, AsyncResponse>(url, Arg.Any<AnalyzeSchema>()).Returns(expectedResponse);

// Act and Assert
await analyzeClient.Invoking(y => y.AnalyzeText(source, analyzeSchema))
.Should().ThrowAsync<ArgumentException>();

await analyzeClient.DidNotReceive().PostAsync<AnalyzeSchema, SyncResponse>(url, Arg.Any<AnalyzeSchema>());
}

[Test]
public async Task AnalyzeTextCallBack_Should_Call_PostAsync_Returning_SyncResponse_With_CallBack_Parameter()
{
// Input and Output
var url = AbstractRestClient.GetUri(_options, $"{UriSegments.READ}");
var expectedResponse = new AutoFaker<AsyncResponse>().Generate();
var source = new AutoFaker<TextSource>().Generate();
var analyzeSchema = new AutoFaker<AnalyzeSchema>().Generate();
// analyzeSchema is not null here as we first need to get the querystring with the callBack included

// Fake Clients
var httpClient = MockHttpClient.CreateHttpClientWithResult(expectedResponse);
var analyzeClient = Substitute.For<AnalyzeClient>(_apiKey, _options, null);

// Mock methods
analyzeClient.When(x => x.PostAsync<TextSource, AnalyzeSchema, AsyncResponse>(Arg.Any<string>(), Arg.Any<AnalyzeSchema>(), Arg.Any<TextSource>())).DoNotCallBase();
analyzeClient.PostAsync<TextSource, AnalyzeSchema, AsyncResponse>(url, Arg.Any<AnalyzeSchema>(), Arg.Any<TextSource>()).Returns(expectedResponse);

//before we act to test this call with the callBack parameter and not the callBack property we need to null the callBack property
var callBackParameter = analyzeSchema.CallBack;
analyzeSchema.CallBack = null;

// Act
var result = await analyzeClient.AnalyzeTextCallBack(source, callBackParameter, analyzeSchema);

// Assert
await analyzeClient.Received().PostAsync<TextSource, AnalyzeSchema, AsyncResponse>(url, Arg.Any<AnalyzeSchema>(), Arg.Any<TextSource>());
using (new AssertionScope())
{
result.Should().NotBeNull();
result.Should().BeAssignableTo<AsyncResponse>();
result.Should().BeEquivalentTo(expectedResponse);
}
}

[Test]
public async Task AnalyzeTextCallBack_Should_Call_PostAsync_Returning_SyncResponse_With_CallBack_Property()
{
// Input and Output
var url = AbstractRestClient.GetUri(_options, $"{UriSegments.READ}");
var expectedResponse = new AutoFaker<AsyncResponse>().Generate();
var source = new AutoFaker<TextSource>().Generate();
var analyzeSchema = new AutoFaker<AnalyzeSchema>().Generate();

// Fake Clients
var httpClient = MockHttpClient.CreateHttpClientWithResult(expectedResponse);
var analyzeClient = Substitute.For<AnalyzeClient>(_apiKey, _options, null);

// Mock methods
analyzeClient.When(x => x.PostAsync<TextSource, AnalyzeSchema, AsyncResponse>(Arg.Any<string>(), Arg.Any<AnalyzeSchema>(), Arg.Any<TextSource>())).DoNotCallBase();
analyzeClient.PostAsync<TextSource, AnalyzeSchema, AsyncResponse>(url, Arg.Any<AnalyzeSchema>(), Arg.Any<TextSource>()).Returns(expectedResponse);

// Act
var result = await analyzeClient.AnalyzeTextCallBack(source, null, analyzeSchema);

// Assert
await analyzeClient.Received().PostAsync<TextSource, AnalyzeSchema, AsyncResponse>(url, Arg.Any<AnalyzeSchema>(), Arg.Any<TextSource>());
using (new AssertionScope())
{
result.Should().NotBeNull();
result.Should().BeAssignableTo<AsyncResponse>();
result.Should().BeEquivalentTo(expectedResponse);
}
}

[Test]
public async Task AnalyzeTextCallBack_Should_Throw_ArgumentException_With_CallBack_Property_And_CallBack_Parameter_Set()
{
// Input and Output
var url = AbstractRestClient.GetUri(_options, $"{UriSegments.READ}");
var expectedResponse = new AutoFaker<AsyncResponse>().Generate();
var source = new AutoFaker<TextSource>().Generate();
var analyzeSchema = new AutoFaker<AnalyzeSchema>().Generate();

// Fake Clients
var httpClient = MockHttpClient.CreateHttpClientWithResult(expectedResponse);
var analyzeClient = Substitute.For<AnalyzeClient>(_apiKey, _options, null);

// Mock methods
analyzeClient.When(x => x.PostAsync<AnalyzeSchema, AsyncResponse>(Arg.Any<string>(), Arg.Any<AnalyzeSchema>())).DoNotCallBase();
analyzeClient.PostAsync<AnalyzeSchema, AsyncResponse>(url, Arg.Any<AnalyzeSchema>()).Returns(expectedResponse);
var callBackParameter = analyzeSchema.CallBack;

// Act and Assert
await analyzeClient.Invoking(y => y.AnalyzeTextCallBack(source, callBackParameter, analyzeSchema))
.Should().ThrowAsync<ArgumentException>();

await analyzeClient.DidNotReceive().PostAsync<AnalyzeSchema, AsyncResponse>(url, Arg.Any<AnalyzeSchema>());
}

[Test]
public async Task AnalyzeTextCallBack_Should_Throw_ArgumentException_With_No_CallBack_Set()
{
// Input and Output
var url = AbstractRestClient.GetUri(_options, $"{UriSegments.READ}");
var expectedResponse = new AutoFaker<SyncResponse>().Generate();
var source = new AutoFaker<TextSource>().Generate();
var analyzeSchema = new AutoFaker<AnalyzeSchema>().Generate();
analyzeSchema.CallBack = null;

// Fake Clients
var httpClient = MockHttpClient.CreateHttpClientWithResult(expectedResponse);
var analyzeClient = Substitute.For<AnalyzeClient>(_apiKey, _options, null);

// Mock methods
analyzeClient.When(x => x.PostAsync<AnalyzeSchema, SyncResponse>(Arg.Any<string>(), Arg.Any<AnalyzeSchema>())).DoNotCallBase();
analyzeClient.PostAsync<AnalyzeSchema, SyncResponse>(url, Arg.Any<AnalyzeSchema>()).Returns(expectedResponse);

// Act and Assert
await analyzeClient.Invoking(y => y.AnalyzeTextCallBack(source, null, analyzeSchema))
.Should().ThrowAsync<ArgumentException>();

await analyzeClient.DidNotReceive().PostAsync<AnalyzeSchema, SyncResponse>(url, Arg.Any<AnalyzeSchema>());
}

[Test]
public async Task AnalyzeFile_With_Stream_Should_Call_PostAsync_Returning_SyncResponse()
{
Expand Down
58 changes: 57 additions & 1 deletion Deepgram/Clients/Analyze/v1/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,32 @@ public async Task<SyncResponse> AnalyzeUrl(UrlSource source, AnalyzeSchema? anal
return result;
}

/// <summary>
/// Analyze by providing text
/// </summary>
/// <param name="source">Text that is to be analyzed <see cref="TextSource"></param>
/// <param name="analyzeSchema">Options for the transcription <see cref="AnalyzeSchema"/></param>
/// <returns><see cref="SyncResponse"/></returns>
public async Task<SyncResponse> AnalyzeText(TextSource source, AnalyzeSchema? analyzeSchema, CancellationTokenSource? cancellationToken = default, Dictionary<string, string>? addons = null, Dictionary<string, string>? headers = null)
{
Log.Verbose("AnalyzeClient.AnalyzeText", "ENTER");
Log.Information("AnalyzeText", $"source: {source}");
Log.Information("AnalyzeText", $"analyzeSchema:\n{JsonSerializer.Serialize(analyzeSchema, JsonSerializeOptions.DefaultOptions)}");

VerifyNoCallBack(nameof(AnalyzeText), analyzeSchema);

var uri = GetUri(_options, UriSegments.READ);
var result = await PostAsync<TextSource, AnalyzeSchema, SyncResponse>(
uri, analyzeSchema, source, cancellationToken, addons, headers
);

Log.Information("AnalyzeText", $"{uri} Succeeded");
Log.Debug("AnalyzeText", $"result: {result}");
Log.Verbose("AnalyzeClient.AnalyzeText", "LEAVE");

return result;
}


/// <summary>
/// Analyzes a file using the provided byte array
Expand Down Expand Up @@ -156,6 +182,36 @@ public async Task<AsyncResponse> AnalyzeUrlCallBack(UrlSource source, string? ca

return result;
}

/// <summary>
/// Analyze by providing text and a CallBack
/// </summary>
/// <param name="source">Text that is to be analyzed <see cref="UrlSource"/></param>
/// <param name="callBack">CallBack url</param>
/// <param name="analyzeSchema">Options for the transcription<see cref="AnalyzeSchema"></param>
/// <returns><see cref="AsyncResponse"/></returns>
public async Task<AsyncResponse> AnalyzeTextCallBack(TextSource source, string? callBack, AnalyzeSchema? analyzeSchema, CancellationTokenSource? cancellationToken = default, Dictionary<string, string>? addons = null, Dictionary<string, string>? headers = null)
{
Log.Verbose("AnalyzeClient.AnalyzeTextCallBack", "ENTER");
Log.Information("AnalyzeTextCallBack", $"source: {source}");
Log.Information("AnalyzeTextCallBack", $"callBack: {callBack}");
Log.Information("AnalyzeTextCallBack", $"analyzeSchema:\n{JsonSerializer.Serialize(analyzeSchema, JsonSerializeOptions.DefaultOptions)}");

VerifyOneCallBackSet(nameof(AnalyzeTextCallBack), callBack, analyzeSchema);
if (callBack != null)
analyzeSchema.CallBack = callBack;

var uri = GetUri(_options, UriSegments.READ);
var result = await PostAsync<TextSource, AnalyzeSchema, AsyncResponse>(
uri, analyzeSchema, source, cancellationToken, addons, headers
);

Log.Information("AnalyzeTextCallBack", $"{uri} Succeeded");
Log.Debug("AnalyzeTextCallBack", $"result: {result}");
Log.Verbose("AnalyzeClient.AnalyzeTextCallBack", "LEAVE");

return result;
}
#endregion

#region CallbackChecks
Expand All @@ -168,7 +224,7 @@ private void VerifyNoCallBack(string method, AnalyzeSchema? analyzeSchema)
var exStr = $"CallBack cannot be provided as schema option to a synchronous transcription when calling {method}. Use {nameof(AnalyzeFileCallBack)} or {nameof(AnalyzeUrlCallBack)}";
Log.Error("VerifyNoCallBack", $"Exception: {exStr}");
throw new ArgumentException(exStr);
}
}
}

private void VerifyOneCallBackSet(string method, string? callBack, AnalyzeSchema? analyzeSchema)
Expand Down
17 changes: 17 additions & 0 deletions Deepgram/Clients/Interfaces/v1/IAnalyzeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ public interface IAnalyzeClient
public Task<SyncResponse> AnalyzeUrl(UrlSource source, AnalyzeSchema? analyzeSchema, CancellationTokenSource? cancellationToken = default,
Dictionary<string, string>? addons = null, Dictionary<string, string>? headers = null);

/// <summary>
/// Analyze by providing text
/// </summary>
/// <param name="source">Text that is to be analyzed <see cref="TextSource"></param>
/// <param name="analyzeSchema">Options for the transcription <see cref="AnalyzeSchema"/></param>
/// <returns><see cref="SyncResponse"/></returns>
public Task<SyncResponse> AnalyzeText(TextSource source, AnalyzeSchema? analyzeSchema, CancellationTokenSource? cancellationToken = default, Dictionary<string, string>? addons = null, Dictionary<string, string>? headers = null);

/// <summary>
/// Analyzes a file using the provided byte array
/// </summary>
Expand Down Expand Up @@ -70,5 +78,14 @@ public Task<AsyncResponse> AnalyzeFileCallBack(Stream source, string? callBack,
public Task<AsyncResponse> AnalyzeUrlCallBack(UrlSource source, string? callBack, AnalyzeSchema? analyzeSchema,
CancellationTokenSource? cancellationToken = default, Dictionary<string, string>? addons = null,
Dictionary<string, string>? headers = null);

// <summary>
/// Analyze by providing text and a CallBack
/// </summary>
/// <param name="source">Text that is to be analyzed <see cref="UrlSource"/></param>
/// <param name="callBack">CallBack url</param>
/// <param name="analyzeSchema">Options for the transcription<see cref="AnalyzeSchema"></param>
/// <returns><see cref="AsyncResponse"/></returns>
public Task<AsyncResponse> AnalyzeTextCallBack(TextSource source, string? callBack, AnalyzeSchema? analyzeSchema, CancellationTokenSource? cancellationToken = default, Dictionary<string, string>? addons = null, Dictionary<string, string>? headers = null);
dvonthenen marked this conversation as resolved.
Show resolved Hide resolved
#endregion
}
24 changes: 24 additions & 0 deletions Deepgram/Models/Analyze/v1/TextSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2024 Deepgram .NET SDK contributors. All Rights Reserved.
// Use of this source code is governed by a MIT license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

namespace Deepgram.Models.Analyze.v1;

public class TextSource(string text)
{
/// <summary>
/// Url of the text to analyze
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("text")]
public string? Text { get; set; } = text;
dvonthenen marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Override ToString method to serialize the object
/// </summary>
public override string ToString()
{
return Regex.Unescape(JsonSerializer.Serialize(this, JsonSerializeOptions.DefaultOptions));
}
}
dvonthenen marked this conversation as resolved.
Show resolved Hide resolved

Loading