From 17598ab89b4fb6fbdbb8190c902c243de1e689db Mon Sep 17 00:00:00 2001
From: librelois <elois@ifee.fr>
Date: Sat, 16 Nov 2019 19:19:26 +0100
Subject: [PATCH] [feat] gva: configure host & port

---
 bin/dunitrust-server/src/cli.rs  | 10 ++++
 lib/core/conf/src/lib.rs         |  8 ++++
 lib/core/module/src/lib.rs       |  2 +
 lib/modules/gva/Cargo.toml       |  3 +-
 lib/modules/gva/src/errors.rs    | 26 +++++++++++
 lib/modules/gva/src/lib.rs       | 80 ++++++++++++++++++++++----------
 lib/modules/gva/src/webserver.rs | 18 +++++--
 7 files changed, 116 insertions(+), 31 deletions(-)
 create mode 100644 lib/modules/gva/src/errors.rs

diff --git a/bin/dunitrust-server/src/cli.rs b/bin/dunitrust-server/src/cli.rs
index 3fb1136e..20f451bf 100644
--- a/bin/dunitrust-server/src/cli.rs
+++ b/bin/dunitrust-server/src/cli.rs
@@ -25,6 +25,7 @@ use durs_core::commands::{
 };
 use durs_core::errors::DursCoreError;
 use durs_core::DursCore;
+use durs_gva::{GvaModule, GvaOpt};
 use durs_network::cli::sync::SyncOpt;
 use durs_ws2p_v1_legacy::{WS2POpt, WS2Pv1Module};
 use log::Level;
@@ -68,6 +69,12 @@ impl ExecutableModuleCommand for DursCliOpt {
                     env!("CARGO_PKG_VERSION"),
                 )
             }
+            DursCliSubCommand::Gva(module_opts) => DursCore::execute_module_command::<GvaModule>(
+                options,
+                module_opts,
+                env!("CARGO_PKG_NAME"),
+                env!("CARGO_PKG_VERSION"),
+            ),
             _ => unreachable!(),
         }
     }
@@ -156,6 +163,9 @@ pub enum DursCliSubCommand {
     /// Synchronize
     #[structopt(name = "sync", setting(structopt::clap::AppSettings::ColoredHelp))]
     SyncOpt(SyncOpt),
+    /// GVA module subcommand
+    #[structopt(name = "gva", setting(structopt::clap::AppSettings::ColoredHelp))]
+    Gva(GvaOpt),
     /// WS2P1 module subcommand
     #[structopt(name = "ws2p1", setting(structopt::clap::AppSettings::ColoredHelp))]
     Ws2p1(WS2POpt),
diff --git a/lib/core/conf/src/lib.rs b/lib/core/conf/src/lib.rs
index a1be9932..9141d218 100644
--- a/lib/core/conf/src/lib.rs
+++ b/lib/core/conf/src/lib.rs
@@ -279,6 +279,14 @@ impl DursConfTrait for DuRsConf {
             DuRsConf::V2 { .. } => 2,
         }
     }
+    fn get_currency(&self) -> CurrencyName {
+        match *self {
+            DuRsConf::V1(ref conf_v1) => conf_v1.currency.clone(),
+            DuRsConf::V2 {
+                ref global_conf, ..
+            } => global_conf.currency.clone(),
+        }
+    }
     fn set_currency(&mut self, new_currency: CurrencyName) {
         match *self {
             DuRsConf::V1(ref mut conf_v1) => conf_v1.currency = new_currency,
diff --git a/lib/core/module/src/lib.rs b/lib/core/module/src/lib.rs
index 71400044..2d10c83f 100644
--- a/lib/core/module/src/lib.rs
+++ b/lib/core/module/src/lib.rs
@@ -136,6 +136,8 @@ pub trait DursConfTrait:
     fn my_node_id(&self) -> u32 {
         self.get_global_conf().my_node_id()
     }
+    /// Get currency name
+    fn get_currency(&self) -> CurrencyName;
     /// Set currency
     fn set_currency(&mut self, new_currency: CurrencyName);
     /// Change module conf
diff --git a/lib/modules/gva/Cargo.toml b/lib/modules/gva/Cargo.toml
index b6f91e4c..ea24ecd6 100644
--- a/lib/modules/gva/Cargo.toml
+++ b/lib/modules/gva/Cargo.toml
@@ -18,6 +18,7 @@ durs-conf = { path = "../../core/conf" }
 durs-message =  { path = "../../core/message" }
 durs-module = { path = "../../core/module" }
 durs-network = { path = "../../core/network" }
+durs-network-documents = { path = "../../dunp/network-documents" }
 dubp-common-doc = { path = "../../dubp/common-doc"} #, version = "0.1.0" }
 durs-common-tools = { path = "../../tools/common-tools" }
 dubp-currency-params = { path = "../../dubp/currency-params" }
@@ -31,6 +32,6 @@ log = "0.4.8"
 serde = "1.0.102"
 serde_derive = "1.0.102"
 serde_json = "1.0.41"
-structopt= "0.2.18"
+structopt= "0.3.4"
 
 [features]
diff --git a/lib/modules/gva/src/errors.rs b/lib/modules/gva/src/errors.rs
new file mode 100644
index 00000000..4c10a5ac
--- /dev/null
+++ b/lib/modules/gva/src/errors.rs
@@ -0,0 +1,26 @@
+//  Copyright (C) 2017-2019  The AXIOM TEAM Association.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Gva errors
+
+use failure::Fail;
+
+#[derive(Debug, Fail)]
+/// GVA error
+pub enum GvaError {
+    /// Invalid host
+    #[fail(display = "Invalid host")]
+    InvalidHost,
+}
diff --git a/lib/modules/gva/src/lib.rs b/lib/modules/gva/src/lib.rs
index ce417cdf..1e71e6f6 100644
--- a/lib/modules/gva/src/lib.rs
+++ b/lib/modules/gva/src/lib.rs
@@ -44,9 +44,11 @@ extern crate structopt;
 extern crate juniper;
 
 mod context;
+mod errors;
 mod schema;
 mod webserver;
 
+use crate::errors::GvaError;
 use dubp_currency_params::CurrencyName;
 use durs_common_tools::fatal_error;
 use durs_common_tools::traits::merge::Merge;
@@ -60,6 +62,7 @@ use durs_module::{
 
 //use durs_module::*;
 use durs_network::events::NetworkEvent;
+use durs_network_documents::host::Host;
 
 use std::ops::Deref;
 use std::sync::mpsc;
@@ -68,30 +71,43 @@ use std::time::{Duration, SystemTime};
 
 static MODULE_NAME: &str = "gva";
 
+static DEFAULT_HOST: &str = "127.0.0.1";
+const DEFAULT_PORT: u16 = 10_901;
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Gva Module Configuration
 pub struct GvaConf {
-    test_fake_conf_field: String,
+    host: String,
+    port: u16,
 }
 
 impl Default for GvaConf {
     fn default() -> Self {
         GvaConf {
-            test_fake_conf_field: String::from("default value"),
+            host: DEFAULT_HOST.to_owned(),
+            port: DEFAULT_PORT,
         }
     }
 }
 
+impl std::fmt::Display for GvaConf {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+        write!(f, "host: {}\nport: {}", self.host, self.port,)
+    }
+}
+
 #[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Gva user Configuration
 pub struct GvaUserConf {
-    test_fake_conf_field: Option<String>,
+    host: Option<String>,
+    port: Option<u16>,
 }
 
 impl Merge for GvaUserConf {
     fn merge(self, other: Self) -> Self {
         GvaUserConf {
-            test_fake_conf_field: self.test_fake_conf_field.or(other.test_fake_conf_field),
+            host: self.host.or(other.host),
+            port: self.port.or(other.port),
         }
     }
 }
@@ -110,14 +126,15 @@ pub enum GvaMsg {
 }
 
 #[derive(StructOpt, Debug, Clone)]
-#[structopt(
-    name = "gva",
-    raw(setting = "structopt::clap::AppSettings::ColoredHelp")
-)]
+#[structopt(name = "gva", setting(structopt::clap::AppSettings::ColoredHelp))]
 /// Gva subcommand options
 pub struct GvaOpt {
-    /// Change test conf fake field
-    pub new_conf_field: String,
+    /// Change GVA API host listen
+    #[structopt(long = "host", parse(try_from_str = Host::parse))]
+    pub host: Option<Host>,
+    #[structopt(long = "port")]
+    /// Change GVA API port listen
+    pub port: Option<u16>,
 }
 
 #[derive(Debug, Clone)]
@@ -164,37 +181,50 @@ impl DursModule<DuRsConf, DursMsg> for GvaModule {
         let mut conf = GvaConf::default();
 
         if let Some(ref module_user_conf) = module_user_conf {
-            if let Some(ref test_fake_conf_field) = module_user_conf.test_fake_conf_field {
-                conf.test_fake_conf_field = test_fake_conf_field.to_owned();
+            if let Some(ref host) = module_user_conf.host {
+                conf.host = host.to_owned();
+            }
+            if let Some(port) = module_user_conf.port {
+                conf.port = port;
             }
         }
 
         Ok((conf, module_user_conf))
     }
     fn exec_subcommand(
-        _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
+        soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
         _keys: RequiredKeysContent,
-        module_conf: Self::ModuleConf,
-        _module_user_conf: Option<Self::ModuleUserConf>,
+        _module_conf: Self::ModuleConf,
+        module_user_conf: Option<Self::ModuleUserConf>,
         subcommand_args: Self::ModuleOpt,
     ) -> Option<Self::ModuleUserConf> {
-        let new_gva_conf = GvaUserConf {
-            test_fake_conf_field: Some(subcommand_args.new_conf_field.to_owned()),
-        };
-        info!(
-            "Succesfully exec skeleton subcommand whit terminal name : {} and conf={:?}!",
-            subcommand_args.new_conf_field, module_conf
-        );
-        Some(new_gva_conf)
+        let new_gva_user_conf = GvaUserConf {
+            host: subcommand_args.host.map(|h| h.to_string()),
+            port: subcommand_args.port,
+        }
+        .merge(module_user_conf.unwrap_or_default());
+        match Self::generate_module_conf(
+            Some(&soft_meta_datas.conf.get_currency()),
+            &soft_meta_datas.conf.get_global_conf(),
+            Some(new_gva_user_conf.clone()),
+        ) {
+            Ok((new_gva_conf, _)) => println!("New GVA configuration:\n{}", new_gva_conf),
+            Err(e) => println!("Fail to change GVA confguration : {:?}", e),
+        }
+
+        Some(new_gva_user_conf)
     }
     fn start(
         soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
         _keys: RequiredKeysContent,
-        _conf: Self::ModuleConf,
+        conf: Self::ModuleConf,
         router_sender: mpsc::Sender<RouterThreadMessage<DursMsg>>,
     ) -> Result<(), failure::Error> {
         let _start_time = SystemTime::now();
 
+        // Check conf validity
+        let host = Host::parse(&conf.host).map_err(|_| GvaError::InvalidHost)?;
+
         // Instanciate Gva module datas
         let datas = GvaModuleDatas {
             child_threads: Vec::new(),
@@ -262,7 +292,7 @@ impl DursModule<DuRsConf, DursMsg> for GvaModule {
         let smd: SoftwareMetaDatas<DuRsConf> = soft_meta_datas.clone();
         let router_sender_clone = router_sender.clone();
         thread::spawn(move || {
-            if let Err(e) = webserver::start_web_server(&smd) {
+            if let Err(e) = webserver::start_web_server(&smd, host, conf.port) {
                 error!("GVA http web server error  : {}  ", e);
             } else {
                 info!("GVA http web server stop.")
diff --git a/lib/modules/gva/src/webserver.rs b/lib/modules/gva/src/webserver.rs
index 0f1379fe..89606c6b 100644
--- a/lib/modules/gva/src/webserver.rs
+++ b/lib/modules/gva/src/webserver.rs
@@ -20,6 +20,8 @@ use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer};
 use durs_common_tools::fatal_error;
 use durs_conf::DuRsConf;
 use durs_module::SoftwareMetaDatas;
+use durs_network_documents::host::Host;
+use durs_network_documents::url::Url;
 use futures::future::Future;
 use juniper::http::graphiql::graphiql_source;
 use juniper::http::GraphQLRequest;
@@ -27,7 +29,7 @@ use std::net::SocketAddr;
 use std::sync::Arc;
 
 fn graphiql() -> HttpResponse {
-    let html = graphiql_source("http://127.0.0.1:3000/graphql");
+    let html = graphiql_source("/graphql");
     HttpResponse::Ok()
         .content_type("text/html; charset=utf-8")
         .body(html)
@@ -50,9 +52,15 @@ fn graphql(
     })
 }
 
-pub fn start_web_server(soft_meta_datas: &SoftwareMetaDatas<DuRsConf>) -> std::io::Result<()> {
-    info!("GVA web server start.");
-    let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
+pub fn start_web_server(
+    soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
+    host: Host,
+    port: u16,
+) -> std::io::Result<()> {
+    info!("GVA web server start...");
+
+    let addrs: Vec<SocketAddr> =
+        Url::from_host_port_path(host, port, None).to_listenable_addr("http")?;
 
     // Create Juniper schema
     let schema = std::sync::Arc::new(create_schema());
@@ -73,6 +81,6 @@ pub fn start_web_server(soft_meta_datas: &SoftwareMetaDatas<DuRsConf>) -> std::i
             .service(web::resource("/graphql").route(web::post().to_async(graphql)))
             .service(web::resource("/graphiql").route(web::get().to(graphiql)))
     })
-    .bind(addr)?
+    .bind(&addrs[..])?
     .run()
 }
-- 
GitLab