diff --git a/.gitignore b/.gitignore
index 54f96b6..fb32c20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,6 +39,7 @@ Thumbs.db
# =============== #
.vs/
.vscode/
+.claude/
# =============== #
# Rider #
diff --git a/Runtime/Scripts/BetaHubDiagnostics.cs b/Runtime/Scripts/BetaHubDiagnostics.cs
new file mode 100644
index 0000000..e0cf79b
--- /dev/null
+++ b/Runtime/Scripts/BetaHubDiagnostics.cs
@@ -0,0 +1,81 @@
+using System;
+using UnityEngine;
+
+namespace BetaHub
+{
+ ///
+ /// Interface for contextual diagnostics providing error handling and logging capabilities.
+ ///
+ public interface IContextualDiagnostics
+ {
+ ///
+ /// Logs an informational message for an operation.
+ ///
+ void LogInfo(string operation, string message);
+
+ ///
+ /// Logs a successful operation completion.
+ ///
+ void LogSuccess(string operation, string message);
+
+ ///
+ /// Logs progress information for a long-running operation.
+ ///
+ void LogProgress(string operation, string message);
+
+ ///
+ /// Logs an error with a custom message and exception details.
+ ///
+ void LogError(string operation, string message, Exception ex = null);
+ }
+
+ ///
+ /// Main entry point for BetaHub diagnostics.
+ ///
+ public static class BetaHubDiagnostics
+ {
+ ///
+ /// Creates a contextual diagnostics service for a specific component or operation context.
+ ///
+ public static IContextualDiagnostics ForContext(string contextName)
+ {
+ return new ContextualDiagnostics(contextName);
+ }
+ }
+
+ ///
+ /// Implementation of contextual diagnostics that provides consistent logging.
+ ///
+ internal class ContextualDiagnostics : IContextualDiagnostics
+ {
+ private readonly string _contextName;
+
+ public ContextualDiagnostics(string contextName)
+ {
+ _contextName = contextName ?? throw new ArgumentNullException(nameof(contextName));
+ }
+
+ public void LogInfo(string operation, string message)
+ {
+ Debug.Log($"[INFO] {_contextName}.{operation}: {message}");
+ }
+
+ public void LogSuccess(string operation, string message)
+ {
+ Debug.Log($"[SUCCESS] {_contextName}.{operation}: {message}");
+ }
+
+ public void LogProgress(string operation, string message)
+ {
+ Debug.Log($"[PROGRESS] {_contextName}.{operation}: {message}");
+ }
+
+ public void LogError(string operation, string message, Exception ex = null)
+ {
+ string errorMessage = ex != null
+ ? $"[ERROR] {_contextName}.{operation}: {message} - {ex.Message}\n{ex.StackTrace}"
+ : $"[ERROR] {_contextName}.{operation}: {message}";
+ Debug.LogError(errorMessage);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Scripts/BetaHubDiagnostics.cs.meta b/Runtime/Scripts/BetaHubDiagnostics.cs.meta
new file mode 100644
index 0000000..39b7006
--- /dev/null
+++ b/Runtime/Scripts/BetaHubDiagnostics.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7157588a478ce8b4fa2fff73cf1b9d5b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Scripts/BugReportUI.cs b/Runtime/Scripts/BugReportUI.cs
index 8318fd5..2ab0d4b 100644
--- a/Runtime/Scripts/BugReportUI.cs
+++ b/Runtime/Scripts/BugReportUI.cs
@@ -546,10 +546,6 @@ private IEnumerator SubmitBugReportCoroutine()
BugReportPanel.SetActive(false);
},
MediaUploadType,
- (error) =>
- {
- onIssueError(new ErrorMessage { error = error });
- },
customFieldsData.Count > 0 ? customFieldsData : null
),
(ex) => // done
diff --git a/Runtime/Scripts/Issue.cs b/Runtime/Scripts/Issue.cs
index f53e37c..d8f3ae7 100644
--- a/Runtime/Scripts/Issue.cs
+++ b/Runtime/Scripts/Issue.cs
@@ -7,6 +7,7 @@ namespace BetaHub
using UnityEngine;
using UnityEngine.Networking;
+
// Represents an issue (persistent or not on BetaHub)
public class Issue
{
@@ -40,7 +41,20 @@ public class Issue
private ReportSubmittedUI _reportSubmittedUI;
private bool _emailMyReport = false;
-
+
+ // Contextual diagnostics service for consistent error handling and logging
+ private IContextualDiagnostics _diagnostics;
+
+ private void InitializeDiagnostics()
+ {
+ string issueContext = string.IsNullOrEmpty(Id) ? "Draft" : Id;
+ _diagnostics = BetaHubDiagnostics.ForContext($"Issue({issueContext})");
+ if (_diagnostics == null)
+ {
+ Debug.LogWarning("Failed to initialize diagnostics");
+ }
+ }
+
// Reference structs moved to be nested inside Issue class
public struct LogFileReference
{
@@ -77,23 +91,26 @@ public Issue(string betahubEndpoint, string projectId,
if (!authToken.StartsWith("tkn-"))
{
- throw new Exception("Auth token must start with tkn-");
+ throw new ArgumentException("Auth token must start with tkn-", nameof(authToken));
}
if (string.IsNullOrEmpty(projectId))
{
- throw new Exception("Project ID is required");
+ throw new ArgumentException("Project ID is required", nameof(projectId));
}
if (messagePanelUI == null)
{
- throw new Exception("Message panel UI is required");
+ throw new ArgumentNullException(nameof(messagePanelUI), "Message panel UI is required");
}
if (reportSubmittedUI == null)
{
- throw new Exception("Report submitted UI is required");
+ throw new ArgumentNullException(nameof(reportSubmittedUI), "Report submitted UI is required");
}
+
+ // Initialize diagnostics for this issue instance
+ InitializeDiagnostics();
}
// Posts an issue to BetaHub.
@@ -112,17 +129,27 @@ public IEnumerator PostIssue(string description, string steps = null,
List screenshots = null, List logFiles = null,
int releaseId = 0, string releaseLabel = "",
bool autoPublish = false,
- Action onAllMediaUploaded = null, MediaUploadType mediaUploadType = MediaUploadType.UploadInBackground, Action onError = null,
+ Action onAllMediaUploaded = null, MediaUploadType mediaUploadType = MediaUploadType.UploadInBackground,
Dictionary customFields = null)
+ {
+ yield return PostIssueInternal(description, steps, screenshots, logFiles, releaseId, releaseLabel, autoPublish, onAllMediaUploaded, mediaUploadType, customFields);
+ }
+
+ private IEnumerator PostIssueInternal(string description, string steps,
+ List screenshots, List logFiles,
+ int releaseId, string releaseLabel,
+ bool autoPublish,
+ Action onAllMediaUploaded, MediaUploadType mediaUploadType,
+ Dictionary customFields)
{
if (Id != null)
{
- throw new Exception("Issue instance cannot be reused for posting.");
+ throw new InvalidOperationException("Issue instance cannot be reused for posting.");
}
if (releaseId > 0 && !string.IsNullOrEmpty(releaseLabel))
{
- throw new Exception("Cannot set both release ID and release label");
+ throw new ArgumentException("Cannot set both release ID and release label");
}
// Initialize state for this posting attempt
@@ -130,30 +157,17 @@ public IEnumerator PostIssue(string description, string steps = null,
_mediaUploadComplete = false;
_isPublished = false;
- string issueIdLocal = null; // Use local variables for draft result callback
- string issueUrlLocal = null;
- string updateIssueAuthTokenLocal = null;
- string error = null;
+ yield return PostIssueDraft(description, steps, releaseId, releaseLabel, customFields);
- yield return PostIssueDraft(description, steps, releaseId, releaseLabel, customFields, (draftResult) =>
- {
- issueIdLocal = draftResult.IssueId;
- updateIssueAuthTokenLocal = draftResult.UpdateIssueAuthToken;
- error = draftResult.Error;
- issueUrlLocal = draftResult.Url;
- });
+ // Draft created successfully, store ID and token in member variables
+ this.Id = _lastDraftResult.IssueId;
+ this.Url = _lastDraftResult.Url;
+ this._updateIssueAuthToken = _lastDraftResult.UpdateIssueAuthToken;
- if (error != null)
- {
- Debug.LogError("Error posting issue: " + error);
- onError?.Invoke(error);
- yield break;
- }
+ // Update diagnostics with the new issue ID
+ InitializeDiagnostics();
- // Draft created successfully, store ID and token in member variables
- this.Id = issueIdLocal;
- this.Url = issueUrlLocal;
- this._updateIssueAuthToken = updateIssueAuthTokenLocal;
+ _diagnostics?.LogSuccess("PostIssueDraft", $"Created draft issue with ID {this.Id}");
if (mediaUploadType == MediaUploadType.UploadInBackground)
{
@@ -169,19 +183,26 @@ public IEnumerator PostIssue(string description, string steps = null,
// Mark media as complete and attempt to publish if requested
yield return MarkMediaCompleteAndTryPublish();
+
+ _diagnostics?.LogSuccess("PostIssue", "Issue posting flow completed successfully");
}
public IEnumerator SubmitEmail(string email)
+ {
+ yield return SubmitEmailInternal(email);
+ }
+
+ private IEnumerator SubmitEmailInternal(string email)
{
if (string.IsNullOrEmpty(email))
{
- throw new Exception("Email is required");
+ throw new ArgumentException("Email is required", nameof(email));
}
// issue must be persistent
if (string.IsNullOrEmpty(Id))
{
- throw new Exception("Issue must be persistent before submitting email");
+ throw new InvalidOperationException("Issue must be persistent before submitting email");
}
WWWForm form = new WWWForm();
@@ -195,9 +216,11 @@ public IEnumerator SubmitEmail(string email)
if (www.result != UnityWebRequest.Result.Success)
{
- Debug.LogError("Error submitting email: " + www.error);
+ throw new InvalidOperationException($"Error submitting email (HTTP {www.responseCode}): {www.error}");
}
}
+
+ _diagnostics?.LogSuccess("SubmitEmail", $"Email {email} submitted successfully");
}
@@ -205,11 +228,18 @@ public IEnumerator SubmitEmail(string email)
// If media upload is already complete, it publishes immediately.
// Otherwise, it sets a flag ensuring publication occurs once media upload finishes.
public IEnumerator Publish(bool emailMyReport)
+ {
+ yield return PublishInternal(emailMyReport);
+ }
+
+ private IEnumerator PublishInternal(bool emailMyReport)
{
_emailMyReport = emailMyReport;
-
+
// Request publishing and attempt to publish if conditions are met
yield return RequestPublishAndTryPublish();
+
+ _diagnostics?.LogSuccess("Publish", "Publish request completed successfully");
}
// Helper method called after media upload completes
@@ -225,7 +255,7 @@ private IEnumerator MarkMediaCompleteAndTryPublish()
}
else
{
- Debug.LogWarning("Failed to acquire lock for MarkMediaCompleteAndTryPublish within timeout");
+ throw new TimeoutException("Failed to acquire lock for MarkMediaCompleteAndTryPublish within timeout");
}
}
finally
@@ -251,7 +281,7 @@ private IEnumerator RequestPublishAndTryPublish()
}
else
{
- Debug.LogWarning("Failed to acquire lock for RequestPublishAndTryPublish within timeout");
+ throw new TimeoutException("Failed to acquire lock for RequestPublishAndTryPublish within timeout");
}
}
finally
@@ -283,7 +313,7 @@ private IEnumerator CheckAndPublishIfReady()
}
else
{
- Debug.LogWarning("Failed to acquire lock for CheckAndPublishIfReady within timeout");
+ throw new TimeoutException("Failed to acquire lock for CheckAndPublishIfReady within timeout");
}
}
finally
@@ -297,13 +327,16 @@ private IEnumerator CheckAndPublishIfReady()
if (shouldPublish)
{
yield return PublishNow();
+ _diagnostics?.LogSuccess("PublishNow", "Issue published successfully");
}
// else: Conditions not met yet, publishing will be checked again later if needed.
}
// posts a draft issue to BetaHub.
- // returns the issue id and the update issue auth token via callback
- private IEnumerator PostIssueDraft(string description, string steps, int releaseId, string releaseLabel, Dictionary customFields, Action onResult)
+ // stores the draft result in _lastDraftResult field
+ // throws exceptions on failure
+ private DraftResult _lastDraftResult;
+ private IEnumerator PostIssueDraft(string description, string steps, int releaseId, string releaseLabel, Dictionary customFields)
{
WWWForm form = new WWWForm();
form.AddField("issue[description]", description);
@@ -328,7 +361,7 @@ private IEnumerator PostIssueDraft(string description, string steps, int release
}
string url = GetPostIssueUrl();
-
+
using (UnityWebRequest www = UnityWebRequest.Post(url, form))
{
www.SetRequestHeader("Authorization", "FormUser " + _createIssueAuthToken);
@@ -340,12 +373,15 @@ private IEnumerator PostIssueDraft(string description, string steps, int release
{
string errorMessage = www.downloadHandler.text;
// try parsing as json, the format should be {"error":"...","status":"..."}
- try {
+ try
+ {
ErrorMessage errorMessageObject = JsonUtility.FromJson(errorMessage);
if (errorMessageObject != null)
{
errorMessage = errorMessageObject.error;
- } else {
+ }
+ else
+ {
errorMessage = "Unknown error";
}
}
@@ -354,57 +390,54 @@ private IEnumerator PostIssueDraft(string description, string steps, int release
Debug.LogError("Error parsing error message: " + e);
}
- Debug.LogError("Response code: " + www.responseCode);
- onResult?.Invoke(new DraftResult { IssueId = null, UpdateIssueAuthToken = null, Url = null, Error = errorMessage });
- yield break;
+ throw new InvalidOperationException($"Failed to create issue draft (HTTP {www.responseCode}): {errorMessage}");
}
string response = www.downloadHandler.text;
IssueResponse issueResponse = JsonUtility.FromJson(response);
- onResult?.Invoke(new DraftResult { IssueId = issueResponse.id, UpdateIssueAuthToken = issueResponse.token, Url = issueResponse.url, Error = null });
+ _lastDraftResult = new DraftResult { IssueId = issueResponse.id, UpdateIssueAuthToken = issueResponse.token, Url = issueResponse.url };
}
}
private IEnumerator PostAllMedia(List screenshots, List logFiles, GameRecorder gameRecorder)
{
- if (screenshots != null)
+ int totalFiles = (screenshots?.Count ?? 0) + (logFiles?.Count ?? 0) + (gameRecorder != null ? 1 : 0);
+ int uploadedFiles = 0;
+
+ _diagnostics?.LogInfo("PostAllMedia", $"Starting upload of {totalFiles} media files");
+
+ if (screenshots != null && screenshots.Count > 0)
{
- Debug.Log("Posting " + screenshots.Count + " screenshots");
-
- foreach (var screenshot in screenshots)
+ _diagnostics?.LogProgress("PostAllMedia", $"Uploading {screenshots.Count} screenshots");
+
+ for (int i = 0; i < screenshots.Count; i++)
{
- yield return PostScreenshot(screenshot);
+ var screenshot = screenshots[i];
+ yield return TryPostScreenshot(screenshot, ++uploadedFiles, totalFiles);
}
}
- else
- {
- Debug.Log("No screenshots to post");
- }
-
- if (logFiles != null)
+
+ if (logFiles != null && logFiles.Count > 0)
{
- Debug.Log("Posting " + logFiles.Count + " log files");
- foreach (var logFile in logFiles)
+ _diagnostics?.LogProgress("PostAllMedia", $"Uploading {logFiles.Count} log files");
+
+ for (int i = 0; i < logFiles.Count; i++)
{
- yield return PostLogFile(logFile);
+ var logFile = logFiles[i];
+ yield return TryPostLogFile(logFile, ++uploadedFiles, totalFiles);
}
}
- else
- {
- Debug.Log("No log files to post");
- }
if (gameRecorder != null)
{
- Debug.Log("Posting video");
- yield return PostVideo(gameRecorder);
- }
- else
- {
- Debug.Log("No video to post");
+ _diagnostics?.LogProgress("PostAllMedia", "Uploading video");
+ yield return TryPostVideo(gameRecorder, ++uploadedFiles, totalFiles);
}
+
+ _diagnostics?.LogSuccess("PostAllMedia", $"All {totalFiles} media files uploaded successfully");
}
+
private string GetPostIssueUrl()
{
return _betahubEndpoint + "projects/" + _projectId + "/issues.json";
@@ -414,41 +447,114 @@ private string GetSubmitEmailUrl()
{
return _betahubEndpoint + "projects/" + _projectId + "/issues/g-" + Id + "/set_reporter_email";
}
-
- private IEnumerator PostScreenshot(ScreenshotFileReference screenshot)
+
+ // Wrapper methods to handle try-catch with yield returns
+ private IEnumerator TryPostScreenshot(ScreenshotFileReference screenshot, int currentFile, int totalFiles)
+ {
+ IEnumerator coroutine = PostScreenshot(screenshot, currentFile, totalFiles);
+ bool hasMore = true;
+
+ while (hasMore)
+ {
+ try
+ {
+ hasMore = coroutine.MoveNext();
+ if (!hasMore)
+ break;
+ }
+ catch (Exception ex)
+ {
+ _diagnostics?.LogError("PostAllMedia", $"Failed to upload screenshot", ex);
+ yield break; // Exit the coroutine on error
+ }
+
+ yield return coroutine.Current;
+ }
+ }
+
+ private IEnumerator TryPostLogFile(LogFileReference logFile, int currentFile, int totalFiles)
{
- if (File.Exists(screenshot.path))
+ IEnumerator coroutine = PostLogFile(logFile, currentFile, totalFiles);
+ bool hasMore = true;
+
+ while (hasMore)
{
- yield return UploadFile("screenshots", "screenshot[image]", screenshot.path, "image/png");
+ try
+ {
+ hasMore = coroutine.MoveNext();
+ if (!hasMore)
+ break;
+ }
+ catch (Exception ex)
+ {
+ _diagnostics?.LogError("PostAllMedia", $"Failed to upload log file", ex);
+ yield break; // Exit the coroutine on error
+ }
+
+ yield return coroutine.Current;
+ }
+ }
- if (screenshot.removeAfterUpload)
+ private IEnumerator TryPostVideo(GameRecorder gameRecorder, int currentFile, int totalFiles)
+ {
+ IEnumerator coroutine = PostVideo(gameRecorder, currentFile, totalFiles);
+ bool hasMore = true;
+
+ while (hasMore)
+ {
+ try
{
- File.Delete(screenshot.path);
+ hasMore = coroutine.MoveNext();
+ if (!hasMore)
+ break;
}
+ catch (Exception ex)
+ {
+ _diagnostics?.LogError("PostAllMedia", $"Failed to upload video", ex);
+ yield break; // Exit the coroutine on error
+ }
+
+ yield return coroutine.Current;
+ }
+ }
+
+ private IEnumerator PostScreenshot(ScreenshotFileReference screenshot, int currentFile, int totalFiles)
+ {
+ if (!File.Exists(screenshot.path))
+ {
+ throw new FileNotFoundException($"Screenshot file not found: {screenshot.path}");
+ }
+
+ yield return UploadFile("screenshots", "screenshot[image]", screenshot.path, "image/png");
+
+ if (screenshot.removeAfterUpload)
+ {
+ File.Delete(screenshot.path);
}
+
+ _diagnostics?.LogProgress("PostScreenshot", $"Screenshot {Path.GetFileName(screenshot.path)} uploaded ({currentFile}/{totalFiles})");
}
-
- private IEnumerator PostLogFile(LogFileReference logFile)
+
+ private IEnumerator PostLogFile(LogFileReference logFile, int currentFile, int totalFiles)
{
+ string fileName;
+
if (logFile.logger != null)
{
- // Use logger's safe read method to avoid sharing violations
- Debug.Log("Reading BetaHub log file safely using Logger instance");
byte[] fileData = logFile.logger.ReadLogFileBytes();
- if (fileData != null)
+ if (fileData == null)
{
- string fileName = Path.GetFileName(logFile.logger.LogPath) ?? "BH_Player.log";
- yield return UploadStringAsFile("log_files", "log_file[file]", fileData, fileName, "text/plain");
- }
- else
- {
- Debug.LogError("Failed to read log file data from Logger instance");
+ throw new InvalidOperationException("Failed to read log file data from Logger instance");
}
+
+ fileName = Path.GetFileName(logFile.logger.LogPath) ?? "BH_Player.log";
+ yield return UploadStringAsFile("log_files", "log_file[file]", fileData, fileName, "text/plain");
}
else if (File.Exists(logFile.path))
{
// Original file path logic
+ fileName = Path.GetFileName(logFile.path);
yield return UploadFile("log_files", "log_file[file]", logFile.path, "text/plain");
if (logFile.removeAfterUpload)
@@ -456,55 +562,64 @@ private IEnumerator PostLogFile(LogFileReference logFile)
File.Delete(logFile.path);
}
}
+ else
+ {
+ throw new FileNotFoundException($"Log file not found: {logFile.path}");
+ }
+
+ _diagnostics?.LogProgress("PostLogFile", $"Log file {fileName} uploaded ({currentFile}/{totalFiles})");
}
-
- private IEnumerator PostVideo(GameRecorder gameRecorder)
+
+ private IEnumerator PostVideo(GameRecorder gameRecorder, int currentFile, int totalFiles)
{
- if (gameRecorder != null)
+ string videoPath = gameRecorder.StopRecordingAndSaveLastMinute();
+ if (string.IsNullOrEmpty(videoPath))
{
- string videoPath = gameRecorder.StopRecordingAndSaveLastMinute();
- if (!string.IsNullOrEmpty(videoPath) && File.Exists(videoPath))
- {
- yield return UploadFile("video_clips", "video_clip[video]", videoPath, "video/mp4");
+ throw new InvalidOperationException("GameRecorder failed to produce video file");
+ }
- // Delete the video file after uploading
- File.Delete(videoPath);
- }
+ if (!File.Exists(videoPath))
+ {
+ throw new FileNotFoundException($"Video file not found: {videoPath}");
}
+
+ yield return UploadFile("video_clips", "video_clip[video]", videoPath, "video/mp4");
+
+ // Delete the video file after uploading
+ File.Delete(videoPath);
+
+ _diagnostics?.LogProgress("PostVideo", $"Video uploaded ({currentFile}/{totalFiles})");
}
-
+
private IEnumerator UploadFile(string endpoint, string fieldName, string filePath, string contentType)
{
bool isLogFile = Path.GetExtension(filePath).Equals(".log", StringComparison.OrdinalIgnoreCase);
byte[] fileData;
-
+
if (isLogFile)
{
if (BugReportUI.Logger != null && filePath == BugReportUI.Logger.LogPath)
{
- Debug.Log("Reading log file safely using Logger instance");
fileData = BugReportUI.Logger.ReadLogFileBytes();
if (fileData == null)
{
- Debug.LogError("Failed to read log file data from Logger instance");
- yield break;
+ throw new InvalidOperationException("Failed to read log file data from Logger instance");
}
}
else
{
BugReportUI.PauseLogger();
-
+
try
{
fileData = File.ReadAllBytes(filePath);
}
catch (Exception ex)
{
- Debug.LogError($"Error reading file {filePath}: {ex.Message}");
BugReportUI.ResumeLogger();
- yield break;
+ throw new InvalidOperationException($"Error reading file {filePath}: {ex.Message}", ex);
}
-
+
BugReportUI.ResumeLogger();
}
}
@@ -517,11 +632,10 @@ private IEnumerator UploadFile(string endpoint, string fieldName, string filePat
}
catch (Exception ex)
{
- Debug.LogError($"Error reading file {filePath}: {ex.Message}");
- yield break;
+ throw new InvalidOperationException($"Error reading file {filePath}: {ex.Message}", ex);
}
}
-
+
yield return UploadStringAsFile(endpoint, fieldName, fileData, Path.GetFileName(filePath), contentType);
}
@@ -529,8 +643,7 @@ private IEnumerator UploadStringAsFile(string endpoint, string fieldName, byte[]
{
if (fileData == null)
{
- Debug.LogError($"Cannot upload {fileName}: file data is null");
- yield break;
+ throw new ArgumentNullException(nameof(fileData), $"Cannot upload {fileName}: file data is null");
}
WWWForm form = new WWWForm();
@@ -547,12 +660,9 @@ private IEnumerator UploadStringAsFile(string endpoint, string fieldName, byte[]
if (www.result != UnityWebRequest.Result.Success)
{
- Debug.LogError($"Error uploading {fileName}: {www.error}");
- }
- else
- {
- Debug.Log($"{fileName} uploaded successfully!");
+ throw new InvalidOperationException($"Error uploading {fileName} (HTTP {www.responseCode}): {www.error}");
}
+ // Success logging handled by calling method
}
}
@@ -561,8 +671,7 @@ private IEnumerator PublishNow()
// Ensure we have valid parameters before proceeding
if (string.IsNullOrEmpty(Id) || string.IsNullOrEmpty(_updateIssueAuthToken))
{
- Debug.LogError("Cannot publish issue: Missing Issue ID or Update Token.");
- yield break;
+ throw new InvalidOperationException("Cannot publish issue: Missing Issue ID or Update Token.");
}
WWWForm form = new WWWForm();
@@ -579,38 +688,34 @@ private IEnumerator PublishNow()
if (www.result != UnityWebRequest.Result.Success)
{
- Debug.LogError("Error publishing issue: " + www.error);
+ throw new InvalidOperationException($"Error publishing issue (HTTP {www.responseCode}): {www.error}");
}
- else
+ // Success logging handled by calling method
+ if (_projectId == BugReportUI.DEMO_PROJECT_ID)
{
- Debug.Log("Issue published successfully!");
-
- if (_projectId == BugReportUI.DEMO_PROJECT_ID)
- {
- Debug.Log("Demo project published issue: " + Url);
- }
+ _diagnostics?.LogInfo("PublishNow", "Demo project published issue: " + Url);
}
}
}
-
-
- private class ErrorMessage
+
+
+ private class ErrorMessage
{
public string error;
public string status;
}
- private class IssueResponse
+ private class IssueResponse
{
public string id;
public string token;
public string url;
}
- private struct DraftResult {
+ private struct DraftResult
+ {
public string IssueId;
public string UpdateIssueAuthToken;
- public string Error;
public string Url;
}
}