Repository: feast-dev/feast

Issue:
FeatureService should support "description" as first class named-value argument as Entity does
## Expected Behavior 
...
```
# Define your feature service and the features it will serve
driver_fs = FeatureService(name="driver_ranking_fv_svc",
                           features=[driver_hourly_stats_view],
                           description="Used for training an ElasticNetCV model"})
```
```
feast feature-services describe driver_ranking_fv_svc
spec:
  name: driver_ranking_fv_svc
  features:
  - featureViewName: driver_hourly_stats
    featureColumns:
    - name: conv_rate
      valueType: FLOAT
    - name: acc_rate
      valueType: FLOAT
    - name: avg_daily_trips
      valueType: INT64
description: Used for training an ElasticNetCV model
meta: {}
```
Extend the argument list in the [FeatureService ](https://github.com/feast-dev/feast/blob/7dff49a194a25a62927c1ee7022caf0651f68f38/sdk/python/feast/feature_service.py#L24)to support `description=None` as default
## Current Behavior

```
# Define your feature service and the features it will serve
driver_fs = FeatureService(name="driver_ranking_fv_svc",
                           features=[driver_hourly_stats_view],
                           tags={"description": "Used for training an ElasticNetCV model"})
```
Then query the FS from the cli

```
feast feature-services describe driver_ranking_fv_svc
spec:
  name: driver_ranking_fv_svc
  features:
  - featureViewName: driver_hourly_stats
    featureColumns:
    - name: conv_rate
      valueType: FLOAT
    - name: acc_rate
      valueType: FLOAT
    - name: avg_daily_trips
      valueType: INT64
  tags:
    description: Used for training an ElasticNetCV model
meta: {}
```
## Steps to reproduce

Follow the steps above in the current behavior

### Specifications

- Version: v0.12v



Discussion:

Resolving patch:
diff --git a/sdk/python/feast/feature_service.py b/sdk/python/feast/feature_service.py
--- a/sdk/python/feast/feature_service.py
+++ b/sdk/python/feast/feature_service.py
@@ -31,6 +31,7 @@ class FeatureService:
     name: str
     features: List[FeatureViewProjection]
     tags: Dict[str, str]
+    description: Optional[str] = None
     created_timestamp: Optional[datetime] = None
     last_updated_timestamp: Optional[datetime] = None
 
@@ -39,6 +40,7 @@ def __init__(
         name: str,
         features: List[Union[FeatureTable, FeatureView, FeatureViewProjection]],
         tags: Optional[Dict[str, str]] = None,
+        description: Optional[str] = None,
     ):
         """
         Creates a FeatureService object.
@@ -56,6 +58,7 @@ def __init__(
             else:
                 raise ValueError(f"Unexpected type: {type(feature)}")
         self.tags = tags or {}
+        self.description = description
         self.created_timestamp = None
         self.last_updated_timestamp = None
 
@@ -97,6 +100,11 @@ def from_proto(feature_service_proto: FeatureServiceProto):
                 for fp in feature_service_proto.spec.features
             ],
             tags=dict(feature_service_proto.spec.tags),
+            description=(
+                feature_service_proto.spec.description
+                if feature_service_proto.spec.description != ""
+                else None
+            ),
         )
 
         if feature_service_proto.meta.HasField("created_timestamp"):
@@ -137,6 +145,8 @@ def to_proto(self) -> FeatureServiceProto:
 
         if self.tags:
             spec.tags.update(self.tags)
+        if self.description:
+            spec.description = self.description
 
         feature_service_proto = FeatureServiceProto(spec=spec, meta=meta)
         return feature_service_proto


Test patch:
diff --git a/sdk/python/tests/unit/test_feature_service.py b/sdk/python/tests/unit/test_feature_service.py
new file mode 100644
--- /dev/null
+++ b/sdk/python/tests/unit/test_feature_service.py
@@ -0,0 +1,14 @@
+from feast import FeatureService
+
+
+def test_feature_service_with_description():
+    feature_service = FeatureService(
+        name="my-feature-service", features=[], description="a clear description"
+    )
+    assert feature_service.to_proto().spec.description == "a clear description"
+
+
+def test_feature_service_without_description():
+    feature_service = FeatureService(name="my-feature-service", features=[])
+    #
+    assert feature_service.to_proto().spec.description == ""