Skip to content

Commit 7ace6a1

Browse files
authored
Merge pull request #37 from UncoderIO/field-class-refactoring
field class refactoring
2 parents 164b34c + d9404a0 commit 7ace6a1

File tree

19 files changed

+134
-145
lines changed

19 files changed

+134
-145
lines changed

translator/app/translator/core/functions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def concat_kwargs(kwargs: Dict[str, str]) -> str:
5858

5959
@staticmethod
6060
def map_field(field: Field, source_mapping: SourceMapping) -> str:
61-
generic_field_name = field.generic_names_map[source_mapping.source_id]
61+
generic_field_name = field.get_generic_field_name(source_mapping.source_id)
6262
mapped_field = source_mapping.fields_mapping.get_platform_field_name(generic_field_name=generic_field_name)
6363
if isinstance(mapped_field, list):
6464
mapped_field = mapped_field[0]

translator/app/translator/core/mapping.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,7 @@ def get_suitable_source_mappings(self, *args, **kwargs) -> List[SourceMapping]:
122122

123123
def get_source_mapping(self, source_id: str) -> Optional[SourceMapping]:
124124
return self._source_mappings.get(source_id)
125+
126+
@property
127+
def default_mapping(self) -> SourceMapping:
128+
return self._source_mappings[DEFAULT_MAPPING_NAME]

translator/app/translator/core/mixins/logic.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
from typing import List, Union
22

3-
from app.translator.core.models.field import Field, Keyword
4-
from app.translator.core.models.identifier import Identifier
53
from app.translator.core.custom_types.tokens import LogicalOperatorType, GroupType
4+
from app.translator.core.models.field import FieldValue, Keyword
5+
from app.translator.core.models.identifier import Identifier
66

77

88
class ANDLogicOperatorMixin:
99

1010
@staticmethod
11-
def get_missed_and_token_indices(tokens: List[Union[Field, Keyword, Identifier]]) -> List[int]:
11+
def get_missed_and_token_indices(tokens: List[Union[FieldValue, Keyword, Identifier]]) -> List[int]:
1212
missed_and_indices = []
1313
for index in range(len(tokens) - 1):
1414
token = tokens[index]
1515
next_token = tokens[index + 1]
16-
if (isinstance(token, (Field, Keyword))
17-
and not (isinstance(next_token, Identifier) and (
18-
next_token.token_type in LogicalOperatorType
19-
or next_token.token_type == GroupType.R_PAREN))):
16+
if ((isinstance(token, (FieldValue, Keyword))
17+
or isinstance(token, Identifier) and token.token_type == GroupType.R_PAREN)
18+
and not (isinstance(next_token, Identifier)
19+
and (next_token.token_type
20+
in (LogicalOperatorType.AND, LogicalOperatorType.OR, GroupType.R_PAREN)))):
2021
missed_and_indices.append(index + 1)
2122
return list(reversed(missed_and_indices))
2223

23-
def add_and_token_if_missed(self, tokens: List[Union[Field, Keyword, Identifier]]) -> List[Union[Field, Keyword, Identifier]]:
24+
def add_and_token_if_missed(self,
25+
tokens: List[Union[FieldValue, Keyword, Identifier]]
26+
) -> List[Union[FieldValue, Keyword, Identifier]]:
2427
indices = self.get_missed_and_token_indices(tokens=tokens)
2528
for index in indices:
2629
tokens.insert(index, Identifier(token_type=LogicalOperatorType.AND))

translator/app/translator/core/models/field.py

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,36 @@
11
from typing import Union, Optional
22

3+
from app.translator.core.mapping import SourceMapping, DEFAULT_MAPPING_NAME
34
from app.translator.core.models.identifier import Identifier
45
from app.translator.core.custom_types.tokens import OperatorType
56

67

78
class Field:
8-
def __init__(self, source_name: str, operator: Identifier = None, value: Union[int, str, list, tuple] = None):
9+
def __init__(self, source_name: str):
10+
self.source_name = source_name
11+
self.__generic_names_map = {}
12+
13+
def get_generic_field_name(self, source_id: str) -> Optional[str]:
14+
return self.__generic_names_map.get(source_id)
15+
16+
def set_generic_names_map(self, source_mappings: list[SourceMapping], default_mapping: SourceMapping) -> None:
17+
generic_names_map = {
18+
source_mapping.source_id: source_mapping.fields_mapping.get_generic_field_name(self.source_name)
19+
for source_mapping in source_mappings
20+
}
21+
if DEFAULT_MAPPING_NAME not in generic_names_map:
22+
fields_mapping = default_mapping.fields_mapping
23+
generic_names_map[DEFAULT_MAPPING_NAME] = fields_mapping.get_generic_field_name(self.source_name)
24+
25+
self.__generic_names_map = generic_names_map
26+
27+
28+
class FieldValue:
29+
def __init__(self, source_name: str, operator: Identifier, value: Union[int, str, list, tuple]):
30+
self.field = Field(source_name=source_name)
931
self.operator = operator
1032
self.values = []
1133
self.__add_value(value)
12-
self.source_name = source_name # input translation field name
13-
self.generic_names_map = {}
1434

1535
@property
1636
def value(self):
@@ -30,31 +50,7 @@ def __add__(self, other):
3050
self.values.append(other)
3151

3252
def __repr__(self):
33-
if self.operator:
34-
return f"{self.source_name} {self.operator.token_type} {self.values}"
35-
36-
return f"{self.source_name}"
37-
38-
def __eq__(self, other):
39-
if isinstance(other, Field):
40-
return self._hash == other._hash
41-
"""For OR operator check"""
42-
if self.source_name == other.source_name and self.operator == other.operator:
43-
return True
44-
return False
45-
46-
def __neq__(self, other):
47-
"""For AND operator check"""
48-
if self.source_name != other.source_name:
49-
return True
50-
return False
51-
52-
@property
53-
def _hash(self):
54-
return hash(str(self))
55-
56-
def __hash__(self):
57-
return hash(str(self))
53+
return f"{self.field.source_name} {self.operator.token_type} {self.values}"
5854

5955

6056
class Keyword:

translator/app/translator/core/models/functions/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
from dataclasses import dataclass, field
44
from typing import List, Union
55

6-
from app.translator.core.models.field import Field, Keyword
6+
from app.translator.core.models.field import Field, FieldValue, Keyword
77
from app.translator.core.models.identifier import Identifier
88

99

1010
@dataclass
1111
class Function:
1212
name: str = None
13-
args: List[Union[Field, Keyword, Function, Identifier]] = field(default_factory=list)
13+
args: List[Union[Field, FieldValue, Keyword, Function, Identifier]] = field(default_factory=list)
1414
as_clause: str = None
1515
by_clauses: List[Field] = field(default_factory=list)
1616

translator/app/translator/core/parser.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
from app.translator.core.functions import PlatformFunctions
2323
from app.translator.core.mapping import BasePlatformMappings, SourceMapping
24-
from app.translator.core.models.field import Field
24+
from app.translator.core.models.field import FieldValue
2525
from app.translator.core.models.functions.base import ParsedFunctions
2626
from app.translator.core.models.platform_details import PlatformDetails
2727
from app.translator.core.models.parser_output import SiemContainer, MetaInfoContainer
@@ -50,15 +50,15 @@ def get_tokens_and_source_mappings(self,
5050
if not query:
5151
raise TokenizerGeneralException("Can't translate empty query. Please provide more details")
5252
tokens = self.tokenizer.tokenize(query=query)
53-
field_tokens = self.tokenizer.filter_tokens(tokens, Field)
53+
field_tokens = [token.field for token in self.tokenizer.filter_tokens(tokens, FieldValue)]
5454
field_names = [field.source_name for field in field_tokens]
55-
suitable_source_mappings = self.mappings.get_suitable_source_mappings(field_names=field_names, **log_sources)
56-
self.tokenizer.set_field_generic_names_map(field_tokens, suitable_source_mappings, self.mappings)
55+
source_mappings = self.mappings.get_suitable_source_mappings(field_names=field_names, **log_sources)
56+
self.tokenizer.set_field_tokens_generic_names_map(field_tokens, source_mappings, self.mappings.default_mapping)
5757

58-
return tokens, suitable_source_mappings
58+
return tokens, source_mappings
5959

6060
def set_functions_fields_generic_names(self,
6161
functions: ParsedFunctions,
6262
source_mappings: List[SourceMapping]) -> None:
63-
field_tokens = self.tokenizer.filter_function_tokens(tokens=functions.functions)
64-
self.tokenizer.set_field_generic_names_map(field_tokens, source_mappings, self.mappings)
63+
field_tokens = self.tokenizer.get_field_tokens_from_func_args(args=functions.functions)
64+
self.tokenizer.set_field_tokens_generic_names_map(field_tokens, source_mappings, self.mappings.default_mapping)

translator/app/translator/core/render.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@
2727
from app.translator.core.exceptions.parser import UnsupportedOperatorException
2828
from app.translator.core.functions import PlatformFunctions
2929
from app.translator.core.mapping import BasePlatformMappings, SourceMapping, LogSourceSignature, DEFAULT_MAPPING_NAME
30-
from app.translator.core.models.field import Field, Keyword
30+
from app.translator.core.models.field import Field, FieldValue, Keyword
3131
from app.translator.core.models.functions.base import Function, ParsedFunctions
32+
from app.translator.core.models.identifier import Identifier
3233
from app.translator.core.models.platform_details import PlatformDetails
3334
from app.translator.core.models.parser_output import MetaInfoContainer
34-
from app.translator.core.custom_types.tokens import LogicalOperatorType, OperatorType, GroupType
35+
from app.translator.core.custom_types.tokens import LogicalOperatorType, OperatorType
3536

3637

3738
class BaseQueryFieldValue(ABC):
@@ -133,7 +134,7 @@ def generate_functions(self, functions: List[Function], source_mapping: SourceMa
133134
return self.platform_functions.render(functions, source_mapping) if self.platform_functions else ""
134135

135136
def map_field(self, field: Field, source_mapping: SourceMapping) -> List[str]:
136-
generic_field_name = field.generic_names_map[source_mapping.source_id]
137+
generic_field_name = field.get_generic_field_name(source_mapping.source_id)
137138
# field can be mapped to corresponding platform field name or list of platform field names
138139
mapped_field = source_mapping.fields_mapping.get_platform_field_name(generic_field_name=generic_field_name)
139140
if not mapped_field and self.is_strict_mapping:
@@ -145,10 +146,10 @@ def map_field(self, field: Field, source_mapping: SourceMapping) -> List[str]:
145146
return mapped_field if mapped_field else [generic_field_name] if generic_field_name else [field.source_name]
146147

147148
def apply_token(self,
148-
token: Union[Field, Keyword, LogicalOperatorType, GroupType],
149+
token: Union[FieldValue, Keyword, Identifier],
149150
source_mapping: SourceMapping) -> str:
150-
if isinstance(token, (Field, Keyword)):
151-
mapped_fields = self.map_field(token, source_mapping) if isinstance(token, Field) else [None]
151+
if isinstance(token, FieldValue):
152+
mapped_fields = self.map_field(token.field, source_mapping)
152153
if len(mapped_fields) > 1:
153154
return self.group_token % self.operator_map[LogicalOperatorType.OR].join([
154155
self.field_value_map.apply_field_value(field=field, operator=token.operator, value=token.value)
@@ -158,12 +159,17 @@ def apply_token(self,
158159
return self.field_value_map.apply_field_value(field=mapped_fields[0],
159160
operator=token.operator,
160161
value=token.value)
162+
elif isinstance(token, Keyword):
163+
return self.field_value_map.apply_field_value(field=None,
164+
operator=token.operator,
165+
value=token.value)
161166
elif token.token_type in LogicalOperatorType:
162167
return self.operator_map.get(token.token_type)
168+
163169
return token.token_type
164170

165171
def generate_query(self,
166-
query: List[Union[Field, Keyword, LogicalOperatorType, GroupType]],
172+
query: List[Union[FieldValue, Keyword, Identifier]],
167173
source_mapping: SourceMapping) -> str:
168174
result_values = []
169175
for token in query:
@@ -173,8 +179,7 @@ def generate_query(self,
173179
def wrap_query_with_meta_info(self, meta_info: MetaInfoContainer, query: str):
174180
if meta_info and (meta_info.id or meta_info.title):
175181
query_meta_info = "\n".join(
176-
self.wrap_with_comment(f"{key}{value}")
177-
for key, value in {"name: ": meta_info.title, "uuid: ": meta_info.id}.items() if value
182+
self.wrap_with_comment(f"{key}{value}") for key, value in {"name: ": meta_info.title, "uuid: ": meta_info.id}.items() if value
178183
)
179184
query = f"{query}\n\n{query_meta_info}"
180185
return query

translator/app/translator/core/tokenizer.py

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,20 @@
2727
TokenizerGeneralException,
2828
QueryParenthesesException
2929
)
30-
from app.translator.core.mapping import SourceMapping, DEFAULT_MAPPING_NAME, BasePlatformMappings
31-
from app.translator.core.models.field import Field, Keyword
30+
from app.translator.core.mapping import SourceMapping
31+
from app.translator.core.models.field import Field, FieldValue, Keyword
3232
from app.translator.core.models.functions.base import Function
3333
from app.translator.core.models.functions.sort import SortArg
3434
from app.translator.core.models.identifier import Identifier
3535
from app.translator.core.custom_types.tokens import OperatorType, GroupType
3636
from app.translator.tools.utils import get_match_group
3737

38-
TOKEN_TYPE = Union[Field, Keyword, Identifier]
38+
TOKEN_TYPE = Union[FieldValue, Keyword, Identifier]
3939

4040

4141
class BaseTokenizer(ABC):
4242
@abstractmethod
43-
def tokenize(self, query: str) -> List[Union[Field, Keyword, Identifier]]:
43+
def tokenize(self, query: str) -> List[Union[FieldValue, Keyword, Identifier]]:
4444
raise NotImplementedError()
4545

4646

@@ -180,18 +180,18 @@ def process_value_wildcard_symbols(self,
180180
return self._clean_value(value, wildcard_symbol), op
181181

182182
@staticmethod
183-
def create_field(field_name: str, operator: Identifier, value: Union[str, List]) -> Field:
184-
return Field(operator=operator, value=value, source_name=field_name)
183+
def create_field_value(field_name: str, operator: Identifier, value: Union[str, List]) -> FieldValue:
184+
return FieldValue(source_name=field_name, operator=operator, value=value)
185185

186-
def search_field_value(self, query):
186+
def search_field_value(self, query) -> Tuple[FieldValue, str]:
187187
field_name = self.search_field(query)
188188
operator = self.search_operator(query, field_name)
189189
query, operator, value = self.search_value(query=query, operator=operator, field_name=field_name)
190190
value, operator_token = self.process_value_wildcard_symbols(value=value,
191191
operator=operator,
192192
wildcard_symbol=self.wildcard_symbol)
193-
field = self.create_field(field_name=field_name, operator=operator_token, value=value)
194-
return field, query
193+
field_value = self.create_field_value(field_name=field_name, operator=operator_token, value=value)
194+
return field_value, query
195195

196196
def _match_field_value(self, query: str, white_space_pattern: str = r"\s+") -> bool:
197197
single_value_operator_group = fr"(?:{'|'.join(self.single_value_operators_map)})"
@@ -208,7 +208,7 @@ def _match_field_value(self, query: str, white_space_pattern: str = r"\s+") -> b
208208

209209
return False
210210

211-
def _get_identifier(self, query: str) -> Tuple[Union[Field, Keyword, Identifier], str]:
211+
def _get_identifier(self, query: str) -> Tuple[Union[FieldValue, Keyword, Identifier], str]:
212212
query = query.strip("\n").strip(" ").strip("\n")
213213
if query.startswith(GroupType.L_PAREN):
214214
return Identifier(token_type=GroupType.L_PAREN), query[1:]
@@ -240,7 +240,7 @@ def _validate_parentheses(tokens):
240240
raise QueryParenthesesException()
241241
return True
242242

243-
def tokenize(self, query: str) -> List[Union[Field, Keyword, Identifier]]:
243+
def tokenize(self, query: str) -> List[Union[FieldValue, Keyword, Identifier]]:
244244
tokenized = []
245245
while query:
246246
identifier, query = self._get_identifier(query=query)
@@ -250,34 +250,28 @@ def tokenize(self, query: str) -> List[Union[Field, Keyword, Identifier]]:
250250

251251
@staticmethod
252252
def filter_tokens(tokens: List[TOKEN_TYPE],
253-
token_type: Union[Type[Field], Type[Keyword], Type[Identifier]]) -> List[TOKEN_TYPE]:
253+
token_type: Union[Type[FieldValue], Type[Keyword], Type[Identifier]]) -> List[TOKEN_TYPE]:
254254
return [token for token in tokens if isinstance(token, token_type)]
255255

256-
def filter_function_tokens(self,
257-
tokens: List[Union[Field, Keyword, Identifier, Function, SortArg]]) -> List[TOKEN_TYPE]:
256+
def get_field_tokens_from_func_args(self,
257+
args: List[Union[Field, FieldValue, Keyword, Identifier, Function, SortArg]]
258+
) -> List[Field]:
258259
result = []
259-
for token in tokens:
260-
if isinstance(token, Field):
261-
result.append(token)
262-
elif isinstance(token, Function):
263-
result.extend(self.filter_function_tokens(tokens=token.args))
264-
result.extend(self.filter_function_tokens(tokens=token.by_clauses))
265-
elif isinstance(token, SortArg):
266-
result.append(token.field)
260+
for arg in args:
261+
if isinstance(arg, Field):
262+
result.append(arg)
263+
elif isinstance(arg, FieldValue):
264+
result.append(arg.field)
265+
elif isinstance(arg, Function):
266+
result.extend(self.get_field_tokens_from_func_args(args=arg.args))
267+
result.extend(self.get_field_tokens_from_func_args(args=arg.by_clauses))
268+
elif isinstance(arg, SortArg):
269+
result.append(arg.field)
267270
return result
268271

269272
@staticmethod
270-
def set_field_generic_names_map(tokens: List[Field],
271-
source_mappings: List[SourceMapping],
272-
platform_mappings: BasePlatformMappings) -> None:
273+
def set_field_tokens_generic_names_map(tokens: List[Field],
274+
source_mappings: List[SourceMapping],
275+
default_mapping: SourceMapping) -> None:
273276
for token in tokens:
274-
generic_names_map = {
275-
source_mapping.source_id: source_mapping.fields_mapping.get_generic_field_name(token.source_name)
276-
for source_mapping in source_mappings
277-
}
278-
if DEFAULT_MAPPING_NAME not in generic_names_map:
279-
default_source_mapping = platform_mappings.get_source_mapping(DEFAULT_MAPPING_NAME)
280-
fields_mapping = default_source_mapping.fields_mapping
281-
generic_names_map[DEFAULT_MAPPING_NAME] = fields_mapping.get_generic_field_name(token.source_name)
282-
283-
token.generic_names_map = generic_names_map
277+
token.set_generic_names_map(source_mappings, default_mapping)

0 commit comments

Comments
 (0)