konecty-sdk-python 2.0.0__tar.gz → 2.0.2__tar.gz
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.
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/client.py +2 -11
- konecty_sdk_python-2.0.2/KonectySdkPython/lib/feature_types/__init__.py +46 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/feature_types/cross_module_query.py +9 -3
- konecty_sdk_python-2.0.2/KonectySdkPython/lib/feature_types/query_json.py +242 -0
- konecty_sdk_python-2.0.2/KonectySdkPython/lib/serialization.py +13 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/aggregation.py +1 -1
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/export.py +1 -1
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/query.py +4 -2
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/stream.py +2 -1
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/types.py +2 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/PKG-INFO +1 -1
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/pyproject.toml +1 -1
- konecty_sdk_python-2.0.0/KonectySdkPython/lib/feature_types/__init__.py +0 -22
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/.gitignore +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/__init__.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/cli/__init__.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/cli/apply.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/cli/backup.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/cli/pull.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/__init__.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/exceptions.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/feature_types/kpi.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/file_manager.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/filters.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/http.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/model.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/__init__.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/base.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/change_user.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/comments.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/files.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/notifications.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/subscriptions.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/settings.py +0 -0
- {konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/README.md +0 -0
|
@@ -10,7 +10,6 @@ import aiohttp
|
|
|
10
10
|
from .exceptions import (
|
|
11
11
|
KonectyAPIError,
|
|
12
12
|
KonectyError,
|
|
13
|
-
KonectySerializationError,
|
|
14
13
|
KonectyValidationError,
|
|
15
14
|
)
|
|
16
15
|
from .file_manager import FileManager
|
|
@@ -18,6 +17,7 @@ from .filters import KonectyFilter, KonectyFindParams
|
|
|
18
17
|
from .http import request as _http_request
|
|
19
18
|
from .http import StreamResponse
|
|
20
19
|
from .feature_types.kpi import KpiConfig
|
|
20
|
+
from .serialization import json_serial
|
|
21
21
|
from .services.aggregation import AggregationService
|
|
22
22
|
from .services.change_user import ChangeUserService
|
|
23
23
|
from .services.comments import CommentsService
|
|
@@ -27,15 +27,13 @@ from .services.notifications import NotificationsService
|
|
|
27
27
|
from .services.query import QueryResult, QueryService
|
|
28
28
|
from .services.stream import FindStreamResult, StreamService
|
|
29
29
|
from .services.subscriptions import SubscriptionsService
|
|
30
|
-
from .types import KonectyDateTime, KonectyUpdateId
|
|
30
|
+
from .types import KonectyDateTime, KonectyDict, KonectyUpdateId
|
|
31
31
|
|
|
32
32
|
# Configura o logger do urllib3 para mostrar apenas erros
|
|
33
33
|
logging.getLogger("urllib3.connectionpool").setLevel(logging.ERROR)
|
|
34
34
|
|
|
35
35
|
logger = logging.getLogger(__name__)
|
|
36
36
|
|
|
37
|
-
KonectyDict = Dict[str, Any]
|
|
38
|
-
|
|
39
37
|
KONECTY_UPDATE_IGNORE_FIELDS = [
|
|
40
38
|
"_id",
|
|
41
39
|
"code",
|
|
@@ -57,13 +55,6 @@ def get_first_dict(items: List[Any]) -> Optional[KonectyDict]:
|
|
|
57
55
|
return None
|
|
58
56
|
|
|
59
57
|
|
|
60
|
-
def json_serial(obj: Any) -> str:
|
|
61
|
-
"""Serializa objetos para JSON."""
|
|
62
|
-
if isinstance(obj, datetime):
|
|
63
|
-
return {"$date": obj.isoformat()}
|
|
64
|
-
raise KonectySerializationError()
|
|
65
|
-
|
|
66
|
-
|
|
67
58
|
class KonectyClient:
|
|
68
59
|
def __init__(self, base_url: str, token: str) -> None:
|
|
69
60
|
self.base_url = base_url
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Feature-specific types (KPI, Graph, Pivot, CrossModuleQuery, QueryJson, etc.). Re-exported for convenience."""
|
|
2
|
+
|
|
3
|
+
from .cross_module_query import (
|
|
4
|
+
DEFAULT_PRIMARY_LIMIT,
|
|
5
|
+
MAX_RELATION_LIMIT,
|
|
6
|
+
MAX_RELATIONS,
|
|
7
|
+
Aggregator,
|
|
8
|
+
CrossModuleQuery,
|
|
9
|
+
CrossModuleRelation,
|
|
10
|
+
)
|
|
11
|
+
from .kpi import KpiConfig, KpiOperation
|
|
12
|
+
from .query_json import (
|
|
13
|
+
AggregatorName,
|
|
14
|
+
AggregatorSpec,
|
|
15
|
+
ConditionValue,
|
|
16
|
+
ExplicitJoinCondition,
|
|
17
|
+
FilterMatch,
|
|
18
|
+
QueryFilter,
|
|
19
|
+
QueryFilterCondition,
|
|
20
|
+
QueryJson,
|
|
21
|
+
QueryRelation,
|
|
22
|
+
QuerySortItem,
|
|
23
|
+
SortDirection,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"Aggregator",
|
|
28
|
+
"AggregatorName",
|
|
29
|
+
"AggregatorSpec",
|
|
30
|
+
"ConditionValue",
|
|
31
|
+
"CrossModuleQuery",
|
|
32
|
+
"CrossModuleRelation",
|
|
33
|
+
"DEFAULT_PRIMARY_LIMIT",
|
|
34
|
+
"ExplicitJoinCondition",
|
|
35
|
+
"FilterMatch",
|
|
36
|
+
"KpiConfig",
|
|
37
|
+
"KpiOperation",
|
|
38
|
+
"MAX_RELATION_LIMIT",
|
|
39
|
+
"MAX_RELATIONS",
|
|
40
|
+
"QueryFilter",
|
|
41
|
+
"QueryFilterCondition",
|
|
42
|
+
"QueryJson",
|
|
43
|
+
"QueryRelation",
|
|
44
|
+
"QuerySortItem",
|
|
45
|
+
"SortDirection",
|
|
46
|
+
]
|
|
@@ -10,7 +10,9 @@ MAX_RELATION_LIMIT = 100_000
|
|
|
10
10
|
DEFAULT_RELATION_LIMIT = 1000
|
|
11
11
|
DEFAULT_PRIMARY_LIMIT = 1000
|
|
12
12
|
|
|
13
|
-
AggregatorName =
|
|
13
|
+
AggregatorName = (
|
|
14
|
+
str # count, countDistinct, sum, avg, min, max, first, last, push, addToSet
|
|
15
|
+
)
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class Aggregator(BaseModel):
|
|
@@ -46,7 +48,9 @@ class CrossModuleRelation(BaseModel):
|
|
|
46
48
|
limit: int = Field(default=DEFAULT_RELATION_LIMIT, ge=1, le=MAX_RELATION_LIMIT)
|
|
47
49
|
start: int = Field(default=0, ge=0)
|
|
48
50
|
aggregators: Dict[str, Aggregator] = Field(..., min_length=1)
|
|
49
|
-
relations: Optional[List["CrossModuleRelation"]] = Field(
|
|
51
|
+
relations: Optional[List["CrossModuleRelation"]] = Field(
|
|
52
|
+
default=None, max_length=MAX_RELATIONS
|
|
53
|
+
)
|
|
50
54
|
|
|
51
55
|
model_config = {"extra": "allow"}
|
|
52
56
|
|
|
@@ -63,7 +67,9 @@ class CrossModuleQuery(BaseModel):
|
|
|
63
67
|
sort: Optional[Union[str, List[SortItem]]] = None
|
|
64
68
|
limit: int = Field(default=DEFAULT_PRIMARY_LIMIT, ge=1, le=MAX_RELATION_LIMIT)
|
|
65
69
|
start: int = Field(default=0, ge=0)
|
|
66
|
-
relations: List[CrossModuleRelation] = Field(
|
|
70
|
+
relations: List[CrossModuleRelation] = Field(
|
|
71
|
+
default_factory=list, max_length=MAX_RELATIONS
|
|
72
|
+
)
|
|
67
73
|
groupBy: List[str] = Field(default_factory=list, alias="groupBy")
|
|
68
74
|
aggregators: Dict[str, Aggregator] = Field(default_factory=dict)
|
|
69
75
|
includeTotal: bool = Field(default=True, alias="includeTotal")
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Strongly-typed query creator for POST /rest/query/json (Konecty cross-module query).
|
|
3
|
+
Use classes/dataclasses with properties for full intellisense; no builder pattern.
|
|
4
|
+
Serializes to the exact payload Konecty expects via to_dict().
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Any, Dict, List, Literal, Optional, Union
|
|
11
|
+
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
# Literal types (Konecty schema)
|
|
14
|
+
# ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
AggregatorName = Literal[
|
|
17
|
+
"count",
|
|
18
|
+
"countDistinct",
|
|
19
|
+
"sum",
|
|
20
|
+
"avg",
|
|
21
|
+
"min",
|
|
22
|
+
"max",
|
|
23
|
+
"first",
|
|
24
|
+
"last",
|
|
25
|
+
"push",
|
|
26
|
+
"addToSet",
|
|
27
|
+
]
|
|
28
|
+
"""Allowed aggregator names for relations (Konecty schema)."""
|
|
29
|
+
|
|
30
|
+
FilterMatch = Literal["and", "or"]
|
|
31
|
+
SortDirection = Literal["ASC", "DESC"]
|
|
32
|
+
|
|
33
|
+
# Value in a filter condition: scalar or list (e.g. for "in" operator)
|
|
34
|
+
ConditionValue = Union[str, int, float, bool, None, List[Union[str, int, float, bool]]]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# Filter (KonFilter)
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=False)
|
|
43
|
+
class QueryFilterCondition:
|
|
44
|
+
"""Single condition: term, operator, optional value. Mirrors Konecty Condition."""
|
|
45
|
+
|
|
46
|
+
term: str
|
|
47
|
+
operator: str
|
|
48
|
+
value: Optional[ConditionValue] = None
|
|
49
|
+
editable: Optional[bool] = None
|
|
50
|
+
disabled: Optional[bool] = None
|
|
51
|
+
sort: Optional[int] = None
|
|
52
|
+
|
|
53
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
54
|
+
out: Dict[str, Any] = {"term": self.term, "operator": self.operator}
|
|
55
|
+
if self.value is not None:
|
|
56
|
+
out["value"] = self.value
|
|
57
|
+
if self.editable is not None:
|
|
58
|
+
out["editable"] = self.editable
|
|
59
|
+
if self.disabled is not None:
|
|
60
|
+
out["disabled"] = self.disabled
|
|
61
|
+
if self.sort is not None:
|
|
62
|
+
out["sort"] = self.sort
|
|
63
|
+
return out
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=False)
|
|
67
|
+
class QueryFilter:
|
|
68
|
+
"""Filter for query or relation. match + conditions; optional textSearch and nested filters."""
|
|
69
|
+
|
|
70
|
+
match: FilterMatch = "and"
|
|
71
|
+
conditions: List[QueryFilterCondition] = field(default_factory=list)
|
|
72
|
+
text_search: Optional[str] = None
|
|
73
|
+
filters: Optional[List["QueryFilter"]] = None
|
|
74
|
+
|
|
75
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
76
|
+
out: Dict[str, Any] = {"match": self.match}
|
|
77
|
+
if self.conditions:
|
|
78
|
+
out["conditions"] = [c.to_dict() for c in self.conditions]
|
|
79
|
+
if self.text_search is not None:
|
|
80
|
+
out["textSearch"] = self.text_search
|
|
81
|
+
if self.filters:
|
|
82
|
+
out["filters"] = [f.to_dict() for f in self.filters]
|
|
83
|
+
return out
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
# Sort
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class QuerySortItem:
|
|
93
|
+
"""Sort item: property and direction. Konecty sort element."""
|
|
94
|
+
|
|
95
|
+
property: str
|
|
96
|
+
direction: SortDirection = "ASC"
|
|
97
|
+
|
|
98
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
99
|
+
return {"property": self.property, "direction": self.direction}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# ---------------------------------------------------------------------------
|
|
103
|
+
# Join (explicit on)
|
|
104
|
+
# ---------------------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass(frozen=True)
|
|
108
|
+
class ExplicitJoinCondition:
|
|
109
|
+
"""Explicit join: left and right keys. Optional in relation."""
|
|
110
|
+
|
|
111
|
+
left: str
|
|
112
|
+
right: str
|
|
113
|
+
|
|
114
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
115
|
+
return {"left": self.left, "right": self.right}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# ---------------------------------------------------------------------------
|
|
119
|
+
# Aggregator
|
|
120
|
+
# ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@dataclass(frozen=True)
|
|
124
|
+
class AggregatorSpec:
|
|
125
|
+
"""Aggregator in a relation. aggregator (name); field required for countDistinct and similar."""
|
|
126
|
+
|
|
127
|
+
aggregator: AggregatorName
|
|
128
|
+
field: Optional[str] = None
|
|
129
|
+
|
|
130
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
131
|
+
out: Dict[str, Any] = {"aggregator": self.aggregator}
|
|
132
|
+
if self.field is not None:
|
|
133
|
+
out["field"] = self.field
|
|
134
|
+
return out
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
# Relation (nested)
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
# Sort in relation: list of QuerySortItem or single string (e.g. "_createdAt DESC")
|
|
142
|
+
RelationSort = Union[List[QuerySortItem], str]
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass(frozen=False)
|
|
146
|
+
class QueryRelation:
|
|
147
|
+
"""
|
|
148
|
+
Relation in a cross-module query. document + lookup; at least one aggregator required.
|
|
149
|
+
Optional: on, filter, fields, sort, limit, start; optional nested relations.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
document: str
|
|
153
|
+
lookup: str
|
|
154
|
+
aggregators: Dict[str, AggregatorSpec]
|
|
155
|
+
on: Optional[ExplicitJoinCondition] = None
|
|
156
|
+
filter: Optional[QueryFilter] = None
|
|
157
|
+
fields: Optional[str] = None
|
|
158
|
+
sort: Optional[RelationSort] = None
|
|
159
|
+
limit: int = 1000
|
|
160
|
+
start: int = 0
|
|
161
|
+
relations: Optional[List["QueryRelation"]] = None
|
|
162
|
+
|
|
163
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
164
|
+
if not self.aggregators:
|
|
165
|
+
raise ValueError("At least one aggregator is required per relation")
|
|
166
|
+
out: Dict[str, Any] = {
|
|
167
|
+
"document": self.document,
|
|
168
|
+
"lookup": self.lookup,
|
|
169
|
+
"aggregators": {k: v.to_dict() for k, v in self.aggregators.items()},
|
|
170
|
+
"limit": self.limit,
|
|
171
|
+
"start": self.start,
|
|
172
|
+
}
|
|
173
|
+
if self.on is not None:
|
|
174
|
+
out["on"] = self.on.to_dict()
|
|
175
|
+
if self.filter is not None:
|
|
176
|
+
out["filter"] = self.filter.to_dict()
|
|
177
|
+
if self.fields is not None:
|
|
178
|
+
out["fields"] = self.fields
|
|
179
|
+
if self.sort is not None:
|
|
180
|
+
if isinstance(self.sort, str):
|
|
181
|
+
out["sort"] = self.sort
|
|
182
|
+
else:
|
|
183
|
+
out["sort"] = [s.to_dict() for s in self.sort]
|
|
184
|
+
if self.relations:
|
|
185
|
+
out["relations"] = [r.to_dict() for r in self.relations]
|
|
186
|
+
return out
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# ---------------------------------------------------------------------------
|
|
190
|
+
# Root query (CrossModuleQuery)
|
|
191
|
+
# ---------------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
# Sort at root: list of QuerySortItem or string
|
|
194
|
+
QuerySort = Union[List[QuerySortItem], str]
|
|
195
|
+
|
|
196
|
+
# Limits (Konecty constants)
|
|
197
|
+
MAX_RELATION_LIMIT_QUERY = 100_000
|
|
198
|
+
DEFAULT_PRIMARY_LIMIT_QUERY = 1000
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@dataclass(frozen=False)
|
|
202
|
+
class QueryJson:
|
|
203
|
+
"""
|
|
204
|
+
Fully-typed body for POST /rest/query/json. Primary document + optional filter,
|
|
205
|
+
fields, sort, limit, start, relations, groupBy, aggregators, includeTotal, includeMeta.
|
|
206
|
+
Use to_dict() to get the API payload (camelCase where required).
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
document: str
|
|
210
|
+
filter: Optional[QueryFilter] = None
|
|
211
|
+
fields: Optional[str] = None
|
|
212
|
+
sort: Optional[QuerySort] = None
|
|
213
|
+
limit: int = DEFAULT_PRIMARY_LIMIT_QUERY
|
|
214
|
+
start: int = 0
|
|
215
|
+
relations: List[QueryRelation] = field(default_factory=list)
|
|
216
|
+
group_by: List[str] = field(default_factory=list)
|
|
217
|
+
aggregators: Dict[str, AggregatorSpec] = field(default_factory=dict)
|
|
218
|
+
include_total: bool = True
|
|
219
|
+
include_meta: bool = False
|
|
220
|
+
|
|
221
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
222
|
+
"""Payload for POST /rest/query/json (camelCase for includeTotal, includeMeta, groupBy)."""
|
|
223
|
+
out: Dict[str, Any] = {
|
|
224
|
+
"document": self.document,
|
|
225
|
+
"limit": max(1, min(self.limit, MAX_RELATION_LIMIT_QUERY)),
|
|
226
|
+
"start": max(0, self.start),
|
|
227
|
+
"relations": [r.to_dict() for r in self.relations],
|
|
228
|
+
"groupBy": self.group_by,
|
|
229
|
+
"aggregators": {k: v.to_dict() for k, v in self.aggregators.items()},
|
|
230
|
+
"includeTotal": self.include_total,
|
|
231
|
+
"includeMeta": self.include_meta,
|
|
232
|
+
}
|
|
233
|
+
if self.filter is not None:
|
|
234
|
+
out["filter"] = self.filter.to_dict()
|
|
235
|
+
if self.fields is not None:
|
|
236
|
+
out["fields"] = self.fields
|
|
237
|
+
if self.sort is not None:
|
|
238
|
+
if isinstance(self.sort, str):
|
|
239
|
+
out["sort"] = self.sort
|
|
240
|
+
else:
|
|
241
|
+
out["sort"] = [s.to_dict() for s in self.sort]
|
|
242
|
+
return out
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""JSON serialization helpers for Konecty API (e.g. $date format)."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .exceptions import KonectySerializationError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def json_serial(obj: Any) -> Any:
|
|
10
|
+
"""Serialize objects to JSON-friendly form (e.g. datetime -> $date)."""
|
|
11
|
+
if isinstance(obj, datetime):
|
|
12
|
+
return {"$date": obj.isoformat()}
|
|
13
|
+
raise KonectySerializationError()
|
{konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/query.py
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import json
|
|
4
4
|
from typing import Any, AsyncGenerator, Dict, List, Optional, Union
|
|
5
5
|
|
|
6
|
-
from ..
|
|
6
|
+
from ..types import KonectyDict
|
|
7
7
|
from ..exceptions import KonectyAPIError
|
|
8
8
|
from ..http import StreamResponse
|
|
9
9
|
from .base import BaseService
|
|
@@ -93,7 +93,9 @@ class QueryService(BaseService):
|
|
|
93
93
|
Execute a cross-module query. POST /rest/query/json. Body is CrossModuleQuery (dict or model).
|
|
94
94
|
Returns QueryResult with .stream (async generator), .total, .meta.
|
|
95
95
|
"""
|
|
96
|
-
if hasattr(body, "
|
|
96
|
+
if hasattr(body, "to_dict"):
|
|
97
|
+
payload = body.to_dict()
|
|
98
|
+
elif hasattr(body, "model_dump"):
|
|
97
99
|
payload = body.model_dump(by_alias=True, exclude_none=True)
|
|
98
100
|
else:
|
|
99
101
|
payload = dict(body)
|
{konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/stream.py
RENAMED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import json
|
|
4
4
|
from typing import Any, AsyncGenerator, Dict, Optional
|
|
5
5
|
|
|
6
|
-
from ..
|
|
6
|
+
from ..serialization import json_serial
|
|
7
|
+
from ..types import KonectyDict
|
|
7
8
|
from ..filters import KonectyFilter, KonectyFindParams
|
|
8
9
|
from ..http import StreamResponse
|
|
9
10
|
from .base import BaseService
|
|
@@ -8,6 +8,8 @@ from pydantic import BaseModel, ConfigDict, Field
|
|
|
8
8
|
from pydantic.json_schema import JsonSchemaValue
|
|
9
9
|
from pydantic_core import CoreSchema, core_schema
|
|
10
10
|
|
|
11
|
+
KonectyDict = Dict[str, Any]
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
class KonectyDateTimeError(Exception):
|
|
13
15
|
"""Exceção base para erros de data/hora."""
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"""Feature-specific types (KPI, Graph, Pivot, CrossModuleQuery, etc.). Re-exported for convenience."""
|
|
2
|
-
|
|
3
|
-
from .cross_module_query import (
|
|
4
|
-
CrossModuleQuery,
|
|
5
|
-
CrossModuleRelation,
|
|
6
|
-
Aggregator,
|
|
7
|
-
DEFAULT_PRIMARY_LIMIT,
|
|
8
|
-
MAX_RELATION_LIMIT,
|
|
9
|
-
MAX_RELATIONS,
|
|
10
|
-
)
|
|
11
|
-
from .kpi import KpiConfig, KpiOperation
|
|
12
|
-
|
|
13
|
-
__all__ = [
|
|
14
|
-
"Aggregator",
|
|
15
|
-
"CrossModuleQuery",
|
|
16
|
-
"CrossModuleRelation",
|
|
17
|
-
"DEFAULT_PRIMARY_LIMIT",
|
|
18
|
-
"KpiConfig",
|
|
19
|
-
"KpiOperation",
|
|
20
|
-
"MAX_RELATION_LIMIT",
|
|
21
|
-
"MAX_RELATIONS",
|
|
22
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/feature_types/kpi.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/change_user.py
RENAMED
|
File without changes
|
{konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/comments.py
RENAMED
|
File without changes
|
{konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/files.py
RENAMED
|
File without changes
|
{konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/notifications.py
RENAMED
|
File without changes
|
{konecty_sdk_python-2.0.0 → konecty_sdk_python-2.0.2}/KonectySdkPython/lib/services/subscriptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|