47 const auto parsedUri =
ParseUri(url);
50 const auto amzDateIt = parsedQuery.find(
"X-Amz-Date");
52 if (amzDateIt == parsedQuery.end())
60 const auto amzExpiresIt = parsedQuery.find(
"X-Amz-Expires");
62 if (amzExpiresIt == parsedQuery.end())
65 int64_t expiresSeconds;
68 amzExpiresIt->second.data(),
69 amzExpiresIt->second.data() + amzExpiresIt->second.size(),
72 if (expiresParseResult.ec != std::errc {})
75 return (time + std::chrono::seconds { expiresSeconds }) <
76 std::chrono::system_clock::now();
81 public std::enable_shared_from_this<ResumedSnaphotUploadOperation>
90 std::string_view snapshotId, std::string_view confirmationUrl)
91 : mProjectCloudExtension { projectCloudExtension }
92 , mProjectId { mProjectCloudExtension.GetCloudProjectId() }
93 , mSnapshotId { snapshotId }
94 , mConfirmationUrl { confirmationUrl }
95 , mCancellationContext { concurrency::CancellationContext::Create() }
105 std::string_view confirmationUrl)
109 auto operation = std::make_shared<ResumedSnaphotUploadOperation>(
110 Tag {}, projectCloudExtension, snapshotId, confirmationUrl);
114 operation->mPendingProjectBlobData =
115 cloudProjectsDatabase.GetPendingProjectBlob(projectId, snapshotId);
117 operation->mPendingProjectBlocks =
118 cloudProjectsDatabase.GetPendingProjectBlocks(projectId, snapshotId);
120 const int64_t totalBlocks = operation->mPendingProjectBlocks.size();
122 if (operation->mPendingProjectBlobData)
124 operation->mHasExpiredUrls =
125 IsUrlExpired(operation->mPendingProjectBlobData->UploadUrl);
128 for (
const auto& block : operation->mPendingProjectBlocks)
130 if (operation->mHasExpiredUrls)
133 operation->mHasExpiredUrls =
IsUrlExpired(block.UploadUrl);
137 operation, totalBlocks,
138 operation->mPendingProjectBlobData.has_value());
146 mPendingProjectBlobData->ConfirmUrl,
147 mPendingProjectBlobData->FailUrl };
151 mPendingProjectBlobData->BlobData,
152 [
this, weakThis = weak_from_this()](
auto result)
154 auto strongThis = weakThis.lock();
160 mProjectId, mSnapshotId);
164 mProjectCloudExtension.OnProjectDataUploaded(*this);
168 FailSync(std::move(result));
174 if (!mCompleted.exchange(
true))
175 mProjectCloudExtension.OnSyncCompleted(
181 if (!mCompleted.exchange(
true))
182 mProjectCloudExtension.OnSyncCompleted(
193 if (mPendingProjectBlocks.empty())
194 MarkSnapshotSynced();
196 auto project = mProjectCloudExtension.GetProject().lock();
207 std::vector<BlockUploadTask> blockTasks;
208 blockTasks.reserve(mPendingProjectBlocks.size());
210 for (
const auto& pendingBlock : mPendingProjectBlocks)
216 pendingBlock.ConfirmUrl,
217 pendingBlock.FailUrl };
220 static_cast<sampleFormat>(pendingBlock.BlockSampleFormat);
221 task.
Block.
Hash = pendingBlock.BlockHash;
222 task.
Block.
Id = pendingBlock.BlockId;
228 blockTasks.push_back(std::move(task));
234 mProjectId, mSnapshotId);
238 XO(
"Local project data was removed before the sync has completed")
246 [
this, weakThis = weak_from_this()](
250 auto strongThis = weakThis.lock();
258 mProjectId, block.
Id);
260 mProjectCloudExtension.OnBlockUploaded(
264 const auto completed =
268 const bool succeeded = completed && progress.
FailedBlocks == 0;
274 MarkSnapshotSynced();
276 FailSync(std::move(blockResponseResult));
284 else if (mPendingProjectBlobData.has_value())
292 using namespace network_manager;
294 mProjectId, mSnapshotId) };
300 response->setRequestFinishedCallback(
301 [
this, response, weakThis = weak_from_this()](
auto)
303 auto strongThis = weakThis.lock();
307 if (response->getError() != NetworkError::NoError)
309 FailSync(DeduceUploadError(*response));
323 UpdateUrls(*syncState);
325 if (mPendingProjectBlobData.has_value())
334 if (mPendingProjectBlobData.has_value())
338 mPendingProjectBlobData = {};
340 mProjectId, mSnapshotId);
350 std::unordered_map<std::string, UploadUrls> urlsLookup;
352 urlsLookup.emplace(urls.Id, urls);
354 for (
auto& block : mPendingProjectBlocks)
356 auto it = urlsLookup.find(block.BlockHash);
358 if (it == urlsLookup.end())
361 mProjectId, block.BlockId);
366 block.UploadUrl = urlsLookup[block.BlockHash].UploadUrl;
367 block.ConfirmUrl = urlsLookup[block.BlockHash].SuccessUrl;
368 block.FailUrl = urlsLookup[block.BlockHash].FailUrl;
371 mPendingProjectBlocks.erase(
373 mPendingProjectBlocks.begin(), mPendingProjectBlocks.end(),
374 [&urlsLookup](
auto& block)
375 { return urlsLookup.find(block.BlockHash) == urlsLookup.end(); }),
376 mPendingProjectBlocks.end());
381 using namespace network_manager;
382 Request request { mConfirmationUrl };
388 response->setRequestFinishedCallback(
389 [
this, response, weakThis = weak_from_this()](
auto)
391 auto strongThis = weakThis.lock();
396 mProjectId, mSnapshotId);
398 if (response->getError() != NetworkError::NoError)
407 mCancellationContext->OnCancelled(response);
417 return mCompleted.load();
422 mCancellationContext->Cancel();
439 std::atomic<bool> mCompleted {
false };
441 bool mHasExpiredUrls {
false };
452 auto pendingSnapshots = cloudProjectsDatabase.GetPendingSnapshots(
455 if (!pendingSnapshots.empty() && onBeforeUploadStarts)
458 for (
const auto& snapshot : pendingSnapshots)
459 ResumedSnaphotUploadOperation::Perform(
460 projectCloudExtension, snapshot.SnapshotId, snapshot.ConfirmUrl);
Declare functions to perform UTF-8 to std::wstring conversions.
Declare functions to work with date and time string representations.
@ ProjectOpenedAndUploadResumed
FromCharsResult FromChars(const char *buffer, const char *last, float &value) noexcept
Parse a string into a single precision floating point value, always uses the dot as decimal.
Declare functions to convert numeric types to string representation.
Declare an interface for HTTP response.
Declare a class for performing HTTP requests.
Declare a class for constructing HTTP requests.
QueryFields ParseUriQuery(std::string_view query, std::string_view delimiter) noexcept
Parses URI query and returns QueryFields structure with parsed fields.
UriFields ParseUri(std::string_view uri) noexcept
Thrown for failure of file or database operations in deeply nested places.
static WaveTrackFactory & Get(AudacityProject &project)
std::string GetSnapshotSyncUrl(std::string_view projectId, std::string_view snapshotId) const
static CloudProjectsDatabase & Get()
void RemovePendingProjectBlock(std::string_view projectId, int64_t blockId)
void RemovePendingSnapshot(std::string_view projectId, std::string_view snapshotId)
void RemovePendingProjectBlob(std::string_view projectId, std::string_view snapshotId)
static DataUploader & Get()
void Upload(CancellationContextPtr cancellationContex, const ServiceConfig &config, const UploadUrls &target, std::vector< uint8_t > data, std::function< void(ResponseResult)> callback, std::function< void(double)> progressCallback={})
static std::shared_ptr< MissingBlocksUploader > Create(CancellationContextPtr cancellationContex, const ServiceConfig &serviceConfig, std::vector< BlockUploadTask > uploadTasks, MissingBlocksUploadProgressCallback progress)
void OnSyncResumed(std::shared_ptr< ProjectUploadOperation > uploadOperation, int64_t missingBlocksCount, bool needsProjectUpload)
This method is called from the UI thread.
std::string GetCloudProjectId() const
void SetUploadData(const ProjectUploadData &data) override
void MarkSnapshotSynced()
std::vector< PendingProjectBlockData > mPendingProjectBlocks
static void Perform(ProjectCloudExtension &projectCloudExtension, std::string_view snapshotId, std::string_view confirmationUrl)
std::optional< PendingProjectBlobData > mPendingProjectBlobData
void FailSync(ResponseResult result)
std::shared_ptr< MissingBlocksUploader > mMissingBlocksUploader
void FailSync(CloudSyncError error)
ResumedSnaphotUploadOperation(Tag, ProjectCloudExtension &projectCloudExtension, std::string_view snapshotId, std::string_view confirmationUrl)
void UpdateUrls(const ProjectSyncState &syncState)
~ResumedSnaphotUploadOperation() override
concurrency::CancellationContextPtr mCancellationContext
bool IsCompleted() const override
std::string mConfirmationUrl
ProjectCloudExtension & mProjectCloudExtension
static NetworkManager & GetInstance()
ResponsePtr doPost(const Request &request, const void *data, size_t size)
ResponsePtr doGet(const Request &request)
const auto sampleBlockFactory
bool IsUrlExpired(const std::string &url)
CloudSyncError::ErrorType DeduceError(SyncResultCode code)
std::optional< ProjectSyncState > DeserializeProjectSyncState(const std::string &data)
void ResumeProjectUpload(ProjectCloudExtension &projectCloudExtension, std::function< void(AudiocomTrace)> onBeforeUploadStarts)
CloudSyncError MakeClientFailure(const TranslatableString &message)
CLOUD_AUDIOCOM_API CloudSyncError DeduceUploadError(audacity::network_manager::IResponse &response)
bool IsUploadRecoverable(SyncResultCode code)
void SetCommonHeaders(Request &request)
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.
std::shared_ptr< CancellationContext > CancellationContextPtr
std::string ToUTF8(const std::wstring &wstr)
bool ParseISO8601Date(const std::string &dateString, SystemTime *time)
std::chrono::system_clock::time_point SystemTime
std::vector< UploadUrls > MissingBlocks