Improve uploader client connection and diagnostics
This commit is contained in:
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Photobooth\PhotoboothConnectRedeemRequest;
|
||||
use App\Models\Event;
|
||||
use App\Services\Photobooth\PhotoboothConnectCodeService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
@@ -33,6 +34,7 @@ class PhotoboothConnectController extends Controller
|
||||
|
||||
return response()->json([
|
||||
'data' => [
|
||||
'event_name' => $this->resolveEventName($event),
|
||||
'upload_url' => route('api.v1.photobooth.upload'),
|
||||
'username' => $setting->username,
|
||||
'password' => $setting->password,
|
||||
@@ -42,4 +44,27 @@ class PhotoboothConnectController extends Controller
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveEventName(?Event $event): ?string
|
||||
{
|
||||
if (! $event) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$name = $event->name;
|
||||
|
||||
if (is_string($name) && trim($name) !== '') {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (is_array($name)) {
|
||||
foreach ($name as $value) {
|
||||
if (is_string($value) && trim($value) !== '') {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $event->slug ?: null;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 67 B |
@@ -30,6 +30,7 @@
|
||||
<TextBlock Text="Upload-Ordner" FontWeight="SemiBold" />
|
||||
<TextBlock x:Name="FolderText" Text="Noch nicht ausgewählt." TextWrapping="Wrap" />
|
||||
<Button x:Name="PickFolderButton" Content="Ordner auswählen" Click="PickFolderButton_Click" IsEnabled="False" />
|
||||
<Button x:Name="TestUploadButton" Content="Test-Upload senden" Click="TestUploadButton_Click" IsEnabled="False" />
|
||||
</StackPanel>
|
||||
|
||||
<ToggleSwitch x:Name="QuietToggle" Content="Ruhiger Modus (nur Fehler anzeigen)" />
|
||||
@@ -41,6 +42,17 @@
|
||||
<TextBlock Text="Status" FontWeight="SemiBold" />
|
||||
<TextBlock x:Name="StatusText" Text="Nicht verbunden." TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="LastUploadText" Text="Letzter Upload: —" />
|
||||
<TextBlock x:Name="QueueStatusText" Text="Warteschlange: 0 · Läuft: 0 · Fehlgeschlagen: 0" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Background="#1F000000" Padding="12" CornerRadius="8">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Text="Details" FontWeight="SemiBold" />
|
||||
<TextBlock x:Name="EventNameText" Text="Event: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="BaseUrlText" Text="Basis-URL: —" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="VersionText" Text="App-Version: —" />
|
||||
<TextBlock x:Name="LastErrorText" Text="Letzter Fehler: —" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
|
||||
@@ -3,9 +3,12 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Threading;
|
||||
using PhotoboothUploader.Models;
|
||||
using PhotoboothUploader.Services;
|
||||
@@ -22,6 +25,10 @@ public partial class MainWindow : Window
|
||||
private FileSystemWatcher? _watcher;
|
||||
private readonly Dictionary<string, UploadItem> _uploadsByPath = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly HashSet<string> _failedPaths = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly string _userAgent;
|
||||
private int _queuedCount;
|
||||
private int _uploadingCount;
|
||||
private int _failedCount;
|
||||
|
||||
public ObservableCollection<UploadItem> RecentUploads { get; } = new();
|
||||
|
||||
@@ -29,8 +36,10 @@ public partial class MainWindow : Window
|
||||
{
|
||||
InitializeComponent();
|
||||
_settings = _settingsStore.Load();
|
||||
_settings.BaseUrl ??= DefaultBaseUrl;
|
||||
_client = new PhotoboothConnectClient(_settings.BaseUrl);
|
||||
_settings.BaseUrl = NormalizeBaseUrl(_settings.BaseUrl) ?? DefaultBaseUrl;
|
||||
_userAgent = $"FotospielPhotoboothUploader/{GetAppVersion()}";
|
||||
_client = new PhotoboothConnectClient(_settings.BaseUrl, _userAgent);
|
||||
_uploadService.Configure(_userAgent);
|
||||
_settingsStore.Save(_settings);
|
||||
DataContext = this;
|
||||
ApplySettings();
|
||||
@@ -38,6 +47,20 @@ public partial class MainWindow : Window
|
||||
|
||||
private async void ConnectButton_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var normalizedBaseUrl = NormalizeBaseUrl(_settings.BaseUrl);
|
||||
if (normalizedBaseUrl is null)
|
||||
{
|
||||
UpdateStatus("Ungültige Basis-URL. Bitte Support kontaktieren.");
|
||||
SetLastError("Ungültige Basis-URL.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.Equals(normalizedBaseUrl, _settings.BaseUrl, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_settings.BaseUrl = normalizedBaseUrl;
|
||||
_client = new PhotoboothConnectClient(_settings.BaseUrl, _userAgent);
|
||||
}
|
||||
|
||||
var code = (CodeBox.Text ?? string.Empty).Trim();
|
||||
|
||||
if (code.Length != 6 || code.Any(ch => ch is < '0' or > '9'))
|
||||
@@ -54,18 +77,23 @@ public partial class MainWindow : Window
|
||||
if (response.Data is null)
|
||||
{
|
||||
StatusText.Text = response.Message ?? "Verbindung fehlgeschlagen.";
|
||||
SetLastError(response.Message ?? "Verbindung fehlgeschlagen.");
|
||||
ConnectButton.IsEnabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
ClearLastError();
|
||||
_settings.UploadUrl = ResolveUploadUrl(response.Data.UploadUrl);
|
||||
_settings.Username = response.Data.Username;
|
||||
_settings.Password = response.Data.Password;
|
||||
_settings.ResponseFormat = response.Data.ResponseFormat;
|
||||
_settings.EventName = response.Data.EventName;
|
||||
_settingsStore.Save(_settings);
|
||||
|
||||
StatusText.Text = "Verbunden. Upload bereit.";
|
||||
PickFolderButton.IsEnabled = true;
|
||||
TestUploadButton.IsEnabled = true;
|
||||
UpdateDiagnostics();
|
||||
StartUploadPipelineIfReady();
|
||||
ConnectButton.IsEnabled = true;
|
||||
}
|
||||
@@ -105,9 +133,12 @@ public partial class MainWindow : Window
|
||||
{
|
||||
StatusText.Text = "Verbunden. Upload bereit.";
|
||||
PickFolderButton.IsEnabled = true;
|
||||
TestUploadButton.IsEnabled = true;
|
||||
StartUploadPipelineIfReady();
|
||||
}
|
||||
|
||||
UpdateCountersText();
|
||||
UpdateDiagnostics();
|
||||
UpdateSteps();
|
||||
}
|
||||
|
||||
@@ -173,29 +204,40 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnQueued(string path)
|
||||
{
|
||||
_queuedCount = Math.Max(0, _queuedCount + 1);
|
||||
UpdateUpload(path, UploadStatus.Queued);
|
||||
UpdateStatusIfAllowed($"Wartet: {Path.GetFileName(path)}", false);
|
||||
UpdateCountersText();
|
||||
}
|
||||
|
||||
private void OnUploading(string path)
|
||||
{
|
||||
_queuedCount = Math.Max(0, _queuedCount - 1);
|
||||
_uploadingCount = Math.Max(0, _uploadingCount + 1);
|
||||
UpdateUpload(path, UploadStatus.Uploading);
|
||||
UpdateStatusIfAllowed($"Upload läuft: {Path.GetFileName(path)}", false);
|
||||
UpdateCountersText();
|
||||
}
|
||||
|
||||
private void OnSuccess(string path)
|
||||
{
|
||||
_failedPaths.Remove(path);
|
||||
_uploadingCount = Math.Max(0, _uploadingCount - 1);
|
||||
UpdateUpload(path, UploadStatus.Success);
|
||||
UpdateStatusIfAllowed($"Hochgeladen: {Path.GetFileName(path)}", false);
|
||||
UpdateCountersText();
|
||||
}
|
||||
|
||||
private void OnFailure(string path)
|
||||
{
|
||||
_failedPaths.Add(path);
|
||||
_uploadingCount = Math.Max(0, _uploadingCount - 1);
|
||||
_failedCount = Math.Max(0, _failedCount + 1);
|
||||
UpdateUpload(path, UploadStatus.Failed);
|
||||
UpdateStatusIfAllowed($"Upload fehlgeschlagen: {Path.GetFileName(path)}", true);
|
||||
SetLastError($"Upload fehlgeschlagen: {Path.GetFileName(path)}");
|
||||
UpdateRetryButton();
|
||||
UpdateCountersText();
|
||||
}
|
||||
|
||||
private void UpdateUpload(string path, UploadStatus status)
|
||||
@@ -244,13 +286,22 @@ public partial class MainWindow : Window
|
||||
|
||||
private void RetryFailedButton_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_failedPaths.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var retried = _failedPaths.Count;
|
||||
foreach (var path in _failedPaths.ToList())
|
||||
{
|
||||
_uploadService.Enqueue(path, OnQueued);
|
||||
}
|
||||
|
||||
_failedPaths.Clear();
|
||||
_failedCount = Math.Max(0, _failedCount - retried);
|
||||
_queuedCount = Math.Max(0, _queuedCount + retried);
|
||||
UpdateRetryButton();
|
||||
UpdateCountersText();
|
||||
}
|
||||
|
||||
private void UpdateSteps()
|
||||
@@ -264,6 +315,37 @@ public partial class MainWindow : Window
|
||||
StepReadyText.Text = ready ? "3. Upload läuft ✓" : "3. Upload läuft";
|
||||
}
|
||||
|
||||
private async void TestUploadButton_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_settings.UploadUrl))
|
||||
{
|
||||
UpdateStatus("Bitte zuerst verbinden.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var tempPath = await CreateSampleUploadAsync();
|
||||
_uploadService.Enqueue(tempPath, OnQueued);
|
||||
UpdateStatusIfAllowed("Test-Upload hinzugefügt.", false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
UpdateStatus("Test-Upload konnte nicht erstellt werden.");
|
||||
SetLastError("Test-Upload konnte nicht erstellt werden.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> CreateSampleUploadAsync()
|
||||
{
|
||||
var uri = new Uri("avares://PhotoboothUploader/Assets/sample-upload.png");
|
||||
await using var source = AssetLoader.Open(uri);
|
||||
var tempPath = Path.Combine(Path.GetTempPath(), $"fotospiel-test-{DateTimeOffset.Now:yyyyMMddHHmmss}.png");
|
||||
await using var target = File.Create(tempPath);
|
||||
await source.CopyToAsync(target);
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
private string? ResolveUploadUrl(string? uploadUrl)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(uploadUrl))
|
||||
@@ -279,4 +361,82 @@ public partial class MainWindow : Window
|
||||
var baseUri = new Uri(_settings.BaseUrl ?? DefaultBaseUrl, UriKind.Absolute);
|
||||
return new Uri(baseUri, uploadUrl).ToString();
|
||||
}
|
||||
|
||||
private static string? NormalizeBaseUrl(string? baseUrl)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(baseUrl))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var trimmed = baseUrl.Trim();
|
||||
|
||||
if (Uri.TryCreate(trimmed, UriKind.Absolute, out var absolute))
|
||||
{
|
||||
return absolute.GetLeftPart(UriPartial.Authority);
|
||||
}
|
||||
|
||||
if (Uri.TryCreate($"https://{trimmed}", UriKind.Absolute, out absolute))
|
||||
{
|
||||
return absolute.GetLeftPart(UriPartial.Authority);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void UpdateCountersText()
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
QueueStatusText.Text = $"Warteschlange: {_queuedCount} · Läuft: {_uploadingCount} · Fehlgeschlagen: {_failedCount}";
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateDiagnostics()
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
EventNameText.Text = $"Event: {_settings.EventName ?? "—"}";
|
||||
BaseUrlText.Text = $"Basis-URL: {_settings.BaseUrl ?? "—"}";
|
||||
VersionText.Text = $"App-Version: {GetAppVersion()}";
|
||||
LastErrorText.Text = $"Letzter Fehler: {FormatLastError()}";
|
||||
});
|
||||
}
|
||||
|
||||
private void SetLastError(string message)
|
||||
{
|
||||
_settings.LastError = message;
|
||||
_settings.LastErrorAt = DateTimeOffset.Now.ToString("O");
|
||||
_settingsStore.Save(_settings);
|
||||
UpdateDiagnostics();
|
||||
}
|
||||
|
||||
private void ClearLastError()
|
||||
{
|
||||
_settings.LastError = null;
|
||||
_settings.LastErrorAt = null;
|
||||
_settingsStore.Save(_settings);
|
||||
UpdateDiagnostics();
|
||||
}
|
||||
|
||||
private string FormatLastError()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_settings.LastError))
|
||||
{
|
||||
return "—";
|
||||
}
|
||||
|
||||
if (DateTimeOffset.TryParse(_settings.LastErrorAt, out var timestamp))
|
||||
{
|
||||
return $"{timestamp:dd.MM.yyyy HH:mm} – {_settings.LastError}";
|
||||
}
|
||||
|
||||
return _settings.LastError;
|
||||
}
|
||||
|
||||
private static string GetAppVersion()
|
||||
{
|
||||
var version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
return version is null ? "0.0.0" : version.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ public sealed class PhotoboothConnectResponse
|
||||
|
||||
public sealed class PhotoboothConnectPayload
|
||||
{
|
||||
[JsonPropertyName("event_name")]
|
||||
public string? EventName { get; set; }
|
||||
|
||||
[JsonPropertyName("upload_url")]
|
||||
public string? UploadUrl { get; set; }
|
||||
|
||||
|
||||
@@ -3,9 +3,12 @@ namespace PhotoboothUploader.Models;
|
||||
public sealed class PhotoboothSettings
|
||||
{
|
||||
public string? BaseUrl { get; set; }
|
||||
public string? EventName { get; set; }
|
||||
public string? UploadUrl { get; set; }
|
||||
public string? Username { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public string? ResponseFormat { get; set; }
|
||||
public string? WatchFolder { get; set; }
|
||||
public string? LastError { get; set; }
|
||||
public string? LastErrorAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\logo.png" />
|
||||
<AvaloniaResource Include="Assets\sample-upload.png" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
@@ -10,41 +12,111 @@ namespace PhotoboothUploader.Services;
|
||||
|
||||
public sealed class PhotoboothConnectClient
|
||||
{
|
||||
private const int MaxRetries = 2;
|
||||
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly JsonSerializerOptions _jsonOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
};
|
||||
|
||||
public PhotoboothConnectClient(string baseUrl)
|
||||
public PhotoboothConnectClient(string baseUrl, string userAgent)
|
||||
{
|
||||
_httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(baseUrl),
|
||||
Timeout = DefaultTimeout,
|
||||
};
|
||||
|
||||
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(userAgent);
|
||||
}
|
||||
|
||||
public async Task<PhotoboothConnectResponse> RedeemAsync(string code, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await _httpClient.PostAsJsonAsync("/api/v1/photobooth/connect", new { code }, cancellationToken);
|
||||
var payload = await response.Content.ReadFromJsonAsync<PhotoboothConnectResponse>(_jsonOptions, cancellationToken);
|
||||
var request = new { code };
|
||||
|
||||
if (payload is null)
|
||||
for (var attempt = 0; attempt <= MaxRetries; attempt++)
|
||||
{
|
||||
return new PhotoboothConnectResponse
|
||||
try
|
||||
{
|
||||
Message = response.ReasonPhrase ?? "Verbindung fehlgeschlagen.",
|
||||
};
|
||||
using var response = await _httpClient.PostAsJsonAsync("/api/v1/photobooth/connect", request, cancellationToken);
|
||||
var payload = await ReadPayloadAsync(response, cancellationToken);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return payload ?? Fail(response.ReasonPhrase ?? "Verbindung fehlgeschlagen.");
|
||||
}
|
||||
|
||||
if (response.StatusCode is HttpStatusCode.UnprocessableEntity or HttpStatusCode.Conflict or HttpStatusCode.Unauthorized)
|
||||
{
|
||||
return payload ?? Fail(response.ReasonPhrase ?? "Verbindung fehlgeschlagen.");
|
||||
}
|
||||
|
||||
if (attempt < MaxRetries && IsTransientStatus(response.StatusCode))
|
||||
{
|
||||
await Task.Delay(GetRetryDelay(attempt), cancellationToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
return payload ?? Fail(response.ReasonPhrase ?? "Verbindung fehlgeschlagen.");
|
||||
}
|
||||
catch (TaskCanceledException) when (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (attempt < MaxRetries)
|
||||
{
|
||||
await Task.Delay(GetRetryDelay(attempt), cancellationToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
return Fail("Zeitüberschreitung bei der Verbindung.");
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
if (attempt < MaxRetries)
|
||||
{
|
||||
await Task.Delay(GetRetryDelay(attempt), cancellationToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
return Fail("Netzwerkfehler. Bitte Verbindung prüfen.");
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return Fail("Serverantwort konnte nicht gelesen werden.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return Fail("Verbindung fehlgeschlagen.");
|
||||
}
|
||||
|
||||
private async Task<PhotoboothConnectResponse?> ReadPayloadAsync(HttpResponseMessage response, CancellationToken cancellationToken)
|
||||
{
|
||||
if (response.Content.Headers.ContentLength == 0)
|
||||
{
|
||||
return new PhotoboothConnectResponse
|
||||
{
|
||||
Message = payload.Message ?? "Verbindung fehlgeschlagen.",
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
return payload;
|
||||
return await response.Content.ReadFromJsonAsync<PhotoboothConnectResponse>(_jsonOptions, cancellationToken);
|
||||
}
|
||||
|
||||
private static bool IsTransientStatus(HttpStatusCode statusCode)
|
||||
{
|
||||
return statusCode is HttpStatusCode.RequestTimeout or HttpStatusCode.TooManyRequests
|
||||
or HttpStatusCode.BadGateway or HttpStatusCode.ServiceUnavailable or HttpStatusCode.GatewayTimeout
|
||||
or HttpStatusCode.InternalServerError;
|
||||
}
|
||||
|
||||
private static TimeSpan GetRetryDelay(int attempt)
|
||||
{
|
||||
return TimeSpan.FromMilliseconds(500 * (attempt + 1));
|
||||
}
|
||||
|
||||
private static PhotoboothConnectResponse Fail(string message)
|
||||
{
|
||||
return new PhotoboothConnectResponse
|
||||
{
|
||||
Message = message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,20 @@ namespace PhotoboothUploader.Services;
|
||||
|
||||
public sealed class UploadService
|
||||
{
|
||||
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(20);
|
||||
private readonly Channel<string> _queue = Channel.CreateUnbounded<string>();
|
||||
private readonly ConcurrentDictionary<string, byte> _pending = new(StringComparer.OrdinalIgnoreCase);
|
||||
private string _userAgent = "FotospielPhotoboothUploader";
|
||||
private CancellationTokenSource? _cts;
|
||||
|
||||
public void Configure(string userAgent)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(userAgent))
|
||||
{
|
||||
_userAgent = userAgent;
|
||||
}
|
||||
}
|
||||
|
||||
public void Start(
|
||||
PhotoboothSettings settings,
|
||||
Action<string> onQueued,
|
||||
@@ -61,6 +71,9 @@ public sealed class UploadService
|
||||
}
|
||||
|
||||
using var client = new HttpClient();
|
||||
client.Timeout = DefaultTimeout;
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd(_userAgent);
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
while (await _queue.Reader.WaitToReadAsync(token))
|
||||
{
|
||||
|
||||
@@ -39,6 +39,7 @@ class PhotoboothConnectCodeTest extends TenantTestCase
|
||||
{
|
||||
$event = Event::factory()->for($this->tenant)->create([
|
||||
'slug' => 'connect-code-redeem',
|
||||
'name' => 'Winterhochzeit',
|
||||
]);
|
||||
|
||||
EventPhotoboothSetting::factory()
|
||||
@@ -59,6 +60,7 @@ class PhotoboothConnectCodeTest extends TenantTestCase
|
||||
]);
|
||||
|
||||
$redeem->assertOk()
|
||||
->assertJsonPath('data.event_name', 'Winterhochzeit')
|
||||
->assertJsonPath('data.upload_url', fn ($value) => is_string($value) && $value !== '')
|
||||
->assertJsonPath('data.username', 'pbconnect')
|
||||
->assertJsonPath('data.password', 'SECRET12');
|
||||
|
||||
Reference in New Issue
Block a user