airbyte-source-iterable 0.4.0__py3-none-any.whl → 0.5.1__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 (29) hide show
  1. {airbyte_source_iterable-0.4.0.dist-info → airbyte_source_iterable-0.5.1.dist-info}/METADATA +2 -2
  2. airbyte_source_iterable-0.5.1.dist-info/RECORD +31 -0
  3. source_iterable/components.py +41 -0
  4. source_iterable/manifest.yaml +440 -0
  5. source_iterable/schemas/campaigns.json +18 -0
  6. source_iterable/schemas/campaigns_metrics.json +1 -0
  7. source_iterable/schemas/channels.json +4 -0
  8. source_iterable/schemas/email_bounce.json +10 -0
  9. source_iterable/schemas/email_click.json +18 -0
  10. source_iterable/schemas/email_complaint.json +10 -0
  11. source_iterable/schemas/email_open.json +15 -0
  12. source_iterable/schemas/email_send.json +38 -0
  13. source_iterable/schemas/email_send_skip.json +38 -0
  14. source_iterable/schemas/email_subscribe.json +11 -0
  15. source_iterable/schemas/email_unsubscribe.json +15 -0
  16. source_iterable/schemas/events.json +8 -0
  17. source_iterable/schemas/list_users.json +2 -0
  18. source_iterable/schemas/lists.json +4 -0
  19. source_iterable/schemas/message_types.json +3 -0
  20. source_iterable/schemas/metadata.json +1 -0
  21. source_iterable/schemas/templates.json +8 -0
  22. source_iterable/schemas/users.json +132 -11
  23. source_iterable/source.py +54 -96
  24. source_iterable/spec.json +2 -3
  25. source_iterable/streams.py +0 -123
  26. source_iterable/utils.py +0 -7
  27. airbyte_source_iterable-0.4.0.dist-info/RECORD +0 -29
  28. {airbyte_source_iterable-0.4.0.dist-info → airbyte_source_iterable-0.5.1.dist-info}/WHEEL +0 -0
  29. {airbyte_source_iterable-0.4.0.dist-info → airbyte_source_iterable-0.5.1.dist-info}/entry_points.txt +0 -0
source_iterable/source.py CHANGED
@@ -2,20 +2,14 @@
2
2
  # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
3
  #
4
4
 
5
- from typing import Any, List, Mapping, Tuple
5
+ from typing import Any, List, Mapping
6
6
 
7
- import requests.exceptions
8
- from airbyte_cdk.models import SyncMode
9
- from airbyte_cdk.sources import AbstractSource
7
+ from airbyte_cdk.sources.declarative.yaml_declarative_source import YamlDeclarativeSource
10
8
  from airbyte_cdk.sources.streams import Stream
11
9
  from airbyte_cdk.sources.streams.http.requests_native_auth import TokenAuthenticator
12
- from source_iterable.utils import read_full_refresh
13
10
 
14
11
  from .streams import (
15
- AccessCheck,
16
- Campaigns,
17
12
  CampaignsMetrics,
18
- Channels,
19
13
  CustomEvent,
20
14
  EmailBounce,
21
15
  EmailClick,
@@ -25,7 +19,6 @@ from .streams import (
25
19
  EmailSendSkip,
26
20
  EmailSubscribe,
27
21
  EmailUnsubscribe,
28
- Events,
29
22
  HostedUnsubscribeClick,
30
23
  InAppClick,
31
24
  InAppClose,
@@ -36,10 +29,6 @@ from .streams import (
36
29
  InAppSendSkip,
37
30
  InboxMessageImpression,
38
31
  InboxSession,
39
- Lists,
40
- ListUsers,
41
- MessageTypes,
42
- Metadata,
43
32
  Purchase,
44
33
  PushBounce,
45
34
  PushOpen,
@@ -53,101 +42,70 @@ from .streams import (
53
42
  SmsSendSkip,
54
43
  SmsUsageInfo,
55
44
  Templates,
56
- Users,
57
45
  WebPushClick,
58
46
  WebPushSend,
59
47
  WebPushSendSkip,
60
48
  )
61
49
 
50
+ """
51
+ This file provides the necessary constructs to interpret a provided declarative YAML configuration file into
52
+ source connector.
62
53
 
63
- class SourceIterable(AbstractSource):
64
- """
65
- Note: there are some redundant endpoints
66
- (e.g. [`export/userEvents`](https://api.iterable.com/api/docs#export_exportUserEvents)
67
- and [`events/{email}`](https://api.iterable.com/api/docs#events_User_events)).
68
- In this case it's better to use the one which takes params as a query param rather than as part of the url param.
69
- """
54
+ WARNING: Do not modify this file.
55
+ """
70
56
 
71
- def check_connection(self, logger, config) -> Tuple[bool, any]:
72
- try:
73
- authenticator = TokenAuthenticator(token=config["api_key"], auth_header="Api-Key", auth_method="")
74
- list_gen = Lists(authenticator=authenticator).read_records(sync_mode=SyncMode.full_refresh)
75
- next(list_gen)
76
- return True, None
77
- except Exception as e:
78
- return False, f"Unable to connect to Iterable API with the provided credentials - {e}"
57
+ # Declarative Source
58
+ class SourceIterable(YamlDeclarativeSource):
59
+ def __init__(self):
60
+ super().__init__(**{"path_to_yaml": "manifest.yaml"})
79
61
 
80
62
  def streams(self, config: Mapping[str, Any]) -> List[Stream]:
81
- def all_streams_accessible():
82
- access_check_stream = AccessCheck(authenticator=authenticator)
83
- try:
84
- next(read_full_refresh(access_check_stream), None)
85
- except requests.exceptions.RequestException as e:
86
- if e.response.status_code == requests.codes.UNAUTHORIZED:
87
- return False
88
- raise
89
- return True
63
+ streams = super().streams(config=config)
90
64
 
91
65
  authenticator = TokenAuthenticator(token=config["api_key"], auth_header="Api-Key", auth_method="")
92
66
  # end date is provided for integration tests only
93
67
  start_date, end_date = config["start_date"], config.get("end_date")
94
68
  date_range = {"start_date": start_date, "end_date": end_date}
95
- streams = [
96
- Campaigns(authenticator=authenticator),
97
- CampaignsMetrics(authenticator=authenticator, **date_range),
98
- Channels(authenticator=authenticator),
99
- Lists(authenticator=authenticator),
100
- MessageTypes(authenticator=authenticator),
101
- Metadata(authenticator=authenticator),
102
- Templates(authenticator=authenticator, **date_range),
103
- ]
104
- # Iterable supports two types of Server-side api keys:
105
- # - read only
106
- # - server side
107
- # The first one has a limited set of supported APIs, so others are filtered out here.
108
- # A simple check is done - a read operation on a stream that can be accessed only via a Server side API key.
109
- # If read is successful - other streams should be supported as well.
110
- # More on this - https://support.iterable.com/hc/en-us/articles/360043464871-API-Keys-
111
- if all_streams_accessible():
112
- streams.extend(
113
- [
114
- Users(authenticator=authenticator, **date_range),
115
- ListUsers(authenticator=authenticator),
116
- EmailBounce(authenticator=authenticator, **date_range),
117
- EmailClick(authenticator=authenticator, **date_range),
118
- EmailComplaint(authenticator=authenticator, **date_range),
119
- EmailOpen(authenticator=authenticator, **date_range),
120
- EmailSend(authenticator=authenticator, **date_range),
121
- EmailSendSkip(authenticator=authenticator, **date_range),
122
- EmailSubscribe(authenticator=authenticator, **date_range),
123
- EmailUnsubscribe(authenticator=authenticator, **date_range),
124
- PushSend(authenticator=authenticator, **date_range),
125
- PushSendSkip(authenticator=authenticator, **date_range),
126
- PushOpen(authenticator=authenticator, **date_range),
127
- PushUninstall(authenticator=authenticator, **date_range),
128
- PushBounce(authenticator=authenticator, **date_range),
129
- WebPushSend(authenticator=authenticator, **date_range),
130
- WebPushClick(authenticator=authenticator, **date_range),
131
- WebPushSendSkip(authenticator=authenticator, **date_range),
132
- InAppSend(authenticator=authenticator, **date_range),
133
- InAppOpen(authenticator=authenticator, **date_range),
134
- InAppClick(authenticator=authenticator, **date_range),
135
- InAppClose(authenticator=authenticator, **date_range),
136
- InAppDelete(authenticator=authenticator, **date_range),
137
- InAppDelivery(authenticator=authenticator, **date_range),
138
- InAppSendSkip(authenticator=authenticator, **date_range),
139
- InboxSession(authenticator=authenticator, **date_range),
140
- InboxMessageImpression(authenticator=authenticator, **date_range),
141
- SmsSend(authenticator=authenticator, **date_range),
142
- SmsBounce(authenticator=authenticator, **date_range),
143
- SmsClick(authenticator=authenticator, **date_range),
144
- SmsReceived(authenticator=authenticator, **date_range),
145
- SmsSendSkip(authenticator=authenticator, **date_range),
146
- SmsUsageInfo(authenticator=authenticator, **date_range),
147
- Purchase(authenticator=authenticator, **date_range),
148
- CustomEvent(authenticator=authenticator, **date_range),
149
- HostedUnsubscribeClick(authenticator=authenticator, **date_range),
150
- Events(authenticator=authenticator),
151
- ]
152
- )
69
+
70
+ # TODO: migrate streams below to low code as slicer logic will be migrated to generator based
71
+ streams.extend(
72
+ [
73
+ CampaignsMetrics(authenticator=authenticator, **date_range),
74
+ Templates(authenticator=authenticator, **date_range),
75
+ EmailBounce(authenticator=authenticator, **date_range),
76
+ EmailClick(authenticator=authenticator, **date_range),
77
+ EmailComplaint(authenticator=authenticator, **date_range),
78
+ EmailOpen(authenticator=authenticator, **date_range),
79
+ EmailSend(authenticator=authenticator, **date_range),
80
+ EmailSendSkip(authenticator=authenticator, **date_range),
81
+ EmailSubscribe(authenticator=authenticator, **date_range),
82
+ EmailUnsubscribe(authenticator=authenticator, **date_range),
83
+ PushSend(authenticator=authenticator, **date_range),
84
+ PushSendSkip(authenticator=authenticator, **date_range),
85
+ PushOpen(authenticator=authenticator, **date_range),
86
+ PushUninstall(authenticator=authenticator, **date_range),
87
+ PushBounce(authenticator=authenticator, **date_range),
88
+ WebPushSend(authenticator=authenticator, **date_range),
89
+ WebPushClick(authenticator=authenticator, **date_range),
90
+ WebPushSendSkip(authenticator=authenticator, **date_range),
91
+ InAppSend(authenticator=authenticator, **date_range),
92
+ InAppOpen(authenticator=authenticator, **date_range),
93
+ InAppClick(authenticator=authenticator, **date_range),
94
+ InAppClose(authenticator=authenticator, **date_range),
95
+ InAppDelete(authenticator=authenticator, **date_range),
96
+ InAppDelivery(authenticator=authenticator, **date_range),
97
+ InAppSendSkip(authenticator=authenticator, **date_range),
98
+ InboxSession(authenticator=authenticator, **date_range),
99
+ InboxMessageImpression(authenticator=authenticator, **date_range),
100
+ SmsSend(authenticator=authenticator, **date_range),
101
+ SmsBounce(authenticator=authenticator, **date_range),
102
+ SmsClick(authenticator=authenticator, **date_range),
103
+ SmsReceived(authenticator=authenticator, **date_range),
104
+ SmsSendSkip(authenticator=authenticator, **date_range),
105
+ SmsUsageInfo(authenticator=authenticator, **date_range),
106
+ Purchase(authenticator=authenticator, **date_range),
107
+ CustomEvent(authenticator=authenticator, **date_range),
108
+ HostedUnsubscribeClick(authenticator=authenticator, **date_range),
109
+ ]
110
+ )
153
111
  return streams
source_iterable/spec.json CHANGED
@@ -1,5 +1,4 @@
1
1
  {
2
- "documentationUrl": "https://docs.airbyte.com/integrations/sources/iterable",
3
2
  "connectionSpecification": {
4
3
  "$schema": "http://json-schema.org/draft-07/schema#",
5
4
  "title": "Iterable Spec",
@@ -10,14 +9,14 @@
10
9
  "api_key": {
11
10
  "type": "string",
12
11
  "title": "API Key",
13
- "description": "Iterable API Key. See the <a href=\"https://docs.airbyte.com/integrations/sources/iterable\">docs</a> for more information on how to obtain this key.",
12
+ "description": "Iterable API Key. See the <a href=\\\"https://docs.airbyte.com/integrations/sources/iterable\\\">docs</a> for more information on how to obtain this key.",
14
13
  "airbyte_secret": true,
15
14
  "order": 0
16
15
  },
17
16
  "start_date": {
18
17
  "type": "string",
19
18
  "title": "Start Date",
20
- "description": "The date from which you'd like to replicate data for Iterable, in the format YYYY-MM-DDT00:00:00Z. All data generated after this date will be replicated.",
19
+ "description": "The date from which you'd like to replicate data for Iterable, in the format YYYY-MM-DDT00:00:00Z. All data generated after this date will be replicated.",
21
20
  "examples": ["2021-04-01T00:00:00Z"],
22
21
  "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$",
23
22
  "order": 1,
@@ -4,7 +4,6 @@
4
4
 
5
5
  import csv
6
6
  import json
7
- import urllib.parse as urlparse
8
7
  from abc import ABC, abstractmethod
9
8
  from io import StringIO
10
9
  from typing import Any, Dict, Iterable, List, Mapping, MutableMapping, Optional, Union
@@ -66,11 +65,6 @@ class IterableStream(HttpStream, ABC):
66
65
  """
67
66
  return None
68
67
 
69
- def check_unauthorized_key(self, response: requests.Response) -> bool:
70
- if response.status_code == codes.UNAUTHORIZED:
71
- self.logger.warning(f"Provided API Key has not sufficient permissions to read from stream: {self.data_field}")
72
- return True
73
-
74
68
  def check_generic_error(self, response: requests.Response) -> bool:
75
69
  """
76
70
  https://github.com/airbytehq/oncall/issues/1592#issuecomment-1499109251
@@ -129,9 +123,6 @@ class IterableStream(HttpStream, ABC):
129
123
  yield from super().read_records(sync_mode, cursor_field=cursor_field, stream_slice=stream_slice, stream_state=stream_state)
130
124
  except (HTTPError, UserDefinedBackoffException, DefaultBackoffException) as e:
131
125
  response = e.response
132
- if self.check_unauthorized_key(response):
133
- self.ignore_further_slices = True
134
- return
135
126
  if self.check_generic_error(response):
136
127
  return
137
128
  raise e
@@ -342,42 +333,6 @@ class IterableExportEventsStreamAdjustableRange(IterableExportStreamAdjustableRa
342
333
  return ResourceSchemaLoader(package_name_from_class(self.__class__)).get_schema("events")
343
334
 
344
335
 
345
- class Lists(IterableStream):
346
- data_field = "lists"
347
-
348
- def path(self, **kwargs) -> str:
349
- return "lists"
350
-
351
-
352
- class ListUsers(IterableStream):
353
- primary_key = "listId"
354
- data_field = "getUsers"
355
- name = "list_users"
356
- # enable caching, because this stream used by other ones
357
- use_cache = True
358
-
359
- def path(self, stream_slice: Optional[Mapping[str, Any]] = None, **kwargs) -> str:
360
- return f"lists/{self.data_field}?listId={stream_slice['list_id']}"
361
-
362
- def stream_slices(self, **kwargs) -> Iterable[Optional[Mapping[str, any]]]:
363
- lists = Lists(authenticator=self._cred)
364
- for list_record in lists.read_records(sync_mode=kwargs.get("sync_mode", SyncMode.full_refresh)):
365
- yield {"list_id": list_record["id"]}
366
-
367
- def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]:
368
- list_id = self._get_list_id(response.url)
369
- for user in response.iter_lines():
370
- yield {"email": user.decode(), "listId": list_id}
371
-
372
- @staticmethod
373
- def _get_list_id(url: str) -> int:
374
- parsed_url = urlparse.urlparse(url)
375
- for q in parsed_url.query.split("&"):
376
- key, value = q.split("=")
377
- if key == "listId":
378
- return int(value)
379
-
380
-
381
336
  class Campaigns(IterableStream):
382
337
  data_field = "campaigns"
383
338
 
@@ -465,71 +420,6 @@ class CampaignsMetrics(IterableStream):
465
420
  return result
466
421
 
467
422
 
468
- class Channels(IterableStream):
469
- data_field = "channels"
470
-
471
- def path(self, **kwargs) -> str:
472
- return "channels"
473
-
474
-
475
- class MessageTypes(IterableStream):
476
- data_field = "messageTypes"
477
- name = "message_types"
478
-
479
- def path(self, **kwargs) -> str:
480
- return "messageTypes"
481
-
482
-
483
- class Metadata(IterableStream):
484
- primary_key = None
485
- data_field = "results"
486
-
487
- def path(self, **kwargs) -> str:
488
- return "metadata"
489
-
490
-
491
- class Events(IterableStream):
492
- """
493
- https://api.iterable.com/api/docs#export_exportUserEvents
494
- """
495
-
496
- primary_key = None
497
- data_field = "events"
498
- common_fields = ("itblInternal", "_type", "createdAt", "email")
499
-
500
- def path(self, **kwargs) -> str:
501
- return "export/userEvents"
502
-
503
- def request_params(self, stream_slice: Optional[Mapping[str, Any]], **kwargs) -> MutableMapping[str, Any]:
504
- params = super().request_params(**kwargs)
505
- params.update({"email": stream_slice["email"], "includeCustomEvents": "true"})
506
-
507
- return params
508
-
509
- def stream_slices(self, **kwargs) -> Iterable[Optional[Mapping[str, any]]]:
510
- lists = ListUsers(authenticator=self._cred)
511
- stream_slices = lists.stream_slices()
512
-
513
- for stream_slice in stream_slices:
514
- for list_record in lists.read_records(sync_mode=kwargs.get("sync_mode", SyncMode.full_refresh), stream_slice=stream_slice):
515
- yield {"email": list_record["email"]}
516
-
517
- def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]:
518
- """
519
- Parse jsonl response body.
520
- Put common event fields at the top level.
521
- Put the rest of the fields in the `data` subobject.
522
- """
523
- jsonl_records = StringIO(response.text)
524
- for record in jsonl_records:
525
- record_dict = json.loads(record)
526
- record_dict_common_fields = {}
527
- for field in self.common_fields:
528
- record_dict_common_fields[field] = record_dict.pop(field, None)
529
-
530
- yield {**record_dict_common_fields, "data": record_dict}
531
-
532
-
533
423
  class EmailBounce(IterableExportStreamAdjustableRange):
534
424
  data_field = "emailBounce"
535
425
 
@@ -687,16 +577,3 @@ class Templates(IterableExportStreamRanged):
687
577
  for record in records:
688
578
  record[self.cursor_field] = self._field_to_datetime(record[self.cursor_field])
689
579
  yield record
690
-
691
-
692
- class Users(IterableExportStreamRanged):
693
- data_field = "user"
694
- cursor_field = "profileUpdatedAt"
695
-
696
-
697
- class AccessCheck(ListUsers):
698
- # since 401 error is failed silently in all the streams,
699
- # we need another class to distinguish an empty stream from 401 response
700
- def check_unauthorized_key(self, response: requests.Response) -> bool:
701
- # this allows not retrying 401 and raising the error upstream
702
- return response.status_code != codes.UNAUTHORIZED
source_iterable/utils.py CHANGED
@@ -24,10 +24,3 @@ def dateutil_parse(text):
24
24
  dt.microsecond,
25
25
  tz=dt.tzinfo or pendulum.tz.UTC,
26
26
  )
27
-
28
-
29
- def read_full_refresh(stream_instance: Stream):
30
- slices = stream_instance.stream_slices(sync_mode=SyncMode.full_refresh)
31
- for _slice in slices:
32
- for record in stream_instance.read_records(stream_slice=_slice, sync_mode=SyncMode.full_refresh):
33
- yield record
@@ -1,29 +0,0 @@
1
- source_iterable/__init__.py,sha256=8WKQT800ggxG1vGPoA_ZHUkozwEM7qMhGMhzEPCdA4o,65
2
- source_iterable/run.py,sha256=-Bvz772Z7iJWExDoJS7chxv725hG4e9e5Engrtp66iE,236
3
- source_iterable/schemas/campaigns.json,sha256=3sDq9ekGVuIoAb0kXhul-5sbaz1Vqm36MnLr0cNfPpY,1191
4
- source_iterable/schemas/campaigns_metrics.json,sha256=huOj1N5iXi-jvM0ztAZjf2mUIfd2KeWKSwQ7pNmsx6g,109
5
- source_iterable/schemas/channels.json,sha256=xKQgmDcVeRcN0TgqUY1oEFTjXeHdVeUjCix7OnnjkLg,285
6
- source_iterable/schemas/email_bounce.json,sha256=GgVv_AesrbRypkePrM1WH5upCYyTffhxxFksG-tSvCo,815
7
- source_iterable/schemas/email_click.json,sha256=IwQ2GfqhQuefPNDIzbf8Dx7WjxtzcabnlRgTEAA57e4,1267
8
- source_iterable/schemas/email_complaint.json,sha256=GgVv_AesrbRypkePrM1WH5upCYyTffhxxFksG-tSvCo,815
9
- source_iterable/schemas/email_open.json,sha256=Vhd2CH2nYXNiLF9pWMiz3GyG82kml1VGkQY1YgW2AI4,1094
10
- source_iterable/schemas/email_send.json,sha256=DPf-L2VWjyI_iLWNgTp9TZQNi6ee7Pjha39Hmodc5WQ,3027
11
- source_iterable/schemas/email_send_skip.json,sha256=6aVemp9UpFjyAJbavlSq0r15KrU4sJUj_Wks2VedkEE,3021
12
- source_iterable/schemas/email_subscribe.json,sha256=LuFhVl3Vz0IbZM0wpNp29KIAyTJQYE_GDNRMy5o2Mws,930
13
- source_iterable/schemas/email_unsubscribe.json,sha256=E4v8BrTrhKvaMb9wHU55mk8kWwL4vG4I7cytjFUneiE,1153
14
- source_iterable/schemas/events.json,sha256=pOFyNytQlWMiTkeGzMlDesPc1Miop8AlGBltd95cCL8,679
15
- source_iterable/schemas/list_users.json,sha256=Z3ff2_IEaJeDNuudl7SJVnj3eBCuM552ts4JT0qNENE,167
16
- source_iterable/schemas/lists.json,sha256=iNY9jN7k4LwxnQ7OhAl99gQf-CIzcFRKC4cNoJVB6D4,280
17
- source_iterable/schemas/message_types.json,sha256=GLFtrZk2vkBhnk-OMnQ--cl2VYQNJgPU4a1oBhlZcz4,220
18
- source_iterable/schemas/metadata.json,sha256=WP_wq49tAsvetXEtQ3KeGxVpqTYCbLjZacNkTohi3jw,109
19
- source_iterable/schemas/templates.json,sha256=dC7lKUnG3mQe9-Wj-jd7OyYruQEWvYT8DHfW9oDLXT4,569
20
- source_iterable/schemas/users.json,sha256=WBl7sJBKU3r_BqGF5Nzv0C-rbHj0DoI-PRHopxA7s2k,7490
21
- source_iterable/slice_generators.py,sha256=neRuWD52Xnr84KvWQ0kjaErR44sOKjxIyLCNeofohzE,6482
22
- source_iterable/source.py,sha256=RC-3qvp_M6n4vKXsxyyOk8c6tqwh6t7FKX64XeMHcAk,6707
23
- source_iterable/spec.json,sha256=QQ2V2eUQW88gtgpNjpxOe_zFbBDFIU8zI7Gtv25LC2I,1083
24
- source_iterable/streams.py,sha256=6x2T0r89IIGsB7iO7suSBBAI96uyFlM4PuTky5MxTek,23693
25
- source_iterable/utils.py,sha256=ppDgm3BpTDsXY6AsLg3POL3mKj2gkyEmontg86l3ifQ,932
26
- airbyte_source_iterable-0.4.0.dist-info/METADATA,sha256=d_jPB8qa4zWDkC2qlXhrFPgOvn4wQ-JqwsUvdMxHunw,5350
27
- airbyte_source_iterable-0.4.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
28
- airbyte_source_iterable-0.4.0.dist-info/entry_points.txt,sha256=pjzrNtsOX3jW37IK0U5pSmLiraiuLTPr5aB5-hBXpAo,59
29
- airbyte_source_iterable-0.4.0.dist-info/RECORD,,