Skip to content

Commit 86eb221

Browse files
authored
add some tests for manifests with references and some additional invalid test cases and error handling refinement (#19528)
* add some tests for manifests with references and some additional invalid test cases and error handling refinement * pr feedback to use str * remove print
1 parent 5219a72 commit 86eb221

File tree

3 files changed

+113
-9
lines changed

3 files changed

+113
-9
lines changed

airbyte-connector-builder-server/connector_builder/impl/default_api.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from connector_builder.generated.models.streams_list_request_body import StreamsListRequestBody
2222
from connector_builder.impl.low_code_cdk_adapter import LowCodeSourceAdapter
2323
from fastapi import Body, HTTPException
24-
from jsonschema import ValidationError
2524

2625

2726
class DefaultApiImpl(DefaultApi):
@@ -97,7 +96,7 @@ async def list_streams(self, streams_list_request_body: StreamsListRequestBody =
9796
)
9897
)
9998
except Exception as error:
100-
raise HTTPException(status_code=400, detail=f"Could not list streams with with error: {error.args[0]}")
99+
raise HTTPException(status_code=400, detail=f"Could not list streams with with error: {str(error)}")
101100
return StreamsListRead(streams=stream_list_read)
102101

103102
async def read_stream(self, stream_read_request_body: StreamReadRequestBody = Body(None, description="")) -> StreamRead:
@@ -121,7 +120,7 @@ async def read_stream(self, stream_read_request_body: StreamReadRequestBody = Bo
121120
single_slice.pages.append(message_group)
122121
except Exception as error:
123122
# TODO: We're temporarily using FastAPI's default exception model. Ideally we should use exceptions defined in the OpenAPI spec
124-
raise HTTPException(status_code=400, detail=f"Could not perform read with with error: {error.args[0]}")
123+
raise HTTPException(status_code=400, detail=f"Could not perform read with with error: {str(error)}")
125124

126125
return StreamRead(logs=log_messages, slices=[single_slice])
127126

@@ -199,6 +198,6 @@ def _create_response_from_log_message(self, log_message: AirbyteLogMessage) -> O
199198
def _create_low_code_adapter(manifest: Dict[str, Any]) -> LowCodeSourceAdapter:
200199
try:
201200
return LowCodeSourceAdapter(manifest=manifest)
202-
except ValidationError as error:
201+
except Exception as error:
203202
# TODO: We're temporarily using FastAPI's default exception model. Ideally we should use exceptions defined in the OpenAPI spec
204-
raise HTTPException(status_code=400, detail=f"Invalid connector manifest with error: {error.message}")
203+
raise HTTPException(status_code=400, detail=f"Invalid connector manifest with error: {str(error)}")

airbyte-connector-builder-server/unit_tests/connector_builder/impl/test_default_api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ def test_invalid_manifest():
352352
)
353353

354354
assert actual_exception.value.status_code == expected_status_code
355-
assert actual_exception.value.detail == expected_detail
355+
assert expected_detail in actual_exception.value.detail
356356

357357

358358
def test_read_stream_invalid_group_format():
@@ -380,7 +380,7 @@ def test_read_stream_invalid_group_format():
380380

381381
def test_read_stream_returns_error_if_stream_does_not_exist():
382382
expected_status_code = 400
383-
expected_detail = "Could not perform read with with error: The requested stream not_in_manifest was not found in the source. Available streams: dict_keys(['hashiras', 'breathing-techniques'])"
383+
expected_detail = "Could not perform read with with error: \"The requested stream not_in_manifest was not found in the source. Available streams: dict_keys(['hashiras', 'breathing-techniques'])\""
384384

385385
api = DefaultApiImpl()
386386
loop = asyncio.get_event_loop()
@@ -390,7 +390,7 @@ def test_read_stream_returns_error_if_stream_does_not_exist():
390390
)
391391

392392
assert actual_exception.value.status_code == expected_status_code
393-
assert actual_exception.value.detail == expected_detail
393+
assert expected_detail in actual_exception.value.detail
394394

395395

396396
@pytest.mark.parametrize(

airbyte-connector-builder-server/unit_tests/connector_builder/impl/test_low_code_cdk_adapter.py

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import requests
1111
from airbyte_cdk.models import AirbyteLogMessage, AirbyteMessage, AirbyteRecordMessage, Level, Type
1212
from airbyte_cdk.sources.declarative.declarative_stream import DeclarativeStream
13+
from airbyte_cdk.sources.declarative.parsers.undefined_reference_exception import UndefinedReferenceException
1314
from airbyte_cdk.sources.streams.http import HttpStream
1415
from connector_builder.impl.low_code_cdk_adapter import LowCodeSourceAdapter
1516

@@ -18,6 +19,7 @@ class MockConcreteStream(HttpStream, ABC):
1819
"""
1920
Test class used to verify errors are correctly thrown when the adapter receives unexpected outputs
2021
"""
22+
2123
def primary_key(self) -> Optional[Union[str, List[str], List[List[str]]]]:
2224
return None
2325

@@ -94,6 +96,69 @@ def parse_response(
9496
],
9597
"check": {"stream_names": ["hashiras"], "class_name": "airbyte_cdk.sources.declarative.checks.check_stream.CheckStream"},
9698
}
99+
100+
MANIFEST_WITH_REFERENCES = {
101+
"version": "0.1.0",
102+
"definitions": {
103+
"selector": {
104+
"extractor": {
105+
"field_pointer": []
106+
}
107+
},
108+
"requester": {
109+
"url_base": "https://demonslayers.com/api/v1/",
110+
"http_method": "GET",
111+
"authenticator": {
112+
"type": "BearerAuthenticator",
113+
"api_token": "{{ config['api_key'] }}"
114+
}
115+
},
116+
"retriever": {
117+
"record_selector": {
118+
"$ref": "*ref(definitions.selector)"
119+
},
120+
"paginator": {
121+
"type": "NoPagination"
122+
},
123+
"requester": {
124+
"$ref": "*ref(definitions.requester)"
125+
}
126+
},
127+
"base_stream": {
128+
"retriever": {
129+
"$ref": "*ref(definitions.retriever)"
130+
}
131+
},
132+
"ranks_stream": {
133+
"$ref": "*ref(definitions.base_stream)",
134+
"$options": {
135+
"name": "ranks",
136+
"primary_key": "id",
137+
"path": "/ranks"
138+
}
139+
}
140+
},
141+
"streams": ["*ref(definitions.ranks_stream)"],
142+
"check": {
143+
"stream_names": ["ranks"]
144+
},
145+
"spec": {
146+
"documentation_url": "https://docsurl.com",
147+
"connection_specification": {
148+
"title": "Source Name Spec",
149+
"type": "object",
150+
"required": ["api_key"],
151+
"additionalProperties": True,
152+
"properties": {
153+
"api_key": {
154+
"type": "string",
155+
"description": "API Key"
156+
}
157+
}
158+
}
159+
}
160+
}
161+
97162
INVALID_MANIFEST = {
98163
"version": "0.1.0",
99164
"definitions": {
@@ -128,6 +193,17 @@ def test_get_http_streams():
128193
assert actual_urls == expected_urls
129194

130195

196+
def test_get_http_manifest_with_references():
197+
expected_urls = {"https://demonslayers.com/api/v1/ranks"}
198+
199+
adapter = LowCodeSourceAdapter(MANIFEST_WITH_REFERENCES)
200+
actual_streams = adapter.get_http_streams(config={})
201+
actual_urls = {http_stream.url_base + http_stream.path() for http_stream in actual_streams}
202+
203+
assert len(actual_streams) == len(expected_urls)
204+
assert actual_urls == expected_urls
205+
206+
131207
def test_get_http_streams_non_declarative_streams():
132208
non_declarative_stream = MockConcreteStream()
133209

@@ -141,7 +217,8 @@ def test_get_http_streams_non_declarative_streams():
141217

142218

143219
def test_get_http_streams_non_http_stream():
144-
declarative_stream_non_http_retriever = DeclarativeStream(name="hashiras", primary_key="id", retriever=MagicMock(), config={}, options={})
220+
declarative_stream_non_http_retriever = DeclarativeStream(name="hashiras", primary_key="id", retriever=MagicMock(), config={},
221+
options={})
145222

146223
mock_source = MagicMock()
147224
mock_source.streams.return_value = [declarative_stream_non_http_retriever]
@@ -182,3 +259,31 @@ def test_read_streams():
182259

183260
for i, expected_message in enumerate(expected_messages):
184261
assert actual_messages[i] == expected_message
262+
263+
264+
def test_read_streams_invalid_reference():
265+
invalid_reference_manifest = {
266+
"version": "0.1.0",
267+
"definitions": {
268+
"selector": {
269+
"extractor": {
270+
"field_pointer": []
271+
}
272+
},
273+
"ranks_stream": {
274+
"$ref": "*ref(definitions.base_stream)",
275+
"$options": {
276+
"name": "ranks",
277+
"primary_key": "id",
278+
"path": "/ranks"
279+
}
280+
}
281+
},
282+
"streams": ["*ref(definitions.ranks_stream)"],
283+
"check": {
284+
"stream_names": ["ranks"]
285+
}
286+
}
287+
288+
with pytest.raises(UndefinedReferenceException):
289+
LowCodeSourceAdapter(invalid_reference_manifest)

0 commit comments

Comments
 (0)