diff --git a/Makefile b/Makefile index a4339fe..8e93124 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ RUNNER_FLAGS := --with-appdir \ --env=AT_SPI_BUS_ADDRESS=unix:path=/run/flatpak/at-spi-bus \ --env=DESKTOP_SESSION=$(DESKTOP_SESSION) \ --env=LANG=$(LANG) \ + --env=G_MESSAGES_DEBUG=Tarug \ --env=WAYLAND_DISPLAY=$(WAYLAND_DISPLAY) \ --env=XDG_CURRENT_DESKTOP=$(XDG_CURRENT_DESKTOP) \ --env=XDG_SESSION_DESKTOP=$(XDG_SESSION_DESKTOP) \ @@ -108,6 +109,9 @@ lsp: meson: flatpak build $(RUNNER_FLAGS) $(REPO_DIR) /usr/bin/meson +shell: + flatpak build $(RUNNER_FLAGS) $(REPO_DIR) /usr/bin/bash + test: flatpak build $(RUNNER_FLAGS) $(REPO_DIR) /usr/bin/meson test -C $(BUILD_DIR) diff --git a/resources/gtk/connection-view.blp b/resources/gtk/connection-view.blp index 31db19c..e69b81b 100644 --- a/resources/gtk/connection-view.blp +++ b/resources/gtk/connection-view.blp @@ -126,16 +126,6 @@ template $TarugConnectionView: Adw.Bin { } } - // $TarugConnectionSidebar sidebar { - // width-request: 300; - // connections: bind template.viewmodel as <$TarugConnectionViewModel>.connections; - // selected-connection: bind template.viewmodel as <$TarugConnectionViewModel>.selected-connection; - // request-new-connection => $add_new_connection(); - // request_dup_connection => $dup_connection(); - // request_remove_connection => $remove_connection(); - // request_connect_database => $active_connection(); - // } - [end] Adw.Bin { child: WindowHandle { @@ -279,7 +269,7 @@ template $TarugConnectionView: Adw.Bin { } Entry host_entry { - placeholder-text: "localhost"; + placeholder-text: "Example: localhost"; activate => $on_entry_activated(); changed => $on_text_changed(); @@ -301,7 +291,7 @@ template $TarugConnectionView: Adw.Bin { } Entry port_entry { - placeholder-text: "5432"; + placeholder-text: "i.e 5432"; activate => $on_entry_activated(); changed => $on_text_changed(); @@ -313,7 +303,6 @@ template $TarugConnectionView: Adw.Bin { } Entry user_entry { - placeholder-text: "postgres"; activate => $on_entry_activated(); changed => $on_text_changed(); @@ -325,7 +314,6 @@ template $TarugConnectionView: Adw.Bin { } PasswordEntry password_entry { - placeholder-text: ""; show-peek-icon: true; activate => $on_entry_activated(); changed => $on_text_changed(); @@ -413,7 +401,7 @@ template $TarugConnectionView: Adw.Bin { spacing: 8; Spinner spinner { - spinning: bind template.viewmodel as <$TarugConnectionViewModel>.is-connectting; + spinning: bind template.viewmodel as <$TarugConnectionViewModel>.is_pending; } Button connect_btn { @@ -431,15 +419,5 @@ template $TarugConnectionView: Adw.Bin { } }; } - - // $TarugConnectionForm form { - // width-request: 800; - // selected-connection: bind sidebar.selected-connection; - // is-connectting: bind template.viewmodel as <$TarugConnectionViewModel>.is-connectting; - // current-state: bind template.viewmodel as <$TarugConnectionViewModel>.current-state; - // menu-model: primary_menu; - // request-database => $active_connection (); - // connections-changed => $save_connections (); - // } } } diff --git a/src/services/SQLService.vala b/src/services/SQLService.vala index 6cb1874..4a187f4 100644 --- a/src/services/SQLService.vala +++ b/src/services/SQLService.vala @@ -57,7 +57,6 @@ namespace Tarug { var db_url = build_connection_string(conn); debug("Connecting to %s", db_url); start_connect (db_url); - /* * Begin the polling loop to keep checking the connection is good Reference: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PQCONNECTSTARTPARAMS @@ -122,12 +121,56 @@ namespace Tarug { } } - private string build_connection_string(Connection conn) { + private string build_connection_string(Connection conn) throws TarugError { + + long port = long.parse (conn.port); + if (port == 0) { + throw new TarugError.CONNECTION_ERROR("Port `%s` must be a number".printf (conn.port)); + } else if (port <= 0 || port >= 65535) { + throw new TarugError.CONNECTION_ERROR("Port `%ld` not in range [0-65535]".printf (port)); + } + + string user = conn.user.strip(); + if (user == "") { + throw new TarugError.CONNECTION_ERROR("User must not be blank"); + } + + + string password = conn.password.strip(); + if (password == "") { + throw new TarugError.CONNECTION_ERROR("Password must not be blank"); + } + + string host = conn.host.strip(); + if (host == "") { + throw new TarugError.CONNECTION_ERROR("Host must not be blank"); + } + + string dbname = conn.database.strip(); + if (dbname == "") { + throw new TarugError.CONNECTION_ERROR("Database must not be blank"); + } + var connection_timeout = settings.get_int("connection-timeout"); var query_timeout = settings.get_int("query-timeout"); - string db_url = conn.connection_string(connection_timeout, query_timeout); + var options = @"\'-c statement_timeout=$(query_timeout * 1000)\'"; + + var builder = new StringBuilder(""); + builder.append_printf ("user=%s ", user); + builder.append_printf ("password=%s ", password); + builder.append_printf ("sslmode=%s ", conn.use_ssl ? "verify-full" : "disable"); + builder.append_printf ("host=%s ", host); + builder.append_printf ("port=%ld ", port); + builder.append_printf ("dbname=%s ", dbname); + builder.append_printf ("application_name=%s ", Config.APP_NAME); + builder.append_printf ("connect_timeout=%d ", connection_timeout); + builder.append_printf ("options=%s ", options); + if (conn.use_ssl) { + builder.append(@" sslrootcert=$(conn.cert_path)"); + } + - return db_url; + return(builder.free_and_steal()); } public async Relation exec_query (Query query) throws TarugError { diff --git a/src/ui/views/ConnectionView.vala b/src/ui/views/ConnectionView.vala index 7ecc1ac..e90512f 100644 --- a/src/ui/views/ConnectionView.vala +++ b/src/ui/views/ConnectionView.vala @@ -23,12 +23,6 @@ namespace Tarug { construct { setup_paned(paned); this.viewmodel = autowire (); - - this.err_dialog = new Adw.AlertDialog("Connection Failed", null) { - default_response = "okay", - }; - this.err_dialog.add_response("okay", "OK"); - var action_group = new SimpleActionGroup(); action_group.add_action_entries(ACTION_ENTRIES, this); this.insert_action_group("conn", action_group); @@ -162,14 +156,8 @@ namespace Tarug { // Save ref so it does not be cleaned by GC this.bindings = create_form_bind_group(); - viewmodel.bind_property("selected-connection", this.bindings, "source", BindingFlags.SYNC_CREATE); - viewmodel.bind_property("is-connectting", connect_btn, "sensitive", INVERT_BOOLEAN | SYNC_CREATE); - viewmodel.bind_property("err-msg", this.err_dialog, "body", SYNC_CREATE); - viewmodel.notify["current-state"].connect(() => { - if (viewmodel.current_state == ConnectionViewModel.State.ERROR) { - this.err_dialog.present(this); - } - }); + viewmodel.bind_property("selected_connection", this.bindings, "source", BindingFlags.SYNC_CREATE); + viewmodel.bind_property("is_pending", connect_btn, "sensitive", INVERT_BOOLEAN | SYNC_CREATE); selection_model.bind_property("selected", viewmodel, "selected-connection", DEFAULT | BIDIRECTIONAL, from_selected, to_selected); @@ -183,11 +171,17 @@ namespace Tarug { return(true); }); + + viewmodel.connect_database_failed.connect((error) => { + var d = new Gtk.AlertDialog ("Connection Failed") { + detail = error + }; + d.show (get_parrent_window (this)); + }); } private BindingGroup create_form_bind_group (){ var binddings = new BindingGroup(); - // debug ("set_up connection form bindings group"); binddings.bind("name", name_entry, "text", SYNC_CREATE | BIDIRECTIONAL); binddings.bind("host", host_entry, "text", SYNC_CREATE | BIDIRECTIONAL); binddings.bind("port", port_entry, "text", SYNC_CREATE | BIDIRECTIONAL); @@ -196,8 +190,6 @@ namespace Tarug { binddings.bind("database", database_entry, "text", SYNC_CREATE | BIDIRECTIONAL); binddings.bind("use_ssl", ssl_switch, "active", SYNC_CREATE | BIDIRECTIONAL); binddings.bind("cert_path", cert_path, "text", SYNC_CREATE | BIDIRECTIONAL); - // debug ("set_up binddings done"); - return(binddings); } diff --git a/src/utils/types.vala b/src/utils/types.vala index dd8311c..50e3207 100644 --- a/src/utils/types.vala +++ b/src/utils/types.vala @@ -65,6 +65,16 @@ namespace Tarug { return result.error == null; } + public async void sleep(int milliseconds) { + Source source = new TimeoutSource (milliseconds); + source.set_callback (() => { + sleep.callback (); + return false; // run once + }); + source.attach (MainContext.default ()); + yield; + } + public class Vec: Object { static int DEFAULT_CAPACITY = 64; diff --git a/src/viewmodels/ConnectionViewModel.vala b/src/viewmodels/ConnectionViewModel.vala index 72b70ef..e1c5a34 100644 --- a/src/viewmodels/ConnectionViewModel.vala +++ b/src/viewmodels/ConnectionViewModel.vala @@ -1,12 +1,5 @@ namespace Tarug { public class ConnectionViewModel : BaseViewModel { - public enum State { - IDLE, - CONNECTING, - ERROR - } - - uint timeout_id = 0; public ConnectionRepository repository { get; private set; } public SQLService sql_service { get; private set; } @@ -14,15 +7,14 @@ namespace Tarug { - // States - - public State current_state { get; private set; default = State.IDLE; } - public string err_msg { get; private set; default = "hello world"; } + // Props + public bool is_pending { get; private set; default = false;} public ObservableList connections { get; private set; default = new ObservableList (); } public Connection ? selected_connection { get; set; } - /** True when trying to establish a connection util know results. */ - public bool is_connectting { get; set; default = false; } + // Signals + public signal void connect_database_failed(string error_message); + public ConnectionViewModel(ConnectionRepository repository, SQLService sql_service, NavigationService navigation_service){ base(); @@ -36,14 +28,6 @@ namespace Tarug { if (connections.empty()) { // new_connection(); } - - this.bind_property("current-state", this, "is-connectting", SYNC_CREATE, from_state_to_connecting); - - // Auto save data each 10 secs in case app crash. - // Timeout.add_seconds (10, () => { - // repository.save (connections.to_list ()); - // return Source.CONTINUE; - // }, Priority.LOW); } public void new_connection (){ @@ -87,20 +71,21 @@ namespace Tarug { } public async void active_connection (Connection connection){ - this.current_state = State.CONNECTING; try { + this.is_pending = true; yield sql_service.connect_db (connection); EventBus.instance().connection_active(connection); } catch (TarugError err) { - this.err_msg = err.message.dup(); debug("Error: %s", err.message); - this.current_state = State.ERROR; + this.connect_database_failed(err.message.dup ()); + this.is_pending = false; return; } - this.current_state = State.IDLE; + this.is_pending = false; } + public List export_connections (){ return(repository.find_all()); } @@ -116,16 +101,5 @@ namespace Tarug { return(Source.REMOVE); }); } - - private bool from_state_to_connecting (Binding binding, Value from, ref Value to){ - ConnectionViewModel.State state = (ConnectionViewModel.State) from.get_enum(); - if (state == ConnectionViewModel.State.CONNECTING) { - to.set_boolean(true); - } else { - to.set_boolean(false); - } - - return(true); - } } }