Skip to content

Commit 3fd13f4

Browse files
committed
add update gtfs-rt fixes and unit test coverage
1 parent 5656197 commit 3fd13f4

File tree

7 files changed

+251
-15
lines changed

7 files changed

+251
-15
lines changed

functions-python/operations_api/src/feeds_operations/impl/feeds_operations_impl.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import logging
1818
import os
19-
from typing import Annotated, Type
19+
from typing import Annotated
2020

2121
from deepdiff import DeepDiff
2222
from fastapi import HTTPException
@@ -50,7 +50,7 @@ class OperationsApiImpl(BaseOperationsApi):
5050
def detect_changes(
5151
feed: Gtfsfeed,
5252
update_request_feed: UpdateRequestGtfsFeed | UpdateRequestGtfsRtFeed,
53-
impl_class: Type[UpdateRequestGtfsFeedImpl] | Type[UpdateRequestGtfsRtFeedImpl],
53+
impl_class: UpdateRequestGtfsFeedImpl | UpdateRequestGtfsRtFeedImpl,
5454
) -> DeepDiff:
5555
"""
5656
Detect changes between the feed and the update request.
@@ -74,7 +74,7 @@ def detect_changes(
7474
logging.info("Detect update changes: no changes detected")
7575
return diff
7676

77-
@validate_request(Type[UpdateRequestGtfsFeed], "update_request_gtfs_feed")
77+
@validate_request(UpdateRequestGtfsFeed, "update_request_gtfs_feed")
7878
async def update_gtfs_feed(
7979
self,
8080
update_request_gtfs_feed: Annotated[
@@ -92,7 +92,7 @@ async def update_gtfs_feed(
9292
...
9393
return await self._update_feed(update_request_gtfs_feed, DataType.GTFS)
9494

95-
@validate_request(Type[UpdateRequestGtfsRtFeed], "update_request_gtfs_rt_feed")
95+
@validate_request(UpdateRequestGtfsRtFeed, "update_request_gtfs_rt_feed")
9696
async def update_gtfs_rt_feed(
9797
self,
9898
update_request_gtfs_rt_feed: Annotated[
@@ -121,7 +121,7 @@ async def _update_feed(
121121
try:
122122
session = start_db_session(os.getenv("FEEDS_DATABASE_URL"))
123123
feed: Gtfsfeed = query_feed_by_stable_id(
124-
session, update_request_feed.id, data_type.name
124+
session, update_request_feed.id, data_type.value
125125
)
126126
if feed is None:
127127
raise HTTPException(

functions-python/operations_api/src/feeds_operations/impl/models/entity_type_impl.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
from pydantic import BaseModel
2+
13
from feeds_operations_gen.models.entity_type import EntityType
24
from database_gen.sqlacodegen_models import Entitytype as EntityTypeOrm
35

46

5-
class EntityTypeImpl(EntityType):
7+
class EntityTypeImpl(BaseModel):
68
"""Implementation of the EntityType model.
79
This class converts a SQLAlchemy row DB object with the gtfs feed fields to a Pydantic model.
810
"""
@@ -21,11 +23,11 @@ def from_orm(cls, obj: EntityTypeOrm | None) -> EntityType | None:
2123
"""
2224
if obj is None:
2325
return None
24-
return EntityType(obj.name)
26+
return EntityType(obj.name.lower())
2527

2628
@classmethod
2729
def to_orm(cls, entity_type: EntityType) -> EntityTypeOrm:
2830
"""
2931
Convert a Pydantic model to a SQLAlchemy row object.
3032
"""
31-
return EntityTypeOrm(name=entity_type.name)
33+
return EntityTypeOrm(name=entity_type.name.upper())

functions-python/operations_api/tests/conftest.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,26 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
#
16-
from database_gen.sqlacodegen_models import Gtfsfeed
16+
from database_gen.sqlacodegen_models import Gtfsfeed, Gtfsrealtimefeed, Entitytype
1717
from test_utils.database_utils import clean_testing_db, get_testing_session
1818

19+
feed_mdb_41 = Gtfsrealtimefeed(
20+
id="mdb-41",
21+
data_type="gtfs_rt",
22+
feed_name="London Transit Commission(RT",
23+
note="note",
24+
producer_url="producer_url",
25+
authentication_type="1",
26+
authentication_info_url="authentication_info_url",
27+
api_key_parameter_name="api_key_parameter_name",
28+
license_url="license_url",
29+
stable_id="mdb-41",
30+
status="active",
31+
feed_contact_email="feed_contact_email",
32+
provider="provider",
33+
entitytypes=[Entitytype(name="vp")],
34+
)
35+
1936
feed_mdb_40 = Gtfsfeed(
2037
id="mdb-40",
2138
data_type="gtfs",
@@ -30,22 +47,20 @@
3047
status="active",
3148
feed_contact_email="feed_contact_email",
3249
provider="provider",
50+
# gtfs_rt_feeds=[feed_mdb_41],
3351
)
3452

3553

3654
def populate_database():
3755
"""
3856
Populates the database with fake data with the following distribution:
3957
- 1 GTFS feeds
40-
- 5 active
41-
- 5 inactive
42-
- 5 GTFS Realtime feeds
43-
- 9 GTFS datasets
44-
- 3 active in active feeds
45-
- 6 active in inactive feeds
58+
- 1 GTFS Realtime feeds
4659
"""
4760
session = get_testing_session()
4861

62+
session.add(feed_mdb_41)
63+
# session.flush()
4964
session.add(feed_mdb_40)
5065
session.commit()
5166

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from database_gen.sqlacodegen_models import Entitytype
2+
from feeds_operations.impl.models.entity_type_impl import EntityTypeImpl
3+
from feeds_operations_gen.models.entity_type import EntityType
4+
5+
6+
def test_from_orm():
7+
entity_type = Entitytype(name="VP")
8+
result = EntityTypeImpl.from_orm(entity_type)
9+
assert result.name == "VP"
10+
11+
12+
def test_from_orm_none():
13+
result = EntityTypeImpl.from_orm(None)
14+
assert result is None
15+
16+
17+
def test_to_orm():
18+
entity_type = EntityType("vp")
19+
result = EntityTypeImpl.to_orm(entity_type)
20+
assert result.name == "VP"
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
from unittest.mock import MagicMock
2+
from database_gen.sqlacodegen_models import (
3+
Gtfsfeed,
4+
Redirectingid,
5+
Externalid,
6+
Gtfsrealtimefeed,
7+
)
8+
from feeds_operations.impl.models.update_request_gtfs_rt_feed_impl import (
9+
UpdateRequestGtfsRtFeedImpl,
10+
)
11+
from feeds_operations_gen.models.authentication_type import AuthenticationType
12+
from feeds_operations_gen.models.entity_type import EntityType
13+
from feeds_operations_gen.models.feed_status import FeedStatus
14+
from feeds_operations_gen.models.source_info import SourceInfo
15+
from feeds_operations_gen.models.update_request_gtfs_rt_feed import (
16+
UpdateRequestGtfsRtFeed,
17+
)
18+
from operations_api.src.feeds_operations.impl.models.redirect_impl import RedirectImpl
19+
from operations_api.src.feeds_operations.impl.models.external_id_impl import (
20+
ExternalIdImpl,
21+
)
22+
23+
24+
def test_from_orm():
25+
redirecting_id = Redirectingid(target=MagicMock(stable_id="target_stable_id"))
26+
external_id = Externalid(associated_id="external_id")
27+
gtfs_feed = Gtfsrealtimefeed(
28+
stable_id="stable_id",
29+
status="active",
30+
provider="provider",
31+
feed_name="feed_name",
32+
note="note",
33+
feed_contact_email="[email protected]",
34+
producer_url="http://producer.url",
35+
authentication_type=1,
36+
authentication_info_url="http://auth.info.url",
37+
api_key_parameter_name="api_key",
38+
license_url="http://license.url",
39+
redirectingids=[redirecting_id],
40+
externalids=[external_id],
41+
)
42+
43+
result = UpdateRequestGtfsRtFeedImpl.from_orm(gtfs_feed)
44+
assert result.id == "stable_id"
45+
assert result.status == "active"
46+
assert result.provider == "provider"
47+
assert result.feed_name == "feed_name"
48+
assert result.note == "note"
49+
assert result.feed_contact_email == "[email protected]"
50+
assert result.source_info.producer_url == "http://producer.url"
51+
assert result.source_info.authentication_type == 1
52+
assert result.source_info.authentication_info_url == "http://auth.info.url"
53+
assert result.source_info.api_key_parameter_name == "api_key"
54+
assert result.source_info.license_url == "http://license.url"
55+
assert len(result.redirects) == 1
56+
assert result.redirects[0].target_id == "target_stable_id"
57+
assert len(result.external_ids) == 1
58+
assert result.external_ids[0].external_id == "external_id"
59+
60+
61+
def test_from_orm_none():
62+
result = UpdateRequestGtfsRtFeedImpl.from_orm(None)
63+
assert result is None
64+
65+
66+
def test_to_orm():
67+
update_request = UpdateRequestGtfsRtFeed(
68+
id="stable_id",
69+
status=FeedStatus.ACTIVE,
70+
provider="provider",
71+
feed_name="feed_name",
72+
note="note",
73+
feed_contact_email="[email protected]",
74+
source_info=SourceInfo(
75+
producer_url="http://producer.url",
76+
authentication_type=AuthenticationType.NUMBER_1,
77+
authentication_info_url="http://auth.info.url",
78+
api_key_parameter_name="api_key",
79+
license_url="http://license.url",
80+
),
81+
redirects=[RedirectImpl(target_id="target_stable_id", comment="Test comment")],
82+
external_ids=[ExternalIdImpl(external_id="external_id")],
83+
entity_types=[EntityType.VP],
84+
feed_references=["feed_reference"],
85+
)
86+
entity = Gtfsrealtimefeed(id="1", stable_id="stable_id", data_type="gtfs")
87+
target_feed = Gtfsfeed(id=2, stable_id="target_stable_id")
88+
session = MagicMock()
89+
session.query.return_value.filter.return_value.first.return_value = target_feed
90+
91+
result = UpdateRequestGtfsRtFeedImpl.to_orm(update_request, entity, session)
92+
assert result.status == "active"
93+
assert result.provider == "provider"
94+
assert result.feed_name == "feed_name"
95+
assert result.note == "note"
96+
assert result.feed_contact_email == "[email protected]"
97+
assert result.producer_url == "http://producer.url"
98+
assert result.authentication_type == "1"
99+
assert result.authentication_info_url == "http://auth.info.url"
100+
assert result.api_key_parameter_name == "api_key"
101+
assert result.license_url == "http://license.url"
102+
assert len(result.redirectingids) == 1
103+
assert result.redirectingids[0].target_id == target_feed.id
104+
assert len(result.externalids) == 1
105+
assert result.externalids[0].associated_id == "external_id"
106+
107+
108+
def test_to_orm_invalid_source_info():
109+
update_request = UpdateRequestGtfsRtFeed(
110+
id="stable_id",
111+
status=FeedStatus.ACTIVE,
112+
provider="provider",
113+
feed_name="feed_name",
114+
note="note",
115+
feed_contact_email="[email protected]",
116+
source_info=None,
117+
redirects=[RedirectImpl(target_id="target_stable_id", comment="Test comment")],
118+
external_ids=[ExternalIdImpl(external_id="external_id")],
119+
entity_types=[EntityType.VP],
120+
feed_references=["feed_reference"],
121+
)
122+
entity = Gtfsrealtimefeed(id="1", stable_id="stable_id", data_type="gtfs")
123+
target_feed = Gtfsfeed(id=2, stable_id="target_stable_id")
124+
session = MagicMock()
125+
session.query.return_value.filter.return_value.first.return_value = target_feed
126+
127+
result = UpdateRequestGtfsRtFeedImpl.to_orm(update_request, entity, session)
128+
129+
assert result.producer_url is None
130+
assert result.authentication_type is None
131+
assert result.authentication_info_url is None
132+
assert result.api_key_parameter_name is None
133+
assert result.license_url is None

functions-python/operations_api/tests/feeds_operations/impl/test_feeds_operations_impl.py renamed to functions-python/operations_api/tests/feeds_operations/impl/test_feeds_operations_impl_gtfs.py

File renamed without changes.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import os
2+
from unittest import mock
3+
from unittest.mock import patch
4+
5+
import pytest
6+
from starlette.responses import Response
7+
8+
from database_gen.sqlacodegen_models import Gtfsrealtimefeed
9+
from feeds_operations.impl.feeds_operations_impl import OperationsApiImpl
10+
from feeds_operations_gen.models.authentication_type import AuthenticationType
11+
from feeds_operations_gen.models.entity_type import EntityType
12+
from feeds_operations_gen.models.feed_status import FeedStatus
13+
from feeds_operations_gen.models.source_info import SourceInfo
14+
from feeds_operations_gen.models.update_request_gtfs_rt_feed import (
15+
UpdateRequestGtfsRtFeed,
16+
)
17+
from operations_api.tests.conftest import feed_mdb_41
18+
from test_utils.database_utils import get_testing_session, default_db_url
19+
20+
21+
@pytest.fixture
22+
def update_request_gtfs_rt_feed():
23+
return UpdateRequestGtfsRtFeed(
24+
id=feed_mdb_41.stable_id,
25+
status=FeedStatus(feed_mdb_41.status.lower()),
26+
external_ids=[],
27+
provider=feed_mdb_41.provider,
28+
feed_name=feed_mdb_41.feed_name,
29+
note=feed_mdb_41.note,
30+
feed_contact_email=feed_mdb_41.feed_contact_email,
31+
source_info=SourceInfo(
32+
producer_url=feed_mdb_41.producer_url,
33+
authentication_type=AuthenticationType(
34+
int(feed_mdb_41.authentication_type)
35+
),
36+
authentication_info_url=feed_mdb_41.authentication_info_url,
37+
api_key_parameter_name=feed_mdb_41.api_key_parameter_name,
38+
license_url=feed_mdb_41.license_url,
39+
),
40+
redirects=[],
41+
operational_status_action="no_change",
42+
entity_types=[EntityType.VP],
43+
)
44+
45+
46+
@patch("helpers.logger.Logger")
47+
@mock.patch.dict(
48+
os.environ,
49+
{
50+
"FEEDS_DATABASE_URL": default_db_url,
51+
},
52+
)
53+
@pytest.mark.asyncio
54+
async def test_update_gtfs_feed_field_change(_, update_request_gtfs_rt_feed):
55+
update_request_gtfs_rt_feed.feed_name = "New feed name"
56+
with get_testing_session() as session:
57+
api = OperationsApiImpl()
58+
response: Response = await api.update_gtfs_rt_feed(update_request_gtfs_rt_feed)
59+
assert response.status_code == 200
60+
61+
db_feed = (
62+
session.query(Gtfsrealtimefeed)
63+
.filter(Gtfsrealtimefeed.stable_id == feed_mdb_41.stable_id)
64+
.one()
65+
)
66+
assert db_feed.feed_name == "New feed name"

0 commit comments

Comments
 (0)