Skip to content

[codex] Add authenticated cloud file uploads#698

Merged
fernandotonon merged 5 commits into
masterfrom
codex/cloud-authenticated-uploads
May 30, 2026
Merged

[codex] Add authenticated cloud file uploads#698
fernandotonon merged 5 commits into
masterfrom
codex/cloud-authenticated-uploads

Conversation

@fernandotonon
Copy link
Copy Markdown
Owner

@fernandotonon fernandotonon commented May 29, 2026

Summary

Adds a first authenticated QtMesh Cloud upload flow from the editor.

  • Adds Cloud > Upload Files..., prompting sign-in when needed and using the stored QtMesh Cloud session token.
  • Creates a private cloud project, requests signed upload URLs, uploads selected project files on a worker thread, completes the upload, and offers to open the project page.
  • Adds CloudUploadPlanner for project slug generation, asset role inference, MIME lookup, and upload descriptor creation.
  • Covers planner behavior with unit tests.

Links #684.

Validation

  • cmake --build build_local --target QtMeshEditor -j2
  • cmake --build build_local --target UnitTests -j2
  • ./build_local/bin/UnitTests --gtest_filter=CloudUploadPlanner.*

Notes

The worktree still has an unrelated existing src/dependencies/ogre-procedural submodule marker that is not included in this PR.

Summary by CodeRabbit

  • New Features

    • Cloud file upload from the Cloud menu via "Upload Files…" with multi-file selection, project creation/selection, progress dialog, cancellation, and optional opening of the created project.
    • Project listing from the cloud to pick existing projects; automatic slug generation and retry on name conflicts.
  • Tests

    • Added coverage for slug generation, asset role inference, descriptor building, project fetching, and cancellable uploads.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

Warning

Review limit reached

@fernandotonon, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 14 minutes and 51 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9ad6c269-91f2-413e-b6c1-fc3e7a8143a6

📥 Commits

Reviewing files that changed from the base of the PR and between 0543ced and 73c6d18.

📒 Files selected for processing (4)
  • src/CMakeLists.txt
  • src/QtMeshCloudClient.cpp
  • src/mainwindow.cpp
  • tests/CMakeLists.txt
📝 Walkthrough

Walkthrough

This PR implements a complete QtMesh Cloud file upload flow: adds CloudUploadPlanner utilities (slug generation, role inference, descriptor building), tests, build updates, MainWindow menu/handler for multi-file upload with project selection/creation, and QtMeshCloudClient support for project listing and cancellable uploads.

Changes

Cloud File Upload Feature

Layer / File(s) Summary
CloudUploadPlanner Utilities Library
src/CloudUploadPlanner.h, src/CloudUploadPlanner.cpp
New public namespace providing three utilities: makeProjectSlug() normalizes input names into lowercase hyphen-separated slugs with fallback, 64-char limit, and trailing-dash trimming; inferAssetRole() maps file extensions to asset role strings (model, skeleton, animation, material, texture, metadata, sidecar, or default); buildAssetFileDescriptors() constructs a list of QtMeshCloudClient::AssetFileDescriptor objects with path, upload name, role, MIME type, and file size.
CloudUploadPlanner Test Coverage
src/CloudUploadPlanner_test.cpp
GoogleTest suite with cases covering slug normalization with fallback, 64-character truncation without trailing dash (including collision/retry path), filename-to-role mapping (including metadata.json), and descriptor construction validating path, uploadName, role, sizeBytes, and non-empty mimeType.
Build Configuration
src/CMakeLists.txt, tests/CMakeLists.txt
Adds CloudUploadPlanner.cpp to SRC_FILES and TEST_SRC_FILES, and CloudUploadPlanner.h to HEADER_FILES so the new source/header build into main and test targets.
MainWindow Cloud Upload Declarations
src/mainwindow.h
Declares private slot uploadFilesToQtMeshCloud() and adds QAction* m_cloudUploadFilesAction member for the new cloud upload menu action.
MainWindow Cloud Upload Handler
src/mainwindow.cpp
Implements complete upload workflow: checks cloud authentication status and optionally prompts sign-in, opens multi-file picker for supported assets, prompts or lets user pick a project (with CloudUploadPlanner slug and 409-retry suffix), requests upload URLs, runs multi-threaded uploads with an atomic cancellation flag and QProgressDialog progress, calls completeUpload, and shows completion/cancel/error UI including optional "Open Project" link. Adds related includes and an internal CloudUploadWorkerOutcome struct.
QtMeshCloudClient API and Cancellation
src/QtMeshCloudClient.h, src/QtMeshCloudClient.cpp
Adds ProjectSummary/ProjectsListResult and fetchProjects(bearerToken, timeoutMs), extends FileUploadResult with canceled flag, and updates uploadFileContent to accept const std::atomic_bool* canceled with early-cancel and mid-upload abort via a QTimer that calls reply->abort().
QtMeshCloudClient Test Adjustments
src/QtMeshCloudClient_test.cpp
Adds a test asserting fetchProjects fails with a missing bearer token and updates uploadFileContent test calls to insert a nullptr cancellation argument matching the new signature.

Sequence Diagram

sequenceDiagram
    participant User
    participant MainWindow
    participant CloudAuth
    participant FileDialog
    participant CloudUploadPlanner
    participant QtMeshCloudClient
    participant UploadWorker
    participant ProgressDialog
    User->>MainWindow: Click "Upload Files…"
    MainWindow->>CloudAuth: Check sign-in status
    MainWindow->>FileDialog: Show file picker
    FileDialog-->>MainWindow: Return selected paths
    MainWindow->>MainWindow: Prompt for project name / choose existing
    MainWindow->>CloudUploadPlanner: Generate slug from name
    CloudUploadPlanner-->>MainWindow: Normalized project slug
    MainWindow->>QtMeshCloudClient: fetchProjects / create project (handle 409)
    QtMeshCloudClient-->>MainWindow: Project ID, upload URLs
    MainWindow->>CloudUploadPlanner: Build asset descriptors
    CloudUploadPlanner-->>MainWindow: Descriptors (role, mime, size)
    MainWindow->>UploadWorker: Start upload on worker thread (pass atomic cancel)
    UploadWorker->>QtMeshCloudClient: uploadFileContent (cancellable)
    QtMeshCloudClient-->>UploadWorker: Upload result (or canceled)
    UploadWorker->>ProgressDialog: Update progress
    UploadWorker-->>MainWindow: Return outcome (ok/error/canceled)
    MainWindow->>User: Show completion summary / open project URL
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I scrub names into tidy slugs tonight,
Map textures, models, and metadata right,
Threads hum softly as bytes take flight,
Progress bars blink in the dusky light,
QtMesh gardens grow by morning's bright delight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding authenticated cloud file uploads functionality to the editor.
Description check ✅ Passed The PR description provides a clear summary and technical details, including validation steps, but deviates from the template structure with custom sections.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/cloud-authenticated-uploads

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@fernandotonon fernandotonon marked this pull request as ready for review May 29, 2026 13:30
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c930ab9ab5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/mainwindow.cpp Outdated
Comment on lines +2504 to +2505
slug = QStringLiteral("%1-%2")
.arg(slug, QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMddhhmmss")));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve slug length when retrying conflicts

When the initial generated slug is already at CloudUploadPlanner's 64-character limit, appending -yyyyMMddhhmmss on a 409 produces a 79-character slug. In the duplicate-long-project-name case the retry no longer satisfies the planner's own slug-length constraint and is likely to be rejected by the API instead of resolving the conflict; truncate the base slug before adding the timestamp suffix.

Useful? React with 👍 / 👎.

Comment thread src/mainwindow.cpp Outdated
Comment on lines +2566 to +2567
if (mainFileId.isEmpty())
mainFileId = uploads.at(i).fileId;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Choose the model file as the upload main file

For multi-file uploads this marks whichever file happens to be returned first by the file dialog as mainFileId. If the user selects a texture/material/sidecar before the mesh, or the dialog returns an alphabetically earlier texture, completeUpload is told that the non-model file is the main asset even though descriptors include roles; that can make the cloud project scan/viewer attach to the wrong file. Prefer the first descriptor with role model and fall back only when no model is present.

Useful? React with 👍 / 👎.

mainwindow.cpp references CloudUploadPlanner; include the source in
TEST_SRC_FILES so MaterialEditorQML_* executables link successfully.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/mainwindow.cpp`:
- Around line 2533-2599: The worker currently only checks canceled between
network calls so canceling during an in-flight upload doesn't abort the active
QNetworkReply; update the upload path to be abortable by passing a cancellation
hook into QtMeshCloudClient::uploadFileContent (and/or completeUpload) so the
client can abort the active QNetworkReply immediately. Concretely, change
QtMeshCloudClient::uploadFileContent signature to accept a cancellation callback
or pointer to an atomic_bool (e.g. std::function<bool()> isCanceled or const
std::atomic_bool* canceled) and inside uploadFileContent store the
QNetworkReply*, connect the cancellation hook to call reply->abort(), and
poll/observe the hook while the reply is in-flight; in the worker lambda pass
&canceled (or a small lambda capturing &canceled) into uploadFileContent so
canceled.store(true) triggers immediate abort, and keep setting outcome.canceled
when abort is observed so the modal loop can quit promptly.
- Around line 2501-2506: The retry path for createProject uses the original slug
and simply appends a timestamp suffix, which can exceed the 64-char limit;
update the retry to re-normalize/truncate the slug before appending the "-%2"
timestamp so the final slug stays within 64 chars. Locate
CloudUploadPlanner::makeProjectSlug usage and the slug variable, then compute
the collision-safe base by truncating the original slug to (64 - 1 -
timestampLength) characters (accounting for the dash) or by invoking a helper
that enforces the 64-char limit, then append QStringLiteral("-%2") with
QDateTime::currentDateTimeUtc().toString(...) and call
QtMeshCloudClient::createProject(token, projectName, slug) with that normalized
retry slug. Ensure the final slug length is validated before retrying.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c04cc6f-615a-47d8-8f0c-b4c68995a35e

📥 Commits

Reviewing files that changed from the base of the PR and between 043fe1f and c930ab9.

📒 Files selected for processing (6)
  • src/CMakeLists.txt
  • src/CloudUploadPlanner.cpp
  • src/CloudUploadPlanner.h
  • src/CloudUploadPlanner_test.cpp
  • src/mainwindow.cpp
  • src/mainwindow.h

Comment thread src/mainwindow.cpp Outdated
Comment thread src/mainwindow.cpp
Let uploads target existing cloud projects via GET /v1/projects, abort
in-flight PUTs when the user cancels, re-normalize slug collision retries,
and prefer model files as the upload main file.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/QtMeshCloudClient_test.cpp (1)

183-217: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Add tests for the new behavior, not just the argument guards.

This change adds project-list parsing and cooperative upload cancellation, but the new assertions only cover fast-fail input validation. Please add coverage for at least one parsed fetchProjects() response and one canceled uploadFileContent() path.

As per coding guidelines "**/*_test.cpp: Add Google Test unit tests for new functionality."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/QtMeshCloudClient_test.cpp` around lines 183 - 217, Your tests only cover
argument-guard fast-fail paths; add unit tests that exercise the new parsing and
cancellation behavior: add one test for QtMeshCloudClient::fetchProjects that
injects a mocked successful HTTP response (JSON with at least one project entry)
and assert result.ok is true and parsed project fields match the JSON, and add
one test for QtMeshCloudClient::uploadFileContent that simulates a cooperative
cancellation (construct an UploadTarget configured to cancel or trigger the
cancel path used by UploadTarget) and assert the call returns ok==false with
errorString indicating cancellation; follow the existing test patterns in the
file for injecting network responses/timeouts and reference
QtMeshCloudClient::fetchProjects, QtMeshCloudClient::uploadFileContent and
QtMeshCloudClient::UploadTarget to locate code to exercise.
src/mainwindow.cpp (1)

2627-2680: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Propagate cancellation into completeUpload() as well.

canceled is only checked before QtMeshCloudClient::completeUpload(...). If the user hits Cancel during the finalization request, the modal loop still waits for that network call to finish, so the workflow can hang on a slow/stalled /files/complete even though PUT uploads are now abortable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mainwindow.cpp` around lines 2627 - 2680, The finalization call doesn't
propagate the cancellation flag into QtMeshCloudClient::completeUpload, so a
user cancel during that network request will still block; update the worker
lambda to call a cancellation-aware overload of completeUpload (or modify
QtMeshCloudClient::completeUpload) to accept the same &canceled token, pass
&canceled into the call, and after the call check for result.canceled and set
outcome.canceled (and set outcome.errorString on error) just like you do for
uploadFileContent so cancellation during completeUpload is handled and reported.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/QtMeshCloudClient.cpp`:
- Around line 594-615: The code currently treats
root.value("projects").toArray() as [] when the field is missing or not an array
and still returns out.ok = true; change the parsing logic in the function that
calls parseJsonObjectBody (the loop that fills out.projects and sets out.ok) to
explicitly verify that root.contains("projects") and that
root.value(QStringLiteral("projects")).isArray(); if the check fails set a
descriptive out.errorString (e.g. "missing or invalid 'projects' field") and
return out without setting out.ok = true; keep the existing ProjectSummary
extraction loop for the valid-case array and only set out.ok = true after
successful validation.

---

Outside diff comments:
In `@src/mainwindow.cpp`:
- Around line 2627-2680: The finalization call doesn't propagate the
cancellation flag into QtMeshCloudClient::completeUpload, so a user cancel
during that network request will still block; update the worker lambda to call a
cancellation-aware overload of completeUpload (or modify
QtMeshCloudClient::completeUpload) to accept the same &canceled token, pass
&canceled into the call, and after the call check for result.canceled and set
outcome.canceled (and set outcome.errorString on error) just like you do for
uploadFileContent so cancellation during completeUpload is handled and reported.

In `@src/QtMeshCloudClient_test.cpp`:
- Around line 183-217: Your tests only cover argument-guard fast-fail paths; add
unit tests that exercise the new parsing and cancellation behavior: add one test
for QtMeshCloudClient::fetchProjects that injects a mocked successful HTTP
response (JSON with at least one project entry) and assert result.ok is true and
parsed project fields match the JSON, and add one test for
QtMeshCloudClient::uploadFileContent that simulates a cooperative cancellation
(construct an UploadTarget configured to cancel or trigger the cancel path used
by UploadTarget) and assert the call returns ok==false with errorString
indicating cancellation; follow the existing test patterns in the file for
injecting network responses/timeouts and reference
QtMeshCloudClient::fetchProjects, QtMeshCloudClient::uploadFileContent and
QtMeshCloudClient::UploadTarget to locate code to exercise.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0d6c7357-a283-415f-a140-0994b6133dfd

📥 Commits

Reviewing files that changed from the base of the PR and between c930ab9 and 0543ced.

📒 Files selected for processing (6)
  • src/CloudUploadPlanner_test.cpp
  • src/QtMeshCloudClient.cpp
  • src/QtMeshCloudClient.h
  • src/QtMeshCloudClient_test.cpp
  • src/mainwindow.cpp
  • tests/CMakeLists.txt
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/CloudUploadPlanner_test.cpp

Comment thread src/QtMeshCloudClient.cpp
fernandotonon and others added 2 commits May 29, 2026 20:58
Treat a missing or non-array projects field as an error instead of
silently presenting an empty project list in the upload picker.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sonarqubecloud
Copy link
Copy Markdown

@fernandotonon fernandotonon merged commit 1777b89 into master May 30, 2026
20 checks passed
@fernandotonon fernandotonon deleted the codex/cloud-authenticated-uploads branch May 30, 2026 02:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant