konecty_sdk_python 2.1.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 (35) hide show
  1. KonectySdkPython/__init__.py +41 -0
  2. KonectySdkPython/cli/__init__.py +38 -0
  3. KonectySdkPython/cli/apply.py +555 -0
  4. KonectySdkPython/cli/backup.py +161 -0
  5. KonectySdkPython/cli/pull.py +309 -0
  6. KonectySdkPython/lib/__init__.py +43 -0
  7. KonectySdkPython/lib/client.py +950 -0
  8. KonectySdkPython/lib/exceptions.py +26 -0
  9. KonectySdkPython/lib/feature_types/__init__.py +46 -0
  10. KonectySdkPython/lib/feature_types/cross_module_query.py +78 -0
  11. KonectySdkPython/lib/feature_types/kpi.py +21 -0
  12. KonectySdkPython/lib/feature_types/query_json.py +242 -0
  13. KonectySdkPython/lib/file_manager.py +248 -0
  14. KonectySdkPython/lib/filters.py +155 -0
  15. KonectySdkPython/lib/http.py +95 -0
  16. KonectySdkPython/lib/model.py +88 -0
  17. KonectySdkPython/lib/serialization.py +13 -0
  18. KonectySdkPython/lib/services/__init__.py +5 -0
  19. KonectySdkPython/lib/services/aggregation.py +142 -0
  20. KonectySdkPython/lib/services/auth.py +105 -0
  21. KonectySdkPython/lib/services/base.py +63 -0
  22. KonectySdkPython/lib/services/change_user.py +64 -0
  23. KonectySdkPython/lib/services/comments.py +89 -0
  24. KonectySdkPython/lib/services/export.py +54 -0
  25. KonectySdkPython/lib/services/files.py +59 -0
  26. KonectySdkPython/lib/services/notifications.py +41 -0
  27. KonectySdkPython/lib/services/query.py +192 -0
  28. KonectySdkPython/lib/services/stream.py +133 -0
  29. KonectySdkPython/lib/services/subscriptions.py +26 -0
  30. KonectySdkPython/lib/settings.py +152 -0
  31. KonectySdkPython/lib/types.py +418 -0
  32. konecty_sdk_python-2.1.1.dist-info/METADATA +155 -0
  33. konecty_sdk_python-2.1.1.dist-info/RECORD +35 -0
  34. konecty_sdk_python-2.1.1.dist-info/WHEEL +4 -0
  35. konecty_sdk_python-2.1.1.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,161 @@
1
+ """Script para fazer backup dos documentos do MongoDB."""
2
+
3
+ import asyncio
4
+ import json
5
+ import tarfile
6
+ import tempfile
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ import click
12
+ from pymongo import MongoClient
13
+ from rich.console import Console
14
+ from rich.progress import Progress
15
+
16
+ console = Console()
17
+
18
+
19
+ @click.command(name="backup")
20
+ @click.option("--host", default="localhost", help="Host do MongoDB")
21
+ @click.option("--port", type=int, default=27017, help="Porta do MongoDB")
22
+ @click.option("--database", required=True, help="Nome do banco de dados")
23
+ @click.option("--output", default="backups", help="Diretório para salvar o backup")
24
+ @click.option("--username", help="Usuário do MongoDB")
25
+ @click.option("--password", help="Senha do MongoDB")
26
+ @click.option("--replicaset", help="Nome do ReplicaSet do MongoDB (ex: rs0)")
27
+ @click.option("--version", help="Rótulo de versão para o arquivo de backup")
28
+ @click.option("--mongo-url", help="URL do MongoDB")
29
+ def backup_command(
30
+ host: str,
31
+ port: int,
32
+ database: str,
33
+ output: str,
34
+ username: Optional[str],
35
+ password: Optional[str],
36
+ replicaset: Optional[str],
37
+ version: Optional[str],
38
+ mongo_url: Optional[str],
39
+ ) -> None:
40
+ """Gera backup dos documentos do MongoDB."""
41
+ asyncio.run(
42
+ _backup_command(
43
+ host=host,
44
+ port=port,
45
+ database=database,
46
+ output=output,
47
+ username=username,
48
+ password=password,
49
+ replicaset=replicaset,
50
+ version=version,
51
+ mongo_url=mongo_url,
52
+ )
53
+ )
54
+
55
+
56
+ async def _backup_command(
57
+ host: str,
58
+ port: int,
59
+ database: str,
60
+ output: str,
61
+ username: Optional[str],
62
+ password: Optional[str],
63
+ replicaset: Optional[str],
64
+ version: Optional[str],
65
+ mongo_url: Optional[str],
66
+ ) -> None:
67
+ """Internal async implementation of backup command."""
68
+ output_dir = Path(output).resolve()
69
+ output_dir.mkdir(parents=True, exist_ok=True)
70
+
71
+ # Gera nome do arquivo de backup
72
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
73
+ version_label = f"_{version}" if version else ""
74
+ backup_file = output_dir / f"backup_{timestamp}{version_label}.tar.gz"
75
+
76
+ uri = mongo_url
77
+
78
+ if not uri:
79
+ uri_params = []
80
+ if replicaset:
81
+ uri_params.extend(
82
+ [
83
+ f"replicaSet={replicaset}",
84
+ "directConnection=false",
85
+ "retryWrites=true",
86
+ "w=majority",
87
+ ]
88
+ )
89
+
90
+ uri_suffix = f"?{'&'.join(uri_params)}" if uri_params else ""
91
+
92
+ if username and password:
93
+ uri = f"mongodb://{username}:{password}@{host}:{port}/admin{uri_suffix}"
94
+ else:
95
+ uri = f"mongodb://{host}:{port}{uri_suffix}"
96
+
97
+ client = MongoClient(
98
+ uri,
99
+ serverSelectionTimeoutMS=30000,
100
+ connectTimeoutMS=20000,
101
+ socketTimeoutMS=20000,
102
+ maxPoolSize=1,
103
+ )
104
+ db = client[database]
105
+ collection = db["MetaObjects"]
106
+
107
+ # Cria diretório temporário
108
+ with tempfile.TemporaryDirectory() as temp_dir:
109
+ temp_path = Path(temp_dir)
110
+
111
+ # Busca todos os documentos
112
+ documents = list(collection.find({"type": {"$in": ["composite", "document"]}}))
113
+
114
+ with Progress() as progress:
115
+ task = progress.add_task("[cyan]Gerando backup...", total=len(documents))
116
+
117
+ # Processa cada documento
118
+ for doc in documents:
119
+ doc_path = temp_path / doc["name"]
120
+ doc_path.mkdir(parents=True, exist_ok=True)
121
+
122
+ # Salva documento principal
123
+ doc_file = doc_path / "document.json"
124
+ doc_file.write_text(json.dumps(doc, indent=2))
125
+
126
+ # Busca documentos relacionados
127
+ related = list(collection.find({"document": doc["name"]}))
128
+
129
+ for rel in related:
130
+ rel_type = rel["type"]
131
+ type_path = doc_path / rel_type
132
+ type_path.mkdir(exist_ok=True)
133
+
134
+ rel_file = type_path / f"{rel['name']}.json"
135
+ rel_file.write_text(json.dumps(rel, indent=2))
136
+
137
+ progress.update(task, advance=1)
138
+
139
+ # Cria arquivo tar.gz
140
+ with tarfile.open(backup_file, "w:gz") as tar:
141
+ tar.add(temp_dir, arcname="metadata")
142
+
143
+ client.close()
144
+ console.print(
145
+ f"[green]Backup concluído com sucesso:[/green] [cyan]{backup_file}[/cyan]"
146
+ )
147
+
148
+
149
+ def main():
150
+ """Entry point for the CLI."""
151
+ import asyncio
152
+ import sys
153
+
154
+ if sys.platform == "win32":
155
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
156
+
157
+ backup_command()
158
+
159
+
160
+ if __name__ == "__main__":
161
+ main()
@@ -0,0 +1,309 @@
1
+ """Script para extrair dados do MongoDB e gerar arquivos JSON."""
2
+
3
+ import asyncio
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Literal, Optional, TypedDict, Union, cast
7
+
8
+ import black
9
+ import click
10
+ import inquirer
11
+ from pymongo import MongoClient
12
+ from rich.console import Console
13
+ from rich.progress import Progress
14
+ from rich.table import Table
15
+
16
+
17
+ class DocFiles(TypedDict):
18
+ document: List[Path]
19
+ view: List[Path]
20
+ list: List[Path]
21
+ pivot: List[Path]
22
+ access: List[Path]
23
+ hook: List[Path]
24
+
25
+
26
+ class RelatedResults(TypedDict):
27
+ view: List[str]
28
+ list: List[str]
29
+ pivot: List[str]
30
+ access: List[str]
31
+
32
+
33
+ class MongoFilter(TypedDict, total=False):
34
+ document: str
35
+ type: str
36
+ name: str
37
+ or_conditions: List[Dict[str, str]]
38
+
39
+
40
+ class MongoCondition(TypedDict, total=False):
41
+ type: str
42
+ name: str
43
+
44
+
45
+ MongoQuery = Dict[str, Union[str, List[MongoCondition]]]
46
+
47
+
48
+ DocType = Literal["document", "view", "list", "pivot", "access", "hook"]
49
+ MetaType = Literal["view", "list", "pivot", "access"]
50
+
51
+
52
+ console = Console()
53
+
54
+
55
+ def format_code(name: str, code: str) -> str:
56
+ """Formata o código JavaScript usando black."""
57
+ try:
58
+ return black.format_str(code, mode=black.Mode())
59
+ except Exception as error:
60
+ console.print(f"[red]Erro ao formatar código {name}[/red]: {str(error)}")
61
+ return code
62
+
63
+
64
+ async def write_file(file_path: str, content: str) -> None:
65
+ """Escreve conteúdo em um arquivo, criando diretórios se necessário."""
66
+ try:
67
+ path = Path(file_path)
68
+ path.parent.mkdir(parents=True, exist_ok=True)
69
+ path.write_text(content)
70
+ except Exception as error:
71
+ console.print(f"[red]Erro ao escrever arquivo {file_path}[/red]: {str(error)}")
72
+
73
+
74
+ @click.command(name="pull")
75
+ @click.option("--host", default="localhost", help="Host do MongoDB")
76
+ @click.option("--port", type=int, default=27017, help="Porta do MongoDB")
77
+ @click.option("--database", default="default", help="Nome do banco de dados")
78
+ @click.option("--output", default="metadata", help="Diretório para salvar os metadados")
79
+ @click.option("--username", help="Usuário do MongoDB")
80
+ @click.option("--password", help="Senha do MongoDB")
81
+ @click.option("--replicaset", help="Nome do ReplicaSet do MongoDB (ex: rs0)")
82
+ @click.option("--document", help="Nome do documento para baixar")
83
+ @click.option("--view", help="Nome da view específica para extrair")
84
+ @click.option("--list", "list_param", help="Nome da lista específica para extrair")
85
+ @click.option("--pivot", help="Nome do pivot específico para extrair")
86
+ @click.option("--access", help="Nome do access específica para extrair")
87
+ @click.option("--hook", help="Nome do hook específico para extrair")
88
+ @click.option("--mongo-url", help="URL do MongoDB")
89
+ @click.option(
90
+ "--all",
91
+ "extract_all",
92
+ is_flag=True,
93
+ help="Extrair todas as collections sem perguntar",
94
+ )
95
+ def pull_command(
96
+ host: str,
97
+ port: int,
98
+ database: str,
99
+ output: str,
100
+ username: Optional[str],
101
+ password: Optional[str],
102
+ replicaset: Optional[str],
103
+ document: Optional[str],
104
+ view: Optional[str],
105
+ list_param: Optional[str],
106
+ pivot: Optional[str],
107
+ access: Optional[str],
108
+ hook: Optional[str],
109
+ extract_all: bool,
110
+ mongo_url: Optional[str],
111
+ ) -> None:
112
+ """Baixa metadados do MongoDB."""
113
+ asyncio.run(
114
+ _pull_command(
115
+ host=host,
116
+ port=port,
117
+ database=database,
118
+ output=output,
119
+ username=username,
120
+ password=password,
121
+ replicaset=replicaset,
122
+ document=document,
123
+ view=view,
124
+ list_param=list_param,
125
+ pivot=pivot,
126
+ access=access,
127
+ hook=hook,
128
+ extract_all=extract_all,
129
+ mongo_url=mongo_url,
130
+ )
131
+ )
132
+
133
+
134
+ async def _pull_command(
135
+ host: str,
136
+ port: int,
137
+ database: str,
138
+ output: str,
139
+ username: Optional[str],
140
+ password: Optional[str],
141
+ replicaset: Optional[str],
142
+ document: Optional[str],
143
+ view: Optional[str],
144
+ list_param: Optional[str],
145
+ pivot: Optional[str],
146
+ access: Optional[str],
147
+ hook: Optional[str],
148
+ mongo_url: Optional[str],
149
+ extract_all: bool,
150
+ ) -> None:
151
+ """Internal async implementation of pull command."""
152
+ output_dir = Path(output).resolve()
153
+ output_dir.mkdir(parents=True, exist_ok=True)
154
+
155
+ uri = mongo_url
156
+
157
+ if not uri:
158
+ uri_params = []
159
+ if replicaset:
160
+ uri_params.extend(
161
+ [
162
+ f"replicaSet={replicaset}",
163
+ "directConnection=false",
164
+ "retryWrites=true",
165
+ "w=majority",
166
+ ]
167
+ )
168
+
169
+ uri_suffix = f"?{'&'.join(uri_params)}" if uri_params else ""
170
+
171
+ if username and password:
172
+ uri = f"mongodb://{username}:{password}@{host}:{port}/admin{uri_suffix}"
173
+ else:
174
+ uri = f"mongodb://{host}:{port}{uri_suffix}"
175
+
176
+ client = MongoClient(
177
+ uri,
178
+ serverSelectionTimeoutMS=30000,
179
+ connectTimeoutMS=20000,
180
+ socketTimeoutMS=20000,
181
+ maxPoolSize=1,
182
+ )
183
+ db = client[database]
184
+ collection = db["MetaObjects"]
185
+
186
+ table = Table(title="Resultados da Extração")
187
+ table.add_column("Documento")
188
+ table.add_column("Hook")
189
+ table.add_column("View")
190
+ table.add_column("List")
191
+ table.add_column("Pivot")
192
+ table.add_column("Access")
193
+
194
+ if document is None and not extract_all:
195
+ document_names = list(
196
+ collection.find(
197
+ {"type": {"$in": ["composite", "document"]}}, {"name": 1}
198
+ ).sort("name", 1)
199
+ )
200
+ if not document_names:
201
+ console.print("[red]Nenhum documento encontrado.[/red]")
202
+ return
203
+
204
+ document_names = [doc["name"] for doc in document_names]
205
+ document_names.append("Todos")
206
+
207
+ questions = [
208
+ inquirer.List(
209
+ "document",
210
+ message="Selecione o documento para extrair",
211
+ choices=document_names,
212
+ )
213
+ ]
214
+ answers = inquirer.prompt(questions)
215
+ if not answers:
216
+ return
217
+ document = "all" if answers["document"] == "Todos" else answers["document"]
218
+ elif extract_all:
219
+ document = "all"
220
+
221
+ filter_query: Dict[str, Any] = {"type": {"$in": ["composite", "document"]}}
222
+ if document != "all":
223
+ filter_query["name"] = document
224
+
225
+ documents = list(collection.find(filter_query).sort("name", 1))
226
+
227
+ with Progress() as progress:
228
+ task = progress.add_task("[cyan]Extraindo metadados...", total=len(documents))
229
+
230
+ for doc in documents:
231
+ doc_path = output_dir / doc["name"]
232
+ doc_path.mkdir(parents=True, exist_ok=True)
233
+
234
+ # Processando hooks
235
+ hook_results = []
236
+ if "validationData" in doc:
237
+ await write_file(
238
+ str(doc_path / "hook" / "validationData.json"),
239
+ json.dumps(doc["validationData"], indent=2),
240
+ )
241
+ hook_results.append("✓ validationData")
242
+
243
+ for script_type in [
244
+ "scriptBeforeValidation",
245
+ "validationScript",
246
+ "scriptAfterSave",
247
+ ]:
248
+ if script_type in doc:
249
+ formatted = format_code(f"{script_type}.js", doc[script_type])
250
+ await write_file(
251
+ str(doc_path / "hook" / f"{script_type}.js"), formatted
252
+ )
253
+ hook_results.append(f"✓ {script_type}")
254
+
255
+ # Processando views, lists, pivots e access
256
+ related_results: RelatedResults = {
257
+ "view": [],
258
+ "list": [],
259
+ "pivot": [],
260
+ "access": [],
261
+ }
262
+ for type_name, param in [
263
+ ("view", extract_all or view),
264
+ ("list", extract_all or list_param),
265
+ ("pivot", extract_all or pivot),
266
+ ("access", extract_all or access),
267
+ ]:
268
+ if param is not None:
269
+ condition = {"type": type_name}
270
+ if not extract_all:
271
+ condition["name"] = param
272
+ related_metas = list(collection.find(condition).sort("_id", 1))
273
+ for meta in related_metas:
274
+ meta_type = cast(MetaType, meta["type"])
275
+ if meta_type in ("view", "list", "pivot", "access"):
276
+ await write_file(
277
+ str(doc_path / meta_type / f"{meta['name']}.json"),
278
+ json.dumps(meta, indent=2),
279
+ )
280
+ related_results[meta_type].append(f"✓ {meta['name']}")
281
+
282
+ table.add_row(
283
+ doc["name"],
284
+ "\n".join(hook_results),
285
+ "\n".join(related_results["view"]),
286
+ "\n".join(related_results["list"]),
287
+ "\n".join(related_results["pivot"]),
288
+ "\n".join(related_results["access"]),
289
+ )
290
+
291
+ progress.update(task, advance=1)
292
+
293
+ console.print(table)
294
+ client.close()
295
+
296
+
297
+ def main():
298
+ """Entry point for the CLI."""
299
+ import asyncio
300
+ import sys
301
+
302
+ if sys.platform == "win32":
303
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
304
+
305
+ pull_command()
306
+
307
+
308
+ if __name__ == "__main__":
309
+ main()
@@ -0,0 +1,43 @@
1
+ """
2
+ Konecty SDK Python - A Python SDK for interacting with Konecty platform
3
+ """
4
+
5
+ from .client import KonectyClient, KonectyDict, KonectyFilter, KonectyFindParams
6
+ from .file_manager import FileManager
7
+ from .model import KonectyModelGenerator
8
+ from .settings import fill_settings, fill_settings_sync
9
+ from .types import Address as KonectyAddress
10
+ from .types import (
11
+ KonectyBaseModel,
12
+ KonectyDateTime,
13
+ KonectyEmail,
14
+ KonectyLabel,
15
+ KonectyLookup,
16
+ KonectyPersonName,
17
+ KonectyPhone,
18
+ KonectyUpdateId,
19
+ KonectyUser,
20
+ )
21
+
22
+ __all__ = [
23
+ "KonectyClient",
24
+ "KonectyModel",
25
+ "FileManager",
26
+ "KonectyModelGenerator",
27
+ "fill_settings",
28
+ "fill_settings_sync",
29
+ "KonectyAddress",
30
+ "KonectyBaseModel",
31
+ "KonectyDateTime",
32
+ "KonectyEmail",
33
+ "KonectyLabel",
34
+ "KonectyLookup",
35
+ "KonectyPersonName",
36
+ "KonectyPhone",
37
+ "KonectyUser",
38
+ "KonectyModel",
39
+ "KonectyFilter",
40
+ "KonectyFindParams",
41
+ "KonectyDict",
42
+ "KonectyUpdateId",
43
+ ]