How can you simulate a conflict in CouchDB without using replication? - couchdb

I'd like to write a unit test for my app that simulates a conflict during replication. Is there a way to simulate a conflict using only a single CouchDB database and server?

I assume you want to get a document containing a conflict in your database, rather than a 409 Conflict response?
So, create a document in the database with a known _id:
$ curl http://localhost:5984/scratch/foo -X PUT -H "Content-Type: application/json" -d '{}'
{"ok":true,"id":"foo","rev":"1-967a00dff5e02add41819138abb3284d"}
Then use the bulk docs API with the all_or_nothing: true option to update the same document with a deliberately bad or no _rev, adding some different document attributes for good measure:
$ curl http://localhost:5984/scratch/_bulk_docs -X POST -H "Content-Type: application/json" -d '{"all_or_nothing": true, "docs": [{"_id": "foo", "abc": 123}]}'
[{"id":"foo","rev":"1-15c813a2b4b312c6915821b01a1986c5"}]
You should then have a conflict in the document:
$ curl http://localhost:5984/scratch/foo?conflicts=true
{"_id":"foo","_rev":"1-967a00dff5e02add41819138abb3284d","_conflicts":["1-15c813a2b4b312c6915821b01a1986c5"]}
You can also perform a normal query with ?new_edits=false as described by CouchDB committer Randall Leeds.
$ curl http://localhost:5984/scratch?new_edits=false -X POST -H "Content-Type: application/json" -d '{"_id": "foo", "abc": 123}'

Googled further after asking the question, and it looks like the answer is to use the all-or-nothing mode of the bulk document API.
http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API
Look near the end of the page.

Just post two documents with the same _id attribute. This creates a conflict since the 2nd doc will not contain the proper _rev attribute. Remember, you need to include the latest _rev attribute in each subsequent post so that CouchDB knows you are up to date.
Also, you can create two databases on the same server and replicate between those.

Related

creating a selector for document's name

I am using CouchDB v3.1.0 and am trying to create a selector for /{db}/_find to find documents named "foobar".
In the docs section 1.3.6.1.1 Selector Basics, there are several mentions of a $title:
"selector": {
"$title": "Live And Let Die"
}
However, when I try to use it like so:
curl -X POST -H "Content-Type: application/json" \
http://my-host-machine:5984/testdb/_find \
-d '{"selector":{"$title":"foobar"}}' --user admin | jq .
The following error message is output:
{
"error": "invalid_operator",
"reason": "Invalid operator: $title"
}
What does work is using the _id field in the selector: '{"selector":{"_id":"foobar"}}'
However, this doesn't feel correct to use a document's unique identifier. My questions:
Does $title work? (Am I just using it incorrectly)
Is _id an appropriate method?
If a name field has a dollar sign ($) prefix, CouchDB interprets it as an operator. $title however isn't a valid operator and the related selector samples in the documentation seem to be wrong.
On the other hand, when you need to select documents by a field that is preceded by a dollar sign ($), this answer may be useful.

How to purge CouchDB documents

I need to fully delete, as in Purge, several documents from CouchDB version 2.1.
I have been reading about /db/_purge on docs.couchdb.org, but the process is not clear to me. There is a sentence "The format of the request must include the document ID and one or more revisions that must be purged".
How do I do this in Postman or in a browser? Do I actually enclose my doc _id & rev(s) in braces? I am struggling with how to correctly format a _purge request.
As of now, it is my understanding that _purge does not work in 2.0 and 2.1.
For more information look at this JIRA post.
Note that document purging is only supported in CouchDB versions prior to 2.0, and from 2.3 onward. Early versions of clustered CouchDB (2.0.x and 2.1.x) did not support purging, although this was poorly documented!
The documentation explains, and provides an example:
{
"c6114c65e295552ab1019e2b046b10e": [
"3-b06fcd1c1c9e0ec7c480ee8aa467bf3b",
"3-0e871ef78849b0c206091f1a7af6ec41"
]
}
So that means in the format of:
{
"<doc id>": [
"<rev>",
"<rev>"
]
}
This should be the body of your HTTP request, with a Content-Type of application/json. You won't be able to do that in a browser, without using JavaScript.
With curl it would look like:
curl -X POST http://<server url>/<database>/_purge -H 'Content-Type: application/json' -d '{"<doc id>":["<rev1>","<rev2>"]}'
I just wrote this tool to purge and compact databases in CouchDB.
https://github.com/nisbus/couch_db_cleaner
I hope the documentation in the README is clear and it helps.

Possible to replicate couch database with illegal name

I'm using this command to replicate a 100mb database
curl -H 'Content-Type: application/json' \
-X POST http://localhost:5984/_replicate \
-d '{"source": "http://example.com:5984/bad_name_with_underscore", "target": "good_name"}'
I cannot replicate, because CouchDB says the source database name contains illegal chars. I can understand CouchDB folks discourage user to create bad database name, but reading from it is no harm.
I'm not an admin of source CouchDB, so I tried to export database as JSON and then bulk put to new database. But I met {"error":"bad_request","reason":"Missing JSON list of 'docs'"}. Although I have tried to modify the dump.json by changing the structure to {"docs": [...]}.
I'd like to know, is there any other way I can replicate this database with some underscore in name?
I have resolved the problem by using a client - PouchDB. Here is the code.
const PouchDB = require('pouchdb')
const source = new PouchDB("http://example.com:5984/bad_name_with_underscore")
source.replicate.to("http://localhost:5984/good_name")
.on('complete', console.log)
.on('error', console.error)
This works pretty well, so I post this to share with you all.

How to tag a Commit in API using curl command

I am trying to use a curl command to tag a commit. Is this possible? I went through the create-a-tag-object link from GitHub, but it doesn't work.
Creating a tag is a bit complicated, but if you just follow the API docs you should be fine. Note that the API docs say:
Note that creating a tag object does not create the reference that makes a tag in Git. If you want to create an annotated tag in Git, you have to do this call to create the tag object, and then create the refs/tags/[tag] reference. If you want to create a lightweight tag, you simply have to create the reference - this call would be unnecessary.
So, before you continue with creating a tag, you should know what kind of tag you want to create -- annotated or lightweight. Basically, an annotated tag is the same as a lightweight, but it also contains a message of the tag, info about the author of the tag, and the date and time when the tag was created. A lightweight tag is just a named pointer to a specific commit in your history.
Ok, so, what the API docs basically say is: if you want to create an annotated tag - you will have to make 2 API calls, and if you want to create a lightweight tag - you will have to make just 1 call. So, I'll give an example of creating an annotated tag with the 2 API calls, and if you want to create a lightweight tag - just skip the first API call and go to the second.
To create an annotated tag, you have to:
Step 1
Create a tag object using the tags API. The API docs are a bit unclear here how the parameters should be passed. What's missing is an example of the message that you need to send to the server. So, create a file called tag_object_req.json on your local disk, and put the following JSON document in it:
{
"tag": "v0.0.1",
"object": "c5f8759ffd808d4a57ea36c63960f3e2cc6fcc2b",
"message": "creating a tag",
"tagger": {
"name": "Ivan Zuzak",
"email": "izuzak#gmail.com",
"date": "2012-06-17T14:53:35-07:00"
},
"type": "commit"
}
Obviously, you have to replace the information in the document to reflect your situation. The meaning of the parameters are described in the API docs here.
After you have saved the file, you can make an API call using curl to create the tag object:
curl -v -X POST -d #tag_object_req.json --header "Content-Type:application/json" -u izuzak "https://api.github.com/repos/izuzak/test/git/tags"
So, the -v part will force curl to output all HTTP headers, the -X POST part means that an HTTP POST request must be made, -d #tag_object_req.json specifies which file will be used as the content (body) of the POST request, --header "Content-Type:application/json" specifies the media type of the request (JSON message), and -u izuzak specifies your username for authorization (and curl will ask you for your password when you make the request).
The response you get should be a 201 Created HTTP response, with the JSON message structured as this:
{
"sha": "e6d9fb6b9a13cab11923345e2400d5cf8df97267",
"url": "https://api.github.com/repos/izuzak/test/git/tags/e6d9fb6b9a13cab11923345e2400d5cf8df97267",
"tagger": {
"name": "Ivan Zuzak",
"email": "izuzak#gmail.com",
"date": "2012-06-17T21:53:35Z"
},
"object": {
"sha": "c5f8759ffd808d4a57ea36c63960f3e2cc6fcc2b",
"type": "commit",
"url": "https://api.github.com/repos/izuzak/test/git/commits/c5f8759ffd808d4a57ea36c63960f3e2cc6fcc2b"
},
"tag": "v0.0.1",
"message": "creating a tag"
}
Before continuing, notice the sha attribute of the object you just created (e6d9fb6b9a13cab11923345e2400d5cf8df97267) because you will use this value in the next step.
Step 2
Create a tag reference using the references API. The API docs are much clearer here about what the request should look like. So, you first have to create another file on the disk, named tag_ref_req.json, and put this content inside:
{
"ref": "refs/tags/v0.0.1",
"sha": "e6d9fb6b9a13cab11923345e2400d5cf8df97267"
}
However, notice that the value of the sha in this JSON depends on the type of tag you are creating. If you are creating an annotated tag, the value of the sha is the same value you got from the previous step - the sha of the tag object (e6d9fb6b9a13cab11923345e2400d5cf8df97267). However, if you are creating a lightweight tag, the value of the sha is the sha of the commit object you are tagging with the tag, because you have not created a tag object. In my case, you can see in step 1 that the commit object I am tagging is c5f8759ffd808d4a57ea36c63960f3e2cc6fcc2b, and this will be different in your case of course if you are creating a lightweight tag.
Ok, so after you have created this file and defined the sha and the name of the tag, you can make an API request in a similar way as in the previous step, using curl:
curl -v -X POST -d #tag_ref_req.json --header "Content-Type:application/json" -u izuzak "https://api.github.com/repos/izuzak/test/git/refs"
Notice that we are now making a request to https://api.github.com/repos/izuzak/test/git/refs with the second file as the content.
The response should again be a 201 Created HTTP response, and the body will be a JSON document looking like this:
{
"ref": "refs/tags/v0.0.1",
"url": "https://api.github.com/repos/izuzak/test/git/refs/tags/v0.0.1",
"object": {
"sha": "e6d9fb6b9a13cab11923345e2400d5cf8df97267",
"type": "tag",
"url": "https://api.github.com/repos/izuzak/test/git/tags/e6d9fb6b9a13cab11923345e2400d5cf8df97267"
}
}
And now you can navigate to your project on GitHub and go to "Switch branches/tags" and see your tag there.
Hope this helps!
You can also try the new Releases API
http://developer.github.com/v3/repos/releases/#create-a-release
curl \
--user <username> \
--header "Accept: application/vnd.github.manifold-preview" \
--data "tag_name=mytagname" \
"https://api.github.com/repos/<username>/<repository>/releases"
This is curl will create Releases. (But as ChucK mentioned it may create only lightweight tag, I haven't personally verified this )
curl \
--user <Your Github username> \
--header "Accept: application/vnd.github.manifold-preview" \
--data '{"tag_name": "v1.0.0", "target_commitish": "master", "name": "v1.0.0", "body": "Description of the release", "draft": false, "prerelease": false }' \
"https://api.github.com/repos/<OrganizationName>/<RepoName>/releases" -X POST
Adding this here, incase anybody come looking for this, like me.

Retrieve just deleted document

I deleted a document but I can still see it in _changes, so I can see last valid _rev, which is deleted, so get doc with id and last revision just returns:
{
"_id":"25efa4ec8489d8b89b34c5cad6000059",
"_rev":"3-a982bd6dccce8f405433f8453ab86880",
"_deleted":true
}
and no other attributes.
How can I recover in this situation? Previous revision can not be seen in _changes. Will writing empty document (setting _deleted to false) help to see all revisions info?
Ok, figured it out, if anyone interested:
get deleted history, e.g.:
curl http://example.iriscouch.com/test/_changes
you'll see deleted documents with $id and $rev, put empty document as new version, e.g.:
curl -X PUT http://example.iriscouch.com/test/$id?rev=$rev -H "Content-Type: application/json" -d {}
now you can get all revisions info, e.g:
curl http://example.iriscouch.com/test/$id?revs_info=true
obtain version before deletion, e.g.:
curl http://example.iriscouch.com/test/$id?rev=$prev_rev
put it back to couchdb, e.g.:
curl -X PUT http://example.iriscouch.com/test/$id?rev=$rev -H \'Content-Type: application/json\' -d \'$data\'
Let me know if you have any better way, or script.
Just been restoring deleted data from a couchdb. This is how I solved it after a little help from the good people on couchdb irc.
1) A get request to $db/$id?revs=true&open_revs=all where $db is your database name and $id is the id of the doc you deleted.
2) Clean the response. The result of this request wasn't valid json, strangely, and required cleaning. The following worked for me:
var deletedDoc = JSON.parse(xhReq.responseText.substring(xhReq.responseText.indexOf("{"), xhReq.responseText.lastIndexOf("}") + 1));
The resulting object looks like this:
{
"_id":"37b580b03b903da2b50f88587d89c15d",
"_rev":"2-bf3a2888dfe1ce0facef18720dcf97e2",
"_deleted":true,
"_revisions":{
"start":2,
"ids":["bf3a2888dfe1ce0facef18720dcf97e2","85f141069731f6bc77c910b0341e599f"]
}
}
3) Now we can construct the last revision number, the one before it was deleted. Pull out the second guid in the _revisions.ids array and append it with _revisions.start - 1 and a "-" character. This gives you the rev id of the document just before it was deleted.
var preDeleteRevisionNumber = (deletedDoc._revisions.start - 1) + "-"+ deletedDoc._revisions.ids[1];
4) Now collect the original (predelete data) with a get to $db/$id?rev=$preDeleteRevisionNumber
5) To overwrite the old deleted entry you have to post or put back the document with the correct id and the latest revision number (not the pre delete revision number, but the revision number the document has now it has been deleted).
Hope this helps someone.
I found an easy way to restore a deleted document, just copy the previous (undeleted) revision to "itself". It works perfectly with attachments too.
https://docs.couchdb.org/en/stable/api/document/common.html#copying-from-a-specific-revision
After you found the id and rev of you document (as avalez explained: https://stackoverflow.com/a/10857330/846168)
Instead of retrieving data and put it back, just use the action COPY with it's $id as destination:
COPY http://example.iriscouch.com/test/$id?rev=$rev HTTP/1.1
Accept: application/json
Destination: $id
or with curl:
curl -X COPY "http://example.iriscouch.com/test/$id?rev=$rev" -H "Destination: $id"

Resources