Repository: etendosoftware/com.etendoerp.copilot.toolpack
Pull request #19: Hotfix/#40 etp 1354
Author: login: fran-roig
Created: 2025-04-04T12:46:27Z | Merged: 2025-04-04T12:46:49Z



Diff:
diff --git a/build.gradle b/build.gradle
index c7caeed..cd68bdd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@
 */
 
 group          = "com.etendoerp"
-version        = "1.7.0"
+version        = "1.8.0"
 description    = "Specialized tool set for Etendo Copilot"
 ext.artifact   = "copilot.toolpack"
 ext.repository = "https://maven.pkg.github.com/etendosoftware/com.etendoerp.copilot.extensions"
@@ -52,6 +52,6 @@ repositories {
 * Ex: implementation "com.sun.mail:javax.mail:1.6.2"
 */
 dependencies {
-    implementation('com.etendoerp:copilot:1.12.0')
+    implementation('com.etendoerp:copilot:1.13.0')
     implementation 'com.github.jsqlparser:jsqlparser:5.1'
 }
diff --git a/src-db/database/sourcedata/AD_ELEMENT.xml b/src-db/database/sourcedata/AD_ELEMENT.xml
index 24d66e5..38b5f65 100644
--- a/src-db/database/sourcedata/AD_ELEMENT.xml
+++ b/src-db/database/sourcedata/AD_ELEMENT.xml
@@ -56,18 +56,4 @@
 <!--A929EEDECED34D4ABED125A649895B47-->  <ISGLOSSARY><![CDATA[N]]></ISGLOSSARY>
 <!--A929EEDECED34D4ABED125A649895B47--></AD_ELEMENT>
 
-<!--ABBD6494073E460CA0C6F14CBDB31E8D--><AD_ELEMENT>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <AD_ELEMENT_ID><![CDATA[ABBD6494073E460CA0C6F14CBDB31E8D]]></AD_ELEMENT_ID>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <AD_ORG_ID><![CDATA[0]]></AD_ORG_ID>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <ISACTIVE><![CDATA[Y]]></ISACTIVE>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <COLUMNNAME><![CDATA[Etcoapi_Api_ID]]></COLUMNNAME>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <NAME><![CDATA[API ID]]></NAME>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <PRINTNAME><![CDATA[API ID]]></PRINTNAME>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <DESCRIPTION><![CDATA[The ID of the API.]]></DESCRIPTION>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <HELP><![CDATA[This ID is used to find the API.]]></HELP>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <AD_MODULE_ID><![CDATA[DAD8E003446345E1BD052DC3539DAA53]]></AD_MODULE_ID>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <ISGLOSSARY><![CDATA[N]]></ISGLOSSARY>
-<!--ABBD6494073E460CA0C6F14CBDB31E8D--></AD_ELEMENT>
-
 </data>
diff --git a/src-db/database/sourcedata/AD_MODULE.xml b/src-db/database/sourcedata/AD_MODULE.xml
index 5e6e269..bc8a68e 100644
--- a/src-db/database/sourcedata/AD_MODULE.xml
+++ b/src-db/database/sourcedata/AD_MODULE.xml
@@ -6,7 +6,7 @@
 <!--DAD8E003446345E1BD052DC3539DAA53-->  <AD_ORG_ID><![CDATA[0]]></AD_ORG_ID>
 <!--DAD8E003446345E1BD052DC3539DAA53-->  <ISACTIVE><![CDATA[Y]]></ISACTIVE>
 <!--DAD8E003446345E1BD052DC3539DAA53-->  <NAME><![CDATA[Tool Pack]]></NAME>
-<!--DAD8E003446345E1BD052DC3539DAA53-->  <VERSION><![CDATA[1.7.0]]></VERSION>
+<!--DAD8E003446345E1BD052DC3539DAA53-->  <VERSION><![CDATA[1.8.0]]></VERSION>
 <!--DAD8E003446345E1BD052DC3539DAA53-->  <DESCRIPTION><![CDATA[Specialized tool set for Etendo Copilot]]></DESCRIPTION>
 <!--DAD8E003446345E1BD052DC3539DAA53-->  <HELP><![CDATA[Tool Pack is a module for Etendo Copilot that provides a set of specialized tools available to any assistant. It adds specific functionalities to enhance performance and expand capabilities, making assistants more versatile and effective.]]></HELP>
 <!--DAD8E003446345E1BD052DC3539DAA53-->  <TYPE><![CDATA[M]]></TYPE>
diff --git a/src-test/src/com/etendoerp/copilot/toolpack/webhook/WebhooksTests.java b/src-test/src/com/etendoerp/copilot/toolpack/webhook/WebhooksTests.java
new file mode 100644
index 0000000..c6bddee
--- /dev/null
+++ b/src-test/src/com/etendoerp/copilot/toolpack/webhook/WebhooksTests.java
@@ -0,0 +1,149 @@
+package com.etendoerp.copilot.toolpack.webhook;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.openbravo.base.secureApp.VariablesSecureApp;
+import org.openbravo.base.session.OBPropertiesProvider;
+import org.openbravo.base.weld.test.WeldBaseTest;
+import org.openbravo.client.kernel.RequestContext;
+import org.openbravo.dal.core.OBContext;
+import org.openbravo.dal.service.OBDal;
+import org.openbravo.test.base.TestConstants;
+
+import com.etendoerp.copilot.toolpack.webhooks.ExecSQL;
+import com.etendoerp.copilot.toolpack.webhooks.SimSearch;
+
+/**
+ * Unit tests for the Webhooks in the Copilot Toolpack.
+ *
+ */
+
+public class WebhooksTests extends WeldBaseTest {
+  public static final String DATA = "data";
+  public static final String ID = "id";
+  private AutoCloseable mocks;
+
+  /**
+   * Sets up the test environment before each test.
+   *
+   * @throws Exception
+   *     if an error occurs during setup
+   */
+  @Before
+  public void setUp() throws Exception {
+    mocks = MockitoAnnotations.openMocks(this);
+    super.setUp();
+
+    OBContext.setOBContext(TestConstants.Users.ADMIN, TestConstants.Roles.SYS_ADMIN, TestConstants.Clients.SYSTEM,
+        TestConstants.Orgs.MAIN);
+    VariablesSecureApp vars = new VariablesSecureApp(OBContext.getOBContext().getUser().getId(),
+        OBContext.getOBContext().getCurrentClient().getId(), OBContext.getOBContext().getCurrentOrganization().getId());
+    RequestContext.get().setVariableSecureApp(vars);
+
+    OBDal.getInstance().flush();
+
+    vars.setSessionValue("#User_Client", OBContext.getOBContext().getCurrentClient().getId());
+    RequestContext.get().setVariableSecureApp(vars);
+    OBPropertiesProvider.setInstance(new OBPropertiesProvider());
+  }
+
+  /**
+   * Tests the execSQLWebhook method.
+   * <p>
+   * This test method verifies the functionality of the execSQLWebhook method in the ExecSQL class.
+   * It tests different modes of SQL execution and checks the responses.
+   * </p>
+   *
+   * @throws Exception
+   *     if an error occurs during the test
+   */
+  @Test
+  public void execSQLWebhook() {
+    ExecSQL ex = new ExecSQL();
+    Map<String, String> parameter = new HashMap<>();
+    Map<String, String> respVars;
+
+    // Test SHOW_TABLES mode
+    parameter.put("Mode", "SHOW_TABLES");
+    respVars = new HashMap<>();
+    ex.get(parameter, respVars);
+    assertTrue(!respVars.keySet().isEmpty());
+
+    // Test SHOW_COLUMNS mode
+    parameter.put("Mode", "SHOW_COLUMNS");
+    parameter.put("Table", "C_BPartner");
+    respVars = new HashMap<>();
+    ex.get(parameter, respVars);
+    assertTrue(!respVars.keySet().isEmpty());
+
+    // Test EXEC mode with a query that should fail
+    try {
+      parameter.put("Mode", "EXEC");
+      parameter.put("Query", "SELECT * FROM ad_field");
+      respVars = new HashMap<>();
+      ex.get(parameter, respVars);
+    } catch (Exception e) {
+      // The message must be "The table ad_field must have an alias."
+      assertTrue(StringUtils.containsIgnoreCase(e.getMessage(), "The table ad_field must have an alias."));
+    }
+
+    // Test EXEC mode with a valid query
+    parameter.put("Mode", "EXEC");
+    parameter.put("Query", "SELECT * FROM ad_field af");
+    respVars = new HashMap<>();
+    ex.get(parameter, respVars);
+    assertTrue(!respVars.keySet().isEmpty());
+    assertTrue(StringUtils.isNotEmpty(respVars.get("data")));
+  }
+
+  /**
+   * Tests the simSearch method.
+   * <p>
+   * This test method verifies the functionality of the simSearch method in the SimSearch class.
+   * It tests the search functionality and checks the responses.
+   * </p>
+   *
+   * @throws Exception
+   *     if an error occurs during the test
+   */
+  @Test
+  public void simSearch() throws Exception {
+    SimSearch ss = new SimSearch();
+    Map<String, String> parameter = new HashMap<>();
+    Map<String, String> respVars;
+
+    // Test search with a search term and entity name
+    parameter.put("searchTerm", "c_order");
+    parameter.put("entityName", "ADTable");
+    respVars = new HashMap<>();
+    ss.get(parameter, respVars);
+    assertTrue(!respVars.keySet().isEmpty());
+    assertTrue(StringUtils.isNotEmpty(respVars.get("message")));
+    JSONObject json = new JSONObject(respVars.get("message"));
+    assertTrue(json.has("data"));
+    assertTrue(json.getJSONArray("data").length() > 0);
+  }
+
+  /**
+   * Cleans up the test environment after each test.
+   */
+  @After
+  public void tearDown() {
+    OBContext.setOBContext(TestConstants.Users.ADMIN, TestConstants.Roles.SYS_ADMIN, TestConstants.Clients.SYSTEM,
+        TestConstants.Orgs.MAIN);
+    VariablesSecureApp vars = new VariablesSecureApp(OBContext.getOBContext().getUser().getId(),
+        OBContext.getOBContext().getCurrentClient().getId(), OBContext.getOBContext().getCurrentOrganization().getId());
+    RequestContext.get().setVariableSecureApp(vars);
+    OBDal.getInstance().flush();
+    OBDal.getInstance().commitAndClose();
+  }
+}
diff --git a/tools/AttachFileTool.py b/tools/AttachFileTool.py
index 1642950..90173a2 100644
--- a/tools/AttachFileTool.py
+++ b/tools/AttachFileTool.py
@@ -2,8 +2,7 @@
 import os
 from typing import Type, Optional, Dict
 
-from copilot.core import utils
-from copilot.core.etendo_utils import call_webhook, get_etendo_token
+from copilot.core.etendo_utils import call_webhook, get_etendo_host, get_etendo_token
 from copilot.core.tool_input import ToolInput, ToolField
 from copilot.core.tool_wrapper import ToolWrapper
 from copilot.core.utils import copilot_debug
@@ -56,9 +55,7 @@ def run(self, input_params: Dict, *args, **kwargs) -> Dict:
             file_base64 = base64.b64encode(file_content).decode("utf-8")
         file_name = os.path.basename(filepath)
         access_token = get_etendo_token()
-        etendo_host = utils.read_optional_env_var(
-            "ETENDO_HOST", "http://host.docker.internal:8080/etendo"
-        )
+        etendo_host = get_etendo_host()
         copilot_debug(f"ETENDO_HOST: {etendo_host}")
         return self.attach_file(
             etendo_host, access_token, ad_tab_id, record_id, file_name, file_base64


Reviews:
user: login: etendobot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T12:47:17Z
user: login: etendobot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T12:47:26Z
user: login: etendobot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T12:47:32Z
user: login: etendobot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T12:47:44Z
user: login: etendobot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T12:47:49Z

Review comments:
path: build.gradle
user: login: etendobot
body: # GPT Review for build.gradle
### Review

- **Estimated effort to review [1-5]:**  
1, because the changes are straightforward version updates in the `build.gradle` file, which are simple to review and verify.
- **Score:**  95

### Code feedback

- **File:** 
build.gradle
- **Language:** 
gradle
- **Suggestion:** 
Consider adding a comment or a changelog entry to document the reason for the version updates. This can help future developers understand the context of the change. [medium]
- **Label:** 
documentation
- **Existing code:**


```gradle
version        = "1.8.0"
...
implementation('com.etendoerp:copilot:1.13.0')
```

- **Improved code:**


```gradle
// Updated version to 1.8.0 to include new features and bug fixes
version        = "1.8.0"
...
// Updated copilot dependency to 1.13.0 for compatibility with new features
implementation('com.etendoerp:copilot:1.13.0')
```
created_at: 2025-04-04T12:47:17Z
author_association: CONTRIBUTOR
path: src-db/database/sourcedata/AD_ELEMENT.xml
user: login: etendobot
body: # GPT Review for AD_ELEMENT.xml
### Review

- **Estimated effort to review [1-5]:**  
1, because the PR involves a simple deletion of an XML element without any complex logic or additional code changes.
- **Score:**  95

### Code feedback

- **File:** 
src-db/database/sourcedata/AD_ELEMENT.xml
- **Language:** 
xml
- **Suggestion:** 
Ensure that the removal of the `AD_ELEMENT` entry does not affect any dependent components or references in the system. Verify that this element is not used elsewhere in the codebase or database schema. [important]
- **Label:** 
possible issue
- **Existing code:**


```xml
<!--ABBD6494073E460CA0C6F14CBDB31E8D--><AD_ELEMENT>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <AD_ELEMENT_ID><![CDATA[ABBD6494073E460CA0C6F14CBDB31E8D]]></AD_ELEMENT_ID>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <AD_ORG_ID><![CDATA[0]]></AD_ORG_ID>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <ISACTIVE><![CDATA[Y]]></ISACTIVE>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <COLUMNNAME><![CDATA[Etcoapi_Api_ID]]></COLUMNNAME>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <NAME><![CDATA[API ID]]></NAME>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <PRINTNAME><![CDATA[API ID]]></PRINTNAME>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <DESCRIPTION><![CDATA[The ID of the API.]]></DESCRIPTION>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <HELP><![CDATA[This ID is used to find the API.]]></HELP>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <AD_MODULE_ID><![CDATA[DAD8E003446345E1BD052DC3539DAA53]]></AD_MODULE_ID>
<!--ABBD6494073E460CA0C6F14CBDB31E8D-->  <ISGLOSSARY><![CDATA[N]]></ISGLOSSARY>
<!--ABBD6494073E460CA0C6F14CBDB31E8D--></AD_ELEMENT>
```

- **Improved code:**


```xml
<!-- Ensure that the removal of this element does not affect any dependent components or references in the system. -->
```
created_at: 2025-04-04T12:47:26Z
author_association: CONTRIBUTOR
path: src-db/database/sourcedata/AD_MODULE.xml
user: login: etendobot
body: # GPT Review for AD_MODULE.xml
### Review

- **Estimated effort to review [1-5]:**  
1, because the change is minimal, involving only a version update in an XML file. This requires a straightforward review to ensure the version number is correctly updated.
- **Score:**  95

### Code feedback

- **File:** 
src-db/database/sourcedata/AD_MODULE.xml
- **Language:** 
xml
- **Suggestion:** 
Ensure that the version update is consistent with any related documentation or dependencies that might rely on this version number. This is important to maintain coherence across the project. [medium]
- **Label:** 
consistency
- **Existing code:**


```xml
<!--DAD8E003446345E1BD052DC3539DAA53-->  <VERSION><![CDATA[1.8.0]]></VERSION>
```

- **Improved code:**


```xml
<!--DAD8E003446345E1BD052DC3539DAA53-->  <VERSION><![CDATA[1.8.0]]></VERSION>
```
created_at: 2025-04-04T12:47:31Z
author_association: CONTRIBUTOR
path: src-test/src/com/etendoerp/copilot/toolpack/webhook/WebhooksTests.java
user: login: etendobot
body: # GPT Review for WebhooksTests.java
### Review

- **Estimated effort to review [1-5]:**  
3, because the PR introduces a new test class with multiple test methods, which requires a thorough review to ensure the tests are comprehensive and correctly implemented. The complexity is moderate due to the use of mock objects and the need to verify SQL execution and search functionalities.
- **Score:**  85

### Code feedback

- **File:** 
src-test/src/com/etendoerp/copilot/toolpack/webhook/WebhooksTests.java
- **Language:** 
java
- **Suggestion:** 
Consider using `assertFalse` instead of `assertTrue(!condition)` for better readability and to directly express the intent of the test. This change will make the test assertions clearer and more maintainable. [medium]
- **Label:** 
best practice
- **Existing code:**


```java
assertTrue(!respVars.keySet().isEmpty());
```

- **Improved code:**


```java
assertFalse(respVars.keySet().isEmpty());
```

- **File:** 
src-test/src/com/etendoerp/copilot/toolpack/webhook/WebhooksTests.java
- **Language:** 
java
- **Suggestion:** 
In the `execSQLWebhook` test method, consider adding assertions to verify the expected content of `respVars` after executing the SQL queries. This will ensure that the test not only checks for non-empty responses but also validates the correctness of the data returned. [important]
- **Label:** 
enhancement
- **Existing code:**


```java
assertTrue(!respVars.keySet().isEmpty());
assertTrue(StringUtils.isNotEmpty(respVars.get("data")));
```

- **Improved code:**


```java
assertFalse(respVars.keySet().isEmpty());
assertTrue(StringUtils.isNotEmpty(respVars.get("data")));
// Add assertions to verify expected content
assertEquals("ExpectedValue", respVars.get("ExpectedKey"));
```

- **File:** 
src-test/src/com/etendoerp/copilot/toolpack/webhook/WebhooksTests.java
- **Language:** 
java
- **Suggestion:** 
In the `simSearch` test method, consider adding more specific assertions to verify the content of the JSON response. This will help ensure that the search functionality is returning the correct data structure and values. [important]
- **Label:** 
enhancement
- **Existing code:**


```java
assertTrue(json.has("data"));
assertTrue(json.getJSONArray("data").length() > 0);
```

- **Improved code:**


```java
assertTrue(json.has("data"));
assertTrue(json.getJSONArray("data").length() > 0);
// Add assertions to verify specific content in the JSON response
JSONObject firstItem = json.getJSONArray("data").getJSONObject(0);
assertEquals("ExpectedValue", firstItem.getString("ExpectedKey"));
```
created_at: 2025-04-04T12:47:44Z
author_association: CONTRIBUTOR
path: tools/AttachFileTool.py
user: login: etendobot
body: # GPT Review for AttachFileTool.py
### Review

- **Estimated effort to review [1-5]:**  
2, because the changes are straightforward, involving a simple refactor to replace a utility function call with another. The code is not complex, and the logic remains largely unchanged.
- **Score:**  95

### Code feedback

- **File:** 
tools/AttachFileTool.py
- **Language:** 
python
- **Suggestion:** 
Consider adding error handling for the `get_etendo_host()` function call to ensure that any issues with retrieving the host are caught and handled gracefully. This will improve the robustness of the code. [important]
- **Label:** 
error handling
- **Existing code:**


```python
etendo_host = get_etendo_host()
copilot_debug(f"ETENDO_HOST: {etendo_host}")
```

- **Improved code:**


```python
try:
    etendo_host = get_etendo_host()
    copilot_debug(f"ETENDO_HOST: {etendo_host}")
except Exception as e:
    copilot_debug(f"Failed to retrieve ETENDO_HOST: {e}")
    return {"error": "Failed to retrieve Etendo host"}
```
created_at: 2025-04-04T12:47:49Z
author_association: CONTRIBUTOR

PR comments:
user: login: etendobot
created_at: 2025-04-04T12:47:08Z
body: > [!WARNING] 
> ## Git Police 👮 
> Invalid pull request title. Make sure it contains the appropriate issue/task reference and is descriptive of the change made. 

> For more information, visit: [Methodology for repository managment](https://etendoproject.atlassian.net/wiki/spaces/ERP/pages/2795077633/Methodology+for+repository+management+with+GIT)
author_association: CONTRIBUTOR
user: login: sonarscanetendo[bot]
created_at: 2025-04-04T12:47:52Z
body: ![Passed](https://sonar.etendo.cloud/static/communityBranchPlugin/checks/QualityGateBadge/passed.svg?sanitize=true)

# Analysis Details
## 2 Issues
- ![Bug](https://sonar.etendo.cloud/static/communityBranchPlugin/common/bug.svg?sanitize=true) 0 Bugs
- ![Vulnerability](https://sonar.etendo.cloud/static/communityBranchPlugin/common/vulnerability.svg?sanitize=true) 0 Vulnerabilities
- ![Code Smell](https://sonar.etendo.cloud/static/communityBranchPlugin/common/code_smell.svg?sanitize=true) 2 Code Smells

## Coverage and Duplications
- ![Coverage](https://sonar.etendo.cloud/static/communityBranchPlugin/checks/CoverageChart/0.svg?sanitize=true) 0.00% Coverage (0.00% Estimated after merge)
- ![Duplications](https://sonar.etendo.cloud/static/communityBranchPlugin/checks/Duplications/3.svg?sanitize=true) 0.00% Duplicated Code (0.00% Estimated after merge)

**Project ID:** etendosoftware_com.etendoerp.copilot.toolpack_AZLoBD8_CLkcVhrqygqx

[View in SonarQube](https://sonar.etendo.cloud/dashboard?id=etendosoftware_com.etendoerp.copilot.toolpack_AZLoBD8_CLkcVhrqygqx&pullRequest=19)


author_association: NONE