on master help sign in
connection by database name

refactored error handling

commitb196bf4182b5fa4d27b4e495ca303b0f1f618eea authorAgentM <agentm@themactionfaction.com> dateThu 4 Aug 2016, 11:05 (10 years ago) parent2e1fe6c
7 files changed+63−33
ProjectM36/Client.hs+1−1 ProjectM36/Client/Json.hs+7−1 ProjectM36/Error.hs+1−1 ProjectM36/Server/RemoteCallTypes/Json.hs+4 ProjectM36/Server/WebSocket.hs+29−27 ProjectM36/Server/project-m36.js+4 ProjectM36/Server/websocket-client.js+17−3
ProjectM36/Client.hs+1−1
@@ -145,7 +145,7 @@ data ConnectionError = SetupDatabaseDirectoryError PersistenceError |
145145 IOExceptionError IOException |
146146 NoSuchDatabaseByNameError DatabaseName |
147147 LoginError
148− deriving (Show, Eq)
148+ deriving (Show, Eq, Generic)
149149
150150remoteDBLookupName :: DatabaseName -> String
151151remoteDBLookupName = (++) "db-"
ProjectM36/Client/Json.hs+7−1
@@ -3,6 +3,12 @@ module ProjectM36.Client.Json where
33import Data.Aeson
44import ProjectM36.Server.RemoteCallTypes.Json ()
55import ProjectM36.Client
6+import Control.Exception (IOException)
67
78instance ToJSON EvaluatedNotification
8−instance FromJSON EvaluatedNotification
9+instance FromJSON EvaluatedNotification
10+
11+instance ToJSON ConnectionError
12+
13+instance ToJSON IOException where
14+ toJSON err = object ["IOException" .= show err]
ProjectM36/Error.hs+1−1
@@ -74,7 +74,7 @@ instance NFData RelationalError where rnf = genericRnf
7474
7575data PersistenceError = InvalidDirectoryError FilePath |
7676 MissingTransactionError TransactionId
77− deriving (Show, Eq)
77+ deriving (Show, Eq, Generic)
7878
7979--collapse list of errors into normal error- if there is just one, just return one
8080someErrors :: [RelationalError] -> RelationalError
ProjectM36/Server/RemoteCallTypes/Json.hs+4
@@ -5,6 +5,7 @@ import ProjectM36.Server.RemoteCallTypes
55import ProjectM36.Base
66import ProjectM36.ConcreteTypeRep
77import ProjectM36.Error
8+import Control.Exception (IOException)
89
910import Data.Aeson
1011import Data.UUID.Aeson ()
@@ -152,3 +153,6 @@ instance FromJSON MergeError
152153
153154instance ToJSON MergeStrategy
154155instance FromJSON MergeStrategy
156+
157+instance ToJSON PersistenceError
158+
ProjectM36/Server/WebSocket.hs+29−27
@@ -35,7 +35,7 @@ main = do
3535 --this built-in server is apparently not meant for production use, but it's easier to test than starting up the wai or snap interfaces
3636 putStrLn "listening on 8888"
3737 WS.runServer "0.0.0.0" 8888 (websocketProxyServer serverPort serverHost)
38−
38+
3939websocketProxyServer :: Port -> Hostname -> WS.ServerApp
4040websocketProxyServer port host pending = do
4141 conn <- WS.acceptRequest pending
@@ -46,21 +46,28 @@ websocketProxyServer port host pending = do
4646 if not (connectdbmsg `T.isPrefixOf` dbmsg) then unexpectedMsg >> WS.sendClose conn ("" :: T.Text)
4747 else do
4848 let dbname = T.unpack $ T.drop (T.length connectdbmsg) dbmsg
49− (sessionId, dbconn) <- createConnection conn dbname port host
50− --phase 2- accept tutoriald commands
51− _ <- forever $ do
52− msg <- (WS.receiveData conn) :: IO T.Text
53− let tutdprefix = "executetutd:"
54− case msg of
55− _ | tutdprefix `T.isPrefixOf` msg -> do
56− let tutdString = T.drop (T.length tutdprefix) msg
57− case parseTutorialD (T.unpack tutdString) of
58− Left err -> handleOpResult conn dbconn (DisplayErrorResult ("parse error: " `T.append` T.pack (show err)))
59− Right parsed -> do
60− result <- evalTutorialD sessionId dbconn parsed
61− handleOpResult conn dbconn result
62− _ -> unexpectedMsg
63− pure ()
49+ eDbconn <- createConnection conn dbname port host
50+ case eDbconn of
51+ Left err -> sendError conn err
52+ Right dbconn -> do
53+ eSessionId <- createSessionAtHead "master" dbconn
54+ case eSessionId of
55+ Left err -> sendError conn err
56+ Right sessionId -> do
57+ --phase 2- accept tutoriald commands
58+ _ <- forever $ do
59+ msg <- (WS.receiveData conn) :: IO T.Text
60+ let tutdprefix = "executetutd:"
61+ case msg of
62+ _ | tutdprefix `T.isPrefixOf` msg -> do
63+ let tutdString = T.drop (T.length tutdprefix) msg
64+ case parseTutorialD (T.unpack tutdString) of
65+ Left err -> handleOpResult conn dbconn (DisplayErrorResult ("parse error: " `T.append` T.pack (show err)))
66+ Right parsed -> do
67+ result <- evalTutorialD sessionId dbconn parsed
68+ handleOpResult conn dbconn result
69+ _ -> unexpectedMsg
70+ pure ()
6471
6572notificationCallback :: WS.Connection -> NotificationCallback
6673notificationCallback conn notifName evaldNotif = WS.sendTextData conn (encode (object ["notificationname" .= notifName,
@@ -68,17 +75,12 @@ notificationCallback conn notifName evaldNotif = WS.sendTextData conn (encode (o
6875 ]))
6976
7077--this creates a new database for each connection- perhaps not what we want (?)
71−createConnection :: WS.Connection -> DatabaseName -> Port -> Hostname -> IO (SessionId, Connection)
72−createConnection wsconn dbname port host = do
73− eConn <- connectProjectM36 (RemoteProcessConnectionInfo dbname (createNodeId host port) (notificationCallback wsconn))
74− case eConn of
75− Left err -> error $ "failed to connect to database" ++ show err
76− Right conn -> do
77− eSessionId <- createSessionAtHead "master" conn
78− case eSessionId of
79− Left err -> error $ "failed to create connection on master branch" ++ show err
80− Right sessionId -> pure (sessionId, conn)
81−
78+createConnection :: WS.Connection -> DatabaseName -> Port -> Hostname -> IO (Either ConnectionError Connection)
79+createConnection wsconn dbname port host = connectProjectM36 (RemoteProcessConnectionInfo dbname (createNodeId host port) (notificationCallback wsconn))
80+
81+sendError :: (ToJSON a) => WS.Connection -> a -> IO ()
82+sendError conn err = WS.sendTextData conn (encode (object ["displayerror" .= err]))
83+
8284handleOpResult :: WS.Connection -> Connection -> TutorialDOperatorResult -> IO ()
8385handleOpResult conn db QuitResult = WS.sendClose conn ("close" :: T.Text) >> close db
8486handleOpResult conn _ (DisplayResult out) = WS.sendTextData conn (encode (object ["display" .= out]))
ProjectM36/Server/project-m36.js+4
@@ -58,6 +58,10 @@ ProjectM36Connection.prototype.handleResponse = function(message)
5858
5959 if(error)
6060 {
61+ if(error.tag)
62+ {
63+ error=error.tag; // for error objects
64+ }
6165 this.statuscallback(new ProjectM36Status(null, null, error));
6266 }
6367}
ProjectM36/Server/websocket-client.js+17−3
@@ -66,17 +66,22 @@ function connectOrDisconnect(form)
6666 connectionOpened,
6767 function(e) {console.log("connection error"+e)},
6868 updateStatus,
69− function(e) {});
69+ connectionClosed);
7070 toggleConnectionFields(form, false, "Connecting...");
7171 }
7272 return false;
7373}
7474
75+function connectionClosed(event)
76+{
77+ toggleConnectionFields(document.getElementById("connection"), true, "Connect");
78+}
79+
7580function toggleConnectionFields(form, enabled, status)
7681{
7782 form.elements["connect"].value = status;
7883 var readonlyElements = [form.elements["host"], form.elements["port"], form.elements["dbname"]];
79− for(var ein=0; ein < readonlyElements - 1; ein++)
84+ for(var ein=0; ein < readonlyElements.length; ein++)
8085 {
8186 var e = readonlyElements[ein];
8287 if(enabled)
@@ -98,7 +103,16 @@ function connectionOpened(event)
98103function execTutorialD()
99104{
100105 var tutd = document.getElementById("tutd").value;
101− conn.executeTutorialD(tutd);
106+ if(!window.conn || window.conn.readyState() != 1)
107+ {
108+ var err = document.createElement("span");
109+ err.textContent = "Cannot execute command until a database connection is established.";
110+ appendResult(tutd, err);
111+ }
112+ else
113+ {
114+ conn.executeTutorialD(tutd);
115+ }
102116 return false;
103117}
104118