Skip to content

[codex] Add QtMesh Cloud asset upload client - #695

Merged
fernandotonon merged 4 commits into
masterfrom
feat/scan-target-363
May 28, 2026
Merged

[codex] Add QtMesh Cloud asset upload client#695
fernandotonon merged 4 commits into
masterfrom
feat/scan-target-363

Conversation

@fernandotonon

@fernandotonon fernandotonon commented May 28, 2026

Copy link
Copy Markdown
Owner

Summary

Adds the first editor-side client surface for QtMesh Cloud asset uploads, matching the qtmesh.dev upload API implemented for cloud asset retention.

Changes

  • Extends QtMeshCloudClient with bearer-token methods to:
    • create a private cloud project
    • request upload URLs
    • upload local file content
    • complete an upload
    • fetch a project manifest for future listing/open-from-cloud flows
  • Keeps the existing static cloud scan APIs (fetchRules, uploadScanReport) intact for CLI scan compatibility.
  • Adds cloud upload/project Sentry breadcrumbs without logging tokens or upload URLs.
  • Adds validation-path tests for missing credentials and incomplete upload requests.

Notes

This is the client/API foundation only. The editor still needs follow-up slices for browser/device login, token persistence, packaging/dependency discovery, and GUI upload flow from issue #684.

Validation

  • cmake --build build_local --target UnitTests -j2
  • QT_QPA_PLATFORM=offscreen ./build_local/bin/UnitTests --gtest_filter='QtMeshCloudClient*'

Focused test result: 21 QtMeshCloudClient tests passed.

Summary by CodeRabbit

  • New Features

    • Added --target option to scan CLI as a CI-friendly alternative to --profile
    • Scan reports now display selected profile in JSON, SARIF, and text outputs
    • Added cloud API support for project creation, file uploads, upload completion, and manifest retrieval
  • Bug Fixes

    • Validation prevents conflicting --target and --profile values
  • Tests

    • Added cloud API error handling tests for missing or invalid inputs
    • Extended scan tests to verify profile inclusion in report outputs

Review Change Stack

fernandotonon and others added 3 commits May 27, 2026 23:44
Add `qtmesh scan --target <id>` as a CI-friendly alias for selecting a bundled
platform profile. Active profile is emitted into JSON, SARIF, and text reports.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

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 46 minutes and 30 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: e0fdba06-feff-49a6-bd88-1cd9cf4a8a2c

📥 Commits

Reviewing files that changed from the base of the PR and between 7a4004a and 4dd6f06.

📒 Files selected for processing (1)
  • src/QtMeshCloudClient.cpp
📝 Walkthrough

Walkthrough

The PR adds CLI profile tracking and cloud project/upload API methods. The --target CLI option (--profile alias) is parsed, validated, and tracked through scan execution to appear in JSON/SARIF/text reports. Five new QtMeshCloudClient methods enable creating projects, requesting upload URLs, uploading files, completing uploads, and fetching manifests with consistent validation and error handling.

Changes

CLI Profile Tracking

Layer / File(s) Summary
CLI argument parsing for --target option
src/CLIPipeline.cpp
Adds --target <id> CLI option, parses and stores it separately, validates that conflicting --target and --profile values are rejected with exit code 2, and updates help text.
Profile tracking through scan execution
src/CLIPipeline.cpp
Tracks the actually-loaded platform profile ID in activeProfileId during scan execution and includes it in JSON output as "profile" field and in text output as Profile: <id> header.
Report formatting with profile context
src/ScanEngine.h, src/ScanEngine.cpp, src/CLIPipeline.cpp
Updates formatText and formatSarif to accept optional activeProfileId parameter; CLI report writers pass activeProfileId for both CLI-specified report paths (--report, --sarif) and config-driven outputs.
CLI profile tracking tests
src/CLIPipeline_test.cpp
Validates end-to-end --target functionality, verifies "profile" field in JSON and SARIF outputs, and asserts unknown --target values exit with code 2.

Cloud Project Upload API

Layer / File(s) Summary
Cloud client API contracts and result types
src/QtMeshCloudClient.h
Declares result structs (ProjectResult, UploadUrlsResult, FileUploadResult, CompleteUploadResult, ManifestResult) and descriptors (AssetFileDescriptor, UploadTarget); adds static method signatures for project creation and multi-step upload workflow.
Cloud client implementation and error handling
src/QtMeshCloudClient.cpp, src/QtMeshCloudClient_test.cpp
Implements createProject, requestUploadUrls, uploadFileContent, completeUpload, and fetchProjectManifest with consistent validation (bearer token, required fields), synchronous Qt HTTP requests with authentication, JSON response parsing, Sentry breadcrumbs, and error result structs; comprehensive error path tests validate immediate failure and specific error messages for missing/invalid inputs.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • fernandotonon/QtMeshEditor#684: The PR's new QtMeshCloudClient methods (createProject, requestUploadUrls, uploadFileContent, completeUpload, fetchProjectManifest) directly implement the cloud upload API surface described in this epic.

Possibly related PRs

  • fernandotonon/QtMeshEditor#682: Both modify platform profile handling in src/CLIPipeline.cpp (profile ID parsing, selection, and usage during scan/report generation).
  • fernandotonon/QtMeshEditor#276: Both extend cmdScan CLI test coverage in src/CLIPipeline_test.cpp with new scan validation and report-path assertions.

Poem

🐰 A rabbit hops through clouds on high,
Tracking profiles as scans run by.
Projects created, files take flight,
Reports now show which profile's right!
Upload complete, the manifest glows—
Much work done, the rabbit knows. 🌤️

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description provides a clear summary, technical details of changes, and validation steps. However, it does not follow the repository's required template structure with sections like 'Technical Details' bullets, features, bugfixes, or PS1 runtime rip checklist. Restructure the description to match the template: add 'Technical Details' section with bullet points, clearly label any new features/bugfixes, and include PS1 runtime checklist if applicable.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the primary change: adding QtMesh Cloud asset upload client functionality, which aligns with the main modifications across QtMeshCloudClient header/implementation and related test files.
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 feat/scan-target-363

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 28, 2026 21:24

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

Copy link
Copy Markdown

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: 7a4004acb3

ℹ️ 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/QtMeshCloudClient.cpp Outdated
out.errorString = QStringLiteral("could not open file: %1").arg(pathLeaf(localPath));
return out;
}
const QByteArray payload = file.readAll();

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 Stream uploads instead of buffering whole assets

When uploading large meshes or texture archives, file.readAll() loads the entire asset into a QByteArray before QNetworkAccessManager::put, so the editor/CLI must hold a full extra copy of the file in memory and can freeze or run out of memory on multi-GB assets. Since this client is intended for asset uploads, keep the QFile open and pass it as the upload device instead of materializing the whole payload.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/QtMeshCloudClient.cpp (2)

487-518: ⚖️ Poor tradeoff

Avoid loading the entire asset into memory; stream from the QFile device.

file.readAll() materializes the whole file into a QByteArray (and nam.put(req, payload) holds another reference). With a 120s default timeout the API clearly anticipates large assets, so this can spike memory by the full file size per upload. Since the request is driven synchronously by a local QEventLoop, the QFile stays alive for the request duration and can be passed as the upload device, using file.size() for the pre-flight checks.

♻️ Stream the file instead of buffering it
     QFile file(localPath);
     if (!file.open(QIODevice::ReadOnly)) {
         out.errorString = QStringLiteral("could not open file: %1").arg(pathLeaf(localPath));
         return out;
     }
-    const QByteArray payload = file.readAll();
-    file.close();
-    if (target.sizeBytes > 0 && payload.size() != target.sizeBytes) {
+    const qint64 fileSize = file.size();
+    if (target.sizeBytes > 0 && fileSize != target.sizeBytes) {
         out.errorString = QStringLiteral("file size changed before upload: %1").arg(pathLeaf(localPath));
         return out;
     }
@@
     QNetworkRequest req(url);
     req.setHeader(QNetworkRequest::UserAgentHeader, QStringLiteral("qtmesheditor"));
-    req.setHeader(QNetworkRequest::ContentLengthHeader, payload.size());
+    req.setHeader(QNetworkRequest::ContentLengthHeader, fileSize);

Then upload via the device (reply = nam.put(req, &file);) and default out.sizeBytes to fileSize.

🤖 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.cpp` around lines 487 - 518, The code currently reads
the whole file into memory via file.readAll() and calls nam.put(req, payload);
instead stream the upload from the QFile device: open the QFile (use file.size()
for the size check and for ContentLengthHeader), do not call file.readAll(), and
call nam.put(req, &file) so the network layer streams from the file; ensure the
QFile remains alive for the lifetime of the QNetworkReply (e.g. allocate QFile
on the heap or otherwise keep it in scope and close/delete it only after reply
finishes) and update references to remove payload and out.sizeBytes to reflect
file.size().

519-521: ⚖️ Poor tradeoff

Confirm these synchronous uploads never run on the GUI thread.

loop.exec() blocks the calling thread until the transfer completes (up to the 120s timeoutMs for uploadFileContent). If any of these methods are invoked from the GUI thread in the upcoming upload flow (issue #684), the UI will freeze for the duration. Consider documenting/enforcing that callers run these on a worker thread.

🤖 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.cpp` around lines 519 - 521, The synchronous wait using
QEventLoop::exec() (the three lines with QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();) can block the GUI thread for up to timeoutMs in uploadFileContent;
enforce that uploadFileContent (and any callers) never run on the GUI thread by
adding a runtime check at the start of uploadFileContent (compare
QThread::currentThread() to qApp->thread()) and either assert/return an error or
automatically offload the work to a worker thread (e.g., QtConcurrent::run or
QMetaObject::invokeMethod on a worker object with Qt::QueuedConnection) so the
blocking loop.exec() runs only on a non-GUI thread; alternatively convert the
function to an asynchronous API using the QNetworkReply finished signal and a
callback/future instead of blocking.
🤖 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.

Nitpick comments:
In `@src/QtMeshCloudClient.cpp`:
- Around line 487-518: The code currently reads the whole file into memory via
file.readAll() and calls nam.put(req, payload); instead stream the upload from
the QFile device: open the QFile (use file.size() for the size check and for
ContentLengthHeader), do not call file.readAll(), and call nam.put(req, &file)
so the network layer streams from the file; ensure the QFile remains alive for
the lifetime of the QNetworkReply (e.g. allocate QFile on the heap or otherwise
keep it in scope and close/delete it only after reply finishes) and update
references to remove payload and out.sizeBytes to reflect file.size().
- Around line 519-521: The synchronous wait using QEventLoop::exec() (the three
lines with QEventLoop loop; QObject::connect(reply, &QNetworkReply::finished,
&loop, &QEventLoop::quit); loop.exec();) can block the GUI thread for up to
timeoutMs in uploadFileContent; enforce that uploadFileContent (and any callers)
never run on the GUI thread by adding a runtime check at the start of
uploadFileContent (compare QThread::currentThread() to qApp->thread()) and
either assert/return an error or automatically offload the work to a worker
thread (e.g., QtConcurrent::run or QMetaObject::invokeMethod on a worker object
with Qt::QueuedConnection) so the blocking loop.exec() runs only on a non-GUI
thread; alternatively convert the function to an asynchronous API using the
QNetworkReply finished signal and a callback/future instead of blocking.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 56eb3b98-62ab-4f6f-bdcb-8978554750f4

📥 Commits

Reviewing files that changed from the base of the PR and between 5d537b5 and 7a4004a.

📒 Files selected for processing (7)
  • src/CLIPipeline.cpp
  • src/CLIPipeline_test.cpp
  • src/QtMeshCloudClient.cpp
  • src/QtMeshCloudClient.h
  • src/QtMeshCloudClient_test.cpp
  • src/ScanEngine.cpp
  • src/ScanEngine.h

@fernandotonon
fernandotonon merged commit 2d0fad8 into master May 28, 2026
13 checks passed
@fernandotonon
fernandotonon deleted the feat/scan-target-363 branch May 28, 2026 21:42
@sonarqubecloud

Copy link
Copy Markdown

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