Skip to content

Commit

Permalink
Merge pull request #6 from waifuvault/bucket
Browse files Browse the repository at this point in the history
Update with Bucket API
  • Loading branch information
nakedmcse authored Aug 20, 2024
2 parents 5234e48 + 3c7045b commit c503c9c
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 18 deletions.
79 changes: 69 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ This API contains 5 interactions:
3. Update File Info
4. Delete File
5. Get File
6. Create Bucket
7. Delete Bucket
8. Get Bucket

The package is namespaced to `Waifuvault`, so to import it, simply:

Expand All @@ -33,16 +36,17 @@ using Waifuvault;

To Upload a file, use the `uploadFile` function. This function takes the following options as an object:

| Option | Type | Description | Required | Extra info |
|----------------|--------------|-------------------------------------------------------------|----------------|----------------------------------|
| `filename` | `string ` | The path to the file to upload | true if File | File path |
| `url` | `string` | The URL of the file to target | true if URL | Filename with extension |
| `buffer` | `byte array` | Byte array containing file to upload | true if buffer | Needs filename set also |
| `expires` | `string` | A string containing a number and a unit (1d = 1day) | false | Valid units are `m`, `h` and `d` |
| `hideFilename` | `boolean` | If true, then the uploaded filename won't appear in the URL | false | Defaults to `false` |
| `password` | `string` | If set, then the uploaded file will be encrypted | false | |
| `ct` | `canceltoken`| An optional cancellation token that can be passed in | false | Standard cancellation token |
| `oneTimeDownload` | `boolean` | if supplied, the file will be deleted as soon as it is accessed | false | |
| Option | Type | Description | Required | Extra info |
|-------------------|---------------|-----------------------------------------------------------------|----------------|----------------------------------|
| `filename` | `string ` | The path to the file to upload | true if File | File path |
| `url` | `string` | The URL of the file to target | true if URL | Filename with extension |
| `buffer` | `byte array` | Byte array containing file to upload | true if buffer | Needs filename set also |
| `bucketToken` | `string` | Token for a bucket to upload the file into | false | Create bucket gives token |
| `expires` | `string` | A string containing a number and a unit (1d = 1day) | false | Valid units are `m`, `h` and `d` |
| `hideFilename` | `boolean` | If true, then the uploaded filename won't appear in the URL | false | Defaults to `false` |
| `password` | `string` | If set, then the uploaded file will be encrypted | false | |
| `ct` | `canceltoken` | An optional cancellation token that can be passed in | false | Standard cancellation token |
| `oneTimeDownload` | `boolean` | if supplied, the file will be deleted as soon as it is accessed | false | |

Using a URL:

Expand Down Expand Up @@ -216,4 +220,59 @@ try {
} catch(OperationCanceledException) {
Console.WriteLine("Canceled download");
}
```

### Create Bucket

Buckets are virtual collections that are linked to your IP and a token. When you create a bucket, you will receive a bucket token that you can use in Get Bucket to get all the files in that bucket

> **NOTE:** Only one bucket is allowed per client IP address, if you call it more than once, it will return the same bucket token
To create a bucket, use the `createBucket` function. This function does not take any arguments.

```csharp
using Waifuvault;
var bucket = await Waifuvault.Api.createBucket();
Console.WriteLine(bucket.token);
```

### Delete Bucket

Deleting a bucket will delete the bucket and all the files it contains.

> **IMPORTANT:** All contained files will be **DELETED** along with the Bucket!
To delete a bucket, you must call the `deleteBucket` function with the following options as parameters:

| Option | Type | Description | Required | Extra info |
|-------------|-----------|-----------------------------------|----------|-------------------|
| `token` | `string` | The token of the bucket to delete | true | |

> **NOTE:** `deleteBucket` will only ever either return `true` or throw an exception if the token is invalid
```csharp
using Waifuvault;
var resp = await Waifuvault.Api.deleteBucket("some-bucket-token");
Console.WriteLine(resp);
```

### Get Bucket

To get the list of files contained in a bucket, you use the `getBucket` functions and supply the token.
This function takes the following options as parameters:

| Option | Type | Description | Required | Extra info |
|-------------|-----------|-------------------------|----------|-------------------|
| `token` | `string` | The token of the bucket | true | |

This will respond with the bucket and all the files the bucket contains.

```csharp
using Waifuvault;
var bucket = await Waifuvault.Api.getBucket("some-bucket-token");
Console.WriteLine(bucket.token);
foreach(var file in bucket.files)
{
Console.WriteLine(file.token);
}
```
23 changes: 20 additions & 3 deletions models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ public class FileUpload
public string? filename { get; set; }
public string? url { get; set; }
public byte[]? buffer { get; set; }
public string? bucketToken { get; set; }
public string? expires { get; set; }
public string? password { get; set; }
public bool? hidefilename { get; set; }
public bool? oneTimeDownload { get; set; }

public FileUpload(string target, string? expires = null, string? password = null, bool? hidefilename = null, bool? oneTimeDownload = null) {
public FileUpload(string target, string? bucket = null, string? expires = null, string? password = null, bool? hidefilename = null, bool? oneTimeDownload = null) {
if(target.ToLower().StartsWith("http://") || target.ToLower().StartsWith("https://"))
{
this.url = target;
Expand All @@ -23,15 +24,17 @@ public FileUpload(string target, string? expires = null, string? password = null
this.filename = target;
}
this.buffer = null;
this.bucketToken = bucket;
this.expires = expires;
this.password = password;
this.hidefilename = hidefilename;
this.oneTimeDownload = oneTimeDownload;
}

public FileUpload(byte[] buffer, string filename, string? expires = null, string? password = null, bool? hidefilename = null, bool? oneTimeDownload = null) {
public FileUpload(byte[] buffer, string filename, string? bucket = null, string? expires = null, string? password = null, bool? hidefilename = null, bool? oneTimeDownload = null) {
this.buffer = buffer;
this.filename = filename;
this.bucketToken = bucket;
this.expires = expires;
this.password = password;
this.hidefilename = hidefilename;
Expand Down Expand Up @@ -72,19 +75,33 @@ public class FileResponse
{
public string? token { get; set; }
public string? url { get; set; }
public string? bucket { get; set; }

[JsonConverter(typeof(StringConverter))]
public string? retentionPeriod { get; set; }
public FileOptions? options { get; set; }

public FileResponse(string? token = null, string? url = null, string? retentionPeriod = null, FileOptions? options = null) {
public FileResponse(string? token = null, string? url = null, string? bucket = null, string? retentionPeriod = null, FileOptions? options = null) {
this.token = token;
this.url = url;
this.bucket = bucket;
this.retentionPeriod = retentionPeriod;
this.options = options;
}
}

public class BucketResponse
{
public string? token { get; set; }
public List<FileResponse> files { get; set; }

public BucketResponse(string? token = null)
{
this.token = token;
files = new List<FileResponse>();
}
}

public class ErrorResponse
{
public string name { get; set; }

Check warning on line 107 in models.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 107 in models.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
Expand Down
70 changes: 67 additions & 3 deletions tests/waifuvaultTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class waifuvaultTests
public Mock<HttpMessageHandler> deleteTrue = new Mock<HttpMessageHandler>(MockBehavior.Strict);
public Mock<HttpMessageHandler> badRequest = new Mock<HttpMessageHandler>(MockBehavior.Strict);
public Mock<HttpMessageHandler> fileReturn = new Mock<HttpMessageHandler>(MockBehavior.Strict);
public Mock<HttpMessageHandler> bucketReturn = new Mock<HttpMessageHandler>(MockBehavior.Strict);

public waifuvaultTests() {
setupMocks();
Expand All @@ -26,7 +27,7 @@ private void setupMocks() {
)
.ReturnsAsync(new HttpResponseMessage(){
StatusCode = System.Net.HttpStatusCode.OK,
Content = new StringContent("{\"url\":\"https://waifuvault.moe/f/something\", \"token\":\"test-token\", \"retentionPeriod\":100, \"options\":{\"protected\":false, \"hideFilename\":false, \"oneTimeDownload\":false}}")
Content = new StringContent("{\"url\":\"https://waifuvault.moe/f/something\", \"token\":\"test-token\", \"bucket\":\"test-bucket\", \"retentionPeriod\":100, \"options\":{\"protected\":false, \"hideFilename\":false, \"oneTimeDownload\":false}}")
})
.Verifiable();

Expand All @@ -38,7 +39,7 @@ private void setupMocks() {
)
.ReturnsAsync(new HttpResponseMessage(){
StatusCode = System.Net.HttpStatusCode.OK,
Content = new StringContent("{\"url\":\"https://waifuvault.moe/f/something\", \"token\":\"test-token\", \"retentionPeriod\":100, \"options\":{\"protected\":true, \"hideFilename\":false, \"oneTimeDownload\":false}}")
Content = new StringContent("{\"url\":\"https://waifuvault.moe/f/something\", \"token\":\"test-token\", \"bucket\":\"test-bucket\", \"retentionPeriod\":100, \"options\":{\"protected\":true, \"hideFilename\":false, \"oneTimeDownload\":false}}")
})
.Verifiable();

Expand All @@ -50,7 +51,7 @@ private void setupMocks() {
)
.ReturnsAsync(new HttpResponseMessage(){
StatusCode = System.Net.HttpStatusCode.OK,
Content = new StringContent("{\"url\":\"https://waifuvault.moe/f/something\", \"token\":\"test-token\", \"retentionPeriod\":\"10 minutes\", \"options\":{\"protected\":false, \"hideFilename\":false, \"oneTimeDownload\":false}}")
Content = new StringContent("{\"url\":\"https://waifuvault.moe/f/something\", \"token\":\"test-token\", \"bucket\":\"test-bucket\", \"retentionPeriod\":\"10 minutes\", \"options\":{\"protected\":false, \"hideFilename\":false, \"oneTimeDownload\":false}}")
})
.Verifiable();

Expand Down Expand Up @@ -89,6 +90,18 @@ private void setupMocks() {
Content = new ByteArrayContent(new byte[4] {0x00, 0x01, 0x01, 0x00})
})
.Verifiable();

bucketReturn.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage(){
StatusCode = System.Net.HttpStatusCode.OK,
Content = new StringContent("{\"token\":\"test-bucket\", \"files\":[]}")
})
.Verifiable();
}

[Fact]
Expand Down Expand Up @@ -304,6 +317,57 @@ public async Task TestDownload() {
ItExpr.IsAny<CancellationToken>());
Assert.True(response.GetType() == typeof(byte[]));
}

[Fact]
public async Task TestCreateBucket() {
// Given
bucketReturn.Invocations.Clear();
Waifuvault.Api.customHttpClient = new HttpClient(bucketReturn.Object);

// When
var response = await Waifuvault.Api.createBucket();

// Then
bucketReturn.Protected().Verify("SendAsync",Times.Once(),
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Get
&& req.RequestUri.ToString().Contains("/bucket/create")),
ItExpr.IsAny<CancellationToken>());
Assert.Equal("test-bucket",response.token);
}

[Fact]
public async Task TestGetBucket() {
// Given
bucketReturn.Invocations.Clear();
Waifuvault.Api.customHttpClient = new HttpClient(bucketReturn.Object);

// When
var response = await Waifuvault.Api.getBucket("test-bucket");

// Then
bucketReturn.Protected().Verify("SendAsync",Times.Once(),
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Post
&& req.RequestUri.ToString().Contains("/bucket/get")),
ItExpr.IsAny<CancellationToken>());
Assert.Equal("test-bucket",response.token);
}

[Fact]
public async Task TestDeleteBucket() {
// Given
deleteTrue.Invocations.Clear();
Waifuvault.Api.customHttpClient = new HttpClient(deleteTrue.Object);

// When
var response = await Waifuvault.Api.deleteBucket("test-bucket");

// Then
deleteTrue.Protected().Verify("SendAsync",Times.Once(),
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Delete
&& req.RequestUri.ToString().Contains("/bucket/test-bucket")),
ItExpr.IsAny<CancellationToken>());
Assert.True(response);
}

[Fact]
public void TestBuildArgs() {
Expand Down
2 changes: 1 addition & 1 deletion waifuVault-csharp-api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<PackageId>Waifuvault</PackageId>
<Version>1.3.4</Version>
<Version>1.3.5</Version>
<Authors>Walker Aldridge (walker@waifuvault.moe)</Authors>
<Company>waifuvault.moe</Company>
<PackageTags>waifuvault;temp file hosting;waifu;vault</PackageTags>
Expand Down
39 changes: 38 additions & 1 deletion waifuvault.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net;
using System.Text;
using System.Text.Json;

namespace Waifuvault;
Expand All @@ -8,9 +9,45 @@ public class Api
public const string baseURL = "https://waifuvault.moe/rest";
public static HttpClient? customHttpClient;

public static async Task<BucketResponse> createBucket()
{
var client = customHttpClient ?? new HttpClient();
var cts = new CancellationTokenSource();
var url = $"{baseURL}/bucket/create";
var createResponse = await client.GetAsync(url);
await checkError(createResponse,false);
var createResponseData = await createResponse.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<BucketResponse>(createResponseData) ?? new BucketResponse();
}

public static async Task<bool> deleteBucket(string token, CancellationToken? ct = null)
{
var client = customHttpClient ?? new HttpClient();
var cts = new CancellationTokenSource();
var url = $"{baseURL}/bucket/{token}";
var urlResponse = await client.DeleteAsync(url,ct != null ? ct.Value : cts.Token);
await checkError(urlResponse,false);
var urlResponseData = await urlResponse.Content.ReadAsStringAsync();
return urlResponseData == "true";
}

public static async Task<BucketResponse> getBucket(string token, CancellationToken? ct = null)
{
var client = customHttpClient ?? new HttpClient();
var cts = new CancellationTokenSource();
var url = $"{baseURL}/bucket/get";
var data = new { bucket_token = token };
var jsonData = JsonSerializer.Serialize(data);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
var getResponse = await client.PostAsync(url, content, ct != null ? ct.Value : cts.Token);
await checkError(getResponse,false);
var getResponseData = await getResponse.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<BucketResponse>(getResponseData) ?? new BucketResponse();
}

public static async Task<FileResponse> uploadFile(FileUpload fileObj, CancellationToken? ct = null) {
var retval = new FileResponse();
var targetUrl = fileObj.buildURL(baseURL);
var targetUrl = fileObj.buildURL(String.IsNullOrEmpty(fileObj.bucketToken) ? baseURL : baseURL + $"/{fileObj.bucketToken}");

if (!String.IsNullOrEmpty(fileObj.url)) {
// URL Upload
Expand Down

0 comments on commit c503c9c

Please sign in to comment.