diff --git a/.travis.yml b/.travis.yml index 4d413ec3b..0e4f44d1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ env: - GOARCH=amd64 - GOARCH=amd64 ENTERPRISE=1 install: - - make install-dep install-statik vendor generate-statik + - make install-dep vendor script: - make test before_deploy: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c991caeab..2ad9ab13d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,7 +78,7 @@ Pilosa includes a Makefile that automates several tasks: make install ``` -- Install build dependencies (dep, statik, and protoc): +- Install build dependencies (dep and protoc): ```sh make install-build-deps @@ -114,12 +114,6 @@ Pilosa includes a Makefile that automates several tasks: make release ``` -- Generate static assets for the WebUI: - - ```sh - make generate-statik - ``` - - Regenerate protocol buffer files in `internal/`: ```sh diff --git a/Dockerfile b/Dockerfile index 7cdb93cc0..2b83c71c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM golang:1.10.2 as builder COPY . /go/src/github.com/pilosa/pilosa/ RUN cd /go/src/github.com/pilosa/pilosa \ - && CGO_ENABLED=0 make install-dep install-statik install FLAGS="-a" + && CGO_ENABLED=0 make install-dep install FLAGS="-a" FROM scratch diff --git a/Makefile b/Makefile index 3f17f0b2e..821ba33b6 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build check-clean clean cover cover-viz default docker docker-build docker-test generate generate-protoc generate-statik install install-build-deps install-dep install-protoc install-protoc-gen-gofast install-statik prerelease prerelease-build prerelease-upload release release-build require-dep require-protoc require-protoc-gen-gofast require-statik test +.PHONY: build check-clean clean cover cover-viz default docker docker-build docker-test generate generate-protoc install install-build-deps install-dep install-protoc install-protoc-gen-gofast prerelease prerelease-build prerelease-upload release release-build require-dep require-protoc require-protoc-gen-gofast test CLONE_URL=github.com/pilosa/pilosa VERSION := $(shell git describe --tags 2> /dev/null || echo unknown) @@ -88,16 +88,12 @@ install: vendor generate-protoc: require-protoc require-protoc-gen-gofast go generate github.com/pilosa/pilosa/internal -# `go generate` statik assets (WebUI) -generate-statik: require-statik - go generate github.com/pilosa/pilosa/statik - # `go generate` stringers generate-stringer: go generate github.com/pilosa/pilosa # `go generate` all needed packages -generate: generate-protoc generate-statik generate-stringer +generate: generate-protoc generate-stringer # Create Docker image from Dockerfile docker: @@ -126,23 +122,17 @@ endef require-dep: $(call require,dep) -require-statik: - $(call require,statik) - require-protoc-gen-gofast: $(call require,protoc-gen-gofast) require-protoc: $(call require,protoc) -install-build-deps: install-dep install-statik install-protoc-gen-gofast install-protoc install-stringer +install-build-deps: install-dep install-protoc-gen-gofast install-protoc install-stringer install-dep: go get -u github.com/golang/dep/cmd/dep -install-statik: - go get -u github.com/rakyll/statik - install-stringer: go get -u golang.org/x/tools/cmd/stringer diff --git a/docs/getting-started.md b/docs/getting-started.md index c5383e81c..276b0bff9 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -112,10 +112,6 @@ Note that both the user IDs and the repository IDs were remapped to sequential i #### Make Some Queries -
-

Note the Pilosa server comes with a WebUI for constructing queries in a browser. In local development, it is available at localhost:10101.

-
- Which repositories did user 14 star: ``` request curl localhost:10101/index/repository/query \ diff --git a/docs/installation.md b/docs/installation.md index cf743721f..639293338 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -142,11 +142,10 @@ There are four ways to install Pilosa on MacOS: Use [Homebrew](https://brew.sh/) git clone https://github.com/pilosa/pilosa.git ``` -3. Build the Pilosa repo (the `make generate-statik` line isn't necessary but builds a nice [webUI](../webui/) into Pilosa): +3. Build the Pilosa repo: ``` cd $GOPATH/src/github.com/pilosa/pilosa make install-build-deps - make generate-statik make install ``` @@ -294,11 +293,10 @@ There are three ways to install Pilosa on Linux: download the binary (recommende git clone https://github.com/pilosa/pilosa.git ``` -3. Build the Pilosa repo (the `make generate-statik` line isn't necessary but builds a nice [webUI](../webui/) into Pilosa): +3. Build the Pilosa repo: ``` cd $GOPATH/src/github.com/pilosa/pilosa make install-build-deps - make generate-statik make install ``` diff --git a/docs/webui.md b/docs/webui.md index 07acd2ab4..94377fbe8 100644 --- a/docs/webui.md +++ b/docs/webui.md @@ -9,13 +9,27 @@ nav = [ ## WebUI -The Pilosa server comes packaged with in-browser WebUI. When you run a local Pilosa server on the default host, you can access it at [localhost:10101](http://localhost:10101). +A web-based app called Pilosa WebUI is available in a separate package. This can be used for constructing queries and viewing the cluster status. -This can be used for constructing queries and viewing the cluster status. +### Installation + +Releases are [available on Github](https://github.com/pilosa/webui/releases) as well as on [Homebrew](https://brew.sh/) for Mac. + +Installing on a Mac with Homebrew is simple; just run: + +``` +brew install pilosa-webui +``` + +You may also build from source by checking out the [repo on Github](https://github.com/pilosa/webui) and running: + +``` +make install +``` ### Console -The [Console view](http://localhost:10101/#console) allows you to enter [PQL](../query-language/) queries and run them against your locally running server. First you must select an Index with the Select index dropdown. +The Console view allows you to enter [PQL](../query-language/) queries and run them against your locally running server. First you must select an Index with the Select index dropdown. Each query's result will be displayed in the Output section along with the query time. @@ -39,4 +53,4 @@ Frame creation also supports options like `timeQuantum`. When creating a new fra ### Cluster Admin -Use the [Cluster Admin tab](http://localhost:10101/#admin) to view the current status of your cluster. This contains information on each node in the cluster, plus the list of Indexes and Frames. +Use the Cluster Admin tab to view the current status of your cluster. This contains information on each node in the cluster, plus the list of Indexes and Frames. diff --git a/filesystem.go b/filesystem.go deleted file mode 100644 index 5664f0987..000000000 --- a/filesystem.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2017 Pilosa Corp. -// -// 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. - -package pilosa - -import ( - "fmt" - "net/http" -) - -// Ensure nopFileSystem implements interface. -var _ FileSystem = &nopFileSystem{} - -// FileSystem represents an interface for a WebUI file system. -type FileSystem interface { - New() (http.FileSystem, error) -} - -func init() { - NopFileSystem = &nopFileSystem{} -} - -// NopFileSystem represents a FileSystem that returns an error if called. -var NopFileSystem FileSystem - -type nopFileSystem struct{} - -// New is a no-op implementation of FileSystem New method. -func (n *nopFileSystem) New() (http.FileSystem, error) { - return nil, fmt.Errorf("file system not implemented") -} diff --git a/handler.go b/handler.go index 083d40279..c4bb364ea 100644 --- a/handler.go +++ b/handler.go @@ -42,8 +42,6 @@ import ( type Handler struct { Handler http.Handler - FileSystem FileSystem - Logger Logger // Keeps the query argument validators for each handler @@ -87,8 +85,7 @@ func OptHandlerAllowedOrigins(origins []string) HandlerOption { // NewHandler returns a new instance of Handler with a default logger. func NewHandler(opts ...HandlerOption) (*Handler, error) { handler := &Handler{ - FileSystem: NopFileSystem, - Logger: NopLogger, + Logger: NopLogger, } handler.Handler = NewRouter(handler) handler.populateValidators() @@ -137,8 +134,7 @@ func (h *Handler) queryArgValidator(next http.Handler) http.Handler { // NewRouter creates a new mux http router. func NewRouter(handler *Handler) *mux.Router { router := mux.NewRouter() - router.HandleFunc("/", handler.handleWebUI).Methods("GET") - router.HandleFunc("/assets/{file}", handler.handleWebUI).Methods("GET") + router.HandleFunc("/", handler.handleHome).Methods("GET") router.HandleFunc("/cluster/message", handler.handlePostClusterMessage).Methods("POST") router.HandleFunc("/cluster/resize/set-coordinator", handler.handlePostClusterResizeSetCoordinator).Methods("POST") router.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux).Methods("GET") @@ -226,19 +222,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } -func (h *Handler) handleWebUI(w http.ResponseWriter, r *http.Request) { - // If user is using curl, don't chuck HTML at them - if strings.HasPrefix(r.UserAgent(), "curl") { - http.Error(w, "Welcome. Pilosa is running. Visit https://www.pilosa.com/docs/ for more information or try the WebUI by visiting this URL in your browser.", http.StatusNotFound) - return - } - filesystem, err := h.FileSystem.New() - if err != nil { - _ = h.writeQueryResponse(w, r, &QueryResponse{Err: err}) - h.Logger.Printf("Pilosa WebUI is not available. Please run `make generate-statik` before building Pilosa with `make install`.") - return - } - http.FileServer(filesystem).ServeHTTP(w, r) +func (h *Handler) handleHome(w http.ResponseWriter, r *http.Request) { + http.Error(w, "Welcome. Pilosa is running. Visit https://www.pilosa.com/docs/ for more information.", http.StatusNotFound) } // handleGetSchema handles GET /schema requests. diff --git a/handler_test.go b/handler_test.go index 036512de4..f9e490d9d 100644 --- a/handler_test.go +++ b/handler_test.go @@ -31,7 +31,6 @@ import ( "github.com/pilosa/pilosa" "github.com/pilosa/pilosa/internal" "github.com/pilosa/pilosa/pql" - "github.com/pilosa/pilosa/statik" "github.com/pilosa/pilosa/test" ) @@ -915,34 +914,6 @@ func TestHandler_RecalculateCaches(t *testing.T) { } -func TestHandler_WebUI(t *testing.T) { - hldr := test.MustOpenHolder() - defer hldr.Close() - - h := test.MustNewHandler() - h.API.Holder = hldr.Holder - h.API.Cluster = test.NewCluster(1) - h.FileSystem = &statik.FileSystem{} - - w := httptest.NewRecorder() - h.ServeHTTP(w, test.MustNewHTTPRequest("GET", "/", nil)) - if w.Code != http.StatusOK { - t.Fatalf("unexpected status code: %d", w.Code) - } - if !strings.Contains(w.Body.String(), "Pilosa WebUI") { - t.Fatalf("WebUI is not being served correctly.") - } - - // If curl is the client, the response should be different - w = httptest.NewRecorder() - req := test.MustNewHTTPRequest("GET", "/", nil) - req.Header.Add("User-Agent", "curl/7.54.0") - h.ServeHTTP(w, req) - if !strings.Contains(w.Body.String(), "try the WebUI") { - t.Fatalf("WebUI is not being served correctly.") - } -} - func TestHandler_CORS(t *testing.T) { hldr := test.MustOpenHolder() defer hldr.Close() diff --git a/server/server.go b/server/server.go index 345807ec2..bb75a1d2e 100644 --- a/server/server.go +++ b/server/server.go @@ -39,7 +39,6 @@ import ( "github.com/pilosa/pilosa/gcnotify" "github.com/pilosa/pilosa/gopsutil" "github.com/pilosa/pilosa/gossip" - "github.com/pilosa/pilosa/statik" "github.com/pilosa/pilosa/statsd" "github.com/pkg/errors" ) @@ -170,7 +169,6 @@ func (m *Command) SetupServer() error { return errors.Wrap(err, "wrapping handler") } handler.Logger = m.logger - handler.FileSystem = &statik.FileSystem{} handler.API = pilosa.NewAPI() handler.API.Logger = m.logger diff --git a/statik/.gitignore b/statik/.gitignore deleted file mode 100644 index 485c0c57d..000000000 --- a/statik/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/statik.go diff --git a/statik/filesystem.go b/statik/filesystem.go deleted file mode 100644 index e3bf95cb1..000000000 --- a/statik/filesystem.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2017 Pilosa Corp. -// -// 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. -// -//go:generate statik -src=../webui -dest=.. -// -// Package statik contains static assets for the Web UI. `go generate` or -// `make generate-statik` will produce statik.go, which is ignored by git. -package statik - -import ( - "net/http" - - "github.com/pilosa/pilosa" - "github.com/rakyll/statik/fs" -) - -// Ensure nopFileSystem implements interface. -var _ pilosa.FileSystem = &FileSystem{} - -// FileSystem represents a static FileSystem. -type FileSystem struct{} - -// New is a statik implementation of FileSystem New method. -func (s *FileSystem) New() (http.FileSystem, error) { - return fs.New() -} diff --git a/webui/assets/chevron-down.png b/webui/assets/chevron-down.png deleted file mode 100644 index 3312489a2..000000000 Binary files a/webui/assets/chevron-down.png and /dev/null differ diff --git a/webui/assets/main.js b/webui/assets/main.js deleted file mode 100644 index 265f51997..000000000 --- a/webui/assets/main.js +++ /dev/null @@ -1,594 +0,0 @@ -class REPL { - constructor(input, output, button, completer) { - this.input = input - this.output = output - this.button = button - this.completer = completer - this.history = [] - this.history_index = 0 - this.history_buffer = '' - this.result_number = 0 - } - bind_events() { - var repl = this - var keys = { - TAB: 9, - ENTER: 13, - UP_ARROW: 38, - DOWN_ARROW: 40 - } - - this.input.addEventListener("keydown", function(e) { - if (e.keyCode == keys.UP_ARROW) { - e.preventDefault() - if (repl.input.value.substring(0, repl.input.selectionStart).indexOf('\n') == '-1') { - if (repl.history_index == 0) { - return - } else { - if (repl.history_index == repl.history.length) { - repl.history_buffer = repl.input.value - } - repl.history_index-- - repl.input.value = repl.history[repl.history_index] - repl.input.setSelectionRange(repl.input.value.length, repl.input.value.length) - } - } - } - if (e.keyCode == keys.DOWN_ARROW) { - e.preventDefault() - if (repl.input.value.substring(repl.input.selectionEnd, repl.input.length).indexOf('\n') == '-1') { - if (repl.history_index == repl.history.length) { - return - } else { - repl.history_index++ - if (repl.history_index == repl.history.length) { - repl.input.value = repl.history_buffer - } else { - repl.input.value = repl.history[repl.history_index] - } - repl.input.setSelectionRange(repl.input.value.length, repl.input.value.length) - } - } - } - if (e.keyCode == keys.ENTER && !e.shiftKey) { - e.preventDefault() - repl.submit(); - } - if (e.keyCode == keys.TAB) { - e.preventDefault() - repl.completer.complete() - } - }) - repl.button.onclick = function() { - repl.submit(); - }; - } - - submit() { - this.history_buffer = '' - this.history_index = this.history.length - this.history[this.history_index] = this.input.value - this.history_index++ - this.process_query(this.input.value) - this.input.value = "" - } - - process_query(query) { - var xhr = new XMLHttpRequest(); - var url, data, request, command_name; - var e = document.getElementById("index-dropdown"); - var indexname = e.options[e.selectedIndex].text; - var repl = this; - if (query.startsWith(":")) { - var parsed_query = parse_query(query, indexname); - if (Object.keys(parsed_query).length === 0) { - repl.create_single_output({ - "input": query, - "output": "invalid query", - "status": 400, - "indexname": indexname, - }); - return; - } else { - // set selectedIndex from dropdown list - if (parsed_query.command === "use") { - for (var i = 0; i < e.options.length; i++) { - if (e.options[i].text === parsed_query.command_name) { - e.selectedIndex = i; - break; - } - } - return; - } - url = parsed_query.url; - data = parsed_query.data; - request = parsed_query.request; - command_name = parsed_query.command_name; - } - } - else { - url = '/index/' + indexname + '/query' - request = "POST" - data = query - } - xhr.open(request, url); - xhr.setRequestHeader('Content-Type', 'application/text'); - - var start_time = new Date().getTime(); - xhr.onload = function () { - var end_time = new Date().getTime(); - repl.result_number++ - repl.create_single_output({ - "input": query, - "output": xhr.responseText, - "status": xhr.status, - "indexname": indexname, - "querytime_ms": end_time - start_time, - }); - }; - - xhr.send(data); - // Remove index from dropdown with delete index command - if (request === 'DELETE' && url === '/index/' + command_name){ - for (var i = 0; i < e.options.length; i++) { - if (e.options[i].text === command_name) { - e.remove(i); - break; - } - } - } - } - - create_single_output(res) { - var node = document.createElement("div"); - node.classList.add('output'); - var output_string = res['output'] - var result_class = "result-output" - var getting_started_errors = [ - 'index not found', - 'frame not found', - ] - var output_json; - if (isJSON(output_string)) { - output_json = JSON.parse(output_string) - } - // handle output formatting - if (res["status"] != 200) { - result_class = "result-error"; - if (output_json) { - if ("error" in output_json) { - if (getting_started_errors.indexOf(output_json['error']) >= 0) { - output_string += `
-
- Just getting started? Try this:
- :create index test
- :use test
- :create frame foo
- SetBit(rowID=0, columnID=0, frame=foo) # Use PQL to set a bit - ` - } - } - } - } - - - var markup =` -
-
-
-
-
Input
-        - Source: ${res.indexname} -
-
- ${res.input} -
-
-
-
-
output
-        - ${res.querytime_ms} ms -
-
- ${output_string} -
-
Expand
- -
-
-
- -
-
- ` - node.innerHTML = markup; - this.output.insertBefore(node, this.output.firstChild); - - // Expand when overflow - var element = this.output.firstChild.getElementsByClassName(result_class)[0]; - var expand = this.output.firstChild.getElementsByClassName("expand")[0]; - if (element.clientHeight < element.scrollHeight) { - expand.style.display = 'block'; - } else { - expand.style.display = 'none'; - } - expand.onclick = function () { - element.style.height = element.scrollHeight + "px"; - expand.style.display = 'none'; - return false; - }; - } - - populate_index_dropdown() { - var xhr = new XMLHttpRequest(); - xhr.open('GET', '/schema') - var select = document.getElementById('index-dropdown') - - xhr.onload = function() { - var schema = JSON.parse(xhr.responseText) - for(var i=0; i 0) { - select.value = 1; - } - } - xhr.send(null) - } - -} - -function populate_version() { - var xhr = new XMLHttpRequest(); - xhr.open('GET', '/version') - var node = document.getElementById('server-version') - - xhr.onload = function() { - var version = JSON.parse(xhr.responseText)['version'] - var version_major_minor = /v?(\d+\.\d+).*/.exec(version)[1] - var doc_link = document.getElementById('nav-documentation') - doc_link.onclick = function() { - window.open('https://www.pilosa.com/docs/v' + version_major_minor + '/introduction/') - } - node.innerHTML = "Pilosa v" + version - } - xhr.send(null) -} - -function handle_nav_click(e) { - // e.id = "nav-xxx" - name = e.id.substring(4) - set_active_pane_by_name(name) - window.location.hash = name -} - -function set_active_pane_by_name(name) { - // toggle the nav buttons - document.getElementsByClassName("nav-active")[0].classList.remove("nav-active") - document.getElementById("nav-" + name).classList.add("nav-active") - - // toggle the main interface content divs - document.getElementsByClassName("interface-active")[0].classList.remove("interface-active") - document.getElementById('interface-' + name).classList.add("interface-active") - - // hack hack - switch(name) { - case "cluster": - update_cluster_status() - break - case "documentation": - open_external_docs() - break - } -} - - -function update_cluster_status() { - var xhr = new XMLHttpRequest(); - xhr.open('GET', '/status') - xhr.onload = function() { - var status = JSON.parse(xhr.responseText) - render_status(status) - } - xhr.send(null) - - var xhrSchema = new XMLHttpRequest(); - xhrSchema.open('GET', '/schema') - xhrSchema.onload = function() { - var schema = JSON.parse(xhrSchema.responseText) - render_schema(schema) - } - xhrSchema.send(null) -} - -function render_status(status) { - // render node table - var nodes_div = document.getElementById("status-nodes") - while (nodes_div.firstChild) { - nodes_div.removeChild(nodes_div.firstChild); - } - - var nodes = status["nodes"] - table = document.createElement("table") - tbody = document.createElement("tbody") - table.appendChild(tbody) - var caption = document.createElement("caption") - caption.innerHTML = "(" + nodes.length + ")" - table.appendChild(caption) - - var header = document.createElement('tr') - markup = `Host - ID - Coordinator` - header.innerHTML = markup - tbody.appendChild(header) - for(var n=0; n${nodes[n]["uri"]["host"]}:${nodes[n]["uri"]["port"]} - ${nodes[n]["id"]} - ${nodes[n]["isCoordinator"]}` - row.innerHTML = markup - tbody.appendChild(row) - } - nodes_div.appendChild(table) -} - -function render_schema(schema) { - // render index tables - var indexes_div = document.getElementById("status-indexes") - while (indexes_div.firstChild) { - indexes_div.removeChild(indexes_div.firstChild); - } - - var indexes = schema["indexes"] // TODO currently comes from only node 0 - for(var n=0; nName - Cache Type - Cache Size` - header.innerHTML = markup - tbody.appendChild(header) - - var frames = indexes[n]["frames"] - if(frames) { - for(var m=0; m${frames[m]["name"]} - ${frames[m]["options"]["cacheType"]} - ${frames[m]["options"]["cacheSize"]}` - tbody.appendChild(row) - } - } - indexes_div.appendChild(table) - } - - // render slice tables - // TODO enable when Slices element is present in status response - /* - var slices_div = document.getElementById("status-slices") - data = "" - for(var n=0; n" - } - } - slices_div.innerHTML = data - */ - -} - -function open_external_docs() { - window.open("https://www.pilosa.com/docs"); -} - -function check_anchor_uri() { - var pane_names = {"console": 0, "cluster": 0, "documentation": 0} - var anchor = window.location.hash.substr(1); - if(anchor in pane_names) { - set_active_pane_by_name(anchor) - } -} - -Date.prototype.today = function () { - return this.getFullYear() +"/"+ (((this.getMonth()+1) < 10)?"0":"") + (this.getMonth()+1) +"/"+ ((this.getDate() < 10)?"0":"") + this.getDate(); -} - -Date.prototype.timeNow = function () { - return ((this.getHours() < 10)?"0":"") + this.getHours() +":"+ ((this.getMinutes() < 10)?"0":"") + this.getMinutes() +":"+ ((this.getSeconds() < 10)?"0":"") + this.getSeconds(); -} - -populate_version() - - -class Autocompleter { - constructor(input, output) { - this.input = input - this.output = output - this.keyword_map = this.static_keywords - this.init_dynamic_keywords() - } - - get static_keywords() { - return { - // keyword: length of substring that comes after cursor - "SetBit()": 1, - "ClearBit()": 1, - "SetRowAttrs()": 1, - "SetColumnAttrs()": 1, - "Bitmap()": 1, - "Union()": 1, - "Intersect()": 1, - "Difference()": 1, - "Count()": 1, - "Range()": 1, - "TopN()": 1, - "frame=": 0, - } - } - - complete() { - var completer = this - // extract word fragment ending at cursor. a word fragment: - // - starts with last nonalpha character before cursor (or beginning of string) - // - ends at cursor - var word_start = completer.input.selectionEnd-1 - while(word_start>0) { - var c = completer.input.value.charCodeAt(word_start) - if(!((c>64 && c<91) || (c>96 && c<123))) { - word_start++ - break - } - word_start-- - } - var input_word = completer.input.value.substring(word_start, completer.input.selectionEnd) - - // check for keyword match and insert if exactly one match - var matches = [] - for(var keyword in this.keyword_map) { - if(keyword.startsWith(input_word)){ - matches.push(keyword) - } - } - if(matches.length > 1) { - // completer.output.innerHTML = whatever - } - - if(matches.length == 1) { - // completer.output.innerHTML = "" - var cursor_pos = completer.input.selectionEnd - var completion = matches[0].substring(input_word.length) - var before = completer.input.value.substring(0, cursor_pos) - var after = completer.input.value.substring(cursor_pos) - completer.input.value = before + completion + after - var new_pos = cursor_pos + completion.length - this.keyword_map[matches[0]] - completer.input.setSelectionRange(new_pos, new_pos) - } - } - - init_dynamic_keywords() { - // hit /schema, parse indexes, frames, add to list - } - - add_keyword() { - // call when index or frame created in webui - } - - remove_keyword() { - // call when index or frame deleted in webui - // issue: if e.g. multiple indexes have same frame, removing one removes all. - // solution: maintain count. requires more elaborate representation of keywords. - } -} - -var input = document.getElementById('query') -var output = document.getElementById('outputs') -var button = document.getElementById('query-btn') -var autocomplete_output = document.getElementById('autocomplete-container') - -autocompleter = new Autocompleter(input, autocomplete_output) -repl = new REPL(input, output, button, autocompleter) -repl.populate_index_dropdown() -repl.bind_events() - -input.focus() - -check_anchor_uri() - -function isJSON(str) { - try { - JSON.parse(str) - } catch (e) { - return false - } - return true -} - -function parse_query(query, indexname) { - var keys = query.replace(/\s+/g, " ").split(" "); - var command = keys[0]; - var command_type = keys[1]; - var command_name = keys[2]; - var option_str = keys.slice(3, keys.length) - var options = parse_options(option_str); - if (command !== ":use") { - if (!command_name){ - return {} - } - } - - var parsed_query = {}; - parsed_query["command"] = command.substr(1, command.length); - parsed_query["command_name"] = command_name; - switch (command) { - case ":create": - parsed_query["request"] = "POST"; - if(Object.keys(options).length === 0) { - parsed_query["data"] = ""; - } else { - var opts = {"options":{}}; - for (var o in options) { - opts.options[o] = options[o] - } - parsed_query["data"] = JSON.stringify(opts); - } - switch (command_type){ - case "index": - parsed_query["url"] = '/index/' + command_name; - break; - case "frame": - parsed_query["url"] = '/index/' + indexname + '/frame/' + command_name; - break - } - break; - case ":delete": - parsed_query["request"] = "DELETE"; - switch (command_type){ - case "index": - parsed_query["url"] = '/index/' + command_name; - parsed_query["data"] = ""; - break; - case "frame": - parsed_query["url"] = '/index/' + indexname + '/frame/' + command_name; - parsed_query["data"] = ""; - break; - } - break; - case ":use": - parsed_query["command_name"] = keys[1]; - break; - default: - return {} - } - return parsed_query; -} - -function parse_options(option_str) { - var int_keys = ["cacheSize"]; - var options = {}; - for (var i = 0; i < option_str.length; i++) { - var parts = option_str[i].split('='); - if (int_keys.indexOf(parts[0]) !== -1 ){ - options[parts[0]] = Number(parts[1]) - } else { - options[parts[0]] = parts[1] - } - } - return options; -} diff --git a/webui/assets/nav-cluster-active.svg b/webui/assets/nav-cluster-active.svg deleted file mode 100644 index 871d6f2af..000000000 --- a/webui/assets/nav-cluster-active.svg +++ /dev/null @@ -1 +0,0 @@ -nav_cluster_1 diff --git a/webui/assets/nav-cluster.svg b/webui/assets/nav-cluster.svg deleted file mode 100644 index baf6310ea..000000000 --- a/webui/assets/nav-cluster.svg +++ /dev/null @@ -1 +0,0 @@ -nav_cluster_1 \ No newline at end of file diff --git a/webui/assets/nav-console-active.svg b/webui/assets/nav-console-active.svg deleted file mode 100644 index 2263c2a69..000000000 --- a/webui/assets/nav-console-active.svg +++ /dev/null @@ -1 +0,0 @@ -nav_console \ No newline at end of file diff --git a/webui/assets/nav-console.svg b/webui/assets/nav-console.svg deleted file mode 100644 index e9d2a9569..000000000 --- a/webui/assets/nav-console.svg +++ /dev/null @@ -1 +0,0 @@ -nav_console diff --git a/webui/assets/nav-documentation-active.svg b/webui/assets/nav-documentation-active.svg deleted file mode 100644 index b6e994980..000000000 --- a/webui/assets/nav-documentation-active.svg +++ /dev/null @@ -1 +0,0 @@ -documentation diff --git a/webui/assets/nav-documentation.svg b/webui/assets/nav-documentation.svg deleted file mode 100644 index 12cd2108c..000000000 --- a/webui/assets/nav-documentation.svg +++ /dev/null @@ -1 +0,0 @@ -documentation \ No newline at end of file diff --git a/webui/assets/nav_item1.svg b/webui/assets/nav_item1.svg deleted file mode 100644 index d6d7f779e..000000000 --- a/webui/assets/nav_item1.svg +++ /dev/null @@ -1 +0,0 @@ -nav_item1 \ No newline at end of file diff --git a/webui/assets/style.css b/webui/assets/style.css deleted file mode 100644 index 2cf65d086..000000000 --- a/webui/assets/style.css +++ /dev/null @@ -1,336 +0,0 @@ -*{ - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -body{ - font-family: sans-serif; - background-color: #fbfcfd; - margin: 0; - color: #102445; -} -h2{ - margin-bottom: 30px; -} - -h5{ - text-transform: uppercase; - letter-spacing: 2px; - line-height: 1.21; - margin: 0; -} -a{ - line-height: 1.38; - letter-spacing: 0.2px; - text-decoration: none; - color: #102445; -} - - -a:hover{ - color: #1db598; -} - -textarea{ - width: 100%; - margin-bottom: 10px; - border-radius: 2px; - background-color: #fbfcfd; - border: solid 1.5px #e4eff4; - font-family: monospace; - font-size: 16px; - line-height: 1.5; - letter-spacing: 1.1px; - outline: none; - padding: 30px; -} - - -select{ - /*-webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: url("img/chevron-down.png") no-repeat calc(100% - 10px) !important;*/ - border-radius: 3px; - background-color: #fbfcfd; - width: 187px; - height: 50px; - border: solid 1.5px #e4eff4; - font-size: 18px; - font-weight: bold; - line-height: 1.39; - letter-spacing: 0.2px; - color: #102445; - padding: 10.5px; - -} - -button{ - width: 165px; - height: 50px; - border-radius: 3px; - background-color: #1db598; - outline: none; - border: none; - font-size: 16px; - color: white; -} - -em{ - font-style: normal; - opacity: 0.5; - font-size: 14px; - font-weight: 500; - letter-spacing: 0.2px; - color: #102445; -} - -.header{ - height: 92px; - display: flex; - align-items: center; - justify-content: space-between; - width: 90%; - margin: auto; -} - -.container{ - display: flex; - height:100%; - min-height: 100vh; -} -.nav{ - color: white; - display: flex; - flex-direction: column; - width: 150px; - background: #3c5f8d; -} - -.nav-item{ - height:150px; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - border-bottom: 3px solid #2a4871; - cursor: pointer; -} - -.nav-active{ - background: #f2f7f9; - font-weight: bold; - color: #1db598; -} - -.nav-item > .nav-image { - display: flex; -} - -.nav-item > .nav-image-active { - display: none; -} - -.nav-active > .nav-image { - display: none; -} - -.nav-active > .nav-image-active { - display: flex; -} - - -.interface{ - display: none; - flex: 1; - flex-direction: column; - align-items: center; - background: #f2f7f9; -} - -.interface-active{ - display: flex; -} - -.query{ - margin-bottom: 30px; -} -.query, -.output-container, -.status-container{ - width: 75%; -} - -.output{ - margin-bottom: 30px; -} - -.input-controls{ - display: flex; - justify-content: flex-end; -} - -.tabs{ - display: flex; - background: #eaf2f6; -} -.active-tab{ - background: white; - font-weight: bold; - color: #1db598; - -} - -.tab{ - height:60px; - width: 100px; - border-top-right-radius: 5px; - display: flex; - align-items: center; - justify-content: center; - visibility: visible; - cursor: pointer; - -} - -.pane{ - background: white; - padding: 30px; - display: none; -} - -.active{ - display: block; -} - -.result-io-header{ - display: flex; - align-items: center; - margin-bottom: 15px; -} - -.result-input, -.result-output, -.result-error{ - height: 60px; - border-radius: 2px; - background-color: #fafafa; - border: solid 1.5px #e4eff4; - font-family: monospace; - font-size: 16px; - line-height: 1.5; - letter-spacing: 1.1px; - color: #102445; - padding: 15px; - margin-bottom: 15px; - word-break: break-all; - overflow-wrap: break-word; - overflow:hidden; -} - - -.result-output{ - background-color: #edf9f7; - border-left: solid 4px #1db598; -} - -.result-error{ - background-color: #fbf1f0; - border-left: solid 4px #fa3035; - color: #fa3035; -} - -.raw{ - height: 253px; - display: flex; - align-items: center; - justify-content: center; -} - - -.result-table > table { - border-left: solid 4px #1db598; -} - -table{ - border: solid 0.5px #e0e0e0; - width: 100%; - margin-bottom: 30px; - /*color:#3c5f8d;*/ -} -caption{ - text-align:left; - font-size: 16px; - font-weight: bold; - line-height: 1.21; - letter-spacing: 2px; - text-align: left; -} -th{ - font-size: 14px; - font-weight: bold; - line-height: 1.21; - letter-spacing: 2px; - color: #102445; - text-transform: uppercase; - text-align: left; - padding: 21px 30px; - background-color: white; -} -tr{ - border: solid 0.5px #e0e0e0; - background-color: white; -} -tr:nth-child(even) { - background-color: #f2f7f9; -} -td{ - padding: 21px 30px; -} - -.expand { - text-align: center; -} - -.query h2 { - display: inline-block; -} - -.query-tooltip { - position: relative; - display: inline; - color: #000; - margin-left: 5px; -} - -.query-tooltip:hover { - color: #000; -} - -.query-tooltip-content { - background-color: rgb(250, 250, 250); - border: solid 1.5px #e4eff4; - color: #102445; - border-radius: 2px; - padding: 15px; - margin-bottom: 15px; - - position: absolute; - left: 80px; - top: -30px; - z-index: 1; -} - -.query-tooltip-container { - position: relative; - visibility: hidden; -} - -.query-tooltip:hover+.query-tooltip-container{ - visibility: visible; -} - -.code{ - font-family: monospace; -} - diff --git a/webui/index.html b/webui/index.html deleted file mode 100644 index f6bc369de..000000000 --- a/webui/index.html +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - Pilosa WebUI - - - -
- -
-
-
- -
- -
-

Query

- ? -
-
-
PQL
-
- SetBit(frame=foo, row=0, col=0)
- ClearBit(frame=foo, row=0, col=0)
- SetRowAttrs(frame=foo, row=0, color="blue")
- SetColumnAttrs(frame=foo, col=0, shape="circle")
- SetFieldValue(frame=foo, col=0, age=30)
- Bitmap(frame=foo, row=0)
- Range(frame=foo, row=0, start="2010-01", end="2017-03")
- Count(<BITMAP_CALL>)
- TopN([BITMAP_CALL], frame=foo, n=20)
- Union([BITMAP_CALL, ...])
- Intersect(<BITMAP_CALL>, [BITMAP_CALL, ...])
- Difference(<BITMAP_CALL>, <BITMAP_CALL>)
- Xor([BITMAP_CALL, ...])
- Min([BITMAP_CALL], frame=foo, field=age)
- Max([BITMAP_CALL], frame=foo, field=age)
- Sum([BITMAP_CALL], frame=foo, field=age) -
-
-
Special commands
-
- :create index test
- :use test
- :create frame foo
- :delete index test
- :delete frame foo -
-
- <tab>: autocomplete
- <up>/<down>: history
-
-
- -
-
- -     - -
-
-
- -
-

Output

-
- -
-
- -
- -
-
-

Nodes

-
-
-
-
-

Indexes

-
-
-
-
- -
- -
- -
- docs! -
- -
- - -