Skip to content

Commit

Permalink
Add support for querying data models through graphql (#1548)
Browse files Browse the repository at this point in the history
  • Loading branch information
erlendvollset authored Dec 13, 2023
1 parent b20cfc9 commit 864e1e1
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ Changes are grouped as follows
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.

## [7.6.0] - 2023-12-13
### Added
- Support for querying data models through graphql. See `client.data_modeling.graphql.query`.

## [7.5.7] - 2023-12-12
### Fixed
- Certain combinations of `start`/`end` and `granularity` would cause `retrieve_dataframe_in_tz` to raise due to
Expand Down
30 changes: 30 additions & 0 deletions cognite/client/_api/data_modeling/graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from cognite.client.data_classes.data_modeling.graphql import DMLApplyResult
from cognite.client.data_classes.data_modeling.ids import DataModelId
from cognite.client.exceptions import CogniteGraphQLError, GraphQLErrorSpec
from cognite.client.utils._auxiliary import interpolate_and_url_encode


class DataModelingGraphQLAPI(APIClient):
Expand Down Expand Up @@ -133,3 +134,32 @@ def apply_dml(
query_name = "upsertGraphQlDmlVersion"
res = self._post_graphql(url_path="/dml/graphql", query_name=query_name, json=payload)
return DMLApplyResult.load(res[query_name]["result"])

def query(self, id: DataModelIdentifier, query: str, variables: dict[str, Any] | None = None) -> dict[str, Any]:
"""Execute a GraphQl query against a given data model.
Args:
id (DataModelIdentifier): The data model to query.
query (str): The query to issue.
variables (dict[str, Any] | None): An optional dict of variables to pass to the query.
Returns:
dict[str, Any]: The query result
Examples:
Execute a graphql query against a given data model::
>>> from cognite.client import CogniteClient
>>> c = CogniteClient()
>>> res = c.data_modeling.graphql.query(
... id=("mySpace", "myDataModel", "v1"),
... query="listThings { items { thingProperty } }",
... )
"""
dm_id = DataModelId.load(id)
endpoint = interpolate_and_url_encode(
"/userapis/spaces/{}/datamodels/{}/versions/{}/graphql", dm_id.space, dm_id.external_id, dm_id.version
)
res = self._post_graphql(url_path=endpoint, query_name="", json={"query": query, "variables": variables})
return res
2 changes: 1 addition & 1 deletion cognite/client/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __future__ import annotations

__version__ = "7.5.7"
__version__ = "7.6.0"
__api_subversion__ = "V20220125"
9 changes: 9 additions & 0 deletions cognite/client/data_classes/data_modeling/graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,12 @@ def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None =
created_time=resource["createdTime"],
last_updated_time=resource["lastUpdatedTime"],
)


@dataclass
class GraphQlQueryResult(CogniteObject):
items: list[dict[str, Any]]

@classmethod
def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> Self:
return cls(resource["items"])
4 changes: 4 additions & 0 deletions docs/source/data_modeling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,7 @@ GraphQL
Apply DML
^^^^^^^^^
.. automethod:: cognite.client._api.data_modeling.graphql.DataModelingGraphQLAPI.apply_dml

Execute GraphQl query
^^^^^^^^^^^^^^^^^^^^^
.. automethod:: cognite.client._api.data_modeling.graphql.DataModelingGraphQLAPI.query
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tool.poetry]
name = "cognite-sdk"

version = "7.5.7"
version = "7.6.0"
description = "Cognite Python SDK"
readme = "README.md"
documentation = "https://cognite-sdk-python.readthedocs-hosted.com"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
import pytest

from cognite.client import CogniteClient
from cognite.client.data_classes.data_modeling import DataModel, DataModelApply, DataModelId, Space
from cognite.client.data_classes.data_modeling import (
DataModel,
DataModelApply,
DataModelId,
Space,
)
from cognite.client.exceptions import CogniteGraphQLError


Expand All @@ -14,6 +19,15 @@ def data_model(cognite_client: CogniteClient, integration_test_space: Space) ->
)


@pytest.fixture(scope="session")
def data_model_for_query_test(cognite_client: CogniteClient, integration_test_space: Space) -> DataModel:
data_model = cognite_client.data_modeling.data_models.apply(
DataModelApply(integration_test_space.space, "DataModelForGraphQlQueryTest", "1")
)
cognite_client.data_modeling.graphql.apply_dml(data_model.as_id(), "type Thing { someProp: String! }")
return data_model


class TestDataModelingGraphQLAPI:
def test_apply_dml(self, cognite_client: CogniteClient, data_model: DataModel) -> None:
dml = "type SomeType { someProp: String! } type AnotherType { anotherProp: String! }"
Expand Down Expand Up @@ -64,3 +78,64 @@ def test_wipe_and_regenerate_dml(self, cognite_client: CogniteClient, data_model
}
"""
assert res.strip() == textwrap.dedent(expected).strip()

def test_query(self, cognite_client: CogniteClient, data_model_for_query_test: DataModel) -> None:
query = """
{
listThing {
items {
externalId
space
someProp
}
}
}
"""
res = cognite_client.data_modeling.graphql.query(data_model_for_query_test.as_id(), query)
assert res == {"listThing": {"items": []}}

def test_query_with_intent(self, cognite_client: CogniteClient, data_model_for_query_test: DataModel) -> None:
query = """
query MyQuery {
listThing {
items {
externalId
space
someProp
}
}
}
"""
res = cognite_client.data_modeling.graphql.query(data_model_for_query_test.as_id(), query)
assert res == {"listThing": {"items": []}}

def test_query_with_variables(self, cognite_client: CogniteClient, data_model_for_query_test: DataModel) -> None:
query = """
query MyQuery($first: Int) {
listThing(first: $first) {
items {
externalId
space
someProp
}
}
}
"""
res = cognite_client.data_modeling.graphql.query(
data_model_for_query_test.as_id(), query, variables={"first": 10}
)
assert res == {"listThing": {"items": []}}

def test_query_with_error(self, cognite_client: CogniteClient, data_model_for_query_test: DataModel) -> None:
query = """
{
listThing {
items {
i_dont_exist
}
}
}
"""

with pytest.raises(CogniteGraphQLError, match="Field 'i_dont_exist' in type 'Thing' is undefined"):
cognite_client.data_modeling.graphql.query(data_model_for_query_test.as_id(), query)

0 comments on commit 864e1e1

Please sign in to comment.