diff --git a/src/AnthropicClient/AnthropicApiClient.cs b/src/AnthropicClient/AnthropicApiClient.cs index cfcb63c..95d7e01 100644 --- a/src/AnthropicClient/AnthropicApiClient.cs +++ b/src/AnthropicClient/AnthropicApiClient.cs @@ -388,6 +388,15 @@ public class AnthropicApiClient : IAnthropicApiClient return await CreateResultAsync(response); } + /// + public async Task>> ListFilesAsync(PagingRequest? request = null, CancellationToken cancellationToken = default) + { + var pagingRequest = request ?? new PagingRequest(); + var endpoint = $"{FilesEndpoint}?{pagingRequest.ToQueryParameters()}"; + var response = await SendRequestAsync(endpoint, cancellationToken: cancellationToken); + return await CreateResultAsync>(response); + } + private async IAsyncEnumerable>> GetAllPagesAsync(string endpoint, int limit = 20, [EnumeratorCancellation] CancellationToken cancellationToken = default) { var pagingRequest = new PagingRequest(limit: limit); diff --git a/src/AnthropicClient/IAnthropicApiClient.cs b/src/AnthropicClient/IAnthropicApiClient.cs index 0dee03f..bae3b70 100644 --- a/src/AnthropicClient/IAnthropicApiClient.cs +++ b/src/AnthropicClient/IAnthropicApiClient.cs @@ -119,4 +119,12 @@ public interface IAnthropicApiClient /// A token to cancel the asynchronous operation. /// A task that represents the asynchronous operation. The task result contains the response as an where T is . Task> CreateFileAsync(CreateFileRequest request, CancellationToken cancellationToken = default); + + /// + /// Lists files asynchronously, returning a single page of results. + /// + /// The paging request to use for listing the files. + /// A token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. The task result contains the response as an where T is where T is . + Task>> ListFilesAsync(PagingRequest? request = null, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs b/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs index 1b85111..568bd64 100644 --- a/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs +++ b/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs @@ -708,4 +708,22 @@ public class AnthropicApiClientTests(ConfigurationFixture configFixture) : EndTo result.Value.Name.Should().Be(fileName); result.Value.MimeType.Should().Be(fileType); } + + [Fact] + public async Task ListFilesAsync_WhenCalled_ItShouldReturnPageOfFiles() + { + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Add("anthropic-beta", "files-api-2025-04-14"); + var client = CreateClient(httpClient); + + var fileBytes = await File.ReadAllBytesAsync(TestFileHelper.GetTestFilePath("story.txt")); + var createFileRequest = new CreateFileRequest(fileBytes, "story.txt", "text/plain"); + var createdFile = await client.CreateFileAsync(createFileRequest); + + var result = await client.ListFilesAsync(); + + result.IsSuccess.Should().BeTrue(); + result.Value.Should().BeOfType>(); + result.Value.Data.Should().ContainSingle(f => f.Id == createdFile.Value.Id); + } } \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs b/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs index ff05315..97a1ef7 100644 --- a/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs +++ b/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs @@ -1965,4 +1965,107 @@ public class AnthropicApiClientTests : IntegrationTest Type = "file" }); } + + [Fact] + public async Task ListFilesAsync_WhenCalled_ItShouldReturnPageOfFiles() + { + _mockHttpMessageHandler + .WhenListFilesRequest() + .Respond( + HttpStatusCode.OK, + "application/json", + @"{ + ""data"": [ + { + ""created_at"": ""2023-11-07T05:31:56Z"", + ""downloadable"": false, + ""filename"": ""example.txt"", + ""id"": ""file_013Zva2CMHLNnXjNJJKqJ2EF"", + ""mime_type"": ""text/plain"", + ""size_bytes"": 1234, + ""type"": ""file"" + } + ], + ""has_more"": true, + ""first_id"": ""1"", + ""last_id"": ""1"" + }" + ); + + var result = await Client.ListFilesAsync(); + + result.IsSuccess.Should().BeTrue(); + result.Value.Should().BeOfType>(); + result.Value.HasMore.Should().BeTrue(); + result.Value.FirstId.Should().Be("1"); + result.Value.LastId.Should().Be("1"); + result.Value.Data.Should().BeEquivalentTo(new AnthropicFile[] + { + new() + { + CreatedAt = DateTimeOffset.Parse("2023-11-07T05:31:56Z"), + Downloadable = false, + Name = "example.txt", + Id = "file_013Zva2CMHLNnXjNJJKqJ2EF", + MimeType = "text/plain", + Size = 1234, + Type = "file" + } + }); + } + + [Fact] + public async Task ListFilesAsync_WhenCalledWithPagingRequest_ItShouldReturnPageOfFiles() + { + var pagingRequest = new PagingRequest(afterId: "next_id", limit: 10); + + _mockHttpMessageHandler + .WhenListFilesRequest() + .WithQueryString(new Dictionary + { + { "after_id", pagingRequest.AfterId }, + { "limit", pagingRequest.Limit.ToString() }, + }) + .Respond( + HttpStatusCode.OK, + "application/json", + @"{ + ""data"": [ + { + ""created_at"": ""2023-11-07T05:31:56Z"", + ""downloadable"": false, + ""filename"": ""example.txt"", + ""id"": ""file_013Zva2CMHLNnXjNJJKqJ2EF"", + ""mime_type"": ""text/plain"", + ""size_bytes"": 1234, + ""type"": ""file"" + } + ], + ""has_more"": true, + ""first_id"": ""1"", + ""last_id"": ""1"" + }" + ); + + var result = await Client.ListFilesAsync(pagingRequest); + + result.IsSuccess.Should().BeTrue(); + result.Value.Should().BeOfType>(); + result.Value.HasMore.Should().BeTrue(); + result.Value.FirstId.Should().Be("1"); + result.Value.LastId.Should().Be("1"); + result.Value.Data.Should().BeEquivalentTo(new AnthropicFile[] + { + new() + { + CreatedAt = DateTimeOffset.Parse("2023-11-07T05:31:56Z"), + Downloadable = false, + Name = "example.txt", + Id = "file_013Zva2CMHLNnXjNJJKqJ2EF", + MimeType = "text/plain", + Size = 1234, + Type = "file" + } + }); + } } \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Integration/IntegrationTest.cs b/tests/AnthropicClient.Tests/Integration/IntegrationTest.cs index f618b44..19c5a78 100644 --- a/tests/AnthropicClient.Tests/Integration/IntegrationTest.cs +++ b/tests/AnthropicClient.Tests/Integration/IntegrationTest.cs @@ -110,4 +110,10 @@ public static class MockHttpMessageHandlerExtensions return mockHttpMessageHandler .SetupBaseRequest(HttpMethod.Post, FilesEndpoint); } + + public static MockedRequest WhenListFilesRequest(this MockHttpMessageHandler mockHttpMessageHandler) + { + return mockHttpMessageHandler + .SetupBaseRequest(HttpMethod.Get, FilesEndpoint); + } } \ No newline at end of file