@@ -1231,6 +1231,235 @@ def test_read_with_concurrent_and_synchronous_streams_with_sequential_state():
1231
1231
assert len (party_members_skills_records ) == 9
1232
1232
1233
1233
1234
+ @freezegun .freeze_time (_NOW )
1235
+ def test_read_with_state_when_state_migration_was_provided ():
1236
+ manifest = {
1237
+ "version" : "5.0.0" ,
1238
+ "definitions" : {
1239
+ "selector" : {
1240
+ "type" : "RecordSelector" ,
1241
+ "extractor" : {"type" : "DpathExtractor" , "field_path" : []},
1242
+ },
1243
+ "requester" : {
1244
+ "type" : "HttpRequester" ,
1245
+ "url_base" : "https://persona.metaverse.com" ,
1246
+ "http_method" : "GET" ,
1247
+ "authenticator" : {
1248
+ "type" : "BasicHttpAuthenticator" ,
1249
+ "username" : "{{ config['api_key'] }}" ,
1250
+ "password" : "{{ config['secret_key'] }}" ,
1251
+ },
1252
+ "error_handler" : {
1253
+ "type" : "DefaultErrorHandler" ,
1254
+ "response_filters" : [
1255
+ {
1256
+ "http_codes" : [403 ],
1257
+ "action" : "FAIL" ,
1258
+ "failure_type" : "config_error" ,
1259
+ "error_message" : "Access denied due to lack of permission or invalid API/Secret key or wrong data region." ,
1260
+ },
1261
+ {
1262
+ "http_codes" : [404 ],
1263
+ "action" : "IGNORE" ,
1264
+ "error_message" : "No data available for the time range requested." ,
1265
+ },
1266
+ ],
1267
+ },
1268
+ },
1269
+ "retriever" : {
1270
+ "type" : "SimpleRetriever" ,
1271
+ "record_selector" : {"$ref" : "#/definitions/selector" },
1272
+ "paginator" : {"type" : "NoPagination" },
1273
+ "requester" : {"$ref" : "#/definitions/requester" },
1274
+ },
1275
+ "incremental_cursor" : {
1276
+ "type" : "DatetimeBasedCursor" ,
1277
+ "start_datetime" : {
1278
+ "datetime" : "{{ format_datetime(config['start_date'], '%Y-%m-%d') }}"
1279
+ },
1280
+ "end_datetime" : {"datetime" : "{{ now_utc().strftime('%Y-%m-%d') }}" },
1281
+ "datetime_format" : "%Y-%m-%d" ,
1282
+ "cursor_datetime_formats" : ["%Y-%m-%d" , "%Y-%m-%dT%H:%M:%S" ],
1283
+ "cursor_granularity" : "P1D" ,
1284
+ "step" : "P15D" ,
1285
+ "cursor_field" : "updated_at" ,
1286
+ "lookback_window" : "P5D" ,
1287
+ "start_time_option" : {
1288
+ "type" : "RequestOption" ,
1289
+ "field_name" : "start" ,
1290
+ "inject_into" : "request_parameter" ,
1291
+ },
1292
+ "end_time_option" : {
1293
+ "type" : "RequestOption" ,
1294
+ "field_name" : "end" ,
1295
+ "inject_into" : "request_parameter" ,
1296
+ },
1297
+ },
1298
+ "base_stream" : {"retriever" : {"$ref" : "#/definitions/retriever" }},
1299
+ "base_incremental_stream" : {
1300
+ "retriever" : {
1301
+ "$ref" : "#/definitions/retriever" ,
1302
+ "requester" : {"$ref" : "#/definitions/requester" },
1303
+ },
1304
+ "incremental_sync" : {"$ref" : "#/definitions/incremental_cursor" },
1305
+ },
1306
+ "party_members_stream" : {
1307
+ "$ref" : "#/definitions/base_incremental_stream" ,
1308
+ "retriever" : {
1309
+ "$ref" : "#/definitions/base_incremental_stream/retriever" ,
1310
+ "requester" : {
1311
+ "$ref" : "#/definitions/requester" ,
1312
+ "request_parameters" : {"filter" : "{{stream_partition['type']}}" },
1313
+ },
1314
+ "record_selector" : {"$ref" : "#/definitions/selector" },
1315
+ "partition_router" : [
1316
+ {
1317
+ "type" : "ListPartitionRouter" ,
1318
+ "values" : ["type_1" , "type_2" ],
1319
+ "cursor_field" : "type" ,
1320
+ }
1321
+ ],
1322
+ },
1323
+ "$parameters" : {
1324
+ "name" : "party_members" ,
1325
+ "primary_key" : "id" ,
1326
+ "path" : "/party_members" ,
1327
+ },
1328
+ "state_migrations" : [
1329
+ {
1330
+ "type" : "CustomStateMigration" ,
1331
+ "class_name" : "unit_tests.sources.declarative.custom_state_migration.CustomStateMigration" ,
1332
+ }
1333
+ ],
1334
+ "schema_loader" : {
1335
+ "type" : "InlineSchemaLoader" ,
1336
+ "schema" : {
1337
+ "$schema" : "https://json-schema.org/draft-07/schema#" ,
1338
+ "type" : "object" ,
1339
+ "properties" : {
1340
+ "id" : {
1341
+ "description" : "The identifier" ,
1342
+ "type" : ["null" , "string" ],
1343
+ },
1344
+ "name" : {
1345
+ "description" : "The name of the party member" ,
1346
+ "type" : ["null" , "string" ],
1347
+ },
1348
+ },
1349
+ },
1350
+ },
1351
+ },
1352
+ },
1353
+ "streams" : [
1354
+ "#/definitions/party_members_stream" ,
1355
+ ],
1356
+ "check" : {"stream_names" : ["party_members" , "locations" ]},
1357
+ "concurrency_level" : {
1358
+ "type" : "ConcurrencyLevel" ,
1359
+ "default_concurrency" : "{{ config['num_workers'] or 10 }}" ,
1360
+ "max_concurrency" : 25 ,
1361
+ },
1362
+ }
1363
+ state = [
1364
+ AirbyteStateMessage (
1365
+ type = AirbyteStateType .STREAM ,
1366
+ stream = AirbyteStreamState (
1367
+ stream_descriptor = StreamDescriptor (name = "party_members" , namespace = None ),
1368
+ stream_state = AirbyteStateBlob (updated_at = "2024-08-21" ),
1369
+ ),
1370
+ ),
1371
+ ]
1372
+ catalog = ConfiguredAirbyteCatalog (
1373
+ streams = [
1374
+ ConfiguredAirbyteStream (
1375
+ stream = AirbyteStream (
1376
+ name = "party_members" ,
1377
+ json_schema = {},
1378
+ supported_sync_modes = [SyncMode .incremental ],
1379
+ ),
1380
+ sync_mode = SyncMode .incremental ,
1381
+ destination_sync_mode = DestinationSyncMode .append ,
1382
+ )
1383
+ ]
1384
+ )
1385
+ source = ConcurrentDeclarativeSource (
1386
+ source_config = manifest , config = _CONFIG , catalog = _CATALOG , state = state
1387
+ )
1388
+ party_members_slices_and_responses = [
1389
+ (
1390
+ {"start" : "2024-08-16" , "end" : "2024-08-30" , "filter" : "type_1" },
1391
+ HttpResponse (
1392
+ json .dumps (
1393
+ [
1394
+ {
1395
+ "id" : "nijima" ,
1396
+ "first_name" : "makoto" ,
1397
+ "last_name" : "nijima" ,
1398
+ "updated_at" : "2024-08-10" ,
1399
+ "type" : 1 ,
1400
+ }
1401
+ ]
1402
+ )
1403
+ ),
1404
+ ),
1405
+ (
1406
+ {"start" : "2024-08-16" , "end" : "2024-08-30" , "filter" : "type_2" },
1407
+ HttpResponse (
1408
+ json .dumps (
1409
+ [
1410
+ {
1411
+ "id" : "nijima" ,
1412
+ "first_name" : "makoto" ,
1413
+ "last_name" : "nijima" ,
1414
+ "updated_at" : "2024-08-10" ,
1415
+ "type" : 2 ,
1416
+ }
1417
+ ]
1418
+ )
1419
+ ),
1420
+ ),
1421
+ (
1422
+ {"start" : "2024-08-31" , "end" : "2024-09-10" , "filter" : "type_1" },
1423
+ HttpResponse (
1424
+ json .dumps (
1425
+ [
1426
+ {
1427
+ "id" : "yoshizawa" ,
1428
+ "first_name" : "sumire" ,
1429
+ "last_name" : "yoshizawa" ,
1430
+ "updated_at" : "2024-09-10" ,
1431
+ "type" : 1 ,
1432
+ }
1433
+ ]
1434
+ )
1435
+ ),
1436
+ ),
1437
+ (
1438
+ {"start" : "2024-08-31" , "end" : "2024-09-10" , "filter" : "type_2" },
1439
+ HttpResponse (
1440
+ json .dumps (
1441
+ [
1442
+ {
1443
+ "id" : "yoshizawa" ,
1444
+ "first_name" : "sumire" ,
1445
+ "last_name" : "yoshizawa" ,
1446
+ "updated_at" : "2024-09-10" ,
1447
+ "type" : 2 ,
1448
+ }
1449
+ ]
1450
+ )
1451
+ ),
1452
+ ),
1453
+ ]
1454
+ with HttpMocker () as http_mocker :
1455
+ _mock_party_members_requests (http_mocker , party_members_slices_and_responses )
1456
+ messages = list (
1457
+ source .read (logger = source .logger , config = _CONFIG , catalog = catalog , state = state )
1458
+ )
1459
+ final_state = get_states_for_stream (stream_name = "party_members" , messages = messages )
1460
+ assert state not in final_state
1461
+
1462
+
1234
1463
@freezegun .freeze_time (_NOW )
1235
1464
@patch (
1236
1465
"airbyte_cdk.sources.streams.concurrent.state_converters.abstract_stream_state_converter.AbstractStreamStateConverter.__init__" ,
0 commit comments