From 8b60eb6961ca4875f7d73c33b892e6322ccf70cb Mon Sep 17 00:00:00 2001 From: TzeYiing Date: Tue, 3 Sep 2024 03:27:39 +0800 Subject: [PATCH 1/4] feat: implement query page --- assets/css/app.scss | 2 + assets/js/app.js | 5 + assets/js/interfaces/DataTable.jsx | 13 + assets/js/interfaces/index.js | 1 + assets/package-lock.json | 104 +++++--- assets/package.json | 3 +- lib/logflare_web/live/query_live.ex | 242 ++++++++++++++++++ lib/logflare_web/router.ex | 6 + .../templates/source/dashboard.html.heex | 5 +- mix.exs | 3 +- mix.lock | 1 + .../live_views/query_live_test.exs | 43 ++++ 12 files changed, 396 insertions(+), 32 deletions(-) create mode 100644 assets/js/interfaces/DataTable.jsx create mode 100644 lib/logflare_web/live/query_live.ex create mode 100644 test/logflare_web/live_views/query_live_test.exs diff --git a/assets/css/app.scss b/assets/css/app.scss index ebc81ae59..9ad737513 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -1,5 +1,7 @@ /* This file is for your main application css. */ @import "~bootstrap/scss/bootstrap"; +@import "../../deps/live_monaco_editor/priv/static/live_monaco_editor.min.css"; + @import "named_variables"; @import "dashboard"; @import "header"; diff --git a/assets/js/app.js b/assets/js/app.js index 7b7484ed3..1e6d0579e 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,6 +1,7 @@ import "../css/app.scss"; import { Socket } from "phoenix"; import "../css/tailwind.css"; + import "bootstrap"; import ClipboardJS from "clipboard"; import * as Dashboard from "./dashboard"; @@ -19,6 +20,8 @@ import sourceLiveViewHooks from "./source_lv_hooks"; import logsLiveViewHooks from "./log_event_live_hooks"; import $ from "jquery"; import moment from "moment"; +import { CodeEditorHook } from "../../deps/live_monaco_editor/priv/static/live_monaco_editor.esm" + // set moment globally before daterangepicker window.moment = moment; @@ -50,6 +53,8 @@ const hooks = { ...logsLiveViewHooks, ...LiveModalHooks, ...BillingHooks, + CodeEditorHook + }; let liveSocket = new LiveSocket("/live", Socket, { diff --git a/assets/js/interfaces/DataTable.jsx b/assets/js/interfaces/DataTable.jsx new file mode 100644 index 000000000..29a62f984 --- /dev/null +++ b/assets/js/interfaces/DataTable.jsx @@ -0,0 +1,13 @@ + +import DataGrid from 'react-data-grid'; + +function DataTable({columns, rows} = props) { + console.log(props) + return rows[i]} + rowsCount={3} + minHeight={150} + />; +} + +export default DataTable \ No newline at end of file diff --git a/assets/js/interfaces/index.js b/assets/js/interfaces/index.js index c13eaff46..2c3d0d294 100644 --- a/assets/js/interfaces/index.js +++ b/assets/js/interfaces/index.js @@ -1,3 +1,4 @@ export {default as EndpointEditor} from "./EndpointEditor.jsx" export {default as EndpointsBrowserList} from "./EndpointsBrowserList.jsx" export {default as ShowEndpoint} from "./ShowEndpoint.jsx" +export {default as DataTable} from "./DataTable.jsx" diff --git a/assets/package-lock.json b/assets/package-lock.json index f0bd55d4a..26067f88f 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -26,6 +26,7 @@ "phoenix_live_view": "file:../deps/phoenix_live_view", "react": "^16.11.0", "react-bootstrap": "^1.6.6", + "react-data-grid": "^6.1.0", "react-dom": "^16.11.0", "react-spinners": "^0.9.0", "sql-formatter": "^2.3.3" @@ -38,20 +39,23 @@ "glob": "^10.3.1", "postcss": "^8.4.31", "sass": "^1.58.3", - "tailwindcss": "^3.3.2" + "tailwindcss": "^3.4.10" } }, "../deps/phoenix": { - "version": "0.0.1" + "version": "1.7.10", + "license": "MIT" }, "../deps/phoenix_html": { - "version": "0.0.1" + "version": "3.3.3" }, "../deps/phoenix_live_react": { - "version": "0.0.1" + "version": "0.4.2", + "license": "MIT" }, "../deps/phoenix_live_view": { - "version": "0.0.1" + "version": "0.20.1", + "license": "MIT" }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", @@ -1685,9 +1689,9 @@ } }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -2046,9 +2050,9 @@ } }, "node_modules/jiti": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", - "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "dev": true, "bin": { "jiti": "bin/jiti.js" @@ -2643,6 +2647,20 @@ "react-dom": ">=16.8.0" } }, + "node_modules/react-data-grid": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-data-grid/-/react-data-grid-6.1.0.tgz", + "integrity": "sha512-N1UtiHvsowEPzhx0VPqQKvGgSza/YNljczbisFDGMjawiGApS2taMv7h+EDXDx49CdaA6ur4eYS0z10x63IUpw==", + "dependencies": { + "object-assign": "^4.1.1", + "react-is-deprecated": "^0.1.2", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.0.0", + "react-dom": "^16.0.0" + } + }, "node_modules/react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", @@ -2662,6 +2680,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-is-deprecated": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/react-is-deprecated/-/react-is-deprecated-0.1.2.tgz", + "integrity": "sha512-n3Y04lqbuwMiSywwAKBwW89YxAPuFwS5tYA4L6wDGLQCdSsod1KSfzCIiTTUvS9hPdaB39HdvxjxAaS0Lk4h+A==" + }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -2827,6 +2850,11 @@ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3078,9 +3106,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", - "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", + "integrity": "sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==", "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -3088,10 +3116,10 @@ "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.2.12", + "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.18.2", + "jiti": "^1.21.0", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", @@ -3103,7 +3131,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, @@ -4517,9 +4544,9 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -4787,9 +4814,9 @@ } }, "jiti": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", - "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "dev": true }, "jquery": { @@ -5190,6 +5217,16 @@ "warning": "^4.0.3" } }, + "react-data-grid": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-data-grid/-/react-data-grid-6.1.0.tgz", + "integrity": "sha512-N1UtiHvsowEPzhx0VPqQKvGgSza/YNljczbisFDGMjawiGApS2taMv7h+EDXDx49CdaA6ur4eYS0z10x63IUpw==", + "requires": { + "object-assign": "^4.1.1", + "react-is-deprecated": "^0.1.2", + "shallowequal": "^1.1.0" + } + }, "react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", @@ -5206,6 +5243,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-is-deprecated": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/react-is-deprecated/-/react-is-deprecated-0.1.2.tgz", + "integrity": "sha512-n3Y04lqbuwMiSywwAKBwW89YxAPuFwS5tYA4L6wDGLQCdSsod1KSfzCIiTTUvS9hPdaB39HdvxjxAaS0Lk4h+A==" + }, "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -5323,6 +5365,11 @@ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==" }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5505,9 +5552,9 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "tailwindcss": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", - "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", + "integrity": "sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==", "dev": true, "requires": { "@alloc/quick-lru": "^5.2.0", @@ -5515,10 +5562,10 @@ "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.2.12", + "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.18.2", + "jiti": "^1.21.0", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", @@ -5530,7 +5577,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, diff --git a/assets/package.json b/assets/package.json index 7e99b6b64..d79e26c3f 100644 --- a/assets/package.json +++ b/assets/package.json @@ -26,6 +26,7 @@ "phoenix_live_view": "file:../deps/phoenix_live_view", "react": "^16.11.0", "react-bootstrap": "^1.6.6", + "react-data-grid": "^6.1.0", "react-dom": "^16.11.0", "react-spinners": "^0.9.0", "sql-formatter": "^2.3.3" @@ -38,6 +39,6 @@ "glob": "^10.3.1", "postcss": "^8.4.31", "sass": "^1.58.3", - "tailwindcss": "^3.3.2" + "tailwindcss": "^3.4.10" } } diff --git a/lib/logflare_web/live/query_live.ex b/lib/logflare_web/live/query_live.ex new file mode 100644 index 000000000..a529c00f7 --- /dev/null +++ b/lib/logflare_web/live/query_live.ex @@ -0,0 +1,242 @@ +defmodule LogflareWeb.QueryLive do + @moduledoc false + use LogflareWeb, :live_view + use Phoenix.Component + + require Logger + + alias Logflare.Endpoints + alias Logflare.Users + + def render(assigns) do + ~H""" + <.subheader> + <:path> + ~/<.subheader_path_link live_patch to={~p"/query"}>query + + + +
+

+ Query your data with BigQuery SQL directly. You can refer to source names directly in your SELECT queries, for example
+ SELECT datetime(timestamp) as timestamp, event_message, metadata from `MyApp.Logs` + Some pointers: +

    +
  • Always have a filter over the timestamp column in your WHERE clause
  • +
  • Use CROSS JOIN UNNEST(my_table.my_column) as col to use nested fields in your query
  • +
  • Smaller time ranges load faster
  • +
+ Read the docs + to find out more about querying Logflare with BigQuery SQL +

+
+
+ <.form for={%{}} phx-submit="run-query" class="tw-min-h-[80px] tw-flex tw-flex-col tw-gap-4"> + "on", + "language" => "sql", + "fontSize" => 12, + "padding" => %{ + "top" => 14 + }, + "fixedOverflowWidgets" => false, + "contextmenu" => false, + "hideCursorInOverviewRuler" => true, + "smoothScrolling" => true, + "scrollbar" => %{ + "vertical" => "auto", + "horizontal" => "hidden", + "verticalScrollbarSize" => 6 + }, + "lineNumbers" => "off", + "glyphMargin" => false, + "lineNumbersMinChars" => 0, + "folding" => false, + "roundedSelection" => true, + "editorClassName" => "", + "minimap" => %{ + "enabled" => false + }, + "placeholder" => "SELECT timestamp, event_message from `MyApp.Source`" + } + ) + } + /> +
+ <%= submit("Run query", class: "btn btn-secondary") %> +
+ + +
+ <.alert variant="warning"> + SQL Parse error! +
+ <%= @parse_error_message %> + +
+
+ +
+

Query result

+

+ No rows returned from query. Try adjusting your query and try again! +

+
+ <% keys = Map.keys(hd(@query_result_rows)) |> Enum.sort() %> + + + + + + + + + + + +
<%= k %>
+ <%= case value = Map.get(row, k) do %> + <% value when is_map(value) or is_list(value) -> %> + + <% value -> %> + + <%= value %> + + <% end %> + + +
+
+
+ """ + end + + def mount(%{}, %{"user_id" => user_id} = params, socket) do + user = Users.get(user_id) + + query_string = + Map.get( + params, + "q", + "SELECT id, timestamp, metadata, event_message \nFROM `YourSource` \nWHERE timestamp > '#{DateTime.utc_now() |> DateTime.to_iso8601()}'" + ) + + socket = + socket + |> assign(:user_id, user_id) + |> assign(:user, user) + |> assign(:query_result_rows, nil) + |> assign(:parse_error_message, nil) + |> assign(:query_string, query_string) + + {:ok, socket} + end + + def handle_params(params, _uri, socket) do + query_string = params["q"] || socket.assigns.query_string + + socket = + socket + |> assign(:query_string, query_string) + |> then(fn + socket when query_string != "" -> + case Endpoints.parse_query_string(query_string) do + {:ok, _} -> + socket + + {:error, err} -> + assign( + socket, + :parse_error_message, + if(is_binary(err), do: err, else: inspect(err)) + ) + |> assign(:query_result_rows, nil) + end + + socket -> + socket + end) + + {:noreply, socket} + end + + def handle_event( + "run-query", + %{"live_monaco_editor" => %{"query" => query_string}}, + %{assigns: %{user: user}} = socket + ) do + socket = + run_query(socket, user, query_string) + |> push_patch(to: ~p"/query?#{%{q: query_string}}") + + {:noreply, socket} + end + + def handle_event( + "parse-query", + %{"value" => query_string}, + socket + ) do + socket = + case Endpoints.parse_query_string(query_string) do + {:ok, _} -> + socket + |> assign(:parse_error_message, nil) + + {:error, err} -> + error = if(is_binary(err), do: err, else: inspect(err)) + + socket + |> assign(:parse_error_message, error) + end + |> assign(:query_string, query_string) + + {:noreply, socket} + end + + def handle_event("parse-query", %{"_target" => ["live_monaco_editor", _]}, socket) do + # ignore change events from the editor field + {:noreply, socket} + end + + defp run_query(socket, user, query_string) do + case Endpoints.run_query_string(user, {:bq_sql, query_string}, params: %{}) do + {:ok, %{rows: rows}} -> + socket + |> put_flash(:info, "Ran query successfully") + |> assign(:query_result_rows, rows) + + {:error, err} -> + socket + |> put_flash(:error, "Error occured when running query: #{inspect(err)}") + end + end +end diff --git a/lib/logflare_web/router.ex b/lib/logflare_web/router.ex index e3830c10d..4b623311c 100644 --- a/lib/logflare_web/router.ex +++ b/lib/logflare_web/router.ex @@ -176,6 +176,12 @@ defmodule LogflareWeb.Router do live("/backends/:id/edit", BackendsLive, :edit) end + scope "/query", LogflareWeb do + pipe_through([:browser, :require_auth]) + + live("/", QueryLive, :index) + end + scope "/endpoints", LogflareWeb do pipe_through([:browser, :require_auth]) diff --git a/lib/logflare_web/templates/source/dashboard.html.heex b/lib/logflare_web/templates/source/dashboard.html.heex index 56a2fa263..d17d6ba13 100644 --- a/lib/logflare_web/templates/source/dashboard.html.heex +++ b/lib/logflare_web/templates/source/dashboard.html.heex @@ -87,7 +87,10 @@
- <.link href={~p"/sources/new"} class="btn btn-primary btn-sm" id="new-source-button"> + <.link href={~p"/query"} class="btn btn-primary btn-sm"> + Run a query + + <.link href={~p"/sources/new"} class="btn btn-primary btn-sm"> New source
diff --git a/mix.exs b/mix.exs index 91c15b993..44a31629e 100644 --- a/mix.exs +++ b/mix.exs @@ -225,7 +225,8 @@ defmodule Logflare.Mixfile do {:opentelemetry_api, "~> 1.2"}, {:opentelemetry_exporter, "~> 1.6"}, {:opentelemetry_phoenix, "~> 1.1"}, - {:opentelemetry_cowboy, "~> 0.2"} + {:opentelemetry_cowboy, "~> 0.2"}, + {:live_monaco_editor, "~> 0.1"} ] end diff --git a/mix.lock b/mix.lock index fc379db6f..24bae1459 100644 --- a/mix.lock +++ b/mix.lock @@ -74,6 +74,7 @@ "jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"}, "key_tools": {:hex, :key_tools, "0.4.1", "4bdf5a39190dc465e58f0c44784b7bb5300bafbbfff2b4ada4d7ec3bfde8d470", [:mix], [], "hexpm", "1a5afce636176481acec2db91066e68af5bf3c512327292a14078ca1aad1a57e"}, "libcluster": {:hex, :libcluster, "3.3.3", "a4f17721a19004cfc4467268e17cff8b1f951befe428975dd4f6f7b84d927fe0", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "7c0a2275a0bb83c07acd17dab3c3bfb4897b145106750eeccc62d302e3bdfee5"}, + "live_monaco_editor": {:hex, :live_monaco_editor, "0.1.8", "149c02cab1c595fe2d2049cffb0a424db2a329a5fa848ee8b778d5acd8694733", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "9a56e88a61cdf6d58081627e4842f4e3a8e3a75dd8749f271a464164ad4530f1"}, "local_cluster": {:hex, :local_cluster, "1.2.1", "8eab3b8a387680f0872eacfb1a8bd5a91cb1d4d61256eec6a655b07ac7030c73", [:mix], [{:global_flags, "~> 1.0", [hex: :global_flags, repo: "hexpm", optional: false]}], "hexpm", "aae80c9bc92c911cb0be085fdeea2a9f5b88f81b6bec2ff1fec244bb0acc232c"}, "logflare_api_client": {:hex, :logflare_api_client, "0.3.5", "c427ebf65a8402d68b056d4a5ef3e1eb3b90c0ad1d0de97d1fe23807e0c1b113", [:mix], [{:bertex, "~> 1.3", [hex: :bertex, repo: "hexpm", optional: false]}, {:finch, "~> 0.10", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:tesla, "~> 1.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "16d29abcb80c4f72745cdf943379da02a201504813c3aa12b4d4acb0302b7723"}, "logflare_etso": {:hex, :logflare_etso, "1.1.2", "040bd3e482aaf0ed20080743b7562242ec5079fd88a6f9c8ce5d8298818292e9", [:mix], [{:ecto, "~> 3.8", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "ab96be42900730a49b132891f43a9be1d52e4ad3ee9ed9cb92565c5f87345117"}, diff --git a/test/logflare_web/live_views/query_live_test.exs b/test/logflare_web/live_views/query_live_test.exs new file mode 100644 index 000000000..54a62dba6 --- /dev/null +++ b/test/logflare_web/live_views/query_live_test.exs @@ -0,0 +1,43 @@ +defmodule LogflareWeb.QueryLiveTest do + @moduledoc false + use LogflareWeb.ConnCase + + setup %{conn: conn} do + insert(:plan) + user = insert(:user) + conn = login_user(conn, user) + {:ok, user: user, conn: conn} + end + + describe "query page" do + test "run a valid query", %{conn: conn} do + GoogleApi.BigQuery.V2.Api.Jobs + |> expect(:bigquery_jobs_query, 1, fn _conn, _proj_id, _opts -> + {:ok, TestUtils.gen_bq_response([%{"ts" => "some-data"}])} + end) + + {:ok, view, _html} = live(conn, "/query") + + # link to show + view + |> element("form") + |> render_submit(%{ + live_monaco_editor: %{ + query: "select current_timestamp() as ts" + } + }) =~ "Ran query successfully" + + assert_patch(view) =~ ~r/current_timestamp/ + assert render(view) =~ "some-data" + end + + test "parser error", %{conn: conn} do + {:ok, view, _html} = live(conn, "/query") + + assert view + |> render_hook("parse-query", %{ + value: "select current_datetime() order-by invalid" + }) =~ "parser error" + end + end +end From fcff7accd62f89fe70aa2324520fed1b0474e3f6 Mon Sep 17 00:00:00 2001 From: TzeYiing Date: Tue, 3 Sep 2024 03:42:20 +0800 Subject: [PATCH 2/4] docs: add in-app docs on query composition --- lib/logflare_web/live/query_live.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/logflare_web/live/query_live.ex b/lib/logflare_web/live/query_live.ex index a529c00f7..437b6deb1 100644 --- a/lib/logflare_web/live/query_live.ex +++ b/lib/logflare_web/live/query_live.ex @@ -25,6 +25,7 @@ defmodule LogflareWeb.QueryLive do
  • Always have a filter over the timestamp column in your WHERE clause
  • Use CROSS JOIN UNNEST(my_table.my_column) as col to use nested fields in your query
  • Smaller time ranges load faster
  • +
  • Endpoint and alert queries can be referenced using `MyEndpointName` for query composition
  • Read the docs to find out more about querying Logflare with BigQuery SQL From 1a70d9d15501b8200c808666240e23392092db64 Mon Sep 17 00:00:00 2001 From: TzeYiing Date: Tue, 3 Sep 2024 14:00:35 +0800 Subject: [PATCH 3/4] chore: version bump --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ff2fd4fbe..9eadd6baa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.8.5 \ No newline at end of file +1.8.6 \ No newline at end of file From 4d4a16742f131254334ce35fb65ec142a67af699 Mon Sep 17 00:00:00 2001 From: TzeYiing Date: Tue, 3 Sep 2024 14:02:50 +0800 Subject: [PATCH 4/4] chore: remove react-data-grid --- assets/js/interfaces/DataTable.jsx | 13 --------- assets/js/interfaces/index.js | 1 - assets/package-lock.json | 45 ------------------------------ assets/package.json | 1 - 4 files changed, 60 deletions(-) delete mode 100644 assets/js/interfaces/DataTable.jsx diff --git a/assets/js/interfaces/DataTable.jsx b/assets/js/interfaces/DataTable.jsx deleted file mode 100644 index 29a62f984..000000000 --- a/assets/js/interfaces/DataTable.jsx +++ /dev/null @@ -1,13 +0,0 @@ - -import DataGrid from 'react-data-grid'; - -function DataTable({columns, rows} = props) { - console.log(props) - return rows[i]} - rowsCount={3} - minHeight={150} - />; -} - -export default DataTable \ No newline at end of file diff --git a/assets/js/interfaces/index.js b/assets/js/interfaces/index.js index 2c3d0d294..c13eaff46 100644 --- a/assets/js/interfaces/index.js +++ b/assets/js/interfaces/index.js @@ -1,4 +1,3 @@ export {default as EndpointEditor} from "./EndpointEditor.jsx" export {default as EndpointsBrowserList} from "./EndpointsBrowserList.jsx" export {default as ShowEndpoint} from "./ShowEndpoint.jsx" -export {default as DataTable} from "./DataTable.jsx" diff --git a/assets/package-lock.json b/assets/package-lock.json index 26067f88f..48ce771d7 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -26,7 +26,6 @@ "phoenix_live_view": "file:../deps/phoenix_live_view", "react": "^16.11.0", "react-bootstrap": "^1.6.6", - "react-data-grid": "^6.1.0", "react-dom": "^16.11.0", "react-spinners": "^0.9.0", "sql-formatter": "^2.3.3" @@ -2647,20 +2646,6 @@ "react-dom": ">=16.8.0" } }, - "node_modules/react-data-grid": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-data-grid/-/react-data-grid-6.1.0.tgz", - "integrity": "sha512-N1UtiHvsowEPzhx0VPqQKvGgSza/YNljczbisFDGMjawiGApS2taMv7h+EDXDx49CdaA6ur4eYS0z10x63IUpw==", - "dependencies": { - "object-assign": "^4.1.1", - "react-is-deprecated": "^0.1.2", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": "^16.0.0", - "react-dom": "^16.0.0" - } - }, "node_modules/react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", @@ -2680,11 +2665,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/react-is-deprecated": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/react-is-deprecated/-/react-is-deprecated-0.1.2.tgz", - "integrity": "sha512-n3Y04lqbuwMiSywwAKBwW89YxAPuFwS5tYA4L6wDGLQCdSsod1KSfzCIiTTUvS9hPdaB39HdvxjxAaS0Lk4h+A==" - }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -2850,11 +2830,6 @@ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==" }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5217,16 +5192,6 @@ "warning": "^4.0.3" } }, - "react-data-grid": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-data-grid/-/react-data-grid-6.1.0.tgz", - "integrity": "sha512-N1UtiHvsowEPzhx0VPqQKvGgSza/YNljczbisFDGMjawiGApS2taMv7h+EDXDx49CdaA6ur4eYS0z10x63IUpw==", - "requires": { - "object-assign": "^4.1.1", - "react-is-deprecated": "^0.1.2", - "shallowequal": "^1.1.0" - } - }, "react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", @@ -5243,11 +5208,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "react-is-deprecated": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/react-is-deprecated/-/react-is-deprecated-0.1.2.tgz", - "integrity": "sha512-n3Y04lqbuwMiSywwAKBwW89YxAPuFwS5tYA4L6wDGLQCdSsod1KSfzCIiTTUvS9hPdaB39HdvxjxAaS0Lk4h+A==" - }, "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -5365,11 +5325,6 @@ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==" }, - "shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/assets/package.json b/assets/package.json index d79e26c3f..0995486c7 100644 --- a/assets/package.json +++ b/assets/package.json @@ -26,7 +26,6 @@ "phoenix_live_view": "file:../deps/phoenix_live_view", "react": "^16.11.0", "react-bootstrap": "^1.6.6", - "react-data-grid": "^6.1.0", "react-dom": "^16.11.0", "react-spinners": "^0.9.0", "sql-formatter": "^2.3.3"