46 const auto parsedUri =
ParseUri(url);
49 const auto amzDateIt = parsedQuery.find(
"X-Amz-Date");
51 if (amzDateIt == parsedQuery.end())
59 const auto amzExpiresIt = parsedQuery.find(
"X-Amz-Expires");
61 if (amzExpiresIt == parsedQuery.end())
64 int64_t expiresSeconds;
67 amzExpiresIt->second.data(),
68 amzExpiresIt->second.data() + amzExpiresIt->second.size(),
71 if (expiresParseResult.ec != std::errc {})
74 return (time + std::chrono::seconds { expiresSeconds }) <
75 std::chrono::system_clock::now();
80 public std::enable_shared_from_this<ResumedSnaphotUploadOperation>
89 std::string_view snapshotId, std::string_view confirmationUrl)
90 : mProjectCloudExtension { projectCloudExtension }
91 , mProjectId { mProjectCloudExtension.GetCloudProjectId() }
92 , mSnapshotId { snapshotId }
93 , mConfirmationUrl { confirmationUrl }
94 , mCancellationContext { concurrency::CancellationContext::Create() }
104 std::string_view confirmationUrl)
108 auto operation = std::make_shared<ResumedSnaphotUploadOperation>(
109 Tag {}, projectCloudExtension, snapshotId, confirmationUrl);
113 operation->mPendingProjectBlobData =
114 cloudProjectsDatabase.GetPendingProjectBlob(projectId, snapshotId);
116 operation->mPendingProjectBlocks =
117 cloudProjectsDatabase.GetPendingProjectBlocks(projectId, snapshotId);
119 const int64_t totalBlocks = operation->mPendingProjectBlocks.size();
121 if (operation->mPendingProjectBlobData)
123 operation->mHasExpiredUrls =
124 IsUrlExpired(operation->mPendingProjectBlobData->UploadUrl);
127 for (
const auto& block : operation->mPendingProjectBlocks)
129 if (operation->mHasExpiredUrls)
132 operation->mHasExpiredUrls =
IsUrlExpired(block.UploadUrl);
136 operation, totalBlocks,
137 operation->mPendingProjectBlobData.has_value());
145 mPendingProjectBlobData->ConfirmUrl,
146 mPendingProjectBlobData->FailUrl };
150 mPendingProjectBlobData->BlobData,
151 [
this, weakThis = weak_from_this()](
auto result)
153 auto strongThis = weakThis.lock();
159 mProjectId, mSnapshotId);
163 mProjectCloudExtension.OnProjectDataUploaded(*this);
167 FailSync(std::move(result));
173 if (!mCompleted.exchange(
true))
174 mProjectCloudExtension.OnSyncCompleted(
this, {});
179 if (!mCompleted.exchange(
true))
180 mProjectCloudExtension.OnSyncCompleted(
this, error);
190 if (mPendingProjectBlocks.empty())
191 MarkSnapshotSynced();
193 auto project = mProjectCloudExtension.GetProject().lock();
204 std::vector<BlockUploadTask> blockTasks;
205 blockTasks.reserve(mPendingProjectBlocks.size());
207 for (
const auto& pendingBlock : mPendingProjectBlocks)
213 pendingBlock.ConfirmUrl,
214 pendingBlock.FailUrl };
217 static_cast<sampleFormat>(pendingBlock.BlockSampleFormat);
218 task.
Block.
Hash = pendingBlock.BlockHash;
219 task.
Block.
Id = pendingBlock.BlockId;
225 blockTasks.push_back(std::move(task));
231 mProjectId, mSnapshotId);
235 XO(
"Local project data was removed before the sync has completed")
243 [
this, weakThis = weak_from_this()](
247 auto strongThis = weakThis.lock();
255 mProjectId, block.
Id);
257 mProjectCloudExtension.OnBlockUploaded(
261 const auto completed =
265 const bool succeeded = completed && progress.
FailedBlocks == 0;
271 MarkSnapshotSynced();
273 FailSync(std::move(blockResponseResult));
281 else if (mPendingProjectBlobData.has_value())
289 using namespace network_manager;
291 mProjectId, mSnapshotId) };
297 response->setRequestFinishedCallback(
298 [
this, response, weakThis = weak_from_this()](
auto)
300 auto strongThis = weakThis.lock();
304 if (response->getError() != NetworkError::NoError)
306 FailSync(DeduceUploadError(*response));
320 UpdateUrls(*syncState);
322 if (mPendingProjectBlobData.has_value())
331 if (mPendingProjectBlobData.has_value())
335 mPendingProjectBlobData = {};
337 mProjectId, mSnapshotId);
347 std::unordered_map<std::string, UploadUrls> urlsLookup;
349 urlsLookup.emplace(urls.Id, urls);
351 for (
auto& block : mPendingProjectBlocks)
353 auto it = urlsLookup.find(block.BlockHash);
355 if (it == urlsLookup.end())
358 mProjectId, block.BlockId);
363 block.UploadUrl = urlsLookup[block.BlockHash].UploadUrl;
364 block.ConfirmUrl = urlsLookup[block.BlockHash].SuccessUrl;
365 block.FailUrl = urlsLookup[block.BlockHash].FailUrl;
368 mPendingProjectBlocks.erase(
370 mPendingProjectBlocks.begin(), mPendingProjectBlocks.end(),
371 [&urlsLookup](
auto& block)
372 { return urlsLookup.find(block.BlockHash) == urlsLookup.end(); }),
373 mPendingProjectBlocks.end());
378 using namespace network_manager;
379 Request request { mConfirmationUrl };
385 response->setRequestFinishedCallback(
386 [
this, response, weakThis = weak_from_this()](
auto)
388 auto strongThis = weakThis.lock();
393 mProjectId, mSnapshotId);
395 if (response->getError() != NetworkError::NoError)
404 mCancellationContext->OnCancelled(response);
414 return mCompleted.load();
419 mCancellationContext->Cancel();
436 std::atomic<bool> mCompleted {
false };
438 bool mHasExpiredUrls {
false };
445 std::function<
void()> onBeforeUploadStarts)
449 auto pendingSnapshots = cloudProjectsDatabase.GetPendingSnapshots(
452 if (!pendingSnapshots.empty() && onBeforeUploadStarts)
453 onBeforeUploadStarts();
455 for (
const auto& snapshot : pendingSnapshots)
456 ResumedSnaphotUploadOperation::Perform(
457 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.
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()> 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