Skip to content

Commit 4a6ffb4

Browse files
Merge pull request #177 from matteobortolazzo/dev
v3.3.0
2 parents 91946bc + b5cdd62 commit 4a6ffb4

File tree

14 files changed

+391
-36
lines changed

14 files changed

+391
-36
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
# 3.3.0 (2022-10-20)
2+
3+
## Features
4+
5+
* **Bulk Delete**: Adds support to replication ([#171](https://github.com/matteobortolazzo/couchdb-net/issues/171))
6+
* **Revision Support**: Support for revisions in add and update ([#170](https://github.com/matteobortolazzo/couchdb-net/pull/170))
7+
8+
## Bug Fixes
9+
10+
* **Replication**: Added replication methods in `ICouchDatabase` interface ([#173](https://github.com/matteobortolazzo/couchdb-net/pull/173))
11+
* **Document ID**: Support IDs with special characters ([#172](https://github.com/matteobortolazzo/couchdb-net/pull/172))
12+
*
113
# 3.2.0 (2022-07-03)
214

315
## Features

LATEST_CHANGE.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
## Features
22

3-
* **Replication**: Adds support to replication ([#151](https://github.com/matteobortolazzo/couchdb-net/pull/151))
4-
* **Attachments**: Adds DownloadAttachmentAsStreamAsync ([#152](https://github.com/matteobortolazzo/couchdb-net/pull/152))
5-
* **IsMatch**: Support multiline regex ([#161](https://github.com/matteobortolazzo/couchdb-net/pull/161))
3+
* **Bulk Delete**: Adds support to replication ([#171](https://github.com/matteobortolazzo/couchdb-net/issues/171))
4+
* **Revision Support**: Support for revisions in add and update ([#170](https://github.com/matteobortolazzo/couchdb-net/pull/170))
65

76
## Bug Fixes
87

9-
* **ElementAt**: Fixes query on .NET 6. ([#156](https://github.com/matteobortolazzo/couchdb-net/pull/156))
10-
* **Attachments**: Fixes attachments in FindAsync. ([#159](https://github.com/matteobortolazzo/couchdb-net/pull/159))
11-
* **Attachments**: Fixes attachments uploads ([#159](https://github.com/matteobortolazzo/couchdb-net/pull/159))
12-
* **Attachments**: Fixes Bad Request on attachment upload. ([#164](https://github.com/matteobortolazzo/couchdb-net/pull/164))
13-
* **GetInfoAsync**: Fixed 32-bit integer overflow. ([#165](https://github.com/matteobortolazzo/couchdb-net/pull/165))
8+
* **Replication**: Added replication methods in `ICouchDatabase` interface ([#173](https://github.com/matteobortolazzo/couchdb-net/pull/173))
9+
* **Document ID**: Support IDs with special characters ([#172](https://github.com/matteobortolazzo/couchdb-net/pull/172))

README.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ var list = await rebels.QueryAsync(someMangoJson);
277277
var list = await rebels.QueryAsync(someMangoObject);
278278
// Bulk
279279
await rebels.AddOrUpdateRangeAsync(moreRebels);
280+
await rebels.DeleteRangeAsync(ids);
281+
await rebels.DeleteRangeAsync(moreRebels);
280282
// Utils
281283
await rebels.CompactAsync();
282284
var info = await rebels.GetInfoAsync();
@@ -389,6 +391,17 @@ string downloadFilePath = await rebels.DownloadAttachment(attachment, downloadFo
389391
Stream responseStream = await rebels.DownloadAttachmentAsStreamAsync(attachment);
390392
```
391393

394+
## Revisions
395+
396+
The options for `FindAsync(..)` and `AddOrUpdateAsync(..)` support passing revision:
397+
398+
```csharp
399+
await _rebels.FindAsync("1", new FindOptions { Rev = "1-xxx" });
400+
await _rebels.AddOrUpdateAsync(r, new AddOrUpdateOptions { Rev = "1-xxx" });
401+
```
402+
403+
For attachements revisions are supported by `CouchAttachment` class which is passing `DocumentRev` to `DownloadAttachmentAsync(..)` and `DownloadAttachmentAsStreamAsync(..)`.
404+
392405
## DB Changes Feed
393406

394407
The following *feed modes* are supported: `normal`, `longpool` and `continuous`.
@@ -731,14 +744,16 @@ Also, the configurator has `ConfigureFlurlClient` to set custom HTTP client opti
731744

732745
## Contributors
733746

734-
[Ben Origas](https://github.com/borigas): Features, ideas and tests like SSL custom validation, multi queryable, async deadlock, cookie authenication and many others.
747+
[Ben Origas](https://github.com/borigas): Features, ideas and tests like SSL custom validation, multi queryable, async deadlock, cookie authentication and many others.
735748

736749
[n9](https://github.com/n9): Proxy authentication, some bug fixes, suggestions and the great feedback on the changes feed feature!
737750

738751
[Marc](https://github.com/bender-ristone): NullValueHandling, bug fixes and suggestions!
739752

740753
[Panos](https://github.com/panoukos41): Help with Views and Table splitting.
741754

742-
[Benjamin Höglinger-Stelzer](https://github.com/nefarius), [mwasson74](https://github.com/mwasson74), [Emre ÇAĞLAR](https://github.com/emrecaglar): Attachments improvments and fixes.
755+
[Benjamin Höglinger-Stelzer](https://github.com/nefarius), [mwasson74](https://github.com/mwasson74), [Emre ÇAĞLAR](https://github.com/emrecaglar): Attachments improvements and fixes.
756+
757+
[Dhiren Sham](https://github.com/dhirensham): Implementing replication.
743758

744-
[Dhiren Sham](https://github.com/dhirensham): Implementing replication.
759+
[Dmitriy Larionov](https://github.com/dmlarionov): Revisions improvements.

src/CouchDB.Driver/CouchDatabase.cs

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
using System.Net;
2727
using System.Text.RegularExpressions;
2828
using CouchDB.Driver.Views;
29+
using CouchDB.Driver.DatabaseApiMethodOptions;
2930

3031
namespace CouchDB.Driver
3132
{
@@ -73,16 +74,20 @@ internal CouchDatabase(IFlurlClient flurlClient, CouchOptions options, QueryCont
7374
#region Find
7475

7576
/// <inheritdoc />
76-
public async Task<TSource?> FindAsync(string docId, bool withConflicts = false,
77-
CancellationToken cancellationToken = default)
77+
public Task<TSource?> FindAsync(string docId, bool withConflicts = false, CancellationToken cancellationToken = default)
78+
=> FindAsync(docId, new FindOptions { Conflicts = withConflicts }, cancellationToken);
79+
80+
/// <inheritdoc />
81+
public async Task<TSource?> FindAsync(string docId, FindOptions options, CancellationToken cancellationToken = default)
7882
{
7983
IFlurlRequest request = NewRequest()
80-
.AppendPathSegment(docId);
84+
.AppendPathSegment(Uri.EscapeDataString(docId));
8185

82-
if (withConflicts)
83-
{
86+
if (options.Conflicts)
8487
request = request.SetQueryParam("conflicts", "true");
85-
}
88+
89+
if (options.Rev != null)
90+
request = request.SetQueryParam("rev", options.Rev);
8691

8792
IFlurlResponse? response = await request
8893
.AllowHttpStatus(HttpStatusCode.NotFound)
@@ -187,22 +192,24 @@ private void InitAttachments(TSource document)
187192
#region Writing
188193

189194
/// <inheritdoc />
190-
public async Task<TSource> AddAsync(TSource document, bool batch = false, CancellationToken cancellationToken = default)
195+
public Task<TSource> AddAsync(TSource document, bool batch = false, CancellationToken cancellationToken = default)
196+
=> AddAsync(document, new AddOptions { Batch = batch }, cancellationToken);
197+
198+
/// <inheritdoc />
199+
public async Task<TSource> AddAsync(TSource document, AddOptions options, CancellationToken cancellationToken = default)
191200
{
192201
Check.NotNull(document, nameof(document));
193202

194203
if (!string.IsNullOrEmpty(document.Id))
195204
{
196-
return await AddOrUpdateAsync(document, batch, cancellationToken)
205+
return await AddOrUpdateAsync(document, new AddOrUpdateOptions { Batch = options.Batch }, cancellationToken)
197206
.ConfigureAwait(false);
198207
}
199208

200209
IFlurlRequest request = NewRequest();
201210

202-
if (batch)
203-
{
211+
if (options.Batch)
204212
request = request.SetQueryParam("batch", "ok");
205-
}
206213

207214
document.SplitDiscriminator = _discriminator;
208215
DocumentSaveResponse response = await request
@@ -219,7 +226,11 @@ await UpdateAttachments(document, cancellationToken)
219226
}
220227

221228
/// <inheritdoc />
222-
public async Task<TSource> AddOrUpdateAsync(TSource document, bool batch = false, CancellationToken cancellationToken = default)
229+
public Task<TSource> AddOrUpdateAsync(TSource document, bool batch = false, CancellationToken cancellationToken = default)
230+
=> AddOrUpdateAsync(document, new AddOrUpdateOptions { Batch = batch }, cancellationToken);
231+
232+
/// <inheritdoc />
233+
public async Task<TSource> AddOrUpdateAsync(TSource document, AddOrUpdateOptions options, CancellationToken cancellationToken = default)
223234
{
224235
Check.NotNull(document, nameof(document));
225236

@@ -229,12 +240,13 @@ public async Task<TSource> AddOrUpdateAsync(TSource document, bool batch = false
229240
}
230241

231242
IFlurlRequest request = NewRequest()
232-
.AppendPathSegment(document.Id);
243+
.AppendPathSegment(Uri.EscapeDataString(document.Id));
233244

234-
if (batch)
235-
{
245+
if (options.Batch)
236246
request = request.SetQueryParam("batch", "ok");
237-
}
247+
248+
if (options.Rev != null)
249+
request = request.SetQueryParam("rev", options.Rev);
238250

239251
document.SplitDiscriminator = _discriminator;
240252
DocumentSaveResponse response = await request
@@ -256,7 +268,7 @@ public async Task RemoveAsync(TSource document, bool batch = false, Cancellation
256268
Check.NotNull(document, nameof(document));
257269

258270
IFlurlRequest request = NewRequest()
259-
.AppendPathSegment(document.Id);
271+
.AppendPathSegment(Uri.EscapeDataString(document.Id));
260272

261273
if (batch)
262274
{
@@ -307,6 +319,35 @@ await UpdateAttachments(document, cancellationToken)
307319
return documents;
308320
}
309321

322+
/// <inheritdoc />
323+
public Task DeleteRangeAsync(IEnumerable<TSource> documents, CancellationToken cancellationToken = default)
324+
{
325+
DocumentId[] docIds = documents.Cast<DocumentId>().ToArray();
326+
return DeleteRangeAsync(docIds, cancellationToken);
327+
}
328+
329+
/// <inheritdoc />
330+
public async Task DeleteRangeAsync(IEnumerable<DocumentId> documentIds, CancellationToken cancellationToken = default)
331+
{
332+
Check.NotNull(documentIds, nameof(documentIds));
333+
334+
var documents = documentIds
335+
.Select(docId => new
336+
{
337+
_id = docId.Id,
338+
_rev = docId.Rev,
339+
_deleted = true
340+
})
341+
.ToArray();
342+
343+
await NewRequest()
344+
.AppendPathSegment("_bulk_docs")
345+
.PostJsonAsync(new { docs = documents }, cancellationToken)
346+
.ReceiveJson<DocumentSaveResponse[]>()
347+
.SendRequestAsync()
348+
.ConfigureAwait(false);
349+
}
350+
310351
/// <inheritdoc />
311352
public async Task EnsureFullCommitAsync(CancellationToken cancellationToken = default)
312353
{
@@ -336,7 +377,7 @@ private async Task UpdateAttachments(TSource document, CancellationToken cancell
336377
new FileStream(attachment.FileInfo.FullName, FileMode.Open));
337378

338379
AttachmentResult response = await NewRequest()
339-
.AppendPathSegment(document.Id)
380+
.AppendPathSegment(Uri.EscapeDataString(document.Id))
340381
.AppendPathSegment(Uri.EscapeUriString(attachment.Name))
341382
.WithHeader("Content-Type", attachment.ContentType)
342383
.WithHeader("If-Match", document.Rev)
@@ -354,8 +395,8 @@ private async Task UpdateAttachments(TSource document, CancellationToken cancell
354395
foreach (CouchAttachment attachment in document.Attachments.GetDeletedAttachments())
355396
{
356397
AttachmentResult response = await NewRequest()
357-
.AppendPathSegment(document.Id)
358-
.AppendPathSegment(attachment.Name)
398+
.AppendPathSegment(Uri.EscapeDataString(document.Id))
399+
.AppendPathSegment(Uri.EscapeDataString(attachment.Name))
359400
.WithHeader("If-Match", document.Rev)
360401
.DeleteAsync(cancellationToken)
361402
.ReceiveJson<AttachmentResult>()
@@ -623,7 +664,7 @@ public async Task<Stream> DownloadAttachmentAsStreamAsync(CouchAttachment attach
623664
}
624665

625666
return await NewRequest()
626-
.AppendPathSegment(attachment.DocumentId)
667+
.AppendPathSegment(Uri.EscapeDataString(attachment.DocumentId))
627668
.AppendPathSegment(Uri.EscapeUriString(attachment.Name))
628669
.WithHeader("If-Match", attachment.DocumentRev)
629670
.GetStreamAsync(cancellationToken)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace CouchDB.Driver.DatabaseApiMethodOptions
6+
{
7+
/// <summary>
8+
/// Options relevant to saving a new document (supported by both PUT /{db}/{docid} and POST /{db}).
9+
/// Check https://docs.couchdb.org/en/stable/api/database/common.html#post--db
10+
/// Check https://docs.couchdb.org/en/stable/api/document/common.html#put--db-docid
11+
/// </summary>
12+
public class AddOptions
13+
{
14+
/// <summary>
15+
/// Stores document in batch mode. Check https://docs.couchdb.org/en/stable/api/database/common.html#api-doc-batch-writes
16+
/// </summary>
17+
public bool Batch { get; set; } = false;
18+
}
19+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace CouchDB.Driver.DatabaseApiMethodOptions
6+
{
7+
/// <summary>
8+
/// Options relevant to saving a document (supported by PUT HTTP-method).
9+
/// Check https://docs.couchdb.org/en/stable/api/document/common.html#put--db-docid
10+
/// </summary>
11+
public class AddOrUpdateOptions
12+
{
13+
/// <summary>
14+
/// Stores document in batch mode. Check https://docs.couchdb.org/en/stable/api/database/common.html#api-doc-batch-writes
15+
/// </summary>
16+
public bool Batch { get; set; } = false;
17+
18+
/// <summary>
19+
/// Document’s revision if updating an existing document.
20+
/// </summary>
21+
public string? Rev { get; set; }
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace CouchDB.Driver.DatabaseApiMethodOptions
6+
{
7+
/// <summary>
8+
/// Options relevant to getting a document (supported by GET HTTP-method).
9+
/// Check https://docs.couchdb.org/en/stable/api/document/common.html#get--db-docid
10+
/// </summary>
11+
public class FindOptions
12+
{
13+
/// <summary>
14+
/// Includes information about conflicts in document. Default is false
15+
/// </summary>
16+
public bool Conflicts { get; set; } = false;
17+
18+
/// <summary>
19+
/// Retrieves document of specified revision. Optional
20+
/// </summary>
21+
public string? Rev { get; set; }
22+
}
23+
}

src/CouchDB.Driver/ICouchClient.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,30 @@ Task<ICouchDatabase<TSource>> GetOrCreateDatabaseAsync<TSource>(int? shards = nu
164164
/// <returns>A task that represents the asynchronous operation. The task result contains the sequence of all active tasks.</returns>
165165
Task<IEnumerable<CouchActiveTask>> GetActiveTasksAsync(CancellationToken cancellationToken = default);
166166

167+
/// <summary>
168+
/// Configures a database replication operation.
169+
/// </summary>
170+
/// <param name="source">Fully qualified source database URL or an object which contains the full URL of the source database with additional parameters like headers.</param>
171+
/// <param name="target">Fully qualified target database URL or an object which contains the full URL of the target database with additional parameters like headers.</param>
172+
/// <param name="replication">An instance of <see cref="CouchReplication"/>.</param>
173+
/// <param name="persistent">Persist the operation to the replication database.</param>
174+
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
175+
/// <returns>Returns True if the operation succeeded, False otherwise.</returns>
176+
Task<bool> ReplicateAsync(string source, string target, CouchReplication? replication = null,
177+
bool persistent = true, CancellationToken cancellationToken = default);
178+
179+
/// <summary>
180+
/// Removes a database replication operation.
181+
/// </summary>
182+
/// <param name="source">Fully qualified source database URL or an object which contains the full URL of the source database with additional parameters like headers.</param>
183+
/// <param name="target">Fully qualified target database URL or an object which contains the full URL of the target database with additional parameters like headers.</param>
184+
/// <param name="replication">An instance of <see cref="CouchReplication"/>.</param>
185+
/// <param name="persistent"></param>
186+
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
187+
/// <returns>Returns True if the operation succeeded, False otherwise.</returns>
188+
Task<bool> RemoveReplicationAsync(string source, string target, CouchReplication? replication = null,
189+
bool persistent = true, CancellationToken cancellationToken = default);
190+
167191
/// <summary>
168192
/// Get the database name for the given type.
169193
/// </summary>

0 commit comments

Comments
 (0)