package org.couchbase.mock.http.capi;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.couchbase.mock.Bucket;
import org.couchbase.mock.JsonUtils;
import org.couchbase.mock.http.HttpAuthVerifier;
import org.couchbase.mock.httpio.HandlerUtil;
import org.couchbase.mock.httpio.HttpServer;
import org.couchbase.mock.views.DesignDocument;
import org.couchbase.mock.views.View;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public final class CAPIServer {
private final Bucket bucket;
final HttpAuthVerifier verifier;
private final Map<String, DesignDocument> designDocMap;
private HttpServer parentServer = null;
public CAPIServer(Bucket bucket, HttpAuthVerifier verifier) {
this.bucket = bucket;
this.verifier = verifier;
this.designDocMap = new ConcurrentHashMap<String, DesignDocument>();
}
public void register(HttpServer server) {
DesignHandler designServer = new DesignHandler(this);
String prefix = String.format("/%s/_design/*", bucket.getName());
server.register(prefix, designServer);
parentServer = server;
}
public void shutdown() {
synchronized (designDocMap) {
for (DesignDocument ddoc : designDocMap.values()) {
removeDesign(ddoc, false);
}
}
parentServer.unregister(String.format("%s/_design/*", bucket.getName()));
}
private String makeViewPaths(DesignDocument design, View view) {
return String.format("/%s/%s/_view/%s", bucket.getName(), design.getId(), view.getName());
}
private void handleViewPaths(DesignDocument design, boolean enabled) {
for (View view : design.getViews()) {
String path = makeViewPaths(design, view);
if (enabled) {
System.err.printf("Registering name '%s'\n", path);
parentServer.register(path, new ViewHandler(view, bucket));
} else {
parentServer.unregister(path);
}
}
}
private void removeDesign(DesignDocument design, boolean needSync) {
DesignDocument oldDocument;
if (needSync) {
synchronized (designDocMap) {
oldDocument = designDocMap.remove(design.getId());
}
} else {
oldDocument = designDocMap.remove(design.getId());
}
if (oldDocument != null) {
handleViewPaths(design, false);
}
}
public void removeDesign(DesignDocument design) {
removeDesign(design, true);
}
void addDesign(DesignDocument design) {
synchronized (designDocMap) {
removeDesign(design, false);
handleViewPaths(design, true);
designDocMap.put(design.getId(), design);
}
}
DesignDocument findDesign(PathInfo info) {
return designDocMap.get(info.getDesignId());
}
private Map<String,Object> getSingleViewInfo(View view) {
Map<String,Object> topLevel = new HashMap<String, Object>();
String sMap = view.getMapSource();
String sRed = view.getReduceSource();
if (sMap != null) {
topLevel.put("map", sMap);
}
if (sRed != null) {
topLevel.put("reduce", sRed);
}
return topLevel;
}
private Map<String,Object> getSingleDdocInfo(DesignDocument ddoc) {
Map<String,Object> topLevel = new HashMap<String, Object>();
topLevel.put("controllers", new HashMap<String,Object>());
Map<String,Object> doc = new HashMap<String, Object>();
topLevel.put("doc", doc);
// Meta
Map<String,Object> meta = new HashMap<String, Object>();
doc.put("meta", meta);
meta.put("id", ddoc.getId());
meta.put("rev", ddoc.hashCode());
Map<String,Object> json = new HashMap<String, Object>();
doc.put("json", json);
json.put("_id", ddoc.getId()); // This is not a typo. Server uses _id here
json.put("language", "javascript");
Map<String,Object> views = new HashMap<String, Object>();
json.put("views", views);
for (View view : ddoc.getViews()) {
Map<String,Object> viewInfo = getSingleViewInfo(view);
views.put(view.getName(), viewInfo);
}
return topLevel;
}
public Map<String,Object> getDddocApiInfo() {
// {
Map<String,Object> retvalOuterObject = new HashMap<String, Object>();
// "rows": [
List<Map<String,Object>> retvalRows = new ArrayList<Map<String, Object>>();
retvalOuterObject.put("rows", retvalRows);
synchronized (designDocMap) {
for (DesignDocument ddoc : designDocMap.values()) {
retvalRows.add(getSingleDdocInfo(ddoc));
}
}
return retvalOuterObject;
}
public static String makeError(String errStr, String reasonStr) {
Map<String,String> mm = new HashMap<String, String>();
mm.put("error", errStr);
mm.put("reason", reasonStr);
return JsonUtils.encode(mm) + "\n";
}
public static String makeError(String reasonStr) {
return makeError("unknown_error", reasonStr);
}
public static void makeNotFoundError(HttpResponse response) {
HandlerUtil.makeJsonResponse(response, TXT_NOTFOUND);
response.setStatusCode(HttpStatus.SC_NOT_FOUND);
}
public static void makeNotFoundError(HttpResponse response, String detail) {
String s = makeError("not_found", detail);
s += "\n";
HandlerUtil.makeJsonResponse(response, s);
response.setStatusCode(HttpStatus.SC_NOT_FOUND);
}
final static Set<String> ALLOWED_DDOC_METHODS = new HashSet<String>();
static {
ALLOWED_DDOC_METHODS.add("GET");
ALLOWED_DDOC_METHODS.add("PUT");
ALLOWED_DDOC_METHODS.add("HEAD");
ALLOWED_DDOC_METHODS.add("DELETE");
}
static private final String TXT_NOTFOUND = "{\"error\":\"not_found\",\"reason\":\"missing\"}\n";
}