Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
104c32a
refactor: backend execute_query/3 returns Adaptor.QueryResult
msmithstubbs Mar 27, 2026
d4bb1da
feat: PostgresAdaptor include total_rows in result
msmithstubbs Mar 27, 2026
c4ddf4d
refactor: decouple SearchLV LQL from BigQuery TableSchema
msmithstubbs Mar 23, 2026
8a3c1d3
fix: dialyzer warnings
msmithstubbs Mar 24, 2026
5591e81
feat: add Postgres support to SearchOperations
msmithstubbs Mar 24, 2026
9e95727
style: alias nested modules (credo)
msmithstubbs Mar 24, 2026
0bcb70a
implement PostgresAdaptor.test_connection/1
msmithstubbs Mar 24, 2026
13c03b6
fix: postgres adaptor aggregates
msmithstubbs Mar 25, 2026
b8a15b5
fix: execute_query can return error (dialyzer)
msmithstubbs Mar 25, 2026
b027992
test: SearchOperations postgres unit tests
msmithstubbs Mar 26, 2026
736c187
merge base: main + refactor/adaptor-query-result
msmithstubbs Mar 27, 2026
6a2e185
feat: Monaco editor with LQL support for search field
msmithstubbs Mar 4, 2026
1a6ef6c
use suggest_saved_searches for autocomplete
msmithstubbs Mar 9, 2026
6e73a4b
fix aggregate autocomplete
msmithstubbs Mar 9, 2026
6d22b71
test: SearchLV postgres backend search
msmithstubbs Mar 26, 2026
fca24e2
ensure enter submits search
msmithstubbs Mar 10, 2026
453c133
Merge branch 'main' into feat/postgres-search
Ziinc Mar 26, 2026
a4e02ef
use SavedSearches.Cache
msmithstubbs Mar 10, 2026
a591794
test tidy up
msmithstubbs Mar 27, 2026
25a78ff
refactor: lql completions
msmithstubbs Mar 10, 2026
6cfb3c7
refactor: move postgres result normalisation to PostgresAdaptor
msmithstubbs Mar 27, 2026
b987cd0
set saved searches on mount
msmithstubbs Mar 10, 2026
e53e043
refactor: handle querystring updates in updated() hook
msmithstubbs Mar 11, 2026
0d4d104
fix: test
msmithstubbs Mar 11, 2026
f7f0608
ui: style suggested fields, source show field to be consistent with m…
msmithstubbs Mar 11, 2026
40ccfdf
test: SearchLV query field has saved searches, schema fields
msmithstubbs Mar 12, 2026
0b224b4
ui: consistency for source search fields
msmithstubbs Mar 12, 2026
249640f
Merge branch 'main' into feat/o11y-1309
Ziinc Mar 12, 2026
39c5410
refactor: handle error in Lql editor mount
msmithstubbs Mar 16, 2026
d71d18a
refactor: simplify lql schema fields passed to client
msmithstubbs Mar 16, 2026
5fece1e
ui: condolidate lql group_by completions into single suggestion
msmithstubbs Mar 16, 2026
2e74eca
ui: place cursor inside c:count completion
msmithstubbs Mar 16, 2026
cf96056
ui: drop `*` from lql select completion
msmithstubbs Mar 16, 2026
c8c9864
ui: consolidate lql completion metadata and timestamp into m: and t:
msmithstubbs Mar 16, 2026
5898ba3
ui: lql search only suggests saved searchs for first char
msmithstubbs Mar 16, 2026
39281f4
test: refute non matching message
msmithstubbs Mar 27, 2026
40674a7
test: add test coverage for lql_language.js
msmithstubbs Mar 16, 2026
31491d6
test: full list of aggregate types
msmithstubbs Mar 27, 2026
02c1327
refactor: move SourceSchema fetch to LiveView
msmithstubbs Mar 19, 2026
7d731f9
bump ci
msmithstubbs Mar 29, 2026
da2c2ef
test: put_sql_string matches on backend_type
msmithstubbs Apr 1, 2026
8630f3e
test: e2e integration test for SearchLV
msmithstubbs Mar 30, 2026
4f4a147
ci: disable static asset gzip encoding in test
msmithstubbs Apr 1, 2026
76bfa27
test: e2e integration test for search chart controls
msmithstubbs Apr 2, 2026
cd80605
chore: bump playwright_ex
msmithstubbs Apr 2, 2026
7caf750
Merge branch 'main' into feat/o11y-1309
msmithstubbs Apr 3, 2026
5414c16
formatting
msmithstubbs Apr 3, 2026
ef8fcfb
Merge branch 'main' into feat/o11y-1309
msmithstubbs Apr 9, 2026
ba4b339
fix: bad merge
msmithstubbs Apr 9, 2026
ba01e3a
Merge branch 'main' into feat/o11y-1309
Ziinc Apr 10, 2026
28a2ed7
Merge branch 'main' into feat/o11y-1309
msmithstubbs Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,8 @@ jobs:
- name: Install dependencies
run: mix do deps.get + deps.compile

- name: Build assets
run: npm run deploy --prefix assets

- name: Run Tests
run: mix test.e2e
20 changes: 20 additions & 0 deletions .github/workflows/elixir-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
pull_request:
branches: ["**"]
paths:
- "assets/**"
- "lib/**"
- "config/**"
- "*.exs"
Expand Down Expand Up @@ -163,3 +164,22 @@ jobs:

- name: Run ${{ matrix.commands.name }}
run: ${{ matrix.commands.run }}

frontend:
name: Frontend Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
cache-dependency-path: assets/package-lock.json

- name: Install frontend dependencies
run: npm ci --prefix assets

- name: Run frontend unit tests
run: npm test --prefix assets
7 changes: 7 additions & 0 deletions assets/css/source_log_search.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@
}
}

// Prevent live_monaco_editor auto-resize from expanding the single-line editor
.lql-editor-wrapper [phx-hook="CodeEditorHook"] {
height: 32px !important;
min-height: 32px !important;
max-height: 32px !important;
}

.message {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
}
Expand Down
5 changes: 3 additions & 2 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import sourceLiveViewHooks from "./source_lv_hooks";
import $ from "jquery";
import moment from "moment";
import { CodeEditorHook } from "../../deps/live_monaco_editor/priv/static/live_monaco_editor.esm"
import LqlEditorWrapper from "./lql_editor_wrapper_hook"


// set moment globally before daterangepicker
Expand All @@ -47,8 +48,8 @@ const hooks = {
...sourceLiveViewHooks,
...LiveModalHooks,
...BillingHooks,
CodeEditorHook

CodeEditorHook,
LqlEditorWrapper,
};

let liveSocket = new LiveSocket("/live", Socket, {
Expand Down
179 changes: 179 additions & 0 deletions assets/js/lql_editor_wrapper_hook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import {
registerLqlLanguage,
registerLqlCompletionProvider,
} from "./lql_language";

const parseSchemaFields = (schemaFieldsJson) => {
if (!schemaFieldsJson) return {};

return JSON.parse(schemaFieldsJson);
};

const parseSuggestedSearches = (suggestedSearchesJson) => {
if (!suggestedSearchesJson) return [];

return JSON.parse(suggestedSearchesJson);
};

const LqlEditorWrapper = {
mounted() {
this._schemaFields = parseSchemaFields(this.el.dataset.schemaFieldsJson);
this._suggestedSearches = parseSuggestedSearches(
this.el.dataset.suggestedSearchesJson,
);
this._completionDisposable = null;
this._editor = null;
this._editorDisposables = [];
this._pendingServerValue = null;
this._handleSubmitRequest = () => {
this.submitSearch();
};
this._handleEditorMounted = (event) => {
const { editor } = event.detail;
const standaloneEditor = editor.standalone_code_editor;

if (this._editor === standaloneEditor && this._completionDisposable) {
return;
}

this.disposeEditorBindings();
this._editor = standaloneEditor;
this.applyPendingEditorValue();

const monaco = window.monaco;

try {
registerLqlLanguage(monaco);
} catch (_) {
console.log("Failed to register LQL language", _);
}

const model = standaloneEditor.getModel();
monaco.editor.setModelLanguage(model, "lql");

this._completionDisposable = registerLqlCompletionProvider(
monaco,
() => this._schemaFields,
() => this._suggestedSearches,
);

standaloneEditor.addCommand(
monaco.KeyCode.Enter,
() => {
this.submitSearch();
},
"!suggestWidgetVisible",
);

this._editorDisposables = [
standaloneEditor.onDidChangeModelContent(() => {
const value = standaloneEditor.getValue();
this.pushEvent("querystring_changed", { querystring: value });
}),
standaloneEditor.onDidFocusEditorText(() => {
this.pushEvent("form_focus", { value: standaloneEditor.getValue() });
}),
standaloneEditor.onDidBlurEditorText(() => {
this.pushEvent("form_blur", { value: standaloneEditor.getValue() });
}),
];
};

this.el.addEventListener("lql:submit", this._handleSubmitRequest);
this.el.addEventListener("lme:editor_mounted", this._handleEditorMounted);
},

updated() {
this._schemaFields = parseSchemaFields(this.el.dataset.schemaFieldsJson);
this._suggestedSearches = parseSuggestedSearches(
this.el.dataset.suggestedSearchesJson,
);
const value = this.el.dataset.querystring ?? "";

if (!this._editor) {
this._pendingServerValue = value;
return;
}

this.setEditorValue(value);
},

applyPendingEditorValue() {
if (!this._editor || this._pendingServerValue === null) {
return;
}

const value = this._pendingServerValue;
this._pendingServerValue = null;
this.setEditorValue(value);
},

setEditorValue(value) {
const currentValue = this._editor?.getValue?.();

if (!this._editor || currentValue === value) {
return;
}

const hadTextFocus = this._editor.hasTextFocus();
const model = this._editor?.getModel?.();
const suggestController = this._editor?.getContribution?.(
"editor.contrib.suggestController",
);

this._editor.setValue(value);

if (hadTextFocus && model) {
const endPosition = {
lineNumber: model.getLineCount(),
column: model.getLineMaxColumn(model.getLineCount()),
};

this._editor.setPosition(endPosition);
suggestController?.cancelSuggestWidget();
}
},

collectRecommendedFields() {
const searchControl =
this.el.closest("#source-logs-search-control") || this.el.parentElement;
const fields = {};

searchControl
?.querySelectorAll('#recommended_fields input[name^="fields["]')
.forEach((input) => {
const match = input.name.match(/^fields\[(.+)\]$/);

if (match) {
fields[match[1]] = input.value;
}
});

return fields;
},

submitSearch() {
const querystring = this._editor?.getValue?.() ?? "";

this.pushEvent("start_search", {
querystring,
fields: this.collectRecommendedFields(),
});
},

disposeEditorBindings() {
this._editorDisposables.forEach((disposable) => disposable?.dispose?.());
this._editorDisposables = [];

if (this._completionDisposable) {
this._completionDisposable.dispose();
this._completionDisposable = null;
}
},

destroyed() {
this.disposeEditorBindings();
},
};

export default LqlEditorWrapper;
Loading
Loading