|
1 | | -import importlib |
2 | | -import inspect |
3 | | -import logging |
4 | | -import re |
5 | | -from pathlib import Path |
6 | | - |
7 | | -import aenum |
8 | | - |
9 | 1 | from core.events import message |
10 | | -from core.schemas import ( |
| 2 | +from core.schemas import ( # noqa: F401 (imported for their side-effect registration) |
11 | 3 | dfiq, |
12 | 4 | entity, |
13 | 5 | graph, |
|
20 | 12 | user, |
21 | 13 | ) |
22 | 14 |
|
23 | | -logger = logging.getLogger(__name__) |
24 | | -logging.getLogger().setLevel(logging.INFO) |
25 | | - |
26 | | - |
27 | | -def register_module(module_name, base_module): |
28 | | - """ |
29 | | - Register the classes for the schema implementation files |
30 | | -
|
31 | | - module_name: The module name to load |
32 | | - base_module: The base module to register the classes in (entity, indicator, observable) |
33 | | - """ |
34 | | - module = importlib.import_module(module_name) |
35 | | - module_base_name = base_module.__name__.split(".")[-1] |
36 | | - schema_base_class = getattr(base_module, module_base_name.capitalize()) |
37 | | - schema_type_mapping = getattr(base_module, "TYPE_MAPPING") |
38 | | - schema_types = getattr(base_module, f"{module_base_name.capitalize()}Types", None) |
39 | | - schema_enum = getattr(base_module, f"{module_base_name.capitalize()}Type") |
40 | | - for _, obj in inspect.getmembers(module, inspect.isclass): |
41 | | - if issubclass(obj, schema_base_class) and "type" in obj.model_fields: |
42 | | - obs_type = obj.model_fields["type"].default |
43 | | - logger.info(f"Registering class {obj.__name__} defining type <{obs_type}>") |
44 | | - aenum.extend_enum(schema_enum, obs_type, obs_type) |
45 | | - schema_type_mapping[obs_type] = obj |
46 | | - setattr(base_module, obj.__name__, obj) |
47 | | - if not schema_types: |
48 | | - schema_types = obj |
49 | | - else: |
50 | | - schema_types |= obj |
51 | | - setattr(base_module, f"{module_base_name.capitalize()}Types", schema_types) |
52 | | - |
53 | | - |
54 | | -def register_classes(schema_root_type, base_module): |
55 | | - """ |
56 | | - Register the classes for the schema root type |
57 | | -
|
58 | | - schema_root_type: The schema root type to work with (entities, indicators, observables) |
59 | | - base_module: The base module to register the classes in (entity, indicator, observable) |
60 | | - """ |
61 | | - module_base_name = base_module.__name__.split(".")[-1] |
62 | | - logger.info(f"Registering {module_base_name} classes") |
63 | | - for schema_file in Path(__file__).parent.glob(f"{schema_root_type}/**/*.py"): |
64 | | - if schema_file.stem == "__init__": |
65 | | - continue |
66 | | - if schema_file.parent.stem == schema_root_type: |
67 | | - module_name = f"core.schemas.{schema_root_type}.{schema_file.stem}" |
68 | | - elif schema_file.parent.stem == "private": |
69 | | - module_name = f"core.schemas.{schema_root_type}.private.{schema_file.stem}" |
70 | | - try: |
71 | | - register_module(module_name, base_module) |
72 | | - except Exception: |
73 | | - logger.exception(f"Failed to register classes from {module_name}") |
74 | | - |
75 | | - |
76 | | -def load_entities(): |
77 | | - entity.TYPE_MAPPING = {"entity": entity.Entity, "entities": entity.Entity} |
78 | | - register_classes("entities", entity) |
79 | | - |
80 | | - |
81 | | -def load_indicators(): |
82 | | - indicator.TYPE_MAPPING = { |
83 | | - "indicator": indicator.Indicator, |
84 | | - "indicators": indicator.Indicator, |
85 | | - } |
86 | | - register_classes("indicators", indicator) |
87 | | - |
88 | | - |
89 | | -def load_observables(): |
90 | | - observable.TYPE_MAPPING = { |
91 | | - "observable": observable.Observable, |
92 | | - "observables": observable.Observable, |
93 | | - } |
94 | | - if "guess" not in observable.ObservableType.__members__: |
95 | | - aenum.extend_enum(observable.ObservableType, "guess", "guess") |
96 | | - register_classes("observables", observable) |
97 | | - |
98 | | - |
99 | | -load_observables() |
100 | | -load_entities() |
101 | | -load_indicators() |
102 | | - |
| 15 | +# Importing the schema modules above triggers their static type registration |
| 16 | +# (see the "Static type registry" sections in observable.py / entity.py / |
| 17 | +# indicator.py; dfiq.py registers its own types inline). Rebuild the event |
| 18 | +# message model now that every referenced schema type is importable. |
103 | 19 | message.EventMessage.model_rebuild() |
0 commit comments