Repository: HapticX/happyx (language: python)

Issue:
Exception thrown on invalid value for Enum in model
An exception is thrown when an incorrect value is received for an enum parameter in a model. Don't know how to handle the exception

This code throws an exception when the POST is sent with an incorrect value in 'k'
```
import happyx

type
  Kind = enum
    circle
    square

model Obj:
  k: Kind
  st: string

serve("127.0.0.1", 5000):
  post "/[data:Obj:json]":
    echo data
    return ""
```

This is run with:
* happyx-3.9.0
* Nim devel
* OS: Archlinux



Discussion / hints:
In the next commit on `dev` branch new route type will be added:

```nim
import happyx

type
  Kind = enum
    circle
    square

model Obj:
  k: Kind
  st: string

serve("127.0.0.1", 5000):
  post "/[data:Obj:json]":
    echo data
    return ""
  onException:
    # here built-in variables:
    # `url`, `body` is string
    # `e` is `ref Exception`
    echo "URL: ", url
    echo "BODY: ", body
    echo "EXCEPTION: [", e.name, "] - ", e.msg
```


## Examples
Input is:
```http
POST / HTTP/1.1
Host: localhost:5000
Content-Type: application/json
Cache-Control: no-cache

{"k": "circl1e", "st": "hello"}
```
Output is:
```
URL: /
BODY: {"k": "circl1e", "st": "hello"}
EXCEPTION: [ValueError] - Invalid enum value: circl1e
```

Input is:
```http
POST / HTTP/1.1
Host: localhost:5000
Content-Type: application/json
Cache-Control: no-cache

{"k": "circl1e", "st":
```
Output is:
```
URL: /
BODY: {"k": "circl1e", "st":
EXCEPTION: [JsonParsingError] - input(1, 22) Error: { expected
```
Using the current dev branch I get the following error at compile time:

> /home/charly/tmp/nim/rest/MetrotelAPI/happyx/src/happyx/spa/components.nim(1039, 18) Error: > undeclared identifier: 'initTagVm'
> candidates (edit distance, scope distance); see '--spellSuggest':
>  (2, 5): 'initTag'

I'm not sure if I'm using the dev branch correctly. I cloned the repo in the same directory as the example program, checked out the dev branch and imported with

``` Nim
import ./happyx/src/happyx
```

@bluemax75, this error was solved in the last commit on `dev` 👀
Great. After updating the new endpoint onException is working
Thank you very much. Excellent work you are doing
I'm afraid that onException is not using the value of statusCode
If I try to return 422 error code with the following code:

```Nim
import ./happyx/src/happyx

type
  Kind = enum
    circle
    square

model Obj:
  k: Kind
  st: string

serve("127.0.0.1", 5000):
  post "/[data:Obj:json]":
    echo data
    return ""

  onException:
    echo "Exception"
    echo e.name
    echo e.msg
    statusCode = 422
    return "Exception"
```

I get a 200 Ok response code

Fix patch:
diff --git a/src/happyx.nim b/src/happyx.nim
index a8f8a5d64..1bd57bcfa 100755
--- a/src/happyx.nim
+++ b/src/happyx.nim
@@ -302,7 +302,7 @@ when not defined(js):
 import
   happyx/core/[exceptions, constants, queries],
   happyx/sugar/[use, sgr, js, style],
-  happyx/spa/[renderer, state, components, translatable, tag],
+  happyx/spa/[renderer, state, components, translatable, tag, spa_utils],
   happyx/tmpl_engine/[engine],
   happyx/routing/[mounting, routing, decorators],
   happyx/ssr/utils
@@ -329,7 +329,8 @@ export
   mounting,
   sgr,
   js,
-  utils
+  utils,
+  spa_utils
 
 
 # Language bindings
diff --git a/src/happyx/private/macro_utils.nim b/src/happyx/private/macro_utils.nim
index 787c87798..156da364b 100644
--- a/src/happyx/private/macro_utils.nim
+++ b/src/happyx/private/macro_utils.nim
@@ -882,18 +882,40 @@ proc buildHtmlProcedure*(root, body: NimNode, inComponent: bool = false,
             cycleVar = " + " & cycleTmpVar  & ")}"
             registerEvent = fmt"registerEventScoped{uniqueId.value}{uniqueId.value+2}"
             callRegister = newCall(registerEvent)
-          var procParams = @[ident"ComponentEventHandler"]
+          when defined(js):
+            var procParams: seq[string] = @[]
+          else:
+            var procParams = @[ident"ComponentEventHandler"]
           for i in cycleVars:
-            procParams.add(newIdentDefs(i, ident"auto"))
+            when defined(js):
+              procParams.add("`" & $i & "`")
+            else:
+              procParams.add(newIdentDefs(i, ident"auto"))
             callRegister.add(i)
           when defined(js):
-            events.add(
-              newCall(
-                "addEventListener",
-                newDotExpr(ident("__el" & $elemEventId), ident"Element"),
-                newLit(event), procedure
+            procedure.body.insert(0, newNimNode(nnkPragma).add(
+              newNimNode(nnkExprColonExpr).add(
+                ident"emit", newLit("`" & $args[^1][0] & "` = event;")
               )
-            )
+            ))
+            procedure.body.insert(0, newNimNode(nnkVarSection).add(
+              newIdentDefs(args[^1][0], ident"Event", newEmptyNode())
+            ))
+            events.add(newStmtList(
+              newNimNode(nnkPragma).add(
+                newNimNode(nnkExprColonExpr).add(
+                  ident"emit", newLit("const __elSc" & $elemEventId & " = (" & procParams.join(",") & ") => {")
+                )
+              ),
+              newCall(
+                "eventListener", ident("__el" & $elemEventId), newLit(event), procedure.body
+              ),
+              newNimNode(nnkPragma).add(
+                newNimNode(nnkExprColonExpr).add(
+                  ident"emit", newLit("};\n__elSc" & $elemEventId & "(" & procParams.join(",") & ");")
+                )
+              ),
+            ))
           else:
             result.addAttribute(
               newLit(evname),
@@ -921,12 +943,19 @@ proc buildHtmlProcedure*(root, body: NimNode, inComponent: bool = false,
         # In component and not in cycle
         else:
           when defined(js):
-            events.add(
-              newCall(
-                "addEventListener",
-                newDotExpr(ident("__el" & $elemEventId), ident"Element"),
-                newLit(event), procedure
+            procedure.body.insert(0, newNimNode(nnkPragma).add(
+              newNimNode(nnkExprColonExpr).add(
+                ident"emit", newLit("`" & $args[^1][0] & "` = event;")
               )
+            ))
+            procedure.body.insert(0, newNimNode(nnkVarSection).add(
+              newIdentDefs(args[^1][0], ident"Event", newEmptyNode())
+            ))
+            events.add(
+              newCall("eventListener", ident("__el" & $elemEventId), newLit(event),
+              newNimNode(nnkBlockStmt).add(
+                newEmptyNode(), procedure.body
+              ))
             )
           else:
             result.addAttribute(
@@ -953,18 +982,40 @@ proc buildHtmlProcedure*(root, body: NimNode, inComponent: bool = false,
             cycleVar = " + " & cycleTmpVar  & ")}"
             registerEvent = fmt"registerEventScoped{uniqueId.value}{uniqueId.value+2}"
             callRegister = newCall(registerEvent)
-          var procParams = @[ident"AppEventHandler"]
+          when defined(js):
+            var procParams: seq[string] = @[]
+          else:
+            var procParams = @[ident"AppEventHandler"]
           for i in cycleVars:
-            procParams.add(newIdentDefs(i, ident"any"))
+            when defined(js):
+              procParams.add("`" & $i & "`")
+            else:
+              procParams.add(newIdentDefs(i, ident"auto"))
             callRegister.add(i)
           when defined(js):
-            events.add(
-              newCall(
-                "addEventListener",
-                newDotExpr(ident("__el" & $elemEventId), ident"Element"),
-                newLit(event), procedure
+            procedure.body.insert(0, newNimNode(nnkPragma).add(
+              newNimNode(nnkExprColonExpr).add(
+                ident"emit", newLit("`" & $args[^1][0] & "` = event;")
               )
-            )
+            ))
+            procedure.body.insert(0, newNimNode(nnkVarSection).add(
+              newIdentDefs(args[^1][0], ident"Event", newEmptyNode())
+            ))
+            events.add(newStmtList(
+              newNimNode(nnkPragma).add(
+                newNimNode(nnkExprColonExpr).add(
+                  ident"emit", newLit("const __elSc" & $elemEventId & " = (" & procParams.join(",") & ") => {")
+                )
+              ),
+              newCall(
+                "eventListener", ident("__el" & $elemEventId), newLit(event), procedure.body
+              ),
+              newNimNode(nnkPragma).add(
+                newNimNode(nnkExprColonExpr).add(
+                  ident"emit", newLit("};\n__elSc" & $elemEventId & "(" & procParams.join(",") & ");")
+                )
+              ),
+            ))
           else:
             result.addAttribute(
               newLit(evname),
@@ -989,12 +1040,19 @@ proc buildHtmlProcedure*(root, body: NimNode, inComponent: bool = false,
         # not in component and not in cycle
         else:
           when defined(js):
-            events.add(
-              newCall(
-                "addEventListener",
-                newDotExpr(ident("__el" & $elemEventId), ident"Element"),
-                newLit(event), procedure
+            procedure.body.insert(0, newNimNode(nnkPragma).add(
+              newNimNode(nnkExprColonExpr).add(
+                ident"emit", newLit("`" & $args[^1][0] & "` = event;")
               )
+            ))
+            procedure.body.insert(0, newNimNode(nnkVarSection).add(
+              newIdentDefs(args[^1][0], ident"Event", newEmptyNode())
+            ))
+            events.add(
+              newCall("eventListener", ident("__el" & $elemEventId), newLit(event),
+              newNimNode(nnkBlockStmt).add(
+                newEmptyNode(), procedure.body
+              ))
             )
           else:
             result.addAttribute(
diff --git a/src/happyx/routing/routing.nim b/src/happyx/routing/routing.nim
index b22a52732..cd1077be8 100755
--- a/src/happyx/routing/routing.nim
+++ b/src/happyx/routing/routing.nim
@@ -33,7 +33,13 @@ elif exportJvm:
     ../bindings/java_types
 
 
-const declaredPathParams = CacheTable"HappyXDeclaredPathParams"
+const
+  declaredPathParams = CacheTable"HappyXDeclaredPathParams"
+  onException* = CacheTable"HappyXOnException"
+
+
+static:
+  onException["e"] = newStmtList()
 
 
 type
@@ -520,7 +526,7 @@ proc exportRouteArgs*(urlPath, routePath, body: NimNode): NimNode =
                   newCall("echo", newCall("fmt", newLit"json parse error: {getCurrentExceptionMsg()}"))
                 else:
                   newEmptyNode(),
-                newCall("__onException", urlPath, body, newCall"getCurrentException"),
+                onException["e"],
                 newCall(
                   "answerJson",
                   ident"req",
@@ -537,7 +543,7 @@ proc exportRouteArgs*(urlPath, routePath, body: NimNode): NimNode =
                   newCall("echo", newCall("fmt", newLit"json kind error: {getCurrentExceptionMsg()}"))
                 else:
                   newEmptyNode(),
-                newCall("__onException", urlPath, body, newCall"getCurrentException"),
+                onException["e"],
                 newCall(
                   "answerJson",
                   ident"req",
@@ -554,7 +560,7 @@ proc exportRouteArgs*(urlPath, routePath, body: NimNode): NimNode =
                   newCall("echo", newCall("fmt", newLit"json unknown error: {getCurrentExceptionMsg()}"))
                 else:
                   newEmptyNode(),
-                newCall("__onException", urlPath, body, newCall"getCurrentException"),
+                onException["e"],
                 newCall(
                   "answerJson",
                   ident"req",
diff --git a/src/happyx/spa/spa_utils.nim b/src/happyx/spa/spa_utils.nim
new file mode 100644
index 000000000..5bafea307
--- /dev/null
+++ b/src/happyx/spa/spa_utils.nim
@@ -0,0 +1,19 @@
+import
+  std/macros
+
+
+macro eventListener*(obj: untyped, event: string, body: untyped): untyped =
+  newStmtList(
+    newNimNode(nnkPragma).add(
+      newNimNode(nnkExprColonExpr).add(
+        ident"emit",
+        newLit("`" & $obj & "`.addEventListener('" & $event & "', (event) => {")
+      )
+    ),
+    body,
+    newNimNode(nnkPragma).add(
+      newNimNode(nnkExprColonExpr).add(
+        ident"emit", newLit("});")
+      )
+    ),
+  )
diff --git a/src/happyx/ssr/server.nim b/src/happyx/ssr/server.nim
index b99b5fd30..a36c722e5 100755
--- a/src/happyx/ssr/server.nim
+++ b/src/happyx/ssr/server.nim
@@ -749,7 +749,6 @@ macro routes*(server: Server, body: untyped = newStmtList()): untyped =
     stmtList = newStmtList()
     staticDirs: seq[NimNode]
     notFoundNode = newEmptyNode()
-    onException = newStmtList()
     procStmt = newProc(
       ident"handleRequest",
       [
@@ -1497,7 +1496,9 @@ socketToSsr.onmessage=function(m){
           notFoundNode = statement[1]
         of "onexception":
           detectReturnStmt(statement[1])
-          onException = statement[1]
+          statement[1].insert(0, newLetStmt(ident"e", newCall"getCurrentException"))
+          onException["e"].add(statement[1])
+          echo onException["e"].toStrLit
         of "middleware":
           detectReturnStmt(statement[1])
           stmtList.insert(0, statement[1])
@@ -1605,17 +1606,6 @@ socketToSsr.onmessage=function(m){
     else:
       newEmptyNode(),
     setup,
-    newProc(
-      ident"__onException",
-      [
-        newEmptyNode(),
-        newIdentDefs(ident"url", ident"string"),
-        newIdentDefs(ident"body", ident"string"),
-        newIdentDefs(ident"e", newNimNode(nnkRefTy).add(newDotExpr(ident"system", ident"Exception"))),
-      ],
-      onException,
-      nnkTemplateDef
-    ),
     newProc(
       ident"__wsError",
       [newEmptyNode(), newIdentDefs(wsClientI, wsType)],


Test patch:
diff --git a/tests/components/component_for.nim b/tests/components/component_for.nim
index 9213e6724..fbed546b0 100644
--- a/tests/components/component_for.nim
+++ b/tests/components/component_for.nim
@@ -11,6 +11,7 @@ component ComponentFor:
           "{i}"
           @click:
             self.counter += 1
+            {.emit: "console.log(`i`, `ev`)".}
             echo i
       !debugCurrent
   
diff --git a/tests/testc20.nim b/tests/testc20.nim
index de104ac34..7b0e2a0d2 100644
--- a/tests/testc20.nim
+++ b/tests/testc20.nim
@@ -1,51 +1,23 @@
 import ../src/happyx
 
+type
+  Kind = enum
+    circle
+    square
 
-type Kind = enum
-  str
-  num
+model Obj:
+  k: Kind
+  st: string
 
+serve("127.0.0.1", 5000):
+  post "/[data:Obj:json]":
+    echo data
+    return ""
 
-model Task:
-  id: int
-
-
-serve "127.0.0.1", 5000:
-  post "/[task:Task]":
-    echo task.id
-    return task.id
-  
-  get "/x/$x":
-    if x == "1":
-      return x
-    echo 1
-    if x == "2":
-      return x
-    echo 2
-    return x
-    echo x
-
-  get "/thisShouldWorkWhithoutRegex":
-    discard
-
-  get "/{k:enum(Kind)}/{x}":
-    "1" & x
-  get "/bool":
-    "2"
-  notfound:
-    "3"
-  
-  staticDir "/testdir"
-
-  staticDir "/testdir" -> "testdir" ~ "html,js,css"
-
-  middleware:
-    outHeaders["x"] = "y"
-  
   onException:
-    # here built-in variables:
-    # `url`, `body` is string
-    # `e` is `ref Exception`
-    echo "URL: ", url
-    echo "BODY: ", body
-    echo "EXCEPTION: [", e.name, "] - ", e.msg
+    echo "Exception"
+    echo e.name
+    echo e.msg
+    statusCode = 422
+    echo statusCode
+    return "Exception"