de.gesellix.couchdb.CouchDbClient.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of couchdb-client Show documentation
Show all versions of couchdb-client Show documentation
A CouchDB client written in Groovy
The newest version!
package de.gesellix.couchdb
import de.gesellix.couchdb.model.NonReducedViewQueryResponse
import de.gesellix.couchdb.model.RowReference
import groovy.transform.PackageScope
import okhttp3.Credentials
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.lang.reflect.Type
import java.time.LocalDate
import static java.nio.charset.StandardCharsets.UTF_8
import static okhttp3.MediaType.parse
class CouchDbClient {
private final static Logger log = LoggerFactory.getLogger(getClass())
OkHttpClient client
int MAX_QUERY_KEY_LENGTH = 5000
Json json
boolean tlsEnabled
String couchdbHost
int couchdbPort
String baseUrl
String couchdbUsername
String couchdbPassword
CouchDbClient(Json json) {
this.client = new OkHttpClient()
this.json = json
this.tlsEnabled = false
this.couchdbHost = "127.0.0.1"
this.couchdbPort = 5984
}
@PackageScope
String getCurlCommandLine(String suffix) {
String authorization = ""
if (couchdbUsername && couchdbPassword) {
authorization = "-H \"Authorization: ${Credentials.basic(couchdbUsername, couchdbPassword)}\""
}
return "curl ${authorization} \"${getBaseUrl()}${suffix ?: ""}\""
}
void updateBaseUrl() {
this.baseUrl = "${tlsEnabled ? "https" : "http"}://${couchdbHost}:${couchdbPort}"
}
String getBaseUrl() {
if (baseUrl == null) {
updateBaseUrl()
}
return baseUrl
}
void setTlsEnabled(boolean tlsEnabled) {
this.tlsEnabled = tlsEnabled
this.updateBaseUrl()
}
void setCouchdbHost(String couchdbHost) {
this.couchdbHost = couchdbHost
this.updateBaseUrl()
}
void setCouchdbPort(int couchdbPort) {
this.couchdbPort = couchdbPort
this.updateBaseUrl()
}
boolean healthy() {
Request.Builder builder = new Request.Builder()
.url(getBaseUrl())
.get()
Request request = builder.build()
Response response = client.newCall(request).execute()
if (!response.successful) {
if (response.code() == 404) {
response.body().close()
return false
} else {
log.error("error {}", response.message())
response.body().close()
throw new IllegalStateException("could not check for database existence")
}
} else {
log.info(response.body().string())
return true
}
}
def R query(String db, String viewName, String key, boolean includeDocs = true) {
String designDocId = "_design/${db.capitalize()}"
return query(db, designDocId, viewName, key, includeDocs)
}
def R query(String db, String designDocId, String viewName, String key, boolean includeDocs = true) {
List query = []
if (includeDocs) {
query.add("include_docs=${includeDocs}")
}
// if (group) {
// query.add("group=${group}")
// }
Map postBody = [:]
boolean doPost = false
if (key) {
String encodedKey = urlEncode(json.encodeQueryValue(key))
if (encodedKey.length() > MAX_QUERY_KEY_LENGTH) {
doPost = true
postBody['key'] = key
} else {
query.add("key=${encodedKey}")
}
}
String queryAsString = query.join("&")
Request.Builder builder = new Request.Builder()
.url(getBaseUrl() +
"/${db.toLowerCase()}" +
"/${designDocId}" +
"/_view/${viewName}" +
"?${queryAsString}")
if (doPost) {
String documentAsJson = json.encodeDocument(postBody)
builder = builder.post(RequestBody.create(documentAsJson, parse("application/json")))
} else {
builder = builder.get()
}
if (couchdbUsername && couchdbPassword) {
builder = builder.header("Authorization", Credentials.basic(couchdbUsername, couchdbPassword))
}
Request request = builder.build()
Response response = client.newCall(request).execute()
if (!response.successful) {
if (response.body().contentLength() > 0) {
log.error("error querying view: {}/{}: {}", response.code(), response.message(), response.body().string())
} else {
log.error("error querying view: {}/{}", response.code(), response.message())
}
throw new IllegalStateException("could not query view")
}
def result = json.consume(response.body().byteStream(), Map)
result.rows.collect { row -> includeDocs ? row.doc : row }
}
def R query(String db, String viewName, Collection keys, boolean includeDocs = true, boolean group = false) {
String designDocId = "_design/${db.capitalize()}"
return query(db, designDocId, viewName, keys, includeDocs, group)
}
def R query(String db, String designDocId, String viewName, Collection keys, boolean includeDocs = true, boolean group = false) {
List query = []
if (includeDocs) {
query.add("include_docs=${includeDocs}")
}
if (group) {
query.add("group=${group}")
}
Map postBody = [:]
boolean doPost = false
if (keys) {
String encodedKeys = json.encodeQueryValue(keys)
if (encodedKeys.length() > MAX_QUERY_KEY_LENGTH) {
doPost = true
postBody['keys'] = keys
} else {
query.add("keys=${encodedKeys}")
}
}
String queryAsString = query.join("&")
Request.Builder builder = new Request.Builder()
.url(getBaseUrl() +
"/${db.toLowerCase()}" +
"/${designDocId}" +
"/_view/${viewName}" +
"?${queryAsString}")
if (doPost) {
String documentAsJson = json.encodeDocument(postBody)
builder = builder.post(RequestBody.create(documentAsJson, parse("application/json")))
} else {
builder = builder.get()
}
if (couchdbUsername && couchdbPassword) {
builder = builder.header("Authorization", Credentials.basic(couchdbUsername, couchdbPassword))
}
Request request = builder.build()
Response response = client.newCall(request).execute()
if (!response.successful) {
if (response.body().contentLength() > 0) {
log.error("error querying view: {}/{}: {}", response.code(), response.message(), response.body().string())
} else {
log.error("error querying view: {}/{}", response.code(), response.message())
}
throw new IllegalStateException("could not query view")
}
def result = json.consume(response.body().byteStream(), Map)
result.rows.collect { row -> includeDocs ? row.doc : row }
}
def getAllDocs(String db, boolean includeDocs = true, boolean includeDesignDoc = false) {
Request.Builder builder = new Request.Builder()
.url("${getBaseUrl()}/${db.toLowerCase()}" +
"/_all_docs" +
"?include_docs=${includeDocs}")
.get()
if (couchdbUsername && couchdbPassword) {
builder = builder.header("Authorization", Credentials.basic(couchdbUsername, couchdbPassword))
}
Request request = builder.build()
Response response = client.newCall(request).execute()
if (!response.successful) {
if (response.body().contentLength() > 0) {
log.error("error querying all_docs: {}/{}: {}", response.code(), response.message(), response.body().string())
} else {
log.error("error querying all_docs: {}/{}", response.code(), response.message())
}
throw new IllegalStateException("could not query all_docs")
} else {
def allDocs = json.consume(response.body().byteStream(), Map)
allDocs = allDocs.rows.collect { row -> includeDocs ? row.doc : row }
if (!includeDesignDoc) {
allDocs = allDocs.grep {
// if (it._id?.startsWith("_design/") || it.id?.startsWith("_design/")) {
// log.info("removing $it")
// }
!it._id?.startsWith("_design/") && !it.id?.startsWith("_design/")
}
}
return allDocs
}
}
, R extends NonReducedViewQueryResponse> R getAllDocs(
Type R, String db, String startkey, String startkeyDocId,
Integer limit = null, boolean includeDocs = true) {
List query = []
if (includeDocs) {
query.add("include_docs=${includeDocs}")
}
if (startkey) {
query.add("startkey=${urlEncode(json.encodeQueryValue(startkey))}")
}
if (startkeyDocId) {
String docId = sanitizeDocId(startkeyDocId)
query.add("startkey_docid=${docId}")
}
if (limit != null) {
query.add("limit=${limit}")
}
String queryAsString = query.join("&")
Request.Builder builder = new Request.Builder()
.url("${getBaseUrl()}/${db.toLowerCase()}" +
"/_all_docs" +
"?${queryAsString}")
.get()
if (couchdbUsername && couchdbPassword) {
builder = builder.header("Authorization", Credentials.basic(couchdbUsername, couchdbPassword))
}
Request request = builder.build()
Response response = client.newCall(request).execute()
if (!response.successful) {
if (response.body().contentLength() > 0) {
log.error("error querying all_docs: {}/{}: {}", response.code(), response.message(), response.body().string())
} else {
log.error("error querying all_docs: {}/{}", response.code(), response.message())
}
throw new IllegalStateException("could not query all_docs")
} else {
R allDocs = json.consume(response.body().byteStream(), R)
return allDocs
}
}
R queryPage(
Type R, String db, String designDocId, String viewName, boolean reduce,
Object startkey, String startkeyDocId,
Integer skip = null, Integer limit = null,
boolean includeDocs = false, boolean includeDesignDoc = false,
Object endkey = null, String endkeyDocId = null,
boolean doPost = false) {
List query = []
query.add("reduce=${reduce}")
if (reduce) {
query.add("group=true")
}
if (!reduce && includeDocs) {
query.add("include_docs=${includeDocs}")
}
Map postBody = [:]
if (startkey) {
// TODO allow non-String and non-Collection instances for startkey
String encodedKey = urlEncode(json.encodeQueryValue(startkey))
if (encodedKey.length() > MAX_QUERY_KEY_LENGTH) {
doPost = true
postBody['startkey'] = startkey
} else {
query.add("startkey=${encodedKey}")
}
}
if (startkeyDocId) {
String docId = sanitizeDocId(startkeyDocId)
query.add("startkey_docid=${docId}")
}
if (endkey) {
// TODO allow non-String and non-Collection instances for endkey
String encodedKey = urlEncode(json.encodeQueryValue(endkey))
if (encodedKey.length() > MAX_QUERY_KEY_LENGTH) {
doPost = true
postBody['endkey'] = endkey
} else {
query.add("endkey=${encodedKey}")
}
}
if (endkeyDocId) {
String docId = sanitizeDocId(endkeyDocId)
query.add("endkey_docid=${docId}")
}
if (skip != null) {
query.add("skip=${skip}")
}
if (limit != null) {
query.add("limit=${limit}")
}
String queryAsString = query.join("&")
Request.Builder builder = new Request.Builder()
.url("${getBaseUrl()}/${db.toLowerCase()}" +
"/${designDocId}" +
"/_view/${viewName}" +
"?${queryAsString}")
if (doPost) {
String documentAsJson = json.encodeDocument(postBody)
builder = builder.post(RequestBody.create(documentAsJson, parse("application/json")))
} else {
builder = builder.get()
}
if (couchdbUsername && couchdbPassword) {
builder = builder.header("Authorization", Credentials.basic(couchdbUsername, couchdbPassword))
}
Request request = builder.build()
Response response = client.newCall(request).execute()
if (!response.successful) {
if (response.body().contentLength() > 0) {
log.error("error querying view: {}/{}: {}", response.code(), response.message(), response.body().string())
} else {
log.error("error querying view: {}/{}", response.code(), response.message())
}
throw new IllegalStateException("could not query view")
} else {
R allDocs = json.consume(response.body().byteStream(), R)
if (!reduce && !includeDesignDoc) {
allDocs.rows.removeIf { it.id?.startsWith("_design/") }
}
return allDocs
}
}
def create(String db, Map document) {
if (document == null) {
throw new IllegalArgumentException("document may not be null")
}
if (document['_rev']) {
log.error("document must be new, but has id({}), rev({})", document['_id'], document['_rev'])
throw new IllegalArgumentException("document must be new")
}
beforeCreate(document)
String documentAsJson = json.encodeDocument(document)
RequestBody body = RequestBody.create(documentAsJson, parse("application/json"))
Request.Builder builder = new Request.Builder()
if (document['_id']) {
String docId = document['_id']
docId = sanitizeDocId(docId)
builder = builder
.url("${getBaseUrl()}/${db.toLowerCase()}/${docId}")
.put(body)
} else {
builder = builder
.url("${getBaseUrl()}/${db.toLowerCase()}")
.post(body)
}
if (couchdbUsername && couchdbPassword) {
builder = builder.header("Authorization", Credentials.basic(couchdbUsername, couchdbPassword))
}
Request request = builder.build()
Response response = client.newCall(request).execute()
Map result = json.consume(response.body().byteStream(), Map)
if (!result.ok) {
log.error("error {}", result)
throw new IllegalStateException("error creating document")
}
Map createdDocument = json.decodeDocument(documentAsJson, Map.class)
createdDocument['_id'] = result.id
createdDocument['_rev'] = result.rev
return createdDocument
}
def update(String db, Map document) {
if (document == null) {
throw new IllegalArgumentException("document may not be null")
}
if (!document['_id']) {
throw new IllegalArgumentException("document id missing")
}
beforeUpdate(document)
String documentAsJson = json.encodeDocument(document)
RequestBody body = RequestBody.create(documentAsJson, parse("application/json"))
def builder = new Request.Builder()
String docId = document['_id']
docId = sanitizeDocId(docId)
builder = builder
.url("${getBaseUrl()}/${db.toLowerCase()}/${docId}")
.put(body)
if (couchdbUsername && couchdbPassword) {
builder = builder.header("Authorization", Credentials.basic(couchdbUsername, couchdbPassword))
}
Request request = builder.build()
Response response = client.newCall(request).execute()
Map result = json.consume(response.body().byteStream(), Map)
if (!result.ok) {
log.error("error {}", result)
throw new IllegalStateException("error updating document")
}
Map updatedDocument = json.decodeDocument(documentAsJson, Map.class)
updatedDocument['_rev'] = result.rev
return updatedDocument
}
List