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.
@@ -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 ResultE
2
- from fa_purity.json import (
3
- JsonObj,
4
- JsonUnfolder,
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
- decode_crm_id,
15
- decode_deparment_id,
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
- decode_optional_date,
18
- decode_product_id,
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.require(raw, "firstName", DecodeUtils.to_str),
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 decode_agent(raw: JsonObj) -> ResultE[AgentObj]:
49
- values = smash.smash_result_5(
50
- decode_user(raw),
51
- JsonUnfolder.require(raw, "isConfirmed", DecodeUtils.to_bool),
52
- JsonUnfolder.require(raw, "emailId", DecodeUtils.to_str),
53
- decode_profile_id(raw),
54
- decode_rol_id(raw),
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, "city", DecodeUtils.to_opt_str).map(
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, "country", DecodeUtils.to_opt_str).map(
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, "street", DecodeUtils.to_opt_str).map(
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
- JsonUnfolder.require(raw, "createdTime", DecodeUtils.to_date_time),
86
- JsonUnfolder.require(raw, "modifiedTime", DecodeUtils.to_date_time),
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
- JsonUnfolder.require(raw, "email", DecodeUtils.to_str),
93
- JsonUnfolder.optional(raw, "facebook", DecodeUtils.to_opt_str).map(
94
- lambda v: v.bind(lambda x: x),
95
- ),
96
- JsonUnfolder.require(raw, "phone", DecodeUtils.to_str),
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
- decode_user(raw),
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.smash_result_4(
116
- JsonUnfolder.require(raw, "ownerId", DecodeUtils.to_str)
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
- decode_crm_id(raw),
120
- JsonUnfolder.optional(raw, "description", DecodeUtils.to_opt_str).map(
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, "state", DecodeUtils.to_opt_str).map(
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, "title", DecodeUtils.to_opt_str).map(
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, "type", DecodeUtils.to_opt_str).map(
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, "zip", DecodeUtils.to_opt_str).map(
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
- decode_optional_date(raw, "modifiedTime"),
146
- decode_require_date(raw, "createdTime"),
147
- decode_optional_date(raw, "closedTime"),
148
- decode_optional_date(raw, "customerResponseTime"),
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.smash_result_5(
158
+ first = smash.smash_result_4(
154
159
  decode_ticket_id(raw),
155
- JsonUnfolder.require(raw, "ticketNumber", DecodeUtils.to_str),
156
- decode_maybe_str(raw, "subject"),
157
- decode_maybe_str(raw, "channel"),
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, "category"),
163
- JsonUnfolder.require(raw, "isEscalated", DecodeUtils.to_bool),
164
- JsonUnfolder.require(raw, "priority", DecodeUtils.to_str),
165
- decode_maybe_str(raw, "resolution"),
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, "clasification"),
170
- decode_maybe_str(raw, "description"),
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
- decode_account_id_ticket(raw),
180
- decode_deparment_id(raw),
181
- decode_team_id(raw),
182
- decode_product_id(raw),
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
- second = smash.smash_result_5(
209
+
210
+ second = smash.smash_result_4(
186
211
  decode_ticket_properties(raw),
187
212
  decode_contact_id(raw),
188
- JsonUnfolder.require(raw, "email", DecodeUtils.to_str),
189
- JsonUnfolder.require(raw, "phone", DecodeUtils.to_str),
190
- decode_maybe_str(raw, "onholdTime"),
213
+ decode_maybe_str(raw, "Email"),
214
+ decode_maybe_str(raw, "Phone"),
191
215
  )
192
- return smash.smash_result_2(first, second).map(lambda v: TicketObj(*v[0], *v[1]))
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: ProfileId
33
- role: RoleId
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: AccountId
68
+ account_id: Maybe[OptionalId]
66
69
  contact_owner: UserId
67
- crm_id: CrmId
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: AccountId
109
- deparment_id: DeparmentId
110
- team_id: TeamId
111
- product_id: ProductId
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: 1.0.0
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 >=1.0.0, <2.0.0
8
+ Requires-Dist: fluidattacks-etl-utils >=2.0.0, <3.0.0
9
9
  Requires-Dist: pure-requests >=3.0.0, <4.0.0