From 335f3187a8eeaded3c9f47334878d43fc22f8ce6 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 30 May 2011 16:41:56 -0700 Subject: [PATCH] blob info page --- lib/go/camli/search/handler.go | 23 ++++---- server/go/camlistored/ui.go | 21 +++++--- server/go/camlistored/ui/blobinfo.html | 16 ++++++ server/go/camlistored/ui/blobinfo.js | 74 ++++++++++++++++++++++++++ server/go/camlistored/ui/camli.js | 55 +++++++++++++++++++ server/go/camlistored/ui/permanode.js | 21 +------- 6 files changed, 174 insertions(+), 36 deletions(-) create mode 100644 server/go/camlistored/ui/blobinfo.html create mode 100644 server/go/camlistored/ui/blobinfo.js diff --git a/lib/go/camli/search/handler.go b/lib/go/camli/search/handler.go index f86c19839..91f43ea68 100644 --- a/lib/go/camli/search/handler.go +++ b/lib/go/camli/search/handler.go @@ -143,18 +143,19 @@ func (sh *searchHandler) serveDescribe(rw http.ResponseWriter, req *http.Request } mime, size, err := sh.index.GetBlobMimeType(br) - if err != nil { - // TODO: special error value for not found - ret["errorText"] = err.String() - } else { - m := dmap(br) - setMimeType(m, mime) - m["size"] = size + if err != os.ENOENT { + if err != nil { + ret["errorText"] = err.String() + } else { + m := dmap(br) + setMimeType(m, mime) + m["size"] = size - if mime == "application/json; camliType=permanode" { - pm := make(jsonMap) - m["permanode"] = pm - sh.populatePermanodeFields(pm, br, sh.owner, dmap) + if mime == "application/json; camliType=permanode" { + pm := make(jsonMap) + m["permanode"] = pm + sh.populatePermanodeFields(pm, br, sh.owner, dmap) + } } } diff --git a/server/go/camlistored/ui.go b/server/go/camlistored/ui.go index 8c7acf988..2a5a6987e 100644 --- a/server/go/camlistored/ui.go +++ b/server/go/camlistored/ui.go @@ -133,6 +133,10 @@ func wantsPermanode(req *http.Request) bool { return req.Method == "GET" && blobref.Parse(req.FormValue("p")) != nil } +func wantsBlobInfo(req *http.Request) bool { + return req.Method == "GET" && blobref.Parse(req.FormValue("b")) != nil +} + func (ui *UIHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { base := req.Header.Get("X-PrefixHandler-PathBase") suffix := req.Header.Get("X-PrefixHandler-PathSuffix") @@ -147,13 +151,18 @@ func (ui *UIHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { file := "" if m := staticFilePattern.FindStringSubmatch(suffix); m != nil { file = m[1] - } else if wantsPermanode(req) { - file = "permanode.html" - } else if req.URL.Path == base { - file = "index.html" } else { - http.Error(rw, "Illegal URL.", 404) - return + switch { + case wantsPermanode(req): + file = "permanode.html" + case wantsBlobInfo(req): + file = "blobinfo.html" + case req.URL.Path == base: + file = "index.html" + default: + http.Error(rw, "Illegal URL.", 404) + return + } } http.ServeFile(rw, req, filepath.Join(ui.FilesDir, file)) } diff --git a/server/go/camlistored/ui/blobinfo.html b/server/go/camlistored/ui/blobinfo.html new file mode 100644 index 000000000..99741670a --- /dev/null +++ b/server/go/camlistored/ui/blobinfo.html @@ -0,0 +1,16 @@ + + + Blob + + + + + +

Blob Search Metadata

+

+
+  

Blob Contents

+

+
+
+
diff --git a/server/go/camlistored/ui/blobinfo.js b/server/go/camlistored/ui/blobinfo.js
new file mode 100644
index 000000000..05b3459d0
--- /dev/null
+++ b/server/go/camlistored/ui/blobinfo.js
@@ -0,0 +1,74 @@
+/*
+Copyright 2011 Google Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Gets the |p| query parameter, assuming that it looks like a blobref.
+function getBlobParam() {
+    var blobRef = getQueryParam('b');
+    return (blobRef && isPlausibleBlobRef(blobRef)) ? blobRef : null;
+}
+
+function blobInfoUpdate(bmap) {
+    var blobpre = document.getElementById('blobpre');
+    var blobref = getBlobParam();
+    if (!blobref) {
+        alert("no blobref?");
+        return;        
+    }
+    var binfo = bmap[blobref];
+    if (!binfo) {
+        blobpre.innerHTML = "(not found)";
+        return;
+    }
+    blobpre.innerHTML = JSON.stringify(binfo, null, 2);
+    if (binfo.camliType) {
+        camliGetBlobContents(
+            blobref,
+            {
+                success: function(data) {
+                    document.getElementById("blobdata").innerHTML = linkifyBlobRefs(data);
+                },
+                fail: alert
+            });
+    } else {
+        document.getElementById("blobdata").innerHTML = "Unknown/binary data; download";
+    }
+}
+
+function linkifyBlobRefs(schemaBlob) {
+    var re = /(\w{3,6}-[a-f0-9]{30,})/g;
+    return schemaBlob.replace(re, "$1");
+}
+
+function blobInfoOnLoad() {
+    var blobref = getBlobParam();
+    if (!blobref) {
+        return
+    }
+    var blobpre = document.getElementById('blobpre');
+    blobpre.innerText = "(loading)";
+    camliDescribeBlob(
+        blobref,
+        {
+            success: blobInfoUpdate,
+            fail: function(msg) {
+                alert("Error describing blob " + blobref + ": " + msg);
+            }
+        }
+    );
+}
+
+window.addEventListener("load", blobInfoOnLoad);
+                            
\ No newline at end of file
diff --git a/server/go/camlistored/ui/camli.js b/server/go/camlistored/ui/camli.js
index e7333c852..3a71b808f 100644
--- a/server/go/camlistored/ui/camli.js
+++ b/server/go/camlistored/ui/camli.js
@@ -60,6 +60,45 @@ function camliSigDiscovery(opts) {
     xhr.send();
 }
 
+function camliDescribeBlob(blobref, opts) {
+    opts = saneOpts(opts);
+    var xhr = new XMLHttpRequest();
+    xhr.onreadystatechange = function() {
+        if (xhr.readyState != 4) { return; }
+        if (xhr.status != 200) {
+            opts.fail("got HTTP status " + xhr.status);
+            return;
+        }
+        var jres;
+        try {
+            jres = JSON.parse(xhr.responseText);
+        } catch (x) {
+            opts.fail("JSON parse error");
+            return;
+        }
+        opts.success(jres);
+    };
+    var path = disco.searchRoot + "camli/search/describe?blobref=" + blobref;
+    xhr.open("GET", path, true);
+    xhr.send();
+}
+
+function camliGetBlobContents(blobref, opts) {
+    opts = saneOpts(opts);
+    var xhr = new XMLHttpRequest();
+    xhr.onreadystatechange = function() {
+        if (xhr.readyState != 4) { return; }
+        if (xhr.status != 200) {
+            opts.fail("got HTTP status " + xhr.status);
+            return;
+        }
+        opts.success(xhr.responseText);
+    };
+    var path = disco.blobRoot + "camli/" + blobref;
+    xhr.open("GET", path, true);
+    xhr.send();
+}
+
 function camliSign(clearObj, opts) {
     opts = saneOpts(opts);
 
@@ -160,3 +199,19 @@ function camliCreateNewPermanode(opts) {
                });
 }
 
+// Returns the first value from the query string corresponding to |key|.
+// Returns null if the key isn't present.
+function getQueryParam(key) {
+    var params = document.location.search.substring(1).split('&');
+    for (var i = 0; i < params.length; ++i) {
+        var parts = params[i].split('=');
+        if (parts.length == 2 && decodeURIComponent(parts[0]) == key)
+            return decodeURIComponent(parts[1]);
+    }
+    return null;
+}
+
+// Returns true if the passed-in string might be a blobref.
+function isPlausibleBlobRef(blobRef) {
+    return /^\w+-[a-f0-9]+$/.test(blobRef);
+}
diff --git a/server/go/camlistored/ui/permanode.js b/server/go/camlistored/ui/permanode.js
index fe42db26e..c7403c373 100644
--- a/server/go/camlistored/ui/permanode.js
+++ b/server/go/camlistored/ui/permanode.js
@@ -14,27 +14,10 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-// Returns the first value from the query string corresponding to |key|.
-// Returns null if the key isn't present.
-function getQueryParam(key) {
-  var params = document.location.search.substring(1).split('&');
-  for (var i = 0; i < params.length; ++i) {
-    var parts = params[i].split('=');
-    if (parts.length == 2 && decodeURIComponent(parts[0]) == key)
-      return decodeURIComponent(parts[1]);
-  }
-  return null;
-}
-
-// Returns true if the passed-in string might be a blobref.
-function isPlausibleBlobRef(blobRef) {
-  return /^\w+-[a-f0-9]+$/.test(blobRef);
-}
-
 // Gets the |p| query parameter, assuming that it looks like a blobref.
 function getPermanodeParam() {
-  var blobRef = getQueryParam('p');
-  return (blobRef && isPlausibleBlobRef(blobRef)) ? blobRef : null;
+    var blobRef = getQueryParam('p');
+    return (blobRef && isPlausibleBlobRef(blobRef)) ? blobRef : null;
 }
 
 window.addEventListener("load", function (e) {