howler-api 3.0.0.dev374__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of howler-api might be problematic. Click here for more details.

Files changed (198) hide show
  1. howler/__init__.py +0 -0
  2. howler/actions/__init__.py +168 -0
  3. howler/actions/add_label.py +111 -0
  4. howler/actions/add_to_bundle.py +159 -0
  5. howler/actions/change_field.py +76 -0
  6. howler/actions/demote.py +160 -0
  7. howler/actions/example_plugin.py +104 -0
  8. howler/actions/prioritization.py +93 -0
  9. howler/actions/promote.py +147 -0
  10. howler/actions/remove_from_bundle.py +133 -0
  11. howler/actions/remove_label.py +111 -0
  12. howler/actions/transition.py +200 -0
  13. howler/api/__init__.py +249 -0
  14. howler/api/base.py +88 -0
  15. howler/api/socket.py +114 -0
  16. howler/api/v1/__init__.py +97 -0
  17. howler/api/v1/action.py +372 -0
  18. howler/api/v1/analytic.py +748 -0
  19. howler/api/v1/auth.py +382 -0
  20. howler/api/v1/clue.py +99 -0
  21. howler/api/v1/configs.py +58 -0
  22. howler/api/v1/dossier.py +222 -0
  23. howler/api/v1/help.py +28 -0
  24. howler/api/v1/hit.py +1181 -0
  25. howler/api/v1/notebook.py +82 -0
  26. howler/api/v1/overview.py +191 -0
  27. howler/api/v1/search.py +788 -0
  28. howler/api/v1/template.py +206 -0
  29. howler/api/v1/tool.py +183 -0
  30. howler/api/v1/user.py +416 -0
  31. howler/api/v1/utils/__init__.py +0 -0
  32. howler/api/v1/utils/etag.py +84 -0
  33. howler/api/v1/view.py +288 -0
  34. howler/app.py +235 -0
  35. howler/common/README.md +125 -0
  36. howler/common/__init__.py +0 -0
  37. howler/common/classification.py +979 -0
  38. howler/common/classification.yml +107 -0
  39. howler/common/exceptions.py +167 -0
  40. howler/common/loader.py +154 -0
  41. howler/common/logging/__init__.py +241 -0
  42. howler/common/logging/audit.py +138 -0
  43. howler/common/logging/format.py +38 -0
  44. howler/common/net.py +79 -0
  45. howler/common/net_static.py +1494 -0
  46. howler/common/random_user.py +316 -0
  47. howler/common/swagger.py +117 -0
  48. howler/config.py +64 -0
  49. howler/cronjobs/__init__.py +29 -0
  50. howler/cronjobs/retention.py +61 -0
  51. howler/cronjobs/rules.py +274 -0
  52. howler/cronjobs/view_cleanup.py +88 -0
  53. howler/datastore/README.md +112 -0
  54. howler/datastore/__init__.py +0 -0
  55. howler/datastore/bulk.py +72 -0
  56. howler/datastore/collection.py +2342 -0
  57. howler/datastore/constants.py +119 -0
  58. howler/datastore/exceptions.py +41 -0
  59. howler/datastore/howler_store.py +105 -0
  60. howler/datastore/migrations/fix_process.py +41 -0
  61. howler/datastore/operations.py +130 -0
  62. howler/datastore/schemas.py +90 -0
  63. howler/datastore/store.py +231 -0
  64. howler/datastore/support/__init__.py +0 -0
  65. howler/datastore/support/build.py +215 -0
  66. howler/datastore/support/schemas.py +90 -0
  67. howler/datastore/types.py +22 -0
  68. howler/error.py +91 -0
  69. howler/external/__init__.py +0 -0
  70. howler/external/generate_mitre.py +96 -0
  71. howler/external/generate_sigma_rules.py +31 -0
  72. howler/external/generate_tlds.py +47 -0
  73. howler/external/reindex_data.py +66 -0
  74. howler/external/wipe_databases.py +58 -0
  75. howler/gunicorn_config.py +25 -0
  76. howler/healthz.py +47 -0
  77. howler/helper/__init__.py +0 -0
  78. howler/helper/azure.py +50 -0
  79. howler/helper/discover.py +59 -0
  80. howler/helper/hit.py +236 -0
  81. howler/helper/oauth.py +247 -0
  82. howler/helper/search.py +92 -0
  83. howler/helper/workflow.py +110 -0
  84. howler/helper/ws.py +378 -0
  85. howler/odm/README.md +102 -0
  86. howler/odm/__init__.py +1 -0
  87. howler/odm/base.py +1543 -0
  88. howler/odm/charter.txt +146 -0
  89. howler/odm/helper.py +416 -0
  90. howler/odm/howler_enum.py +25 -0
  91. howler/odm/models/__init__.py +0 -0
  92. howler/odm/models/action.py +33 -0
  93. howler/odm/models/analytic.py +90 -0
  94. howler/odm/models/assemblyline.py +48 -0
  95. howler/odm/models/aws.py +23 -0
  96. howler/odm/models/azure.py +16 -0
  97. howler/odm/models/cbs.py +44 -0
  98. howler/odm/models/config.py +558 -0
  99. howler/odm/models/dossier.py +33 -0
  100. howler/odm/models/ecs/__init__.py +0 -0
  101. howler/odm/models/ecs/agent.py +17 -0
  102. howler/odm/models/ecs/autonomous_system.py +16 -0
  103. howler/odm/models/ecs/client.py +149 -0
  104. howler/odm/models/ecs/cloud.py +141 -0
  105. howler/odm/models/ecs/code_signature.py +27 -0
  106. howler/odm/models/ecs/container.py +32 -0
  107. howler/odm/models/ecs/dns.py +62 -0
  108. howler/odm/models/ecs/egress.py +10 -0
  109. howler/odm/models/ecs/elf.py +74 -0
  110. howler/odm/models/ecs/email.py +122 -0
  111. howler/odm/models/ecs/error.py +14 -0
  112. howler/odm/models/ecs/event.py +140 -0
  113. howler/odm/models/ecs/faas.py +24 -0
  114. howler/odm/models/ecs/file.py +84 -0
  115. howler/odm/models/ecs/geo.py +30 -0
  116. howler/odm/models/ecs/group.py +18 -0
  117. howler/odm/models/ecs/hash.py +16 -0
  118. howler/odm/models/ecs/host.py +17 -0
  119. howler/odm/models/ecs/http.py +37 -0
  120. howler/odm/models/ecs/ingress.py +12 -0
  121. howler/odm/models/ecs/interface.py +21 -0
  122. howler/odm/models/ecs/network.py +30 -0
  123. howler/odm/models/ecs/observer.py +45 -0
  124. howler/odm/models/ecs/organization.py +12 -0
  125. howler/odm/models/ecs/os.py +21 -0
  126. howler/odm/models/ecs/pe.py +17 -0
  127. howler/odm/models/ecs/process.py +216 -0
  128. howler/odm/models/ecs/registry.py +26 -0
  129. howler/odm/models/ecs/related.py +45 -0
  130. howler/odm/models/ecs/rule.py +51 -0
  131. howler/odm/models/ecs/server.py +24 -0
  132. howler/odm/models/ecs/threat.py +247 -0
  133. howler/odm/models/ecs/tls.py +58 -0
  134. howler/odm/models/ecs/url.py +51 -0
  135. howler/odm/models/ecs/user.py +57 -0
  136. howler/odm/models/ecs/user_agent.py +20 -0
  137. howler/odm/models/ecs/vulnerability.py +41 -0
  138. howler/odm/models/gcp.py +16 -0
  139. howler/odm/models/hit.py +356 -0
  140. howler/odm/models/howler_data.py +328 -0
  141. howler/odm/models/lead.py +24 -0
  142. howler/odm/models/localized_label.py +13 -0
  143. howler/odm/models/overview.py +16 -0
  144. howler/odm/models/pivot.py +40 -0
  145. howler/odm/models/template.py +24 -0
  146. howler/odm/models/user.py +83 -0
  147. howler/odm/models/view.py +34 -0
  148. howler/odm/random_data.py +888 -0
  149. howler/odm/randomizer.py +609 -0
  150. howler/patched.py +5 -0
  151. howler/plugins/__init__.py +25 -0
  152. howler/plugins/config.py +123 -0
  153. howler/remote/__init__.py +0 -0
  154. howler/remote/datatypes/README.md +355 -0
  155. howler/remote/datatypes/__init__.py +98 -0
  156. howler/remote/datatypes/counters.py +63 -0
  157. howler/remote/datatypes/events.py +66 -0
  158. howler/remote/datatypes/hash.py +206 -0
  159. howler/remote/datatypes/lock.py +42 -0
  160. howler/remote/datatypes/queues/__init__.py +0 -0
  161. howler/remote/datatypes/queues/comms.py +59 -0
  162. howler/remote/datatypes/queues/multi.py +32 -0
  163. howler/remote/datatypes/queues/named.py +93 -0
  164. howler/remote/datatypes/queues/priority.py +215 -0
  165. howler/remote/datatypes/set.py +118 -0
  166. howler/remote/datatypes/user_quota_tracker.py +54 -0
  167. howler/security/__init__.py +253 -0
  168. howler/security/socket.py +108 -0
  169. howler/security/utils.py +185 -0
  170. howler/services/__init__.py +0 -0
  171. howler/services/action_service.py +111 -0
  172. howler/services/analytic_service.py +128 -0
  173. howler/services/auth_service.py +323 -0
  174. howler/services/config_service.py +128 -0
  175. howler/services/dossier_service.py +252 -0
  176. howler/services/event_service.py +93 -0
  177. howler/services/hit_service.py +893 -0
  178. howler/services/jwt_service.py +158 -0
  179. howler/services/lucene_service.py +286 -0
  180. howler/services/notebook_service.py +119 -0
  181. howler/services/overview_service.py +44 -0
  182. howler/services/template_service.py +45 -0
  183. howler/services/user_service.py +331 -0
  184. howler/utils/__init__.py +0 -0
  185. howler/utils/annotations.py +28 -0
  186. howler/utils/chunk.py +38 -0
  187. howler/utils/dict_utils.py +200 -0
  188. howler/utils/isotime.py +17 -0
  189. howler/utils/list_utils.py +11 -0
  190. howler/utils/lucene.py +77 -0
  191. howler/utils/path.py +27 -0
  192. howler/utils/socket_utils.py +61 -0
  193. howler/utils/str_utils.py +256 -0
  194. howler/utils/uid.py +47 -0
  195. howler_api-3.0.0.dev374.dist-info/METADATA +71 -0
  196. howler_api-3.0.0.dev374.dist-info/RECORD +198 -0
  197. howler_api-3.0.0.dev374.dist-info/WHEEL +4 -0
  198. howler_api-3.0.0.dev374.dist-info/entry_points.txt +8 -0
@@ -0,0 +1,119 @@
1
+ from howler.odm import (
2
+ IP,
3
+ MAC,
4
+ MD5,
5
+ SHA1,
6
+ SHA256,
7
+ URI,
8
+ UUID,
9
+ Any,
10
+ Boolean,
11
+ CaseInsensitiveKeyword,
12
+ Classification,
13
+ ClassificationString,
14
+ Date,
15
+ Domain,
16
+ Email,
17
+ Enum,
18
+ FlattenedObject,
19
+ Float,
20
+ HowlerHash,
21
+ Integer,
22
+ Json,
23
+ Keyword,
24
+ Long,
25
+ LowerKeyword,
26
+ PhoneNumber,
27
+ Platform,
28
+ Processor,
29
+ SSDeepHash,
30
+ Text,
31
+ UpperKeyword,
32
+ URIPath,
33
+ ValidatedKeyword,
34
+ )
35
+
36
+ # Simple types can be resolved by a direct mapping
37
+ BASE_TYPE_MAPPING = {
38
+ Keyword: "keyword",
39
+ Boolean: "boolean",
40
+ Integer: "integer",
41
+ Long: "long",
42
+ Float: "float",
43
+ Date: "date",
44
+ Text: "text",
45
+ Classification: "keyword",
46
+ ClassificationString: "keyword",
47
+ Enum: "keyword",
48
+ UUID: "keyword",
49
+ IP: "ip",
50
+ Domain: "keyword",
51
+ Email: "keyword",
52
+ URI: "keyword",
53
+ URIPath: "keyword",
54
+ MAC: "keyword",
55
+ PhoneNumber: "keyword",
56
+ SSDeepHash: "text",
57
+ SHA1: "keyword",
58
+ SHA256: "keyword",
59
+ HowlerHash: "keyword",
60
+ MD5: "keyword",
61
+ Platform: "keyword",
62
+ Processor: "keyword",
63
+ FlattenedObject: "nested",
64
+ Any: "keyword",
65
+ UpperKeyword: "keyword",
66
+ LowerKeyword: "keyword",
67
+ Json: "keyword",
68
+ ValidatedKeyword: "keyword",
69
+ CaseInsensitiveKeyword: "keyword",
70
+ }
71
+
72
+ TYPE_MAPPING = {_class.__name__: mapping for _class, mapping in BASE_TYPE_MAPPING.items()}
73
+
74
+ ANALYZER_MAPPING = {
75
+ SSDeepHash: "text_fuzzy",
76
+ }
77
+
78
+ NORMALIZER_MAPPING = {
79
+ SHA1: "lowercase_normalizer",
80
+ SHA256: "lowercase_normalizer",
81
+ HowlerHash: "lowercase_normalizer",
82
+ MD5: "lowercase_normalizer",
83
+ CaseInsensitiveKeyword: "lowercase_normalizer",
84
+ }
85
+
86
+ # TODO: We might want to use custom analyzers for Classification and Enum and not create special backmapping cases
87
+ BACK_MAPPING = {
88
+ v: k
89
+ for k, v in BASE_TYPE_MAPPING.items()
90
+ if k
91
+ not in [
92
+ Enum,
93
+ Classification,
94
+ UUID,
95
+ IP,
96
+ Domain,
97
+ URI,
98
+ URIPath,
99
+ MAC,
100
+ PhoneNumber,
101
+ SSDeepHash,
102
+ Email,
103
+ SHA1,
104
+ SHA256,
105
+ HowlerHash,
106
+ MD5,
107
+ Platform,
108
+ Processor,
109
+ ClassificationString,
110
+ Any,
111
+ UpperKeyword,
112
+ LowerKeyword,
113
+ CaseInsensitiveKeyword,
114
+ Json,
115
+ ValidatedKeyword,
116
+ ]
117
+ }
118
+
119
+ BACK_MAPPING.update({x: Keyword for x in set(ANALYZER_MAPPING.values())})
@@ -0,0 +1,41 @@
1
+ from typing import Iterable, Optional
2
+
3
+ from elasticsearch.helpers.errors import ScanError
4
+
5
+ from howler.common.exceptions import HowlerException, HowlerKeyError
6
+
7
+
8
+ class SearchRetryException(HowlerException):
9
+ pass
10
+
11
+
12
+ class DataStoreException(HowlerException):
13
+ pass
14
+
15
+
16
+ class SearchException(HowlerException):
17
+ pass
18
+
19
+
20
+ class SearchDepthException(HowlerException):
21
+ pass
22
+
23
+
24
+ class ILMException(HowlerException):
25
+ pass
26
+
27
+
28
+ class VersionConflictException(HowlerException):
29
+ pass
30
+
31
+
32
+ class MultiKeyError(HowlerKeyError):
33
+ def __init__(self, keys: Iterable[str], partial_output):
34
+ super().__init__(str(keys))
35
+ self.keys = set(keys)
36
+ self.partial_output = partial_output
37
+
38
+
39
+ class HowlerScanError(HowlerException, ScanError):
40
+ def __init__(self, message: str = "Something went wrong", cause: Optional[Exception] = None) -> None:
41
+ HowlerException.__init__(self, message, cause if cause is not None else ScanError(message))
@@ -0,0 +1,105 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from howler.common.exceptions import HowlerAttributeError
4
+ from howler.datastore.collection import ESCollection, logger
5
+ from howler.odm.models.action import Action
6
+ from howler.odm.models.analytic import Analytic
7
+ from howler.odm.models.dossier import Dossier
8
+ from howler.odm.models.hit import Hit
9
+ from howler.odm.models.overview import Overview
10
+ from howler.odm.models.template import Template
11
+ from howler.odm.models.user import User
12
+ from howler.odm.models.view import View
13
+ from howler.plugins import get_plugins
14
+
15
+ if TYPE_CHECKING:
16
+ from howler.datastore.store import ESStore
17
+
18
+ INDEXES = [
19
+ ("hit", Hit),
20
+ ("template", Template),
21
+ ("overview", Overview),
22
+ ("analytic", Analytic),
23
+ ("action", Action),
24
+ ("user", User),
25
+ ("view", View),
26
+ ("dossier", Dossier),
27
+ ("user_avatar", None),
28
+ ]
29
+
30
+
31
+ class HowlerDatastore(object):
32
+ def __init__(self, datastore_object: "ESStore"):
33
+ self.ds = datastore_object
34
+
35
+ for plugin in get_plugins():
36
+ for _index, _odm in INDEXES:
37
+ if _odm is None:
38
+ continue
39
+
40
+ if modify_odm := plugin.modules.odm.modify_odm.get(_index):
41
+ logger.info("Modifying %s odm with function from plugin %s", _index, plugin.name)
42
+ modify_odm(_odm)
43
+
44
+ for _index, _odm in INDEXES:
45
+ self.ds.register(_index, _odm)
46
+
47
+ def __enter__(self):
48
+ return self
49
+
50
+ def __exit__(self, exc_type, exc_val, exc_tb):
51
+ self.ds.close()
52
+
53
+ def stop_model_validation(self):
54
+ self.ds.validate = False
55
+
56
+ def start_model_validation(self):
57
+ self.ds.validate = True
58
+
59
+ def enable_archive_access(self):
60
+ self.ds.archive_access = True
61
+
62
+ def disable_archive_access(self):
63
+ self.ds.archive_access = False
64
+
65
+ @property
66
+ def hit(self) -> ESCollection[Hit]:
67
+ return self.ds.hit
68
+
69
+ @property
70
+ def template(self) -> ESCollection[Template]:
71
+ return self.ds.template
72
+
73
+ @property
74
+ def overview(self) -> ESCollection[Overview]:
75
+ return self.ds.overview
76
+
77
+ @property
78
+ def view(self) -> ESCollection[View]:
79
+ return self.ds.view
80
+
81
+ @property
82
+ def analytic(self) -> ESCollection[Analytic]:
83
+ return self.ds.analytic
84
+
85
+ @property
86
+ def action(self) -> ESCollection[Action]:
87
+ return self.ds.action
88
+
89
+ @property
90
+ def user(self) -> ESCollection[User]:
91
+ return self.ds.user
92
+
93
+ @property
94
+ def dossier(self) -> ESCollection[Dossier]:
95
+ return self.ds.dossier
96
+
97
+ @property
98
+ def user_avatar(self) -> ESCollection:
99
+ return self.ds.user_avatar
100
+
101
+ def get_collection(self, collection_name: str) -> ESCollection:
102
+ if collection_name in self.ds.get_models():
103
+ return getattr(self, collection_name)
104
+ else:
105
+ raise HowlerAttributeError(f"Collection {collection_name} does not exist.")
@@ -0,0 +1,41 @@
1
+ from howler.common.loader import datastore
2
+ from howler.common.logging import get_logger
3
+
4
+ logger = get_logger(__file__)
5
+
6
+
7
+ def migrate():
8
+ logger.info("Checking for migration preconditions")
9
+
10
+ collection = datastore().hit
11
+
12
+ result = collection.search("_exists_:process.parent.start", as_obj=False, track_total_hits=True, rows=0)
13
+
14
+ if result["total"] > 0:
15
+ logger.info("Preconditions met, continuing.")
16
+
17
+ db_size = collection.search("howler.id:*", track_total_hits=True, rows=0)["total"]
18
+ logger.info(f"Database size pre-migration: {db_size}")
19
+ else:
20
+ logger.info("Preconditions not met, stopping")
21
+ return
22
+
23
+ logger.info(f"We will delete {result['total']} hits. Continue?")
24
+ result = input("y/[n]")
25
+
26
+ if result.lower() != "y":
27
+ logger.warning("Did not receive an OK, stopping")
28
+ return
29
+
30
+ logger.info("Deleting...")
31
+ collection.delete_by_query("_exists_:process.parent.start")
32
+ collection.commit()
33
+
34
+ db_size_after = collection.search("howler.id:*", track_total_hits=True, rows=0)["total"]
35
+ logger.info(f"Database size post-migration: {db_size_after}")
36
+
37
+ logger.info("Migration complete")
38
+
39
+
40
+ if __name__ == "__main__":
41
+ migrate()
@@ -0,0 +1,130 @@
1
+ from typing import Any, Iterator, Optional, Type
2
+
3
+ from howler.common.exceptions import HowlerTypeError, HowlerValueError
4
+ from howler.datastore.collection import ESCollection
5
+ from howler.odm.base import Model
6
+
7
+
8
+ class OdmHelper:
9
+ def __init__(self, obj: Optional[Type[Model]] = None) -> None:
10
+ self.model_name = obj.__name__ if obj else None
11
+ self.valid_fields = list(obj.flat_fields().keys()) if obj else None
12
+ self.fields = obj.flat_fields() if obj else {}
13
+
14
+ def list_add(
15
+ self,
16
+ key: str,
17
+ value: Any,
18
+ explanation: Optional[str] = None,
19
+ silent: bool = False,
20
+ if_missing: bool = False,
21
+ ):
22
+ if self.valid_fields and not any(
23
+ field for field in self.valid_fields if field.startswith(key) and self.fields[field].multivalued
24
+ ):
25
+ raise HowlerValueError(f"Key {key} not found in {self.model_name}")
26
+
27
+ return OdmUpdateOperation(
28
+ ESCollection.UPDATE_APPEND_IF_MISSING if if_missing else ESCollection.UPDATE_APPEND,
29
+ key,
30
+ value,
31
+ explanation,
32
+ silent,
33
+ )
34
+
35
+ def list_remove(
36
+ self,
37
+ key: str,
38
+ value: Any,
39
+ explanation: Optional[str] = None,
40
+ silent: bool = False,
41
+ ):
42
+ if self.valid_fields and not any(
43
+ field for field in self.valid_fields if field.startswith(key) and self.fields[field].multivalued
44
+ ):
45
+ raise HowlerValueError(f"Key {key} not found in {self.model_name}")
46
+
47
+ return OdmUpdateOperation(
48
+ ESCollection.UPDATE_REMOVE,
49
+ key,
50
+ value,
51
+ explanation,
52
+ silent,
53
+ )
54
+
55
+ def update(
56
+ self,
57
+ key: str,
58
+ value: Any,
59
+ explanation: Optional[str] = None,
60
+ silent: bool = False,
61
+ ):
62
+ if self.valid_fields and key not in self.valid_fields:
63
+ raise HowlerValueError(f"Key {key} not found in {self.model_name}")
64
+
65
+ return OdmUpdateOperation(
66
+ ESCollection.UPDATE_SET,
67
+ key,
68
+ value,
69
+ explanation,
70
+ silent,
71
+ )
72
+
73
+
74
+ class OdmUpdateOperation:
75
+ """Provide typing for ODM update operations.
76
+
77
+ Attributes:
78
+ operation: A string defining which operation to perform.
79
+ key: A string defining which value on the ODM to update.
80
+ value: The new value to set.
81
+ """
82
+
83
+ __slots__ = ["operation", "key", "value", "explanation", "silent"]
84
+
85
+ operation: str
86
+ key: str
87
+ value: Any
88
+ explanation: Optional[str]
89
+ silent: bool
90
+
91
+ def __init__(
92
+ self,
93
+ operation: str,
94
+ key: str,
95
+ value: Any,
96
+ explanation: Optional[str] = None,
97
+ silent: bool = False,
98
+ ) -> None:
99
+ if operation not in ESCollection.UPDATE_OPERATIONS:
100
+ raise HowlerValueError(f"Operation {operation} not found in ESCollection.UPDATE_OPERATIONS")
101
+
102
+ self.operation = operation
103
+
104
+ if key is not None and not isinstance(key, str):
105
+ raise HowlerTypeError("Key must be of type str")
106
+ self.key = key
107
+ self.value = value
108
+
109
+ if explanation is not None and not isinstance(explanation, str):
110
+ raise HowlerTypeError("Explanation must be of type str")
111
+ self.explanation = explanation
112
+ self.silent = silent
113
+
114
+ def __iter__(self) -> Iterator[Any]:
115
+ """Returns generator function for iterating over slots.
116
+
117
+ Enables object destructuring:
118
+ operation, key, value = odmOperation
119
+ """
120
+ for slot in self.__slots__:
121
+ if slot in ["explanation", "silent"]:
122
+ continue
123
+
124
+ yield self.__getattribute__(slot)
125
+
126
+ def __repr__(self) -> str:
127
+ return (
128
+ f"OdmUpdateOperation: operation={self.operation}, key={self.key}, "
129
+ + f"value={self.value}, explanation={self.explanation}"
130
+ )
@@ -0,0 +1,90 @@
1
+ default_index = {
2
+ "settings": {
3
+ "analysis": {
4
+ "filter": {
5
+ "text_ws_dsplit": {
6
+ "type": "pattern_replace",
7
+ "pattern": r"(\.)",
8
+ "replacement": " ",
9
+ }
10
+ },
11
+ "analyzer": {
12
+ "string_ci": {
13
+ "type": "custom",
14
+ "tokenizer": "keyword",
15
+ "filter": ["lowercase"],
16
+ },
17
+ "text_fuzzy": {
18
+ "type": "pattern",
19
+ "pattern": r"\s*:\s*",
20
+ "lowercase": False,
21
+ },
22
+ "text_whitespace": {"type": "whitespace"},
23
+ "text_ws_dsplit": {
24
+ "type": "custom",
25
+ "tokenizer": "whitespace",
26
+ "filters": ["text_ws_dsplit"],
27
+ },
28
+ },
29
+ "normalizer": {
30
+ "lowercase_normalizer": {
31
+ "type": "custom",
32
+ "char_filter": [],
33
+ "filter": ["lowercase"],
34
+ }
35
+ },
36
+ }
37
+ },
38
+ "mappings": {},
39
+ }
40
+
41
+ default_mapping = {
42
+ "dynamic": True,
43
+ "properties": {
44
+ "__text__": {"type": "text"},
45
+ },
46
+ }
47
+
48
+ default_dynamic_strings = {
49
+ "strings_as_keywords": {
50
+ "match_mapping_type": "string",
51
+ "mapping": {"type": "keyword", "ignore_above": 8191},
52
+ }
53
+ }
54
+
55
+ default_dynamic_templates = [
56
+ {"int": {"path_match": "*_i", "mapping": {"type": "integer", "store": True}}},
57
+ {"ints": {"path_match": "*_is", "mapping": {"type": "integer", "store": True}}},
58
+ {"long": {"path_match": "*_l", "mapping": {"type": "long", "store": True}}},
59
+ {"longs": {"path_match": "*_ls", "mapping": {"type": "long", "store": True}}},
60
+ {"double": {"path_match": "*_d", "mapping": {"type": "float", "store": True}}},
61
+ {"doubles": {"path_match": "*_ds", "mapping": {"type": "float", "store": True}}},
62
+ {"float": {"path_match": "*_f", "mapping": {"type": "float", "store": True}}},
63
+ {"floats": {"path_match": "*_fs", "mapping": {"type": "float", "store": True}}},
64
+ {"string": {"path_match": "*_s", "mapping": {"type": "keyword", "store": True}}},
65
+ {"strings": {"path_match": "*_ss", "mapping": {"type": "keyword", "store": True}}},
66
+ {"text": {"path_match": "*_t", "mapping": {"type": "text", "store": True}}},
67
+ {"texts": {"path_match": "*_ts", "mapping": {"type": "text", "store": True}}},
68
+ {"boolean": {"path_match": "*_b", "mapping": {"type": "boolean", "store": True}}},
69
+ {"booleans": {"path_match": "*_bs", "mapping": {"type": "boolean", "store": True}}},
70
+ {
71
+ "date": {
72
+ "path_match": "*_dt",
73
+ "mapping": {
74
+ "type": "date",
75
+ "format": "date_optional_time||epoch_millis",
76
+ "store": True,
77
+ },
78
+ }
79
+ },
80
+ {
81
+ "date": {
82
+ "path_match": "*_dts",
83
+ "mapping": {
84
+ "type": "date",
85
+ "format": "date_optional_time||epoch_millis",
86
+ "store": True,
87
+ },
88
+ }
89
+ },
90
+ ]