Add an RPC call to server to get the dataset root

And use that in the heatmap ui
This commit is contained in:
Erik Arvidsson
2015-07-17 10:17:09 -07:00
committed by Erik Arvidsson
parent b221d976dc
commit 2789d477e2
5 changed files with 208 additions and 39 deletions

View File

@@ -8,18 +8,22 @@ var nomsPort = "8000";
var nomsServer = location.protocol + '//' + host + ":" + nomsPort;
var rpc = {
dataset: nomsServer + '/dataset',
get: nomsServer + '/get',
root: nomsServer + '/root'
}
root: nomsServer + '/root',
};
// TODO: Use whatwg-fetch
function fetch(url) {
return new Promise(function(fulfill) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function(e) {
fulfill(e.target.responseText);
});
xhr.open("get", url, true);
xhr.onload = (e) => {
resolve(e.target.responseText);
};
xhr.onerror = (e) => {
reject(e.target.statusText);
};
xhr.open('get', url, true);
xhr.send();
});
}
@@ -32,7 +36,12 @@ function getRoot() {
return fetch(rpc.root);
}
function getDataset(id) {
return fetch(rpc.get + '?id=' + id)
}
module.exports = {
getRoot: getRoot,
getChunk: getChunk
getChunk,
getDataset,
getRoot,
};

View File

@@ -5,12 +5,13 @@
"immutable": "^3.7.4"
},
"devDependencies": {
"babelify": "^6.1.3",
"browserify": "^6.2.0",
"uglify-js": "~2.4.15",
"watchify": "^2.1.1"
},
"scripts": {
"start": "watchify -o explore.js -v -d main.js",
"build": "NODE_ENV=production browserify main.js | uglifyjs -cm > explore.js"
"start": "watchify -o explore.js -t babelify -v -d main.js",
"build": "NODE_ENV=production browserify main.js -t babelify | uglifyjs -cm > explore.js"
}
}

View File

@@ -6,20 +6,12 @@ var Immutable = require('immutable');
var React = require('react');
var Map = require('./map.js');
store.getRoot().then(function(s) {
store.getDataset('mlb/heatmap').then(function(s) {
return decode.readValue(s, store.getChunk);
}).then(function(v) {
return getDatasetRoot(v, 'mlb/heatmap');
}).then(getPitchers).then(renderPitchersList).catch(function(err) {
console.error(err);
});
function getDatasetRoot(root, id) {
return root.first().get('value').find(function(map) {
return map.get('id') === id;
}).get('root');
}
function getPitchers(datasetRoot) {
return datasetRoot.first().get('value')
}

View File

@@ -7,53 +7,91 @@ import (
"net/http"
"github.com/attic-labs/noms/chunks"
"github.com/attic-labs/noms/datas"
"github.com/attic-labs/noms/dataset/mgmt"
"github.com/attic-labs/noms/ref"
)
var (
port *string = flag.String("port", "8000", "")
cs chunks.ChunkStore
port = flag.String("port", "8000", "")
)
func handler(w http.ResponseWriter, r *http.Request) {
type server struct {
chunks.ChunkStore
}
func (s server) handle(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Access-Control-Allow-Origin", "*")
switch r.URL.Path[1:] {
case "root":
cs := s.ChunkStore
w.Header().Add("content-type", "text/plain")
fmt.Fprintf(w, "%v", cs.Root().String())
case "get":
hashString := r.URL.Query()["ref"][0]
ref, err := ref.Parse(hashString)
if err != nil {
http.Error(w, fmt.Sprintf("Parse error: %v", err), http.StatusBadRequest)
return
if refs, ok := r.URL.Query()["ref"]; ok {
s.handleGetRef(w, refs[0])
} else {
http.Error(w, "Missing query param ref", http.StatusBadRequest)
}
reader, err := cs.Get(ref)
if err != nil {
http.Error(w, fmt.Sprintf("Fetch error: %v", err), http.StatusNotFound)
return
case "dataset":
if ids, ok := r.URL.Query()["id"]; ok {
s.handleGetDataset(w, ids[0])
} else {
http.Error(w, "Missing query param id", http.StatusBadRequest)
}
w.Header().Add("content-type", "application/octet-stream")
w.Header().Add("cache-control", "max-age=31536000") // 1 year
io.Copy(w, reader)
default:
http.Error(w, fmt.Sprintf("Unrecognized: %v", r.URL.Path[1:]), http.StatusBadRequest)
}
}
func (s server) handleGetRef(w http.ResponseWriter, hashString string) {
cs := s.ChunkStore
ref, err := ref.Parse(hashString)
if err != nil {
http.Error(w, fmt.Sprintf("Parse error: %v", err), http.StatusBadRequest)
return
}
reader, err := cs.Get(ref)
if err != nil {
// TODO: Maybe we should not expose the internal path?
http.Error(w, fmt.Sprintf("Fetch error: %v", err), http.StatusNotFound)
return
}
if reader == nil {
http.Error(w, fmt.Sprintf("No such ref: %v", hashString), http.StatusNotFound)
return
}
w.Header().Add("content-type", "application/octet-stream")
w.Header().Add("cache-control", "max-age=31536000") // 1 year
io.Copy(w, reader)
}
func (s server) handleGetDataset(w http.ResponseWriter, id string) {
cs := s.ChunkStore
rootDataStore := datas.NewDataStore(cs, cs.(chunks.RootTracker))
dataset := mgmt.GetDatasetRoot(mgmt.GetDatasets(rootDataStore), id)
if dataset == nil {
http.Error(w, fmt.Sprintf("Dataset not found: %s", id), http.StatusNotFound)
return
}
w.Header().Add("content-type", "text/plain")
fmt.Fprintf(w, "%s", dataset.Ref())
}
func main() {
flags := chunks.NewFlags()
flag.Parse()
cs = flags.CreateStore()
cs := flags.CreateStore()
if cs == nil {
flag.Usage()
return
}
http.HandleFunc("/", handler)
http.HandleFunc("/", cs.(server).handle)
http.ListenAndServe(fmt.Sprintf(":%s", *port), nil)
}

View File

@@ -0,0 +1,129 @@
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/attic-labs/noms/chunks"
"github.com/attic-labs/noms/datas"
"github.com/attic-labs/noms/dataset"
"github.com/attic-labs/noms/ref"
"github.com/attic-labs/noms/types"
"github.com/stretchr/testify/assert"
)
var datasetID = "testdataset"
func createTestStore() chunks.ChunkStore {
ms := &chunks.MemoryStore{}
datasetDs := dataset.NewDataset(datas.NewDataStore(ms, ms), datasetID)
datasetRoot := types.NewString("Root value for " + datasetID)
datasetDs = datasetDs.Commit(datas.NewRootSet().Insert(
datas.NewRoot().SetParents(
types.NewSet()).SetValue(datasetRoot)))
return ms
}
func TestBadRequest(t *testing.T) {
assert := assert.New(t)
req, _ := http.NewRequest("GET", "/bad", nil)
w := httptest.NewRecorder()
ms := &chunks.MemoryStore{}
s := server{ms}
s.handle(w, req)
assert.Equal(w.Code, http.StatusBadRequest)
}
func TestRoot(t *testing.T) {
assert := assert.New(t)
req, _ := http.NewRequest("GET", "/root", nil)
w := httptest.NewRecorder()
ms := createTestStore()
s := server{ms}
s.handle(w, req)
assert.Equal(w.Code, http.StatusOK)
ref, err := ref.Parse(w.Body.String())
assert.NoError(err)
assert.Equal(ms.Root(), ref)
}
func TestGetRef(t *testing.T) {
assert := assert.New(t)
ms := createTestStore()
rootRef := ms.Root().String()
req, _ := http.NewRequest("GET", "/get?ref="+rootRef, nil)
w := httptest.NewRecorder()
s := server{ms}
s.handle(w, req)
assert.Equal(w.Code, http.StatusOK)
assert.Equal(`j {"set":[{"ref":"sha1-b432c2dd6d7b6e7e163cab2517d1e6221d5d595c"}]}
`, w.Body.String())
}
func TestGetInvalidRef(t *testing.T) {
assert := assert.New(t)
ms := createTestStore()
rootRef := "sha1-xxx"
req, _ := http.NewRequest("GET", "/get?ref="+rootRef, nil)
w := httptest.NewRecorder()
s := server{ms}
s.handle(w, req)
assert.Equal(w.Code, http.StatusBadRequest)
}
func TestGetNonExistingRef(t *testing.T) {
assert := assert.New(t)
ms := createTestStore()
ref := "sha1-1111111111111111111111111111111111111111"
req, _ := http.NewRequest("GET", "/get?ref="+ref, nil)
w := httptest.NewRecorder()
s := server{ms}
s.handle(w, req)
assert.Equal(w.Code, http.StatusNotFound)
}
func TestGetDataset(t *testing.T) {
assert := assert.New(t)
ms := createTestStore()
req, _ := http.NewRequest("GET", "/dataset?id="+datasetID, nil)
w := httptest.NewRecorder()
s := server{ms}
s.handle(w, req)
assert.Equal(w.Code, http.StatusOK)
}
func TestGetDatasetMissingParam(t *testing.T) {
assert := assert.New(t)
ms := createTestStore()
req, _ := http.NewRequest("GET", "/dataset", nil)
w := httptest.NewRecorder()
s := server{ms}
s.handle(w, req)
assert.Equal(w.Code, http.StatusBadRequest)
}
func TestGetDatasetNotFound(t *testing.T) {
assert := assert.New(t)
ms := createTestStore()
req, _ := http.NewRequest("GET", "/dataset?id=notfound", nil)
w := httptest.NewRecorder()
s := server{ms}
s.handle(w, req)
assert.Equal(w.Code, http.StatusNotFound)
}