airbyte-cdk 6.48.17.dev0__py3-none-any.whl → 6.49.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. airbyte_cdk/cli/source_declarative_manifest/_run.py +69 -10
  2. airbyte_cdk/connector.py +3 -3
  3. airbyte_cdk/entrypoint.py +36 -0
  4. airbyte_cdk/sources/declarative/declarative_component_schema.yaml +274 -68
  5. airbyte_cdk/sources/declarative/extractors/__init__.py +0 -4
  6. airbyte_cdk/sources/declarative/manifest_declarative_source.py +3 -1
  7. airbyte_cdk/sources/declarative/models/declarative_component_schema.py +189 -44
  8. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +155 -60
  9. airbyte_cdk/sources/declarative/resolvers/components_resolver.py +0 -2
  10. airbyte_cdk/sources/declarative/resolvers/config_components_resolver.py +14 -49
  11. airbyte_cdk/sources/declarative/schema/dynamic_schema_loader.py +4 -19
  12. airbyte_cdk/sources/declarative/spec/__init__.py +2 -2
  13. airbyte_cdk/sources/declarative/spec/spec.py +71 -2
  14. airbyte_cdk/sources/declarative/transformations/config_transformations/add_fields.py +10 -23
  15. airbyte_cdk/sources/declarative/transformations/config_transformations/remap_field.py +2 -3
  16. airbyte_cdk/sources/declarative/validators/dpath_validator.py +1 -1
  17. {airbyte_cdk-6.48.17.dev0.dist-info → airbyte_cdk-6.49.0.dist-info}/METADATA +1 -1
  18. {airbyte_cdk-6.48.17.dev0.dist-info → airbyte_cdk-6.49.0.dist-info}/RECORD +22 -24
  19. airbyte_cdk/sources/declarative/extractors/combined_extractor.py +0 -44
  20. airbyte_cdk/sources/declarative/extractors/key_value_extractor.py +0 -46
  21. {airbyte_cdk-6.48.17.dev0.dist-info → airbyte_cdk-6.49.0.dist-info}/LICENSE.txt +0 -0
  22. {airbyte_cdk-6.48.17.dev0.dist-info → airbyte_cdk-6.49.0.dist-info}/LICENSE_SHORT +0 -0
  23. {airbyte_cdk-6.48.17.dev0.dist-info → airbyte_cdk-6.49.0.dist-info}/WHEEL +0 -0
  24. {airbyte_cdk-6.48.17.dev0.dist-info → airbyte_cdk-6.49.0.dist-info}/entry_points.txt +0 -0
@@ -19,6 +19,7 @@ from typing import (
19
19
  Optional,
20
20
  Type,
21
21
  Union,
22
+ cast,
22
23
  get_args,
23
24
  get_origin,
24
25
  get_type_hints,
@@ -33,6 +34,7 @@ from airbyte_cdk.connector_builder.models import (
33
34
  )
34
35
  from airbyte_cdk.models import FailureType, Level
35
36
  from airbyte_cdk.sources.connector_state_manager import ConnectorStateManager
37
+ from airbyte_cdk.sources.declarative import transformations
36
38
  from airbyte_cdk.sources.declarative.async_job.job_orchestrator import AsyncJobOrchestrator
37
39
  from airbyte_cdk.sources.declarative.async_job.job_tracker import JobTracker
38
40
  from airbyte_cdk.sources.declarative.async_job.repository import AsyncJobRepository
@@ -83,9 +85,7 @@ from airbyte_cdk.sources.declarative.decoders.composite_raw_decoder import (
83
85
  Parser,
84
86
  )
85
87
  from airbyte_cdk.sources.declarative.extractors import (
86
- CombinedExtractor,
87
88
  DpathExtractor,
88
- KeyValueExtractor,
89
89
  RecordFilter,
90
90
  RecordSelector,
91
91
  ResponseToFileExtractor,
@@ -144,9 +144,6 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
144
144
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
145
145
  CheckStream as CheckStreamModel,
146
146
  )
147
- from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
148
- CombinedExtractor as CombinedExtractorModel,
149
- )
150
147
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
151
148
  ComplexFieldType as ComplexFieldTypeModel,
152
149
  )
@@ -159,9 +156,21 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
159
156
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
160
157
  ConcurrencyLevel as ConcurrencyLevelModel,
161
158
  )
159
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
160
+ ConfigAddFields as ConfigAddFieldsModel,
161
+ )
162
162
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
163
163
  ConfigComponentsResolver as ConfigComponentsResolverModel,
164
164
  )
165
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
166
+ ConfigMigration as ConfigMigrationModel,
167
+ )
168
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
169
+ ConfigRemapField as ConfigRemapFieldModel,
170
+ )
171
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
172
+ ConfigRemoveFields as ConfigRemoveFieldsModel,
173
+ )
165
174
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
166
175
  ConstantBackoffStrategy as ConstantBackoffStrategyModel,
167
176
  )
@@ -231,6 +240,9 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
231
240
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
232
241
  DpathFlattenFields as DpathFlattenFieldsModel,
233
242
  )
243
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
244
+ DpathValidator as DpathValidatorModel,
245
+ )
234
246
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
235
247
  DynamicSchemaLoader as DynamicSchemaLoaderModel,
236
248
  )
@@ -309,9 +321,6 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
309
321
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
310
322
  KeysToSnakeCase as KeysToSnakeCaseModel,
311
323
  )
312
- from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
313
- KeyValueExtractor as KeyValueExtractorModel,
314
- )
315
324
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
316
325
  LegacySessionTokenAuthenticator as LegacySessionTokenAuthenticatorModel,
317
326
  )
@@ -345,6 +354,9 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
345
354
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
346
355
  ParentStreamConfig as ParentStreamConfigModel,
347
356
  )
357
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
358
+ PredicateValidator as PredicateValidatorModel,
359
+ )
348
360
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
349
361
  PropertiesFromEndpoint as PropertiesFromEndpointModel,
350
362
  )
@@ -409,6 +421,9 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
409
421
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
410
422
  UnlimitedCallRatePolicy as UnlimitedCallRatePolicyModel,
411
423
  )
424
+ from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
425
+ ValidateAdheresToSchema as ValidateAdheresToSchemaModel,
426
+ )
412
427
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import ValueType
413
428
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
414
429
  WaitTimeFromHeader as WaitTimeFromHeaderModel,
@@ -484,7 +499,7 @@ from airbyte_cdk.sources.declarative.requesters.request_options import (
484
499
  RequestOptionsProvider,
485
500
  )
486
501
  from airbyte_cdk.sources.declarative.requesters.request_path import RequestPath
487
- from airbyte_cdk.sources.declarative.requesters.requester import HttpMethod
502
+ from airbyte_cdk.sources.declarative.requesters.requester import HttpMethod, Requester
488
503
  from airbyte_cdk.sources.declarative.resolvers import (
489
504
  ComponentMappingDefinition,
490
505
  ConfigComponentsResolver,
@@ -514,7 +529,7 @@ from airbyte_cdk.sources.declarative.schema import (
514
529
  TypesMap,
515
530
  )
516
531
  from airbyte_cdk.sources.declarative.schema.composite_schema_loader import CompositeSchemaLoader
517
- from airbyte_cdk.sources.declarative.spec import Spec
532
+ from airbyte_cdk.sources.declarative.spec import ConfigMigration, Spec
518
533
  from airbyte_cdk.sources.declarative.stream_slicers import StreamSlicer
519
534
  from airbyte_cdk.sources.declarative.transformations import (
520
535
  AddFields,
@@ -522,6 +537,14 @@ from airbyte_cdk.sources.declarative.transformations import (
522
537
  RemoveFields,
523
538
  )
524
539
  from airbyte_cdk.sources.declarative.transformations.add_fields import AddedFieldDefinition
540
+ from airbyte_cdk.sources.declarative.transformations.config_transformations import (
541
+ ConfigAddFields,
542
+ ConfigRemapField,
543
+ ConfigRemoveFields,
544
+ )
545
+ from airbyte_cdk.sources.declarative.transformations.config_transformations.config_transformation import (
546
+ ConfigTransformation,
547
+ )
525
548
  from airbyte_cdk.sources.declarative.transformations.dpath_flatten_fields import (
526
549
  DpathFlattenFields,
527
550
  KeyTransformation,
@@ -538,6 +561,11 @@ from airbyte_cdk.sources.declarative.transformations.keys_to_lower_transformatio
538
561
  from airbyte_cdk.sources.declarative.transformations.keys_to_snake_transformation import (
539
562
  KeysToSnakeCaseTransformation,
540
563
  )
564
+ from airbyte_cdk.sources.declarative.validators import (
565
+ DpathValidator,
566
+ PredicateValidator,
567
+ ValidateAdheresToSchema,
568
+ )
541
569
  from airbyte_cdk.sources.http_logger import format_http_message
542
570
  from airbyte_cdk.sources.message import (
543
571
  InMemoryMessageRepository,
@@ -626,6 +654,10 @@ class ModelToComponentFactory:
626
654
  CheckDynamicStreamModel: self.create_check_dynamic_stream,
627
655
  CompositeErrorHandlerModel: self.create_composite_error_handler,
628
656
  ConcurrencyLevelModel: self.create_concurrency_level,
657
+ ConfigMigrationModel: self.create_config_migration,
658
+ ConfigAddFieldsModel: self.create_config_add_fields,
659
+ ConfigRemapFieldModel: self.create_config_remap_field,
660
+ ConfigRemoveFieldsModel: self.create_config_remove_fields,
629
661
  ConstantBackoffStrategyModel: self.create_constant_backoff_strategy,
630
662
  CsvDecoderModel: self.create_csv_decoder,
631
663
  CursorPaginationModel: self.create_cursor_pagination,
@@ -649,8 +681,7 @@ class ModelToComponentFactory:
649
681
  DefaultErrorHandlerModel: self.create_default_error_handler,
650
682
  DefaultPaginatorModel: self.create_default_paginator,
651
683
  DpathExtractorModel: self.create_dpath_extractor,
652
- KeyValueExtractorModel: self.create_key_value_extractor,
653
- CombinedExtractorModel: self.create_combined_extractor,
684
+ DpathValidatorModel: self.create_dpath_validator,
654
685
  ResponseToFileExtractorModel: self.create_response_to_file_extractor,
655
686
  ExponentialBackoffStrategyModel: self.create_exponential_backoff_strategy,
656
687
  SessionTokenAuthenticatorModel: self.create_session_token_authenticator,
@@ -684,6 +715,7 @@ class ModelToComponentFactory:
684
715
  OffsetIncrementModel: self.create_offset_increment,
685
716
  PageIncrementModel: self.create_page_increment,
686
717
  ParentStreamConfigModel: self.create_parent_stream_config,
718
+ PredicateValidatorModel: self.create_predicate_validator,
687
719
  PropertiesFromEndpointModel: self.create_properties_from_endpoint,
688
720
  PropertyChunkingModel: self.create_property_chunking,
689
721
  QueryPropertiesModel: self.create_query_properties,
@@ -698,6 +730,7 @@ class ModelToComponentFactory:
698
730
  StateDelegatingStreamModel: self.create_state_delegating_stream,
699
731
  SpecModel: self.create_spec,
700
732
  SubstreamPartitionRouterModel: self.create_substream_partition_router,
733
+ ValidateAdheresToSchemaModel: self.create_validate_adheres_to_schema,
701
734
  WaitTimeFromHeaderModel: self.create_wait_time_from_header,
702
735
  WaitUntilTimeFromHeaderModel: self.create_wait_until_time_from_header,
703
736
  AsyncRetrieverModel: self.create_async_retriever,
@@ -790,6 +823,75 @@ class ModelToComponentFactory:
790
823
  if log not in self._collected_deprecation_logs:
791
824
  self._collected_deprecation_logs.append(log)
792
825
 
826
+ def create_config_migration(
827
+ self, model: ConfigMigrationModel, config: Config
828
+ ) -> ConfigMigration:
829
+ transformations: List[ConfigTransformation] = [
830
+ self._create_component_from_model(transformation, config)
831
+ for transformation in model.transformations
832
+ ]
833
+
834
+ return ConfigMigration(
835
+ description=model.description,
836
+ transformations=transformations,
837
+ )
838
+
839
+ def create_config_add_fields(
840
+ self, model: ConfigAddFieldsModel, config: Config, **kwargs: Any
841
+ ) -> ConfigAddFields:
842
+ fields = [self._create_component_from_model(field, config) for field in model.fields]
843
+ return ConfigAddFields(
844
+ fields=fields,
845
+ condition=model.condition or "",
846
+ )
847
+
848
+ @staticmethod
849
+ def create_config_remove_fields(
850
+ model: ConfigRemoveFieldsModel, config: Config, **kwargs: Any
851
+ ) -> ConfigRemoveFields:
852
+ return ConfigRemoveFields(
853
+ field_pointers=model.field_pointers,
854
+ condition=model.condition or "",
855
+ )
856
+
857
+ @staticmethod
858
+ def create_config_remap_field(
859
+ model: ConfigRemapFieldModel, config: Config, **kwargs: Any
860
+ ) -> ConfigRemapField:
861
+ mapping = cast(Mapping[str, Any], model.map)
862
+ return ConfigRemapField(
863
+ map=mapping,
864
+ field_path=model.field_path,
865
+ config=config,
866
+ )
867
+
868
+ def create_dpath_validator(self, model: DpathValidatorModel, config: Config) -> DpathValidator:
869
+ strategy = self._create_component_from_model(model.validation_strategy, config)
870
+
871
+ return DpathValidator(
872
+ field_path=model.field_path,
873
+ strategy=strategy,
874
+ )
875
+
876
+ def create_predicate_validator(
877
+ self, model: PredicateValidatorModel, config: Config
878
+ ) -> PredicateValidator:
879
+ strategy = self._create_component_from_model(model.validation_strategy, config)
880
+
881
+ return PredicateValidator(
882
+ value=model.value,
883
+ strategy=strategy,
884
+ )
885
+
886
+ @staticmethod
887
+ def create_validate_adheres_to_schema(
888
+ model: ValidateAdheresToSchemaModel, config: Config, **kwargs: Any
889
+ ) -> ValidateAdheresToSchema:
890
+ base_schema = cast(Mapping[str, Any], model.base_schema)
891
+ return ValidateAdheresToSchema(
892
+ schema=base_schema,
893
+ )
894
+
793
895
  @staticmethod
794
896
  def create_added_field_definition(
795
897
  model: AddedFieldDefinitionModel, config: Config, **kwargs: Any
@@ -2228,36 +2330,6 @@ class ModelToComponentFactory:
2228
2330
  parameters=model.parameters or {},
2229
2331
  )
2230
2332
 
2231
- def create_key_value_extractor(
2232
- self,
2233
- model: KeyValueExtractorModel,
2234
- config: Config,
2235
- decoder: Optional[Decoder] = JsonDecoder(parameters={}),
2236
- **kwargs: Any,
2237
- ) -> KeyValueExtractor:
2238
- keys_extractor = self._create_component_from_model(
2239
- model=model.keys_extractor, decoder=decoder, config=config
2240
- )
2241
- values_extractor = self._create_component_from_model(
2242
- model=model.values_extractor, decoder=decoder, config=config
2243
- )
2244
-
2245
- return KeyValueExtractor(keys_extractor=keys_extractor, values_extractor=values_extractor)
2246
-
2247
- def create_combined_extractor(
2248
- self,
2249
- model: CombinedExtractorModel,
2250
- config: Config,
2251
- decoder: Optional[Decoder] = JsonDecoder(parameters={}),
2252
- **kwargs: Any,
2253
- ) -> CombinedExtractor:
2254
- extractors = [
2255
- self._create_component_from_model(model=extractor, decoder=decoder, config=config)
2256
- for extractor in model.extractors
2257
- ]
2258
-
2259
- return CombinedExtractor(extractors=extractors)
2260
-
2261
2333
  @staticmethod
2262
2334
  def create_response_to_file_extractor(
2263
2335
  model: ResponseToFileExtractorModel,
@@ -2472,14 +2544,10 @@ class ModelToComponentFactory:
2472
2544
  schema_type_identifier = self._create_component_from_model(
2473
2545
  model.schema_type_identifier, config=config, parameters=model.parameters or {}
2474
2546
  )
2475
- schema_filter = self._create_component_from_model(
2476
- model.schema_filter, config=config, parameters=model.parameters or {}
2477
- )
2478
2547
  return DynamicSchemaLoader(
2479
2548
  retriever=retriever,
2480
2549
  config=config,
2481
2550
  schema_transformations=schema_transformations,
2482
- schema_filter=schema_filter,
2483
2551
  schema_type_identifier=schema_type_identifier,
2484
2552
  parameters=model.parameters or {},
2485
2553
  )
@@ -3539,13 +3607,49 @@ class ModelToComponentFactory:
3539
3607
  parameters=model.parameters or {},
3540
3608
  )
3541
3609
 
3542
- @staticmethod
3543
- def create_spec(model: SpecModel, config: Config, **kwargs: Any) -> Spec:
3610
+ def create_spec(self, model: SpecModel, config: Config, **kwargs: Any) -> Spec:
3611
+ config_migrations = [
3612
+ self._create_component_from_model(migration, config)
3613
+ for migration in (
3614
+ model.config_normalization_rules.config_migrations
3615
+ if (
3616
+ model.config_normalization_rules
3617
+ and model.config_normalization_rules.config_migrations
3618
+ )
3619
+ else []
3620
+ )
3621
+ ]
3622
+ config_transformations = [
3623
+ self._create_component_from_model(transformation, config)
3624
+ for transformation in (
3625
+ model.config_normalization_rules.transformations
3626
+ if (
3627
+ model.config_normalization_rules
3628
+ and model.config_normalization_rules.transformations
3629
+ )
3630
+ else []
3631
+ )
3632
+ ]
3633
+ config_validations = [
3634
+ self._create_component_from_model(validation, config)
3635
+ for validation in (
3636
+ model.config_normalization_rules.validations
3637
+ if (
3638
+ model.config_normalization_rules
3639
+ and model.config_normalization_rules.validations
3640
+ )
3641
+ else []
3642
+ )
3643
+ ]
3644
+
3544
3645
  return Spec(
3545
3646
  connection_specification=model.connection_specification,
3546
3647
  documentation_url=model.documentation_url,
3547
3648
  advanced_auth=model.advanced_auth,
3548
3649
  parameters={},
3650
+ config_migrations=config_migrations,
3651
+ config_transformations=config_transformations,
3652
+ config_validations=config_validations,
3549
3653
  )
3550
3654
 
3551
3655
  def create_substream_partition_router(
@@ -3641,7 +3745,6 @@ class ModelToComponentFactory:
3641
3745
  field_path=field_path, # type: ignore[arg-type] # field_path can be str and InterpolatedString
3642
3746
  value=interpolated_value,
3643
3747
  value_type=ModelToComponentFactory._json_schema_type_name_to_type(model.value_type),
3644
- create_or_update=model.create_or_update,
3645
3748
  parameters=model.parameters or {},
3646
3749
  )
3647
3750
 
@@ -3688,24 +3791,16 @@ class ModelToComponentFactory:
3688
3791
 
3689
3792
  return StreamConfig(
3690
3793
  configs_pointer=model_configs_pointer,
3691
- default_values=model.default_values,
3692
3794
  parameters=model.parameters or {},
3693
3795
  )
3694
3796
 
3695
3797
  def create_config_components_resolver(
3696
3798
  self, model: ConfigComponentsResolverModel, config: Config
3697
3799
  ) -> Any:
3698
- model_stream_configs = (
3699
- model.stream_config if isinstance(model.stream_config, list) else [model.stream_config]
3800
+ stream_config = self._create_component_from_model(
3801
+ model.stream_config, config=config, parameters=model.parameters or {}
3700
3802
  )
3701
3803
 
3702
- stream_configs = [
3703
- self._create_component_from_model(
3704
- stream_config, config=config, parameters=model.parameters or {}
3705
- )
3706
- for stream_config in model_stream_configs
3707
- ]
3708
-
3709
3804
  components_mapping = [
3710
3805
  self._create_component_from_model(
3711
3806
  model=components_mapping_definition_model,
@@ -3718,7 +3813,7 @@ class ModelToComponentFactory:
3718
3813
  ]
3719
3814
 
3720
3815
  return ConfigComponentsResolver(
3721
- stream_configs=stream_configs,
3816
+ stream_config=stream_config,
3722
3817
  config=config,
3723
3818
  components_mapping=components_mapping,
3724
3819
  parameters=model.parameters or {},
@@ -22,7 +22,6 @@ class ComponentMappingDefinition:
22
22
  value: Union["InterpolatedString", str]
23
23
  value_type: Optional[Type[Any]]
24
24
  parameters: InitVar[Mapping[str, Any]]
25
- create_or_update: Optional[bool] = False
26
25
 
27
26
 
28
27
  @dataclass(frozen=True)
@@ -35,7 +34,6 @@ class ResolvedComponentMappingDefinition:
35
34
  value: "InterpolatedString"
36
35
  value_type: Optional[Type[Any]]
37
36
  parameters: InitVar[Mapping[str, Any]]
38
- create_or_update: Optional[bool] = False
39
37
 
40
38
 
41
39
  @deprecated("This class is experimental. Use at your own risk.", category=ExperimentalClassWarning)
@@ -4,8 +4,7 @@
4
4
 
5
5
  from copy import deepcopy
6
6
  from dataclasses import InitVar, dataclass, field
7
- from itertools import product
8
- from typing import Any, Dict, Iterable, List, Mapping, Optional, Union
7
+ from typing import Any, Dict, Iterable, List, Mapping, Union
9
8
 
10
9
  import dpath
11
10
  from typing_extensions import deprecated
@@ -29,7 +28,6 @@ class StreamConfig:
29
28
 
30
29
  configs_pointer: List[Union[InterpolatedString, str]]
31
30
  parameters: InitVar[Mapping[str, Any]]
32
- default_values: Optional[List[Any]] = None
33
31
 
34
32
  def __post_init__(self, parameters: Mapping[str, Any]) -> None:
35
33
  self.configs_pointer = [
@@ -50,7 +48,7 @@ class ConfigComponentsResolver(ComponentsResolver):
50
48
  parameters (InitVar[Mapping[str, Any]]): Additional parameters for interpolation.
51
49
  """
52
50
 
53
- stream_configs: List[StreamConfig]
51
+ stream_config: StreamConfig
54
52
  config: Config
55
53
  components_mapping: List[ComponentMappingDefinition]
56
54
  parameters: InitVar[Mapping[str, Any]]
@@ -84,7 +82,6 @@ class ConfigComponentsResolver(ComponentsResolver):
84
82
  field_path=field_path,
85
83
  value=interpolated_value,
86
84
  value_type=component_mapping.value_type,
87
- create_or_update=component_mapping.create_or_update,
88
85
  parameters=parameters,
89
86
  )
90
87
  )
@@ -94,35 +91,17 @@ class ConfigComponentsResolver(ComponentsResolver):
94
91
  )
95
92
 
96
93
  @property
97
- def _stream_config(self):
98
- def resolve_path(pointer):
99
- return [
100
- node.eval(self.config) if not isinstance(node, str) else node for node in pointer
101
- ]
102
-
103
- def normalize_configs(configs):
104
- return configs if isinstance(configs, list) else [configs]
105
-
106
- def prepare_streams():
107
- for stream_config in self.stream_configs:
108
- path = resolve_path(stream_config.configs_pointer)
109
- stream_configs = dpath.get(dict(self.config), path, default=[])
110
- stream_configs = normalize_configs(stream_configs)
111
- if stream_config.default_values:
112
- stream_configs += stream_config.default_values
113
- yield [(i, item) for i, item in enumerate(stream_configs)]
114
-
115
- def merge_combination(combo):
116
- result = {}
117
- for config_index, (elem_index, elem) in enumerate(combo):
118
- if isinstance(elem, dict):
119
- result.update(elem)
120
- else:
121
- result.setdefault(f"source_config_{config_index}", (elem_index, elem))
122
- return result
123
-
124
- all_indexed_streams = list(prepare_streams())
125
- return [merge_combination(combo) for combo in product(*all_indexed_streams)]
94
+ def _stream_config(self) -> Iterable[Mapping[str, Any]]:
95
+ path = [
96
+ node.eval(self.config) if not isinstance(node, str) else node
97
+ for node in self.stream_config.configs_pointer
98
+ ]
99
+ stream_config = dpath.get(dict(self.config), path, default=[])
100
+
101
+ if not isinstance(stream_config, list):
102
+ stream_config = [stream_config]
103
+
104
+ return stream_config
126
105
 
127
106
  def resolve_components(
128
107
  self, stream_template_config: Dict[str, Any]
@@ -151,21 +130,7 @@ class ConfigComponentsResolver(ComponentsResolver):
151
130
  )
152
131
 
153
132
  path = [path.eval(self.config, **kwargs) for path in resolved_component.field_path]
154
- parsed_value = self._parse_yaml_if_possible(value)
155
- updated = dpath.set(updated_config, path, parsed_value)
156
133
 
157
- if parsed_value and not updated and resolved_component.create_or_update:
158
- dpath.new(updated_config, path, parsed_value)
134
+ dpath.set(updated_config, path, value)
159
135
 
160
136
  yield updated_config
161
-
162
- @staticmethod
163
- def _parse_yaml_if_possible(value: Any) -> Any:
164
- if isinstance(value, str):
165
- try:
166
- import yaml
167
-
168
- return yaml.safe_load(value)
169
- except Exception:
170
- return value
171
- return value
@@ -10,7 +10,6 @@ from typing import Any, List, Mapping, MutableMapping, Optional, Union
10
10
  import dpath
11
11
  from typing_extensions import deprecated
12
12
 
13
- from airbyte_cdk.sources.declarative.extractors.record_filter import RecordFilter
14
13
  from airbyte_cdk.sources.declarative.interpolation.interpolated_boolean import InterpolatedBoolean
15
14
  from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
16
15
  from airbyte_cdk.sources.declarative.retrievers.retriever import Retriever
@@ -127,7 +126,6 @@ class DynamicSchemaLoader(SchemaLoader):
127
126
  parameters: InitVar[Mapping[str, Any]]
128
127
  schema_type_identifier: SchemaTypeIdentifier
129
128
  schema_transformations: List[RecordTransformation] = field(default_factory=lambda: [])
130
- schema_filter: Optional[RecordFilter] = None
131
129
 
132
130
  def get_json_schema(self) -> Mapping[str, Any]:
133
131
  """
@@ -153,18 +151,20 @@ class DynamicSchemaLoader(SchemaLoader):
153
151
  )
154
152
  properties[key] = value
155
153
 
156
- filtred_transformed_properties = self._transform(self._filter(properties))
154
+ transformed_properties = self._transform(properties, {})
157
155
 
158
156
  return {
159
157
  "$schema": "https://json-schema.org/draft-07/schema#",
160
158
  "type": "object",
161
159
  "additionalProperties": True,
162
- "properties": filtred_transformed_properties,
160
+ "properties": transformed_properties,
163
161
  }
164
162
 
165
163
  def _transform(
166
164
  self,
167
165
  properties: Mapping[str, Any],
166
+ stream_state: StreamState,
167
+ stream_slice: Optional[StreamSlice] = None,
168
168
  ) -> Mapping[str, Any]:
169
169
  for transformation in self.schema_transformations:
170
170
  transformation.transform(
@@ -173,21 +173,6 @@ class DynamicSchemaLoader(SchemaLoader):
173
173
  )
174
174
  return properties
175
175
 
176
- def _filter(
177
- self,
178
- properties: Mapping[str, Any],
179
- ) -> Mapping[str, Any]:
180
- if not self.schema_filter:
181
- return properties
182
-
183
- filtered_properties = {}
184
- for item in self.schema_filter.filter_records(
185
- ({k: v} for k, v in properties.items()),
186
- {},
187
- ):
188
- filtered_properties.update(item)
189
- return filtered_properties
190
-
191
176
  def _get_key(
192
177
  self,
193
178
  raw_schema: MutableMapping[str, Any],
@@ -2,6 +2,6 @@
2
2
  # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
- from airbyte_cdk.sources.declarative.spec.spec import Spec
5
+ from airbyte_cdk.sources.declarative.spec.spec import ConfigMigration, Spec
6
6
 
7
- __all__ = ["Spec"]
7
+ __all__ = ["Spec", "ConfigMigration"]
@@ -2,15 +2,33 @@
2
2
  # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
- from dataclasses import InitVar, dataclass
6
- from typing import Any, Mapping, Optional
5
+ import json
6
+ from dataclasses import InitVar, dataclass, field
7
+ from typing import Any, List, Mapping, MutableMapping, Optional
7
8
 
9
+ import orjson
10
+
11
+ from airbyte_cdk.config_observation import create_connector_config_control_message
12
+ from airbyte_cdk.entrypoint import AirbyteEntrypoint
8
13
  from airbyte_cdk.models import (
9
14
  AdvancedAuth,
10
15
  ConnectorSpecification,
11
16
  ConnectorSpecificationSerializer,
12
17
  )
18
+ from airbyte_cdk.models.airbyte_protocol_serializers import AirbyteMessageSerializer
13
19
  from airbyte_cdk.sources.declarative.models.declarative_component_schema import AuthFlow
20
+ from airbyte_cdk.sources.declarative.transformations.config_transformations.config_transformation import (
21
+ ConfigTransformation,
22
+ )
23
+ from airbyte_cdk.sources.declarative.validators.validator import Validator
24
+ from airbyte_cdk.sources.message.repository import InMemoryMessageRepository, MessageRepository
25
+ from airbyte_cdk.sources.source import Source
26
+
27
+
28
+ @dataclass
29
+ class ConfigMigration:
30
+ transformations: List[ConfigTransformation]
31
+ description: Optional[str] = None
14
32
 
15
33
 
16
34
  @dataclass
@@ -27,6 +45,10 @@ class Spec:
27
45
  parameters: InitVar[Mapping[str, Any]]
28
46
  documentation_url: Optional[str] = None
29
47
  advanced_auth: Optional[AuthFlow] = None
48
+ config_migrations: List[ConfigMigration] = field(default_factory=list)
49
+ config_transformations: List[ConfigTransformation] = field(default_factory=list)
50
+ config_validations: List[Validator] = field(default_factory=list)
51
+ message_repository: MessageRepository = InMemoryMessageRepository()
30
52
 
31
53
  def generate_spec(self) -> ConnectorSpecification:
32
54
  """
@@ -46,3 +68,50 @@ class Spec:
46
68
 
47
69
  # We remap these keys to camel case because that's the existing format expected by the rest of the platform
48
70
  return ConnectorSpecificationSerializer.load(obj)
71
+
72
+ def migrate_config(
73
+ self, args: List[str], source: Source, config: MutableMapping[str, Any]
74
+ ) -> None:
75
+ """
76
+ Apply all specified config transformations to the provided config and save the modified config to the given path and emit a control message.
77
+
78
+ :param args: Command line arguments
79
+ :param source: Source instance
80
+ :param config: The user-provided config to migrate
81
+ """
82
+ config_path = AirbyteEntrypoint(source).extract_config(args)
83
+
84
+ if not config_path:
85
+ return
86
+
87
+ mutable_config = dict(config)
88
+ for migration in self.config_migrations:
89
+ for transformation in migration.transformations:
90
+ transformation.transform(mutable_config)
91
+
92
+ if mutable_config != config:
93
+ with open(config_path, "w") as f:
94
+ json.dump(mutable_config, f)
95
+ self.message_repository.emit_message(
96
+ create_connector_config_control_message(mutable_config)
97
+ )
98
+ for message in self.message_repository.consume_queue():
99
+ print(orjson.dumps(AirbyteMessageSerializer.dump(message)).decode())
100
+
101
+ def transform_config(self, config: MutableMapping[str, Any]) -> None:
102
+ """
103
+ Apply all config transformations to the provided config.
104
+
105
+ :param config: The user-provided configuration
106
+ """
107
+ for transformation in self.config_transformations:
108
+ transformation.transform(config)
109
+
110
+ def validate_config(self, config: MutableMapping[str, Any]) -> None:
111
+ """
112
+ Apply all config validations to the provided config.
113
+
114
+ :param config: The user-provided configuration
115
+ """
116
+ for validator in self.config_validations:
117
+ validator.validate(config)