Repository: cognitedata/cognite-sdk-python

Issue:
TypeError: '<=' not supported between instances of 'int' and 'str' in /cognite/client/utils/_time.py", line 97, in ms_to_datetime in if not (MIN_TIMESTAMP_MS <= ms <= MAX_TIMESTAMP_MS)
**System information (please complete the following information):**
 - OS: macOS Sonoma v 14.1
 - Python Version: 3.11
 - SDK Version: 7.17.3

**Describe the bug**
In cognite/client/utils/_time.py
One needs to make sure that item[k] is integer. Otherwise In a `Type error` in the ms_to_datetime funciton
(see error at the end)
```
def _convert_and_isoformat_time_attrs_in_dict(item: dict) -> dict:
    for k in TIME_ATTRIBUTES.intersection(item):
        try:
            item[k] = ms_to_datetime(item[k]).isoformat(sep=" ", timespec="milliseconds")
        except ValueError:
            pass
    return item
```
**To Reproduce**
Runnable code reproducing the error.
```
"""
Test creating a data model in akerbp-sandbox
"""

# Authenticate with akerbp-sandbox
from cognite.client import CogniteClient, ClientConfig
from cognite.client.credentials import OAuthInteractive
from variables import (
    TENANT_ID,
    CDF_CLIENT_ID,
    CDF_CLIENT_NAME,
    CDF_BASE_URL,
)


# Create OAuth credentials
credentials = OAuthInteractive(
    authority_url=f"https://login.microsoftonline.com/{TENANT_ID}",
    client_id=CDF_CLIENT_ID,  # type: ignore
    scopes=[f"{CDF_BASE_URL}/.default"],
)

# Create client configurations
configurations = ClientConfig(
    client_name=CDF_CLIENT_NAME,  # type: ignore
    base_url=CDF_BASE_URL,
    project="akerbp-sandbox",  # type: ignore
    credentials=credentials,
)

# Initialize client
client = CogniteClient(configurations)




# Create a data model
from cognite.client.data_classes.data_modeling import DataModelApply
from cognite.client.data_classes.data_modeling import SpaceApply
from cognite.client.exceptions import CogniteAPIError

def apply_space(client: CogniteClient):
    space = client.data_modeling.spaces.apply(
        SpaceApply(
            space="test_sp_aad_cdf_mapping",
            name="test space for AAD CDF mapping",
        )
    )
    print(space)


def apply_data_model(client: CogniteClient):
    dm = client.data_modeling.data_models.apply(
        DataModelApply(
            space="test_sp_aad_cdf_mapping",
            external_id="test_dm_aad_cdf_mapping",
            version="v1",
            description="mapping between AAD members and CDF resources",
            name="test AAD CDF mapping",
        )
    )
    print(dm)


def apply_dml_to_data_model(client: CogniteClient):
    dm = client.data_modeling.graphql.apply_dml(
        id=("test_sp_aad_cdf_mapping", "test_dm_aad_cdf_mapping", "v1"),
        dml="""
type Group {
    id: String!
    name: String!
    members: [Member]
} 
type Member {
    id: String!
    name: String!
    type: String!
    groups: [Group]
}   
"""
)
    print("Done!")


def main() -> None:
    try:
        apply_data_model(client)
    except CogniteAPIError:
        apply_space(client)
        apply_data_model(client)
    
    apply_dml_to_data_model(client)


if __name__ == "__main__":
    main()
```

**Expected behavior**
```
{
    "space": "test_sp_aad_cdf_mapping",
    "external_id": "test_dm_aad_cdf_mapping",
    "version": "v1",
    "created_time": "2024-02-15T13:18:18.986Z",
    "last_updated_time": "2024-02-16T08:55:53.150Z"
}
```
**Screenshots**
```
Traceback (most recent call last):
  File "/Users/andrian.gasper@cognitedata.com/Cognite/CDF-Aker-BP-Project/Extract-info-AAD/remote_repo/inso-dataops-incubator/integrating-aad-and-cdf-group-data/src/test.py", line 144, in <module>
    main()
  File "/Users/andrian.gasper@cognitedata.com/Cognite/CDF-Aker-BP-Project/Extract-info-AAD/remote_repo/inso-dataops-incubator/integrating-aad-and-cdf-group-data/src/test.py", line 140, in main
    apply_dml_to_data_model(client)
  File "/Users/andrian.gasper@cognitedata.com/Cognite/CDF-Aker-BP-Project/Extract-info-AAD/remote_repo/inso-dataops-incubator/integrating-aad-and-cdf-group-data/src/test.py", line 130, in apply_dml_to_data_model
    print(dm)
  File "/Users/andrian.gasper@cognitedata.com/Cognite/CDF-Aker-BP-Project/Extract-info-AAD/remote_repo/inso-dataops-incubator/integrating-aad-and-cdf-group-data/.venv/lib/python3.11/site-packages/cognite/client/data_classes/_base.py", line 126, in __str__
    item = convert_and_isoformat_time_attrs(self.dump(camel_case=False))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/andrian.gasper@cognitedata.com/Cognite/CDF-Aker-BP-Project/Extract-info-AAD/remote_repo/inso-dataops-incubator/integrating-aad-and-cdf-group-data/.venv/lib/python3.11/site-packages/cognite/client/utils/_time.py", line 209, in convert_and_isoformat_time_attrs
    return _convert_and_isoformat_time_attrs_in_dict(item)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/andrian.gasper@cognitedata.com/Cognite/CDF-Aker-BP-Project/Extract-info-AAD/remote_repo/inso-dataops-incubator/integrating-aad-and-cdf-group-data/.venv/lib/python3.11/site-packages/cognite/client/utils/_time.py", line 191, in _convert_and_isoformat_time_attrs_in_dict
    item[k] = ms_to_datetime(item[k]).isoformat(sep=" ", timespec="milliseconds")
              ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/andrian.gasper@cognitedata.com/Cognite/CDF-Aker-BP-Project/Extract-info-AAD/remote_repo/inso-dataops-incubator/integrating-aad-and-cdf-group-data/.venv/lib/python3.11/site-packages/cognite/client/utils/_time.py", line 97, in ms_to_datetime
    if not (MIN_TIMESTAMP_MS <= ms <= MAX_TIMESTAMP_MS):
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: '<=' not supported between instances of 'int' and 'str'
```




Discussion:

Resolving patch:
diff --git a/cognite/client/_version.py b/cognite/client/_version.py
--- a/cognite/client/_version.py
+++ b/cognite/client/_version.py
@@ -1,4 +1,4 @@
 from __future__ import annotations
 
-__version__ = "7.20.0"
+__version__ = "7.20.1"
 __api_subversion__ = "V20220125"
diff --git a/cognite/client/data_classes/data_modeling/graphql.py b/cognite/client/data_classes/data_modeling/graphql.py
--- a/cognite/client/data_classes/data_modeling/graphql.py
+++ b/cognite/client/data_classes/data_modeling/graphql.py
@@ -7,6 +7,7 @@
 
 from cognite.client.data_classes._base import CogniteObject
 from cognite.client.data_classes.data_modeling.ids import DataModelId
+from cognite.client.utils import _json
 
 if TYPE_CHECKING:
     from cognite.client import CogniteClient
@@ -19,8 +20,11 @@ class DMLApplyResult(CogniteObject):
     version: str
     name: str | None
     description: str | None
-    created_time: int
-    last_updated_time: int
+    created_time: str
+    last_updated_time: str
+
+    def __str__(self) -> str:
+        return _json.dumps(self.dump(camel_case=False), indent=4)
 
     def as_id(self) -> DataModelId:
         return DataModelId(


Test patch:
diff --git a/tests/tests_unit/test_data_classes/test_data_models/test_graphql.py b/tests/tests_unit/test_data_classes/test_data_models/test_graphql.py
new file mode 100644
--- /dev/null
+++ b/tests/tests_unit/test_data_classes/test_data_models/test_graphql.py
@@ -0,0 +1,16 @@
+from cognite.client.data_classes.data_modeling.graphql import DMLApplyResult
+
+
+def test_dmlapplyresult_is_str_convertable() -> None:
+    # Bug prior to 7.20.1 would try to convert non-integer timestamps 'created_time' and 'last_updated_time'
+    # from millis since epoch and fail (already string-formatted nicely):
+    res = DMLApplyResult(
+        space="test-space",
+        external_id="test-xid",
+        version="v1",
+        name="foo",
+        description="bar",
+        created_time="2024-02-18T22:49:54.932Z",
+        last_updated_time="2024-02-18T22:55:20.660Z",
+    )
+    assert str(res)