Audacity 3.2.0
CloudSyncDTO.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 CloudSyncDTO.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
11#include "CloudSyncDTO.h"
12
13#include <numeric>
14#include <set>
15#include <unordered_set>
16
17#include <rapidjson/document.h>
18#include <rapidjson/reader.h>
19#include <rapidjson/stringbuffer.h>
20#include <rapidjson/writer.h>
21
22#include <algorithm>
23
24#include "wxFileNameWrapper.h"
25
27{
28
29namespace
30{
31bool Deserialize(const rapidjson::Value& value, std::string& result)
32{
33 if (!value.IsString())
34 return false;
35
36 result = value.GetString();
37
38 return true;
39}
40
41bool Deserialize(const rapidjson::Value& value, int& result)
42{
43 if (!value.IsInt64())
44 return false;
45
46 result = value.GetInt();
47
48 return true;
49}
50
51bool Deserialize(const rapidjson::Value& value, long& result)
52{
53 if (!value.IsInt64())
54 return false;
55
56 if (sizeof(long) == sizeof(int64_t))
57 result = value.GetInt64();
58 else
59 result = value.GetInt();
60
61 return true;
62}
63
64bool Deserialize(const rapidjson::Value& value, long long& result)
65{
66 if (!value.IsInt64())
67 return false;
68
69 result = value.GetInt64();
70
71 return true;
72}
73
74bool Deserialize(const rapidjson::Value& value, bool& result)
75{
76 if (!value.IsBool())
77 return false;
78
79 result = value.GetBool();
80
81 return true;
82}
83
84template<typename T>
85bool Deserialize(
86 const rapidjson::Value& value, std::string_view zKey, T& result);
87
88template<typename T>
89bool DeserializeArray(const rapidjson::Value& value, std::vector<T>& result);
90
91template<typename T>
93 const rapidjson::Value& value, std::string_view zKey, std::vector<T>& result)
94{
95 if (!value.IsObject() || !value.HasMember(zKey.data()))
96 return false;
97
98 return DeserializeArray(value[zKey.data()], result);
99}
100
101bool Deserialize(const rapidjson::Value& value, UploadUrls& urls)
102{
103 if (!value.IsObject())
104 return {};
105
106 UploadUrls tempUrls;
107
108 if (!Deserialize(value, "id", tempUrls.Id))
109 return {};
110
111 if (!Deserialize(value, "url", tempUrls.UploadUrl))
112 return {};
113
114 if (!Deserialize(value, "success", tempUrls.SuccessUrl))
115 return {};
116
117 if (!Deserialize(value, "fail", tempUrls.FailUrl))
118 return {};
119
120 urls = std::move(tempUrls);
121
122 return true;
123}
124
125bool Deserialize(const rapidjson::Value& value, VersionInfo& urls)
126{
127 if (!value.IsObject())
128 return {};
129
130 VersionInfo tempVersion;
131
132 if (!Deserialize(value, "id", tempVersion.Id))
133 return {};
134
135 if (!Deserialize(value, "name", tempVersion.Name))
136 return {};
137
138 if (!Deserialize(value, "snapshot_id", tempVersion.SnapshotId))
139 return {};
140
141 if (!Deserialize(value, "date_created", tempVersion.Created))
142 return {};
143
144 if (!Deserialize(value, "date_updated", tempVersion.Updated))
145 return {};
146
147 urls = std::move(tempVersion);
148
149 return true;
150}
151
152bool Deserialize(const rapidjson::Value& value, SnapshotBlockInfo& urls)
153{
154 if (!value.IsObject())
155 return {};
156
157 SnapshotBlockInfo tempBlock;
158
159 if (!Deserialize(value, "hash", tempBlock.Hash))
160 return {};
161
162 if (!Deserialize(value, "url", tempBlock.Url))
163 return {};
164
165 urls = std::move(tempBlock);
166
167 return true;
168}
169
170bool Deserialize(const rapidjson::Value& value, SnapshotInfo& snapshotInfo)
171{
172 if (!value.IsObject())
173 return {};
174
175 SnapshotInfo tempSnapshot;
176
177 if (!Deserialize(value, "id", tempSnapshot.Id))
178 return {};
179
180 if (!Deserialize(value, "parent_id", tempSnapshot.ParentId))
181 return {};
182
183 if (!Deserialize(value, "date_created", tempSnapshot.Created))
184 return {};
185
186 if (!Deserialize(value, "date_updated", tempSnapshot.Updated))
187 return {};
188
189 if (!Deserialize(value, "date_synced", tempSnapshot.Synced))
190 return {};
191
192 if (!Deserialize(value, "file_size", tempSnapshot.FileSize))
193 return {};
194
195 if (!Deserialize(value, "blocks_size", tempSnapshot.BlocksSize))
196 return {};
197
198 Deserialize(value, "file_url", tempSnapshot.FileUrl);
199 DeserializeArray(value, "blocks", tempSnapshot.Blocks);
200
201 snapshotInfo = std::move(tempSnapshot);
202
203 return true;
204}
205
206bool Deserialize(const rapidjson::Value& value, ProjectInfo& projectInfo)
207{
208 if (!value.IsObject())
209 return {};
210
211 ProjectInfo tempProject;
212
213 if (!Deserialize(value, "id", tempProject.Id))
214 return {};
215
216 if (!Deserialize(value, "username", tempProject.Username))
217 return {};
218
219 if (!Deserialize(value, "author_name", tempProject.AuthorName))
220 return {};
221
222 if (!Deserialize(value, "slug", tempProject.Slug))
223 return {};
224
225 if (!Deserialize(value, "name", tempProject.Name))
226 return {};
227
228 if (!Deserialize(value, "details", tempProject.Details))
229 return {};
230
231 if (!Deserialize(value, "date_created", tempProject.Created))
232 return {};
233
234 if (!Deserialize(value, "date_updated", tempProject.Updated))
235 return {};
236
237 Deserialize(value, "head", tempProject.HeadSnapshot);
239 value, "latest_synced_snapshot_id", tempProject.LastSyncedSnapshotId);
240
241 projectInfo = std::move(tempProject);
242
243 return true;
244}
245
246bool Deserialize(const rapidjson::Value& value, ProjectSyncState& urls)
247{
248 if (!value.IsObject())
249 return {};
250
251 ProjectSyncState tempState;
252
253 if (!Deserialize(value, "mixdown", tempState.MixdownUrls))
254 return {};
255
256 if (!Deserialize(value, "file", tempState.FileUrls))
257 return {};
258
259 if (!DeserializeArray(value, "blocks", tempState.MissingBlocks))
260 return {};
261
262 urls = std::move(tempState);
263
264 return true;
265}
266
267bool Deserialize(const rapidjson::Value& value, CreateSnapshotResponse& reponse)
268{
269 if (!value.IsObject())
270 return {};
271
272 CreateSnapshotResponse tempResponse;
273
274 if (!Deserialize(value, "project", tempResponse.Project))
275 return {};
276
277 if (!Deserialize(value, "snapshot", tempResponse.Snapshot))
278 return {};
279
280 if (!Deserialize(value, "sync", tempResponse.SyncState))
281 return {};
282
283 reponse = std::move(tempResponse);
284
285 return true;
286}
287
288bool Deserialize(const rapidjson::Value& value, PaginationInfo& info)
289{
290 if (!value.IsObject())
291 return {};
292
293 PaginationInfo tempInfo;
294
295 if (!Deserialize(value, "total", tempInfo.TotalCount))
296 return {};
297
298 if (!Deserialize(value, "pages", tempInfo.PagesCount))
299 return {};
300
301 if (!Deserialize(value, "page", tempInfo.CurrentPage))
302 return {};
303
304 if (!Deserialize(value, "size", tempInfo.PageSize))
305 return {};
306
307 info = std::move(tempInfo);
308
309 return true;
310}
311
313 const rapidjson::Value& value, PaginatedProjectsResponse& response)
314{
315 if (!value.IsObject())
316 return {};
317
318 PaginatedProjectsResponse tempResponse;
319
320 if (!DeserializeArray(value, "items", tempResponse.Items))
321 return {};
322
323 if (!Deserialize(value, "pagination", tempResponse.Pagination))
324 return {};
325
326 response = std::move(tempResponse);
327
328 return true;
329}
330
331template<typename T>
333 const rapidjson::Value& value, std::string_view zKey, T& result)
334{
335 if (!value.IsObject() || !value.HasMember(zKey.data()))
336 return false;
337
338 return Deserialize(value[zKey.data()], result);
339}
340
341template<typename T>
342bool DeserializeArray(const rapidjson::Value& value, std::vector<T>& result)
343{
344 if (!value.IsArray())
345 return false;
346
347 result.clear();
348 result.reserve(value.Size());
349
350 for (const auto& item : value.GetArray())
351 {
352 T value;
353
354 if (!Deserialize(item, value))
355 return false;
356
357 result.push_back(std::move(value));
358 }
359
360 return true;
361}
362
363template<typename T>
364bool Deserialize(std::string_view data, T& result)
365{
366 rapidjson::Document document;
367 document.Parse(data.data(), data.size());
368
369 if (document.HasParseError())
370 return false;
371
372 return Deserialize(document, result);
373}
374
375} // namespace
376
377std::string Serialize(const ProjectForm& form)
378{
379 using namespace rapidjson;
380
381 Document document;
382 document.SetObject();
383
384 if (!form.Name.empty())
385 document.AddMember(
386 "name", StringRef(form.Name.c_str()), document.GetAllocator());
387
388 if (!form.HeadSnapshotId.empty())
389 document.AddMember(
390 "head_snapshot_id", StringRef(form.HeadSnapshotId.c_str()),
391 document.GetAllocator());
392
393 Value hashesArray(kArrayType);
394
395 for (const auto& hash : form.Hashes)
396 hashesArray.PushBack(StringRef(hash.c_str()), document.GetAllocator());
397
398 document.AddMember("blocks", hashesArray, document.GetAllocator());
399
400 if (form.Force)
401 document.AddMember("force", form.Force, document.GetAllocator());
402
403 StringBuffer buffer;
404 Writer<StringBuffer> writer(buffer);
405 document.Accept(writer);
406
407 return buffer.GetString();
408}
409
410std::optional<ProjectSyncState>
411DeserializeProjectSyncState(const std::string& data)
412{
413 ProjectSyncState result;
414
415 if (Deserialize(data, result))
416 return std::move(result);
417
418 return {};
419}
420
421std::optional<CreateSnapshotResponse>
422DeserializeCreateSnapshotResponse(const std::string& data)
423{
425
426 if (Deserialize(data, result))
427 return std::move(result);
428
429 return {};
430}
431
432std::optional<PaginatedProjectsResponse>
434{
436
437 if (Deserialize(data, result))
438 return std::move(result);
439
440 return {};
441}
442
443std::optional<ProjectInfo> DeserializeProjectInfo(const std::string& data)
444{
445 ProjectInfo result;
446
447 if (Deserialize(data, result))
448 return std::move(result);
449
450 return {};
451}
452
453std::optional<SnapshotInfo> DeserializeSnapshotInfo(const std::string& data)
454{
455 SnapshotInfo result;
456
457 if (Deserialize(data, result))
458 return std::move(result);
459
460 return {};
461}
462
463namespace
464{
465wxString SafeName(wxString name)
466{
467 static const std::unordered_set<wxChar> invalidChars {
468 L'/', L'\\', L':', L'*', L'?', L'"', L'\'', L'<', L'>', L'|', L'@',
469 L'&', '$', L'%', L'^', L';', L'~', L'`', L'[', L']', L'{', L'}',
470 };
471
472 for (size_t i = 0; i < name.length(); ++i)
473 {
474 if (invalidChars.find(name[i]) != invalidChars.end())
475 name[i] = wxT('_');
476 }
477
478 return name;
479}
480} // namespace
481
482wxString
483MakeSafeProjectPath(const wxString& rootDir, const wxString& projectName)
484{
485 const auto safeName = SafeName(projectName);
486
487 auto path = wxFileNameWrapper { rootDir, safeName, "aup3" };
488
489 int iteration = 0;
490 while (path.FileExists())
491 path.SetName(wxString::Format("%s_%d", safeName, ++iteration));
492
493 return path.GetFullPath();
494}
495
496std::string Serialize(NetworkStats stats)
497{
498 using namespace rapidjson;
499
500 Document document;
501 document.SetObject();
502
503 document.AddMember("is_download", stats.IsDownload, document.GetAllocator());
504 document.AddMember("bytes", stats.Bytes, document.GetAllocator());
505 document.AddMember("blocks", stats.Blocks, document.GetAllocator());
506 document.AddMember("mixes", stats.Mixes, document.GetAllocator());
507 document.AddMember("files", stats.Files, document.GetAllocator());
508
509 StringBuffer buffer;
510 Writer<StringBuffer> writer(buffer);
511 document.Accept(writer);
512
513 return buffer.GetString();
514}
515
516} // namespace audacity::cloud::audiocom::sync
wxT("CloseDown"))
wxString name
Definition: TagsEditor.cpp:166
bool DeserializeArray(const rapidjson::Value &value, std::string_view zKey, std::vector< T > &result)
std::optional< ProjectSyncState > DeserializeProjectSyncState(const std::string &data)
std::optional< ProjectInfo > DeserializeProjectInfo(const std::string &data)
wxString MakeSafeProjectPath(const wxString &rootDir, const wxString &projectName)
std::string Serialize(const ProjectForm &form)
std::optional< PaginatedProjectsResponse > DeserializePaginatedProjectsResponse(const std::string &data)
std::optional< SnapshotInfo > DeserializeSnapshotInfo(const std::string &data)
std::optional< CreateSnapshotResponse > DeserializeCreateSnapshotResponse(const std::string &data)
std::vector< SnapshotBlockInfo > Blocks
Definition: CloudSyncDTO.h:89