cloudquery-plugin-sdk 0.1.33__py2.py3-none-any.whl → 0.1.34__py2.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.
@@ -5,6 +5,7 @@ from cloudquery.sdk import message
5
5
  from cloudquery.sdk import schema
6
6
  from typing import List, Generator, Dict
7
7
  import pyarrow as pa
8
+ from cloudquery.sdk.schema.table import Table
8
9
  from cloudquery.sdk.types import JSONType
9
10
  from dataclasses import dataclass, field
10
11
 
@@ -109,5 +110,9 @@ class MemDB(plugin.Plugin):
109
110
  else:
110
111
  raise NotImplementedError(f"Unknown message type {type(msg)}")
111
112
 
113
+ def read(self, table: Table) -> Generator[message.ReadMessage, None, None]:
114
+ for table, record in self._db.items():
115
+ yield message.ReadMessage(record)
116
+
112
117
  def close(self) -> None:
113
118
  self._db = {}
@@ -81,8 +81,14 @@ class PluginServicer(plugin_pb2_grpc.PluginServicer):
81
81
  # unknown sync message type
82
82
  raise NotImplementedError()
83
83
 
84
- def Read(self, request, context):
85
- raise NotImplementedError()
84
+ def Read(
85
+ self, request: plugin_pb2.Read.Request, context
86
+ ) -> Generator[plugin_pb2.Read.Response, None, None]:
87
+ schema = arrow.new_schema_from_bytes(request.table)
88
+ table = Table.from_arrow_schema(schema)
89
+ for msg in self._plugin.read(table):
90
+ buf = arrow.record_to_bytes(msg.record)
91
+ yield plugin_pb2.Read.Response(record=buf)
86
92
 
87
93
  def Write(
88
94
  self, request_iterator: Generator[plugin_pb2.Write.Request, None, None], context
@@ -93,7 +99,9 @@ class PluginServicer(plugin_pb2_grpc.PluginServicer):
93
99
  if field == "migrate_table":
94
100
  sc = arrow.new_schema_from_bytes(msg.migrate_table.table)
95
101
  table = Table.from_arrow_schema(sc)
96
- yield WriteMigrateTableMessage(table=table)
102
+ yield WriteMigrateTableMessage(
103
+ table=table, migrate_force=msg.migrate_table.migrate_force
104
+ )
97
105
  elif field == "insert":
98
106
  yield WriteInsertMessage(
99
107
  record=arrow.new_record_from_bytes(msg.insert.record)
@@ -5,3 +5,4 @@ from .write import (
5
5
  WriteMigrateTableMessage,
6
6
  WriteDeleteStale,
7
7
  )
8
+ from .read import ReadMessage
@@ -0,0 +1,6 @@
1
+ import pyarrow as pa
2
+
3
+
4
+ class ReadMessage:
5
+ def __init__(self, record: pa.RecordBatch):
6
+ self.record = record
@@ -12,8 +12,9 @@ class WriteInsertMessage(WriteMessage):
12
12
 
13
13
 
14
14
  class WriteMigrateTableMessage(WriteMessage):
15
- def __init__(self, table: Table):
15
+ def __init__(self, table: Table, migrate_force: bool):
16
16
  self.table = table
17
+ self.migrate_force = migrate_force
17
18
 
18
19
 
19
20
  class WriteDeleteStale(WriteMessage):
@@ -93,5 +93,8 @@ class Plugin:
93
93
  def write(self, writer: Generator[message.WriteMessage, None, None]) -> None:
94
94
  raise NotImplementedError()
95
95
 
96
+ def read(self, table: Table) -> Generator[message.ReadMessage, None, None]:
97
+ raise NotImplementedError()
98
+
96
99
  def close(self) -> None:
97
100
  raise NotImplementedError()
@@ -1,5 +1,16 @@
1
1
  from .column import Column
2
- from .table import Table, tables_to_arrow_schemas, filter_dfs
2
+ from .table import (
3
+ Table,
4
+ tables_to_arrow_schemas,
5
+ filter_dfs,
6
+ TableColumnChangeType,
7
+ TableColumnChange,
8
+ TableColumnChangeType,
9
+ get_table_changes,
10
+ get_table_column,
11
+ flatten_tables_recursive,
12
+ flatten_tables,
13
+ )
3
14
  from .resource import Resource
4
15
 
5
16
  # from .table_resolver import TableReso
@@ -55,7 +55,9 @@ class Column:
55
55
  arrow.METADATA_TRUE if self.incremental_key else arrow.METADATA_FALSE
56
56
  ),
57
57
  }
58
- return pa.field(self.name, self.type, metadata=metadata)
58
+ return pa.field(
59
+ self.name, self.type, metadata=metadata, nullable=not self.not_null
60
+ )
59
61
 
60
62
  @staticmethod
61
63
  def from_arrow_field(field: pa.Field) -> Column:
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import copy
4
+ from enum import IntEnum
4
5
  import fnmatch
5
- from typing import List
6
+ from typing import List, Optional
6
7
 
7
8
  import pyarrow as pa
8
9
 
@@ -10,6 +11,10 @@ from cloudquery.sdk.schema import arrow
10
11
  from .column import Column
11
12
 
12
13
 
14
+ CQ_SYNC_TIME_COLUMN = "cq_sync_time"
15
+ CQ_SOURCE_NAME_COLUMN = "cq_source_name"
16
+
17
+
13
18
  class Client:
14
19
  pass
15
20
 
@@ -192,9 +197,137 @@ def _filter_dfs_impl(t, parent_matched, include, exclude, skip_dependent_tables)
192
197
  return None
193
198
 
194
199
 
195
- def flatten_tables(tables: List[Table]) -> List[Table]:
196
- flattened: List[Table] = []
200
+ class TableColumnChangeType:
201
+ ADD = 1
202
+ REMOVE = 2
203
+ REMOVE_UNIQUE_CONSTRAINT = 3
204
+
205
+
206
+ class TableColumnChange:
207
+ def __init__(
208
+ self,
209
+ type: TableColumnChangeType,
210
+ column_name: str,
211
+ current: Optional[Column],
212
+ previous: Optional[Column],
213
+ ):
214
+ self.type = type
215
+ self.column_name = column_name
216
+ self.current = current
217
+ self.previous = previous
218
+
219
+
220
+ class TableColumnChangeType(IntEnum):
221
+ UNKNOWN = 0
222
+ ADD = 1
223
+ UPDATE = 2
224
+ REMOVE = 3
225
+ REMOVE_UNIQUE_CONSTRAINT = 4
226
+ MOVE_TO_CQ_ONLY = 5
227
+
228
+
229
+ def get_table_changes(new: Table, old: Table) -> List[TableColumnChange]:
230
+ changes = []
231
+
232
+ # Special case: Moving from individual PKs to singular PK on _cq_id
233
+ new_pks = new.primary_keys
234
+ if (
235
+ len(new_pks) == 1
236
+ and new_pks[0] == "CqIDColumn"
237
+ and get_table_column(old, "CqIDColumn") is None
238
+ and len(old.primary_keys) > 0
239
+ ):
240
+ changes.append(
241
+ TableColumnChange(
242
+ type=TableColumnChangeType.MOVE_TO_CQ_ONLY,
243
+ )
244
+ )
245
+
246
+ for c in new.columns:
247
+ other_column = get_table_column(old, c.name)
248
+ # A column was added to the table definition
249
+ if other_column is None:
250
+ changes.append(
251
+ TableColumnChange(
252
+ type=TableColumnChangeType.ADD,
253
+ column_name=c.name,
254
+ current=c,
255
+ previous=None,
256
+ )
257
+ )
258
+ continue
259
+
260
+ # Column type or options (e.g. PK, Not Null) changed in the new table definition
261
+ if (
262
+ c.type != other_column.type
263
+ or c.not_null != other_column.not_null
264
+ or c.primary_key != other_column.primary_key
265
+ ):
266
+ changes.append(
267
+ TableColumnChange(
268
+ type=TableColumnChangeType.UPDATE,
269
+ column_name=c.name,
270
+ current=c,
271
+ previous=other_column,
272
+ )
273
+ )
274
+
275
+ # Unique constraint was removed
276
+ if not c.unique and other_column.unique:
277
+ changes.append(
278
+ TableColumnChange(
279
+ type=TableColumnChangeType.REMOVE_UNIQUE_CONSTRAINT,
280
+ column_name=c.name,
281
+ current=c,
282
+ previous=other_column,
283
+ )
284
+ )
285
+
286
+ # A column was removed from the table definition
287
+ for c in old.columns:
288
+ if get_table_column(new, c.name) is None:
289
+ changes.append(
290
+ TableColumnChange(
291
+ type=TableColumnChangeType.REMOVE,
292
+ column_name=c.name,
293
+ current=None,
294
+ previous=c,
295
+ )
296
+ )
297
+
298
+ return changes
299
+
300
+
301
+ def get_table_column(table: Table, column_name: str) -> Optional[Column]:
302
+ for c in table.columns:
303
+ if c.name == column_name:
304
+ return c
305
+ return None
306
+
307
+
308
+ def flatten_tables_recursive(original_tables: List[Table]) -> List[Table]:
309
+ tables = []
310
+ for table in original_tables:
311
+ table_copy = Table(
312
+ name=table.name,
313
+ columns=table.columns,
314
+ relations=table.relations,
315
+ title=table.title,
316
+ description=table.description,
317
+ is_incremental=table.is_incremental,
318
+ parent=table.parent,
319
+ )
320
+ tables.append(table_copy)
321
+ tables.extend(flatten_tables_recursive(table.relations))
322
+ return tables
323
+
324
+
325
+ def flatten_tables(original_tables: List[Table]) -> List[Table]:
326
+ tables = flatten_tables_recursive(original_tables)
327
+ seen = set()
328
+ deduped = []
197
329
  for table in tables:
198
- flattened.append(table)
199
- flattened.extend(flatten_tables(table.relations))
200
- return flattened
330
+ if table.name not in seen:
331
+ deduped.append(table)
332
+ seen.add(table.name)
333
+ return deduped
@@ -21,3 +21,6 @@ class JSONType(pa.ExtensionType):
21
21
  # return an instance of this subclass given the serialized
22
22
  # metadata.
23
23
  return JSONType()
24
+
25
+
26
+ pa.register_extension_type(JSONType())
@@ -23,3 +23,6 @@ class UUIDType(pa.ExtensionType):
23
23
  # return an instance of this subclass given the serialized
24
24
  # metadata.
25
25
  return UUIDType()
26
+
27
+
28
+ pa.register_extension_type(UUIDType())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudquery-plugin-sdk
3
- Version: 0.1.33
3
+ Version: 0.1.34
4
4
  Summary: CloudQuery Plugin SDK for Python
5
5
  Home-page: https://github.com/cloudquery/plugin-sdk-python
6
6
  Author: CloudQuery LTD
@@ -1,19 +1,20 @@
1
- cloudquery_plugin_sdk-0.1.33-py3.11-nspkg.pth,sha256=DPwVEJKI3l-PsvgSiQi4zcKjh50W15YbcAr9gUpExc4,487
1
+ cloudquery_plugin_sdk-0.1.34-py3.11-nspkg.pth,sha256=DPwVEJKI3l-PsvgSiQi4zcKjh50W15YbcAr9gUpExc4,487
2
2
  cloudquery/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  cloudquery/sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  cloudquery/sdk/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  cloudquery/sdk/internal/memdb/__init__.py,sha256=fzRjFUy47sNj4GzDcxPQPg9MwgSlRJK-XXoOxb40qM8,25
6
- cloudquery/sdk/internal/memdb/memdb.py,sha256=MMpfKJVwYS5eXqanZh6vqyvgXSg0Vr9MtLbUgkapsxo,3980
6
+ cloudquery/sdk/internal/memdb/memdb.py,sha256=YLr5F85ZTwbw5w9_NHvDvV7IfyOvWUzthGu64UK-1-M,4200
7
7
  cloudquery/sdk/internal/servers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  cloudquery/sdk/internal/servers/discovery_v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  cloudquery/sdk/internal/servers/discovery_v1/discovery.py,sha256=iYQpSw1aSXgKK4zcHxIZSvaA9j-IQfDkeHnVur4sogw,360
10
10
  cloudquery/sdk/internal/servers/plugin_v3/__init__.py,sha256=5sE_Gdw1MnNOTdlHkd3Von1YEIo1s-jvMxSiq9WyRiw,35
11
- cloudquery/sdk/internal/servers/plugin_v3/plugin.py,sha256=AXKUwcPPWvmrtOHlhq1y30g6y9gDzmxutx0eWi_YJoA,4597
12
- cloudquery/sdk/message/__init__.py,sha256=KfRu1ZmLz4r-9qgOsXqgAZYetj2_MKjKAJ2HRgARi2w,191
11
+ cloudquery/sdk/internal/servers/plugin_v3/plugin.py,sha256=sxu9MNRpiAvDxjnGMoruJTYenuh1dDLByTsQJjJjyJo,5004
12
+ cloudquery/sdk/message/__init__.py,sha256=iJ9PqhjjG4zsWgd2O5hbNHD6sZP0ergFUvaF14I5na4,221
13
+ cloudquery/sdk/message/read.py,sha256=iBm5-TSTKMPl4YdfhN4viFD11hpmkgP8id7C6IQ2yaI,119
13
14
  cloudquery/sdk/message/sync.py,sha256=O2deyqCbZ84s5DvYgEozN1JMpWmlhXVPUOatM4fjq3w,283
14
- cloudquery/sdk/message/write.py,sha256=i30d2nC_YGcGlei0qBd3yHatbJ2C72ZF6QYWURdysYM,545
15
+ cloudquery/sdk/message/write.py,sha256=zVhlElsFsjbfnruPEIlUqX1AfWoXLFbQpm8idFQ5SGk,609
15
16
  cloudquery/sdk/plugin/__init__.py,sha256=xksQLxBZNbWE0dhPLSttDDW-ns6oXrEjd03g_mH0Pkg,61
16
- cloudquery/sdk/plugin/plugin.py,sha256=1coTWQmQmt0oRsuqvwYkkqdvw2wgE7eHIW3qP1Cznu8,2382
17
+ cloudquery/sdk/plugin/plugin.py,sha256=KPkdhNqjeKlFWtcpfSnJ5mGEWMQYL2zqvql7fcsoVQE,2499
17
18
  cloudquery/sdk/scalar/__init__.py,sha256=EEp4c0kNhYZpD_vt84OLnYcFW82HoyrYgKOik53uM0Y,412
18
19
  cloudquery/sdk/scalar/binary.py,sha256=8MxVoqjmZCkHyKtjUzsHQKmnpsZxX8UfL6Rd4sOVgBg,980
19
20
  cloudquery/sdk/scalar/bool.py,sha256=mtOqB8na2zRKga7OGDYg24XcbIZ6uo24cAe5HdwZfIg,1344
@@ -33,11 +34,11 @@ cloudquery/sdk/scalar/vector.py,sha256=uROcuTyla7rOARn0qhIwPH38StqfaHsaGOClT9W7x
33
34
  cloudquery/sdk/scheduler/__init__.py,sha256=ZliDWvyQ8CYAiwyRNt8-kkmkFyw1kpqnc74JYhASMv8,110
34
35
  cloudquery/sdk/scheduler/scheduler.py,sha256=1QGQg9vIWeTI2B25YS9pWew3QekczpetAx8zSDXuHCU,8101
35
36
  cloudquery/sdk/scheduler/table_resolver.py,sha256=VyxPZliVESRb_c1cL0nj1Gn2xmxmumlGMG_rCNzBQJY,1489
36
- cloudquery/sdk/schema/__init__.py,sha256=6y65eQdg7OhBevUAlLzVmuhOXe0dd5Gr28hzygMmBBw,161
37
+ cloudquery/sdk/schema/__init__.py,sha256=dU_CQoqDEtNGwx4r431G9ZhLWfEPE2F7holEa2rKT7M,350
37
38
  cloudquery/sdk/schema/arrow.py,sha256=Voj9HeEuU4dTB-blsG4yKc-8UFdW_Z5qRcYNeI7wHFg,437
38
- cloudquery/sdk/schema/column.py,sha256=lsvscBXBP4QJFTi0xdzmJJUN245llZW4nmw0buOBe9k,2781
39
+ cloudquery/sdk/schema/column.py,sha256=jTrg19Z7nBfF27s192bxQpOA73EeeGWmBhHf2Jt8XS4,2831
39
40
  cloudquery/sdk/schema/resource.py,sha256=R75VuFAGb1GBsRZdZgh5gvHbxhQkyYa_4_EVuFmbFIY,866
40
- cloudquery/sdk/schema/table.py,sha256=4jiuJkt_R96FAiQou7yusBjDiEOBX3Bq7dOHm3TX9h8,6033
41
+ cloudquery/sdk/schema/table.py,sha256=56ILjDOmcoLe3jw5jQuu-XKKIIF10NGQw7cnphQLq44,9769
41
42
  cloudquery/sdk/serve/__init__.py,sha256=zNN32S3GdnVbJ9ouWwnvlxj0TpS5G_woty8vURoRk6s,34
42
43
  cloudquery/sdk/serve/plugin.py,sha256=q5XjxG-zkl_Y407UtOmDwus4gFVzYqO654axXinOzg0,14423
43
44
  cloudquery/sdk/stateclient/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -46,11 +47,11 @@ cloudquery/sdk/transformers/__init__.py,sha256=hGnQNiUbOE2qWrWZZLIIM6KqXvFrz_6Wh
46
47
  cloudquery/sdk/transformers/openapi.py,sha256=5qHM62OtybWvzVqGh1cqh2ZbN9KtKx-zmdSK2JH3pVg,1517
47
48
  cloudquery/sdk/transformers/transformers.py,sha256=9USd5VDEMkyk-RfQboMhr9BwC05935MTuM4DZIxhPv4,330
48
49
  cloudquery/sdk/types/__init__.py,sha256=VRRTI0mHh_TxkNht9qyr3eouNV71jv1v6z9UDFvLPU8,54
49
- cloudquery/sdk/types/json.py,sha256=d1EYb8-C74U71IUajHgbetD4wTS7fIsh04qY84mq2-U,653
50
- cloudquery/sdk/types/uuid.py,sha256=Xhax4Pyfwd-rxnBSBZLt9k2Mlx2FVCyUOcKWcRiHA0U,677
51
- cloudquery_plugin_sdk-0.1.33.dist-info/LICENSE,sha256=qDABaRGjSKVOib1U8viw2P_96sIK7Puo426784oD9f8,15976
52
- cloudquery_plugin_sdk-0.1.33.dist-info/METADATA,sha256=TfDbsj5iE4DfHQDLOvAyG5e3_BvjbHmjzHRGHChA49o,1846
53
- cloudquery_plugin_sdk-0.1.33.dist-info/WHEEL,sha256=M4n4zmFKzQZk4mLCcycNIzIXO7YPKE_b5Cw4PnhHEg8,109
54
- cloudquery_plugin_sdk-0.1.33.dist-info/namespace_packages.txt,sha256=D13SSF-LACnBbtgkIVGVOAVT5hqwn2E-v0NGcuyprk4,11
55
- cloudquery_plugin_sdk-0.1.33.dist-info/top_level.txt,sha256=D13SSF-LACnBbtgkIVGVOAVT5hqwn2E-v0NGcuyprk4,11
56
- cloudquery_plugin_sdk-0.1.33.dist-info/RECORD,,
50
+ cloudquery/sdk/types/json.py,sha256=U1aUwf_swF2HZw6Rytdw5HD_hHZMjqnezigu4PnSz4o,694
51
+ cloudquery/sdk/types/uuid.py,sha256=hbC3BGeBXu9hyL4fD8NSo7ezZ-0oOcnvut02QD4TbA0,718
52
+ cloudquery_plugin_sdk-0.1.34.dist-info/LICENSE,sha256=qDABaRGjSKVOib1U8viw2P_96sIK7Puo426784oD9f8,15976
53
+ cloudquery_plugin_sdk-0.1.34.dist-info/METADATA,sha256=YP41tdAtZ56HY2VpVyLIz6-j_ArNxvFY9MdZA3V7Rg8,1846
54
+ cloudquery_plugin_sdk-0.1.34.dist-info/WHEEL,sha256=GUeE9LxUgRABPG7YM0jCNs9cBsAIx0YAkzCB88PMLgc,109
55
+ cloudquery_plugin_sdk-0.1.34.dist-info/namespace_packages.txt,sha256=D13SSF-LACnBbtgkIVGVOAVT5hqwn2E-v0NGcuyprk4,11
56
+ cloudquery_plugin_sdk-0.1.34.dist-info/top_level.txt,sha256=D13SSF-LACnBbtgkIVGVOAVT5hqwn2E-v0NGcuyprk4,11
57
+ cloudquery_plugin_sdk-0.1.34.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (72.2.0)
2
+ Generator: setuptools (73.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any