fluidattacks_zoho_sdk 1.0.0__py3-none-any.whl → 2.0.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.
- fluidattacks_zoho_sdk/__init__.py +3 -5
- fluidattacks_zoho_sdk/_decoders.py +115 -31
- fluidattacks_zoho_sdk/_http_client/__init__.py +17 -0
- fluidattacks_zoho_sdk/_http_client/_client.py +348 -0
- fluidattacks_zoho_sdk/_http_client/_core.py +151 -0
- fluidattacks_zoho_sdk/_http_client/paginate.py +35 -0
- fluidattacks_zoho_sdk/bulk_export/__init__.py +29 -0
- fluidattacks_zoho_sdk/bulk_export/_client.py +195 -0
- fluidattacks_zoho_sdk/bulk_export/_decode.py +41 -0
- fluidattacks_zoho_sdk/bulk_export/core.py +97 -0
- fluidattacks_zoho_sdk/bulk_export/utils.py +72 -0
- fluidattacks_zoho_sdk/ids.py +8 -4
- fluidattacks_zoho_sdk/zoho_desk/__init__.py +51 -0
- fluidattacks_zoho_sdk/zoho_desk/_client.py +88 -0
- fluidattacks_zoho_sdk/zoho_desk/_decode.py +201 -86
- fluidattacks_zoho_sdk/zoho_desk/core.py +41 -24
- {fluidattacks_zoho_sdk-1.0.0.dist-info → fluidattacks_zoho_sdk-2.0.0.dist-info}/METADATA +2 -2
- fluidattacks_zoho_sdk-2.0.0.dist-info/RECORD +26 -0
- fluidattacks_zoho_sdk-1.0.0.dist-info/RECORD +0 -16
- {fluidattacks_zoho_sdk-1.0.0.dist-info → fluidattacks_zoho_sdk-2.0.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from fluidattacks_zoho_sdk._http_client import ClientFactory, HttpJsonClient, TokenManager
|
|
4
|
+
from fluidattacks_zoho_sdk.auth import Credentials
|
|
5
|
+
from fluidattacks_zoho_sdk.bulk_export import BulkApiFactory
|
|
6
|
+
from fluidattacks_zoho_sdk.bulk_export.core import BulkClient
|
|
7
|
+
from fluidattacks_zoho_sdk.ids import OrgId
|
|
8
|
+
|
|
9
|
+
from . import _client
|
|
10
|
+
from .core import AgentObj, ContactObj, DeskClient, UserObj
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _bulk_client(creds: Credentials, org_id: OrgId, token_manager: TokenManager) -> BulkClient:
|
|
14
|
+
return BulkApiFactory.new(creds, org_id, token_manager)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _from_client(
|
|
18
|
+
client: HttpJsonClient,
|
|
19
|
+
creds: Credentials,
|
|
20
|
+
org_id: OrgId,
|
|
21
|
+
token: TokenManager,
|
|
22
|
+
) -> DeskClient:
|
|
23
|
+
bulk_client = _bulk_client(creds, org_id, token)
|
|
24
|
+
|
|
25
|
+
return DeskClient(
|
|
26
|
+
get_teams=_client.get_teams(client),
|
|
27
|
+
fetch_bulk_agents=_client.fetch_bulk_export_agents(bulk_client),
|
|
28
|
+
fetch_bulk_tickets=_client.fetch_bulk_export_tickets(bulk_client),
|
|
29
|
+
fetch_bulk_contacts=_client.fetch_bulk_contacts(bulk_client),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class DeskClientFactory:
|
|
35
|
+
@staticmethod
|
|
36
|
+
def new(creds: Credentials, org_id: OrgId, token_manager: TokenManager) -> DeskClient:
|
|
37
|
+
return _from_client(
|
|
38
|
+
ClientFactory.new(creds, org_id, token_manager),
|
|
39
|
+
creds,
|
|
40
|
+
org_id,
|
|
41
|
+
token_manager,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"AgentObj",
|
|
47
|
+
"ContactObj",
|
|
48
|
+
"DeskClient",
|
|
49
|
+
"DeskClientFactory",
|
|
50
|
+
"UserObj",
|
|
51
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from fa_purity import Cmd, FrozenList, ResultE, cast_exception
|
|
5
|
+
from fa_purity.json import Primitive, UnfoldedFactory
|
|
6
|
+
from fluidattacks_etl_utils.bug import Bug
|
|
7
|
+
|
|
8
|
+
from fluidattacks_zoho_sdk._decoders import assert_single
|
|
9
|
+
from fluidattacks_zoho_sdk._http_client import HttpJsonClient, RelativeEndpoint
|
|
10
|
+
from fluidattacks_zoho_sdk.bulk_export import BulkClient, BulkEndpoint, FileName, ModuleName
|
|
11
|
+
|
|
12
|
+
from ._decode import decode_agents, decode_contacts, decode_teams, decode_tickets
|
|
13
|
+
from .core import AgentObj, ContactObj, DerivedAgent, TeamObj, TicketObj
|
|
14
|
+
|
|
15
|
+
LOG = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def fetch_bulk_contacts(
|
|
19
|
+
client: BulkClient,
|
|
20
|
+
) -> Cmd[ResultE[FrozenList[ContactObj]]]:
|
|
21
|
+
endpoint_bulk = BulkEndpoint("bulkExport")
|
|
22
|
+
module_name = ModuleName("contacts")
|
|
23
|
+
file_name = FileName("Contacts__1.csv")
|
|
24
|
+
|
|
25
|
+
return client.fetch_bulk(module_name, endpoint_bulk, file_name).map(
|
|
26
|
+
lambda result: (
|
|
27
|
+
result.alt(
|
|
28
|
+
lambda e: cast_exception(
|
|
29
|
+
Bug.new("_generate_bulk_export_contacts", inspect.currentframe(), e, ()),
|
|
30
|
+
),
|
|
31
|
+
).bind(decode_contacts)
|
|
32
|
+
),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def fetch_bulk_export_tickets(
|
|
37
|
+
client: BulkClient,
|
|
38
|
+
) -> Cmd[ResultE[FrozenList[TicketObj]]]:
|
|
39
|
+
endpoint_bulk = BulkEndpoint("bulkExport")
|
|
40
|
+
module_name = ModuleName("tickets")
|
|
41
|
+
file_name = FileName("Cases__1.csv")
|
|
42
|
+
|
|
43
|
+
return client.fetch_bulk(module_name, endpoint_bulk, file_name).map(
|
|
44
|
+
lambda result: (
|
|
45
|
+
result.alt(
|
|
46
|
+
lambda e: cast_exception(
|
|
47
|
+
Bug.new("_generate_bulk_export_tickets", inspect.currentframe(), e, ()),
|
|
48
|
+
),
|
|
49
|
+
).bind(decode_tickets)
|
|
50
|
+
),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_teams(
|
|
55
|
+
client: HttpJsonClient,
|
|
56
|
+
) -> Cmd[ResultE[FrozenList[tuple[TeamObj, FrozenList[DerivedAgent]]]]]:
|
|
57
|
+
endpoint = RelativeEndpoint.new("teams")
|
|
58
|
+
params: dict[str, Primitive] = {}
|
|
59
|
+
return client.get(
|
|
60
|
+
endpoint,
|
|
61
|
+
UnfoldedFactory.from_dict(params),
|
|
62
|
+
).map(
|
|
63
|
+
lambda result: (
|
|
64
|
+
result.alt(
|
|
65
|
+
lambda e: cast_exception(Bug.new("_get_teams", inspect.currentframe(), e, ())),
|
|
66
|
+
)
|
|
67
|
+
.bind(assert_single)
|
|
68
|
+
.bind(lambda teams: decode_teams(teams))
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def fetch_bulk_export_agents(
|
|
74
|
+
client: BulkClient,
|
|
75
|
+
) -> Cmd[ResultE[FrozenList[AgentObj]]]:
|
|
76
|
+
endpoint_bulk = BulkEndpoint("bulkExport")
|
|
77
|
+
module_name = ModuleName("agents")
|
|
78
|
+
file_name = FileName("Agents__1.csv")
|
|
79
|
+
|
|
80
|
+
return client.fetch_bulk(module_name, endpoint_bulk, file_name).map(
|
|
81
|
+
lambda result: (
|
|
82
|
+
result.alt(
|
|
83
|
+
lambda e: cast_exception(
|
|
84
|
+
Bug.new("_generate_bulk_export_agent", inspect.currentframe(), e, ()),
|
|
85
|
+
),
|
|
86
|
+
).bind(decode_agents)
|
|
87
|
+
),
|
|
88
|
+
)
|
|
@@ -1,35 +1,40 @@
|
|
|
1
|
-
from fa_purity import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
from fa_purity import (
|
|
2
|
+
FrozenList,
|
|
3
|
+
Maybe,
|
|
4
|
+
PureIterFactory,
|
|
5
|
+
Result,
|
|
6
|
+
ResultE,
|
|
7
|
+
ResultTransform,
|
|
8
|
+
Unsafe,
|
|
5
9
|
)
|
|
10
|
+
from fa_purity.json import JsonObj, JsonPrimitiveUnfolder, JsonUnfolder, Unfolder
|
|
6
11
|
from fluidattacks_etl_utils import smash
|
|
7
12
|
from fluidattacks_etl_utils.decode import DecodeUtils
|
|
8
|
-
from fluidattacks_etl_utils.natural import Natural
|
|
13
|
+
from fluidattacks_etl_utils.natural import Natural, NaturalOperations
|
|
9
14
|
|
|
10
15
|
from fluidattacks_zoho_sdk._decoders import (
|
|
11
|
-
decode_account_id,
|
|
12
|
-
decode_account_id_ticket,
|
|
13
16
|
decode_contact_id,
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
decode_contact_id_bulk,
|
|
18
|
+
decode_department_id,
|
|
19
|
+
decode_department_id_team,
|
|
20
|
+
decode_id_team,
|
|
21
|
+
decode_list_objs,
|
|
16
22
|
decode_maybe_str,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
decode_profile_id,
|
|
20
|
-
decode_require_date,
|
|
21
|
-
decode_rol_id,
|
|
22
|
-
decode_team_id,
|
|
23
|
+
decode_opt_date_to_utc,
|
|
24
|
+
decode_optional_id,
|
|
23
25
|
decode_ticket_id,
|
|
24
26
|
decode_user_id,
|
|
27
|
+
decode_user_id_bulk,
|
|
25
28
|
)
|
|
26
|
-
from fluidattacks_zoho_sdk.ids import UserId
|
|
29
|
+
from fluidattacks_zoho_sdk.ids import AccountId, ProductId, UserId
|
|
27
30
|
from fluidattacks_zoho_sdk.zoho_desk.core import (
|
|
28
31
|
AgentObj,
|
|
29
32
|
ContactAddres,
|
|
30
33
|
ContactDates,
|
|
31
34
|
ContactInfo,
|
|
32
35
|
ContactObj,
|
|
36
|
+
DerivedAgent,
|
|
37
|
+
TeamObj,
|
|
33
38
|
TicketDates,
|
|
34
39
|
TicketObj,
|
|
35
40
|
TicketProperties,
|
|
@@ -37,44 +42,45 @@ from fluidattacks_zoho_sdk.zoho_desk.core import (
|
|
|
37
42
|
)
|
|
38
43
|
|
|
39
44
|
|
|
45
|
+
def parse_bool(v: str) -> bool:
|
|
46
|
+
lv = v.lower().strip()
|
|
47
|
+
if lv == "true":
|
|
48
|
+
return True
|
|
49
|
+
if lv == "false":
|
|
50
|
+
return False
|
|
51
|
+
msg = f"Invalid boolean string: {v}"
|
|
52
|
+
raise ValueError(msg)
|
|
53
|
+
|
|
54
|
+
|
|
40
55
|
def decode_user(raw: JsonObj) -> ResultE[UserObj]:
|
|
41
56
|
return smash.smash_result_3(
|
|
42
57
|
decode_user_id(raw),
|
|
43
|
-
JsonUnfolder.
|
|
58
|
+
JsonUnfolder.optional(raw, "firstName", DecodeUtils.to_opt_str).map(
|
|
59
|
+
lambda v: v.bind(lambda j: j),
|
|
60
|
+
),
|
|
44
61
|
JsonUnfolder.require(raw, "lastName", DecodeUtils.to_str),
|
|
45
62
|
).map(lambda obj: UserObj(*obj))
|
|
46
63
|
|
|
47
64
|
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
JsonUnfolder.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
smash.bind_chain(
|
|
59
|
-
values,
|
|
60
|
-
lambda vals: JsonUnfolder.require(raw, "status", DecodeUtils.to_str).map(
|
|
61
|
-
lambda status: (*vals, status),
|
|
62
|
-
),
|
|
63
|
-
)
|
|
64
|
-
.alt(lambda v: v.map(lambda le: le, lambda r: r))
|
|
65
|
-
.map(lambda obj: AgentObj(*obj))
|
|
66
|
-
)
|
|
65
|
+
def decode_user_bulk(raw: JsonObj) -> ResultE[UserObj]:
|
|
66
|
+
return smash.smash_result_3(
|
|
67
|
+
decode_user_id_bulk(raw),
|
|
68
|
+
JsonUnfolder.optional(raw, "First Name", DecodeUtils.to_opt_str).map(
|
|
69
|
+
lambda v: v.bind(lambda j: j),
|
|
70
|
+
),
|
|
71
|
+
JsonUnfolder.require(raw, "Last Name", DecodeUtils.to_str),
|
|
72
|
+
).map(lambda v: UserObj(*v))
|
|
67
73
|
|
|
68
74
|
|
|
69
75
|
def decode_contact_addres(raw: JsonObj) -> ResultE[ContactAddres]:
|
|
70
76
|
return smash.smash_result_3(
|
|
71
|
-
JsonUnfolder.optional(raw, "
|
|
77
|
+
JsonUnfolder.optional(raw, "City", DecodeUtils.to_opt_str).map(
|
|
72
78
|
lambda v: v.bind(lambda x: x),
|
|
73
79
|
),
|
|
74
|
-
JsonUnfolder.optional(raw, "
|
|
80
|
+
JsonUnfolder.optional(raw, "Country", DecodeUtils.to_opt_str).map(
|
|
75
81
|
lambda v: v.bind(lambda x: x),
|
|
76
82
|
),
|
|
77
|
-
JsonUnfolder.optional(raw, "
|
|
83
|
+
JsonUnfolder.optional(raw, "Street", DecodeUtils.to_opt_str).map(
|
|
78
84
|
lambda v: v.bind(lambda x: x),
|
|
79
85
|
),
|
|
80
86
|
).map(lambda obj: ContactAddres(*obj))
|
|
@@ -82,57 +88,52 @@ def decode_contact_addres(raw: JsonObj) -> ResultE[ContactAddres]:
|
|
|
82
88
|
|
|
83
89
|
def decode_contact_dates(raw: JsonObj) -> ResultE[ContactDates]:
|
|
84
90
|
return smash.smash_result_2(
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
decode_opt_date_to_utc(raw, "Created Time"),
|
|
92
|
+
decode_opt_date_to_utc(raw, "Modified Time"),
|
|
87
93
|
).map(lambda obj: ContactDates(*obj))
|
|
88
94
|
|
|
89
95
|
|
|
90
96
|
def decode_contact_info(raw: JsonObj) -> ResultE[ContactInfo]:
|
|
91
97
|
return smash.smash_result_5(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
),
|
|
96
|
-
|
|
97
|
-
JsonUnfolder.optional(raw, "mobile", DecodeUtils.to_opt_str).map(
|
|
98
|
-
lambda v: v.bind(lambda x: x),
|
|
99
|
-
),
|
|
100
|
-
JsonUnfolder.optional(raw, "secondaryEmail", DecodeUtils.to_opt_str).map(
|
|
101
|
-
lambda v: v.bind(lambda x: x),
|
|
102
|
-
),
|
|
98
|
+
decode_maybe_str(raw, "Email"),
|
|
99
|
+
decode_maybe_str(raw, "Facebook"),
|
|
100
|
+
decode_maybe_str(raw, "Phone"),
|
|
101
|
+
decode_maybe_str(raw, "Mobile"),
|
|
102
|
+
decode_maybe_str(raw, "Secondary Email"),
|
|
103
103
|
).map(lambda obj: ContactInfo(*obj))
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
def decode_contact_obj(raw: JsonObj) -> ResultE[ContactObj]:
|
|
107
107
|
first = smash.smash_result_5(
|
|
108
|
-
|
|
108
|
+
decode_contact_id_bulk(raw),
|
|
109
|
+
decode_user_bulk(raw),
|
|
109
110
|
decode_contact_info(raw),
|
|
110
111
|
decode_contact_addres(raw),
|
|
111
112
|
decode_contact_dates(raw),
|
|
112
|
-
decode_account_id(raw),
|
|
113
113
|
)
|
|
114
114
|
|
|
115
|
-
second = smash.
|
|
116
|
-
|
|
115
|
+
second = smash.smash_result_5(
|
|
116
|
+
decode_optional_id(raw, "Account ID"),
|
|
117
|
+
JsonUnfolder.require(raw, "Contact Owner", DecodeUtils.to_str)
|
|
117
118
|
.bind(lambda v: Natural.from_int(int(v)))
|
|
118
119
|
.map(lambda j: UserId(j)),
|
|
119
|
-
|
|
120
|
-
JsonUnfolder.optional(raw, "
|
|
120
|
+
decode_optional_id(raw, "CRM ID"),
|
|
121
|
+
JsonUnfolder.optional(raw, "Description", DecodeUtils.to_opt_str).map(
|
|
121
122
|
lambda v: v.bind(lambda j: j),
|
|
122
123
|
),
|
|
123
|
-
JsonUnfolder.optional(raw, "
|
|
124
|
+
JsonUnfolder.optional(raw, "State", DecodeUtils.to_opt_str).map(
|
|
124
125
|
lambda v: v.bind(lambda j: j),
|
|
125
126
|
),
|
|
126
127
|
)
|
|
127
128
|
|
|
128
129
|
three = smash.smash_result_3(
|
|
129
|
-
JsonUnfolder.optional(raw, "
|
|
130
|
+
JsonUnfolder.optional(raw, "Title", DecodeUtils.to_opt_str).map(
|
|
130
131
|
lambda v: v.bind(lambda j: j),
|
|
131
132
|
),
|
|
132
|
-
JsonUnfolder.optional(raw, "
|
|
133
|
+
JsonUnfolder.optional(raw, "Type", DecodeUtils.to_opt_str).map(
|
|
133
134
|
lambda v: v.bind(lambda j: j),
|
|
134
135
|
),
|
|
135
|
-
JsonUnfolder.optional(raw, "
|
|
136
|
+
JsonUnfolder.optional(raw, "Zip", DecodeUtils.to_opt_str).map(
|
|
136
137
|
lambda v: v.bind(lambda j: j),
|
|
137
138
|
),
|
|
138
139
|
)
|
|
@@ -140,53 +141,167 @@ def decode_contact_obj(raw: JsonObj) -> ResultE[ContactObj]:
|
|
|
140
141
|
return smash.smash_result_3(first, second, three).map(lambda v: ContactObj(*v[0], *v[1], *v[2]))
|
|
141
142
|
|
|
142
143
|
|
|
144
|
+
def decode_contacts(raws: FrozenList[JsonObj]) -> ResultE[FrozenList[ContactObj]]:
|
|
145
|
+
return decode_list_objs(raws, decode_contact_obj)
|
|
146
|
+
|
|
147
|
+
|
|
143
148
|
def decode_ticket_dates(raw: JsonObj) -> ResultE[TicketDates]:
|
|
144
149
|
return smash.smash_result_4(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
150
|
+
decode_opt_date_to_utc(raw, "Modified Time"),
|
|
151
|
+
decode_opt_date_to_utc(raw, "Created Time"),
|
|
152
|
+
decode_opt_date_to_utc(raw, "Ticket Closed Time"),
|
|
153
|
+
decode_opt_date_to_utc(raw, "Customer Response Time"),
|
|
149
154
|
).map(lambda v: TicketDates(*v))
|
|
150
155
|
|
|
151
156
|
|
|
152
157
|
def decode_ticket_properties(raw: JsonObj) -> ResultE[TicketProperties]:
|
|
153
|
-
first = smash.
|
|
158
|
+
first = smash.smash_result_4(
|
|
154
159
|
decode_ticket_id(raw),
|
|
155
|
-
|
|
156
|
-
decode_maybe_str(raw, "
|
|
157
|
-
decode_maybe_str(raw, "
|
|
158
|
-
decode_maybe_str(raw, "status"),
|
|
160
|
+
decode_maybe_str(raw, "Subject"),
|
|
161
|
+
decode_maybe_str(raw, "Channel"),
|
|
162
|
+
decode_maybe_str(raw, "Status"),
|
|
159
163
|
)
|
|
160
164
|
|
|
161
165
|
second = smash.smash_result_4(
|
|
162
|
-
decode_maybe_str(raw, "
|
|
163
|
-
JsonUnfolder.require(raw, "
|
|
164
|
-
|
|
165
|
-
decode_maybe_str(raw, "
|
|
166
|
+
decode_maybe_str(raw, "Category"),
|
|
167
|
+
JsonUnfolder.require(raw, "Is Escalated", DecodeUtils.to_str).map(parse_bool),
|
|
168
|
+
decode_maybe_str(raw, "Priority"),
|
|
169
|
+
decode_maybe_str(raw, "Resolution"),
|
|
166
170
|
)
|
|
167
171
|
|
|
168
172
|
three = smash.smash_result_2(
|
|
169
|
-
decode_maybe_str(raw, "
|
|
170
|
-
decode_maybe_str(raw, "
|
|
173
|
+
decode_maybe_str(raw, "Classifications"),
|
|
174
|
+
decode_maybe_str(raw, "Description"),
|
|
171
175
|
)
|
|
172
176
|
return smash.smash_result_3(first, second, three).map(
|
|
173
177
|
lambda v: TicketProperties(*v[0], *v[1], *v[2]),
|
|
174
178
|
)
|
|
175
179
|
|
|
176
180
|
|
|
181
|
+
def decode_maybe_account_id(raw: JsonObj) -> ResultE[Maybe[AccountId]]:
|
|
182
|
+
return JsonUnfolder.optional(raw, "Account ID", DecodeUtils.to_opt_str).map(
|
|
183
|
+
lambda v: v.bind(lambda x: x)
|
|
184
|
+
.map(
|
|
185
|
+
lambda j: Natural.from_int(int(j)).alt(Unsafe.raise_exception).to_union(),
|
|
186
|
+
)
|
|
187
|
+
.map(lambda obj: AccountId(obj)),
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def decode_maybe_product_id(raw: JsonObj) -> ResultE[Maybe[ProductId]]:
|
|
192
|
+
return JsonUnfolder.optional(raw, "Product ID", DecodeUtils.to_opt_str).map(
|
|
193
|
+
lambda v: v.bind(lambda x: x)
|
|
194
|
+
.map(
|
|
195
|
+
lambda j: Natural.from_int(int(j)).alt(Unsafe.raise_exception).to_union(),
|
|
196
|
+
)
|
|
197
|
+
.map(lambda obj: ProductId(obj)),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
177
201
|
def decode_ticket_obj(raw: JsonObj) -> ResultE[TicketObj]:
|
|
178
202
|
first = smash.smash_result_5(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
203
|
+
decode_optional_id(raw, "Account ID"),
|
|
204
|
+
decode_optional_id(raw, "Department"),
|
|
205
|
+
decode_optional_id(raw, "Team ID"),
|
|
206
|
+
decode_optional_id(raw, "Product ID"),
|
|
183
207
|
decode_ticket_dates(raw),
|
|
184
208
|
)
|
|
185
|
-
|
|
209
|
+
|
|
210
|
+
second = smash.smash_result_4(
|
|
186
211
|
decode_ticket_properties(raw),
|
|
187
212
|
decode_contact_id(raw),
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
decode_maybe_str(raw, "onholdTime"),
|
|
213
|
+
decode_maybe_str(raw, "Email"),
|
|
214
|
+
decode_maybe_str(raw, "Phone"),
|
|
191
215
|
)
|
|
192
|
-
|
|
216
|
+
|
|
217
|
+
third = smash.smash_result_2(
|
|
218
|
+
decode_maybe_str(raw, "Ticket On Hold Time"),
|
|
219
|
+
decode_maybe_str(raw, "Stakeholder"),
|
|
220
|
+
)
|
|
221
|
+
return smash.smash_result_3(first, second, third).map(lambda v: TicketObj(*v[0], *v[1], *v[2]))
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def decode_tickets(raws: FrozenList[JsonObj]) -> ResultE[FrozenList[TicketObj]]:
|
|
225
|
+
return decode_list_objs(raws, decode_ticket_obj)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def decode_team_obj(raw: JsonObj) -> ResultE[TeamObj]:
|
|
229
|
+
return smash.smash_result_4(
|
|
230
|
+
decode_department_id(raw),
|
|
231
|
+
decode_id_team(raw),
|
|
232
|
+
JsonUnfolder.require(raw, "description", DecodeUtils.to_str),
|
|
233
|
+
JsonUnfolder.require(raw, "name", DecodeUtils.to_str),
|
|
234
|
+
).map(lambda team: TeamObj(*team))
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def decode_derived_agent(
|
|
238
|
+
id_agent: str,
|
|
239
|
+
) -> ResultE[DerivedAgent]:
|
|
240
|
+
return Result.success(DerivedAgent(UserId(NaturalOperations.absolute(int(id_agent)))))
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def decode_team_obj_with_agents(raw: JsonObj) -> ResultE[tuple[TeamObj, FrozenList[DerivedAgent]]]:
|
|
244
|
+
team_r: ResultE[TeamObj] = smash.smash_result_4(
|
|
245
|
+
decode_department_id_team(raw),
|
|
246
|
+
decode_id_team(raw),
|
|
247
|
+
JsonUnfolder.require(raw, "description", DecodeUtils.to_str),
|
|
248
|
+
JsonUnfolder.require(raw, "name", DecodeUtils.to_str),
|
|
249
|
+
).map(lambda team: TeamObj(*team))
|
|
250
|
+
|
|
251
|
+
derived_agents_json = JsonUnfolder.require(
|
|
252
|
+
raw,
|
|
253
|
+
"derivedAgents",
|
|
254
|
+
lambda v: Unfolder.to_list_of(
|
|
255
|
+
v,
|
|
256
|
+
lambda j: Unfolder.to_primitive(j).bind(lambda j: JsonPrimitiveUnfolder.to_str(j)),
|
|
257
|
+
),
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
list_agents = derived_agents_json.bind(
|
|
261
|
+
lambda agents: ResultTransform.all_ok(
|
|
262
|
+
tuple(PureIterFactory.from_list(agents).map(decode_derived_agent)),
|
|
263
|
+
),
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
return team_r.bind(lambda team: list_agents.map(lambda agents: (team, agents)))
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def decode_derived_agents(agents: FrozenList[str]) -> ResultE[FrozenList[DerivedAgent]]:
|
|
270
|
+
if not agents:
|
|
271
|
+
return Result.failure(ValueError("Expected a list wiht elements"))
|
|
272
|
+
return ResultTransform.all_ok(
|
|
273
|
+
PureIterFactory.from_list(agents).map(lambda v: decode_derived_agent(v)).to_list(),
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def decode_teams(raw: JsonObj) -> ResultE[FrozenList[tuple[TeamObj, FrozenList[DerivedAgent]]]]:
|
|
278
|
+
return JsonUnfolder.require(
|
|
279
|
+
raw,
|
|
280
|
+
"teams",
|
|
281
|
+
lambda v: Unfolder.to_list_of(
|
|
282
|
+
v,
|
|
283
|
+
lambda j: Unfolder.to_json(j).bind(decode_team_obj_with_agents),
|
|
284
|
+
),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
# DECODERS AGENTS
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def decode_agent_obj(raw: JsonObj) -> ResultE[AgentObj]:
|
|
292
|
+
first = smash.smash_result_3(
|
|
293
|
+
decode_user_bulk(raw),
|
|
294
|
+
JsonUnfolder.require(raw, "IsConfirmed", DecodeUtils.to_str).map(parse_bool),
|
|
295
|
+
JsonUnfolder.require(raw, "Email", DecodeUtils.to_str),
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
second = smash.smash_result_3(
|
|
299
|
+
JsonUnfolder.require(raw, "Profile", DecodeUtils.to_str),
|
|
300
|
+
JsonUnfolder.require(raw, "Role", DecodeUtils.to_str),
|
|
301
|
+
JsonUnfolder.require(raw, "Status", DecodeUtils.to_str),
|
|
302
|
+
)
|
|
303
|
+
return smash.smash_result_2(first, second).map(lambda v: AgentObj(*v[0], *v[1]))
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def decode_agents(raws: FrozenList[JsonObj]) -> ResultE[FrozenList[AgentObj]]:
|
|
307
|
+
return decode_list_objs(raws, decode_agent_obj)
|
|
@@ -1,26 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from dataclasses import dataclass
|
|
2
4
|
|
|
3
|
-
from fa_purity import Maybe
|
|
5
|
+
from fa_purity import Cmd, FrozenList, Maybe, ResultE
|
|
4
6
|
from fa_purity.date_time import DatetimeUTC
|
|
5
7
|
|
|
6
8
|
from fluidattacks_zoho_sdk.ids import (
|
|
7
|
-
AccountId,
|
|
8
9
|
ContactId,
|
|
9
|
-
CrmId,
|
|
10
10
|
DeparmentId,
|
|
11
|
-
ProductId,
|
|
12
|
-
ProfileId,
|
|
13
|
-
RoleId,
|
|
14
11
|
TeamId,
|
|
15
12
|
TicketId,
|
|
16
13
|
UserId,
|
|
17
14
|
)
|
|
18
15
|
|
|
19
16
|
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class OptionalId:
|
|
19
|
+
id: str
|
|
20
|
+
|
|
21
|
+
|
|
20
22
|
@dataclass(frozen=True)
|
|
21
23
|
class UserObj:
|
|
22
24
|
id_user: UserId
|
|
23
|
-
first_name: str
|
|
25
|
+
first_name: Maybe[str]
|
|
24
26
|
last_name: str
|
|
25
27
|
|
|
26
28
|
|
|
@@ -29,22 +31,22 @@ class AgentObj:
|
|
|
29
31
|
user: UserObj
|
|
30
32
|
is_confirmed: bool
|
|
31
33
|
email: str
|
|
32
|
-
profile:
|
|
33
|
-
role:
|
|
34
|
+
profile: str
|
|
35
|
+
role: str
|
|
34
36
|
status: str
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
@dataclass(frozen=True)
|
|
38
40
|
class ContactDates:
|
|
39
|
-
created_time: DatetimeUTC
|
|
40
|
-
modified_time: DatetimeUTC
|
|
41
|
+
created_time: Maybe[DatetimeUTC]
|
|
42
|
+
modified_time: Maybe[DatetimeUTC]
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
@dataclass(frozen=True)
|
|
44
46
|
class ContactInfo:
|
|
45
|
-
email: str
|
|
47
|
+
email: Maybe[str]
|
|
46
48
|
facebook: Maybe[str]
|
|
47
|
-
phone: str
|
|
49
|
+
phone: Maybe[str]
|
|
48
50
|
mobile: Maybe[str]
|
|
49
51
|
secondary_email: Maybe[str]
|
|
50
52
|
|
|
@@ -58,13 +60,14 @@ class ContactAddres:
|
|
|
58
60
|
|
|
59
61
|
@dataclass(frozen=True)
|
|
60
62
|
class ContactObj:
|
|
63
|
+
contact_id: ContactId
|
|
61
64
|
user: UserObj
|
|
62
65
|
contact_info: ContactInfo
|
|
63
66
|
contact_addres: ContactAddres
|
|
64
67
|
contact_dates: ContactDates
|
|
65
|
-
account_id:
|
|
68
|
+
account_id: Maybe[OptionalId]
|
|
66
69
|
contact_owner: UserId
|
|
67
|
-
crm_id:
|
|
70
|
+
crm_id: Maybe[OptionalId]
|
|
68
71
|
description: Maybe[str]
|
|
69
72
|
state: Maybe[str]
|
|
70
73
|
title: Maybe[str]
|
|
@@ -80,10 +83,15 @@ class TeamObj:
|
|
|
80
83
|
name: str
|
|
81
84
|
|
|
82
85
|
|
|
86
|
+
@dataclass(frozen=True)
|
|
87
|
+
class DerivedAgent:
|
|
88
|
+
id_user: UserId
|
|
89
|
+
|
|
90
|
+
|
|
83
91
|
@dataclass(frozen=True)
|
|
84
92
|
class TicketDates:
|
|
85
93
|
modified_time: Maybe[DatetimeUTC]
|
|
86
|
-
created_time: DatetimeUTC
|
|
94
|
+
created_time: Maybe[DatetimeUTC]
|
|
87
95
|
closed_time: Maybe[DatetimeUTC]
|
|
88
96
|
customer_response_time: Maybe[DatetimeUTC]
|
|
89
97
|
|
|
@@ -91,13 +99,12 @@ class TicketDates:
|
|
|
91
99
|
@dataclass(frozen=True)
|
|
92
100
|
class TicketProperties:
|
|
93
101
|
id_ticket: TicketId
|
|
94
|
-
ticket_number: str
|
|
95
102
|
suject: Maybe[str]
|
|
96
103
|
channel: Maybe[str]
|
|
97
104
|
status: Maybe[str]
|
|
98
105
|
category: Maybe[str]
|
|
99
106
|
is_escalated: bool
|
|
100
|
-
priority: str
|
|
107
|
+
priority: Maybe[str]
|
|
101
108
|
resolution: Maybe[str]
|
|
102
109
|
classification: Maybe[str]
|
|
103
110
|
description: Maybe[str]
|
|
@@ -105,13 +112,23 @@ class TicketProperties:
|
|
|
105
112
|
|
|
106
113
|
@dataclass(frozen=True)
|
|
107
114
|
class TicketObj:
|
|
108
|
-
account_id:
|
|
109
|
-
deparment_id:
|
|
110
|
-
team_id:
|
|
111
|
-
product_id:
|
|
115
|
+
account_id: Maybe[OptionalId]
|
|
116
|
+
deparment_id: Maybe[OptionalId]
|
|
117
|
+
team_id: Maybe[OptionalId]
|
|
118
|
+
product_id: Maybe[OptionalId]
|
|
112
119
|
ticket_date: TicketDates
|
|
113
120
|
ticket_properties: TicketProperties
|
|
114
121
|
contact_id: ContactId
|
|
115
|
-
email: str
|
|
116
|
-
phone: str
|
|
122
|
+
email: Maybe[str]
|
|
123
|
+
phone: Maybe[str]
|
|
117
124
|
onhold_time: Maybe[str]
|
|
125
|
+
stakeholder: Maybe[str]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass(frozen=True)
|
|
129
|
+
class DeskClient:
|
|
130
|
+
get_teams: Cmd[ResultE[FrozenList[tuple[TeamObj, FrozenList[DerivedAgent]]]]]
|
|
131
|
+
# bulk export
|
|
132
|
+
fetch_bulk_agents: Cmd[ResultE[FrozenList[AgentObj]]]
|
|
133
|
+
fetch_bulk_tickets: Cmd[ResultE[FrozenList[TicketObj]]]
|
|
134
|
+
fetch_bulk_contacts: Cmd[ResultE[FrozenList[ContactObj]]]
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fluidattacks_zoho_sdk
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: zoho SDK
|
|
5
5
|
Author-email: Product Team <development@fluidattacks.com>
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Requires-Dist: fa-purity >=2.5.0, <3.0.0
|
|
8
|
-
Requires-Dist: fluidattacks-etl-utils >=
|
|
8
|
+
Requires-Dist: fluidattacks-etl-utils >=2.0.0, <3.0.0
|
|
9
9
|
Requires-Dist: pure-requests >=3.0.0, <4.0.0
|