Skip to content

Commit

Permalink
Merge pull request #336 from kaller01/implement-analyze-text
Browse files Browse the repository at this point in the history
Implement Analyze Text
  • Loading branch information
davidvonthenen authored Sep 27, 2024
2 parents 52fb7c1 + 9aeeb0d commit bfcffd0
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 1 deletion.
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);
#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;

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

0 comments on commit bfcffd0

Please sign in to comment.