Skip to content

Commit b24c88d

Browse files
committed
initial working cleanup of lookups code
1 parent fda382f commit b24c88d

File tree

2 files changed

+18
-23
lines changed

2 files changed

+18
-23
lines changed

contentctl/input/director.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from contentctl.objects.playbook import Playbook
1515
from contentctl.objects.deployment import Deployment
1616
from contentctl.objects.macro import Macro
17-
from contentctl.objects.lookup import Lookup
17+
from contentctl.objects.lookup import LookupAdapter, Lookup
1818
from contentctl.objects.atomic import AtomicEnrichment
1919
from contentctl.objects.security_content_object import SecurityContentObject
2020
from contentctl.objects.data_source import DataSource
@@ -157,7 +157,8 @@ def createSecurityContent(self, contentType: SecurityContentType) -> None:
157157
modelDict = YmlReader.load_file(file)
158158

159159
if contentType == SecurityContentType.lookups:
160-
lookup = Lookup.model_validate(modelDict, context={"output_dto":self.output_dto, "config":self.input_dto})
160+
lookup = LookupAdapter.validate_python(modelDict, context={"output_dto":self.output_dto, "config":self.input_dto})
161+
#lookup = Lookup.model_validate(modelDict, context={"output_dto":self.output_dto, "config":self.input_dto})
161162
self.output_dto.addContentToDictMappings(lookup)
162163

163164
elif contentType == SecurityContentType.macros:

contentctl/objects/lookup.py

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from pydantic import field_validator, ValidationInfo, model_validator, FilePath, model_serializer, Field, NonNegativeInt, computed_field
3+
from pydantic import field_validator, ValidationInfo, model_validator, FilePath, model_serializer, Field, NonNegativeInt, computed_field, TypeAdapter
44
from enum import StrEnum, auto
55
from typing import TYPE_CHECKING, Optional, Any, Union, Literal, Annotated, Self
66
import re
@@ -45,6 +45,7 @@ class Lookup(SecurityContentObject, abc.ABC):
4545
min_matches: None | NonNegativeInt = Field(default=None)
4646
max_matches: None | Annotated[NonNegativeInt, Field(ge=1, le=1000)] = Field(default=None)
4747
case_sensitive_match: None | bool = Field(default=None)
48+
4849

4950

5051

@@ -108,11 +109,10 @@ def get_lookups(text_field: str, director:DirectorOutputDto, ignore_lookups:set[
108109

109110

110111

111-
112-
113-
class CSVLookup(Lookup):
114-
lookup_type: Literal[Lookup_Type.csv]
115-
112+
class FileBackedLookup(Lookup, abc.ABC):
113+
# For purposes of the disciminated union, the child classes which
114+
# inherit from this class must declare the typing of lookup_type
115+
# themselves, hence it is not defined in the Lookup class
116116

117117
@model_validator(mode="after")
118118
def ensure_lookup_file_exists(self)->Self:
@@ -124,9 +124,9 @@ def ensure_lookup_file_exists(self)->Self:
124124
@cached_property
125125
def filename(self)->FilePath:
126126
if self.file_path is None:
127-
raise ValueError("Cannot get the filename of the lookup CSV because the YML file_path attribute is None")
127+
raise ValueError(f"Cannot get the filename of the lookup {self.lookup_type} because the YML file_path attribute is None") #type: ignore
128128

129-
csv_file = self.file_path.parent / f"{self.file_path.stem}.csv"
129+
csv_file = self.file_path.parent / f"{self.file_path.stem}.{self.lookup_type}" #type: ignore
130130
return csv_file
131131

132132
@computed_field
@@ -138,10 +138,11 @@ def app_filename(self)->FilePath:
138138
2. Only apply the datetime stamp if it is version > 1. This makes the code a small fraction
139139
more complicated, but preserves longstanding CSV that have not been modified in a long time
140140
'''
141-
return pathlib.Path(f"{self.filename.stem}_{self.date.year}{self.date.month:02}{self.date.day:02}.csv")
142-
143-
141+
return pathlib.Path(f"{self.filename.stem}_{self.date.year}{self.date.month:02}{self.date.day:02}.{self.lookup_type}") #type: ignore
144142

143+
class CSVLookup(FileBackedLookup):
144+
lookup_type:Literal[Lookup_Type.csv]
145+
145146
@model_serializer
146147
def serialize_model(self):
147148
#Call parent serializer
@@ -158,13 +159,6 @@ def serialize_model(self):
158159

159160
@model_validator(mode="after")
160161
def ensure_correct_csv_structure(self)->Self:
161-
162-
163-
if self.filename.suffix != ".csv":
164-
raise ValueError(f"All Lookup files must be CSV files and end in .csv. The following file does not: '{self.filename}'")
165-
166-
167-
168162
# https://docs.python.org/3/library/csv.html#csv.DictReader
169163
# Column Names (fieldnames) determine by the number of columns in the first row.
170164
# If a row has MORE fields than fieldnames, they will be dumped in a list under the key 'restkey' - this should throw an Exception
@@ -200,7 +194,7 @@ def ensure_correct_csv_structure(self)->Self:
200194

201195
class KVStoreLookup(Lookup):
202196
lookup_type: Literal[Lookup_Type.kvstore]
203-
collection: str = Field(description="Name of the KVStore Collection. Note that collection MUST equal the name.")
197+
collection: str = Field(description="Name of the KVStore Collection. Note that collection MUST equal the name. This is a duplicate field, so it will be removed eventually.")
204198
fields: list[str] = Field(description="The names of the fields/headings for the KVStore.", min_length=1)
205199

206200

@@ -225,9 +219,9 @@ def serialize_model(self):
225219
model.update(super_fields)
226220
return model
227221

228-
class MlModel(Lookup):
222+
class MlModel(FileBackedLookup):
229223
lookup_type: Literal[Lookup_Type.mlmodel]
230224

231225

232-
226+
LookupAdapter = TypeAdapter(Annotated[CSVLookup | KVStoreLookup | MlModel, Field(discriminator="lookup_type")])
233227

0 commit comments

Comments
 (0)