Repository: vishalpalaniappan/asp-query-server
Pull request #26: Add feature to query ASP database using the query handler.
Author: login: vishalpalaniappan
Created: 2025-04-21T20:46:27Z | Merged: 2025-04-22T22:27:20Z

This PR implements functionality to query the ASP database. This will be used by ASV to visualize the systems, deployments and their corresponding traces. 

## Queries

The following are a list of queries that the query server accepts.

### 1. Get Systems
This query returns all the systems in the database. 

#### Query
```
{
    "queryType":  "GET_SYSTEMS"
}
```
#### Response
The response to this query will be structured as follows:
```
{
    "queryType": "GET_SYSTEMS",
    "data": {
        "systemId": 1234,
        "systemVersion": "0.0.2"
    },
    "response":  <list of systems>
}
```
### 2. Get Programs
This query returns all the programs for a given system id and version.
#### Query
```
{
    "queryType": "GET_PROGRAMS",
    "data": {
        "systemId": 1234,
        "systemVersion": "0.0.2"
    }
}
```
#### Response
The response to this query will be structured as follows:
```
{
    "queryType": "GET_PROGRAMS",
    "data": {
        "systemId": 1234,
        "systemVersion": "0.0.2"
    },
    "response":  <list of programs>
}
```
### 3. Get Deployments
This query returns all the deployments for a given system id and version. 
#### Query
```
{
    "queryType": "GET_DEPLOYMENTS",
    "data": {
        "systemId": 1234,
        "systemVersion": "0.0.2"
    }
}
```
#### Response
The response to this query will be structured as follows:
```
{
    "queryType": "GET_DEPLOYMENTS",
    "data": {
        "systemId": 1234,
        "systemVersion": "0.0.2"
    },
    "response":  <list of deployment ids>
}
```
### 4. Get Traces
This query returns all the unique traces for a given system id and version.,
#### Query
```
{
    "queryType": "GET_TRACES",
    "data": {
        "systemId": 1234,
        "systemVersion": "0.0.2",
        "deploymentId": "ff842dc3-92a4-4d0a-9a67-d27444f3f98f"
    }
}
```
#### Response
The response to this query will be structured as follows:
```
{
    "queryType": "GET_TRACES",
    "data": {
        "systemId": 1234,
        "systemVersion": "0.0.2",
        "deploymentId": "ff842dc3-92a4-4d0a-9a67-d27444f3f98f"
    },
    "response":  <list of traces>
}
```
## Malformed Queries
In the event that a required field is not available in the query, the response will indicate that an error has occurred and provide the reason for the error.
#### Example
```
{
    "queryType": "GET_DEPLOYMENTS",
    "data": {
        "systemVersion": "0.0.2"
    },
    "response": "Request does not contain a system id.",
    "error": true
}
```

## Invalid System Information
In the event that the provided system information is invalid, it will result in the program trying to access a table that doesn't exist. This will result in a response with an error and a reason for the error.

#### Example
```
{
    "queryType": "GET_DEPLOYMENTS",
    "data": {
        "systemId": 134,
        "systemVersion": "0.0.2"
    },
    "error": true,
    "response": "Database error: no such table: 134_0.0.2_deployments"
}
```

## Validation Performed

Ran the server using:

`python3 server.py`

Connected to server using postman using url `ws://localhost:8765` and performed the following validation:

### Valid Queries
1. Sent message to get all tables and verified that the received table info is accurate.
2. Sent message to get all programs for a given systemid+version and verified that the received programs are valid. 
3. Sent message to get all deployments for a given systemid+version and verified that the received programs are valid.
4. Sent message to get all traces for a given systemid+version+deploymentid and verified that the received programs are valid.

### Invalid Queries
1. Sent message without queryType to server and verified the response.
2. Sent message with invalid queryType to server and verified the response.
3. Sent message without required keys (systemId and systemVersion) and verified the response.
4. Sent message without with incorrect system information and verified the response.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
- **New Features**
  - Added support for retrieving systems, programs, deployments, and traces via new query message types.
  - Introduced a database reader for accessing and managing ASP SQLite database content.
  - Implemented dedicated handlers for processing various query types and returning structured responses.
  - Added comprehensive documentation detailing supported query formats and responses.

- **Refactor**
  - Streamlined message handling logic for queries, improving clarity and maintainability.
  - Improved error handling for invalid JSON messages and unknown query types.
  - Updated WebSocket server to use the new message receiving function.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/components/query_handler/MSG_TYPES.py b/components/query_handler/MSG_TYPES.py
index 82a3a7e..1c632a4 100644
--- a/components/query_handler/MSG_TYPES.py
+++ b/components/query_handler/MSG_TYPES.py
@@ -1,4 +1,6 @@
 MSG_TYPES = {
-    "GET_UNIQUE_TRACES": 1,
-    "GET_FILE_TREES": 2
+    "GET_SYSTEMS": 1,
+    "GET_PROGRAMS": 2,
+    "GET_DEPLOYMENTS": 3,
+    "GET_TRACES": 4
 }
diff --git a/components/query_handler/README.md b/components/query_handler/README.md
new file mode 100644
index 0000000..1f5044f
--- /dev/null
+++ b/components/query_handler/README.md
@@ -0,0 +1,135 @@
+## Query Path
+
+1. A websocket server receives messages from a client. 
+2. The query is handled by receiveMessage in queryHandler.py.
+3. The query is passed to the handleQuery in queryHandler.py.
+4. The query is processed by calling the relevant function from queryFunctions.py (database operations are performed using SystemDatabaseReader class instance).
+5. The query result is sent back to the user.
+
+## Queries
+The following are a list of queries that the query server accepts.
+
+### 1. Get Systems
+This query returns all the systems in the database. 
+
+#### Query
+```
+{
+    "queryType":  "GET_SYSTEMS"
+}
+```
+#### Response
+The response to this query will be structured as follows:
+```
+{
+    "queryType": "GET_SYSTEMS",
+    "data": {
+        "systemId": 1234,
+        "systemVersion": "0.0.2"
+    },
+    "response":  <list of systems>
+}
+```
+### 2. Get Programs
+This query returns all the programs for a given system id and version.
+#### Query
+```
+{
+    "queryType": "GET_PROGRAMS",
+    "data": {
+        "systemId": 1234,
+        "systemVersion": "0.0.2"
+    }
+}
+```
+#### Response
+The response to this query will be structured as follows:
+```
+{
+    "queryType": "GET_PROGRAMS",
+    "data": {
+        "systemId": 1234,
+        "systemVersion": "0.0.2"
+    },
+    "response":  <list of programs>
+}
+```
+### 3. Get Deployments
+This query returns all the deployments for a given system id and version. 
+#### Query
+```
+{
+    "queryType": "GET_DEPLOYMENTS",
+    "data": {
+        "systemId": 1234,
+        "systemVersion": "0.0.2"
+    }
+}
+```
+#### Response
+The response to this query will be structured as follows:
+```
+{
+    "queryType": "GET_DEPLOYMENTS",
+    "data": {
+        "systemId": 1234,
+        "systemVersion": "0.0.2"
+    },
+    "response":  <list of deployment ids>
+}
+```
+### 4. Get Traces
+This query returns all the unique traces for a given system id and version.,
+#### Query
+```
+{
+    "queryType": "GET_TRACES",
+    "data": {
+        "systemId": 1234,
+        "systemVersion": "0.0.2",
+        "deploymentId": "ff842dc3-92a4-4d0a-9a67-d27444f3f98f"
+    }
+}
+```
+#### Response
+The response to this query will be structured as follows:
+```
+{
+    "queryType": "GET_TRACES",
+    "data": {
+        "systemId": 1234,
+        "systemVersion": "0.0.2",
+        "deploymentId": "ff842dc3-92a4-4d0a-9a67-d27444f3f98f"
+    },
+    "response":  <list of traces>
+}
+```
+## Malformed Queries
+In the event that a required field is not available in the query, the response will indicate that an error has occurred and provide the reason for the error.
+#### Example
+```
+{
+    "queryType": "GET_DEPLOYMENTS",
+    "data": {
+        "systemVersion": "0.0.2"
+    },
+    "response": "Request does not contain a system id.",
+    "error": true
+}
+```
+
+## Invalid System Information
+In the event that the provided system information is invalid, it will result in the program trying to access a table that doesn't exist. This will result in a response with an error and a reason for the error.
+
+#### Example
+```
+{
+    "queryType": "GET_DEPLOYMENTS",
+    "data": {
+        "systemId": 134,
+        "systemVersion": "0.0.2"
+    },
+    "error": true,
+    "response": "Database error: no such table: 134_0.0.2_deployments"
+}
+```
\ No newline at end of file
diff --git a/components/query_handler/SystemDatabaseReader.py b/components/query_handler/SystemDatabaseReader.py
new file mode 100644
index 0000000..8014742
--- /dev/null
+++ b/components/query_handler/SystemDatabaseReader.py
@@ -0,0 +1,109 @@
+import sqlite3
+import os
+
+class SystemDatabaseReader:
+    '''
+        This class will perform queries on the ASP database and return
+        the results.
+    '''
+
+    def __init__(self):
+        self.openConnections()
+
+    def openConnections(self):
+        '''
+            Initialize the database connections.
+        '''
+        path = os.path.dirname(os.path.dirname(__file__))
+        aspPath = os.path.join(path, "asp.db")
+        try:
+            self.aspConn = sqlite3.connect(aspPath)
+            self.aspCursor = self.aspConn.cursor()
+        except sqlite3.Error as e:
+            print(f"Database error: {e}")
+            raise
+
+    def closeConnections(self):
+        '''
+            Close the database connections.
+        '''
+        if hasattr(self, 'aspConn'):
+            self.aspConn.close()
+
+    def __del__(self):
+        '''
+            Clean up database connections.
+        '''
+        self.closeConnections()
+
+    def getColumns(self, tableName):
+        '''
+            Get the columns for a table.
+        '''
+        query = f'PRAGMA table_info("{tableName}")' 
+        self.aspCursor.execute(query)
+        return self.aspCursor.fetchall()
+    
+    def addColumnNameToData(self, columns, data):
+        '''
+            Given the column names and the data, create 
+            an object which uses the column names as key.
+        '''
+        obj = {}
+        for index, columnValue in enumerate(data):
+            obj[columns[index][1]] = columnValue
+        return obj
+    
+    def getAllEntriesInTable(self, tableName):
+        '''
+            Given a table name, get all entries in the table.
+        '''
+        query = f'SELECT * FROM "{tableName}"'
+        self.aspCursor.execute(query)
+        rows = self.aspCursor.fetchall() 
+        columns = self.getColumns(tableName)
+
+        results = []
+        for row in rows:
+            results.append(self.addColumnNameToData(columns, row))
+
+        return results
+    
+    def getSystems(self):
+        '''
+            Get all systems in the database.
+        '''
+        return self.getAllEntriesInTable("SYSTEMTABLES")
+
+    def getPrograms(self, systemId, systemVersion):     
+        '''
+            Get all programs given a system id and system version.
+        '''   
+        tableName = f"{systemId}_{systemVersion}_programs"        
+        return self.getAllEntriesInTable(tableName)
+
+    def getDeployments(self, systemId, systemVersion):  
+        '''
+            Get all the deployments for the given system.
+        '''            
+        tableName = f"{systemId}_{systemVersion}_deployments"
+        return self.getAllEntriesInTable(tableName)
+
+    def getTraces(self, systemId, systemVersion, deploymentId):
+        '''
+            Get all the traces given a system id, version and deployment id.            
+            # TODO: Add funtionality to filter traces by start and end timestamp.       
+        '''
+        tableName = f"{systemId}_{systemVersion}_traces"
+
+        query = f'SELECT * FROM "{tableName}" WHERE deployment_id = ?'
+        self.aspCursor.execute(query, [deploymentId])
+        rows = self.aspCursor.fetchall() 
+
+        columns = self.getColumns(tableName)
+
+        results = []
+        for row in rows:
+            results.append(self.addColumnNameToData(columns, row))
+
+        return results
\ No newline at end of file
diff --git a/components/query_handler/queryFunctions.py b/components/query_handler/queryFunctions.py
new file mode 100644
index 0000000..64c62eb
--- /dev/null
+++ b/components/query_handler/queryFunctions.py
@@ -0,0 +1,137 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Query did not contain a queryType key."
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    try:
+        message["response"] = reader.getSystems()
+    except Exception as e:
+        message["error"] = True
+        message["response"] = f"Database error: {e}"
+
+    return message
+
+def handleGetPrograms(message):
+    '''
+        Get all programs given a system version and id.
+    '''
+    if ("data" not in message):
+        message["response"] = "Query does not contain the required data key."
+        message["error"] = True
+        return message
+    
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Query does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Query does not contain a system version."
+        message["error"] = True
+        return message
+    
+    try:
+        message["response"] = reader.getPrograms(
+            systemId= message["data"]["systemId"],
+            systemVersion= message["data"]["systemVersion"]
+        )
+    except Exception as e:
+        message["error"] = True
+        message["response"] = f"Database error: {e}"
+
+    return message
+
+
+def handleGetDeployments(message):
+    '''
+        Get all deployments given a system id and version.
+    '''
+    if ("data" not in message):
+        message["response"] = "Query does not contain the required data key."
+        message["error"] = True
+        return message
+    
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Query does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Query does not contain a system version."
+        message["error"] = True
+        return message
+
+    try:
+        message["response"] = reader.getDeployments(
+            systemId= message["data"]["systemId"],
+            systemVersion= message["data"]["systemVersion"]
+        )
+    except Exception as e:
+        message["error"] = True
+        message["response"] = f"Database error: {e}"
+
+    return message
+
+
+def handleGetTraces(message):
+    '''
+        Get all traces given a system id, version and deployment id.
+    '''
+    if ("data" not in message):
+        message["response"] = "Query does not contain the required data key."
+        message["error"] = True
+        return message
+    
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Query does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Query does not contain a system version."
+        message["error"] = True
+        return message
+
+    if ("deploymentId" not in data):
+        message["response"] = "Query does not contain a deployment id."
+        message["error"] = True
+        return message
+    
+    try:    
+        message["response"] = reader.getTraces(
+            systemId= message["data"]["systemId"],
+            systemVersion= message["data"]["systemVersion"],
+            deploymentId= message["data"]["deploymentId"],
+        )
+    except Exception as e:
+        message["error"] = True
+        message["response"] = f"Database error: {e}"
+
+    return message
+
+
+def handleUnknownQueryType(message):
+    '''
+        Handle an unkown query type.
+    '''
+    message["response"] = f"Unknown message type: {message['queryType']}"
+    message["error"] = True
+    return message
\ No newline at end of file
diff --git a/components/query_handler/queryHandler.py b/components/query_handler/queryHandler.py
index b30e534..c6b50e0 100644
--- a/components/query_handler/queryHandler.py
+++ b/components/query_handler/queryHandler.py
@@ -1,35 +1,48 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from queryFunctions import *
 
-async def handle_query(websocket):
+def handleQuery (message):
+    '''
+        Handle the query.
+    '''
+    if "queryType" not in message:
+        response = handleUnknownMessage(message= message)
+        return response
+
+    print(f"Received message: {message['queryType']}")    
+    
+    if (message["queryType"] == "GET_SYSTEMS"):            
+        response = handleGetSystems(message= message)
+        
+    elif (message["queryType"] == "GET_PROGRAMS"):            
+        response = handleGetPrograms(message= message)
+    
+    elif (message["queryType"] == "GET_DEPLOYMENTS"):
+        response = handleGetDeployments(message= message)
+    
+    elif (message["queryType"] == "GET_TRACES"):
+        response = handleGetTraces(message= message)
+    
+    else:
+        response = handleUnknownQueryType(message= message)
+
+    return response
+
+async def receiveMessage(websocket):
     '''
         Handles messages from websocket and echos a response.
     '''
-    global processor
-
     async for message in websocket:
-        print(f"Received message: {message}")
-
-        message = json.loads(message)
-
-        if "code" not in message:
-            message["response"] = "Message does not contain a code"
-            message["error"] = True
-            await websocket.send(json.dumps(message))
-
-        elif (message["code"] == MSG_TYPES["GET_UNIQUE_TRACES"]):
-            message["error"] = False
-            await websocket.send(json.dumps(message))
-
-        elif (message["code"] == MSG_TYPES["GET_FILE_TREES"]):
-            message["error"] = False
-            await websocket.send(json.dumps(message))
-
-        else:
-            message["response"] = f"Unknown message type: {message['code']}"
-            message["error"] = True
-            await websocket.send(json.dumps(message))
-
+        try:
+            message = json.loads(message)   
+            response = handleQuery(message=message)   
+            await websocket.send(json.dumps(response))  
+        except json.JSONDecodeError as e:
+            response = {} 
+            response["response"] = "The query does not contain a valid JSON string."
+            response["error"] = True
+            await websocket.send(json.dumps(response))
         
 
         
diff --git a/components/query_handler/server.py b/components/query_handler/server.py
index 0c84d55..b2553bd 100644
--- a/components/query_handler/server.py
+++ b/components/query_handler/server.py
@@ -3,7 +3,7 @@
 import asyncio
 import argparse
 from websockets.asyncio.server import serve
-from queryHandler import handle_query
+from queryHandler import receiveMessage
 
 async def main():
     '''
@@ -24,7 +24,7 @@ async def main():
     args = parser.parse_args()
 
     print(f"Starting WebSocket server on {args.host}:{args.port}")
-    async with serve(handle_query, args.host, args.port) as server:
+    async with serve(receiveMessage, args.host, args.port) as server:
         await server.serve_forever()
 
 if __name__ == "__main__":


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 7**

<details>
<summary>🧹 Nitpick comments (8)</summary><blockquote>

<details>
<summary>components/query_handler/SystemDatabaseReader.py (4)</summary><blockquote>

`17-24`: **Database path construction might be fragile**

The path resolution logic assumes a specific directory structure. Consider making the database path configurable, perhaps through environment variables or a configuration file.

```diff
-        path = os.path.dirname(os.path.dirname(__file__))
-        aspPath = os.path.join(path, "asp.db")
+        # Get path from environment variable or use default
+        db_path = os.environ.get('ASP_DB_PATH')
+        if not db_path:
+            path = os.path.dirname(os.path.dirname(__file__))
+            db_path = os.path.join(path, "asp.db")
+        
+        aspPath = db_path
```

---

`50-58`: **Optimization opportunity for dictionary creation**

The `addColumnNameToData` method could be more efficient by using a dictionary comprehension.

```diff
-        obj = {}
-        for index, columnValue in enumerate(data):
-            obj[columns[index][1]] = columnValue
-        return obj
+        return {columns[index][1]: columnValue for index, columnValue in enumerate(data)}
```

---

`95-110`: **Address the TODO comment about filtering traces**

The TODO comment indicates the need to add functionality to filter traces by start and end timestamp. This would be a valuable feature to implement.

Would you like me to propose an implementation for filtering traces by timestamp?

---

`113-124`: **Potential index error in test code**

The test code accesses `systems[1]` without checking if there are at least two systems in the list, which could cause an index error.

```diff
-    traces = reader.getTraces(
-        systems[1]["system_id"],
-        systems[1]["version"],
-        "127b22dc-a2c3-4b58-a27b-f3ff8ac3997b"
-    )
+    if len(systems) > 1:
+        traces = reader.getTraces(
+            systems[1]["system_id"],
+            systems[1]["version"],
+            "127b22dc-a2c3-4b58-a27b-f3ff8ac3997b"
+        )
+        # Process traces here
+    else:
+        print("Not enough systems to retrieve traces")
```

</blockquote></details>
<details>
<summary>components/query_handler/queryHandler.py (1)</summary><blockquote>

`8-16`: **Fix typo in error message**

There's a typo in the return value on line 16: "Unkown" should be "Unknown".

```diff
-    return "Unkown message code."
+    return "Unknown message code."
```

</blockquote></details>
<details>
<summary>components/query_handler/queryFunctions.py (3)</summary><blockquote>

`1-2`: **Remove unused import**

The `json` module is imported but never used in this file.

```diff
-import json
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

---

`3-9`: **Remove unused parameter**

The `handleUnknownMessage` function accepts a `websocket` parameter but doesn't use it. Remove it to simplify the function signature.

```diff
-def handleUnknownMessage(websocket, message):
+def handleUnknownMessage(message):
     '''
         Handle an unknown message.
     '''
     message["response"] = "Message does not contain a code"
     message["error"] = True
     return message
```

---

`97-103`: **Fix typo in docstring**

There's a typo in the docstring on line 99: "unkown" should be "unknown".

```diff
     '''
-        Handle an unkown code.
+        Handle an unknown code.
     '''
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 858f9eb4b1f6d741d4cb0d9c95dc81f6c781dcf7 and ad5a635fcb4dad2963c9668a8e9a1c015c7a5f7e.

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `components/query_handler/MSG_TYPES.py` (1 hunks)
* `components/query_handler/SystemDatabaseReader.py` (1 hunks)
* `components/query_handler/queryFunctions.py` (1 hunks)
* `components/query_handler/queryHandler.py` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>components/query_handler/queryHandler.py (2)</summary><blockquote>

<details>
<summary>components/query_handler/SystemDatabaseReader.py (1)</summary>

* `SystemDatabaseReader` (4-110)

</details>
<details>
<summary>components/query_handler/queryFunctions.py (5)</summary>

* `handleUnknownMessage` (3-9)
* `handleGetSystems` (11-17)
* `handleGetPrograms` (19-40)
* `handleGetDeployments` (43-64)
* `handleUnknownCode` (97-103)

</details>

</blockquote></details>
<details>
<summary>components/query_handler/queryFunctions.py (1)</summary><blockquote>

<details>
<summary>components/query_handler/SystemDatabaseReader.py (4)</summary>

* `getSystems` (75-79)
* `getPrograms` (81-86)
* `getDeployments` (88-93)
* `getTraces` (95-110)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>components/query_handler/queryHandler.py</summary>

4-4: `from queryFunctions import *` used; unable to detect undefined names

(F403)

---

28-28: `handleUnknownMessage` may be undefined, or defined from star imports

(F405)

---

34-34: `handleGetSystems` may be undefined, or defined from star imports

(F405)

---

41-41: `handleGetPrograms` may be undefined, or defined from star imports

(F405)

---

48-48: `handleGetDeployments` may be undefined, or defined from star imports

(F405)

---

59-59: `handleUnknownCode` may be undefined, or defined from star imports

(F405)

</details>
<details>
<summary>components/query_handler/queryFunctions.py</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>components/query_handler/MSG_TYPES.py (1)</summary>

`1-6`: **LGTM - Message type definitions align with the new query handler architecture**

The message type definitions have been updated to support the new query handling architecture, providing clear numeric codes for each message type.

</details>
<details>
<summary>components/query_handler/queryHandler.py (4)</summary>

`33-38`: **LGTM - Proper handling of GET_SYSTEMS message**

The implementation correctly delegates to the handleGetSystems function and sends the response back to the websocket.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

34-34: `handleGetSystems` may be undefined, or defined from star imports

(F405)

</details>

</details>

---

`40-45`: **LGTM - Proper handling of GET_PROGRAMS message**

The implementation correctly delegates to the handleGetPrograms function and sends the response back to the websocket.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

41-41: `handleGetPrograms` may be undefined, or defined from star imports

(F405)

</details>

</details>

---

`47-52`: **LGTM - Proper handling of GET_DEPLOYMENTS message**

The implementation correctly delegates to the handleGetDeployments function and sends the response back to the websocket.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

48-48: `handleGetDeployments` may be undefined, or defined from star imports

(F405)

</details>

</details>

---

`58-63`: **LGTM - Proper handling of unknown code**

The implementation correctly delegates to the handleUnknownCode function and sends the response back to the websocket.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

59-59: `handleUnknownCode` may be undefined, or defined from star imports

(F405)

</details>

</details>

</details>
<details>
<summary>components/query_handler/queryFunctions.py (2)</summary>

`11-17`: **LGTM - Proper handling of GET_SYSTEMS messages**

The implementation correctly queries for systems without requiring any parameters.

---

`19-40`: **LGTM - Proper parameter validation for GET_PROGRAMS**

The function properly validates the required parameters before calling the database reader.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T17:34:10Z
user: login: vishalpalaniappan
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-22T20:57:32Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T20:58:32Z
user: login: vishalpalaniappan
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-22T20:59:54Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T21:00:29Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 7**

<details>
<summary>♻️ Duplicate comments (4)</summary><blockquote>

<details>
<summary>components/query_handler/SystemDatabaseReader.py (2)</summary><blockquote>

`43-45`: **Sanitize SQL identifiers to prevent injection**

The use of f-strings for SQL queries with table names can pose a security risk. While SQLite treats them as identifiers, this is still vulnerable if user input is received.

You should validate table names against a pattern like `^[A-Za-z_][A-Za-z0-9_]*$` before using them in queries.


Also applies to: 51-53

---

`65-78`: **Add error handling for non-existent tables**

The `getAllEntriesInTable` method doesn't handle SQLite errors that could occur if the table doesn't exist.

Consider adding try/except blocks to catch and handle SQLite errors gracefully.

</blockquote></details>
<details>
<summary>components/query_handler/queryHandler.py (1)</summary><blockquote>

`3-3`: **Avoid wildcard imports**

Using wildcard imports makes it harder to trace which functions are being used and can lead to name collisions.

Consider importing specific functions instead of using the wildcard import.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

3-3: `from queryFunctions import *` used; unable to detect undefined names

(F403)

</details>

</details>

</blockquote></details>
<details>
<summary>components/query_handler/queryFunctions.py (1)</summary><blockquote>

`28-57`: _🛠️ Refactor suggestion_

**Refactor duplicate validation logic**

The validation logic in this function and other handler functions is repetitive. Consider extracting a helper function as suggested in a previous review.



```diff
+def validateParameters(message, required_params):
+    '''
+        Validate that required parameters are present in the message data.
+        Returns (is_valid, error_message) tuple.
+    '''
+    data = message.get("data", {})
+    
+    for param in required_params:
+        if param not in data:
+            return False, f"Request does not contain a {param}."
+    
+    return True, None

 def handleGetPrograms(message):
     '''
         Get all programs given a system version and id.
     '''
-    data = message["data"]
-
-    if ("systemId" not in data):
-        message["response"] = "Request does not contain a system id."
-        message["error"] = True
-        return message
-
-    if ("systemVersion" not in data):
-        message["response"] = "Request does not contain a system version."
-        message["error"] = True
-        return message
+    is_valid, error_msg = validateParameters(message, ["systemId", "systemVersion"])
+    if not is_valid:
+        message["response"] = error_msg
+        message["error"] = True
+        return message

     response = reader.getPrograms(
         systemId= message["data"]["systemId"],
         systemVersion= message["data"]["systemVersion"]
     )

     if (response is None):
         message["error"] = True
-        message["response"] = "An error occured while getting programs."
+        message["response"] = "An error occurred while getting programs."
     else:
         message["error"] = False
         message["response"] = response

     return message
```

Apply similar changes to `handleGetDeployments` and `handleGetTraces`.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (12)</summary><blockquote>

<details>
<summary>components/query_handler/README.md (4)</summary><blockquote>

`9-13`: **Add language specifiers to code blocks**

All code blocks should have a language specified for proper syntax highlighting. For JSON code blocks, use "json" as the language specifier.

Apply this change to all code blocks:

```diff
-```
+```json
 {
     "query":  "GET_SYSTEMS"
 }
```


Also applies to: 16-25, 29-37, 40-49, 53-61, 64-73, 77-86, 89-99, 103-111

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

9-9: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

</details>

</details>

---

`75-75`: **Remove trailing comma in documentation**

There's a trailing comma at the end of line 75 that should be removed.

```diff
-This query returns all the unique traces for a given system id and version.,
+This query returns all the unique traces for a given system id and version.
```

---

`100-101`: **Simplify wording**

The phrase "In the event that" could be simplified.

```diff
-In the event that a required field is not available in the query, the response will indicate that an error has occurred and provide the reason for the error.
+If a required field is not available in the query, the response will indicate that an error has occurred and provide the reason for the error.
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[style] ~100-~100: ‘In the event that’ might be wordy. Consider a shorter alternative.
Context: ...t of traces> } ``` ## Malformed Queries In the event that a required field is not available in th...

(EN_WORDINESS_PREMIUM_IN_THE_EVENT_THAT)

</details>

</details>

---

`102-102`: **Fix heading level increment**

The heading level jumps from h2 to h4, which is not recommended in Markdown.

```diff
-#### Example
+### Example
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

102-102: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4

(MD001, heading-increment)

</details>

</details>

</blockquote></details>
<details>
<summary>components/query_handler/queryHandler.py (1)</summary><blockquote>

`13-13`: **Replace print statement with proper logging**

Using `print` statements for debugging is not recommended in production code. It's better to use a proper logging mechanism.

```diff
-    print(f"Received message: {message['queryType']}")    
+    import logging
+    logging.info(f"Received message: {message['queryType']}")    
```

</blockquote></details>
<details>
<summary>components/query_handler/queryFunctions.py (7)</summary><blockquote>

`1-1`: **Remove unused import**

The `json` module is imported but not used anywhere in the code.


```diff
-import json
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

---

`2-2`: **Update import style for better maintainability**

Consider using explicit relative import syntax for local modules according to Python best practices.


```diff
-from SystemDatabaseReader import SystemDatabaseReader
+from .SystemDatabaseReader import SystemDatabaseReader
```

---

`4-4`: **Consider dependency injection for better testability**

The global reader instance makes testing more difficult. Consider passing the reader as a parameter to each handler function for better testability.


```diff
-reader = SystemDatabaseReader()
+
+def create_handlers(reader=None):
+    """Create handler functions with the given reader or a default one."""
+    if reader is None:
+        reader = SystemDatabaseReader()
+    
+    # Return a dictionary of handlers bound to this reader
+    return {
+        "handleUnknownMessage": lambda message: handleUnknownMessage(reader, message),
+        "handleGetSystems": lambda message: handleGetSystems(reader, message),
+        # ... other handlers
+    }
```

---

`59-88`: **Fix typo in error message**

There's a spelling error in the error message.


```diff
-        message["response"] = "An error occured while getting deployments."
+        message["response"] = "An error occurred while getting deployments."
```

---

`90-125`: **Fix typo in error message and consider removing trailing comma**

There's a spelling error in the error message and an unnecessary trailing comma in the function call.


```diff
     response = reader.getTraces(
         systemId= message["data"]["systemId"],
         systemVersion= message["data"]["systemVersion"],
-        deploymentId= message["data"]["deploymentId"],
+        deploymentId= message["data"]["deploymentId"]
     )

     if (response is None):
         message["error"] = True
-        message["response"] = "An error occured while getting traces."
+        message["response"] = "An error occurred while getting traces."
```

---

`127-130`: **Fix typo in docstring**

There's a spelling error in the docstring.


```diff
 def handleUnknownCode(message):
     '''
-        Handle an unkown code.
+        Handle an unknown code.
     '''
```

---

`49-52`: **Improve error handling with more specific messages**

The error messages when the database response is None are generic. Consider adding more specific error information when possible, such as including the specific parameters that were used in the query.


Example for `handleGetPrograms`:
```diff
     if (response is None):
         message["error"] = True
-        message["response"] = "An error occurred while getting programs."
+        message["response"] = f"No programs found for system ID '{message['data']['systemId']}' with version '{message['data']['systemVersion']}' or the system does not exist."
```

Apply similar specific error messages to other handlers.


Also applies to: 80-83, 117-120

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between ad5a635fcb4dad2963c9668a8e9a1c015c7a5f7e and d7e502982554dccf9b16604244105ea7987690c5.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `components/query_handler/README.md` (1 hunks)
* `components/query_handler/SystemDatabaseReader.py` (1 hunks)
* `components/query_handler/queryFunctions.py` (1 hunks)
* `components/query_handler/queryHandler.py` (1 hunks)
* `components/query_handler/server.py` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (3)</summary>

<details>
<summary>components/query_handler/server.py (1)</summary><blockquote>

<details>
<summary>components/query_handler/queryHandler.py (1)</summary>

* `receieveMessage` (32-45)

</details>

</blockquote></details>
<details>
<summary>components/query_handler/queryFunctions.py (1)</summary><blockquote>

<details>
<summary>components/query_handler/SystemDatabaseReader.py (5)</summary>

* `SystemDatabaseReader` (4-137)
* `getSystems` (80-90)
* `getPrograms` (92-102)
* `getDeployments` (104-114)
* `getTraces` (116-137)

</details>

</blockquote></details>
<details>
<summary>components/query_handler/queryHandler.py (1)</summary><blockquote>

<details>
<summary>components/query_handler/queryFunctions.py (6)</summary>

* `handleUnknownMessage` (6-12)
* `handleGetSystems` (14-26)
* `handleGetPrograms` (28-56)
* `handleGetDeployments` (59-87)
* `handleGetTraces` (90-124)
* `handleUnknownCode` (127-133)

</details>

</blockquote></details>

</details><details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>components/query_handler/README.md</summary>

[style] ~100-~100: ‘In the event that’ might be wordy. Consider a shorter alternative.
Context: ...t of traces> } ``` ## Malformed Queries In the event that a required field is not available in th...

(EN_WORDINESS_PREMIUM_IN_THE_EVENT_THAT)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>components/query_handler/README.md</summary>

9-9: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

16-16: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

29-29: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

40-40: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

53-53: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

64-64: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

77-77: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

89-89: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

---

102-102: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4

(MD001, heading-increment)

---

103-103: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

</details>

</details>
<details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>components/query_handler/queryFunctions.py</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>
<details>
<summary>components/query_handler/queryHandler.py</summary>

3-3: `from queryFunctions import *` used; unable to detect undefined names

(F403)

---

10-10: `handleUnknownMessage` may be undefined, or defined from star imports

(F405)

---

16-16: `handleGetSystems` may be undefined, or defined from star imports

(F405)

---

19-19: `handleGetPrograms` may be undefined, or defined from star imports

(F405)

---

22-22: `handleGetDeployments` may be undefined, or defined from star imports

(F405)

---

25-25: `handleGetTraces` may be undefined, or defined from star imports

(F405)

---

28-28: `handleUnknownCode` may be undefined, or defined from star imports

(F405)

---

41-41: Local variable `e` is assigned to but never used

Remove assignment to unused variable `e`

(F841)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>components/query_handler/SystemDatabaseReader.py (3)</summary>

`1-38`: **LGTM: Good database connection management**

The class correctly initializes and closes database connections, with proper cleanup in `__del__` to ensure connections are properly terminated.

---

`127-136`: **Use parameterized query for trace retrieval - good practice!**

Well done using a parameterized query with `?` placeholder and passing parameters as a list for the `getTraces` method. This correctly prevents SQL injection for the `deploymentId` parameter.

---

`116-120`: <details>
<summary>❓ Verification inconclusive</summary>

**Address the TODO comment for trace filtering**

The code contains a TODO about adding functionality to filter traces by start and end timestamp.

Are you planning to implement this filtering in a future PR? If this is a critical feature, consider implementing it now or creating a separate issue to track this enhancement.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there's already an issue for timestamp filtering
gh issue list --repo vishalpalaniappan/asp-query-server --search "timestamp filter traces"
```

Length of output: 92

---



</details>

**Review the TODO for timestamp-based trace filtering**

I ran `gh issue list --repo vishalpalaniappan/asp-query-server --search "timestamp filter traces"` and didn’t find any existing issue. Please confirm whether:

- You plan to implement start/end timestamp filtering in a future PR  
- There’s already an issue tracking this enhancement  

If filtering by timestamps is critical, consider implementing it now or opening a new issue to track it.

</details>
<details>
<summary>components/query_handler/queryHandler.py (2)</summary>

`9-11`: **Good error handling for missing query type**

Checking for the presence of "queryType" and handling unknown messages appropriately is a good practice.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

10-10: `handleUnknownMessage` may be undefined, or defined from star imports

(F405)

</details>

</details>

---

`15-29`: **Clean message type dispatch logic**

The switch-case style logic for different message types is clean and maintainable. Each query type has its own handler function, which is a good separation of concerns.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

16-16: `handleGetSystems` may be undefined, or defined from star imports

(F405)

---

19-19: `handleGetPrograms` may be undefined, or defined from star imports

(F405)

---

22-22: `handleGetDeployments` may be undefined, or defined from star imports

(F405)

---

25-25: `handleGetTraces` may be undefined, or defined from star imports

(F405)

---

28-28: `handleUnknownCode` may be undefined, or defined from star imports

(F405)

</details>

</details>

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T21:22:53Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (5)</summary><blockquote>

<details>
<summary>components/query_handler/queryFunctions.py (5)</summary><blockquote>

`14-27`: _⚠️ Potential issue_

**Fix missing return statement in error case**

The function conditionally returns in the success case but has no explicit return in the error case. This will lead to inconsistent behavior.


```diff
 def handleGetSystems(message):
     '''
         Get all systems.
     '''
     response = reader.getSystems()

     if (response is None):
         message["error"] = True
         message["response"] = "An error occurred while getting all systems."
+        return message
     else:
         message["error"] = False
         message["response"] = response
         return message
```

---

`28-57`: _🛠️ Refactor suggestion_

**Add defensive coding for message data access**

The function assumes that message has a 'data' key without validation. This could lead to KeyError if a malformed message is processed.


```diff
 def handleGetPrograms(message):
     '''
         Get all programs given a system version and id.
     '''
-    data = message["data"]
+    data = message.get("data", {})
```

Additionally, consider extracting the parameter validation logic as mentioned in previous review comments to reduce duplication across handler functions.

---

`59-88`: _🛠️ Refactor suggestion_

**Apply consistent parameter validation across handler functions**

This function contains duplicate validation logic that's repeated in other handlers. Extract this to a common helper function to improve maintainability.



Create a validation helper as suggested in previous review comments and apply it here:

```diff
 def handleGetDeployments(message):
     '''
         Get all deployments given a system id and version.
     '''
-    data = message["data"]
-
-    if ("systemId" not in data):
-        message["response"] = "Request does not contain a system id."
-        message["error"] = True
-        return message
-
-    if ("systemVersion" not in data):
-        message["response"] = "Request does not contain a system version."
-        message["error"] = True
-        return message
+    is_valid, error_msg = validateParameters(message, ["systemId", "systemVersion"])
+    if not is_valid:
+        message["response"] = error_msg
+        message["error"] = True
+        return message
```

---

`1-133`: _🛠️ Refactor suggestion_

**Add a validateParameters helper function to reduce duplicate code**

The validation logic is duplicated across multiple handler functions. Extract it to a reusable helper function to reduce duplication and improve maintainability.



Add this helper function at the beginning of the file (after imports and reader initialization):

```python
def validateParameters(message, required_params):
    '''
        Validate that required parameters are present in the message data.
        Returns (is_valid, error_message) tuple.
    '''
    data = message.get("data", {})
    
    for param in required_params:
        if param not in data:
            return False, f"Request does not contain a {param}."
    
    return True, None
```

Then update all handler functions to use this helper.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

---

`90-125`: _🛠️ Refactor suggestion_

**Apply consistent parameter validation and remove trailing comma**

Similar to other handlers, extract validation logic to a common helper function. Also, remove the trailing comma in the function call for consistency with other similar calls.

```diff
 def handleGetTraces(message):
     '''
         Get all traces given a system id, version and deployment id.
     '''
-    data = message["data"]
-
-    if ("systemId" not in data):
-        message["response"] = "Request does not contain a system id."
-        message["error"] = True
-        return message
-
-    if ("systemVersion" not in data):
-        message["response"] = "Request does not contain a system version."
-        message["error"] = True
-        return message
-
-    if ("deploymentId" not in data):
-        message["response"] = "Request does not contain a deployment id."
-        message["error"] = True
-        return message
+    is_valid, error_msg = validateParameters(message, ["systemId", "systemVersion", "deploymentId"])
+    if not is_valid:
+        message["response"] = error_msg
+        message["error"] = True
+        return message
    
     response = reader.getTraces(
         systemId= message["data"]["systemId"],
         systemVersion= message["data"]["systemVersion"],
-        deploymentId= message["data"]["deploymentId"],
+        deploymentId= message["data"]["deploymentId"]
     )
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>components/query_handler/queryFunctions.py (4)</summary><blockquote>

`1-1`: **Remove unused import**

The `json` module is imported but never used in this file.

```diff
-import json
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

---

`2-2`: **Use relative import path for better module organization**

The import statement for `SystemDatabaseReader` uses a bare import, which can be fragile when reorganizing code. Consider using a relative import to make the dependency clear.

```diff
-from SystemDatabaseReader import SystemDatabaseReader
+from .SystemDatabaseReader import SystemDatabaseReader
```

---

`4-4`: **Consider initializing the reader in a function scope**

Creating the `SystemDatabaseReader` instance at module level can lead to persistent database connections and potential resource leaks. Consider moving this initialization to a function scope or implementing a singleton pattern.

```diff
-reader = SystemDatabaseReader()
+
+_reader_instance = None
+
+def get_reader():
+    """Return a singleton instance of SystemDatabaseReader."""
+    global _reader_instance
+    if _reader_instance is None:
+        _reader_instance = SystemDatabaseReader()
+    return _reader_instance
```

Then update all references to `reader` in the functions to call `get_reader()` instead.

---

`127-133`: **Fix typo in docstring and add defensive coding**

There's a typo in the docstring ("unkown" instead of "unknown"). Also, the function assumes that message has a 'code' key without validation.

```diff
 def handleUnknownCode(message):
     '''
-        Handle an unkown code.
+        Handle an unknown code.
     '''
-    message["response"] = f"Unknown message type: {message['code']}"
+    message["response"] = f"Unknown message type: {message.get('code', 'Missing code')}"
     message["error"] = True
     return message
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d7e502982554dccf9b16604244105ea7987690c5 and 42b0430cb2224fd2fdf6b2bc776cea4ac0b2c2b4.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `components/query_handler/queryFunctions.py` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>components/query_handler/queryFunctions.py (1)</summary><blockquote>

<details>
<summary>components/query_handler/SystemDatabaseReader.py (5)</summary>

* `SystemDatabaseReader` (4-137)
* `getSystems` (80-90)
* `getPrograms` (92-102)
* `getDeployments` (104-114)
* `getTraces` (116-137)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>components/query_handler/queryFunctions.py</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T21:30:22Z
user: login: vishalpalaniappan
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-22T21:30:49Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T21:31:09Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (4)</summary><blockquote>

<details>
<summary>components/query_handler/queryFunctions.py (4)</summary><blockquote>

`29-58`: **Add defensive coding for missing data key**

The function directly accesses `message["data"]` without checking if the key exists, which could lead to a KeyError. This aligns with a past review comment that hasn't been addressed.

```diff
 def handleGetPrograms(message):
     '''
         Get all programs given a system version and id.
     '''
-    data = message["data"]
+    data = message.get("data", {})
```

---

`59-89`: **Add defensive coding for missing data key in handleGetDeployments**

Similar to `handleGetPrograms`, this function directly accesses `message["data"]` without checking if the key exists.

```diff
 def handleGetDeployments(message):
     '''
         Get all deployments given a system id and version.
     '''
-    data = message["data"]
+    data = message.get("data", {})
```

---

`91-126`: **Add defensive coding for missing data key in handleGetTraces**

The function directly accesses `message["data"]` without checking if the key exists, which could lead to a KeyError.

```diff
 def handleGetTraces(message):
     '''
         Get all traces given a system id, version and deployment id.
     '''
-    data = message["data"]
+    data = message.get("data", {})
```

---

`33-43`: _🛠️ Refactor suggestion_

**Extract common validation logic**

The parameter validation logic is duplicated across `handleGetPrograms`, `handleGetDeployments`, and `handleGetTraces`. As suggested in a previous review, this should be extracted to a helper function.


```diff
+def validateParameters(message, required_params):
+    '''
+        Validate that required parameters are present in the message data.
+        Returns (is_valid, error_message) tuple.
+    '''
+    data = message.get("data", {})
+    
+    for param in required_params:
+        if param not in data:
+            return False, f"Request does not contain a {param}."
+    
+    return True, None

 def handleGetPrograms(message):
     '''
         Get all programs given a system version and id.
     '''
-    data = message["data"]
-
-    if ("systemId" not in data):
-        message["response"] = "Request does not contain a system id."
-        message["error"] = True
-        return message
-
-    if ("systemVersion" not in data):
-        message["response"] = "Request does not contain a system version."
-        message["error"] = True
-        return message
+    is_valid, error_msg = validateParameters(message, ["systemId", "systemVersion"])
+    if not is_valid:
+        message["response"] = error_msg
+        message["error"] = True
+        return message
```

Apply similar changes to `handleGetDeployments` and `handleGetTraces`.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>components/query_handler/queryFunctions.py (5)</summary><blockquote>

`1-1`: **Remove unused import**

The `json` module is imported but never used in this file.

```diff
-import json
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

---

`2-2`: **Use relative import path**

When importing from a module in the same directory, it's better to use a relative import.

```diff
-from SystemDatabaseReader import SystemDatabaseReader
+from .SystemDatabaseReader import SystemDatabaseReader
```

---

`4-4`: **Consider lazy initialization of the reader**

Creating a global instance of `SystemDatabaseReader` at module import time establishes database connections immediately, which might not be necessary if the functions aren't called right away. This could lead to unnecessary resource usage.

Consider initializing the reader when it's first needed:

```diff
-reader = SystemDatabaseReader()
+_reader = None
+
+def get_reader():
+    global _reader
+    if _reader is None:
+        _reader = SystemDatabaseReader()
+    return _reader
```

Then update references to `reader` in the handler functions to use `get_reader()`.

---

`14-28`: **Fix inconsistent return pattern in handleGetSystems**

This function's control flow is inconsistent. In the error case, it sets `message` values but doesn't return, while the success case has an explicit return. This might be a remnant of the issue fixed in commit 42b0430.

```diff
 def handleGetSystems(message):
     '''
         Get all systems.
     '''
     response = reader.getSystems()

     if (response is None):
         message["error"] = True
         message["response"] = "An error occurred while getting all systems."
     else:
         message["error"] = False
         message["response"] = response
-        
 
     return message
```

---

`128-134`: **Fix typo in docstring**

There's a typo in the docstring: "unkown" should be "unknown".

```diff
 def handleUnknownCode(message):
     '''
-        Handle an unkown code.
+        Handle an unknown code.
     '''
     message["response"] = f"Unknown message type: {message['code']}"
     message["error"] = True
     return message
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 42b0430cb2224fd2fdf6b2bc776cea4ac0b2c2b4 and fa38cdbc1d765340c4599bd680a71bb78783212f.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `components/query_handler/queryFunctions.py` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>components/query_handler/queryFunctions.py (1)</summary><blockquote>

<details>
<summary>components/query_handler/SystemDatabaseReader.py (5)</summary>

* `SystemDatabaseReader` (4-137)
* `getSystems` (80-90)
* `getPrograms` (92-102)
* `getDeployments` (104-114)
* `getTraces` (116-137)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>components/query_handler/queryFunctions.py</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T21:32:54Z
user: login: vishalpalaniappan
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-22T21:34:42Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T21:35:08Z
user: login: vishalpalaniappan
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-22T21:37:00Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T21:37:19Z
user: login: vishalpalaniappan
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-22T21:37:49Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T21:38:15Z
user: login: vishalpalaniappan
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-22T21:42:35Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T21:43:15Z
user: login: vishalpalaniappan
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-22T21:59:27Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (3)</summary><blockquote>

<details>
<summary>components/query_handler/queryHandler.py (1)</summary><blockquote>

`3-3`: _🛠️ Refactor suggestion_

**Avoid wildcard imports**

Using wildcard imports (`from queryFunctions import *`) makes it harder to trace which functions are being used and can lead to name collisions. Consider importing specific functions instead.



```diff
-from queryFunctions import *
+from queryFunctions import (
+    handleUnknownMessage, handleGetSystems, handleGetPrograms,
+    handleGetDeployments, handleGetTraces, handleUnknownQueryType
+)
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

3-3: `from queryFunctions import *` used; unable to detect undefined names

(F403)

</details>

</details>

</blockquote></details>
<details>
<summary>components/query_handler/queryFunctions.py (2)</summary><blockquote>

`73-110`: **Consider refactoring duplicate validation logic**

The validation patterns in `handleGetPrograms`, `handleGetDeployments`, and `handleGetTraces` are very similar. Consider extracting a helper function to reduce code duplication.



```diff
+def validateParameters(message, required_params):
+    '''
+        Validate that required parameters are present in the message data.
+        Returns (is_valid, error_message) tuple.
+    '''
+    if "data" not in message:
+        return False, "Request does not contain the required data key."
+    
+    data = message["data"]
+    
+    for param in required_params:
+        if param not in data:
+            return False, f"Request does not contain a {param}."
+    
+    return True, None

 def handleGetDeployments(message):
     '''
         Get all deployments given a system id and version.
     '''
-    if ("data" not in message):
-        message["response"] = "Request does not contain the required data key."
-        message["error"] = True
-        return message
-    
-    data = message["data"]
-
-    if ("systemId" not in data):
-        message["response"] = "Request does not contain a system id."
-        message["error"] = True
-        return message
-
-    if ("systemVersion" not in data):
-        message["response"] = "Request does not contain a system version."
-        message["error"] = True
-        return message
+    is_valid, error_msg = validateParameters(message, ["systemId", "systemVersion"])
+    if not is_valid:
+        message["response"] = error_msg
+        message["error"] = True
+        return message
```

---

`159-165`: _🛠️ Refactor suggestion_

**Add defensive coding for missing queryType key**

The function directly accesses `message['queryType']` without checking if the key exists, which could lead to a KeyError.



```diff
 def handleUnknownQueryType(message):
     '''
         Handle an unkown query type.
     '''
-    message["response"] = f"Unknown message type: {message['queryType']}"
+    query_type = message.get('queryType', 'MISSING_QUERY_TYPE')
+    message["response"] = f"Unknown message type: {query_type}"
     message["error"] = True
     return message
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>components/query_handler/queryHandler.py (1)</summary><blockquote>

`32-45`: **Well-implemented error handling for JSON parsing**

The error handling for JSON decode errors is now correctly implemented with the error flag set to `True`, aligning with error handling patterns throughout the codebase.


One minor optimization:
```diff
-        except json.JSONDecodeError as e:
+        except json.JSONDecodeError:
```

The exception variable `e` is declared but never used, so it can be omitted.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

41-41: Local variable `e` is assigned to but never used

Remove assignment to unused variable `e`

(F841)

</details>

</details>

</blockquote></details>
<details>
<summary>components/query_handler/queryFunctions.py (1)</summary><blockquote>

`1-1`: **Remove unused import**

The `json` module is imported but not used anywhere in this file.


```diff
-import json
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fa38cdbc1d765340c4599bd680a71bb78783212f and 71de69624979e884ef73db3632cfdb748e9cd04a.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `components/query_handler/queryFunctions.py` (1 hunks)
* `components/query_handler/queryHandler.py` (1 hunks)
* `components/query_handler/server.py` (2 hunks)

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (1)</summary>

* components/query_handler/server.py

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>components/query_handler/queryHandler.py (1)</summary><blockquote>

<details>
<summary>components/query_handler/queryFunctions.py (6)</summary>

* `handleUnknownMessage` (6-12)
* `handleGetSystems` (14-31)
* `handleGetPrograms` (33-70)
* `handleGetDeployments` (73-110)
* `handleGetTraces` (113-156)
* `handleUnknownQueryType` (159-165)

</details>

</blockquote></details>
<details>
<summary>components/query_handler/queryFunctions.py (1)</summary><blockquote>

<details>
<summary>components/query_handler/SystemDatabaseReader.py (5)</summary>

* `SystemDatabaseReader` (4-137)
* `getSystems` (80-90)
* `getPrograms` (92-102)
* `getDeployments` (104-114)
* `getTraces` (116-137)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>components/query_handler/queryHandler.py</summary>

3-3: `from queryFunctions import *` used; unable to detect undefined names

(F403)

---

10-10: `handleUnknownMessage` may be undefined, or defined from star imports

(F405)

---

16-16: `handleGetSystems` may be undefined, or defined from star imports

(F405)

---

19-19: `handleGetPrograms` may be undefined, or defined from star imports

(F405)

---

22-22: `handleGetDeployments` may be undefined, or defined from star imports

(F405)

---

25-25: `handleGetTraces` may be undefined, or defined from star imports

(F405)

---

28-28: `handleUnknownQueryType` may be undefined, or defined from star imports

(F405)

---

41-41: Local variable `e` is assigned to but never used

Remove assignment to unused variable `e`

(F841)

</details>
<details>
<summary>components/query_handler/queryFunctions.py</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (6)</summary><blockquote>

<details>
<summary>components/query_handler/queryHandler.py (1)</summary>

`5-30`: **LGTM! Well-structured query handler**

The `handleQuery` function follows a clean pattern with early returns for error cases, clear conditional logic for different query types, and proper delegation to specialized handler functions.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

10-10: `handleUnknownMessage` may be undefined, or defined from star imports

(F405)

---

16-16: `handleGetSystems` may be undefined, or defined from star imports

(F405)

---

19-19: `handleGetPrograms` may be undefined, or defined from star imports

(F405)

---

22-22: `handleGetDeployments` may be undefined, or defined from star imports

(F405)

---

25-25: `handleGetTraces` may be undefined, or defined from star imports

(F405)

---

28-28: `handleUnknownQueryType` may be undefined, or defined from star imports

(F405)

</details>

</details>

</details>
<details>
<summary>components/query_handler/queryFunctions.py (5)</summary>

`2-4`: **LGTM! Good database reader initialization**

Appropriate database reader initialization for handling queries.

---

`6-12`: **LGTM! Well-implemented error handler**

The unknown message handler correctly sets the error flag and returns an appropriate message.

---

`14-31`: **LGTM! Comprehensive error handling for database operations**

The try-except block properly catches any exceptions from database operations, preventing server crashes and providing useful error messages to clients.

---

`33-70`: **LGTM! Well-structured validation with proper error messages**

The handler properly validates all required parameters and includes appropriate error messages. The database error handling is comprehensive.

---

`113-156`: **LGTM! Thorough validation for trace queries**

The function correctly validates all three required parameters and includes comprehensive database error handling.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T22:00:21Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T22:00:32Z

Review comments:
diff_hunk: @@ -0,0 +1,127 @@
+import sqlite3
+import os
+
+class SystemDatabaseReader:
+    '''
+        This class will perform queries on the ASP database and return
+        the results.
+    '''
+
+    def __init__(self):
+        self.openConnections()
+
+    def openConnections(self):
+        '''
+            Initialize the database connections.
+        '''
+        path = os.path.dirname(os.path.dirname(__file__))
+        aspPath = os.path.join(path, "asp.db")
+        try:
+            self.aspConn = sqlite3.connect(aspPath)
+            self.aspCursor = self.aspConn.cursor()
+        except sqlite3.Error as e:
+            print(f"Database error: {e}")
+            raise
+
+    def closeConnections(self):
+        '''
+            Close the database connections.
+        '''
+        if hasattr(self, 'sysIoConn'):
+            self.sysIoConn.close()
+        if hasattr(self, 'aspConn'):
+            self.aspConn.close()
path: components/query_handler/SystemDatabaseReader.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Unused database connection check**

The code checks for the existence of `sysIoConn`, but this attribute is never initialized in the `openConnections()` method. Either remove this check or add the connection initialization.

```diff
-        if hasattr(self, 'sysIoConn'):
-            self.sysIoConn.close()
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        if hasattr(self, 'aspConn'):
            self.aspConn.close()
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->

✅ Addressed in commits 32d25a1 to d7e5029

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-22T17:34:08Z
author_association: NONE
diff_hunk: @@ -0,0 +1,127 @@
+import sqlite3
+import os
+
+class SystemDatabaseReader:
+    '''
+        This class will perform queries on the ASP database and return
+        the results.
+    '''
+
+    def __init__(self):
+        self.openConnections()
+
+    def openConnections(self):
+        '''
+            Initialize the database connections.
+        '''
+        path = os.path.dirname(os.path.dirname(__file__))
+        aspPath = os.path.join(path, "asp.db")
+        try:
+            self.aspConn = sqlite3.connect(aspPath)
+            self.aspCursor = self.aspConn.cursor()
+        except sqlite3.Error as e:
+            print(f"Database error: {e}")
+            raise
+
+    def closeConnections(self):
+        '''
+            Close the database connections.
+        '''
+        if hasattr(self, 'sysIoConn'):
+            self.sysIoConn.close()
+        if hasattr(self, 'aspConn'):
+            self.aspConn.close()
+
+    def __del__(self):
+        '''
+            Clean up database connections.
+        '''
+        self.closeConnections()
+
+    def getColumns(self, cursor, tableName):
+        '''
+            Get the columns for a table.
+        '''
+        query = f'PRAGMA table_info("{tableName}")' 
+        cursor.execute(query)
+        return cursor.fetchall()
path: components/query_handler/SystemDatabaseReader.py
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**SQL injection risk in table name**

Although this is an internal method, it's good practice to avoid f-strings in SQL queries. The `PRAGMA table_info` command is safe here since SQLite treats table names as identifiers, but consider adding a validation check for table names.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there are any risky SQL queries in other parts of the codebase
rg -A 1 -B 1 'execute\(f' --glob='*.py'
```

Length of output: 1266

---



</details>

**Sanitize dynamic table identifiers**  
While SQLite treats table names as identifiers in both `PRAGMA` and `CREATE TABLE` statements, embedding unsanitized f‑string values can introduce injection risks if `tableName`, `sysId` or `sysVer` ever contain unexpected characters. To harden these calls, validate table names against a strict pattern before interpolating:

• Enforce a regex such as `^[A-Za-z_][A-Za-z0-9_]*$`.  
• Centralize this in a helper, e.g.:

```python
import re

def _validate_identifier(name: str) -> str:
    if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', name):
        raise ValueError(f"Invalid SQL identifier: {name!r}")
    return name
```

• Apply it to both locations:

  - components/query_handler/SystemDatabaseReader.py → `getColumns`
  - components/asp/database/SystemWriter.py → dynamic `CREATE TABLE` statements

Example update for `getColumns`:

```diff
-   def getColumns(self, cursor, tableName):
+   def getColumns(self, cursor, tableName):
        '''
            Get the columns for a table.
        '''
-       query = f'PRAGMA table_info("{tableName}")'
+       table = _validate_identifier(tableName)
+       query = f'PRAGMA table_info("{table}")'
        cursor.execute(query)
        return cursor.fetchall()
```

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-22T17:34:08Z
author_association: NONE
diff_hunk: @@ -0,0 +1,127 @@
+import sqlite3
+import os
+
+class SystemDatabaseReader:
+    '''
+        This class will perform queries on the ASP database and return
+        the results.
+    '''
+
+    def __init__(self):
+        self.openConnections()
+
+    def openConnections(self):
+        '''
+            Initialize the database connections.
+        '''
+        path = os.path.dirname(os.path.dirname(__file__))
+        aspPath = os.path.join(path, "asp.db")
+        try:
+            self.aspConn = sqlite3.connect(aspPath)
+            self.aspCursor = self.aspConn.cursor()
+        except sqlite3.Error as e:
+            print(f"Database error: {e}")
+            raise
+
+    def closeConnections(self):
+        '''
+            Close the database connections.
+        '''
+        if hasattr(self, 'sysIoConn'):
+            self.sysIoConn.close()
+        if hasattr(self, 'aspConn'):
+            self.aspConn.close()
+
+    def __del__(self):
+        '''
+            Clean up database connections.
+        '''
+        self.closeConnections()
+
+    def getColumns(self, cursor, tableName):
+        '''
+            Get the columns for a table.
+        '''
+        query = f'PRAGMA table_info("{tableName}")' 
+        cursor.execute(query)
+        return cursor.fetchall()
+    
+    
+    def addColumnNameToData(self, columns, data):
+        '''
+            Given the column names and the data, create 
+            an object which uses the column names as key.
+        '''
+        obj = {}
+        for index, columnValue in enumerate(data):
+            obj[columns[index][1]] = columnValue
+        return obj
+    
+    def getAllEntriesInTable(self, tableName):
+        '''
+            Given a table name, get all entries in the table.
+        '''
+        query = f'SELECT * FROM "{tableName}"'
+        self.aspCursor.execute(query)
+        rows = self.aspCursor.fetchall() 
+        columns = self.getColumns(self.aspCursor, tableName)
+
+        results = []
+        for row in rows:
+            results.append(self.addColumnNameToData(columns, row))
+
+        return results
path: components/query_handler/SystemDatabaseReader.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Missing error handling for non-existent tables**

The `getAllEntriesInTable` method doesn't handle the case where the table doesn't exist. Consider adding try/except to catch and handle SQLite errors.

```diff
     def getAllEntriesInTable(self, tableName):
         '''
             Given a table name, get all entries in the table.
         '''
-        query = f'SELECT * FROM "{tableName}"'
-        self.aspCursor.execute(query)
-        rows = self.aspCursor.fetchall() 
-        columns = self.getColumns(self.aspCursor, tableName)
-
-        results = []
-        for row in rows:
-            results.append(self.addColumnNameToData(columns, row))
-
-        return results
+        try:
+            query = f'SELECT * FROM "{tableName}"'
+            self.aspCursor.execute(query)
+            rows = self.aspCursor.fetchall() 
+            columns = self.getColumns(self.aspCursor, tableName)
+
+            results = []
+            for row in rows:
+                results.append(self.addColumnNameToData(columns, row))
+
+            return results
+        except sqlite3.Error as e:
+            print(f"Error querying table {tableName}: {e}")
+            return []
```

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-22T17:34:08Z
author_association: NONE
diff_hunk: @@ -1,33 +1,65 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from SystemDatabaseReader import SystemDatabaseReader
+from queryFunctions import *
path: components/query_handler/queryHandler.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Avoid wildcard imports**

Using wildcard imports (`from queryFunctions import *`) makes it harder to trace which functions are being used and can lead to name collisions. Consider importing specific functions instead.

```diff
-from queryFunctions import *
+from queryFunctions import (
+    handleUnknownMessage, handleGetSystems, handleGetPrograms,
+    handleGetDeployments, handleGetTraces, handleUnknownCode
+)
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
from SystemDatabaseReader import SystemDatabaseReader
from queryFunctions import (
    handleUnknownMessage, handleGetSystems, handleGetPrograms,
    handleGetDeployments, handleGetTraces, handleUnknownCode
)
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

4-4: `from queryFunctions import *` used; unable to detect undefined names

(F403)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-22T17:34:08Z
author_association: NONE
diff_hunk: @@ -1,33 +1,65 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from SystemDatabaseReader import SystemDatabaseReader
+from queryFunctions import *
+
+reader = SystemDatabaseReader()
+
+def getMessageFromCode(code):
+    '''
+        Get the code description from the code.
+    '''
+    for key in MSG_TYPES:
+        value = MSG_TYPES[key]
+        if (value == code):
+            return key        
+    return "Unkown message code."
 
 async def handle_query(websocket):
     '''
         Handles messages from websocket and echos a response.
     '''
-    global processor
-
     async for message in websocket:
-        print(f"Received message: {message}")
-
-        message = json.loads(message)
+        message = json.loads(message)        
 
+        print(f"Received message: {getMessageFromCode(message['code'])}")
+        
         if "code" not in message:
-            message["response"] = "Message does not contain a code"
-            message["error"] = True
+            handleUnknownMessage(
+                websocket= websocket,
+                message= message
+            )
+        
+        elif (message["code"] == MSG_TYPES["GET_SYSTEMS"]):            
+            message = handleGetSystems(
+                reader= reader,
+                message= message
+            )
             await websocket.send(json.dumps(message))
-
-        elif (message["code"] == MSG_TYPES["GET_UNIQUE_TRACES"]):
-            message["error"] = False
+            
+        elif (message["code"] == MSG_TYPES["GET_PROGRAMS"]):            
+            message = handleGetPrograms(
+                reader= reader,
+                message= message
+            )
             await websocket.send(json.dumps(message))
-
-        elif (message["code"] == MSG_TYPES["GET_FILE_TREES"]):
+        
+        elif (message["code"] == MSG_TYPES["GET_DEPLOYMENTS"]):
+            message = handleGetDeployments(
+                reader= reader,
+                message= message
+            )
+            await websocket.send(json.dumps(message))
+        
+        elif (message["code"] == MSG_TYPES["GET_TRACES"]):
             message["error"] = False
             await websocket.send(json.dumps(message))
path: components/query_handler/queryHandler.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**GET_TRACES handler is incomplete**

Unlike other handlers, the GET_TRACES case sets error to false directly without calling the appropriate handler function. This is inconsistent and likely incorrect.

```diff
        elif (message["code"] == MSG_TYPES["GET_TRACES"]):
-            message["error"] = False
+            message = handleGetTraces(
+                reader= reader,
+                message= message
+            )
            await websocket.send(json.dumps(message))
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        elif (message["code"] == MSG_TYPES["GET_TRACES"]):
            message = handleGetTraces(
                reader= reader,
                message= message
            )
            await websocket.send(json.dumps(message))
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-22T17:34:08Z
author_association: NONE
diff_hunk: @@ -1,33 +1,65 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from SystemDatabaseReader import SystemDatabaseReader
+from queryFunctions import *
+
+reader = SystemDatabaseReader()
+
+def getMessageFromCode(code):
+    '''
+        Get the code description from the code.
+    '''
+    for key in MSG_TYPES:
+        value = MSG_TYPES[key]
+        if (value == code):
+            return key        
+    return "Unkown message code."
 
 async def handle_query(websocket):
     '''
         Handles messages from websocket and echos a response.
     '''
-    global processor
-
     async for message in websocket:
-        print(f"Received message: {message}")
-
-        message = json.loads(message)
+        message = json.loads(message)        
 
+        print(f"Received message: {getMessageFromCode(message['code'])}")
+        
         if "code" not in message:
-            message["response"] = "Message does not contain a code"
-            message["error"] = True
+            handleUnknownMessage(
+                websocket= websocket,
+                message= message
+            )
path: components/query_handler/queryHandler.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Missing response sending for unknown message**

When handling an unknown message (no code), the function returns the modified message but doesn't send it to the websocket, unlike other handlers.

```diff
         if "code" not in message:
-            handleUnknownMessage(
+            message = handleUnknownMessage(
                 websocket= websocket,
                 message= message
             )
+            await websocket.send(json.dumps(message))
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        if "code" not in message:
            message = handleUnknownMessage(
                websocket= websocket,
                message= message
            )
            await websocket.send(json.dumps(message))
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

28-28: `handleUnknownMessage` may be undefined, or defined from star imports

(F405)

</details>

</details>

<!-- This is an auto-generated reply by CodeRabbit -->

✅ Addressed in commits 32d25a1 to d7e5029

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-22T17:34:08Z
author_association: NONE
diff_hunk: @@ -0,0 +1,103 @@
+import json
+
+def handleUnknownMessage(websocket, message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(reader, message):
+    '''
+        Get all systems.
+    '''
+    message["response"] = reader.getSystems()
+    message["error"] = False
+    return message
+
+def handleGetPrograms(reader, message):
+    '''
+        Get all programs given a system version and id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    message["response"] = reader.getPrograms(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+    message["error"] = False
+    return message
+
+
+def handleGetDeployments(reader, message):
+    '''
+        Get all deployments given a system id and version.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    message["response"] = reader.getDeployments(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+    message["error"] = False
+    return message
+
+
+def handleGetTraces(reader, message):
+    '''
+        Get all traces given a system id, version and deployment id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    if ("deploymentId" not in data):
+        message["response"] = "Request does not contain a deployment id."
+        message["error"] = True
+        return message
+    
+    message["response"] = reader.getTraces(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"],
+        deploymentId= message["data"]["deploymentId"],
+    )
+    message["error"] = False
+    return message
path: components/query_handler/queryFunctions.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Refactor duplicate validation logic**

The validation logic in `handleGetPrograms`, `handleGetDeployments`, and `handleGetTraces` is very similar. Consider extracting a helper function to reduce duplication.

```diff
+def validateParameters(message, required_params):
+    '''
+        Validate that required parameters are present in the message data.
+        Returns (is_valid, error_message) tuple.
+    '''
+    data = message.get("data", {})
+    
+    for param in required_params:
+        if param not in data:
+            return False, f"Request does not contain a {param}."
+    
+    return True, None

 def handleGetPrograms(reader, message):
     '''
         Get all programs given a system version and id.
     '''
-    data = message["data"]
-
-    if ("systemId" not in data):
-        message["response"] = "Request does not contain a system id."
-        message["error"] = True
-        return message
-
-    if ("systemVersion" not in data):
-        message["response"] = "Request does not contain a system version."
-        message["error"] = True
-        return message
+    is_valid, error_msg = validateParameters(message, ["systemId", "systemVersion"])
+    if not is_valid:
+        message["response"] = error_msg
+        message["error"] = True
+        return message

     message["response"] = reader.getPrograms(
         systemId= message["data"]["systemId"],
         systemVersion= message["data"]["systemVersion"]
     )
     message["error"] = False
     return message
```

Apply similar changes to `handleGetDeployments` and `handleGetTraces`.


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-22T17:34:08Z
author_association: NONE
diff_hunk: @@ -1,33 +1,65 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from SystemDatabaseReader import SystemDatabaseReader
+from queryFunctions import *
+
+reader = SystemDatabaseReader()
+
+def getMessageFromCode(code):
+    '''
+        Get the code description from the code.
+    '''
+    for key in MSG_TYPES:
+        value = MSG_TYPES[key]
+        if (value == code):
+            return key        
+    return "Unkown message code."
 
 async def handle_query(websocket):
     '''
         Handles messages from websocket and echos a response.
     '''
-    global processor
-
     async for message in websocket:
-        print(f"Received message: {message}")
-
-        message = json.loads(message)
+        message = json.loads(message)        
 
+        print(f"Received message: {getMessageFromCode(message['code'])}")
+        
         if "code" not in message:
-            message["response"] = "Message does not contain a code"
-            message["error"] = True
+            handleUnknownMessage(
+                websocket= websocket,
+                message= message
+            )
path: components/query_handler/queryHandler.py
user: login: vishalpalaniappan
body: Good catch. I ended up restructuring the query handler in commit https://github.com/vishalpalaniappan/asp-query-server/pull/26/commits/df6dc39e2fa96a08a001424c42523e44c4ec952b and addressed this issue there.
created_at: 2025-04-22T20:57:31Z
author_association: OWNER
diff_hunk: @@ -1,33 +1,65 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from SystemDatabaseReader import SystemDatabaseReader
+from queryFunctions import *
+
+reader = SystemDatabaseReader()
+
+def getMessageFromCode(code):
+    '''
+        Get the code description from the code.
+    '''
+    for key in MSG_TYPES:
+        value = MSG_TYPES[key]
+        if (value == code):
+            return key        
+    return "Unkown message code."
 
 async def handle_query(websocket):
     '''
         Handles messages from websocket and echos a response.
     '''
-    global processor
-
     async for message in websocket:
-        print(f"Received message: {message}")
-
-        message = json.loads(message)
+        message = json.loads(message)        
 
+        print(f"Received message: {getMessageFromCode(message['code'])}")
+        
         if "code" not in message:
-            message["response"] = "Message does not contain a code"
-            message["error"] = True
+            handleUnknownMessage(
+                websocket= websocket,
+                message= message
+            )
path: components/query_handler/queryHandler.py
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

@vishalpalaniappan Thanks for confirming! I've looked at your commit and the restructuring of the query handler does address the issue with response handling for unknown messages. Great job implementing the fix. The new structure also looks cleaner and more maintainable.



---

🏁 Script executed:
```
#!/bin/bash
# Let's examine the commit that fixed the issue
git show df6dc39e2fa96a08a001424c42523e44c4ec952b -- components/query_handler/queryHandler.py
```

Length of output: 3304

---



</details>

@vishalpalaniappan Thanks for confirming! I've looked at your commit and the restructuring of the query handler does address the issue with response handling for unknown messages. Great job implementing the fix. The new structure also looks cleaner and more maintainable.



---

<details>
<summary>🧬 Code Graph Analysis Results</summary>

### Relevant snippets from `components/query_handler/queryFunctions.py`

**Lines 6-12:**
```python
def handleUnknownMessage(message):
    '''
        Handle an unknown message.
    '''
    message["response"] = "Message does not contain a code"
    message["error"] = True
    return message
```

**Lines 14-20:**
```python
def handleGetSystems(message):
    '''
        Get all systems.
    '''
    message["response"] = reader.getSystems()
    message["error"] = False
    return message
```

**Lines 22-43:**
```python
def handleGetPrograms(message):
    '''
        Get all programs given a system version and id.
    '''
    data = message["data"]

    if ("systemId" not in data):
        message["response"] = "Request does not contain a system id."
        message["error"] = True
        return message

    if ("systemVersion" not in data):
        message["response"] = "Request does not contain a system version."
        message["error"] = True
        return message

    message["response"] = reader.getPrograms(
        systemId= message["data"]["systemId"],
        systemVersion= message["data"]["systemVersion"]
    )
    message["error"] = False
    return message
```

**Lines 46-67:**
```python
def handleGetDeployments(message):
    '''
        Get all deployments given a system id and version.
    '''
    data = message["data"]

    if ("systemId" not in data):
        message["response"] = "Request does not contain a system id."
        message["error"] = True
        return message

    if ("systemVersion" not in data):
        message["response"] = "Request does not contain a system version."
        message["error"] = True
        return message

    message["response"] = reader.getDeployments(
        systemId= message["data"]["systemId"],
        systemVersion= message["data"]["systemVersion"]
    )
    message["error"] = False
    return message
```

**Lines 70-97:**
```python
def handleGetTraces(message):
    '''
        Get all traces given a system id, version and deployment id.
    '''
    data = message["data"]

    if ("systemId" not in data):
        message["response"] = "Request does not contain a system id."
        message["error"] = True
        return message

    if ("systemVersion" not in data):
        message["response"] = "Request does not contain a system version."
        message["error"] = True
        return message

    if ("deploymentId" not in data):
        message["response"] = "Request does not contain a deployment id."
        message["error"] = True
        return message
    
    message["response"] = reader.getTraces(
        systemId= message["data"]["systemId"],
        systemVersion= message["data"]["systemVersion"],
        deploymentId= message["data"]["deploymentId"],
    )
    message["error"] = False
    return message
```

**Lines 100-106:**
```python
def handleUnknownCode(message):
    '''
        Handle an unkown code.
    '''
    message["response"] = f"Unknown message type: {message['code']}"
    message["error"] = True
    return message
```

---

These snippets define the specific handlers called by `handleQuery` in `queryHandler.py` for different query types, including error handling for missing fields and unknown message types. They provide the detailed logic for processing the queries referenced in the main file.

</details>

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=%40coderabbitai%20spotted%20a%20missing%20websocket%20send%20for%20unknown%20messages%E2%80%94fixing%20this%20ensures%20consistent%20real-time%20responses!%20%F0%9F%9A%80%20Try%20%40coderabbitai%20to%20catch%20subtle%20async%20bugs%20before%20they%20hit%20prod!%20%F0%9F%90%87&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054554156)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer><details>
<summary>🧩 Analysis chain</summary>

@vishalpalaniappan Thanks for confirming! I've looked at your commit and the restructuring of the query handler does address the issue with response handling for unknown messages. Great job implementing the fix. The new structure also looks cleaner and more maintainable.



---

🏁 Script executed:
```
#!/bin/bash
# Let's examine the commit that fixed the issue
git show df6dc39e2fa96a08a001424c42523e44c4ec952b -- components/query_handler/queryHandler.py
```

Length of output: 3304

---



</details>

@vishalpalaniappan Thanks for confirming! I've looked at your commit and the restructuring of the query handler does address the issue with response handling for unknown messages. Great job implementing the fix. The new structure also looks cleaner and more maintainable.



---

<details>
<summary>🧬 Code Graph Analysis Results</summary>

### Relevant snippets from `components/query_handler/queryFunctions.py`

**Lines 6-12:**
```python
def handleUnknownMessage(message):
    '''
        Handle an unknown message.
    '''
    message["response"] = "Message does not contain a code"
    message["error"] = True
    return message
```

**Lines 14-20:**
```python
def handleGetSystems(message):
    '''
        Get all systems.
    '''
    message["response"] = reader.getSystems()
    message["error"] = False
    return message
```

**Lines 22-43:**
```python
def handleGetPrograms(message):
    '''
        Get all programs given a system version and id.
    '''
    data = message["data"]

    if ("systemId" not in data):
        message["response"] = "Request does not contain a system id."
        message["error"] = True
        return message

    if ("systemVersion" not in data):
        message["response"] = "Request does not contain a system version."
        message["error"] = True
        return message

    message["response"] = reader.getPrograms(
        systemId= message["data"]["systemId"],
        systemVersion= message["data"]["systemVersion"]
    )
    message["error"] = False
    return message
```

**Lines 46-67:**
```python
def handleGetDeployments(message):
    '''
        Get all deployments given a system id and version.
    '''
    data = message["data"]

    if ("systemId" not in data):
        message["response"] = "Request does not contain a system id."
        message["error"] = True
        return message

    if ("systemVersion" not in data):
        message["response"] = "Request does not contain a system version."
        message["error"] = True
        return message

    message["response"] = reader.getDeployments(
        systemId= message["data"]["systemId"],
        systemVersion= message["data"]["systemVersion"]
    )
    message["error"] = False
    return message
```

**Lines 70-97:**
```python
def handleGetTraces(message):
    '''
        Get all traces given a system id, version and deployment id.
    '''
    data = message["data"]

    if ("systemId" not in data):
        message["response"] = "Request does not contain a system id."
        message["error"] = True
        return message

    if ("systemVersion" not in data):
        message["response"] = "Request does not contain a system version."
        message["error"] = True
        return message

    if ("deploymentId" not in data):
        message["response"] = "Request does not contain a deployment id."
        message["error"] = True
        return message
    
    message["response"] = reader.getTraces(
        systemId= message["data"]["systemId"],
        systemVersion= message["data"]["systemVersion"],
        deploymentId= message["data"]["deploymentId"],
    )
    message["error"] = False
    return message
```

**Lines 100-106:**
```python
def handleUnknownCode(message):
    '''
        Handle an unkown code.
    '''
    message["response"] = f"Unknown message type: {message['code']}"
    message["error"] = True
    return message
```

---

These snippets define the specific handlers called by `handleQuery` in `queryHandler.py` for different query types, including error handling for missing fields and unknown message types. They provide the detailed logic for processing the queries referenced in the main file.

</details>

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=%40coderabbitai%20spotted%20a%20missing%20websocket%20send%20for%20unknown%20messages%E2%80%94fixing%20this%20ensures%20consistent%20real-time%20responses!%20%F0%9F%9A%80%20Try%20%40coderabbitai%20to%20catch%20subtle%20async%20bugs%20before%20they%20hit%20prod!%20%F0%9F%90%87&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054554156)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-22T20:58:31Z
author_association: NONE
diff_hunk: @@ -0,0 +1,127 @@
+import sqlite3
+import os
+
+class SystemDatabaseReader:
+    '''
+        This class will perform queries on the ASP database and return
+        the results.
+    '''
+
+    def __init__(self):
+        self.openConnections()
+
+    def openConnections(self):
+        '''
+            Initialize the database connections.
+        '''
+        path = os.path.dirname(os.path.dirname(__file__))
+        aspPath = os.path.join(path, "asp.db")
+        try:
+            self.aspConn = sqlite3.connect(aspPath)
+            self.aspCursor = self.aspConn.cursor()
+        except sqlite3.Error as e:
+            print(f"Database error: {e}")
+            raise
+
+    def closeConnections(self):
+        '''
+            Close the database connections.
+        '''
+        if hasattr(self, 'sysIoConn'):
+            self.sysIoConn.close()
+        if hasattr(self, 'aspConn'):
+            self.aspConn.close()
path: components/query_handler/SystemDatabaseReader.py
user: login: vishalpalaniappan
body: Addressed in commit https://github.com/vishalpalaniappan/asp-query-server/pull/26/commits/890bea96855ee9d8935af6db7b83c3ba45c7287e.
created_at: 2025-04-22T20:59:54Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,127 @@
+import sqlite3
+import os
+
+class SystemDatabaseReader:
+    '''
+        This class will perform queries on the ASP database and return
+        the results.
+    '''
+
+    def __init__(self):
+        self.openConnections()
+
+    def openConnections(self):
+        '''
+            Initialize the database connections.
+        '''
+        path = os.path.dirname(os.path.dirname(__file__))
+        aspPath = os.path.join(path, "asp.db")
+        try:
+            self.aspConn = sqlite3.connect(aspPath)
+            self.aspCursor = self.aspConn.cursor()
+        except sqlite3.Error as e:
+            print(f"Database error: {e}")
+            raise
+
+    def closeConnections(self):
+        '''
+            Close the database connections.
+        '''
+        if hasattr(self, 'sysIoConn'):
+            self.sysIoConn.close()
+        if hasattr(self, 'aspConn'):
+            self.aspConn.close()
path: components/query_handler/SystemDatabaseReader.py
user: login: coderabbitai[bot]
body: @vishalpalaniappan Thank you for addressing the issue! I've confirmed that the unused database connection check has been removed. Great job on the quick fix.

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Catch%20unused%20or%20uninitialized%20attributes%20early!%20%40coderabbitai%20spotted%20a%20redundant%20DB%20connection%20check%20and%20suggested%20a%20precise%20fix%20to%20prevent%20runtime%20errors.%20Try%20it%20for%20smarter%20code%20reviews!%20%F0%9F%90%87%F0%9F%94%8D&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054554137)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@vishalpalaniappan Thank you for addressing the issue! I've confirmed that the unused database connection check has been removed. Great job on the quick fix.

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Catch%20unused%20or%20uninitialized%20attributes%20early!%20%40coderabbitai%20spotted%20a%20redundant%20DB%20connection%20check%20and%20suggested%20a%20precise%20fix%20to%20prevent%20runtime%20errors.%20Try%20it%20for%20smarter%20code%20reviews!%20%F0%9F%90%87%F0%9F%94%8D&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054554137)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-22T21:00:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,137 @@
+import sqlite3
+import os
+
+class SystemDatabaseReader:
+    '''
+        This class will perform queries on the ASP database and return
+        the results.
+    '''
+
+    def __init__(self):
+        self.openConnections()
+
+    def openConnections(self):
+        '''
+            Initialize the database connections.
+        '''
+        path = os.path.dirname(os.path.dirname(__file__))
+        aspPath = os.path.join(path, "asp.db")
+        try:
+            self.aspConn = sqlite3.connect(aspPath)
+            self.aspCursor = self.aspConn.cursor()
+        except sqlite3.Error as e:
+            print(f"Database error: {e}")
+            raise
+
+    def closeConnections(self):
+        '''
+            Close the database connections.
+        '''
+        if hasattr(self, 'aspConn'):
+            self.aspConn.close()
+
+    def __del__(self):
+        '''
+            Clean up database connections.
+        '''
+        self.closeConnections()
+
+    def checkIfTableExists(self, tableName):
+        '''
+            Check if the given table exists.
+        '''
+        query = f'PRAGMA table_info("{tableName}")' 
+        self.aspCursor.execute(query)
+        return self.aspCursor.fetchone()
+
+    def getColumns(self, tableName):
+        '''
+            Get the columns for a table.
+        '''
+        query = f'PRAGMA table_info("{tableName}")' 
+        self.aspCursor.execute(query)
+        return self.aspCursor.fetchall()
+    
+    def addColumnNameToData(self, columns, data):
+        '''
+            Given the column names and the data, create 
+            an object which uses the column names as key.
+        '''
+        obj = {}
+        for index, columnValue in enumerate(data):
+            obj[columns[index][1]] = columnValue
+        return obj
+    
+    def getAllEntriesInTable(self, tableName):
+        '''
+            Given a table name, get all entries in the table.
+        '''
+        query = f'SELECT * FROM "{tableName}"'
+        self.aspCursor.execute(query)
+        rows = self.aspCursor.fetchall() 
+        columns = self.getColumns(tableName)
+
+        results = []
+        for row in rows:
+            results.append(self.addColumnNameToData(columns, row))
+
+        return results
+    
+    def getSystems(self):
+        '''
+            Get all systems in the database.
+        '''
+        tableName = "SYSTEMTABLES"
+        tableExists = self.checkIfTableExists(tableName=tableName)
+        
+        if (tableExists is None):
+            return None
+
+        return self.getAllEntriesInTable("SYSTEMTABLES")
+
+    def getPrograms(self, systemId, systemVersion):     
+        '''
+            Get all programs given a system id and system version.
+        '''   
+        tableName = f"{systemId}_{systemVersion}_programs"
+        tableExists = self.checkIfTableExists(tableName=tableName)
path: components/query_handler/SystemDatabaseReader.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Validate table names before constructing dynamic table identifiers**

The table names are constructed using string interpolation with user-provided values (`systemId`, `systemVersion`), which could lead to SQL injection if these values contain unexpected characters.

Add validation for these parameters before using them to construct table names:

```diff
    def getPrograms(self, systemId, systemVersion):     
        '''
            Get all programs given a system id and system version.
        '''   
+       # Validate parameters
+       if not isinstance(systemId, (int, str)) or not isinstance(systemVersion, str):
+           return None
+       
+       # Sanitize identifiers
+       sanitized_id = str(systemId).strip()
+       sanitized_ver = systemVersion.strip()
+       if not sanitized_id or not sanitized_ver:
+           return None
+           
-       tableName = f"{systemId}_{systemVersion}_programs"
+       tableName = f"{sanitized_id}_{sanitized_ver}_programs"
        tableExists = self.checkIfTableExists(tableName=tableName)
```


Also applies to: 108-109, 121-122

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-22T21:22:51Z
author_association: NONE
diff_hunk: @@ -3,7 +3,7 @@
 import asyncio
 import argparse
 from websockets.asyncio.server import serve
-from queryHandler import handle_query
+from queryHandler import receieveMessage
path: components/query_handler/server.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix typo in function name "receieveMessage"**

There's a spelling error in the function name - "receieveMessage" should be "receiveMessage". This is consistent with the same spelling error in queryHandler.py.

Correct the spelling in both files:

```diff
-from queryHandler import receieveMessage
+from queryHandler import receiveMessage
```

```diff
-async with serve(receieveMessage, args.host, args.port) as server:
+async with serve(receiveMessage, args.host, args.port) as server:
```


Also applies to: 27-27

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits a950cc1 to 0b8af02
created_at: 2025-04-22T21:22:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,112 @@
+## Queries
+
+The following are a list of queries that the query server accepts.
+
+### 1. Get Systems
+This query returns all the systems in the database. 
+
+#### Query
+```
+{
+    "query":  "GET_SYSTEMS"
+}
+```
+#### Response
+The response to this query will be structured as follows:
+```
+{
+    "queryType": "GET_SYSTEMS",
+    "data": {
+        "systemId": 1234,
+        "systemVersion": "0.0.2"
+    },
+    "response":  <list of systems>
+}
+```
path: components/query_handler/README.md
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Correct the GET_SYSTEMS response example**

The response example for GET_SYSTEMS shows data containing systemId and systemVersion, but this doesn't align with the expected format where the response should return a list of systems.

Update the example to show a more accurate representation:

```diff
 {
     "queryType": "GET_SYSTEMS",
-    "data": {
-        "systemId": 1234,
-        "systemVersion": "0.0.2"
-    },
-    "response":  <list of systems>
+    "data": {},
+    "response": [
+        {
+            "systemId": 1234,
+            "systemVersion": "0.0.2"
+        },
+        {
+            "systemId": 5678,
+            "systemVersion": "1.0.0"
+        }
+    ],
+    "error": false
 }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
{
    "queryType": "GET_SYSTEMS",
    "data": {},
    "response": [
        {
            "systemId": 1234,
            "systemVersion": "0.0.2"
        },
        {
            "systemId": 5678,
            "systemVersion": "1.0.0"
        }
    ],
    "error": false
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-22T21:22:52Z
author_association: NONE
diff_hunk: @@ -1,35 +1,48 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from queryFunctions import *
 
-async def handle_query(websocket):
+def handleQuery (message):
+    '''
+        Handle the query.
+    '''
+    if "queryType" not in message:
+        response = handleUnknownMessage(message= message)
+        return response
+
+    print(f"Received message: {message['queryType']}")    
+    
+    if (message["queryType"] == "GET_SYSTEMS"):            
+        response = handleGetSystems(message= message)
+        
+    elif (message["queryType"] == "GET_PROGRAMS"):            
+        response = handleGetPrograms(message= message)
+    
+    elif (message["queryType"] == "GET_DEPLOYMENTS"):
+        response = handleGetDeployments(message= message)
+    
+    elif (message["queryType"] == "GET_TRACES"):
+        response = handleGetTraces(message= message)
+    
+    else:
+        response = handleUnknownCode(message= message)
+
+    return response
+
+async def receieveMessage(websocket):
     '''
         Handles messages from websocket and echos a response.
     '''
-    global processor
-
     async for message in websocket:
-        print(f"Received message: {message}")
-
-        message = json.loads(message)
-
-        if "code" not in message:
-            message["response"] = "Message does not contain a code"
-            message["error"] = True
-            await websocket.send(json.dumps(message))
-
-        elif (message["code"] == MSG_TYPES["GET_UNIQUE_TRACES"]):
-            message["error"] = False
-            await websocket.send(json.dumps(message))
-
-        elif (message["code"] == MSG_TYPES["GET_FILE_TREES"]):
-            message["error"] = False
-            await websocket.send(json.dumps(message))
-
-        else:
-            message["response"] = f"Unknown message type: {message['code']}"
-            message["error"] = True
-            await websocket.send(json.dumps(message))
-
+        try:
+            message = json.loads(message)   
+            response = handleQuery(message=message)   
+            await websocket.send(json.dumps(response))  
+        except json.JSONDecodeError as e:
+            response = {} 
+            response["response"] = "The query does not contain a valid JSON string."
+            response["error"] = False
+            await websocket.send(json.dumps(response))
path: components/query_handler/queryHandler.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Incorrect error flag for JSON decode error**

When a JSON decode error occurs, the `error` flag is set to `False`, which is inconsistent with error handling elsewhere in the code.

For error conditions, this flag should be `True`:

```diff
        except json.JSONDecodeError as e:
            response = {} 
            response["response"] = "The query does not contain a valid JSON string."
-           response["error"] = False
+           response["error"] = True
            await websocket.send(json.dumps(response))
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        except json.JSONDecodeError as e:
            response = {} 
            response["response"] = "The query does not contain a valid JSON string."
            response["error"] = True
            await websocket.send(json.dumps(response))
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

41-41: Local variable `e` is assigned to but never used

Remove assignment to unused variable `e`

(F841)

</details>

</details>

<!-- This is an auto-generated reply by CodeRabbit -->

✅ Addressed in commits a950cc1 to ab01cf8
created_at: 2025-04-22T21:22:52Z
author_association: NONE
diff_hunk: @@ -1,35 +1,48 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from queryFunctions import *
 
-async def handle_query(websocket):
+def handleQuery (message):
+    '''
+        Handle the query.
+    '''
+    if "queryType" not in message:
+        response = handleUnknownMessage(message= message)
+        return response
+
+    print(f"Received message: {message['queryType']}")    
+    
+    if (message["queryType"] == "GET_SYSTEMS"):            
+        response = handleGetSystems(message= message)
+        
+    elif (message["queryType"] == "GET_PROGRAMS"):            
+        response = handleGetPrograms(message= message)
+    
+    elif (message["queryType"] == "GET_DEPLOYMENTS"):
+        response = handleGetDeployments(message= message)
+    
+    elif (message["queryType"] == "GET_TRACES"):
+        response = handleGetTraces(message= message)
+    
+    else:
+        response = handleUnknownCode(message= message)
+
+    return response
+
+async def receieveMessage(websocket):
path: components/query_handler/queryHandler.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix typo in function name "receieveMessage"**

There's a spelling error in the function name - "receieveMessage" should be "receiveMessage".

```diff
-async def receieveMessage(websocket):
+async def receiveMessage(websocket):
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
-async def receieveMessage(websocket):
+async def receiveMessage(websocket):
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->

✅ Addressed in commits a950cc1 to 0b8af02
created_at: 2025-04-22T21:22:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,133 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    response = reader.getSystems()
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting all systems."
+    else:
+        message["error"] = False
+        message["response"] = response
+        return message
+
path: components/query_handler/queryFunctions.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix missing return statement in error case**

The function conditionally returns in the success case but has no explicit return in the error case. This can lead to inconsistent behavior.


```diff
 def handleGetSystems(message):
     '''
         Get all systems.
     '''
     response = reader.getSystems()

     if (response is None):
         message["error"] = True
         message["response"] = "An error occured while getting all systems."
+        return message
     else:
         message["error"] = False
         message["response"] = response
         return message
```

Also, there's a typo in the error message: "occured" should be "occurred".

```diff
-        message["response"] = "An error occured while getting all systems."
+        message["response"] = "An error occurred while getting all systems."
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
def handleGetSystems(message):
    '''
        Get all systems.
    '''
    response = reader.getSystems()

    if (response is None):
        message["error"] = True
        message["response"] = "An error occurred while getting all systems."
        return message
    else:
        message["error"] = False
        message["response"] = response
        return message
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->

✅ Addressed in commit 42b0430
created_at: 2025-04-22T21:22:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,133 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    response = reader.getSystems()
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting all systems."
+    else:
+        message["error"] = False
+        message["response"] = response
+        return message
+
+def handleGetPrograms(message):
+    '''
+        Get all programs given a system version and id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getPrograms(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting programs."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetDeployments(message):
+    '''
+        Get all deployments given a system id and version.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getDeployments(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting deployments."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetTraces(message):
+    '''
+        Get all traces given a system id, version and deployment id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    if ("deploymentId" not in data):
+        message["response"] = "Request does not contain a deployment id."
+        message["error"] = True
+        return message
+    
+    response = reader.getTraces(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"],
+        deploymentId= message["data"]["deploymentId"],
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting traces."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleUnknownCode(message):
+    '''
+        Handle an unkown code.
+    '''
+    message["response"] = f"Unknown message type: {message['code']}"
+    message["error"] = True
+    return message
path: components/query_handler/queryFunctions.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Consider adding error handling for missing data in message**

The functions assume that message has a 'data' key, but there's no validation for this. This could lead to KeyError if a message without 'data' is processed.


Add defensive coding by using `message.get("data", {})` instead of `message["data"]` in all handler functions, or add explicit validation for the presence of the 'data' key.

```diff
 def handleGetPrograms(message):
     '''
         Get all programs given a system version and id.
     '''
-    data = message["data"]
+    data = message.get("data", {})
```

Apply similar changes to other handler functions.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
def handleGetPrograms(message):
    '''
        Get all programs given a system version and id.
    '''
-    data = message["data"]
+    data = message.get("data", {})

    if ("systemId" not in data):
        message["response"] = "Request does not contain a system id."
        message["error"] = True
        return message

    if ("systemVersion" not in data):
        message["response"] = "Request does not contain a system version."
        message["error"] = True
        return message

    response = reader.getPrograms(
        systemId= message["data"]["systemId"],
        systemVersion= message["data"]["systemVersion"]
    )

    if (response is None):
        message["error"] = True
        message["response"] = "An error occured while getting programs."
    else:
        message["error"] = False
        message["response"] = response

    return message
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->

✅ Addressed in commit a950cc1

✅ Addressed in commits a950cc1 to 0b8af02

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-22T21:22:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,133 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    response = reader.getSystems()
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting all systems."
+    else:
+        message["error"] = False
+        message["response"] = response
+        return message
+
path: components/query_handler/queryFunctions.py
user: login: vishalpalaniappan
body: Fixed in commit https://github.com/vishalpalaniappan/asp-query-server/pull/26/commits/fa38cdbc1d765340c4599bd680a71bb78783212f. 

The typo was fixed in commit https://github.com/vishalpalaniappan/asp-query-server/pull/26/commits/42b0430cb2224fd2fdf6b2bc776cea4ac0b2c2b4.
created_at: 2025-04-22T21:30:49Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,133 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    response = reader.getSystems()
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting all systems."
+    else:
+        message["error"] = False
+        message["response"] = response
+        return message
+
path: components/query_handler/queryFunctions.py
user: login: coderabbitai[bot]
body: @vishalpalaniappan Thank you for confirming the fixes! I can see you've addressed both the missing return statement and the typo in separate commits. The code should now function correctly in both success and error cases. Great work!

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=%40coderabbitai%20caught%20a%20missing%20return%20in%20error%20handling%20plus%20a%20typo%20fix%E2%80%94ensuring%20consistent%20function%20behavior%20and%20clearer%20messages!%20Boost%20your%20code%20quality%20with%20%40coderabbitai%20%F0%9F%9A%80%F0%9F%90%87&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054904716)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@vishalpalaniappan Thank you for confirming the fixes! I can see you've addressed both the missing return statement and the typo in separate commits. The code should now function correctly in both success and error cases. Great work!

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=%40coderabbitai%20caught%20a%20missing%20return%20in%20error%20handling%20plus%20a%20typo%20fix%E2%80%94ensuring%20consistent%20function%20behavior%20and%20clearer%20messages!%20Boost%20your%20code%20quality%20with%20%40coderabbitai%20%F0%9F%9A%80%F0%9F%90%87&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054904716)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-22T21:31:09Z
author_association: NONE
diff_hunk: @@ -0,0 +1,134 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    response = reader.getSystems()
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting all systems."
+    else:
+        message["error"] = False
+        message["response"] = response
+        
+    return message
+
+def handleGetPrograms(message):
+    '''
+        Get all programs given a system version and id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getPrograms(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting programs."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetDeployments(message):
+    '''
+        Get all deployments given a system id and version.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getDeployments(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting deployments."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetTraces(message):
+    '''
+        Get all traces given a system id, version and deployment id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    if ("deploymentId" not in data):
+        message["response"] = "Request does not contain a deployment id."
+        message["error"] = True
+        return message
+    
+    response = reader.getTraces(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"],
+        deploymentId= message["data"]["deploymentId"],
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting traces."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleUnknownCode(message):
+    '''
+        Handle an unkown code.
+    '''
+    message["response"] = f"Unknown message type: {message['code']}"
+    message["error"] = True
+    return message
path: components/query_handler/queryFunctions.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add defensive coding for missing code key**

The function directly accesses `message['code']` without checking if the key exists, which could lead to a KeyError.

```diff
 def handleUnknownCode(message):
     '''
         Handle an unkown code.
     '''
-    message["response"] = f"Unknown message type: {message['code']}"
+    code = message.get('code', 'MISSING_CODE')
+    message["response"] = f"Unknown message type: {code}"
     message["error"] = True
     return message
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
def handleUnknownCode(message):
    '''
        Handle an unkown code.
    '''
    code = message.get('code', 'MISSING_CODE')
    message["response"] = f"Unknown message type: {code}"
    message["error"] = True
    return message
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-22T21:32:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,134 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    response = reader.getSystems()
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting all systems."
+    else:
+        message["error"] = False
+        message["response"] = response
+        
+    return message
+
+def handleGetPrograms(message):
+    '''
+        Get all programs given a system version and id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getPrograms(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting programs."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetDeployments(message):
+    '''
+        Get all deployments given a system id and version.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getDeployments(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting deployments."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetTraces(message):
+    '''
+        Get all traces given a system id, version and deployment id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    if ("deploymentId" not in data):
+        message["response"] = "Request does not contain a deployment id."
+        message["error"] = True
+        return message
+    
+    response = reader.getTraces(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"],
+        deploymentId= message["data"]["deploymentId"],
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting traces."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleUnknownCode(message):
+    '''
+        Handle an unkown code.
+    '''
+    message["response"] = f"Unknown message type: {message['code']}"
+    message["error"] = True
+    return message
path: components/query_handler/queryFunctions.py
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Add comprehensive error handling for database exceptions**

The current implementation doesn't handle database exceptions that might be raised by the `SystemDatabaseReader` methods. If a database operation fails (e.g., due to connection issues), it would crash the server.

Add try-except blocks around database operations:

```diff
 def handleGetSystems(message):
     '''
         Get all systems.
     '''
-    response = reader.getSystems()
-
-    if (response is None):
-        message["error"] = True
-        message["response"] = "An error occurred while getting all systems."
-    else:
-        message["error"] = False
-        message["response"] = response
+    try:
+        response = reader.getSystems()
+        if (response is None):
+            message["error"] = True
+            message["response"] = "An error occurred while getting all systems."
+        else:
+            message["error"] = False
+            message["response"] = response
+    except Exception as e:
+        message["error"] = True
+        message["response"] = f"Database error: {str(e)}"
 
     return message
```

Apply similar changes to the other handler functions.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if SystemDatabaseReader.py has exception handling that might make this unnecessary
grep -rn "try:" --include="*.py" . | grep -v "queryFunctions.py"
```

Length of output: 426

---

🏁 Script executed:

```shell
#!/bin/bash
# Print the first 200 lines of SystemDatabaseReader.py to review existing exception handling
sed -n '1,200p' components/query_handler/SystemDatabaseReader.py
```

Length of output: 4183

---

🏁 Script executed:

```shell
#!/bin/bash
# Display the beginning of queryHandler.py to see how handler exceptions are managed
sed -n '1,200p' components/query_handler/queryHandler.py
```

Length of output: 1526

---



</details>

**Ensure database errors in query handlers are caught to prevent server crashes**

The `SystemDatabaseReader` only wraps its connection logic in `try/except`, so any SQLite errors during queries (e.g. invalid table, I/O issues) will bubble up through your handlers and crash the WebSocket loop. Wrap each `reader.get*` call in `queryFunctions.py` with a broad `try/except` to safely return an error response.

• File: `components/query_handler/queryFunctions.py`  
• Handlers to update: `handleGetSystems`, `handleGetPrograms`, `handleGetDeployments`, `handleGetTraces`

Example diff for `handleGetSystems`:

```diff
 def handleGetSystems(message):
     '''
         Get all systems.
     '''
-    response = reader.getSystems()
-
-    if (response is None):
-        message["error"] = True
-        message["response"] = "An error occurred while getting all systems."
-    else:
-        message["error"] = False
-        message["response"] = response
+    try:
+        response = reader.getSystems()
+        if response is None:
+            message["error"] = True
+            message["response"] = "An error occurred while getting all systems."
+        else:
+            message["error"] = False
+            message["response"] = response
+    except Exception as e:
+        message["error"] = True
+        message["response"] = f"Database error: {e}"
 
     return message
```

Apply the same pattern to the other handlers so any `sqlite3.Error` (or unexpected exception) returns a well-formed error message instead of crashing the server.  


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
def handleGetSystems(message):
    '''
        Get all systems.
    '''
    try:
        response = reader.getSystems()
        if response is None:
            message["error"] = True
            message["response"] = "An error occurred while getting all systems."
        else:
            message["error"] = False
            message["response"] = response
    except Exception as e:
        message["error"] = True
        message["response"] = f"Database error: {e}"

    return message
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

1-1: `json` imported but unused

Remove unused import: `json`

(F401)

</details>

</details>

<!-- This is an auto-generated reply by CodeRabbit -->

✅ Addressed in commits a950cc1 to 71de696
created_at: 2025-04-22T21:32:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,133 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    response = reader.getSystems()
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting all systems."
+    else:
+        message["error"] = False
+        message["response"] = response
+        return message
+
+def handleGetPrograms(message):
+    '''
+        Get all programs given a system version and id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getPrograms(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting programs."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetDeployments(message):
+    '''
+        Get all deployments given a system id and version.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getDeployments(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting deployments."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetTraces(message):
+    '''
+        Get all traces given a system id, version and deployment id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    if ("deploymentId" not in data):
+        message["response"] = "Request does not contain a deployment id."
+        message["error"] = True
+        return message
+    
+    response = reader.getTraces(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"],
+        deploymentId= message["data"]["deploymentId"],
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting traces."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleUnknownCode(message):
+    '''
+        Handle an unkown code.
+    '''
+    message["response"] = f"Unknown message type: {message['code']}"
+    message["error"] = True
+    return message
path: components/query_handler/queryFunctions.py
user: login: vishalpalaniappan
body: If the data key doesn't exist, then we should return an error because it is an invalid query. 

I have addressed it in commit https://github.com/vishalpalaniappan/asp-query-server/pull/26/commits/a950cc1929a683f26b05deeec60b450022c6a27c.
created_at: 2025-04-22T21:34:42Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,133 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    response = reader.getSystems()
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting all systems."
+    else:
+        message["error"] = False
+        message["response"] = response
+        return message
+
+def handleGetPrograms(message):
+    '''
+        Get all programs given a system version and id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getPrograms(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting programs."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetDeployments(message):
+    '''
+        Get all deployments given a system id and version.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getDeployments(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting deployments."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetTraces(message):
+    '''
+        Get all traces given a system id, version and deployment id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    if ("deploymentId" not in data):
+        message["response"] = "Request does not contain a deployment id."
+        message["error"] = True
+        return message
+    
+    response = reader.getTraces(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"],
+        deploymentId= message["data"]["deploymentId"],
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occured while getting traces."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleUnknownCode(message):
+    '''
+        Handle an unkown code.
+    '''
+    message["response"] = f"Unknown message type: {message['code']}"
+    message["error"] = True
+    return message
path: components/query_handler/queryFunctions.py
user: login: coderabbitai[bot]
body: @vishalpalaniappan I agree with your approach! Explicit validation with an error message for missing "data" keys is more appropriate here than using defaults. It provides clearer feedback to clients about what went wrong with their request, which aligns well with the validation pattern used throughout your code.

Your implementation looks good. Thanks for addressing this issue!



ʕ •ᴥ•ʔ

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: vishalpalaniappan
PR: vishalpalaniappan/asp-query-server#26
File: components/query_handler/queryFunctions.py:0-0
Timestamp: 2025-04-22T21:35:07.018Z
Learning: In the ASP query server, query handler functions should explicitly validate the presence of required message fields (like "data") and return appropriate error messages rather than using default values when fields are missing.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@vishalpalaniappan I agree with your approach! Explicit validation with an error message for missing "data" keys is more appropriate here than using defaults. It provides clearer feedback to clients about what went wrong with their request, which aligns well with the validation pattern used throughout your code.

Your implementation looks good. Thanks for addressing this issue!



ʕ •ᴥ•ʔ

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: vishalpalaniappan
PR: vishalpalaniappan/asp-query-server#26
File: components/query_handler/queryFunctions.py:0-0
Timestamp: 2025-04-22T21:35:07.018Z
Learning: In the ASP query server, query handler functions should explicitly validate the presence of required message fields (like "data") and return appropriate error messages rather than using default values when fields are missing.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-22T21:35:08Z
author_association: NONE
diff_hunk: @@ -1,35 +1,48 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from queryFunctions import *
 
-async def handle_query(websocket):
+def handleQuery (message):
+    '''
+        Handle the query.
+    '''
+    if "queryType" not in message:
+        response = handleUnknownMessage(message= message)
+        return response
+
+    print(f"Received message: {message['queryType']}")    
+    
+    if (message["queryType"] == "GET_SYSTEMS"):            
+        response = handleGetSystems(message= message)
+        
+    elif (message["queryType"] == "GET_PROGRAMS"):            
+        response = handleGetPrograms(message= message)
+    
+    elif (message["queryType"] == "GET_DEPLOYMENTS"):
+        response = handleGetDeployments(message= message)
+    
+    elif (message["queryType"] == "GET_TRACES"):
+        response = handleGetTraces(message= message)
+    
+    else:
+        response = handleUnknownCode(message= message)
+
+    return response
+
+async def receieveMessage(websocket):
path: components/query_handler/queryHandler.py
user: login: vishalpalaniappan
body: Fixed in commit https://github.com/vishalpalaniappan/asp-query-server/pull/26/commits/0b8af02f7f03bde9d11927590361820b49383b34.
created_at: 2025-04-22T21:37:00Z
author_association: OWNER
diff_hunk: @@ -1,35 +1,48 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from queryFunctions import *
 
-async def handle_query(websocket):
+def handleQuery (message):
+    '''
+        Handle the query.
+    '''
+    if "queryType" not in message:
+        response = handleUnknownMessage(message= message)
+        return response
+
+    print(f"Received message: {message['queryType']}")    
+    
+    if (message["queryType"] == "GET_SYSTEMS"):            
+        response = handleGetSystems(message= message)
+        
+    elif (message["queryType"] == "GET_PROGRAMS"):            
+        response = handleGetPrograms(message= message)
+    
+    elif (message["queryType"] == "GET_DEPLOYMENTS"):
+        response = handleGetDeployments(message= message)
+    
+    elif (message["queryType"] == "GET_TRACES"):
+        response = handleGetTraces(message= message)
+    
+    else:
+        response = handleUnknownCode(message= message)
+
+    return response
+
+async def receieveMessage(websocket):
path: components/query_handler/queryHandler.py
user: login: coderabbitai[bot]
body: @vishalpalaniappan Thanks for confirming the fix! The spelling correction to "receiveMessage" will ensure consistent and clear naming throughout the codebase.



(＾◡＾)♡

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Catch%20typos%20early!%20%40coderabbitai%20spotted%20a%20misspelled%20function%20name%20%22receieveMessage%22%20and%20suggested%20the%20fix%20to%20%22receiveMessage%22%E2%80%94boost%20your%20code%20quality%20and%20avoid%20bugs%20effortlessly!%20%F0%9F%90%87%E2%9C%A8&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054904710)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@vishalpalaniappan Thanks for confirming the fix! The spelling correction to "receiveMessage" will ensure consistent and clear naming throughout the codebase.



(＾◡＾)♡

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Catch%20typos%20early!%20%40coderabbitai%20spotted%20a%20misspelled%20function%20name%20%22receieveMessage%22%20and%20suggested%20the%20fix%20to%20%22receiveMessage%22%E2%80%94boost%20your%20code%20quality%20and%20avoid%20bugs%20effortlessly!%20%F0%9F%90%87%E2%9C%A8&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054904710)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-22T21:37:19Z
author_association: NONE
diff_hunk: @@ -1,35 +1,48 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from queryFunctions import *
 
-async def handle_query(websocket):
+def handleQuery (message):
+    '''
+        Handle the query.
+    '''
+    if "queryType" not in message:
+        response = handleUnknownMessage(message= message)
+        return response
+
+    print(f"Received message: {message['queryType']}")    
+    
+    if (message["queryType"] == "GET_SYSTEMS"):            
+        response = handleGetSystems(message= message)
+        
+    elif (message["queryType"] == "GET_PROGRAMS"):            
+        response = handleGetPrograms(message= message)
+    
+    elif (message["queryType"] == "GET_DEPLOYMENTS"):
+        response = handleGetDeployments(message= message)
+    
+    elif (message["queryType"] == "GET_TRACES"):
+        response = handleGetTraces(message= message)
+    
+    else:
+        response = handleUnknownCode(message= message)
+
+    return response
+
+async def receieveMessage(websocket):
     '''
         Handles messages from websocket and echos a response.
     '''
-    global processor
-
     async for message in websocket:
-        print(f"Received message: {message}")
-
-        message = json.loads(message)
-
-        if "code" not in message:
-            message["response"] = "Message does not contain a code"
-            message["error"] = True
-            await websocket.send(json.dumps(message))
-
-        elif (message["code"] == MSG_TYPES["GET_UNIQUE_TRACES"]):
-            message["error"] = False
-            await websocket.send(json.dumps(message))
-
-        elif (message["code"] == MSG_TYPES["GET_FILE_TREES"]):
-            message["error"] = False
-            await websocket.send(json.dumps(message))
-
-        else:
-            message["response"] = f"Unknown message type: {message['code']}"
-            message["error"] = True
-            await websocket.send(json.dumps(message))
-
+        try:
+            message = json.loads(message)   
+            response = handleQuery(message=message)   
+            await websocket.send(json.dumps(response))  
+        except json.JSONDecodeError as e:
+            response = {} 
+            response["response"] = "The query does not contain a valid JSON string."
+            response["error"] = False
+            await websocket.send(json.dumps(response))
path: components/query_handler/queryHandler.py
user: login: vishalpalaniappan
body: Fixed in commit https://github.com/vishalpalaniappan/asp-query-server/pull/26/commits/ab01cf8213d540027d432ab33738bc4ce54cef5b.
created_at: 2025-04-22T21:37:49Z
author_association: OWNER
diff_hunk: @@ -1,35 +1,48 @@
 import json
 from MSG_TYPES import MSG_TYPES
+from queryFunctions import *
 
-async def handle_query(websocket):
+def handleQuery (message):
+    '''
+        Handle the query.
+    '''
+    if "queryType" not in message:
+        response = handleUnknownMessage(message= message)
+        return response
+
+    print(f"Received message: {message['queryType']}")    
+    
+    if (message["queryType"] == "GET_SYSTEMS"):            
+        response = handleGetSystems(message= message)
+        
+    elif (message["queryType"] == "GET_PROGRAMS"):            
+        response = handleGetPrograms(message= message)
+    
+    elif (message["queryType"] == "GET_DEPLOYMENTS"):
+        response = handleGetDeployments(message= message)
+    
+    elif (message["queryType"] == "GET_TRACES"):
+        response = handleGetTraces(message= message)
+    
+    else:
+        response = handleUnknownCode(message= message)
+
+    return response
+
+async def receieveMessage(websocket):
     '''
         Handles messages from websocket and echos a response.
     '''
-    global processor
-
     async for message in websocket:
-        print(f"Received message: {message}")
-
-        message = json.loads(message)
-
-        if "code" not in message:
-            message["response"] = "Message does not contain a code"
-            message["error"] = True
-            await websocket.send(json.dumps(message))
-
-        elif (message["code"] == MSG_TYPES["GET_UNIQUE_TRACES"]):
-            message["error"] = False
-            await websocket.send(json.dumps(message))
-
-        elif (message["code"] == MSG_TYPES["GET_FILE_TREES"]):
-            message["error"] = False
-            await websocket.send(json.dumps(message))
-
-        else:
-            message["response"] = f"Unknown message type: {message['code']}"
-            message["error"] = True
-            await websocket.send(json.dumps(message))
-
+        try:
+            message = json.loads(message)   
+            response = handleQuery(message=message)   
+            await websocket.send(json.dumps(response))  
+        except json.JSONDecodeError as e:
+            response = {} 
+            response["response"] = "The query does not contain a valid JSON string."
+            response["error"] = False
+            await websocket.send(json.dumps(response))
path: components/query_handler/queryHandler.py
user: login: coderabbitai[bot]
body: @vishalpalaniappan Thanks for fixing the error flag issue! The JSON error handling is now consistent with the rest of the code.

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Catch%20JSON%20decode%20errors%20correctly%20by%20setting%20the%20error%20flag%20to%20True%2C%20ensuring%20consistent%20error%20handling%20across%20your%20app!%20%F0%9F%90%87%F0%9F%92%A1%20Try%20%40coderabbitai%20for%20smarter%20code%20reviews!&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054904706)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@vishalpalaniappan Thanks for fixing the error flag issue! The JSON error handling is now consistent with the rest of the code.

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Catch%20JSON%20decode%20errors%20correctly%20by%20setting%20the%20error%20flag%20to%20True%2C%20ensuring%20consistent%20error%20handling%20across%20your%20app!%20%F0%9F%90%87%F0%9F%92%A1%20Try%20%40coderabbitai%20for%20smarter%20code%20reviews!&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054904706)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-22T21:38:15Z
author_association: NONE
diff_hunk: @@ -0,0 +1,137 @@
+import sqlite3
+import os
+
+class SystemDatabaseReader:
+    '''
+        This class will perform queries on the ASP database and return
+        the results.
+    '''
+
+    def __init__(self):
+        self.openConnections()
+
+    def openConnections(self):
+        '''
+            Initialize the database connections.
+        '''
+        path = os.path.dirname(os.path.dirname(__file__))
+        aspPath = os.path.join(path, "asp.db")
+        try:
+            self.aspConn = sqlite3.connect(aspPath)
+            self.aspCursor = self.aspConn.cursor()
+        except sqlite3.Error as e:
+            print(f"Database error: {e}")
+            raise
+
+    def closeConnections(self):
+        '''
+            Close the database connections.
+        '''
+        if hasattr(self, 'aspConn'):
+            self.aspConn.close()
+
+    def __del__(self):
+        '''
+            Clean up database connections.
+        '''
+        self.closeConnections()
+
+    def checkIfTableExists(self, tableName):
+        '''
+            Check if the given table exists.
+        '''
+        query = f'PRAGMA table_info("{tableName}")' 
+        self.aspCursor.execute(query)
+        return self.aspCursor.fetchone()
+
+    def getColumns(self, tableName):
+        '''
+            Get the columns for a table.
+        '''
+        query = f'PRAGMA table_info("{tableName}")' 
+        self.aspCursor.execute(query)
+        return self.aspCursor.fetchall()
+    
+    def addColumnNameToData(self, columns, data):
+        '''
+            Given the column names and the data, create 
+            an object which uses the column names as key.
+        '''
+        obj = {}
+        for index, columnValue in enumerate(data):
+            obj[columns[index][1]] = columnValue
+        return obj
+    
+    def getAllEntriesInTable(self, tableName):
+        '''
+            Given a table name, get all entries in the table.
+        '''
+        query = f'SELECT * FROM "{tableName}"'
+        self.aspCursor.execute(query)
+        rows = self.aspCursor.fetchall() 
+        columns = self.getColumns(tableName)
+
+        results = []
+        for row in rows:
+            results.append(self.addColumnNameToData(columns, row))
+
+        return results
+    
+    def getSystems(self):
+        '''
+            Get all systems in the database.
+        '''
+        tableName = "SYSTEMTABLES"
+        tableExists = self.checkIfTableExists(tableName=tableName)
+        
+        if (tableExists is None):
+            return None
+
+        return self.getAllEntriesInTable("SYSTEMTABLES")
+
+    def getPrograms(self, systemId, systemVersion):     
+        '''
+            Get all programs given a system id and system version.
+        '''   
+        tableName = f"{systemId}_{systemVersion}_programs"
+        tableExists = self.checkIfTableExists(tableName=tableName)
path: components/query_handler/SystemDatabaseReader.py
user: login: vishalpalaniappan
body: Can you add an issue to remind me to address this? Currently, since ASV will build the queries with information that was  loaded from SYSTEMTABLES, I didn't add any validation. 

However, I agree that I need to improve this to address SQL injection and I will do it in a coming PR.
created_at: 2025-04-22T21:42:35Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,137 @@
+import sqlite3
+import os
+
+class SystemDatabaseReader:
+    '''
+        This class will perform queries on the ASP database and return
+        the results.
+    '''
+
+    def __init__(self):
+        self.openConnections()
+
+    def openConnections(self):
+        '''
+            Initialize the database connections.
+        '''
+        path = os.path.dirname(os.path.dirname(__file__))
+        aspPath = os.path.join(path, "asp.db")
+        try:
+            self.aspConn = sqlite3.connect(aspPath)
+            self.aspCursor = self.aspConn.cursor()
+        except sqlite3.Error as e:
+            print(f"Database error: {e}")
+            raise
+
+    def closeConnections(self):
+        '''
+            Close the database connections.
+        '''
+        if hasattr(self, 'aspConn'):
+            self.aspConn.close()
+
+    def __del__(self):
+        '''
+            Clean up database connections.
+        '''
+        self.closeConnections()
+
+    def checkIfTableExists(self, tableName):
+        '''
+            Check if the given table exists.
+        '''
+        query = f'PRAGMA table_info("{tableName}")' 
+        self.aspCursor.execute(query)
+        return self.aspCursor.fetchone()
+
+    def getColumns(self, tableName):
+        '''
+            Get the columns for a table.
+        '''
+        query = f'PRAGMA table_info("{tableName}")' 
+        self.aspCursor.execute(query)
+        return self.aspCursor.fetchall()
+    
+    def addColumnNameToData(self, columns, data):
+        '''
+            Given the column names and the data, create 
+            an object which uses the column names as key.
+        '''
+        obj = {}
+        for index, columnValue in enumerate(data):
+            obj[columns[index][1]] = columnValue
+        return obj
+    
+    def getAllEntriesInTable(self, tableName):
+        '''
+            Given a table name, get all entries in the table.
+        '''
+        query = f'SELECT * FROM "{tableName}"'
+        self.aspCursor.execute(query)
+        rows = self.aspCursor.fetchall() 
+        columns = self.getColumns(tableName)
+
+        results = []
+        for row in rows:
+            results.append(self.addColumnNameToData(columns, row))
+
+        return results
+    
+    def getSystems(self):
+        '''
+            Get all systems in the database.
+        '''
+        tableName = "SYSTEMTABLES"
+        tableExists = self.checkIfTableExists(tableName=tableName)
+        
+        if (tableExists is None):
+            return None
+
+        return self.getAllEntriesInTable("SYSTEMTABLES")
+
+    def getPrograms(self, systemId, systemVersion):     
+        '''
+            Get all programs given a system id and system version.
+        '''   
+        tableName = f"{systemId}_{systemVersion}_programs"
+        tableExists = self.checkIfTableExists(tableName=tableName)
path: components/query_handler/SystemDatabaseReader.py
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

@vishalpalaniappan I've created an issue to remind you about adding validation for the table name parameters. I understand that ASV currently builds the queries with information from SYSTEMTABLES, but adding proper validation will help strengthen security in the future. Thanks for planning to address this in an upcoming PR!

---

🏁 Script executed:
```
#!/bin/bash
# Create GitHub issue for SQL injection vulnerability
gh issue create \
  --title "Validate user input before constructing dynamic table names" \
  --body "## Description

Add parameter validation for `systemId` and `systemVersion` before using them to construct dynamic table names in the SystemDatabaseReader class to prevent potential SQL injection attacks.

## Affected Components
- \`components/query_handler/SystemDatabaseReader.py\` in the following methods:
  - \`getPrograms()\`
  - \`getDeployments()\`
  - \`getTraces()\`

## Implementation Notes
- Validate parameter types and formats
- Sanitize inputs before using in SQL queries
- Consider using prepared statements or parameterized queries where possible

## Background
This issue was identified during the code review of PR #26 (https://github.com/vishalpalaniappan/asp-query-server/pull/26#discussion_r2054904698).

Currently, the code constructs table names using string interpolation with user-provided values without validation, as ASV builds queries with information loaded from SYSTEMTABLES. However, adding validation would improve security best practices." \
  --assignee "vishalpalaniappan"
```

Length of output: 1362

---



</details>

@vishalpalaniappan I've created an issue to remind you about adding validation for the table name parameters. The issue is now available at: https://github.com/vishalpalaniappan/asp-query-server/issues/27

There was a small formatting error in the description where the variable placeholders for `systemId` and `systemVersion` didn't render correctly, but the core content is there. You can update the description if needed.

I understand that ASV currently builds the queries with information from SYSTEMTABLES, and appreciate your plan to address this security improvement in a future PR.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: vishalpalaniappan
PR: vishalpalaniappan/asp-query-server#26
File: components/query_handler/SystemDatabaseReader.py:96-97
Timestamp: 2025-04-22T21:43:14.408Z
Learning: For the ASP query server project, ASV (a client application) builds queries using information loaded from SYSTEMTABLES, which provides some implicit validation of query parameters. However, explicit validation of user inputs like systemId and systemVersion before using them in dynamic SQL table names is planned for a future PR to follow security best practices.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer><details>
<summary>🧩 Analysis chain</summary>

@vishalpalaniappan I've created an issue to remind you about adding validation for the table name parameters. I understand that ASV currently builds the queries with information from SYSTEMTABLES, but adding proper validation will help strengthen security in the future. Thanks for planning to address this in an upcoming PR!

---

🏁 Script executed:
```
#!/bin/bash
# Create GitHub issue for SQL injection vulnerability
gh issue create \
  --title "Validate user input before constructing dynamic table names" \
  --body "## Description

Add parameter validation for `systemId` and `systemVersion` before using them to construct dynamic table names in the SystemDatabaseReader class to prevent potential SQL injection attacks.

## Affected Components
- \`components/query_handler/SystemDatabaseReader.py\` in the following methods:
  - \`getPrograms()\`
  - \`getDeployments()\`
  - \`getTraces()\`

## Implementation Notes
- Validate parameter types and formats
- Sanitize inputs before using in SQL queries
- Consider using prepared statements or parameterized queries where possible

## Background
This issue was identified during the code review of PR #26 (https://github.com/vishalpalaniappan/asp-query-server/pull/26#discussion_r2054904698).

Currently, the code constructs table names using string interpolation with user-provided values without validation, as ASV builds queries with information loaded from SYSTEMTABLES. However, adding validation would improve security best practices." \
  --assignee "vishalpalaniappan"
```

Length of output: 1362

---



</details>

@vishalpalaniappan I've created an issue to remind you about adding validation for the table name parameters. The issue is now available at: https://github.com/vishalpalaniappan/asp-query-server/issues/27

There was a small formatting error in the description where the variable placeholders for `systemId` and `systemVersion` didn't render correctly, but the core content is there. You can update the description if needed.

I understand that ASV currently builds the queries with information from SYSTEMTABLES, and appreciate your plan to address this security improvement in a future PR.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: vishalpalaniappan
PR: vishalpalaniappan/asp-query-server#26
File: components/query_handler/SystemDatabaseReader.py:96-97
Timestamp: 2025-04-22T21:43:14.408Z
Learning: For the ASP query server project, ASV (a client application) builds queries using information loaded from SYSTEMTABLES, which provides some implicit validation of query parameters. However, explicit validation of user inputs like systemId and systemVersion before using them in dynamic SQL table names is planned for a future PR to follow security best practices.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-22T21:43:15Z
author_association: NONE
diff_hunk: @@ -0,0 +1,134 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    response = reader.getSystems()
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting all systems."
+    else:
+        message["error"] = False
+        message["response"] = response
+        
+    return message
+
+def handleGetPrograms(message):
+    '''
+        Get all programs given a system version and id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getPrograms(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting programs."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetDeployments(message):
+    '''
+        Get all deployments given a system id and version.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getDeployments(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting deployments."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetTraces(message):
+    '''
+        Get all traces given a system id, version and deployment id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    if ("deploymentId" not in data):
+        message["response"] = "Request does not contain a deployment id."
+        message["error"] = True
+        return message
+    
+    response = reader.getTraces(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"],
+        deploymentId= message["data"]["deploymentId"],
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting traces."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleUnknownCode(message):
+    '''
+        Handle an unkown code.
+    '''
+    message["response"] = f"Unknown message type: {message['code']}"
+    message["error"] = True
+    return message
path: components/query_handler/queryFunctions.py
user: login: vishalpalaniappan
body: I've added the exception handling in commit https://github.com/vishalpalaniappan/asp-query-server/pull/26/commits/71de69624979e884ef73db3632cfdb748e9cd04a.
created_at: 2025-04-22T21:59:27Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,134 @@
+import json
+from SystemDatabaseReader import SystemDatabaseReader
+
+reader = SystemDatabaseReader()
+
+def handleUnknownMessage(message):
+    '''
+        Handle an unknown message.
+    '''
+    message["response"] = "Message does not contain a code"
+    message["error"] = True
+    return message
+
+def handleGetSystems(message):
+    '''
+        Get all systems.
+    '''
+    response = reader.getSystems()
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting all systems."
+    else:
+        message["error"] = False
+        message["response"] = response
+        
+    return message
+
+def handleGetPrograms(message):
+    '''
+        Get all programs given a system version and id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getPrograms(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting programs."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetDeployments(message):
+    '''
+        Get all deployments given a system id and version.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    response = reader.getDeployments(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"]
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting deployments."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleGetTraces(message):
+    '''
+        Get all traces given a system id, version and deployment id.
+    '''
+    data = message["data"]
+
+    if ("systemId" not in data):
+        message["response"] = "Request does not contain a system id."
+        message["error"] = True
+        return message
+
+    if ("systemVersion" not in data):
+        message["response"] = "Request does not contain a system version."
+        message["error"] = True
+        return message
+
+    if ("deploymentId" not in data):
+        message["response"] = "Request does not contain a deployment id."
+        message["error"] = True
+        return message
+    
+    response = reader.getTraces(
+        systemId= message["data"]["systemId"],
+        systemVersion= message["data"]["systemVersion"],
+        deploymentId= message["data"]["deploymentId"],
+    )
+
+    if (response is None):
+        message["error"] = True
+        message["response"] = "An error occurred while getting traces."
+    else:
+        message["error"] = False
+        message["response"] = response
+
+    return message
+
+
+def handleUnknownCode(message):
+    '''
+        Handle an unkown code.
+    '''
+    message["response"] = f"Unknown message type: {message['code']}"
+    message["error"] = True
+    return message
path: components/query_handler/queryFunctions.py
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

@vishalpalaniappan Great work on implementing the exception handling! I can confirm that you've correctly added try-except blocks to all the query handler functions, which will make your server more robust against unexpected database errors. This is an important improvement that will prevent the WebSocket server from crashing if database operations fail. The changes look good and address the feedback completely. Let's mark this as resolved.



---

🏁 Script executed:
```
#!/bin/bash
# Fetch the commit details to see the changes made
git fetch origin pull/26/head
git show 71de69624979e884ef73db3632cfdb748e9cd04a
```

Length of output: 4630

---



</details>

@vishalpalaniappan Great work on implementing the exception handling! I can confirm that you've correctly added try-except blocks to all the query handler functions, which will make your server more robust against unexpected database errors. This is an important improvement that will prevent the WebSocket server from crashing if database operations fail. The changes look good and address the feedback completely. Let's mark this as resolved.



---

<details>
<summary>🧬 Code Graph Analysis Results</summary>

### Relevant snippets from `components/query_handler/SystemDatabaseReader.py` (lines 4-137)

```python
class SystemDatabaseReader:
    '''
        This class will perform queries on the ASP database and return
        the results.
    '''

    def __init__(self):
        self.openConnections()

    def openConnections(self):
        '''
            Initialize the database connections.
        '''
        path = os.path.dirname(os.path.dirname(__file__))
        aspPath = os.path.join(path, "asp.db")
        try:
            self.aspConn = sqlite3.connect(aspPath)
            self.aspCursor = self.aspConn.cursor()
        except sqlite3.Error as e:
            print(f"Database error: {e}")
            raise

    def closeConnections(self):
        '''
            Close the database connections.
        '''
        if hasattr(self, 'aspConn'):
            self.aspConn.close()

    def __del__(self):
        '''
            Clean up database connections.
        '''
        self.closeConnections()

    def checkIfTableExists(self, tableName):
        '''
            Check if the given table exists.
        '''
        query = f'PRAGMA table_info("{tableName}")' 
        self.aspCursor.execute(query)
        return self.aspCursor.fetchone()

    def getColumns(self, tableName):
        '''
            Get the columns for a table.
        '''
        query = f'PRAGMA table_info("{tableName}")' 
        self.aspCursor.execute(query)
        return self.aspCursor.fetchall()
    
    def addColumnNameToData(self, columns, data):
        '''
            Given the column names and the data, create 
            an object which uses the column names as key.
        '''
        obj = {}
        for index, columnValue in enumerate(data):
            obj[columns[index][1]] = columnValue
        return obj
    
    def getAllEntriesInTable(self, tableName):
        '''
            Given a table name, get all entries in the table.
        '''
        query = f'SELECT * FROM "{tableName}"'
        self.aspCursor.execute(query)
        rows = self.aspCursor.fetchall() 
        columns = self.getColumns(tableName)

        results = []
        for row in rows:
            results.append(self.addColumnNameToData(columns, row))

        return results
    
    def getSystems(self):
        '''
            Get all systems in the database.
        '''
        tableName = "SYSTEMTABLES"
        tableExists = self.checkIfTableExists(tableName=tableName)
        
        if (tableExists is None):
            return None

        return self.getAllEntriesInTable("SYSTEMTABLES")

    def getPrograms(self, systemId, systemVersion):     
        '''
            Get all programs given a system id and system version.
        '''   
        tableName = f"{systemId}_{systemVersion}_programs"
        tableExists = self.checkIfTableExists(tableName=tableName)

        if (tableExists is None):
            return None
        
        return self.getAllEntriesInTable(tableName)

    def getDeployments(self, systemId, systemVersion):  
        '''
            Get all the deployments for the given system.
        '''            
        tableName = f"{systemId}_{systemVersion}_deployments"
        tableExists = self.checkIfTableExists(tableName=tableName)

        if (tableExists is None):
            return None

        return self.getAllEntriesInTable(tableName)

    def getTraces(self, systemId, systemVersion, deploymentId):
        '''
            Get all the traces given a system id, version and deployment id.            
            # TODO: Add funtionality to filter traces by start and end timestamp.       
        '''
        tableName = f"{systemId}_{systemVersion}_traces"
        doesTableExist = self.checkIfTableExists(tableName=tableName)

        if (doesTableExist is None):
            return None

        query = f'SELECT * FROM "{tableName}" WHERE deployment_id = ?'
        self.aspCursor.execute(query, [deploymentId])
        rows = self.aspCursor.fetchall() 

        columns = self.getColumns(tableName)

        results = []
        for row in rows:
            results.append(self.addColumnNameToData(columns, row))

        return results
```

---

This snippet provides the implementation of the `SystemDatabaseReader` class, which is used in `queryFunctions.py` to perform database queries. It includes methods for:

- Opening and closing SQLite database connections.
- Checking if tables exist.
- Retrieving columns and rows from tables.
- Fetching systems, programs, deployments, and traces from the database.
- Handling database errors by raising exceptions.

The exception handling in `queryFunctions.py` wraps calls to these methods, catching exceptions raised by this class.

</details>

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Wrap%20your%20DB%20calls%20in%20try%2Fexcept%20to%20catch%20SQLite%20errors%20and%20prevent%20server%20crashes!%20%40coderabbitai%E2%80%99s%20advice%20ensures%20robust%20error%20handling%20and%20smooth%20WebSocket%20operation.%20%F0%9F%9A%80%F0%9F%90%87%20Try%20it%20now!&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054917715)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer><details>
<summary>🧩 Analysis chain</summary>

@vishalpalaniappan Great work on implementing the exception handling! I can confirm that you've correctly added try-except blocks to all the query handler functions, which will make your server more robust against unexpected database errors. This is an important improvement that will prevent the WebSocket server from crashing if database operations fail. The changes look good and address the feedback completely. Let's mark this as resolved.



---

🏁 Script executed:
```
#!/bin/bash
# Fetch the commit details to see the changes made
git fetch origin pull/26/head
git show 71de69624979e884ef73db3632cfdb748e9cd04a
```

Length of output: 4630

---



</details>

@vishalpalaniappan Great work on implementing the exception handling! I can confirm that you've correctly added try-except blocks to all the query handler functions, which will make your server more robust against unexpected database errors. This is an important improvement that will prevent the WebSocket server from crashing if database operations fail. The changes look good and address the feedback completely. Let's mark this as resolved.



---

<details>
<summary>🧬 Code Graph Analysis Results</summary>

### Relevant snippets from `components/query_handler/SystemDatabaseReader.py` (lines 4-137)

```python
class SystemDatabaseReader:
    '''
        This class will perform queries on the ASP database and return
        the results.
    '''

    def __init__(self):
        self.openConnections()

    def openConnections(self):
        '''
            Initialize the database connections.
        '''
        path = os.path.dirname(os.path.dirname(__file__))
        aspPath = os.path.join(path, "asp.db")
        try:
            self.aspConn = sqlite3.connect(aspPath)
            self.aspCursor = self.aspConn.cursor()
        except sqlite3.Error as e:
            print(f"Database error: {e}")
            raise

    def closeConnections(self):
        '''
            Close the database connections.
        '''
        if hasattr(self, 'aspConn'):
            self.aspConn.close()

    def __del__(self):
        '''
            Clean up database connections.
        '''
        self.closeConnections()

    def checkIfTableExists(self, tableName):
        '''
            Check if the given table exists.
        '''
        query = f'PRAGMA table_info("{tableName}")' 
        self.aspCursor.execute(query)
        return self.aspCursor.fetchone()

    def getColumns(self, tableName):
        '''
            Get the columns for a table.
        '''
        query = f'PRAGMA table_info("{tableName}")' 
        self.aspCursor.execute(query)
        return self.aspCursor.fetchall()
    
    def addColumnNameToData(self, columns, data):
        '''
            Given the column names and the data, create 
            an object which uses the column names as key.
        '''
        obj = {}
        for index, columnValue in enumerate(data):
            obj[columns[index][1]] = columnValue
        return obj
    
    def getAllEntriesInTable(self, tableName):
        '''
            Given a table name, get all entries in the table.
        '''
        query = f'SELECT * FROM "{tableName}"'
        self.aspCursor.execute(query)
        rows = self.aspCursor.fetchall() 
        columns = self.getColumns(tableName)

        results = []
        for row in rows:
            results.append(self.addColumnNameToData(columns, row))

        return results
    
    def getSystems(self):
        '''
            Get all systems in the database.
        '''
        tableName = "SYSTEMTABLES"
        tableExists = self.checkIfTableExists(tableName=tableName)
        
        if (tableExists is None):
            return None

        return self.getAllEntriesInTable("SYSTEMTABLES")

    def getPrograms(self, systemId, systemVersion):     
        '''
            Get all programs given a system id and system version.
        '''   
        tableName = f"{systemId}_{systemVersion}_programs"
        tableExists = self.checkIfTableExists(tableName=tableName)

        if (tableExists is None):
            return None
        
        return self.getAllEntriesInTable(tableName)

    def getDeployments(self, systemId, systemVersion):  
        '''
            Get all the deployments for the given system.
        '''            
        tableName = f"{systemId}_{systemVersion}_deployments"
        tableExists = self.checkIfTableExists(tableName=tableName)

        if (tableExists is None):
            return None

        return self.getAllEntriesInTable(tableName)

    def getTraces(self, systemId, systemVersion, deploymentId):
        '''
            Get all the traces given a system id, version and deployment id.            
            # TODO: Add funtionality to filter traces by start and end timestamp.       
        '''
        tableName = f"{systemId}_{systemVersion}_traces"
        doesTableExist = self.checkIfTableExists(tableName=tableName)

        if (doesTableExist is None):
            return None

        query = f'SELECT * FROM "{tableName}" WHERE deployment_id = ?'
        self.aspCursor.execute(query, [deploymentId])
        rows = self.aspCursor.fetchall() 

        columns = self.getColumns(tableName)

        results = []
        for row in rows:
            results.append(self.addColumnNameToData(columns, row))

        return results
```

---

This snippet provides the implementation of the `SystemDatabaseReader` class, which is used in `queryFunctions.py` to perform database queries. It includes methods for:

- Opening and closing SQLite database connections.
- Checking if tables exist.
- Retrieving columns and rows from tables.
- Fetching systems, programs, deployments, and traces from the database.
- Handling database errors by raising exceptions.

The exception handling in `queryFunctions.py` wraps calls to these methods, catching exceptions raised by this class.

</details>

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Wrap%20your%20DB%20calls%20in%20try%2Fexcept%20to%20catch%20SQLite%20errors%20and%20prevent%20server%20crashes!%20%40coderabbitai%E2%80%99s%20advice%20ensures%20robust%20error%20handling%20and%20smooth%20WebSocket%20operation.%20%F0%9F%9A%80%F0%9F%90%87%20Try%20it%20now!&url=https%3A%2F%2Fgithub.com%2Fvishalpalaniappan%2Fasp-query-server%2Fpull%2F26%23discussion_r2054917715)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-22T22:00:32Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-21T20:46:34Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: rate limited by coderabbit.ai -->

> [!WARNING]
> ## Rate limit exceeded
> 
> @vishalpalaniappan has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait **0 minutes and 30 seconds** before requesting another review.
> 
> <details>
> <summary>⌛ How to resolve this issue?</summary>
> 
> After the wait time has elapsed, a review can be triggered using the `@coderabbitai review` command as a PR comment. Alternatively, push new commits to this PR.
> 
> We recommend that you space out your commits to avoid hitting the rate limit.
> 
> </details>
> 
> 
> <details>
> <summary>🚦 How do rate limits work?</summary>
> 
> CodeRabbit enforces hourly rate limits for each developer per organization.
> 
> Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.
> 
> Please see our [FAQ](https://docs.coderabbit.ai/faq) for further information.
> 
> </details>
> 
> <details>
> <summary>📥 Commits</summary>
> 
> Reviewing files that changed from the base of the PR and between 71de69624979e884ef73db3632cfdb748e9cd04a and 7e3b4ae0c567dda9d48a8dff033bff04e4e19fa4.
> 
> </details>
> 
> <details>
> <summary>📒 Files selected for processing (3)</summary>
> 
> * `components/query_handler/README.md` (1 hunks)
> * `components/query_handler/SystemDatabaseReader.py` (1 hunks)
> * `components/query_handler/queryFunctions.py` (1 hunks)
> 
> </details>

<!-- end of auto-generated comment: rate limited by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update introduces a new database reader class and modular query handler functions to enable structured querying of an ASP SQLite database for systems, programs, deployments, and traces. The message type dictionary is expanded and revised to support these new queries. The main query handling logic is refactored to delegate specific message processing to the new handler functions, improving clarity and maintainability. The changes collectively establish a foundation for robust, code-driven querying of the ASP database with clear message-type handling.

## Changes

| File(s)                                                          | Change Summary                                                                                                                      |
|------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
| components/query_handler/MSG_TYPES.py                            | Replaced old message type keys with new keys: GET_SYSTEMS, GET_PROGRAMS, GET_DEPLOYMENTS, GET_TRACES, each mapped to unique codes. |
| components/query_handler/SystemDatabaseReader.py                 | Added new class `SystemDatabaseReader` with methods to read systems, programs, deployments, and traces from the ASP SQLite database.|
| components/query_handler/queryFunctions.py                       | Introduced new handler functions for each message type, performing validation and invoking corresponding reader methods.            |
| components/query_handler/queryHandler.py                         | Refactored message handling logic to use new handler functions and improved JSON error handling; removed the global processor variable.         |
| components/query_handler/README.md                               | Added documentation detailing supported query types, expected message formats, responses, and error handling behavior.              |
| components/query_handler/server.py                               | Updated WebSocket server import and handler callback from `handle_query` to `receiveMessage`.                                        |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant QueryHandler
    participant SystemDatabaseReader

    Client->>QueryHandler: Sends JSON query message (with queryType)
    QueryHandler->>QueryHandler: Parse and validate message
    QueryHandler->>QueryHandler: Dispatch to specific handler based on queryType
    QueryHandler->>SystemDatabaseReader: Call appropriate method (e.g., getSystems, getPrograms)
    SystemDatabaseReader-->>QueryHandler: Return requested data
    QueryHandler->>Client: Send response with data and error flag
```

## Possibly related PRs

- vishalpalaniappan/asp-query-server#24: Introduces a `SystemWriter` class and logic for writing system metadata and related tables into the same database, complementing the reading functionality added in this PR.

## Poem

> In the warren of code, we dig anew,  
> With paws on the database, queries flew!  
> Systems and programs, deployments and traces,  
> Each message type now finds its places.  
> The handler bunnies hop with glee—  
> Data retrieved as quick as can be!  
> 🐇✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKNwSPbABsvkCiQBHbGlcSHFcLzpIACIAQVp6ADMSamx/MPxIIMp5WIBlK0haahU0RA9sRHgMIjDYD2yXSFhMWkiKaMgAdzL7bAFmdRp6OUhKykgJJBavbjQvTHg0bjmsXu5vX39sxFCMRwEJgCYANg0YBGRbFGZuSLYMXGRE7AwxeHwMefV5Aizgpq4eqQfKFYq4UrlAA03QQDFg3XgPkghzG5RGuTyADUMpMkNhvgAvDyIWS7EjMRAwpR3fCyB5PGGtOokeB8JgUfyIbifWjVWq4KgMaQaNzAxrycoUKR8NAMYXcJ4syCJfA+fBdflhWTcaT8RL/SjwaQcIwARnOACpLQBxEihPJkmiU61cGz2tIYZDzXyk8mUlBYIEecGQkjndyGprbbBsvWfLy/cUA346jwAA2iNoAotAAPp5ACaeWg2YAsnloumI8CuTyvR5qgwvNglN7IF4kKF8Aa/c7kJqgSy2ZAAJIAEW9GHoMqqn0QooMRyttvt1go+CIVBdlrdHooXvQyN4m+3z3wsvsuoY8ESxvofYpY/H6Gnk0oc4wNYaKcDzdbeqZjm+a2AA8jaNixBWVboMgwZRqmuqvvQMZxsg6aPswo60OmyGQBhToUliH4fBguHVMqobfn40j1uUCiPNoh5oB2Xb6jwG5bmglKLgAzCudqhOOJC0vS7CIK6kDurgnresiNJeHSDLnpe3KiLe972IRzDPnhs6kdREp/i2bb4VmuZ5uO2ZWAAMqBhZltmABy0CVrhzKoVy+GYdh7lvgR/rERQn7VhcHh1vOHhMIx1Ttp2uzsQpSnsM+C5GAALAJa7QEK0iSdJslHr4rzwNkYS5Sp6BXupd5RJhz4wvpnxMm+SViY8z6Gb+TYmYB5n5tAkEAMLZm5eGeYBPk4TCAXOkFIUtfQ6ZtQyvnURFDYMRCsVVfF3YGoKcoimKHjXPMiCZGpN61cgLTTp2NTscw8yqhQbD0I0xqIFwt5VRNSTGl49CoIMiBVDUMLwRt9Gasi1R8gw1AeJgkCUBufCDgiLFsGDaCkKjAAedxMVqUMpBdX5GKOBrVBI3wPtpgavc94ifCgyAnlMSi0DCDjwrBVUYJ8YAkATXYpfVE78HwTUYJDwJSjKNHcryA7qFjWBoxegYI9QpMtKEKNa3w+Dymk/j0F0CCRBxpvSODArJjki5YvTetsz0HOUMzUSjBQrwYPrJKUErimIz48jMpUWpWPguzPUGl1kDO9N4bTqeffGSssQA6iQAh5KbADWa7ReQ7xswAFOmXTfQA9HXYfzLAcecAAHAA7CcACs6YAJTrbRkUDpQHgyhpUSvQonKiLg5Bg4GKL4EOdOdvQzLp2v9jCl8FAfGlBiRhsyIxiEjAo6iqqvOvoSwLguDcPXddEOr/QaEwzB11MiAzHMCyB8sVYdcyjcDABKMAitKB12Pl4OupxFwWEgENFgyk+hOBcCdBQrBxL8CDMCGBNEdihGilUBKPYqoCD3iQGmjxlAVywIcXAXQSBkGVOMGW0x5h/0WIAlGzJ4JoDwCwJG68wZdkwKEdMTAlBUFUOobQ1YjBQEjOwwhwRyT0D+HMcYyp/BTBIF0W2woF6jAhEQF+j10wAAFpHKDkdtHggjyiKOUcCQRBAWZRDKKQiEHU5RFyFl0SItBSCaPwU48KgR1GGzfNFO8b0ohAmoDRfRtduij0ceMWgoooB5H6OUbIjxEzywqFKNRIREmZC5I4DwZNUlGPtiiX4eMLG1GsbY2RagHHVLYC4sKOAPEiNgj4iR6AGABI1ME0JujpA1PKQlZkcS2TvTqMkvRxo0nMPSD0ugi4nKZDQAkdQpF5hFCQAwSon4YTJDoAIfxMJta2JSRsrBqCtkeE5vAbmi94LXA/spai1QaBClZlgT2rFBjDFxNFQUaoHqO0ifUroF4i6JEUoY3YqQD76GMOAKAyd2LuMIKQcgVAoX/PYFwXg/BhCz0kHqUYHSVBdK0DoHFJhXGoFQHwoRxAyDKHJSgylfg0AYscM9JojLFB2JZdoXQYBDC4tMAYD+9ZxJ1wlHmO6bQoEVhtHmaAhYrCjQ0NwWQppoiWoMIg2Io4+WkqGQ4dB8hyHwkwKQRAmCEagucPIdMeqDVGtGrhcFzBFATwxDRMNUxHrwXYFQ9C/U8wAFUnKjgAIrJuzAa4ao1ohcDNH5JaSaABio4bLZsGtmPNXAjhFposTG8sb6g6UxiqfAaRIDkEMfGr6XAgIWSLCWcslYC3phmkmsCEEoKjsgHWidwFLLWTsg5Zyrl82QF4uOvCA6Bq5tnelUKcBUBupqHqeC5R9o0SYEQQOxJ6A40QHjWpaYpxhNZLKMGpslhQv2GwPeDAUBKEeBPYKMJRarD5I9RIG4dJMMyH8K+fA+S7CbKER9z7tS6jVkObtkAS5kj0vMdRi5zCWFiF4YF7tDx/HgkoZszhqPIHIRBi8ULtYbAEJ2QD7BjnSCUf071Jymj+ryPqw1xq8jkSwFIlgarHiIA1SmLVrR2h1wDRJk1ZqQ29GwNwcEdAqXrI7cgAjibF2pozVmnNsQRpjWZLuvMZaK05urWNd5Ub8BSHXm+fwjbfbyDw2Zsyi6h2lmgtuxzU7IIRYXRZKytl7KORcm5RaIWLKDVs3mkN6tp4bSg7UIFJBSAy2I3qM0dQNzYCIAiTKBhLXRCUcq1Vnx1Wau1Wpx0/pxwlDueUd0hzKCmvNfVq1Nq7UkoFXVcVvr2Kno9UYWIXaDGMAWAvdMXXnQ9YhH1kgA2ZHkWQECjctBsDCk0ZkXUFBmZRi+rg18IICiQDyOmmy6gQy9bKOGMc6HMDPuQKGXbDFy6gpkC63UgdHosTLnStmfx0wgI0LQAQuEm5Qv8AscQUhcQXo7RQYUKokTIzfJ80ybAgSKDgpkZsccoqfBB6RN9q2UhYD0/dwQIgxBFBIJEUF1EGMLx6gBZA5OW60Cp4weo4yUAGhYjtm2osuxUhooKY02OmAtmYFgcnhySjtsvPLkgaXooylCEZaplGjuPEyEJz4zg7sEYCwoTXWAvg40XMg9ILw3g+s7LgeQQvTL+FVyQGNtQfSo0eAmlUsHXySmvBGsIKhIhpauksTs97ICi8p7iZIuB+aYWVyeLilJqQiUUu1RkeFDrGJjywIosg3fwHDomBiux/ZiEScnjwbuGVffoGzSWL5mSy2oumUguAcpHUQLhbPD49M8goEqO8lGjSPVGCtFKE5zjZk5NrDrWouW3HuOwKIp296PRh/Q+wHpuAwioEgLUosFSg/u4kbQLZ/AIPI6vsljOcfAj0YLB/7zjsSsZL5RAcb9DcaR7hBfQCbxDfIC7oSbYUjbZhj7aUA5ZDjwSqjqiaiPScYwFz7fQGCQC6D4R5h5jVDqBUGVzlBeCJB9zphkEUHpj4AQ7IIYAM7zj0E85MEsHkFQBSKKTlBcE8Feh8GMHMGsHCFUFKBeB0EMECGyH4TwiiBFzUzQDd7Zhiy7CIBSGJCQzd5OTcQkAyFCH4QT7IIu4GHKHGFcYkCmFsAWFsGHK0A2GOAYDOEkDQD4DoGGEwga5eHK6hiuHCET4UZeDZhR5fSjgYDaGOGBFJ6OE+HhFWH2ioGUiGHpHj72hWCcRnjJFTS8zaTzSkS5ET7CSiTKTFHaTYSlGBQkSfCVH2hT7GJ1H+gNFaRNHBSkRl41HsDYQyENZNZgBGAtbkAKZKY5Aqb3RQISglqvD0ILhmoWpjbkYTb8pkrTbOpzZ3QLYGBLZ4ZhqnY2zpiLHLGg7DY6ZW6wqnbnZbQkxQ435XodYTDe4rHc5VC3qVINILx8iJDJD+AdT+7YbsRGQYYeo75ygIjvF8CfGgpjIv7tjphQkkC4S267yyAwhXbMzICrxfLUb3b/SE487i6IhAgUTwTokADk6EoYIa9QawKw3G3eMItM+AJcyAHI+WWoJB92LEG22k6Bu2mBFA0mmKbwtSmQee/MoYaWwehUNJ9smGemBmlsuWQp0MGJeEKM6YxsuEaKeM1E8JVu/4SgpoBgwh7xyaGAEyXQGAZYqppA6Ye4MkB4U4qMe+fAOpOszeeszaHg6JHY/i7YtiOS+E7xgkWRM+7pVCUgckvo2kBJSw+E/gg2FAGgE+sZlczBlJLceAaibIWo7hxyduvgcw249oH4MIbaz+IkSJB+0G2sgOX23p6MaUNpqmJAgkBRp43EcZkArsa8SMHMXIZABO5Cs0FIvkO6mE5Rnw0myoIZCpLIWAcp9Q45A5AY0cliGZMi2Z+RhRg5fS6Y0Z9o1RFeykbpz28AgwwBkwbsSJU8M5WEOE85ZRzRZEJSG59o6hAO5eyUCmeI2pKQh5VRQFleM+kZ55PZgk7R0gt5I5RJNA6EU0kWC53526Dmm+jwvkv5Ko/5W55U0+aIWo6YB5Q2E+iFMF1pUZPZdpDpGA6aKY0AaYt5BUnpD2xsysdEjY04AZ4gQZWeLpP4OQWGjYyArwzFi42YsJzQPZfAl66E6JAA2tEMbNEAALq4Tw45TBDkQGiEngikQqgf7MZ8Aqm4z4yoAyWBJyz8DBgUCaj0QqUoChDw4lrnS6loCJDAp9Dyj2wvC+Chgq4JnzD86fCwq+BooaiLwpD8yIlmWoDt7aA1a4CvQ9AUA8w8DewXiDCPQpAUCt5KncVswmXEnv5IhpB6jMg8jHyBkIp8WRTIkXgFaJjnBRFFCfb0Qt7ejpBdBUArBRDUkuAiwEwv4ohhxFwS6Iz54IgNmKj/4eT7gskni8A/oeC8XokHw2q/5MYAEhiiDAGHUsZExsaQF8BEHN6wF8aer0WIGTxXFmVwXzFMUOXOk2UkCVzon5kUSya3CtbTHtZKUzEuBLE+6M43GPUJDPVQ1sxvU6oxkpm/ViX/UyaTFtbKbwng2yCQ0rEw1QBPVJAvWI0Xm4D9kl4GF/XLmA3yZPB41zE6oUB40E3XHaaw3fLJXk3wWXlQW1G02Lz03A2M2g3zGs2XEI1ehE0ghw2k3S0MXzEIUVRo3fUY1qFyai2Kbi0s1s1k0y2c3E3y0qgG1K06ofUagsVsVphq1PqkAa0i1TFi041g1S2E2c2jHWnNZa3O062u0S140AASSlw26xjW429qU28+exrqBx/Gh8wITy/g7+YgF4cEwIIZzZtQfs5ecoQcHEoeHwlQgYD0HgzCAgF04ya4IZJ4xiDsHYm4t1baLEeGpIbwsAG4QsJdPNMm7xrFOQdtz6+ZSSoQKGcwC1fe6I928EmYEo7FuoMEtUQMuIaed4gGZpNwi+UKMG9eFxKY7NjOoU1MZk89aYMEIMSADsHJxCPo6Etp9pn1Yl6YAA3DLmMBgP4DenenQDfefD4PfYxY/VbQPS4AvRidRGUI3vCF3SZqbYrUjZEHmBKIdjRL3hdumaIKyFIF9fbb5W+IEjRIciLmJc8LHvBBXVXSXLgEyPfBSIqBLlWfRMGDpL0AAFJ5CgROTG533m2RCgOyB6WZCTY7F/RDwNjnAcNcOOJ9FFU+kDVRSCIZXjTiPfJ/At3VTXS3U7ViU7qGmvG4jpgGUQP9K8BF1wPZ1PS6O2JM68XGlEBHbID+DRqjUbloASDazkLwQ7yHQZ5RCIMkACOYlIAT3wjURECKR3K+Dph132wXi4R0x7zd7syebebnCgQyg+glJbQbixXoopNVDH6J7MinHeD26Z5tr3mcxRC8WWNTybxfKQBSNOSiXfUHxkYggHWv60aAEnWMav7nXb1XWeBcbaMgbiAJ3G3c1m3LTUKKXzECND0O101Y0g0B160Sgh0S2y3ujoN4SnHFPt2Aa918MkDIMpiVyUPFz2j5mABJhPhFA28NzgaF/dgyQLg8+pc/nFQzcys77djbMbjZs6HdpgWZLu6q48Cl8L4IpC/IBn8DogE0E2nLcBuN5k05wy07U6pvyKNo1t7eMSqv82s4C2DTYNmLEOOI5BoMwLQOHdapsVHTsTHRKi6gaPNgnccStuS5S45EUKbDUoxC+UTvrsqEZPCVggzYpTICwmsPLecKOGPfaBZcqA4CsJdR9L+GCeegbGK7+JArKEFQwxyW8CZFqEmmFiOlCJ0JOjYOBDFnkNazEIugliuslq5E68yEmplnZhoJ0CWtrIlQiEZNq9k7QAKwyMSWvXdnGkTLPFEM0whPrizNXknReHyY9Im36e3mdh6UbsZK2AXcvRSZyV4Oi3zFjMgCUT0XNN+QMdeUMblfwsCNq4lHrmVeQPQJXOGEQBoDCHtOxIXriSeaXtzoMQpg8lZRVAPHLXyL7sUpRBGylG2AwHvIcBndtT6fM20FqIcC0FMNrFPM9IwQVVEJnMrkiC2GlcJbUC3IYheiHBMPljhhrB2drCGcyEbFu/Y7CKwqScWwo1nlfVqLvXBk7C4KRgy501Rt0whr0wxiAYeIMxq9LCMzAbxhMw9VAPsuQJAJXOGxcpG8KzbO4b/V2tTtKtzgh4dRy7QH3Hi2MRMcSy7aS4HQa2HQx5HSI46jNk0HHRCw9ZGFU2xvYBCM6ClFPPBHnAXNc7fEpfA9fuCrR3XjpAE+czkEI5g8KPSh866ecJ7hmxEzicqNJ4XNXaEAa4GMckSMSYQ+MOhK8zp8/QLPBBKy3nctLrFDQIcuxGpyg+cPsk5fUHwLC83kEdFbkyqOipO2+3wJYxyyPOkM9EoJB/tTB//j08ddRwMwaOAextddAWM3AQnTh+GAx97YqhypHgPrLrytx4Ktgo8FwFQGKnsVKjIsyuoKyvKoYFV/8rQV8ogHmOsgYnQHmJikvj10qlAIct3GgCcLxN3IkAwAIOlMULQEcAAJwLcMDbcnBtxoBtwkCbdoBmgMAAAMZo3cDAHcaAS3HcHg7KeKRQD33c53W3bcRw3c3ca38oiQm3AgZoJwJw536URw6U6UZo533cKQHcm3ncJwm353DA3cU3VXYPAgoPvEyPAgRweP6UiQG3hPiQJwuPAgN3XcwoaA6Uco53uPDARwq3aPz37+vEbcDAyODAZotAXc3cvE6UyP6U3cm3APtA+353aAHcZoqgHcncbcvERwZoRwBoT3EAkAUvSgiPJw4Pm3cPJAbcbc6U1CHcvEyOvEC3RwDAhPAgHc6UR3m3HPoPLEOKBgfXKCA34uw3Rdo3tAeYBK+gQAA= -->

<!-- internal state end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=vishalpalaniappan/asp-query-server&utm_content=26):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: vishalpalaniappan
created_at: 2025-04-22T18:52:11Z
body: @coderabbitai pause
author_association: OWNER
user: login: coderabbitai[bot]
created_at: 2025-04-22T18:52:18Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Reviews paused.

</details>
author_association: NONE
user: login: vishalpalaniappan
created_at: 2025-04-22T21:19:45Z
body: @coderabbitai resume
author_association: OWNER
user: login: coderabbitai[bot]
created_at: 2025-04-22T21:19:51Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Reviews resumed.

</details>
author_association: NONE