From 51c998425b741d89e24c5d08dfb0b733b9cf786d Mon Sep 17 00:00:00 2001 From: Nikolay Cheremnov Date: Fri, 8 Dec 2023 15:45:45 +0700 Subject: [PATCH] Add insight schemas API --- HISTORY.md | 3 +- cybsi/cloud/client.py | 11 + cybsi/cloud/insight/__init__.py | 11 + cybsi/cloud/insight/api.py | 20 ++ cybsi/cloud/insight/schemas.py | 251 ++++++++++++++++++ docs/api.rst | 7 + docs/index.rst | 1 + docs/user/enrichment.rst | 23 ++ docs/user/quickstart.rst | 10 + examples/get_enrichment_results_schema.py | 15 ++ .../get_enrichment_results_schemas_chained.py | 14 + 11 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 cybsi/cloud/insight/__init__.py create mode 100644 cybsi/cloud/insight/api.py create mode 100644 cybsi/cloud/insight/schemas.py create mode 100644 docs/user/enrichment.rst create mode 100644 examples/get_enrichment_results_schema.py create mode 100644 examples/get_enrichment_results_schemas_chained.py diff --git a/HISTORY.md b/HISTORY.md index f263966..871962c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,7 +4,8 @@ Release History 1.1.0 (2023-12-08) ------------------ -- Add api-key limits +- Add api-key limits +- Add insight schemas API 1.0.8 (2023-12-06) ------------------ diff --git a/cybsi/cloud/client.py b/cybsi/cloud/client.py index aa0b1a3..54e4f10 100644 --- a/cybsi/cloud/client.py +++ b/cybsi/cloud/client.py @@ -1,5 +1,6 @@ from .auth import APIKeyAuth, AuthAPI from .client_config import Config +from .insight.api import InsightAPI, InsightAsyncAPI from .internal import AsyncHTTPConnector, HTTPConnector from .iocean import IOCeanAPI, IOCeanAsyncAPI @@ -77,6 +78,11 @@ def iocean(self) -> IOCeanAPI: """IOCean API handle.""" return IOCeanAPI(self._connector) + @property + def insight(self) -> InsightAPI: + """Insight API handle.""" + return InsightAPI(self._connector) + class AsyncClient: """The asynchronous analog of :class:`Client`. @@ -120,3 +126,8 @@ async def aclose(self) -> None: def iocean(self) -> IOCeanAsyncAPI: """IOCean asynchronous API handle.""" return IOCeanAsyncAPI(self._connector) + + @property + def insight(self) -> InsightAsyncAPI: + """Insight asynchronous API handle.""" + return InsightAsyncAPI(self._connector) diff --git a/cybsi/cloud/insight/__init__.py b/cybsi/cloud/insight/__init__.py new file mode 100644 index 0000000..6f575fb --- /dev/null +++ b/cybsi/cloud/insight/__init__.py @@ -0,0 +1,11 @@ +"""Use this section of API to access Insight schemas. +""" + +from .api import InsightAPI, InsightAsyncAPI +from .schemas import ( + SchemaAPI, + SchemaAsyncAPI, + SchemaView, + SchemaCommonView, + SchemaRegistrationView, +) diff --git a/cybsi/cloud/insight/api.py b/cybsi/cloud/insight/api.py new file mode 100644 index 0000000..cd22991 --- /dev/null +++ b/cybsi/cloud/insight/api.py @@ -0,0 +1,20 @@ +from ..internal import BaseAPI, BaseAsyncAPI +from .schemas import SchemaAPI, SchemaAsyncAPI + + +class InsightAPI(BaseAPI): + """Insight API.""" + + @property + def schemas(self) -> SchemaAPI: + """Get Insight schemas handle.""" + return SchemaAPI(self._connector) + + +class InsightAsyncAPI(BaseAsyncAPI): + """Insight asynchronous API.""" + + @property + def schemas(self) -> SchemaAsyncAPI: + """Schemas asynchronous API handle.""" + return SchemaAsyncAPI(self._connector) diff --git a/cybsi/cloud/insight/schemas.py b/cybsi/cloud/insight/schemas.py new file mode 100644 index 0000000..dae37e1 --- /dev/null +++ b/cybsi/cloud/insight/schemas.py @@ -0,0 +1,251 @@ +from typing import Optional + +from ..api import Tag +from ..internal import BaseAPI, BaseAsyncAPI, JsonObject, JsonObjectView +from ..pagination import AsyncPage, Cursor, Page +from ..view import _TaggedView + +_PATH = "/insight/schemas" + + +class SchemaAPI(BaseAPI): + """Schema API.""" + + def register(self, schema: JsonObject) -> "SchemaRegistrationView": + """Register an enrichment task result object schema. + + Note: + Calls `POST /insight/schemas`. + Args: + schema: JSON schema of the enrichment task result object. + See :ref:`enrichment_result_schemas` + for information about schema structure. + Returns: + Schema registration view. + Raises: + :class:`~cybsi.cloud.error.InvalidRequestError`: + Provided values are invalid (see form value requirements). + :class:`~cybsi.cloud.error.ConflictError`: Form contains conflict errors. + Note: + Conflict error codes specific for this method: + * :attr:`~cybsi.cloud.error.ConflictErrorCodes.DuplicateSchema` + """ + resp = self._connector.do_post(path=_PATH, json=schema) + return SchemaRegistrationView(resp.json()) + + def update( + self, + *, + schema_id: str, + tag: Tag, + schema: JsonObject, + ) -> None: + """Update the enrichment task result object schema. + + Note: + Calls `PUT /insight/schemas/{schema_id}`. + Args: + schema_id: URL friendly string, uniquely identifies json schema. + tag: :attr:`SchemaView.tag` value. Use :meth:`view` to retrieve it. + schema: JSON schema of the object. See :ref:`enrichment_result_schemas` + for information about schema structure. + Raises: + :class:`~cybsi.cloud.error.InvalidRequestError`: + Provided values are invalid (see form value requirements). + :class:`~cybsi.cloud.error.SemanticError`: Form contains logic errors. + :class:`~cybsi.cloud.error.ResourceModifiedError`: + Object schema changed since last request. Update tag and retry. + :class:`~cybsi.cloud.error.NotFoundError`: Object schema not found. + Note: + Semantic error codes specific for this method: + * :attr:`~cybsi.cloud.error.SemanticErrorCodes.InvalidSchemaID` + """ + + path = f"{_PATH}/{schema_id}" + self._connector.do_put(path=path, tag=tag, json=schema) + + def view(self, schema_id: str) -> "SchemaView": + """Get the enrichment task result object schema view. + + Note: + Calls `GET /insight/schemas/{schema_id}`. + Args: + schema_id: URL friendly string, uniquely identifies json schema. + Returns: + Schema view. + Raises: + :class:`~cybsi.cloud.error.NotFoundError`: Object schema not found. + """ + + path = f"{_PATH}/{schema_id}" + resp = self._connector.do_get(path=path) + return SchemaView(resp) + + def filter( + self, + *, + cursor: Optional[Cursor] = None, + limit: Optional[int] = None, + ) -> Page["SchemaCommonView"]: + """Get an enrichment task result object schemas filtration list. + + Note: + Calls `GET /insight/schemas`. + Args: + cursor: Page cursor. + limit: Page limit. + Returns: + Page with schema common views and next page cursor. + Raises: + :class:`~cybsi.cloud.error.InvalidRequestError`: + Provided values are invalid (see form value requirements). + """ + + params: JsonObject = {} + if cursor is not None: + params["cursor"] = str(cursor) + if limit is not None: + params["limit"] = limit + resp = self._connector.do_get(path=_PATH, params=params) + return Page(self._connector.do_get, resp, SchemaCommonView) + + +class SchemaAsyncAPI(BaseAsyncAPI): + """Schema asynchronous API.""" + + async def register(self, schema: JsonObject) -> "SchemaRegistrationView": + """Register an enrichment task result object schema. + + Note: + Calls `POST /insight/schemas`. + Args: + schema: JSON schema of the object. See :ref:`enrichment_result_schemas` + for information about schema structure. + Returns: + Schema registration view. + Raises: + :class:`~cybsi.cloud.error.InvalidRequestError`: + Provided values are invalid (see form value requirements). + :class:`~cybsi.cloud.error.ConflictError`: Form contains conflict errors. + Note: + Conflict error codes specific for this method: + * :attr:`~cybsi.cloud.error.ConflictErrorCodes.DuplicateSchema` + """ + resp = await self._connector.do_post(path=_PATH, json=schema) + return SchemaRegistrationView(resp.json()) + + async def update( + self, + *, + schema_id: str, + tag: Tag, + schema: JsonObject, + ) -> None: + """Update the enrichment task result object schema. + + Note: + Calls `PUT /insight/schemas/{schema_id}`. + Args: + schema_id: URL friendly string, uniquely identifies json schema. + tag: :attr:`SchemaView.tag` value. Use :meth:`view` to retrieve it. + schema: JSON schema of the object. See :ref:`enrichment_result_schemas` + for information about schema structure. + Raises: + :class:`~cybsi.cloud.error.InvalidRequestError`: + Provided values are invalid (see form value requirements). + :class:`~cybsi.cloud.error.SemanticError`: Form contains logic errors. + :class:`~cybsi.cloud.error.ResourceModifiedError`: + Object schema changed since last request. Update tag and retry. + :class:`~cybsi.cloud.error.NotFoundError`: Object schema not found. + Note: + Semantic error codes specific for this method: + * :attr:`~cybsi.cloud.error.SemanticErrorCodes.InvalidSchemaID` + """ + + path = f"{_PATH}/{schema_id}" + await self._connector.do_put(path=path, tag=tag, json=schema) + + async def view(self, schema_id: str) -> "SchemaView": + """Get the enrichment task result object schema view. + + Note: + Calls `GET /insight/schemas/{schema_id}`. + Args: + schema_id: URL friendly string, uniquely identifies json schema. + Returns: + Schema view. + Raises: + :class:`~cybsi.cloud.error.NotFoundError`: Object schema not found. + """ + + path = f"{_PATH}/{schema_id}" + resp = await self._connector.do_get(path=path) + return SchemaView(resp) + + async def filter( + self, + *, + cursor: Optional[Cursor] = None, + limit: Optional[int] = None, + ) -> AsyncPage["SchemaCommonView"]: + """Get an enrichment task result object schemas filtration list. + + Note: + Calls `GET /insight/schemas`. + Args: + cursor: Page cursor. + limit: Page limit. + Returns: + Page with schema common views and next page cursor. + Raises: + :class:`~cybsi.cloud.error.InvalidRequestError`: + Provided values are invalid (see form value requirements). + """ + + params: JsonObject = {} + if cursor is not None: + params["cursor"] = str(cursor) + if limit is not None: + params["limit"] = limit + resp = await self._connector.do_get(path=_PATH, params=params) + return AsyncPage(self._connector.do_get, resp, SchemaCommonView) + + +class SchemaRegistrationView(JsonObjectView): + """Schema registration view""" + + @property + def schema_id(self) -> str: + """URL friendly string, uniquely identifies json schema.""" + return self._get("schemaID") + + +class SchemaCommonView(JsonObjectView): + """Schema common view""" + + @property + def schema_id(self) -> str: + """URL friendly string, uniquely identifies json schema.""" + return self._get("schemaID") + + @property + def title(self) -> str: + """The human-readable name of the json schema.""" + return self._get("title") + + +class SchemaView(_TaggedView): + """Schema view""" + + @property + def schema_id(self) -> str: + """URL friendly string, uniquely identifies json schema.""" + return self._get("schemaID") + + @property + def schema(self) -> JsonObject: + """JSON schema of the enrichment task result object + + See :ref:`enrichment_result_schemas` for information about schema structure. + """ + return self.raw() diff --git a/docs/api.rst b/docs/api.rst index f6d5057..80456be 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -42,6 +42,13 @@ IOCean :imported-members: :inherited-members: +Insight +~~~~~~~ +.. automodule:: cybsi.cloud.insight + :members: + :imported-members: + :inherited-members: + Common views and data types ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: cybsi.cloud.Null diff --git a/docs/index.rst b/docs/index.rst index 4fd0a46..63ef34a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -36,6 +36,7 @@ instructions for getting the most out of Cybsi Cloud SDK. user/data-model user/advanced user/authentication + user/enrichment The API Documentation / Guide diff --git a/docs/user/enrichment.rst b/docs/user/enrichment.rst new file mode 100644 index 0000000..2e56873 --- /dev/null +++ b/docs/user/enrichment.rst @@ -0,0 +1,23 @@ +.. _insight: + +Cloud Enrichment Tasks +======================== + +.. _enrichment_result_schemas: + +Enrichment result object schemas +-------------------------------- + +You can make enrichment using object schema +that defines attribute composition of the objects and data types of the attributes +(see :ref:`data_model` for more information). + +To create enrichment tasks, you will need to specify the schema id of the objects that are the results of these tasks. + +In the example bellow we get list of enrichment result object schemas. + +.. literalinclude:: ../../examples/get_enrichment_results_schemas_chained.py + +And one more example of getting schema by ID. + +.. literalinclude:: ../../examples/get_enrichment_results_schema.py diff --git a/docs/user/quickstart.rst b/docs/user/quickstart.rst index 264d100..6b04895 100644 --- a/docs/user/quickstart.rst +++ b/docs/user/quickstart.rst @@ -36,3 +36,13 @@ Get object changes in the collection In the example below we get objects changes happened in the collection. .. literalinclude:: ../../examples/get_collection_objects_changes.py + +.. _working_with_tasks: + +Working with tasks +------------------ + +You also can working with enrichment tasks. Create tasks for enriching indicators and get their results. +The results of the enrichment are objects corresponding to a given schema. + +See :ref:`enrichment tasks examples ` for more information. \ No newline at end of file diff --git a/examples/get_enrichment_results_schema.py b/examples/get_enrichment_results_schema.py new file mode 100644 index 0000000..d730979 --- /dev/null +++ b/examples/get_enrichment_results_schema.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +from cybsi.cloud import Client, Config + +if __name__ == "__main__": + config = Config(api_key="the cryptic string") + + with Client(config) as client: + schema_id = "example-schema" + + # Retrieve schema. It describes all attributes of objects you can encounter + # in the result object of the enrichment task with this schema. + schema_view = client.insight.schemas.view(schema_id="example-schema") + + # Do something with the schema as SchemaView. + print(schema_view) diff --git a/examples/get_enrichment_results_schemas_chained.py b/examples/get_enrichment_results_schemas_chained.py new file mode 100644 index 0000000..448bbb1 --- /dev/null +++ b/examples/get_enrichment_results_schemas_chained.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +from cybsi.cloud import Client, Config +from cybsi.cloud.pagination import chain_pages + +if __name__ == "__main__": + config = Config(api_key="the cryptic string") + + with Client(config) as client: + # Retrieve first page of enrichment result object schemas. + start_page = client.insight.schemas.filter() + + for schema in chain_pages(start_page): + # Do something with the schema as SchemaCommonView. + print(schema)