• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Scripta-Qumranica-Electronica / SQE_API / 29813431067

21 Jul 2026 08:13AM UTC coverage: 83.025% (+0.2%) from 82.822%
29813431067

push

github

web-flow
Merge pull request #74 from Scripta-Qumranica-Electronica/write-path-perf

Edition publishing + published-editions cache; write-path round-trip reduction

1554 of 2355 branches covered (65.99%)

Branch coverage included in aggregate %.

12459 of 14523 relevant lines covered (85.79%)

25422.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.96
/sqe-database-access/EditionRepository.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Data;
4
using System.Linq;
5
using System.Threading.Tasks;
6
using Dapper;
7
using SQE.DatabaseAccess.Helpers;
8
using SQE.DatabaseAccess.Models;
9
using SQE.DatabaseAccess.Queries;
10

11
// ReSharper disable ArrangeRedundantParentheses
12
// ReSharper disable RemoveRedundantBraces
13
namespace SQE.DatabaseAccess;
14

15
public interface IEditionRepository
16
{
17
        Task<IEnumerable<Edition>> ListEditionsAsync(
18
                        uint?   userId
19
                        , uint? editionId
20
                        , bool  published = true
21
                        , bool  personal  = true);
22

23
        Task<Edition> GetEditionAsync(uint? userId, uint editionId);
24

25
        Task ChangeEditionNameAsync(UserInfo editionUser, string name);
26

27
        Task UpdateEditionMetricsAsync(
28
                        UserInfo editionUser
29
                        , uint   width
30
                        , uint   height
31
                        , int    xOrigin
32
                        , int    yOrigin);
33

34
        Task<uint> CopyEditionAsync(
35
                        UserInfo        editionUser
36
                        , string        name            = null
37
                        , string        copyrightHolder = null
38
                        , string        collaborators   = null
39
                        , IDbConnection connection      = null);
40

41
        Task ChangeEditionCopyrightAsync(
42
                        UserInfo        editionUser
43
                        , string        copyrightHolder = null
44
                        , string        collaborators   = null
45
                        , IDbConnection connection      = null);
46

47
        Task<string> ArchiveEditionAsync(UserInfo editionUser, string token);
48

49
        Task PublishEditionAsync(UserInfo editionUser);
50

51
        Task<string> GetArchiveToken(UserInfo editionUser);
52

53
        Task<DetailedUserWithToken> RequestAddEditionEditorAsync(
54
                        UserInfo editionUser
55
                        , string editorEmail
56
                        , bool?  mayRead
57
                        , bool?  mayWrite
58
                        , bool?  mayLock
59
                        , bool?  isAdmin);
60

61
        Task<DetailedEditionPermission> AddEditionEditorAsync(string token, uint userId);
62

63
        Task<List<DetailedEditorRequestPermissions>> GetOutstandingEditionEditorRequestsAsync(
64
                        uint userId);
65

66
        Task<List<DetailedEditorInvitationPermissions>> GetOutstandingEditionEditorInvitationsAsync(
67
                        uint userId);
68

69
        Task<Permission> ChangeEditionEditorRightsAsync(
70
                        UserInfo editionUser
71
                        , string editorEmail
72
                        , bool?  mayRead
73
                        , bool?  mayWrite
74
                        , bool?  mayLock
75
                        , bool?  isAdmin);
76

77
        Task<List<uint>> GetEditionEditorUserIdsAsync(UserInfo editionUser);
78

79
        Task<IEnumerable<Edition>> GetManuscriptEditions(uint? userId, uint manuscriptId);
80

81
        Task<uint?> GetEditionManuscriptIdAsync(uint editionId);
82

83
        Task<List<LetterShape>> GetEditionScriptCollectionAsync(UserInfo editonUser);
84

85
        Task<List<ScriptTextFragment>> GetEditionScriptLines(UserInfo editionUser);
86
        Task<EditionMetadata>          GetEditionMetadata(UserInfo    editionUser);
87
}
88

89
public class EditionRepository(IDatabaseAccessor dba) : IEditionRepository
4,488✔
90
{
91
        public async Task<IEnumerable<Edition>> ListEditionsAsync(
92
                        uint?   userId
93
                        , uint? editionId
94
                        , bool  published = true
95
                        , bool  personal  = true)
96
        {
168✔
97
                var editions = new List<Edition>();
168✔
98
                Edition lastEdition;
99

100
                if (published)
168✔
101
                {
159✔
102
                        await dba.QueryAsync<EditionListQuery.Result, EditorWithPermissions, Edition>(
159✔
103
                                        PublishedEditionListQuery.GetQuery()
159✔
104
                                        , (editionGroup, editor) =>
159✔
105
                                          {
223,556✔
106
                                                  // Set the copyrights for the previous, and now complete, edition before making the new one
159✔
107
                                                  if ((editions.LastOrDefault()?.EditionId != null)
223,556!
108
                                                          && (editions.LastOrDefault()?.EditionId != editionGroup.EditionId))
223,556✔
109
                                                  {
217,514✔
110
                                                          lastEdition = editions.Last();
217,514✔
111

159✔
112
                                                          lastEdition.Copyright = Licence.printLicence(
217,514✔
113
                                                                          lastEdition.CopyrightHolder
217,514✔
114
                                                                          , string.IsNullOrEmpty(lastEdition.Collaborators)
217,514✔
115
                                                                                          ? string.Join(
217,514✔
116
                                                                                                          ", "
217,514✔
117
                                                                                                          , lastEdition.Editors.Select(y =>
217,514✔
118
                                                                                                                                                                   {
2✔
119
                                                                                                                                                                           if ((y
2!
120
                                                                                                                                                                                                        .Forename
2✔
121
                                                                                                                                                                                        == null)
2✔
122
                                                                                                                                                                                   && (y
2✔
123
                                                                                                                                                                                                           .Surname
2✔
124
                                                                                                                                                                                           == null))
2✔
125
                                                                                                                                                                           {
2✔
126
                                                                                                                                                                                   return y
2✔
127
                                                                                                                                                                                                   .EditorEmail;
2✔
128
                                                                                                                                                                           }
217,514✔
129

217,514✔
130
                                                                                                                                                                           return $@"{
×
131
                                                                                                                                                                                   y.Forename
×
132
                                                                                                                                                                           } {
×
133
                                                                                                                                                                                   y.Surname
×
134
                                                                                                                                                                           }".Trim();
×
135
                                                                                                                                                                   }))
2✔
136
                                                                                          : lastEdition.Collaborators);
217,514✔
137
                                                  }
217,514✔
138

159✔
139
                                                  if ((editions.LastOrDefault()?.EditionId == null)
223,556!
140
                                                          || (editions.LastOrDefault()?.EditionId != editionGroup.EditionId))
223,556✔
141
                                                  {
217,673✔
142
                                                          // Now start building the new edition
159✔
143
                                                          lastEdition = new Edition
217,673✔
144
                                                          {
217,673✔
145
                                                                          Name = editionGroup.Name
217,673✔
146
                                                                          , Width = editionGroup.Width
217,673✔
147
                                                                          , Height = editionGroup.Height
217,673✔
148
                                                                          , XOrigin = editionGroup.XOrigin
217,673✔
149
                                                                          , YOrigin = editionGroup.YOrigin
217,673✔
150
                                                                          , PPI = editionGroup.PPI
217,673✔
151
                                                                          , ManuscriptMetricsEditor =
217,673✔
152
                                                                                          editionGroup.ManuscriptMetricsEditor
217,673✔
153
                                                                          , Collaborators = editionGroup.Collaborators
217,673✔
154
                                                                          , Copyright = null
217,673✔
155
                                                                          , //Licence.printLicence(editionGroup.CopyrightHolder, editionGroup.Collaborators),
217,673✔
156
                                                                          CopyrightHolder = editionGroup.CopyrightHolder
217,673✔
157
                                                                          , EditionDataEditorId = editionGroup.EditionDataEditorId
217,673✔
158
                                                                          , EditionId = editionGroup.EditionId
217,673✔
159
                                                                          , IsPublic = editionGroup.IsPublic
217,673✔
160
                                                                          , PublicationDate = editionGroup.PublicationDate
217,673✔
161
                                                                          , LastEdit = editionGroup.LastEdit
217,673✔
162
                                                                          , Locked = editionGroup.Locked
217,673✔
163
                                                                          , Owner =
217,673✔
164
                                                                                          new User
217,673✔
165
                                                                                          {
217,673✔
166
                                                                                                          Email = editionGroup.CurrentEmail
217,673✔
167
                                                                                                          , UserId = editionGroup.CurrentUserId
217,673✔
168
                                                                                                          ,
217,673✔
169
                                                                                          }
217,673✔
170
                                                                          , Permission =
217,673✔
171
                                                                                          new Permission
217,673✔
172
                                                                                          {
217,673✔
173
                                                                                                          IsAdmin = editionGroup.CurrentIsAdmin
217,673✔
174
                                                                                                          , MayLock =
217,673✔
175
                                                                                                                          editionGroup.CurrentMayLock
217,673✔
176
                                                                                                          , MayWrite =
217,673✔
177
                                                                                                                          editionGroup.CurrentMayWrite
217,673✔
178
                                                                                                          , MayRead =
217,673✔
179
                                                                                                                          editionGroup.CurrentMayRead
217,673✔
180
                                                                                                          ,
217,673✔
181
                                                                                          }
217,673✔
182
                                                                          , Thumbnail = editionGroup.Thumbnail
217,673✔
183
                                                                          , ManuscriptId = editionGroup.ManuscriptId
217,673✔
184
                                                                          , Editors = new List<EditorWithPermissions>()
217,673✔
185
                                                                          ,
217,673✔
186
                                                          };
217,673✔
187

159✔
188
                                                          editions.Add(lastEdition);
217,673✔
189
                                                  }
217,673✔
190

159✔
191
                                                  // Add the new editor to this edition
159✔
192
                                                  editions.Last().Editors.Add(editor);
223,556✔
193

159✔
194
                                                  return editions.Last();
223,556✔
195
                                          }
223,556✔
196
                                        , new
159✔
197
                                        {
159✔
198
                                                        UserId = userId
159✔
199
                                                        , EditionId = editionId
159✔
200
                                                        ,
159✔
201
                                        }
159✔
202
                                        , splitOn: "EditorId");
159✔
203

204
                        if (editions.Count <= 0)
159!
205
                                return editions;
×
206

207
                        {
159✔
208
                                lastEdition = editions.Last();
159✔
209

210
                                lastEdition.Copyright = Licence.printLicence(
159!
211
                                                lastEdition.CopyrightHolder
159✔
212
                                                , string.IsNullOrEmpty(lastEdition.Collaborators)
159✔
213
                                                                ? string.Join(
159✔
214
                                                                                ", "
159✔
215
                                                                                , lastEdition.Editors.Select(y =>
159✔
216
                                                                                                                                         {
×
217
                                                                                                                                                 if ((y.Forename == null)
×
218
                                                                                                                                                         && (y.Surname == null))
×
219
                                                                                                                                                 {
×
220
                                                                                                                                                         return y.EditorEmail;
×
221
                                                                                                                                                 }
159✔
222

159✔
223
                                                                                                                                                 return $@"{
×
224
                                                                                                                                                         y.Forename
×
225
                                                                                                                                                 } {
×
226
                                                                                                                                                         y.Surname
×
227
                                                                                                                                                 }".Trim();
×
228
                                                                                                                                         }))
×
229
                                                                : lastEdition.Collaborators);
159✔
230
                        }
159✔
231
                }
159✔
232

233
                if (personal)
168✔
234
                {
160✔
235
                        await dba.QueryAsync<EditionListQuery.Result, EditorWithPermissions, Edition>(
160✔
236
                                        EditionListQuery.GetQuery(
160✔
237
                                                        userId.HasValue
160✔
238
                                                        , editionId.HasValue
160✔
239
                                                        , false
160✔
240
                                                        , personal)
160✔
241
                                        , (editionGroup, editor) =>
160✔
242
                                          {
625✔
243
                                                  // Set the copyrights for the previous, and now complete, edition before making the new one
160✔
244
                                                  if ((editions.LastOrDefault()?.EditionId != null)
625!
245
                                                          && (editions.LastOrDefault()?.EditionId != editionGroup.EditionId))
625✔
246
                                                  {
612✔
247
                                                          lastEdition = editions.Last();
612✔
248

160✔
249
                                                          lastEdition.Copyright = Licence.printLicence(
612✔
250
                                                                          lastEdition.CopyrightHolder
612✔
251
                                                                          , string.IsNullOrEmpty(lastEdition.Collaborators)
612✔
252
                                                                                          ? string.Join(
612✔
253
                                                                                                          ", "
612✔
254
                                                                                                          , lastEdition.Editors.Select(y =>
612✔
255
                                                                                                                                                                   {
463✔
256
                                                                                                                                                                           if ((y
463!
257
                                                                                                                                                                                                        .Forename
463✔
258
                                                                                                                                                                                        == null)
463✔
259
                                                                                                                                                                                   && (y
463✔
260
                                                                                                                                                                                                           .Surname
463✔
261
                                                                                                                                                                                           == null))
463✔
262
                                                                                                                                                                           {
463✔
263
                                                                                                                                                                                   return y
463✔
264
                                                                                                                                                                                                   .EditorEmail;
463✔
265
                                                                                                                                                                           }
612✔
266

612✔
267
                                                                                                                                                                           return $@"{
×
268
                                                                                                                                                                                   y.Forename
×
269
                                                                                                                                                                           } {
×
270
                                                                                                                                                                                   y.Surname
×
271
                                                                                                                                                                           }".Trim();
×
272
                                                                                                                                                                   }))
463✔
273
                                                                                          : lastEdition.Collaborators);
612✔
274
                                                  }
612✔
275

160✔
276
                                                  if ((editions.LastOrDefault()?.EditionId == null)
625!
277
                                                          || (editions.LastOrDefault()?.EditionId != editionGroup.EditionId))
625✔
278
                                                  {
621✔
279
                                                          // Now start building the new edition
160✔
280
                                                          lastEdition = new Edition
621✔
281
                                                          {
621✔
282
                                                                          Name = editionGroup.Name
621✔
283
                                                                          , Width = editionGroup.Width
621✔
284
                                                                          , Height = editionGroup.Height
621✔
285
                                                                          , XOrigin = editionGroup.XOrigin
621✔
286
                                                                          , YOrigin = editionGroup.YOrigin
621✔
287
                                                                          , PPI = editionGroup.PPI
621✔
288
                                                                          , ManuscriptMetricsEditor =
621✔
289
                                                                                          editionGroup.ManuscriptMetricsEditor
621✔
290
                                                                          , Collaborators = editionGroup.Collaborators
621✔
291
                                                                          , Copyright = null
621✔
292
                                                                          , //Licence.printLicence(editionGroup.CopyrightHolder, editionGroup.Collaborators),
621✔
293
                                                                          CopyrightHolder = editionGroup.CopyrightHolder
621✔
294
                                                                          , EditionDataEditorId = editionGroup.EditionDataEditorId
621✔
295
                                                                          , EditionId = editionGroup.EditionId
621✔
296
                                                                          , IsPublic = editionGroup.IsPublic
621✔
297
                                                                          , PublicationDate = editionGroup.PublicationDate
621✔
298
                                                                          , LastEdit = editionGroup.LastEdit
621✔
299
                                                                          , Locked = editionGroup.Locked
621✔
300
                                                                          , Owner =
621✔
301
                                                                                          new User
621✔
302
                                                                                          {
621✔
303
                                                                                                          Email = editionGroup.CurrentEmail
621✔
304
                                                                                                          , UserId = editionGroup.CurrentUserId
621✔
305
                                                                                                          ,
621✔
306
                                                                                          }
621✔
307
                                                                          , Permission =
621✔
308
                                                                                          new Permission
621✔
309
                                                                                          {
621✔
310
                                                                                                          IsAdmin = editionGroup.CurrentIsAdmin
621✔
311
                                                                                                          , MayLock =
621✔
312
                                                                                                                          editionGroup.CurrentMayLock
621✔
313
                                                                                                          , MayWrite =
621✔
314
                                                                                                                          editionGroup.CurrentMayWrite
621✔
315
                                                                                                          , MayRead =
621✔
316
                                                                                                                          editionGroup.CurrentMayRead
621✔
317
                                                                                                          ,
621✔
318
                                                                                          }
621✔
319
                                                                          , Thumbnail = editionGroup.Thumbnail
621✔
320
                                                                          , ManuscriptId = editionGroup.ManuscriptId
621✔
321
                                                                          , Editors = new List<EditorWithPermissions>()
621✔
322
                                                                          ,
621✔
323
                                                          };
621✔
324

160✔
325
                                                          editions.Add(lastEdition);
621✔
326
                                                  }
621✔
327

160✔
328
                                                  // Add the new editor to this edition
160✔
329
                                                  editions.Last().Editors.Add(editor);
625✔
330

160✔
331
                                                  return editions.Last();
625✔
332
                                          }
625✔
333
                                        , new
160✔
334
                                        {
160✔
335
                                                        UserId = userId
160✔
336
                                                        , EditionId = editionId
160✔
337
                                                        ,
160✔
338
                                        }
160✔
339
                                        , splitOn: "EditorId");
160✔
340

341
                        if (editions.Count <= 0)
160!
342
                                return editions;
×
343

344
                        {
160✔
345
                                lastEdition = editions.Last();
160✔
346

347
                                lastEdition.Copyright = Licence.printLicence(
160!
348
                                                lastEdition.CopyrightHolder
160✔
349
                                                , string.IsNullOrEmpty(lastEdition.Collaborators)
160✔
350
                                                                ? string.Join(
160✔
351
                                                                                ", "
160✔
352
                                                                                , lastEdition.Editors.Select(y =>
160✔
353
                                                                                                                                         {
162✔
354
                                                                                                                                                 if ((y.Forename == null)
162!
355
                                                                                                                                                         && (y.Surname == null))
162✔
356
                                                                                                                                                 {
162✔
357
                                                                                                                                                         return y.EditorEmail;
162✔
358
                                                                                                                                                 }
160✔
359

160✔
360
                                                                                                                                                 return $@"{
×
361
                                                                                                                                                         y.Forename
×
362
                                                                                                                                                 } {
×
363
                                                                                                                                                         y.Surname
×
364
                                                                                                                                                 }".Trim();
×
365
                                                                                                                                         }))
162✔
366
                                                                : lastEdition.Collaborators);
160✔
367
                        }
160✔
368
                }
160✔
369

370
                return editions;
168✔
371
        }
168✔
372

373
        public async Task<Edition> GetEditionAsync(uint? userId, uint editionId) //
374
        {
104✔
375
                var editionDictionary = new Dictionary<uint, Edition>();
104✔
376
                Edition lastEdition = null;
104✔
377

378
                await dba.QueryAsync<EditionQuery.Result, EditorWithPermissions, Edition>(
104✔
379
                                EditionQuery.GetQuery(userId.HasValue, true)
104✔
380
                                , (editionGroup, editor) =>
104✔
381
                                  {
104✔
382
                                          // Check if we have moved on to a new edition
104✔
383
                                          if (!editionDictionary.TryGetValue(editionGroup.EditionId, out lastEdition))
104✔
384
                                          {
104✔
385
                                                  // Set the copyrights for the previous, and now complete, edition before making the new one
104✔
386
                                                  if (lastEdition != null)
104!
387
                                                  {
×
388
                                                          lastEdition.Copyright = Licence.printLicence(
×
389
                                                                          lastEdition.CopyrightHolder
×
390
                                                                          , string.IsNullOrEmpty(lastEdition.Collaborators)
×
391
                                                                                          ? string.Join(
×
392
                                                                                                          ", "
×
393
                                                                                                          , lastEdition.Editors.Select(y =>
×
394
                                                                                                                                                                   {
×
395
                                                                                                                                                                           if ((y
×
396
                                                                                                                                                                                                        .Forename
×
397
                                                                                                                                                                                        == null)
×
398
                                                                                                                                                                                   && (y
×
399
                                                                                                                                                                                                           .Surname
×
400
                                                                                                                                                                                           == null))
×
401
                                                                                                                                                                           {
×
402
                                                                                                                                                                                   return y
×
403
                                                                                                                                                                                                   .EditorEmail;
×
404
                                                                                                                                                                           }
×
405

×
406
                                                                                                                                                                           return $@"{
×
407
                                                                                                                                                                                   y.Forename
×
408
                                                                                                                                                                           } {
×
409
                                                                                                                                                                                   y.Surname
×
410
                                                                                                                                                                           }".Trim();
×
411
                                                                                                                                                                   }))
×
412
                                                                                          : lastEdition.Collaborators);
×
413
                                                  }
×
414

104✔
415
                                                  // Now start building the new edition
104✔
416
                                                  lastEdition = new Edition
104✔
417
                                                  {
104✔
418
                                                                  Name = editionGroup.Name
104✔
419
                                                                  , Width = editionGroup.Width
104✔
420
                                                                  , Height = editionGroup.Height
104✔
421
                                                                  , XOrigin = editionGroup.XOrigin
104✔
422
                                                                  , YOrigin = editionGroup.YOrigin
104✔
423
                                                                  , PPI = editionGroup.PPI
104✔
424
                                                                  , ManuscriptMetricsEditor = editionGroup.ManuscriptMetricsEditor
104✔
425
                                                                  , Collaborators = editionGroup.Collaborators
104✔
426
                                                                  , Copyright = null
104✔
427
                                                                  , //Licence.printLicence(editionGroup.CopyrightHolder, editionGroup.Collaborators),
104✔
428
                                                                  CopyrightHolder = editionGroup.CopyrightHolder
104✔
429
                                                                  , EditionDataEditorId = editionGroup.EditionDataEditorId
104✔
430
                                                                  , EditionId = editionGroup.EditionId
104✔
431
                                                                  , IsPublic = editionGroup.IsPublic
104✔
432
                                                                  , PublicationDate = editionGroup.PublicationDate
104✔
433
                                                                  , LastEdit = editionGroup.LastEdit
104✔
434
                                                                  , Locked = editionGroup.Locked
104✔
435
                                                                  , Owner =
104✔
436
                                                                                  new User
104✔
437
                                                                                  {
104✔
438
                                                                                                  Email = editionGroup.CurrentEmail
104✔
439
                                                                                                  , UserId = editionGroup.CurrentUserId
104✔
440
                                                                                                  ,
104✔
441
                                                                                  }
104✔
442
                                                                  , Permission =
104✔
443
                                                                                  new Permission
104✔
444
                                                                                  {
104✔
445
                                                                                                  IsAdmin = editionGroup.CurrentIsAdmin
104✔
446
                                                                                                  , MayLock = editionGroup.CurrentMayLock
104✔
447
                                                                                                  , MayWrite = editionGroup.CurrentMayWrite
104✔
448
                                                                                                  , MayRead = editionGroup.CurrentMayRead
104✔
449
                                                                                                  ,
104✔
450
                                                                                  }
104✔
451
                                                                  , Thumbnail = editionGroup.Thumbnail
104✔
452
                                                                  , ManuscriptId = editionGroup.ManuscriptId
104✔
453
                                                                  , Editors = new List<EditorWithPermissions>()
104✔
454
                                                                  ,
104✔
455
                                                  };
104✔
456

104✔
457
                                                  editionDictionary.Add(lastEdition.EditionId, lastEdition);
104✔
458
                                          }
104✔
459

104✔
460
                                          // Add the new editor to this edition
104✔
461
                                          lastEdition.Editors.Add(editor);
104✔
462

104✔
463
                                          return lastEdition;
104✔
464
                                  }
104✔
465
                                , new
104✔
466
                                {
104✔
467
                                                UserId = userId
104✔
468
                                                , EditionId = editionId
104✔
469
                                                ,
104✔
470
                                }
104✔
471
                                , splitOn: "EditorId");
104✔
472

473
                return lastEdition ?? new Edition();
104!
474
        }
104✔
475

476
        public async Task ChangeEditionNameAsync(UserInfo editionUser, string name)
477
        {
2✔
478
                EditionNameQuery.Result result;
479

480
                await dba.BeginTransactionAsync();
2✔
481

482
                try
483
                {
2✔
484
                        // Here we get the data from the original scroll_data field, we need the scroll_id,
485
                        // which no one in the front end will generally have or care about.
486
                        result = await dba.QuerySingleAsync<EditionNameQuery.Result>(
2✔
487
                                        EditionNameQuery.GetQuery()
2✔
488
                                        , new { editionUser.EditionId });
2✔
489
                }
2✔
490
                catch (InvalidOperationException)
×
491
                {
×
492
                        throw new StandardExceptions.DataNotFoundException(
×
493
                                        "edition"
×
494
                                        , editionUser.EditionId ?? 0);
×
495
                }
496

497
                // Now we create the mutation object for the requested action
498
                // You will want to check the database to make sure you what you are doing.
499
                var nameChangeParams = new DynamicParameters();
2✔
500
                nameChangeParams.Add("@manuscript_id", result.ManuscriptId);
2✔
501
                nameChangeParams.Add("@Name", name);
2✔
502

503
                var nameChangeRequest = new MutationRequest(
2✔
504
                                MutateType.Update
2✔
505
                                , nameChangeParams
2✔
506
                                , "manuscript_data"
2✔
507
                                , result.ManuscriptDataId);
2✔
508

509
                // Now TrackMutation will insert the data, make all relevant changes to the owner tables and take
510
                // care of main_action and single_action.
511
                await dba.WriteToDatabaseAsync(
2✔
512
                                editionUser
2✔
513
                                , new List<MutationRequest> { nameChangeRequest });
2✔
514

515
                dba.CommitTransaction();
2✔
516
        }
2✔
517

518
        /// <summary>
519
        ///  Update the metric estimations of the manuscript for an edition
520
        /// </summary>
521
        /// <param name="editionUser">Details of the user requesting the changes</param>
522
        /// <param name="width">A non-negative estimation of the manuscript width in millimeters (may be zero)</param>
523
        /// <param name="height">A non-negative estimation of the manuscript height in millimeters (may be zero)</param>
524
        /// <param name="xOrigin">
525
        ///  An estimation of the point at which the manuscript begins on the x axis in millimeters (may be
526
        ///  zero)
527
        /// </param>
528
        /// <param name="yOrigin">
529
        ///  An estimation of the point at which the manuscript begins on the x axis in millimeters (may be
530
        ///  zero)(may be zero)
531
        /// </param>
532
        /// <returns></returns>
533
        public async Task UpdateEditionMetricsAsync(
534
                        UserInfo editionUser
535
                        , uint   width
536
                        , uint   height
537
                        , int    xOrigin
538
                        , int    yOrigin)
539
        {
1✔
540
                await dba.BeginTransactionAsync();
1✔
541

542
                var oldRecord = (await dba.QueryAsync<GetEditionManuscriptMetricsDetails.Result>(
1✔
543
                                GetEditionManuscriptMetricsDetails.GetQuery
1✔
544
                                , new { editionUser.EditionId })).ToList();
1✔
545

546
                if (oldRecord.Count != 1)
1!
547
                {
×
548
                        throw new StandardExceptions.DataNotFoundException(
×
549
                                        "manuscript metrics"
×
550
                                        , editionUser.EditionId ?? 0
×
551
                                        , "edition");
×
552
                }
553

554
                var parameters = new DynamicParameters();
1✔
555
                parameters.Add("width", width);
1✔
556
                parameters.Add("height", height);
1✔
557
                parameters.Add("x_origin", xOrigin);
1✔
558
                parameters.Add("y_origin", yOrigin);
1✔
559

560
                parameters.Add("manuscript_id", oldRecord.First().ManuscriptId);
1✔
561

562
                var mutation = new MutationRequest(
1✔
563
                                MutateType.Update
1✔
564
                                , parameters
1✔
565
                                , "manuscript_metrics"
1✔
566
                                , oldRecord.First().ManuscriptMetricsId);
1✔
567

568
                var results = await dba.WriteToDatabaseAsync(editionUser, mutation);
1✔
569

570
                if (results.Count() != 1)
1!
571
                {
×
572
                        throw new StandardExceptions.DataNotWrittenException("update manuscript metrics");
×
573
                }
574

575
                dba.CommitTransaction();
1✔
576
        }
1✔
577

578
        /// <summary>
579
        ///  This creates a new copy of the requested edition, which will be owned with full privileges
580
        ///  by the requesting user.
581
        /// </summary>
582
        /// <param name="editionUser">
583
        ///  User info object contains the editionId that the user wishes to copy and
584
        ///  all user permissions related to it.
585
        /// </param>
586
        /// <param name="name">
587
        ///  New name for the edition.
588
        /// </param>
589
        /// <param name="copyrightHolder">
590
        ///  Name of the person/institution that holds the copyright
591
        ///  (automatically created from user when null)
592
        /// </param>
593
        /// <param name="collaborators">
594
        ///  Names of all collaborators
595
        ///  (automatically created from user and all editors when null)
596
        /// </param>
597
        /// <returns>The editionId of the newly created edition.</returns>
598
        public async Task<uint> CopyEditionAsync(
599
                        UserInfo        editionUser
600
                        , string        name            = null
601
                        , string        copyrightHolder = null
602
                        , string        collaborators   = null
603
                        , IDbConnection connection      = null)
604
        {
136✔
605
                if (!editionUser.EditionId.HasValue)
136!
606
                        throw new StandardExceptions.ImproperInputDataException("edition id");
×
607

608
                // Note, we had tried to make this quicker by collecting all the edition info in a single
609
                // transaction, then performing the writes in a separate transaction. It turns out that
610
                // approach is about 4 times slower than the one here.
611
                List<OwnerTables.Result> ownerTables;
612

613
                ownerTables = (await dba.QueryAsync<OwnerTables.Result>(OwnerTables.GetQuery)).ToList();
136✔
614

615
                // In an effort to speed this up further, I tried disabling foreign keys and unique checks.
616
                // It made no appreciable difference:
617
                // await connection.ExecuteAsync("SET @@session.foreign_key_checks=0;");
618
                // await connection.ExecuteAsync("SET @@session.unique_checks=0;");
619
                await dba.BeginTransactionAsync();
136✔
620

621
                // Create a new edition
622
                await dba.ExecuteAsync(
136✔
623
                                CopyEditionQuery.GetQuery
136✔
624
                                , new
136✔
625
                                {
136✔
626
                                                editionUser.EditionId
136✔
627
                                                , CopyrightHolder = copyrightHolder
136✔
628
                                                , Collaborators = collaborators
136✔
629
                                                ,
136✔
630
                                });
136✔
631

632
                var toEditionId = await dba.QuerySingleAsync<uint>(LastInsertId.GetQuery);
136✔
633

634
                if (toEditionId == 0)
136!
635
                {
×
636
                        throw new StandardExceptions.DataNotWrittenException("create edition");
×
637
                }
638

639
                // Create new edition_editor
640
                await dba.ExecuteAsync(
136✔
641
                                CreateEditionEditorQuery.GetQuery
136✔
642
                                , new
136✔
643
                                {
136✔
644
                                                EditionId = toEditionId
136✔
645
                                                , UserId = editionUser.userId
136✔
646
                                                , MayLock = 1
136✔
647
                                                , IsAdmin = 1
136✔
648
                                                ,
136✔
649
                                });
136✔
650

651
                var toEditionEditorId = await dba.QuerySingleAsync<uint>(LastInsertId.GetQuery);
136✔
652

653
                if (toEditionEditorId == 0)
136!
654
                {
×
655
                        throw new StandardExceptions.DataNotWrittenException("create edition_editor");
×
656
                }
657

658
                uint? manuscriptDataId = null;
136✔
659

660
                if (!string.IsNullOrEmpty(name))
136✔
661
                {
134✔
662
                        await dba.ExecuteAsync(
134✔
663
                                        @"
134✔
664
                        INSERT INTO manuscript_data (manuscript_id, name, creator_id)
134✔
665
                        SELECT manuscript_id, @Name, @UserId
134✔
666
                        FROM edition
134✔
667
                        WHERE edition.edition_id = @EditionId
134✔
668
                        ON DUPLICATE KEY UPDATE manuscript_data_id=LAST_INSERT_ID(manuscript_data_id)"
134✔
669
                                        , new
134✔
670
                                        {
134✔
671
                                                        Name = name
134✔
672
                                                        , UserId = editionUser.userId
134✔
673
                                                        , EditionId = toEditionId
134✔
674
                                                        ,
134✔
675
                                        });
134✔
676

677
                        manuscriptDataId = await dba.QuerySingleAsync<uint>(LastInsertId.GetQuery);
134✔
678
                }
134✔
679

680
                foreach (var ownerTable in ownerTables)
11,016✔
681
                {
5,304✔
682
                        var tableName = ownerTable.TableName;
5,304✔
683

684
                        var tableIdColumn = tableName.Substring(0, tableName.Length - 5) + "id";
5,304✔
685

686
                        if ((tableName == "manuscript_data_owner")
5,304✔
687
                                && manuscriptDataId.HasValue)
5,304✔
688
                        {
134✔
689
                                await dba.ExecuteAsync(
134✔
690
                                                @"
134✔
691
INSERT INTO manuscript_data_owner (manuscript_data_id, edition_id, edition_editor_id)
134✔
692
VALUES (@ManuscriptDataId, @EditionId, @EditionEditorId)"
134✔
693
                                                , new
134✔
694
                                                {
134✔
695
                                                                EditionId = toEditionId
134✔
696
                                                                , EditionEditorId = toEditionEditorId
134✔
697
                                                                , ManuscriptDataId = manuscriptDataId.Value
134✔
698
                                                                ,
134✔
699
                                                });
134✔
700

701
                                continue;
134✔
702
                        }
703

704
                        // Should I do any error checking here?
705
                        await dba.ExecuteAsync(
5,170✔
706
                                        CopyTableQuery.GetQuery(
5,170✔
707
                                                        tableName
5,170✔
708
                                                        , tableIdColumn
5,170✔
709
                                                        , toEditionId
5,170✔
710
                                                        , toEditionEditorId
5,170✔
711
                                                        , editionUser.EditionId.Value));
5,170✔
712
                }
5,170✔
713

714
                //Copy cached transcriptions
715
                const string copyCacheQSL = @"
716
INSERT INTO cached_text_fragment (edition_id, text_fragment_id, transcription_json, transcription_date)
717
SELECT         @NewEditionId,
718
        text_fragment_id,
719
        REGEXP_REPLACE(
720
                REGEXP_REPLACE(transcription_json, '""editorId"":[0-9]+', CONCAT('""editorId"":', @EditionEditorId)),
721
                '""editors"":{.*?}}',
722
                CONCAT(
723
                        CONCAT('""editors"":{""', @EditionEditorId,'"":{""email"":""'),
724
                        (SELECT email FROM user WHERE user_id = @UserId),
725
                        '"",""forename"":',
726
                        (SELECT IF(forename IS NULL, 'null', CONCAT('""', forename, '""')) FROM user WHERE user_id = @UserId),
727
                    ',""surname"":',
728
                    (SELECT IF(surname IS NULL, 'null', CONCAT('""', surname, '""')) FROM user WHERE user_id = @UserId),
729
                        ',""organization"":',
730
                        (SELECT IF(organization IS NULL, 'null', CONCAT('""', organization, '""')) FROM user WHERE user_id = @UserId),
731
                        '}}'
732
                )
733
        ),
734
        transcription_date
735
FROM cached_text_fragment
736
WHERE edition_id = @EditionId";
737

738
                await dba.ExecuteAsync(
136✔
739
                                copyCacheQSL
136✔
740
                                , new
136✔
741
                                {
136✔
742
                                                EditionId = editionUser.EditionId.Value
136✔
743
                                                , NewEditionId = toEditionId
136✔
744
                                                , EditionEditorId = toEditionEditorId
136✔
745
                                                , UserId = editionUser.userId
136✔
746
                                                ,
136✔
747
                                });
136✔
748

749
                //Cleanup
750
                dba.CommitTransaction();
136✔
751

752
                return toEditionId;
136✔
753
        }
136✔
754

755
        /// <summary>
756
        ///  Change copyright holder and/or collaborators of the users current edition.
757
        /// </summary>
758
        /// <param name="editionUser">The user's current state.</param>
759
        /// <param name="copyrightHolder">The new copyright holder name to use</param>
760
        /// <param name="collaborators">
761
        ///  The new collaborator list. Null is meaningful here
762
        ///  and will switch to an autogenerated collaborator listing.
763
        /// </param>
764
        /// <returns></returns>
765
        public async Task ChangeEditionCopyrightAsync(
766
                        UserInfo        editionUser
767
                        , string        copyrightHolder = null
768
                        , string        collaborators   = null
769
                        , IDbConnection connection      = null)
770
        {
1✔
771
                // Let's only allow admins to change these legal details.
772
                if (!editionUser.IsAdmin)
1!
773
                        throw new StandardExceptions.NoAdminPermissionsException(editionUser);
×
774

775
                await dba.ExecuteAsync(
1✔
776
                                UpdateEditionLegalDetailsQuery.GetQuery
1✔
777
                                , new
1✔
778
                                {
1✔
779
                                                editionUser.EditionId
1✔
780
                                                , CopyrightHolder = copyrightHolder
1✔
781
                                                , Collaborators = collaborators
1✔
782
                                                ,
1✔
783
                                });
1✔
784
        }
1✔
785

786
        /// <summary>
787
        ///  Archive an edition that the user is currently subscribed to.
788
        /// </summary>
789
        /// <param name="editionUser">User object requesting the achival</param>
790
        /// <param name="token">
791
        ///  Token required to verify archiving. If this is null, one will be created and sent
792
        ///  to the requester to use a confirmation of the archival process.
793
        /// </param>
794
        /// <returns>Returns a null string if successful; a string with a confirmation token if no token was provided.</returns>
795
        public async Task<string> ArchiveEditionAsync(UserInfo editionUser, string token)
796
        {
145✔
797
                // We only allow admins to delete all data in an unlocked edition.
798
                if (!editionUser.IsAdmin)
145!
799
                        throw new StandardExceptions.NoAdminPermissionsException(editionUser);
×
800

801
                // A token is required to delete an edition (we make sure here that people don't accidentally do it)
802
                if (string.IsNullOrEmpty(token))
145✔
803
                        return await GetArchiveToken(editionUser);
131✔
804

805
                // Verify that the token is still valid
806
                var archiveToken = await dba.ExecuteAsync(
14✔
807
                                DeleteUserEmailTokenQuery.GetTokenQuery
14✔
808
                                , new
14✔
809
                                {
14✔
810
                                                Tokens = new[] { token }
14✔
811
                                                , Type = CreateUserEmailTokenQuery.DeleteEdition
14✔
812
                                                ,
14✔
813
                                });
14✔
814

815
                if (archiveToken != 1)
14!
816
                {
×
817
                        throw new StandardExceptions.DataNotWrittenException(
×
818
                                        "verifying the delete request token");
×
819
                }
820

821
                const string archiveSql = "UPDATE edition SET archived = 1 WHERE edition_id = @EditionId";
822

823
                var archive = await dba.ExecuteAsync(archiveSql, new { editionUser.EditionId });
14✔
824

825
                if (archive != 1)
14!
826
                {
×
827
                        throw new StandardExceptions.DataNotWrittenException(
×
828
                                        "archive edition"
×
829
                                        , "unknown reason");
×
830
                }
831

832
                return null;
14✔
833
        }
145✔
834

835
        /// <summary>
836
        ///  Publishes an edition, making it publicly visible. Only an edition admin may publish.
837
        ///  Publishing is irreversible: the edition is locked so that it can never be changed again
838
        ///  (a public edition is frozen forever; new work must be done on a copy).
839
        /// </summary>
840
        /// <param name="editionUser">User object requesting the publication</param>
841
        public async Task PublishEditionAsync(UserInfo editionUser)
842
        {
1✔
843
                // Only admins may publish; publishing exposes the edition to the world and freezes it.
844
                if (!editionUser.IsAdmin)
1!
845
                        throw new StandardExceptions.NoAdminPermissionsException(editionUser);
×
846

847
                // Setting locked = 1 is what actually enforces immutability: UserInfo.ReadPermissions
848
                // computes MayWrite as (may_write AND NOT locked), so a locked edition can never be edited.
849
                // The `public = 0` guard makes this a no-op for an already-published edition.
850
                const string publishSql =
851
                                "UPDATE edition SET public = 1, locked = 1, publication_date = NOW() "
852
                                + "WHERE edition_id = @EditionId AND public = 0";
853

854
                await dba.ExecuteAsync(publishSql, new { editionUser.EditionId });
1✔
855
        }
1✔
856

857
        public async Task<string> GetArchiveToken(UserInfo editionUser)
858
        {
131✔
859
                // Generate our secret token
860
                var token = Guid.NewGuid().ToString();
131✔
861

862
                // Add the secret token to the database
863
                var userEmailConfirmation = await dba.ExecuteAsync(
131✔
864
                                CreateUserEmailTokenQuery.GetQuery()
131✔
865
                                , new
131✔
866
                                {
131✔
867
                                                UserId = editionUser.userId
131✔
868
                                                , Token = token
131✔
869
                                                , Type = CreateUserEmailTokenQuery.DeleteEdition
131✔
870
                                                ,
131✔
871
                                });
131✔
872

873
                if (userEmailConfirmation != 1) // Something strange must have gone wrong
131!
874
                {
×
875
                        throw new StandardExceptions.DataNotWrittenException("create edition delete token");
×
876
                }
877

878
                return token;
131✔
879
        }
131✔
880

881
        /// <summary>
882
        ///  Initiate a request for a user to be added as editor to an edition. This creates a token, which
883
        ///  the requested editor can use to confirm the request.
884
        /// </summary>
885
        /// <param name="editionUser">User object requesting the new editor</param>
886
        /// <param name="editorEmail">New editor's email address</param>
887
        /// <param name="mayRead">Permission to read</param>
888
        /// <param name="mayWrite">Permission to write</param>
889
        /// <param name="mayLock">Permission to lock</param>
890
        /// <param name="isAdmin">Permission to admin</param>
891
        /// <returns></returns>
892
        public async Task<DetailedUserWithToken> RequestAddEditionEditorAsync(
893
                        UserInfo editionUser
894
                        , string editorEmail
895
                        , bool?  mayRead
896
                        , bool?  mayWrite
897
                        , bool?  mayLock
898
                        , bool?  isAdmin)
899
        {
9✔
900
                // Make sure requesting user is admin; only an edition admin may perform this action
901
                if (!editionUser.IsAdmin)
9!
902
                        throw new StandardExceptions.NoAdminPermissionsException(editionUser);
×
903

904
                // Instantiate the return object
905
                DetailedUserWithToken editorInfo;
906

907
                await dba.BeginTransactionAsync();
9✔
908

909
                // Check if the editor already exists, don't attempt to re-add
910
                if ((await _getEditionEditors(editionUser.EditionId.Value))
9!
911
                        .Any(x => x.Email == editorEmail))
18✔
912
                        throw new StandardExceptions.ConflictingDataException("editor email");
×
913

914
                // Set the permissions object by coalescing with the default values
915
                var permissions = new Permission
9✔
916
                {
9✔
917
                                MayRead = mayRead ?? true
9✔
918
                                , MayWrite = mayWrite ?? false
9✔
919
                                , MayLock = mayLock ?? false
9✔
920
                                , IsAdmin = isAdmin ?? false
9✔
921
                                ,
9✔
922
                };
9✔
923

924
                // Check for invalid settings
925
                if (permissions.IsAdmin
9!
926
                        && !permissions.MayRead)
9✔
927
                {
×
928
                        throw new StandardExceptions.InputDataRuleViolationException(
×
929
                                        "an edition admin must have read rights");
×
930
                }
931

932
                if (permissions.MayWrite
9!
933
                        && !permissions.MayRead)
9✔
934
                {
×
935
                        throw new StandardExceptions.InputDataRuleViolationException(
×
936
                                        "an editor with write rights must have read rights");
×
937
                }
938

939
                // Find the editor
940
                var editorInfoSearch = (await dba.QueryAsync<DetailedUserWithToken>(
9✔
941
                                UserDetails.GetQuery(
9✔
942
                                                new List<string>
9✔
943
                                                {
9✔
944
                                                                "user_id"
9✔
945
                                                                , "forename"
9✔
946
                                                                , "surname"
9✔
947
                                                                , "organization"
9✔
948
                                                                ,
9✔
949
                                                }
9✔
950
                                                , new List<string> { "email" })
9✔
951
                                , new { Email = editorEmail })).ToList();
9✔
952

953
                // Throw a meaningful error if the user's email was not found in the system.
954
                if (!editorInfoSearch.Any())
9!
955
                {
×
956
                        throw new StandardExceptions.DataNotFoundException("editors", editorEmail, "users");
×
957
                }
958

959
                editorInfo = editorInfoSearch.FirstOrDefault();
9✔
960

961
                // Check for existing request
962
                var existingRequestToken = (await dba.QueryAsync<string>(
9✔
963
                                FindEditionEditorRequestByEditorEdition.GetQuery
9✔
964
                                , new
9✔
965
                                {
9✔
966
                                                editionUser.EditionId
9✔
967
                                                , AdminUserId = editionUser.userId
9✔
968
                                                , EditorUserId = editorInfo.UserId
9✔
969
                                                ,
9✔
970
                                })).ToList();
9✔
971

972
                // Add a GUID for this transaction (Reuse any pre-existing ones)
973
                if (existingRequestToken.Any())
9!
974
                        editorInfo.Token = Guid.Parse(existingRequestToken.FirstOrDefault());
×
975
                else
976
                {
9✔
977
                        editorInfo.Token = existingRequestToken.Any()
9!
978
                                        ? Guid.Parse(existingRequestToken.FirstOrDefault())
9✔
979
                                        : Guid.NewGuid();
9✔
980

981
                        // Write the GUID token to the database
982
                        var writtenToken = await dba.ExecuteAsync(
9✔
983
                                        CreateUserEmailTokenQuery.GetQuery()
9✔
984
                                        , new
9✔
985
                                        {
9✔
986
                                                        editorInfo.UserId
9✔
987
                                                        , editorInfo.Token
9✔
988
                                                        , Type = CreateUserEmailTokenQuery.EditorInvite
9✔
989
                                                        ,
9✔
990
                                        });
9✔
991

992
                        if (writtenToken != 1)
9!
993
                        {
×
994
                                throw new StandardExceptions.DataNotWrittenException(
×
995
                                                $"create editor invite token for {editorEmail}");
×
996
                        }
997
                }
9✔
998

999
                // Record the editor request in database
1000
                await dba.ExecuteAsync(
9✔
1001
                                RecordEditionEditorRequest.GetQuery
9✔
1002
                                , new
9✔
1003
                                {
9✔
1004
                                                editorInfo.Token
9✔
1005
                                                , AdminUserId = editionUser.userId
9✔
1006
                                                , EditorUserId = editorInfo.UserId
9✔
1007
                                                , editionUser.EditionId
9✔
1008
                                                , permissions.IsAdmin
9✔
1009
                                                , permissions.MayLock
9✔
1010
                                                , permissions.MayWrite
9✔
1011
                                                ,
9✔
1012
                                });
9✔
1013

1014
                // Complete the transaction
1015
                dba.CommitTransaction();
9✔
1016

1017
                // Get datetime of request
1018
                var date = (await dba.QueryAsync<DateTime>(
9✔
1019
                                GetEditionEditorRequestDate.GetQuery
9✔
1020
                                , new { editorInfo.Token })).AsList();
9✔
1021

1022
                if (date.Count != 1)
9!
1023
                {
×
1024
                        throw new StandardExceptions.DataNotWrittenException("generate edition share request");
×
1025
                }
1026

1027
                editorInfo.Date = date.FirstOrDefault();
9✔
1028

1029
                // Return the results
1030
                return editorInfo;
9✔
1031
        }
9✔
1032

1033
        public async Task<DetailedEditionPermission> AddEditionEditorAsync(string token, uint userId)
1034
        {
7✔
1035
                DetailedEditionPermission editorEditionPermission;
1036

1037
                await dba.BeginTransactionAsync();
7✔
1038

1039
                var editorEditionPermissions = (await dba.QueryAsync<DetailedEditionPermission>(
7✔
1040
                                FindEditionEditorRequestByToken.GetQuery
7✔
1041
                                , new
7✔
1042
                                {
7✔
1043
                                                Token = token
7✔
1044
                                                , EditorUserId = userId
7✔
1045
                                                ,
7✔
1046
                                })).AsList();
7✔
1047

1048
                // Make sure the token exists
1049
                if (!editorEditionPermissions.Any())
7!
1050
                        throw new StandardExceptions.DataNotFoundException("token", token);
×
1051

1052
                editorEditionPermission = editorEditionPermissions.First();
7✔
1053

1054
                editorEditionPermission.MayRead = true; // Invited editors always have read access
7✔
1055

1056
                // Check if the editor already exists, don't attempt to re-add
1057
                if ((await _getEditionEditors(editorEditionPermission.EditionId)).Any(x => x.Email
14!
1058
                                                                                                                                                                   == editorEditionPermission
14✔
1059
                                                                                                                                                                                   .Email))
14✔
1060
                        throw new StandardExceptions.ConflictingDataException("editor email");
×
1061

1062
                // Add the editor
1063
                var editorUpdateExecution = await dba.ExecuteAsync(
7✔
1064
                                CreateDetailedEditionEditorQuery.GetQuery
7✔
1065
                                , new
7✔
1066
                                {
7✔
1067
                                                editorEditionPermission.EditionId
7✔
1068
                                                , editorEditionPermission.Email
7✔
1069
                                                , editorEditionPermission.MayRead
7✔
1070
                                                , editorEditionPermission.MayWrite
7✔
1071
                                                , editorEditionPermission.MayLock
7✔
1072
                                                , editorEditionPermission.IsAdmin
7✔
1073
                                                ,
7✔
1074
                                });
7✔
1075

1076
                if (editorUpdateExecution != 1)
7!
1077
                {
×
1078
                        throw new StandardExceptions.DataNotWrittenException(
×
1079
                                        $"update permissions for {editorEditionPermission.Email}");
×
1080
                }
1081

1082
                // Delete unneeded database entries
1083
                await dba.ExecuteAsync(
7✔
1084
                                DeleteEditionEditorRequest.GetQuery
7✔
1085
                                , new
7✔
1086
                                {
7✔
1087
                                                Token = new Guid(token)
7✔
1088
                                                , EditorUserId = userId
7✔
1089
                                                ,
7✔
1090
                                });
7✔
1091

1092
                await dba.ExecuteAsync(
7✔
1093
                                DeleteUserEmailTokenQuery.GetTokenQuery
7✔
1094
                                , new
7✔
1095
                                {
7✔
1096
                                                Tokens = new List<Guid> { new(token) }
7✔
1097
                                                , Type = CreateUserEmailTokenQuery.EditorInvite
7✔
1098
                                                ,
7✔
1099
                                });
7✔
1100

1101
                dba.CommitTransaction();
7✔
1102

1103
                // Return the results
1104
                return editorEditionPermission;
7✔
1105
        }
7✔
1106

1107
        /// <summary>
1108
        ///  Requests a list of editor requests made by the user, which have not yet been accepted
1109
        /// </summary>
1110
        /// <param name="userId">Id of the admin who has issued the request for a user to become an editor</param>
1111
        /// <returns></returns>
1112
        public async Task<List<DetailedEditorRequestPermissions>>
1113
                        GetOutstandingEditionEditorRequestsAsync(uint userId)
1114
                => (await dba.QueryAsync<DetailedEditorRequestPermissions>(
1✔
1115
                                FindEditionEditorRequestByAdminId.GetQuery
1✔
1116
                                , new { AdminUserId = userId })).ToList();
1✔
1117

1118
        /// <summary>
1119
        ///  Requests a list of invitations to become an editor, which have been sent to the user
1120
        /// </summary>
1121
        /// <param name="userId">Id of the user who has been invited to become editor</param>
1122
        /// <returns></returns>
1123
        public async Task<List<DetailedEditorInvitationPermissions>>
1124
                        GetOutstandingEditionEditorInvitationsAsync(uint userId)
1125
                => (await dba.QueryAsync<DetailedEditorInvitationPermissions>(
1✔
1126
                                FindEditionEditorRequestByEditorId.GetQuery
1✔
1127
                                , new { EditorUserId = userId })).ToList();
1✔
1128

1129
        public async Task<Permission> ChangeEditionEditorRightsAsync(
1130
                        UserInfo editionUser
1131
                        , string editorEmail
1132
                        , bool?  mayRead
1133
                        , bool?  mayWrite
1134
                        , bool?  mayLock
1135
                        , bool?  isAdmin)
1136
        {
7✔
1137
                // Make sure requesting user is admin when raising access, only and edition admin may perform this action
1138
                if (((mayRead ?? false) || (mayWrite ?? false) || (mayLock ?? false) || (isAdmin ?? false))
7!
1139
                        && !editionUser.IsAdmin)
7✔
1140
                        throw new StandardExceptions.NoAdminPermissionsException(editionUser);
×
1141

1142
                // Check if the editor exists
1143
                var editors = await _getEditionEditors(editionUser.EditionId.Value);
7✔
1144

1145
                var currentEditorSettingsList = editors.Where(x => x.Email == editorEmail).ToList();
20✔
1146

1147
                if (currentEditorSettingsList.Count != 1) // There should be only 1 record
7!
1148
                {
×
1149
                        throw new StandardExceptions.DataNotFoundException(
×
1150
                                        "editor email"
×
1151
                                        , editionUser.EditionId.ToString()
×
1152
                                        , "edition_editors");
×
1153
                }
1154

1155
                // Set the new permissions object by coalescing the new settings with those already existing
1156
                var currentEditorSettings = currentEditorSettingsList.First();
7✔
1157

1158
                var permissions = new Permission
7!
1159
                {
7✔
1160
                                MayRead = mayRead ?? currentEditorSettings.MayRead
7✔
1161
                                , MayWrite = mayWrite ?? currentEditorSettings.MayWrite
7✔
1162
                                , MayLock = mayLock ?? currentEditorSettings.MayLock
7✔
1163
                                , IsAdmin = isAdmin ?? currentEditorSettings.IsAdmin
7✔
1164
                                ,
7✔
1165
                };
7✔
1166

1167
                // Make sure we are not removing an admin's read access (that is not allowed)
1168
                if (permissions.IsAdmin
7✔
1169
                        && !permissions.MayRead)
7✔
1170
                {
1✔
1171
                        throw new StandardExceptions.InputDataRuleViolationException(
1✔
1172
                                        "read rights may not be revoked for an edition admin");
1✔
1173
                }
1174

1175
                // Make sure that we are not revoking editor's read access when editor still has write access
1176
                if (permissions.MayWrite
6✔
1177
                        && !permissions.MayRead)
6✔
1178
                {
1✔
1179
                        throw new StandardExceptions.InputDataRuleViolationException(
1✔
1180
                                        "read rights may not be revoked for an editor with write rights");
1✔
1181
                }
1182

1183
                // If the last admin is giving up admin rights, return error message with token for complete delete
1184
                if (!editors.Any(x => ((x.Email == editorEmail) && permissions.IsAdmin)
11!
1185
                                                          || ((x.Email != editorEmail) && x.IsAdmin)))
11✔
1186
                {
2✔
1187
                        throw new StandardExceptions.InputDataRuleViolationException(
2✔
1188
                                        $@"an edition must have at least one admin.
2✔
1189
Please give admin status to another editor before relinquishing admin status for the current user or deleting the edition.
2✔
1190
An admin may delete the edition for all editors with the request DELETE /v1/editions/{
2✔
1191
        editionUser.EditionId.ToString()
2✔
1192
}.");
2✔
1193
                }
1194

1195
                // Perform the update
1196
                var editorUpdateExecution = await dba.ExecuteAsync(
3✔
1197
                                UpdateEditionEditorPermissionsQuery.GetQuery
3✔
1198
                                , new
3✔
1199
                                {
3✔
1200
                                                editionUser.EditionId
3✔
1201
                                                , Email = editorEmail
3✔
1202
                                                , permissions.MayRead
3✔
1203
                                                , permissions.MayWrite
3✔
1204
                                                , permissions.MayLock
3✔
1205
                                                , permissions.IsAdmin
3✔
1206
                                                ,
3✔
1207
                                });
3✔
1208

1209
                if (editorUpdateExecution != 1)
3!
1210
                {
×
1211
                        throw new StandardExceptions.DataNotWrittenException(
×
1212
                                        $"update permissions for {editorEmail}");
×
1213
                }
1214

1215
                // Return the results
1216
                return permissions;
3✔
1217

1218
                // In the future should we email the editor about their change in status?
1219
        }
3✔
1220

1221
        /// <summary>
1222
        ///  Gets the user id's of each editor working on an edition.  This is useful for
1223
        ///  collecting the user id's to which a SignalR message must be broadcast.  This
1224
        ///  data is not intended to be made public to any clients.
1225
        /// </summary>
1226
        /// <param name="editionUser">User object requesting the delete</param>
1227
        /// <returns></returns>
1228
        public async Task<List<uint>> GetEditionEditorUserIdsAsync(UserInfo editionUser)
1229
                => (await dba.QueryAsync<uint>(
157✔
1230
                                EditionEditorUserIds.GetQuery
157✔
1231
                                , new
157✔
1232
                                {
157✔
1233
                                                editionUser.EditionId
157✔
1234
                                                , UserId = editionUser.userId
157✔
1235
                                                ,
157✔
1236
                                })).ToList();
157✔
1237

1238
        public async Task<IEnumerable<Edition>> GetManuscriptEditions(uint? userId, uint manuscriptId)
1239
        {
19✔
1240
                var editions = new List<Edition>();
19✔
1241
                Edition lastEdition;
1242

1243
                await dba.QueryAsync<EditionListQuery.Result, EditorWithPermissions, Edition>(
19✔
1244
                                EditionListQuery.GetQuery(userId.HasValue, false, searchByManuscript: true)
19✔
1245
                                , (editionGroup, editor) =>
19✔
1246
                                  {
44✔
1247
                                          // Set the copyrights for the previous, and now complete, edition before making the new one
19✔
1248
                                          if ((editions.LastOrDefault()?.EditionId != null)
44!
1249
                                                  && (editions.LastOrDefault()?.EditionId != editionGroup.EditionId))
44✔
1250
                                          {
16✔
1251
                                                  lastEdition = editions.Last();
16✔
1252

19✔
1253
                                                  lastEdition.Copyright = Licence.printLicence(
16!
1254
                                                                  lastEdition.CopyrightHolder
16✔
1255
                                                                  , string.IsNullOrEmpty(lastEdition.Collaborators)
16✔
1256
                                                                                  ? string.Join(
16✔
1257
                                                                                                  ", "
16✔
1258
                                                                                                  , lastEdition.Editors.Select(y =>
16✔
1259
                                                                                                                                                           {
×
1260
                                                                                                                                                                   if ((y.Forename
×
1261
                                                                                                                                                                                == null)
×
1262
                                                                                                                                                                           && (y.Surname
×
1263
                                                                                                                                                                                   == null))
×
1264
                                                                                                                                                                   {
×
1265
                                                                                                                                                                           return y
×
1266
                                                                                                                                                                                           .EditorEmail;
×
1267
                                                                                                                                                                   }
16✔
1268

16✔
1269
                                                                                                                                                                   return $@"{
×
1270
                                                                                                                                                                           y.Forename
×
1271
                                                                                                                                                                   } {
×
1272
                                                                                                                                                                           y.Surname
×
1273
                                                                                                                                                                   }".Trim();
×
1274
                                                                                                                                                           }))
×
1275
                                                                                  : lastEdition.Collaborators);
16✔
1276
                                          }
16✔
1277

19✔
1278
                                          if ((editions.LastOrDefault()?.EditionId == null)
44!
1279
                                                  || (editions.LastOrDefault()?.EditionId != editionGroup.EditionId))
44✔
1280
                                          {
35✔
1281
                                                  // Now start building the new edition
19✔
1282
                                                  lastEdition = new Edition
35✔
1283
                                                  {
35✔
1284
                                                                  Name = editionGroup.Name
35✔
1285
                                                                  , Width = editionGroup.Width
35✔
1286
                                                                  , Height = editionGroup.Height
35✔
1287
                                                                  , XOrigin = editionGroup.XOrigin
35✔
1288
                                                                  , YOrigin = editionGroup.YOrigin
35✔
1289
                                                                  , PPI = editionGroup.PPI
35✔
1290
                                                                  , ManuscriptMetricsEditor = editionGroup.ManuscriptMetricsEditor
35✔
1291
                                                                  , Collaborators = editionGroup.Collaborators
35✔
1292
                                                                  , Copyright = null
35✔
1293
                                                                  , //Licence.printLicence(editionGroup.CopyrightHolder, editionGroup.Collaborators),
35✔
1294
                                                                  CopyrightHolder = editionGroup.CopyrightHolder
35✔
1295
                                                                  , EditionDataEditorId = editionGroup.EditionDataEditorId
35✔
1296
                                                                  , EditionId = editionGroup.EditionId
35✔
1297
                                                                  , IsPublic = editionGroup.IsPublic
35✔
1298
                                                                  , PublicationDate = editionGroup.PublicationDate
35✔
1299
                                                                  , LastEdit = editionGroup.LastEdit
35✔
1300
                                                                  , Locked = editionGroup.Locked
35✔
1301
                                                                  , Owner =
35✔
1302
                                                                                  new User
35✔
1303
                                                                                  {
35✔
1304
                                                                                                  Email = editionGroup.CurrentEmail
35✔
1305
                                                                                                  , UserId = editionGroup.CurrentUserId
35✔
1306
                                                                                                  ,
35✔
1307
                                                                                  }
35✔
1308
                                                                  , Permission =
35✔
1309
                                                                                  new Permission
35✔
1310
                                                                                  {
35✔
1311
                                                                                                  IsAdmin = editionGroup.CurrentIsAdmin
35✔
1312
                                                                                                  , MayLock = editionGroup.CurrentMayLock
35✔
1313
                                                                                                  , MayWrite = editionGroup.CurrentMayWrite
35✔
1314
                                                                                                  , MayRead = editionGroup.CurrentMayRead
35✔
1315
                                                                                                  ,
35✔
1316
                                                                                  }
35✔
1317
                                                                  , Thumbnail = editionGroup.Thumbnail
35✔
1318
                                                                  , ManuscriptId = editionGroup.ManuscriptId
35✔
1319
                                                                  , Editors = new List<EditorWithPermissions>()
35✔
1320
                                                                  ,
35✔
1321
                                                  };
35✔
1322

19✔
1323
                                                  editions.Add(lastEdition);
35✔
1324
                                          }
35✔
1325

19✔
1326
                                          // Add the new editor to this edition
19✔
1327
                                          editions.Last().Editors.Add(editor);
44✔
1328

19✔
1329
                                          return editions.Last();
44✔
1330
                                  }
44✔
1331
                                , new
19✔
1332
                                {
19✔
1333
                                                UserId = userId
19✔
1334
                                                , ManuscriptId = manuscriptId
19✔
1335
                                                ,
19✔
1336
                                }
19✔
1337
                                , splitOn: "EditorId");
19✔
1338

1339
                if (editions.Count <= 0)
19!
1340
                        return editions;
×
1341

1342
                {
19✔
1343
                        lastEdition = editions.Last();
19✔
1344

1345
                        lastEdition.Copyright = Licence.printLicence(
19✔
1346
                                        lastEdition.CopyrightHolder
19✔
1347
                                        , string.IsNullOrEmpty(lastEdition.Collaborators)
19✔
1348
                                                        ? string.Join(
19✔
1349
                                                                        ", "
19✔
1350
                                                                        , lastEdition.Editors.Select(y =>
19✔
1351
                                                                                                                                 {
25✔
1352
                                                                                                                                         if ((y.Forename == null)
25!
1353
                                                                                                                                                 && (y.Surname == null))
25✔
1354
                                                                                                                                         {
25✔
1355
                                                                                                                                                 return y.EditorEmail;
25✔
1356
                                                                                                                                         }
19✔
1357

19✔
1358
                                                                                                                                         return $@"{
×
1359
                                                                                                                                                 y.Forename
×
1360
                                                                                                                                         } {
×
1361
                                                                                                                                                 y.Surname
×
1362
                                                                                                                                         }".Trim();
×
1363
                                                                                                                                 }))
25✔
1364
                                                        : lastEdition.Collaborators);
19✔
1365
                }
19✔
1366

1367
                return editions;
19✔
1368
        }
19✔
1369

1370
        public async Task<uint?> GetEditionManuscriptIdAsync(uint editionId)
1371
                => await dba.QuerySingleOrDefaultAsync<uint?>(
17✔
1372
                                EditionManuscriptIdQuery.GetQuery()
17✔
1373
                                , new { EditionId = editionId });
17✔
1374

1375
        public async Task<List<LetterShape>> GetEditionScriptCollectionAsync(UserInfo editonUser)
1376
                => (await dba.QueryAsync<LetterShape>(
2✔
1377
                                EditionScriptQuery.GetQuery
2✔
1378
                                , new
2✔
1379
                                {
2✔
1380
                                                editonUser.EditionId
2✔
1381
                                                , UserId = editonUser.userId ?? 0
2✔
1382
                                                ,
2✔
1383
                                })).ToList();
2✔
1384

1385
        public async Task<List<ScriptTextFragment>> GetEditionScriptLines(UserInfo editionUser)
1386
        {
4✔
1387
                // Placeholders for query mapping
1388
                ScriptTextFragment lastScriptTextFragment = null;
4✔
1389
                ScriptLine lastScriptLine = null;
4✔
1390
                ScriptArtefactCharacters lastScriptArtefactCharacters = null;
4✔
1391
                Character lastCharacters = null;
4✔
1392
                SpatialRoi lastSpatialRoi = null;
4✔
1393
                CharacterAttribute lastCharacterAttribute = null;
4✔
1394
                CharacterStreamPosition lastCharacterStreamPosition = null;
4✔
1395

1396
                var scriptLines = await dba.QueryAsync(
4✔
1397
                                EditionScriptLines.GetQuery
4✔
1398
                                , new[]
4✔
1399
                                {
4✔
1400
                                                typeof(ScriptTextFragment)
4✔
1401
                                                , typeof(ScriptLine)
4✔
1402
                                                , typeof(ScriptArtefactCharacters)
4✔
1403
                                                , typeof(Character)
4✔
1404
                                                , typeof(SpatialRoi)
4✔
1405
                                                , typeof(CharacterAttribute)
4✔
1406
                                                , typeof(CharacterStreamPosition)
4✔
1407
                                                ,
4✔
1408
                                }
4✔
1409
                                , objects =>
4✔
1410
                                  {
8✔
1411
                                          // Collect the mapped objects
4✔
1412
                                          if (!(objects[0] is ScriptTextFragment scriptTextFragment))
8!
1413
                                                  return null;
×
1414

4✔
1415
                                          if (!(objects[1] is ScriptLine scriptLine))
8!
1416
                                                  return null;
×
1417

4✔
1418
                                          if (!(objects[2] is ScriptArtefactCharacters scriptArtefactCharacters))
8!
1419
                                                  return null;
×
1420

4✔
1421
                                          if (!(objects[3] is Character character))
8!
1422
                                                  return null;
×
1423

4✔
1424
                                          if (!(objects[4] is SpatialRoi spatialRoi))
8!
1425
                                                  return null;
×
1426

4✔
1427
                                          if (!(objects[5] is CharacterAttribute characterAttribute))
8!
1428
                                                  return null;
×
1429

4✔
1430
                                          if (!(objects[6] is CharacterStreamPosition characterStreamPosition))
8!
1431
                                                  return null;
×
1432

4✔
1433
                                          // Construct the nestings
4✔
1434
                                          var newTextFragment = scriptTextFragment.TextFragmentId
8✔
1435
                                                                                        != lastScriptTextFragment?.TextFragmentId;
8✔
1436

4✔
1437
                                          if (newTextFragment)
8✔
1438
                                          {
4✔
1439
                                                  lastScriptTextFragment = scriptTextFragment;
4✔
1440

4✔
1441
                                                  lastScriptTextFragment.Lines = new List<ScriptLine>();
4✔
1442
                                          }
4✔
1443

4✔
1444
                                          if (scriptLine.LineId != lastScriptLine?.LineId)
8✔
1445
                                          {
4✔
1446
                                                  lastScriptLine = scriptLine;
4✔
1447

4✔
1448
                                                  lastScriptLine.Artefacts = new List<ScriptArtefactCharacters>();
4✔
1449

4✔
1450
                                                  lastScriptTextFragment.Lines.Add(lastScriptLine);
4✔
1451
                                          }
4✔
1452

4✔
1453
                                          if (scriptArtefactCharacters.ArtefactId
8✔
1454
                                                  != lastScriptArtefactCharacters?.ArtefactId)
8✔
1455
                                          {
4✔
1456
                                                  lastScriptArtefactCharacters = scriptArtefactCharacters;
4✔
1457

4✔
1458
                                                  lastScriptArtefactCharacters.Characters = new List<Character>();
4✔
1459

4✔
1460
                                                  lastScriptLine.Artefacts.Add(lastScriptArtefactCharacters);
4✔
1461
                                          }
4✔
1462

4✔
1463
                                          if (character.SignInterpretationId != lastCharacters?.SignInterpretationId)
8✔
1464
                                          {
4✔
1465
                                                  lastCharacters = character;
4✔
1466

4✔
1467
                                                  lastCharacters.Attributes = new List<CharacterAttribute>();
4✔
1468

4✔
1469
                                                  lastCharacters.Rois = new List<SpatialRoi>();
4✔
1470

4✔
1471
                                                  lastCharacters.NextCharacters = new List<CharacterStreamPosition>();
4✔
1472

4✔
1473
                                                  lastScriptArtefactCharacters.Characters.Add(lastCharacters);
4✔
1474
                                          }
4✔
1475

4✔
1476
                                          if (spatialRoi.SignInterpretationRoiId
8✔
1477
                                                  != lastSpatialRoi?.SignInterpretationRoiId)
8✔
1478
                                          {
8✔
1479
                                                  lastSpatialRoi = spatialRoi;
8✔
1480

4✔
1481
                                                  lastCharacters.Rois.Add(lastSpatialRoi);
8✔
1482
                                          }
8✔
1483

4✔
1484
                                          if (characterAttribute.SignInterpretationAttributeId
8✔
1485
                                                  != lastCharacterAttribute?.SignInterpretationAttributeId)
8✔
1486
                                          {
4✔
1487
                                                  lastCharacterAttribute = characterAttribute;
4✔
1488

4✔
1489
                                                  lastCharacters.Attributes.Add(lastCharacterAttribute);
4✔
1490
                                          }
4✔
1491

4✔
1492
                                          if (characterStreamPosition.PositionInStreamId
8✔
1493
                                                  == lastCharacterStreamPosition?.PositionInStreamId)
8✔
1494
                                                  return scriptTextFragment;
4✔
1495

4✔
1496
                                          lastCharacterStreamPosition = characterStreamPosition;
4✔
1497

4✔
1498
                                          lastCharacters.NextCharacters.Add(lastCharacterStreamPosition);
4✔
1499

4✔
1500
                                          return scriptTextFragment;
4✔
1501
                                  }
8✔
1502
                                , new
4✔
1503
                                {
4✔
1504
                                                editionUser.EditionId
4✔
1505
                                                , UserId = editionUser.userId
4✔
1506
                                                ,
4✔
1507
                                }
4✔
1508
                                , splitOn:
4✔
1509
                                "LineId,ArtefactId,SignInterpretationId,SignInterpretationRoiId,SignInterpretationAttributeId,PositionInStreamId");
4✔
1510

1511
                return scriptLines.Where(x => x != null).ToList();
12✔
1512
        }
4✔
1513

1514
        public async Task<EditionMetadata> GetEditionMetadata(UserInfo editionUser)
1515
        {
×
1516
                try
1517
                {
×
1518
                        var metadata = await dba.QueryFirstAsync<EditionMetadata>(
×
1519
                                        GetManuscriptMetadataQuery.GetQuery
×
1520
                                        , new { editionUser.EditionId });
×
1521

1522
                        return metadata;
×
1523
                }
1524

1525
                catch (InvalidOperationException) { }
×
1526

1527
                return new EditionMetadata();
×
1528
        }
×
1529

1530
        /// <summary>
1531
        ///  This method performs a full wipe of an edition's data. The information for the edition
1532
        ///  remains but the association with this edition is deleted. This method is intended
1533
        ///  for system admins to use.
1534
        /// </summary>
1535
        /// <param name="editionUser">User details for the edition to be deleted</param>
1536
        /// <returns></returns>
1537
        private async Task _fullEditionDelete(UserInfo editionUser)
1538
        {
×
1539
                // Remove write permissions from all editors, so they cannot make any changes while the delete proceeds
1540
                var editors = await _getEditionEditors(editionUser.EditionId.Value);
×
1541

1542
                foreach (var editor in editors)
×
1543
                {
×
1544
                        await ChangeEditionEditorRightsAsync(
×
1545
                                        editionUser
×
1546
                                        , editor.Email
×
1547
                                        , editor.MayRead
×
1548
                                        , false
×
1549
                                        , editor.MayLock
×
1550
                                        , editor.IsAdmin);
×
1551
                }
×
1552

1553
                // Note: I had wrapped the following in a transaction, but this has the problem that it can lockup every
1554
                // *_owner table in the entire database for a significant amount of time (sometimes 1000's of rows will be
1555
                // deleted from a single table). So I am doing it now without any transaction and with some retry
1556
                // logic.  What this means is that a delete might be partially carried out and return with an error,
1557
                // in which case the user will need to try again. This is not too worrisome since an inconsistent state
1558
                // for a deleted edition is not a cause for user concern (the users only care about the edition
1559
                // becoming unusable, not whether any data was left behind). It is a concern for those maintaining the
1560
                // database, and we should discuss what might be done for that.  We could check for this and other things
1561
                // with some "health check" services.
1562
                // Dynamically get all tables that can be part of an edition, that way we don't worry about
1563
                // this breaking due to future updates.
1564
                var dataTables = await dba.QueryAsync<OwnerTables.Result>(OwnerTables.GetQuery);
×
1565

1566
                // Loop over every table and remove every entry with the requested editionId
1567
                // Each individual delete can be async and happen concurrently
1568
                foreach (var dataTable in dataTables)
×
1569
                        await DeleteDataFromOwnerTable(dba, dataTable.TableName, editionUser);
×
1570
        }
×
1571

1572
        private static async Task DeleteDataFromOwnerTable(
1573
                        IDatabaseAccessor dba
1574
                        , string          tableName
1575
                        , UserInfo        editionUser)
1576
        {
×
1577
                await dba.ExecuteAsync(
×
1578
                                DeleteEditionFromTable.GetQuery(tableName)
×
1579
                                , new
×
1580
                                {
×
1581
                                                editionUser.EditionId
×
1582
                                                , UserId = editionUser.userId
×
1583
                                                ,
×
1584
                                });
×
1585
        }
×
1586

1587
        private async Task<List<EditorPermissions>> _getEditionEditors(uint editionId)
1588
                => (await dba.QueryAsync<EditorPermissions>(
23✔
1589
                                GetEditionEditorsWithPermissionsQuery.GetQuery
23✔
1590
                                , new { EditionId = editionId })).ToList();
23✔
1591
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc