-
Notifications
You must be signed in to change notification settings - Fork 6
Add RecordsAPI with write operations #2501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,59 @@ | ||||||
| from collections.abc import Sequence | ||||||
|
|
||||||
| from cognite_toolkit._cdf_tk.client.cdf_client import CDFResourceAPI, PagedResponse | ||||||
| from cognite_toolkit._cdf_tk.client.cdf_client.api import Endpoint | ||||||
| from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, ItemsSuccessResponse, SuccessResponse | ||||||
| from cognite_toolkit._cdf_tk.client.resource_classes.records import RecordIdentifier, RecordRequest, RecordResponse | ||||||
|
|
||||||
|
|
||||||
| class RecordsAPI(CDFResourceAPI[RecordIdentifier, RecordRequest, RecordResponse]): | ||||||
| """API for managing records in CDF streams. | ||||||
|
|
||||||
| Records are scoped to a stream, so the stream_id is passed to each method, | ||||||
| following the same pattern as RawTablesAPI with db_name. | ||||||
| """ | ||||||
|
|
||||||
| def __init__(self, http_client: HTTPClient) -> None: | ||||||
| super().__init__( | ||||||
| http_client=http_client, | ||||||
| method_endpoint_map={ | ||||||
| "create": Endpoint(method="POST", path="/streams/{streamId}/records", item_limit=1000), | ||||||
| "upsert": Endpoint(method="POST", path="/streams/{streamId}/records/upsert", item_limit=1000), | ||||||
| "delete": Endpoint(method="POST", path="/streams/{streamId}/records/delete", item_limit=1000), | ||||||
| }, | ||||||
| ) | ||||||
|
|
||||||
| def _validate_page_response( | ||||||
| self, response: SuccessResponse | ItemsSuccessResponse | ||||||
| ) -> PagedResponse[RecordResponse]: | ||||||
| return PagedResponse[RecordResponse].model_validate_json(response.body) | ||||||
|
|
||||||
| def ingest(self, stream_id: str, items: Sequence[RecordRequest]) -> None: | ||||||
| """Ingest records into a stream. | ||||||
|
|
||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd lean towards this not really being a issue due to the worst case scenario being a malformed / invalid API request that would be rejected anyways |
||||||
| Args: | ||||||
| stream_id: The external ID of the stream to ingest records into. | ||||||
| items: Sequence of RecordRequest items to ingest. | ||||||
| """ | ||||||
| endpoint = f"/streams/{stream_id}/records" | ||||||
| self._request_no_response(items, "create", endpoint=endpoint) | ||||||
|
|
||||||
| def upsert(self, stream_id: str, items: Sequence[RecordRequest]) -> None: | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I try to use create,retrieve, delete, update as far as possible even for upsert endpoints.
Suggested change
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets move stream_id innto the RecordRequest, see how it is solved for RawAPI |
||||||
| """Upsert records into a stream (create or update). | ||||||
|
|
||||||
Magssch marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| Args: | ||||||
| stream_id: The external ID of the stream to upsert records into. | ||||||
| items: Sequence of RecordRequest items to upsert. | ||||||
| """ | ||||||
| endpoint = f"/streams/{stream_id}/records/upsert" | ||||||
| self._request_no_response(items, "upsert", endpoint=endpoint) | ||||||
|
|
||||||
| def delete(self, stream_id: str, items: Sequence[RecordIdentifier]) -> None: | ||||||
|
Comment on lines
+41
to
+51
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, move stream_id into RecordIdentifier. |
||||||
| """Delete records from a stream. | ||||||
|
|
||||||
Magssch marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| Args: | ||||||
| stream_id: The external ID of the stream to delete records from. | ||||||
| items: Sequence of RecordIdentifier objects to delete. | ||||||
| """ | ||||||
| endpoint = f"/streams/{stream_id}/records/delete" | ||||||
| self._request_no_response(items, "delete", endpoint=endpoint) | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| from pydantic import JsonValue | ||
|
|
||
| from cognite_toolkit._cdf_tk.client._resource_base import BaseModelObject, Identifier, RequestResource, ResponseResource | ||
|
|
||
| from .data_modeling._references import ContainerReference | ||
|
|
||
|
|
||
| class RecordIdentifier(Identifier): | ||
| space: str | ||
| external_id: str | ||
|
|
||
| def __str__(self) -> str: | ||
| return f"Record({self.space}, {self.external_id})" | ||
|
|
||
|
|
||
| class RecordSource(BaseModelObject): | ||
| source: ContainerReference | ||
| properties: dict[str, JsonValue] | ||
|
|
||
|
|
||
| class RecordRequest(RequestResource): | ||
| """A record request resource for ingesting into a stream.""" | ||
|
|
||
| space: str | ||
| external_id: str | ||
| sources: list[RecordSource] | ||
|
|
||
| def as_id(self) -> RecordIdentifier: | ||
| return RecordIdentifier(space=self.space, external_id=self.external_id) | ||
|
|
||
|
|
||
| class RecordResponse(ResponseResource[RecordRequest]): | ||
| """A record response from the API.""" | ||
|
|
||
| space: str | ||
| external_id: str | ||
| properties: dict[str, dict[str, JsonValue]] | None = None | ||
|
|
||
| def as_request_resource(self) -> RecordRequest: | ||
| raise NotImplementedError("Converting RecordResponse to RecordRequest is not yet supported.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try to standardize