using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json.Linq; using RudderSdk.Core.Models.Ugc; namespace RudderSdk.Core; /// User-generated content: upload, list, download URLs, deletion. public sealed class UgcService { private readonly RudderClient _client; internal UgcService(RudderClient client) => _client = client; /// /// Uploads a file in one call: pre-signed URL, PUT of the bytes, submission. /// Requires an upload transport in . /// public async Task UploadAsync( string name, byte[] data, string? description = null, JToken? metadata = null, CancellationToken cancellationToken = default) { var uploadTransport = _client.Options.UploadTransport ?? throw new InvalidOperationException("UploadTransport is not configured."); var upload = await GetUploadUrlAsync(name, cancellationToken).ConfigureAwait(false); await uploadTransport.PutAsync( upload.UploadUrl, data ?? Array.Empty(), GetContentType(name), cancellationToken).ConfigureAwait(false); return await SubmitAsync( name, upload.FileKey, data?.LongLength ?? 0, description, metadata, cancellationToken).ConfigureAwait(false); } /// Fetches one page of the player's submissions. public Task ListAsync(string? status = null, int limit = 20, string? cursor = null, CancellationToken cancellationToken = default) => _client.SendAsync( "GET", "/sdk/v1/ugc" + Url.Query( ("status", status), ("limit", limit > 0 ? limit.ToString() : null), ("cursor", cursor)), cancellationToken); /// Iterates over all of the player's submissions, following the cursor pagination. public async IAsyncEnumerable ListAllAsync( string? status = null, int limit = 20, [EnumeratorCancellation] CancellationToken cancellationToken = default) { string? cursor = null; do { var page = await ListAsync(status, limit, cursor, cancellationToken).ConfigureAwait(false); if (page?.Items != null) { foreach (var item in page.Items) yield return item; } cursor = page?.NextCursor; } while (!string.IsNullOrEmpty(cursor)); } /// Returns one submission. public Task GetAsync(string id, CancellationToken cancellationToken = default) => _client.SendAsync("GET", "/sdk/v1/ugc/" + Url.Encode(id), cancellationToken); /// Deletes a submission; returns false when the server refused. public async Task DeleteAsync(string id, CancellationToken cancellationToken = default) { var response = await _client.SendAsync( "DELETE", "/sdk/v1/ugc/" + Url.Encode(id), cancellationToken).ConfigureAwait(false); return response == null || response.Success; } /// Requests a pre-signed upload URL. public Task GetUploadUrlAsync(string filename, CancellationToken cancellationToken = default) => _client.SendAsync( "GET", "/sdk/v1/ugc/upload-url" + Url.Query(("filename", filename)), cancellationToken); /// /// Resolves the download URL of a submission. /// Throws when the server returned none. /// public async Task GetDownloadUrlAsync(string id, CancellationToken cancellationToken = default) { var response = await _client.SendAsync( "GET", "/sdk/v1/ugc/" + Url.Encode(id) + "/download", cancellationToken).ConfigureAwait(false); if (response == null || string.IsNullOrEmpty(response.DownloadUrl)) throw new InvalidOperationException("The server returned no download URL."); return response.DownloadUrl; } /// Registers an uploaded file as a submission. public Task SubmitAsync( string name, string fileKey, long fileSize, string? description = null, JToken? metadata = null, CancellationToken cancellationToken = default) { return _client.SendAsync( "POST", "/sdk/v1/ugc", new SubmitUgcRequest { Name = name, FileKey = fileKey, FileSize = fileSize, Description = description!, Metadata = metadata! }, cancellationToken); } private static string GetContentType(string filename) { var ext = System.IO.Path.GetExtension(filename)?.ToLowerInvariant(); switch (ext) { case ".png": return "image/png"; case ".jpg": case ".jpeg": return "image/jpeg"; case ".gif": return "image/gif"; case ".webp": return "image/webp"; case ".txt": return "text/plain"; default: return "application/octet-stream"; } } }