Skip to content

Commit 34387dc

Browse files
authored
Merge pull request #86 from corva-ai/feature/DEVC-627_make_optional_data_field
DEVC-627 | Handle optional data field
2 parents c9c9e1f + 136c4f3 commit 34387dc

File tree

3 files changed

+67
-8
lines changed

3 files changed

+67
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Fixed
9+
- Filter out records with `None` data from stream time records.
810

911

1012
## [1.11.2] - 2024-01-05

src/corva/models/stream/raw.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class RawMetadata(CorvaBaseEvent):
7373
RecordsDepth = Sequence[RawDepthRecord]
7474
else:
7575
RecordsBase = pydantic.conlist(RawBaseRecord, min_items=1)
76-
RecordsTime = pydantic.conlist(RawTimeRecord, min_items=1)
76+
RecordsTime = pydantic.conlist(RawTimeRecord, min_items=0)
7777
RecordsDepth = pydantic.conlist(RawDepthRecord, min_items=1)
7878

7979

@@ -105,7 +105,10 @@ def is_completed(self) -> bool:
105105
There can only be 1 completed record always located at the end of the list.
106106
"""
107107

108-
return self.records[-1].collection == 'wits.completed'
108+
if not self.records:
109+
return False
110+
111+
return self.records[-1].collection == "wits.completed"
109112

110113
@property
111114
def max_record_value(self) -> Union[int, float]:
@@ -165,33 +168,48 @@ def filter_records(
165168
def set_asset_id(cls, values: dict) -> dict:
166169
"""Calculates asset_id field."""
167170

168-
records: List[RawBaseRecord] = values['records']
171+
records: List[RawBaseRecord] = values["records"]
169172

170-
values["asset_id"] = int(records[0].asset_id)
173+
if records:
174+
values["asset_id"] = int(records[0].asset_id)
171175

172176
return values
173177

174178
@pydantic.root_validator(pre=False, skip_on_failure=True)
175179
def set_company_id(cls, values: dict) -> dict:
176180
"""Calculates company_id field."""
177181

178-
records: List[RawBaseRecord] = values['records']
182+
records: List[RawBaseRecord] = values["records"]
179183

180-
values["company_id"] = int(records[0].company_id)
184+
if records:
185+
values["company_id"] = int(records[0].company_id)
181186

182187
return values
183188

189+
@pydantic.validator("records", pre=True)
190+
def validate_records(cls, v):
191+
if isinstance(v, List):
192+
return [
193+
record
194+
for record in v
195+
if (
196+
(isinstance(record, dict) and record.get("data") is not None)
197+
or (hasattr(record, "data") and record.data is not None)
198+
)
199+
]
200+
return v
201+
184202

185203
class RawStreamTimeEvent(RawStreamEvent):
186204
records: RecordsTime
187205
rerun: Optional[RerunTime] = None
188-
_max_record_value_cache_key: ClassVar[str] = 'last_processed_timestamp'
206+
_max_record_value_cache_key: ClassVar[str] = "last_processed_timestamp"
189207

190208

191209
class RawStreamDepthEvent(RawStreamEvent):
192210
records: RecordsDepth
193211
rerun: Optional[RerunDepth] = None
194-
_max_record_value_cache_key: ClassVar[str] = 'last_processed_depth'
212+
_max_record_value_cache_key: ClassVar[str] = "last_processed_depth"
195213
log_identifier: str = None # type: ignore
196214

197215
@pydantic.root_validator(pre=False, skip_on_failure=True)

tests/unit/test_stream_app.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,3 +615,42 @@ def stream_app(event, api, cache):
615615
result_event: StreamDepthEvent = stream_app(event, context)[0]
616616

617617
assert result_event.log_identifier == 'log_identifier'
618+
619+
620+
def test_raw_stream_event_with_none_data_field_returns_expected_result(context):
621+
"""Make sure that raw stream event with empty data field
622+
can be handled without validation exception.
623+
"""
624+
625+
@stream
626+
def stream_app(event, api, cache):
627+
pytest.fail(
628+
"Stream app call should be skipped "
629+
"because there is no data to build an event"
630+
)
631+
632+
event = [
633+
{
634+
"metadata": {
635+
"app_stream_id": 123,
636+
"apps": {"test-provider.test-app-name": {"app_connection_id": 456}},
637+
"log_type": "time",
638+
"source_type": "drilling",
639+
},
640+
"records": [
641+
{
642+
"app": "corva.wits-historical-import",
643+
"asset_id": 1,
644+
"collection": "wits.completed",
645+
"company_id": 80,
646+
"data": None,
647+
"provider": "corva",
648+
"timestamp": 1688999883,
649+
"version": 1,
650+
} # DEVC-627. This record should be filtered out because data is None.
651+
],
652+
}
653+
]
654+
655+
_ = stream_app(event, context)[0]
656+
assert True, "App call should be skipped"

0 commit comments

Comments
 (0)