Repository: apache/airflow

Issue:
Add Variable, Connection "description" fields available in the API
### Description

I'd like to get the "description" field from the variable, and connection table available through the REST API for the calls:

1. /variables/{key}
2. /connections/{conn_id}

### Use case/motivation

_No response_

### Related issues

_No response_

### Are you willing to submit a PR?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)



Discussion:
Hello @SangwanP,
I checked the description field for variables API, seems like missing, but for connections api, its working properly (for both scenarios, all connections & a specific connection)

Can I request you to confirm the version you are using ? I tried with 2.4
Please assign this ticket to me.
> Please assign this ticket to me.

Done!
@nitinmahawadiwar - You are right, the list connections endpoint does have the description field. I was just going off of the API doc: https://airflow.apache.org/docs/apache-airflow/stable/stable-rest-api-ref.html. In this case, only the Variable API endpoint needs to be updated.
@potiuk - Is the REST API doc an active document? 
It follows releases. API is connected with Airlfow releases. Tje main is development and any changes there are coming in 2.3.0. Should we close that one?
Hello @potiuk @eladkal , I am able to return the description field from get variable api, also found the places to update documentation  (/api_connexion/openapi/v1.yaml - verifying locally) but somehow not able to understand the Test cases & its structure.
Do you have any document to go through for test case implementation.
I am referring https://github.com/apache/airflow/blob/main/TESTING.rst &
updated  test cases in a) tests/models/test_variable.py & b) tests/api_connexion/endpoints/test_variable_endpoint.py 

Thanks

I am afraid not, but if you have some specific questions/ comments - @ephraimbuddy and @mik-laj  might be able to help.
> Hello @potiuk @eladkal , I am able to return the description field from get variable api, also found the places to update documentation (/api_connexion/openapi/v1.yaml - verifying locally) but somehow not able to understand the Test cases & its structure. Do you have any document to go through for test case implementation. I am referring https://github.com/apache/airflow/blob/main/TESTING.rst & updated test cases in a) tests/models/test_variable.py & b) tests/api_connexion/endpoints/test_variable_endpoint.py
> 
> Thanks

You can make the PR, we will guide you on the tests
> > Hello @potiuk @eladkal , I am able to return the description field from get variable api, also found the places to update documentation (/api_connexion/openapi/v1.yaml - verifying locally) but somehow not able to understand the Test cases & its structure. Do you have any document to go through for test case implementation. I am referring https://github.com/apache/airflow/blob/main/TESTING.rst & updated test cases in a) tests/models/test_variable.py & b) tests/api_connexion/endpoints/test_variable_endpoint.py
> > Thanks
> 
> You can make the PR, we will guide you on the tests

Sure @ephraimbuddy Thanks

Resolving patch:
diff --git a/airflow/api_connexion/endpoints/variable_endpoint.py b/airflow/api_connexion/endpoints/variable_endpoint.py
--- a/airflow/api_connexion/endpoints/variable_endpoint.py
+++ b/airflow/api_connexion/endpoints/variable_endpoint.py
@@ -42,13 +42,13 @@ def delete_variable(*, variable_key: str) -> Response:
 
 
 @security.requires_access([(permissions.ACTION_CAN_READ, permissions.RESOURCE_VARIABLE)])
-def get_variable(*, variable_key: str) -> Response:
-    """Get a variables by key"""
-    try:
-        var = Variable.get(variable_key)
-    except KeyError:
+@provide_session
+def get_variable(*, variable_key: str, session: Session = NEW_SESSION) -> Response:
+    """Get a variable by key"""
+    var = session.query(Variable).filter(Variable.key == variable_key)
+    if not var.count():
         raise NotFound("Variable not found")
-    return variable_schema.dump({"key": variable_key, "val": var})
+    return variable_schema.dump(var.first())
 
 
 @security.requires_access([(permissions.ACTION_CAN_READ, permissions.RESOURCE_VARIABLE)])
diff --git a/airflow/api_connexion/schemas/variable_schema.py b/airflow/api_connexion/schemas/variable_schema.py
--- a/airflow/api_connexion/schemas/variable_schema.py
+++ b/airflow/api_connexion/schemas/variable_schema.py
@@ -23,6 +23,7 @@ class VariableSchema(Schema):
 
     key = fields.String(required=True)
     value = fields.String(attribute="val", required=True)
+    description = fields.String(attribute="description", required=False)
 
 
 class VariableCollectionSchema(Schema):


Test patch:
diff --git a/tests/api_connexion/endpoints/test_variable_endpoint.py b/tests/api_connexion/endpoints/test_variable_endpoint.py
--- a/tests/api_connexion/endpoints/test_variable_endpoint.py
+++ b/tests/api_connexion/endpoints/test_variable_endpoint.py
@@ -109,7 +109,7 @@ def test_should_respond_200(self):
             "/api/v1/variables/TEST_VARIABLE_KEY", environ_overrides={'REMOTE_USER': "test"}
         )
         assert response.status_code == 200
-        assert response.json == {"key": "TEST_VARIABLE_KEY", "value": expected_value}
+        assert response.json == {"key": "TEST_VARIABLE_KEY", "value": expected_value, "description": None}
 
     def test_should_respond_404_if_not_found(self):
         response = self.client.get(
@@ -132,8 +132,8 @@ class TestGetVariables(TestVariableEndpoint):
                 "/api/v1/variables?limit=2&offset=0",
                 {
                     "variables": [
-                        {"key": "var1", "value": "1"},
-                        {"key": "var2", "value": "foo"},
+                        {"key": "var1", "value": "1", "description": "I am a variable"},
+                        {"key": "var2", "value": "foo", "description": "Another variable"},
                     ],
                     "total_entries": 3,
                 },
@@ -142,8 +142,8 @@ class TestGetVariables(TestVariableEndpoint):
                 "/api/v1/variables?limit=2&offset=1",
                 {
                     "variables": [
-                        {"key": "var2", "value": "foo"},
-                        {"key": "var3", "value": "[100, 101]"},
+                        {"key": "var2", "value": "foo", "description": "Another variable"},
+                        {"key": "var3", "value": "[100, 101]", "description": None},
                     ],
                     "total_entries": 3,
                 },
@@ -152,7 +152,7 @@ class TestGetVariables(TestVariableEndpoint):
                 "/api/v1/variables?limit=1&offset=2",
                 {
                     "variables": [
-                        {"key": "var3", "value": "[100, 101]"},
+                        {"key": "var3", "value": "[100, 101]", "description": None},
                     ],
                     "total_entries": 3,
                 },
@@ -160,8 +160,8 @@ class TestGetVariables(TestVariableEndpoint):
         ]
     )
     def test_should_get_list_variables(self, query, expected):
-        Variable.set("var1", 1)
-        Variable.set("var2", "foo")
+        Variable.set("var1", 1, "I am a variable")
+        Variable.set("var2", "foo", "Another variable")
         Variable.set("var3", "[100, 101]")
         response = self.client.get(query, environ_overrides={'REMOTE_USER': "test"})
         assert response.status_code == 200
@@ -279,6 +279,7 @@ def test_should_create_variable(self):
         assert response.json == {
             "key": "var_create",
             "value": "{}",
+            "description": None,
         }
 
     def test_should_reject_invalid_request(self):