diff --git a/docs/docs/guide/9-alerts.md b/docs/docs/guide/9-alerts.md index 67c6d877..2adbb61f 100644 --- a/docs/docs/guide/9-alerts.md +++ b/docs/docs/guide/9-alerts.md @@ -24,4 +24,4 @@ Consider configuring a "no queries submitted" alert to monitor system health. If ## Alert Mediums -Groundlight supports the following alerts via Email and Text Message (SMS), with webhook support coming soon. +Groundlight supports the following alerts via Email, Text Message (SMS), and Webhooks. diff --git a/generated/.openapi-generator/FILES b/generated/.openapi-generator/FILES index 10087712..53c314c1 100644 --- a/generated/.openapi-generator/FILES +++ b/generated/.openapi-generator/FILES @@ -42,6 +42,8 @@ docs/PaginatedDetectorList.md docs/PaginatedImageQueryList.md docs/PaginatedRuleList.md docs/PatchedDetectorRequest.md +docs/PayloadTemplate.md +docs/PayloadTemplateRequest.md docs/ROI.md docs/ROIRequest.md docs/ResultTypeEnum.md @@ -107,6 +109,8 @@ groundlight_openapi_client/model/paginated_detector_list.py groundlight_openapi_client/model/paginated_image_query_list.py groundlight_openapi_client/model/paginated_rule_list.py groundlight_openapi_client/model/patched_detector_request.py +groundlight_openapi_client/model/payload_template.py +groundlight_openapi_client/model/payload_template_request.py groundlight_openapi_client/model/result_type_enum.py groundlight_openapi_client/model/roi.py groundlight_openapi_client/model/roi_request.py diff --git a/generated/README.md b/generated/README.md index 62304432..32c53279 100644 --- a/generated/README.md +++ b/generated/README.md @@ -94,6 +94,7 @@ rule_request = RuleRequest( WebhookActionRequest( url="url_example", include_image=True, + payload_template=None, ), ], ) # RuleRequest | @@ -171,6 +172,8 @@ Class | Method | HTTP request | Description - [PaginatedImageQueryList](docs/PaginatedImageQueryList.md) - [PaginatedRuleList](docs/PaginatedRuleList.md) - [PatchedDetectorRequest](docs/PatchedDetectorRequest.md) + - [PayloadTemplate](docs/PayloadTemplate.md) + - [PayloadTemplateRequest](docs/PayloadTemplateRequest.md) - [ROI](docs/ROI.md) - [ROIRequest](docs/ROIRequest.md) - [ResultTypeEnum](docs/ResultTypeEnum.md) diff --git a/generated/docs/ActionsApi.md b/generated/docs/ActionsApi.md index 62acb205..9bf52c09 100644 --- a/generated/docs/ActionsApi.md +++ b/generated/docs/ActionsApi.md @@ -69,6 +69,7 @@ with groundlight_openapi_client.ApiClient(configuration) as api_client: WebhookActionRequest( url="url_example", include_image=True, + payload_template=None, ), ], ) # RuleRequest | diff --git a/generated/docs/PayloadTemplate.md b/generated/docs/PayloadTemplate.md new file mode 100644 index 00000000..a2c716c0 --- /dev/null +++ b/generated/docs/PayloadTemplate.md @@ -0,0 +1,12 @@ +# PayloadTemplate + + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**template** | **str** | | +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/generated/docs/PayloadTemplateRequest.md b/generated/docs/PayloadTemplateRequest.md new file mode 100644 index 00000000..ff584274 --- /dev/null +++ b/generated/docs/PayloadTemplateRequest.md @@ -0,0 +1,12 @@ +# PayloadTemplateRequest + + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**template** | **str** | | +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/generated/docs/WebhookAction.md b/generated/docs/WebhookAction.md index 639f1f7a..a1f6cf2b 100644 --- a/generated/docs/WebhookAction.md +++ b/generated/docs/WebhookAction.md @@ -6,6 +6,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **url** | **str** | | **include_image** | **bool** | | [optional] +**payload_template** | **bool, date, datetime, dict, float, int, list, str, none_type** | | [optional] **any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/generated/docs/WebhookActionRequest.md b/generated/docs/WebhookActionRequest.md index 411d192d..e3b1c59e 100644 --- a/generated/docs/WebhookActionRequest.md +++ b/generated/docs/WebhookActionRequest.md @@ -6,6 +6,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **url** | **str** | | **include_image** | **bool** | | [optional] +**payload_template** | **bool, date, datetime, dict, float, int, list, str, none_type** | | [optional] **any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/generated/groundlight_openapi_client/model/payload_template.py b/generated/groundlight_openapi_client/model/payload_template.py new file mode 100644 index 00000000..2cf74e03 --- /dev/null +++ b/generated/groundlight_openapi_client/model/payload_template.py @@ -0,0 +1,274 @@ +""" + Groundlight API + + Groundlight makes it simple to understand images. You can easily create computer vision detectors just by describing what you want to know using natural language. # noqa: E501 + + The version of the OpenAPI document: 0.18.2 + Contact: support@groundlight.ai + Generated by: https://openapi-generator.tech +""" + +import re # noqa: F401 +import sys # noqa: F401 + +from groundlight_openapi_client.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, + OpenApiModel, +) +from groundlight_openapi_client.exceptions import ApiAttributeError + + +class PayloadTemplate(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = {} + + validations = {} + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return ( + bool, + date, + datetime, + dict, + float, + int, + list, + str, + none_type, + ) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + "template": (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + attribute_map = { + "template": "template", # noqa: E501 + } + + read_only_vars = {} + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, template, *args, **kwargs): # noqa: E501 + """PayloadTemplate - a model defined in OpenAPI + + Args: + template (str): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop("_check_type", True) + _spec_property_naming = kwargs.pop("_spec_property_naming", False) + _path_to_item = kwargs.pop("_path_to_item", ()) + _configuration = kwargs.pop("_configuration", None) + _visited_composed_classes = kwargs.pop("_visited_composed_classes", ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." + % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.template = template + for var_name, var_value in kwargs.items(): + if ( + var_name not in self.attribute_map + and self._configuration is not None + and self._configuration.discard_unknown_keys + and self.additional_properties_type is None + ): + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + "_data_store", + "_check_type", + "_spec_property_naming", + "_path_to_item", + "_configuration", + "_visited_composed_classes", + ]) + + @convert_js_args_to_python_args + def __init__(self, template, *args, **kwargs): # noqa: E501 + """PayloadTemplate - a model defined in OpenAPI + + Args: + template (str): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop("_check_type", True) + _spec_property_naming = kwargs.pop("_spec_property_naming", False) + _path_to_item = kwargs.pop("_path_to_item", ()) + _configuration = kwargs.pop("_configuration", None) + _visited_composed_classes = kwargs.pop("_visited_composed_classes", ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." + % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.template = template + for var_name, var_value in kwargs.items(): + if ( + var_name not in self.attribute_map + and self._configuration is not None + and self._configuration.discard_unknown_keys + and self.additional_properties_type is None + ): + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError( + f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + "class with read only attributes." + ) diff --git a/generated/groundlight_openapi_client/model/payload_template_request.py b/generated/groundlight_openapi_client/model/payload_template_request.py new file mode 100644 index 00000000..7e34645e --- /dev/null +++ b/generated/groundlight_openapi_client/model/payload_template_request.py @@ -0,0 +1,278 @@ +""" + Groundlight API + + Groundlight makes it simple to understand images. You can easily create computer vision detectors just by describing what you want to know using natural language. # noqa: E501 + + The version of the OpenAPI document: 0.18.2 + Contact: support@groundlight.ai + Generated by: https://openapi-generator.tech +""" + +import re # noqa: F401 +import sys # noqa: F401 + +from groundlight_openapi_client.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, + OpenApiModel, +) +from groundlight_openapi_client.exceptions import ApiAttributeError + + +class PayloadTemplateRequest(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = {} + + validations = { + ("template",): { + "min_length": 1, + }, + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return ( + bool, + date, + datetime, + dict, + float, + int, + list, + str, + none_type, + ) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + "template": (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + attribute_map = { + "template": "template", # noqa: E501 + } + + read_only_vars = {} + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, template, *args, **kwargs): # noqa: E501 + """PayloadTemplateRequest - a model defined in OpenAPI + + Args: + template (str): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop("_check_type", True) + _spec_property_naming = kwargs.pop("_spec_property_naming", False) + _path_to_item = kwargs.pop("_path_to_item", ()) + _configuration = kwargs.pop("_configuration", None) + _visited_composed_classes = kwargs.pop("_visited_composed_classes", ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." + % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.template = template + for var_name, var_value in kwargs.items(): + if ( + var_name not in self.attribute_map + and self._configuration is not None + and self._configuration.discard_unknown_keys + and self.additional_properties_type is None + ): + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + "_data_store", + "_check_type", + "_spec_property_naming", + "_path_to_item", + "_configuration", + "_visited_composed_classes", + ]) + + @convert_js_args_to_python_args + def __init__(self, template, *args, **kwargs): # noqa: E501 + """PayloadTemplateRequest - a model defined in OpenAPI + + Args: + template (str): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop("_check_type", True) + _spec_property_naming = kwargs.pop("_spec_property_naming", False) + _path_to_item = kwargs.pop("_path_to_item", ()) + _configuration = kwargs.pop("_configuration", None) + _visited_composed_classes = kwargs.pop("_visited_composed_classes", ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." + % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.template = template + for var_name, var_value in kwargs.items(): + if ( + var_name not in self.attribute_map + and self._configuration is not None + and self._configuration.discard_unknown_keys + and self.additional_properties_type is None + ): + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError( + f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + "class with read only attributes." + ) diff --git a/generated/groundlight_openapi_client/model/webhook_action.py b/generated/groundlight_openapi_client/model/webhook_action.py index 826b3312..d272aad8 100644 --- a/generated/groundlight_openapi_client/model/webhook_action.py +++ b/generated/groundlight_openapi_client/model/webhook_action.py @@ -29,6 +29,12 @@ from groundlight_openapi_client.exceptions import ApiAttributeError +def lazy_import(): + from groundlight_openapi_client.model.payload_template import PayloadTemplate + + globals()["PayloadTemplate"] = PayloadTemplate + + class WebhookAction(ModelNormal): """NOTE: This class is auto generated by OpenAPI Generator. Ref: https://openapi-generator.tech @@ -67,6 +73,7 @@ def additional_properties_type(): This must be a method because a model may have properties that are of type self, this must run after the class is loaded """ + lazy_import() return ( bool, date, @@ -91,9 +98,21 @@ def openapi_types(): openapi_types (dict): The key is attribute name and the value is attribute type. """ + lazy_import() return { "url": (str,), # noqa: E501 "include_image": (bool,), # noqa: E501 + "payload_template": ( + bool, + date, + datetime, + dict, + float, + int, + list, + str, + none_type, + ), # noqa: E501 } @cached_property @@ -103,6 +122,7 @@ def discriminator(): attribute_map = { "url": "url", # noqa: E501 "include_image": "include_image", # noqa: E501 + "payload_template": "payload_template", # noqa: E501 } read_only_vars = {} @@ -149,6 +169,7 @@ def _from_openapi_data(cls, url, *args, **kwargs): # noqa: E501 through its discriminator because we passed in _visited_composed_classes = (Animal,) include_image (bool): [optional] # noqa: E501 + payload_template (bool, date, datetime, dict, float, int, list, str, none_type): [optional] # noqa: E501 """ _check_type = kwargs.pop("_check_type", True) @@ -238,6 +259,7 @@ def __init__(self, url, *args, **kwargs): # noqa: E501 through its discriminator because we passed in _visited_composed_classes = (Animal,) include_image (bool): [optional] # noqa: E501 + payload_template (bool, date, datetime, dict, float, int, list, str, none_type): [optional] # noqa: E501 """ _check_type = kwargs.pop("_check_type", True) diff --git a/generated/groundlight_openapi_client/model/webhook_action_request.py b/generated/groundlight_openapi_client/model/webhook_action_request.py index 9990e3e0..053d1a77 100644 --- a/generated/groundlight_openapi_client/model/webhook_action_request.py +++ b/generated/groundlight_openapi_client/model/webhook_action_request.py @@ -29,6 +29,12 @@ from groundlight_openapi_client.exceptions import ApiAttributeError +def lazy_import(): + from groundlight_openapi_client.model.payload_template_request import PayloadTemplateRequest + + globals()["PayloadTemplateRequest"] = PayloadTemplateRequest + + class WebhookActionRequest(ModelNormal): """NOTE: This class is auto generated by OpenAPI Generator. Ref: https://openapi-generator.tech @@ -68,6 +74,7 @@ def additional_properties_type(): This must be a method because a model may have properties that are of type self, this must run after the class is loaded """ + lazy_import() return ( bool, date, @@ -92,9 +99,21 @@ def openapi_types(): openapi_types (dict): The key is attribute name and the value is attribute type. """ + lazy_import() return { "url": (str,), # noqa: E501 "include_image": (bool,), # noqa: E501 + "payload_template": ( + bool, + date, + datetime, + dict, + float, + int, + list, + str, + none_type, + ), # noqa: E501 } @cached_property @@ -104,6 +123,7 @@ def discriminator(): attribute_map = { "url": "url", # noqa: E501 "include_image": "include_image", # noqa: E501 + "payload_template": "payload_template", # noqa: E501 } read_only_vars = {} @@ -150,6 +170,7 @@ def _from_openapi_data(cls, url, *args, **kwargs): # noqa: E501 through its discriminator because we passed in _visited_composed_classes = (Animal,) include_image (bool): [optional] # noqa: E501 + payload_template (bool, date, datetime, dict, float, int, list, str, none_type): [optional] # noqa: E501 """ _check_type = kwargs.pop("_check_type", True) @@ -239,6 +260,7 @@ def __init__(self, url, *args, **kwargs): # noqa: E501 through its discriminator because we passed in _visited_composed_classes = (Animal,) include_image (bool): [optional] # noqa: E501 + payload_template (bool, date, datetime, dict, float, int, list, str, none_type): [optional] # noqa: E501 """ _check_type = kwargs.pop("_check_type", True) diff --git a/generated/groundlight_openapi_client/models/__init__.py b/generated/groundlight_openapi_client/models/__init__.py index 6a064903..dc3f8705 100644 --- a/generated/groundlight_openapi_client/models/__init__.py +++ b/generated/groundlight_openapi_client/models/__init__.py @@ -43,6 +43,8 @@ from groundlight_openapi_client.model.paginated_image_query_list import PaginatedImageQueryList from groundlight_openapi_client.model.paginated_rule_list import PaginatedRuleList from groundlight_openapi_client.model.patched_detector_request import PatchedDetectorRequest +from groundlight_openapi_client.model.payload_template import PayloadTemplate +from groundlight_openapi_client.model.payload_template_request import PayloadTemplateRequest from groundlight_openapi_client.model.roi import ROI from groundlight_openapi_client.model.roi_request import ROIRequest from groundlight_openapi_client.model.result_type_enum import ResultTypeEnum diff --git a/generated/model.py b/generated/model.py index e767d70b..1f301199 100644 --- a/generated/model.py +++ b/generated/model.py @@ -109,6 +109,14 @@ class NoteRequest(BaseModel): image: Optional[bytes] = None +class PayloadTemplate(BaseModel): + template: str + + +class PayloadTemplateRequest(BaseModel): + template: constr(min_length=1) + + class ROI(BaseModel): """ Mixin for serializers to handle data in the StrictBaseModel format @@ -198,11 +206,13 @@ class VerbEnum(str, Enum): class WebhookAction(BaseModel): url: AnyUrl include_image: Optional[bool] = None + payload_template: Optional[PayloadTemplate] = None class WebhookActionRequest(BaseModel): url: AnyUrl include_image: Optional[bool] = None + payload_template: Optional[PayloadTemplateRequest] = None class Source(str, Enum): diff --git a/generated/test/test_payload_template.py b/generated/test/test_payload_template.py new file mode 100644 index 00000000..7d4af2fd --- /dev/null +++ b/generated/test/test_payload_template.py @@ -0,0 +1,35 @@ +""" + Groundlight API + + Groundlight makes it simple to understand images. You can easily create computer vision detectors just by describing what you want to know using natural language. # noqa: E501 + + The version of the OpenAPI document: 0.18.2 + Contact: support@groundlight.ai + Generated by: https://openapi-generator.tech +""" + +import sys +import unittest + +import groundlight_openapi_client +from groundlight_openapi_client.model.payload_template import PayloadTemplate + + +class TestPayloadTemplate(unittest.TestCase): + """PayloadTemplate unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testPayloadTemplate(self): + """Test PayloadTemplate""" + # FIXME: construct object with mandatory attributes with example values + # model = PayloadTemplate() # noqa: E501 + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/generated/test/test_payload_template_request.py b/generated/test/test_payload_template_request.py new file mode 100644 index 00000000..312fc15c --- /dev/null +++ b/generated/test/test_payload_template_request.py @@ -0,0 +1,35 @@ +""" + Groundlight API + + Groundlight makes it simple to understand images. You can easily create computer vision detectors just by describing what you want to know using natural language. # noqa: E501 + + The version of the OpenAPI document: 0.18.2 + Contact: support@groundlight.ai + Generated by: https://openapi-generator.tech +""" + +import sys +import unittest + +import groundlight_openapi_client +from groundlight_openapi_client.model.payload_template_request import PayloadTemplateRequest + + +class TestPayloadTemplateRequest(unittest.TestCase): + """PayloadTemplateRequest unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testPayloadTemplateRequest(self): + """Test PayloadTemplateRequest""" + # FIXME: construct object with mandatory attributes with example values + # model = PayloadTemplateRequest() # noqa: E501 + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/spec/public-api.yaml b/spec/public-api.yaml index 9476a17c..14687cf5 100644 --- a/spec/public-api.yaml +++ b/spec/public-api.yaml @@ -1237,6 +1237,21 @@ components: * `STANDARD` - STANDARD * `NO_HUMAN_LABELING` - NO_HUMAN_LABELING x-internal: true + PayloadTemplate: + type: object + properties: + template: + type: string + required: + - template + PayloadTemplateRequest: + type: object + properties: + template: + type: string + minLength: 1 + required: + - template ROI: type: object description: Mixin for serializers to handle data in the StrictBaseModel format @@ -1421,6 +1436,10 @@ components: maxLength: 200 include_image: type: boolean + payload_template: + allOf: + - $ref: '#/components/schemas/PayloadTemplate' + nullable: true required: - url WebhookActionRequest: @@ -1433,6 +1452,10 @@ components: maxLength: 200 include_image: type: boolean + payload_template: + allOf: + - $ref: '#/components/schemas/PayloadTemplateRequest' + nullable: true required: - url BinaryClassificationResult: diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index b9fecac2..ca297f10 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -1,3 +1,4 @@ +# pylint: disable=too-many-lines """ experimental_api.py @@ -26,6 +27,7 @@ from groundlight_openapi_client.model.escalation_type_enum import EscalationTypeEnum from groundlight_openapi_client.model.multi_class_mode_configuration import MultiClassModeConfiguration from groundlight_openapi_client.model.patched_detector_request import PatchedDetectorRequest +from groundlight_openapi_client.model.payload_template_request import PayloadTemplateRequest from groundlight_openapi_client.model.rule_request import RuleRequest from groundlight_openapi_client.model.status_enum import StatusEnum from groundlight_openapi_client.model.verb_enum import VerbEnum @@ -41,6 +43,7 @@ EdgeModelInfo, ModeEnum, PaginatedRuleList, + PayloadTemplate, Rule, WebhookAction, ) @@ -163,7 +166,9 @@ def make_action( include_image=include_image, ) - def make_webhook_action(self, url: str, include_image: bool) -> WebhookAction: + def make_webhook_action( + self, url: str, include_image: bool, payload_template: Optional[PayloadTemplate] = None + ) -> WebhookAction: """ Creates a WebhookAction object for use in creating alerts This function serves as a convenience method; WebhookAction objects can also be created directly. @@ -173,12 +178,22 @@ def make_webhook_action(self, url: str, include_image: bool) -> WebhookAction: action = gl.make_webhook_action("https://example.com/webhook", include_image=True) :param url: The URL to send the webhook to :param include_image: Whether to include the triggering image in the webhook payload + :param payload_template: Optional custom template for the webhook payload. The template will be rendered with + the alert data. The template must be a valid Jinja2 template which produces valid JSON when rendered. If no + template is provided, the default template designed for Slack will be used. """ return WebhookAction( url=str(url), include_image=include_image, + payload_template=payload_template, ) + def make_payload_template(self, template: str) -> PayloadTemplate: + """ + Creates a PayloadTemplate object for use in creating alerts + """ + return PayloadTemplate(template=template) + def create_alert( # pylint: disable=too-many-locals, too-many-arguments # noqa: PLR0913 self, detector: Union[str, Detector], @@ -266,6 +281,11 @@ def create_alert( # pylint: disable=too-many-locals, too-many-arguments # noqa WebhookActionRequest( url=str(webhook_action.url), include_image=webhook_action.include_image, + payload_template=( + PayloadTemplateRequest(template=webhook_action.payload_template.template) + if webhook_action.payload_template + else None + ), ) for webhook_action in webhook_actions ] diff --git a/test/unit/test_actions.py b/test/unit/test_actions.py index 65055c51..eb6bf1ee 100644 --- a/test/unit/test_actions.py +++ b/test/unit/test_actions.py @@ -1,7 +1,7 @@ from datetime import datetime import pytest -from groundlight import ExperimentalApi +from groundlight import ApiException, ExperimentalApi from groundlight_openapi_client.exceptions import NotFoundException @@ -90,8 +90,8 @@ def test_create_alert_multiple_webhook_actions(gl_experimental: ExperimentalApi) name = f"Test {datetime.utcnow()}" det = gl_experimental.get_or_create_detector(name, "test_query") condition = gl_experimental.make_condition("CHANGED_TO", {"label": "YES"}) - webhook_action_1 = gl_experimental.make_webhook_action("https://groundlight.ai", include_image=True) - webhook_action_2 = gl_experimental.make_webhook_action("https://example.com/webhook", include_image=False) + webhook_action_1 = gl_experimental.make_webhook_action(url="https://groundlight.ai", include_image=True) + webhook_action_2 = gl_experimental.make_webhook_action(url="https://example.com/webhook", include_image=False) webhook_actions = [webhook_action_1, webhook_action_2] alert = gl_experimental.create_alert(det, f"test_alert_{name}", condition, webhook_actions=webhook_actions) assert len(alert.webhook_action) == len(webhook_actions) @@ -102,10 +102,55 @@ def test_create_alert_webhook_action_and_other_action(gl_experimental: Experimen name = f"Test {datetime.utcnow()}" det = gl_experimental.get_or_create_detector(name, "test_query") condition = gl_experimental.make_condition("CHANGED_TO", {"label": "YES"}) - webhook_action = gl_experimental.make_webhook_action("https://groundlight.ai", include_image=True) + webhook_action = gl_experimental.make_webhook_action(url="https://groundlight.ai", include_image=True) email_action = gl_experimental.make_action("EMAIL", "test@groundlight.ai", False) alert = gl_experimental.create_alert( det, f"test_alert_{name}", condition, webhook_actions=webhook_action, actions=email_action ) assert len(alert.webhook_action) == 1 assert len(alert.action.root) == 1 + + +def test_create_alert_webhook_action_with_payload_template(gl_experimental: ExperimentalApi): + name = f"Test {datetime.utcnow()}" + det = gl_experimental.get_or_create_detector(name, "test_query") + condition = gl_experimental.make_condition("CHANGED_TO", {"label": "YES"}) + payload_template = gl_experimental.make_payload_template('{"text": "This should be a valid payload"}') + webhook_action = gl_experimental.make_webhook_action( + url="https://hooks.slack.com/services/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", + include_image=True, + payload_template=payload_template, + ) + alert = gl_experimental.create_alert(det, f"test_alert_{name}", condition, webhook_actions=webhook_action) + + assert len(alert.webhook_action) == 1 + assert alert.webhook_action[0].payload_template.template == '{"text": "This should be a valid payload"}' + + +def test_create_alert_webhook_action_with_invalid_payload_template(gl_experimental: ExperimentalApi): + name = f"Test {datetime.utcnow()}" + det = gl_experimental.get_or_create_detector(name, "test_query") + condition = gl_experimental.make_condition("CHANGED_TO", {"label": "YES"}) + payload_template = gl_experimental.make_payload_template( + '{"text": "This should not be a valid payload, jinja brackets are not closed properly {{detector_id}"}' + ) + webhook_action = gl_experimental.make_webhook_action( + url="https://groundlight.ai", include_image=True, payload_template=payload_template + ) + + bad_request_exception_status_code = 400 + + with pytest.raises(ApiException) as e: + gl_experimental.create_alert(det, f"test_alert_{name}", condition, webhook_actions=webhook_action) + assert e.value.status == bad_request_exception_status_code + + payload_template = gl_experimental.make_payload_template( + "This should not be a valid payload, it's valid jinja but won't produce valid json" + ) + webhook_action = gl_experimental.make_webhook_action( + url="https://groundlight.ai", include_image=True, payload_template=payload_template + ) + + with pytest.raises(ApiException) as e: + gl_experimental.create_alert(det, f"test_alert_{name}", condition, webhook_actions=webhook_action) + assert e.value.status == bad_request_exception_status_code