This repository has been archived by the owner on Aug 2, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 91
/
Copy pathInsertOperation.cs
102 lines (94 loc) · 4.64 KB
/
InsertOperation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Copyright (c) Microsoft Corporation. All Rights Reserved.
// Licensed under the MIT License.
using Newtonsoft.Json.Linq;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Datasync.Client.Offline.Queue
{
/// <summary>
/// Representation of an insert operation within the operations queue.
/// </summary>
internal class InsertOperation : TableOperation
{
/// <summary>
/// Creates a new <see cref="InsertOperation"/> object.
/// </summary>
/// <param name="tableName">The name of the table that contains the affected item.</param>
/// <param name="itemId">The ID of the affected item.</param>
internal InsertOperation(string tableName, string itemId)
: base(TableOperationKind.Insert, tableName, itemId)
{
}
/// <summary>
/// Collapse this operation with a new operation by cancellation of either operation.
/// </summary>
/// <param name="newOperation">The new operation.</param>
public override void CollapseOperation(TableOperation newOperation)
{
if (newOperation.ItemId != ItemId)
{
throw new ArgumentException("ItemId does not match", nameof(newOperation));
}
// An insert followed by a delete is a no-op, so cancel both operations.
if (newOperation is DeleteOperation)
{
Cancel();
newOperation.Cancel();
}
// An insert followed by an update is still an insert, so cancel the update.
if (newOperation is UpdateOperation)
{
Update();
newOperation.Cancel();
}
}
/// <summary>
/// Executes the operation on the offline store.
/// </summary>
/// <param name="store">The offline store.</param>
/// <param name="item">The item to use for the store operation.</param>
/// <returns>A task that completes when the store operation is completed.</returns>
public override async Task ExecuteOperationOnOfflineStoreAsync(IOfflineStore store, JObject item, CancellationToken cancellationToken = default)
{
var existingItem = await store.GetItemAsync(TableName, ItemId, cancellationToken).ConfigureAwait(false);
if (existingItem != null)
{
throw new OfflineStoreException("Insert failed - the item already exists in the offline store");
}
await store.UpsertAsync(TableName, new[] { item }, false, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Executes the operation against the remote table.
/// </summary>
/// <param name="table">The <see cref="IRemoteTable"/> to use for communication with the backend.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe.</param>
/// <returns>A task that returns the operation result when complete.</returns>
protected override Task<JToken> ExecuteRemoteOperationAsync(IRemoteTable table, CancellationToken cancellationToken)
=> table.InsertItemAsync(Item, cancellationToken);
/// <summary>
/// Validates that the operation can collapse with a new operation.
/// </summary>
/// <param name="newOperation">The new operation.</param>
/// <exception cref="InvalidOperationException">when the operation cannot collapse with the new operation.</exception>
public override void ValidateOperationCanCollapse(TableOperation newOperation)
{
if (newOperation.ItemId != ItemId)
{
throw new ArgumentException($"Cannot collapse insert operation '{Id}' with '{newOperation.Id}' - Item IDs do not match", nameof(newOperation));
}
// Insert followed by an Insert is not allowed.
if (newOperation is InsertOperation)
{
throw new InvalidOperationException($"An insert operation on item '{ItemId}' already exists in the operations queue.");
}
// Insert followed by Delete is allowed, but only if the Insert has not been tried yet.
// If it has been tried, it may not have completed successfully, so we have to ensure
// a consistent offline state.
if (newOperation is DeleteOperation && State != TableOperationState.Pending)
{
throw new InvalidOperationException("Cannot process deletion because there is an in-progress insert operation. Complete a 'Push' operation first.");
}
}
}
}