csc-cia-stne 0.0.30__py3-none-any.whl → 0.0.31__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.
@@ -2,10 +2,35 @@ import requests
2
2
  import base64, json
3
3
  import os
4
4
  import logging
5
+ from pydantic import ValidationError
6
+ from .utilitarios.validations.ServiceNowValidator import InitParamsValidator, RequestValidator, PutValidator, PostValidator, ListTicketValidator, UpdateTicketValidator, AttachFileTicketValidator, GetAttachValidator
5
7
 
6
8
  class ServiceNow:
7
9
 
8
10
  def __init__(self, username: str = None, password: str = None, env: str = None) -> None:
11
+ """
12
+ Inicializa uma instância da classe ServiceNow.
13
+
14
+ Parâmetros:
15
+ username (str): Nome de usuário para autenticação. Obrigatório e não pode ser nulo ou vazio.
16
+ password (str): Senha para autenticação. Obrigatória e não pode ser nula ou vazia.
17
+ env (str): Ambiente no qual a instância será utilizada. Deve ser 'dev', 'qa', 'qas' ou 'prod'.
18
+
19
+ Raises:
20
+ ValueError: Caso qualquer um dos parâmetros não seja fornecido, seja nulo ou vazio, ou se 'env' não for um valor válido.
21
+
22
+ Atributos:
23
+ username (str): Nome de usuário normalizado.
24
+ password (str): Senha normalizada.
25
+ env (str): Nome do ambiente em letras maiúsculas.
26
+ api_url (str): URL base da API correspondente ao ambiente.
27
+ api_header (dict): Cabeçalhos padrão para requisições API.
28
+ """
29
+ try:
30
+ InitParamsValidator(username=username, password=password, env=env, )
31
+ except ValidationError as e:
32
+ raise ValueError("Erro na validação dos dados de input da inicialização da instância 'ServiceNow':", e.errors())
33
+
9
34
  # Verifica se todos os parâmetros foram fornecidos e não estão vazios
10
35
  if not all([username and username.strip(), password and password.strip(), env and env.strip()]):
11
36
  raise ValueError("Todos os parâmetros precisam ser fornecidos para a criação da instância ServiceNow, ou algum dos valores esta nulo ou vazio (username, password, env)")
@@ -40,14 +65,47 @@ class ServiceNow:
40
65
  """
41
66
  return (self.username, self.password)
42
67
 
43
- def request (self, url, params = ""):
68
+ def request (self, url : str , params : str = "", timout : int = 15):
69
+ """
70
+ Realiza uma requisição GET para a URL especificada.
71
+
72
+ Parâmetros:
73
+ url (str): URL para a qual a requisição será enviada.
74
+ params (dict, opcional): Parâmetros de consulta (query parameters) a serem incluídos na requisição.
75
+ Padrão é uma string vazia.
76
+
77
+ Retorno:
78
+ dict: Um dicionário contendo:
79
+ - success (bool): Indica se a requisição foi bem-sucedida.
80
+ - result (dict ou None): Resultado da resposta, se disponível. Contém o conteúdo JSON do campo "result".
81
+ - error (Exception ou None): Objeto de exceção em caso de erro, ou None se não houver erros.
82
+
83
+ Tratamento de exceções:
84
+ - requests.exceptions.HTTPError: Lança um erro HTTP caso o código de status indique falha.
85
+ - requests.exceptions.RequestException: Captura outros erros relacionados à requisição, como timeout ou conexão.
86
+ - Exception: Captura erros inesperados.
87
+
88
+ Logs:
89
+ - Logs de depuração são gerados para erros HTTP, erros de requisição e exceções inesperadas.
90
+ - Um aviso é registrado caso a resposta não contenha o campo "result".
91
+
92
+ Observações:
93
+ - Utiliza autenticação fornecida pelo método privado `__auth()`.
94
+ - Define um tempo limite (`timeout`) de 15 segundos.
95
+ - Verifica o certificado SSL (`verify=True`).
96
+ """
97
+ try:
98
+ RequestValidator(url=url, params=params, timeout=timout)
99
+ except ValidationError as e:
100
+ raise ValueError("Erro na validação dos dados de input do método:", e.errors())
101
+
44
102
  try:
45
103
  response = requests.get(
46
104
  url,
47
105
  params=params,
48
106
  auth=self.__auth(),
49
107
  headers=self.api_header,
50
- timeout=15,
108
+ timeout=timout,
51
109
  verify=True
52
110
  )
53
111
 
@@ -57,22 +115,55 @@ class ServiceNow:
57
115
  # Analisa o conteúdo da resposta JSON
58
116
  result = response.json()
59
117
  if "result" in result:
60
- return result["result"]
118
+ return {"success" : True, "result" : result}
61
119
  else:
62
- logging.warning("A resposta não contém o campo 'result'.")
63
- return {}
120
+ logging.debug("A resposta não contém o campo 'result'.")
121
+ return {"success" : False, "result" : result}
64
122
 
65
123
  except requests.exceptions.HTTPError as http_err:
66
- logging.error(f"Erro HTTP ao buscar os detalhes do ticket: {http_err}")
67
- raise
124
+ logging.debug(f"Erro HTTP ao buscar os detalhes do ticket: {http_err}")
125
+ return {"success" : False, "result" : None, "error" : str(http_err)}
126
+
68
127
  except requests.exceptions.RequestException as req_err:
69
- logging.error(f"Erro ao buscar os detalhes do ticket: {req_err}")
70
- raise
128
+ logging.debug(f"Erro ao buscar os detalhes do ticket: {req_err}")
129
+ return {"success" : False, "result" : None, "error" : str(req_err)}
130
+
71
131
  except Exception as e:
72
- logging.error(f"Erro inesperado: {e}")
73
- raise
132
+ logging.debug(f"Erro inesperado: {e}")
133
+ return {"success" : False, "result" : None, "error" : str(e)}
74
134
 
75
- def put(self, url, payload):
135
+ def put(self, url, payload, timeout = 15):
136
+ """
137
+ Realiza uma requisição PUT para a URL especificada.
138
+
139
+ Parâmetros:
140
+ url (str): URL para a qual a requisição será enviada.
141
+ payload (dict): Dados a serem enviados no corpo da requisição. Será convertido para JSON.
142
+
143
+ Retorno:
144
+ dict: Um dicionário contendo:
145
+ - success (bool): Indica se a requisição foi bem-sucedida.
146
+ - result (dict ou None): Resultado da resposta, se disponível. Contém o conteúdo JSON do campo "result".
147
+
148
+ Tratamento de exceções:
149
+ - requests.exceptions.HTTPError: Lança um erro HTTP caso o código de status indique falha. O erro é registrado nos logs.
150
+ - requests.exceptions.RequestException: Captura outros erros relacionados à requisição, como timeout ou problemas de conexão. O erro é registrado nos logs.
151
+ - Exception: Captura erros inesperados e os registra nos logs.
152
+
153
+ Logs:
154
+ - Registra mensagens de depuração detalhadas quando a atualização é bem-sucedida ou quando a resposta não contém o campo "result".
155
+ - Registra mensagens de erro para exceções HTTP, erros de requisição e outros erros inesperados.
156
+
157
+ Observações:
158
+ - Utiliza autenticação fornecida pelo método privado `__auth()`.
159
+ - Define um tempo limite (`timeout`) de 15 segundos.
160
+ - Verifica o certificado SSL (`verify=True`).
161
+ - O cabeçalho da requisição é definido com `self.api_header`.
162
+ """
163
+ try:
164
+ PutValidator(url=url, payload=payload, timeout=timeout)
165
+ except ValidationError as e:
166
+ raise ValueError("Erro na validação dos dados de input do método:", e.errors())
76
167
 
77
168
  payload = json.dumps(payload)
78
169
 
@@ -82,7 +173,7 @@ class ServiceNow:
82
173
  auth=self.__auth(),
83
174
  headers=self.api_header,
84
175
  data=f"{payload}",
85
- timeout=15,
176
+ timeout=timeout,
86
177
  verify=True
87
178
  )
88
179
 
@@ -93,24 +184,26 @@ class ServiceNow:
93
184
  result = response.json()
94
185
  if "result" in result:
95
186
  update = result["result"]
96
- logging.info(f"Atualização concluída com sucesso. Registro atualizado: {update["sys_id"]} | Alterações: {payload}")
187
+ logging.debug(f"Atualização concluída com sucesso. Registro atualizado: {update["sys_id"]} | Alterações: {payload}")
188
+ return {"success" : True, "result" : update}
97
189
  else:
98
- logging.warning(f"A Resposta da sua requisição não contém o campo 'Result'. Segue o retorno: \n {result} | Alterações: {payload}")
190
+ logging.debug(f"A Resposta da sua requisição não contém o campo 'Result'. Segue o retorno: \n {result} | Alterações: {payload}")
191
+ return {"success" : False, "result" : result}
99
192
 
100
193
  #TRATAMENTOS DE ERRO
101
194
  except requests.exceptions.HTTPError as http_err:
102
-
103
- logging.error(f"Erro HTTP ao tentar atualizar o ticket: {http_err} \n Reposta da solicitação: {response.json().get("error").get("message")}")
104
- raise
195
+ logging.debug(f"Erro HTTP ao tentar atualizar o ticket: {http_err} \n Reposta da solicitação: {response.json().get("error").get("message")}")
196
+ return {"success" : False, "error" : str(http_err) , "result" : None}
197
+
105
198
  except requests.exceptions.RequestException as req_err:
106
-
107
- logging.error(f"Erro ao tentar atualizar o ticket: \n {req_err}")
108
- raise
199
+ logging.debug(f"Erro ao tentar atualizar o ticket: \n {req_err}")
200
+ return {"success" : False, "error" : str(req_err) , "result" : None}
201
+
109
202
  except Exception as e:
110
- logging.error(f"Erro inesperado: \n {e}")
111
- raise
203
+ logging.debug(f"Erro inesperado: \n {e}")
204
+ return {"success" : False, "error" : str(e) , "result" : None}
112
205
 
113
- def post(self, url, variables, header_content_type = ""):
206
+ def post(self, url : str, variables : dict, header_content_type = "", timeout : int = 15):
114
207
  """
115
208
  Função para criar um novo ticket no servicenow usando o API REST.
116
209
 
@@ -122,6 +215,12 @@ class ServiceNow:
122
215
  - Exception: Se ocorrer um erro ao criar o ticket.
123
216
 
124
217
  """
218
+ try:
219
+ PostValidator(url=url, variables=variables)
220
+ except ValidationError as e:
221
+ raise ValueError("Erro na validação dos dados de input do método:", e.errors())
222
+
223
+
125
224
  if header_content_type:
126
225
  header = header_content_type
127
226
  else:
@@ -137,7 +236,8 @@ class ServiceNow:
137
236
  f"{url}",
138
237
  auth=self.__auth(),
139
238
  headers=header,
140
- data=f"{payload}"
239
+ data=f"{payload}",
240
+ timeout=timeout
141
241
  )
142
242
 
143
243
  # VALIDA SE HOUVE SUCESSO NA REQUISIÇÃO
@@ -148,23 +248,25 @@ class ServiceNow:
148
248
  if "result" in result:
149
249
  ticket_number = result["result"].get("number")
150
250
  ticket_sys_id = result["result"].get("sys_id")
151
- logging.info(f"Ticket registrado com sucesso. Número: {ticket_number} | SYS_ID: {ticket_sys_id}")
152
- return result["result"]
251
+ logging.debug(f"Ticket registrado com sucesso. Número: {ticket_number} | SYS_ID: {ticket_sys_id}")
252
+ return {"success" : True, "result" : result["result"]}
153
253
  else:
154
- logging.warning(f"A Resposta da sua requisição não contém o campo 'Result'. Segue o retorno: \n {result}")
254
+ logging.debug(f"A Resposta da sua requisição não contém o campo 'Result'. Segue o retorno: \n {result}")
255
+ return {"success" : False, "result" : result}
155
256
 
156
257
  #TRATAMENTOS DE ERRO
157
258
  except requests.exceptions.HTTPError as http_err:
158
259
 
159
- logging.error(f"Erro HTTP ao tentar registrar o ticket: {http_err} \n Reposta da solicitação: {response.json().get("error").get("message")}")
160
- raise
260
+ logging.debug(f"Erro HTTP ao tentar registrar o ticket: {http_err} \n Reposta da solicitação: {response.json().get("error").get("message")}")
261
+ return {"success" : False, "error" : str(http_err) , "result" : None}
262
+
161
263
  except requests.exceptions.RequestException as req_err:
162
-
163
- logging.error(f"Erro ao tentar registrar o ticket: \n {req_err}")
164
- raise
264
+ logging.debug(f"Erro ao tentar registrar o ticket: \n {req_err}")
265
+ return {"success" : False, "error" : str(req_err) , "result" : None}
266
+
165
267
  except Exception as e:
166
- logging.error(f"Erro inesperado: \n {e}")
167
- raise
268
+ logging.debug(f"Erro inesperado: \n {e}")
269
+ return {"success" : False, "error" : str(e) , "result" : None}
168
270
 
169
271
  def listar_tickets(self, tabela: str = None, campos: list = None, query: str = None, limite: int = 50, timeout:int=15)->dict:
170
272
  """lista tickets do ServiceNow
@@ -179,43 +281,53 @@ class ServiceNow:
179
281
  Returns:
180
282
  dict: dicionário com o resultado da query
181
283
  """
182
- # Validação de 'tabela'
183
- if not tabela or not tabela.strip():
184
- raise ValueError("O parâmetro 'tabela' precisa ser especificado com o nome da tabela de onde os tickets serão listados.")
185
-
186
- # Validação de 'campos'
187
- if not isinstance(campos, list) or not campos or not all(isinstance(campo, str) and campo.strip() for campo in campos):
188
- raise ValueError("O parâmetro 'campos' precisa ser uma lista não vazia de strings válidas com os nomes dos campos do formulário.")
189
-
190
- # Validação de 'query'
191
- if not query or not query.strip():
192
- raise ValueError("O parâmetro 'query' precisa ser uma string não vazia.")
193
-
194
- # Validação de 'timeout'
195
- if not isinstance(timeout, int):
196
- raise ValueError("O parâmetro 'timeout' precisa ser um valor (int) em segundos.")
197
-
198
- # Validação de 'limite'
199
- if not isinstance(limite, int):
200
- raise ValueError("O parâmetro 'limite' precisa ser um número inteiro.")
201
-
202
- params = {}
203
- params["sysparm_query"] = query
204
- params["sysparm_fields"] = ','.join(campos)
205
- params["sysparm_display_value"] = "all"
206
- params["sysparm_limit"] = limite
284
+ try:
285
+ ListTicketValidator(tabela=tabela, campos=campos, query=query, limite=limite, timeout=timeout)
286
+ except ValidationError as e:
287
+ raise ValueError("Erro na validação dos dados de input do método:", e.errors())
288
+
289
+ # # Validação de 'tabela'
290
+ # if not tabela or not tabela.strip():
291
+ # raise ValueError("O parâmetro 'tabela' precisa ser especificado com o nome da tabela de onde os tickets serão listados.")
292
+
293
+ # # Validação de 'campos'
294
+ # if not isinstance(campos, list) or not campos or not all(isinstance(campo, str) and campo.strip() for campo in campos):
295
+ # raise ValueError("O parâmetro 'campos' precisa ser uma lista não vazia de strings válidas com os nomes dos campos do formulário.")
296
+
297
+ # # Validação de 'query'
298
+ # if not query or not query.strip():
299
+ # raise ValueError("O parâmetro 'query' precisa ser uma string não vazia.")
300
+
301
+ # # Validação de 'timeout'
302
+ # if not isinstance(timeout, int):
303
+ # raise ValueError("O parâmetro 'timeout' precisa ser um valor (int) em segundos.")
304
+
305
+ # # Validação de 'limite'
306
+ # if not isinstance(limite, int):
307
+ # raise ValueError("O parâmetro 'limite' precisa ser um número inteiro.")
308
+
309
+ params = {
310
+ "sysparm_query" : query,
311
+ "sysparm_fields" : ','.join(campos),
312
+ "sysparm_display_value" : "all",
313
+ "sysparm_limit" : limite
314
+ }
315
+ # params["sysparm_query"] = query
316
+ # params["sysparm_fields"] = ','.join(campos)
317
+ # params["sysparm_display_value"] = "all"
318
+ # params["sysparm_limit"] = limite
207
319
 
208
320
  url = f"{self.api_url}/now/table/{tabela}"
209
321
 
210
322
  try:
211
- response = self.request(url, params)
323
+ response = self.request(url=url, params=params, timout=timeout)
212
324
  # response = requests.get(url, params=params, auth=(self.username, self.password), headers=self.api_header, verify=True, timeout=timeout)
213
- return {"result":True, "content": response.json()}
325
+ return {"success":True, "result": response.json()}
214
326
 
215
327
  except Exception as e:
216
- return {}
328
+ return {"success" : False, "result" : None, "error" : str(e)}
217
329
 
218
- def update_ticket(self, tabela: str = None, sys_id: str = None, campos: dict = None, timeout:int=15)->dict:
330
+ def update_ticket(self, tabela: str = None, sys_id: str = None, payload: dict = None, timeout:int=15)->dict:
219
331
  """Atualiza as informações de um ticket
220
332
 
221
333
  Args:
@@ -227,34 +339,36 @@ class ServiceNow:
227
339
  Returns:
228
340
  dict: resposta do ServiceNow
229
341
  """
342
+ try:
343
+ UpdateTicketValidator(tabela=tabela, sys_id=sys_id, payload=payload, timeout=timeout)
344
+ except ValidationError as e:
345
+ raise ValueError("Erro na validação dos dados de input do método:", e.errors())
346
+
347
+ # # Validação de 'tabela'
348
+ # if not tabela or not tabela.strip():
349
+ # raise ValueError("O parâmetro 'tabela' precisa ser especificado com o nome da tabela de onde o ticket será atualizado.")
230
350
 
231
- # Validação de 'tabela'
232
- if not tabela or not tabela.strip():
233
- raise ValueError("O parâmetro 'tabela' precisa ser especificado com o nome da tabela de onde o ticket será atualizado.")
234
-
235
- # Validação de 'sys_id'
236
- if not sys_id or not sys_id.strip():
237
- raise ValueError("O parâmetro 'sys_id' precisa ser especificado com o sys_id do ticket.")
351
+ # # Validação de 'sys_id'
352
+ # if not sys_id or not sys_id.strip():
353
+ # raise ValueError("O parâmetro 'sys_id' precisa ser especificado com o sys_id do ticket.")
238
354
 
239
- # Validação de 'campos'
240
- if not isinstance(campos, dict):
241
- raise ValueError("O parâmetro 'campos' precisa ser um dicionário contendo os campos do ticket a serem atualizados.")
355
+ # # Validação de 'payload'
356
+ # if not isinstance(payload, dict):
357
+ # raise ValueError("O parâmetro 'campos' precisa ser um dicionário contendo os campos do ticket a serem atualizados.")
242
358
 
243
- if "assigned_to" not in campos:
359
+ if "assigned_to" not in payload:
244
360
 
245
- campos["assigned_to"] = self.username
361
+ payload["assigned_to"] = self.username
246
362
 
247
363
  try:
248
364
 
249
365
  url = f"{self.api_url}/now/table/{tabela}/{sys_id}"
250
- response = self.put(url, payload=campos)
366
+ response = self.put(url, payload=payload, timeout=timeout)
251
367
  # response = requests.patch(url, auth=(self.username, self.password), headers=self.api_header, data=str(campos).encode('utf-8'), verify=True, timeout=timeout)
252
-
253
- return response.json()
368
+ return {"success" : True, "result" : response.json() , "error" : None}
254
369
 
255
370
  except Exception as e:
256
-
257
- raise e
371
+ return {"success" : False, "result" : None, "error" : str(e) }
258
372
 
259
373
  def anexar_arquivo_no_ticket(self,header_content_type:dict=None, anexo_path:str=None, tabela:str=None, sys_id:str=None, timeout:int=15):
260
374
  """Anexa arquivo em um ticket do ServiceNow
@@ -269,18 +383,22 @@ class ServiceNow:
269
383
  Returns:
270
384
  dict: resposta do ServiceNow
271
385
  """
386
+ try:
387
+ AttachFileTicketValidator(header_content_type=header_content_type, anexo_path=anexo_path, tabela=tabela, sys_id=sys_id, timeout=timeout)
388
+ except ValidationError as e:
389
+ raise ValueError("Erro na validação dos dados de input do método:", e.errors())
390
+
391
+ # # Validação de 'tabela'
392
+ # if not tabela or not tabela.strip():
393
+ # raise ValueError("O parâmetro 'tabela' precisa ser especificado com o nome da tabela do ticket pra onde o arquivo será anexado.")
272
394
 
273
- # Validação de 'tabela'
274
- if not tabela or not tabela.strip():
275
- raise ValueError("O parâmetro 'tabela' precisa ser especificado com o nome da tabela do ticket pra onde o arquivo será anexado.")
276
-
277
- # Validação de 'sys_id'
278
- if not sys_id or not sys_id.strip():
279
- raise ValueError("O parâmetro 'sys_id' precisa ser especificado com o sys_id do ticket que o arquivo será anexado.")
395
+ # # Validação de 'sys_id'
396
+ # if not sys_id or not sys_id.strip():
397
+ # raise ValueError("O parâmetro 'sys_id' precisa ser especificado com o sys_id do ticket que o arquivo será anexado.")
280
398
 
281
- # Validação de 'anexo_path'
282
- if not anexo_path or not anexo_path.strip():
283
- raise ValueError("O parâmetro 'anexo_path' precisa ser especificado com o path para o arquivo a ser anexado.")
399
+ # # Validação de 'anexo_path'
400
+ # if not anexo_path or not anexo_path.strip():
401
+ # raise ValueError("O parâmetro 'anexo_path' precisa ser especificado com o path para o arquivo a ser anexado.")
284
402
 
285
403
  if not os.path.exists(anexo_path):
286
404
  raise FileExistsError(f"O arquivo não foi encontrado ({anexo_path})")
@@ -304,15 +422,36 @@ class ServiceNow:
304
422
 
305
423
  try:
306
424
  url = f"{self.api_url}/now/attachment/file?table_name={tabela}&table_sys_id={sys_id}&file_name={nome_arquivo}"
307
- response = self.post(url, file_data, header_content_type )
425
+ # response = self.post(url, file_data, header_content_type )
426
+ response = self.post(url=url, file_data=file_data, header_content_type=header_content_type_lower, timeout=timeout)
308
427
  # response = requests.post(f"{self.api_url}/now/attachment/file?table_name={tabela}&table_sys_id={sys_id}&file_name={nome_arquivo}", headers=header_content_type, auth=(self.username, self.password), data=file_data, timeout=timeout)
309
- return response.json()
428
+ return {"success" : True, "result" : response.json(), "error" : None}
310
429
 
311
430
  except Exception as e:
312
-
313
- raise e
431
+ return {"success" : False, "result" : None, "error" : str(e)}
314
432
 
315
433
  def valida_header_content_type(self, anexo_path ):
434
+ """
435
+ Valida e define o cabeçalho `Content-Type` baseado na extensão do arquivo anexado.
436
+
437
+ Parâmetros:
438
+ anexo_path (str): Caminho do arquivo que será validado e utilizado para determinar o `Content-Type`.
439
+
440
+ Funcionalidade:
441
+ - Baseado na extensão do arquivo especificado, define um valor apropriado para `header_content_type` caso esteja ausente:
442
+ - `.zip` → `application/zip`
443
+ - `.xlsx` → `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
444
+ - `.pdf` → `application/pdf`
445
+ - `.txt` → `text/plain`
446
+ - Gera um erro se `header_content_type` não for do tipo `dict` após a definição.
447
+
448
+ Erros:
449
+ - ValueError: Caso `header_content_type` não seja um dicionário válido após as validações.
450
+
451
+ Observação:
452
+ - O parâmetro `header_content_type` é uma variável local utilizada para compor os cabeçalhos HTTP, contendo informações do tipo de conteúdo do arquivo.
453
+
454
+ """
316
455
 
317
456
  # Pré validando 'header_content_type'
318
457
  if os.path.splitext(anexo_path)[1].lower() == ".zip" and header_content_type is None:
@@ -331,13 +470,13 @@ class ServiceNow:
331
470
 
332
471
  header_content_type = {"Content-Type": "text/plain"}
333
472
 
334
- # Validação de 'header_content_type'
335
- if not isinstance(header_content_type, dict):
336
- raise ValueError("O parâmetro 'header_content_type' precisa ser um dicionário contendo o 'Content-Type' do arquivo a ser anexado. (Ex: {\"Content-Type\": \"application/zip\"})")
473
+ # # Validação de 'header_content_type'
474
+ # if not isinstance(header_content_type, dict):
475
+ # raise ValueError("O parâmetro 'header_content_type' precisa ser um dicionário contendo o 'Content-Type' do arquivo a ser anexado. (Ex: {\"Content-Type\": \"application/zip\"})")
337
476
 
338
- # Validação de 'header_content_type'
339
- if not isinstance(header_content_type, dict):
340
- raise ValueError("O parâmetro 'header_content_type' precisa ser um dicionário contendo o 'Content-Type' do arquivo a ser anexado. (Ex: {\"Content-Type\": \"application/zip\"})")
477
+ # # Validação de 'header_content_type'
478
+ # if not isinstance(header_content_type, dict):
479
+ # raise ValueError("O parâmetro 'header_content_type' precisa ser um dicionário contendo o 'Content-Type' do arquivo a ser anexado. (Ex: {\"Content-Type\": \"application/zip\"})")
341
480
 
342
481
  return header_content_type
343
482
 
@@ -353,27 +492,32 @@ class ServiceNow:
353
492
  Returns:
354
493
  dict: dicionário com os anexos do ticket
355
494
  """
356
-
357
- # Validação de 'sys_id'
358
- if not isinstance(sys_id, str) or not sys_id.strip():
359
- raise ValueError("O parâmetro 'sys_id' precisa ser uma string não vazia com o sys_id do ticket.")
495
+ try:
496
+ GetAttachValidator(sys_id=sys_id,
497
+ tabela=tabela,
498
+ timeout=timeout,
499
+ download_dir=download_dir,
500
+ timeout=timeout )
501
+ except ValidationError as e:
502
+ raise ValueError("Erro na validação dos dados de input do método:", e.errors())
503
+
504
+ # # Validação de 'sys_id'
505
+ # if not isinstance(sys_id, str) or not sys_id.strip():
506
+ # raise ValueError("O parâmetro 'sys_id' precisa ser uma string não vazia com o sys_id do ticket.")
360
507
 
361
- # Validação de 'tabela'
362
- if not isinstance(tabela, str) or not tabela.strip():
363
- raise ValueError("O parâmetro 'tabela' precisa ser uma string não vazia com o nome da tabela do ticket.")
508
+ # # Validação de 'tabela'
509
+ # if not isinstance(tabela, str) or not tabela.strip():
510
+ # raise ValueError("O parâmetro 'tabela' precisa ser uma string não vazia com o nome da tabela do ticket.")
364
511
 
365
- # Validação de 'timeout'
366
- if not isinstance(timeout, int):
367
- raise ValueError("O parâmetro 'timeout' precisa ser um valor (int) em segundos.")
512
+ # # Validação de 'timeout'
513
+ # if not isinstance(timeout, int):
514
+ # raise ValueError("O parâmetro 'timeout' precisa ser um valor (int) em segundos.")
368
515
 
369
516
  # Validação de 'download_dir'
370
517
  if download_dir is not None:
371
518
  if not isinstance(download_dir, str) or not download_dir.strip():
372
-
373
519
  raise ValueError("O parâmetro 'download_dir' precisa ser a pasta pra onde o anexo será feito o download.")
374
-
375
- if not os.path.exists(download_dir):
376
-
520
+ if not os.path.exists(download_dir):
377
521
  raise NotADirectoryError(f"A pasta informada '{download_dir}' não existe")
378
522
 
379
523
  # Validação de 'campo'
@@ -384,12 +528,13 @@ class ServiceNow:
384
528
 
385
529
  # Convert bytes to base64
386
530
  def __bytes_to_base64(image_bytes):
387
- base64_encoded = base64.b64encode(image_bytes)
531
+ # base64_encoded = base64.b64encode(image_bytes)
388
532
 
389
- # Decode the bytes to a string (UTF-8 encoding)
390
- base64_string = base64_encoded.decode('utf-8')
533
+ # # Decode the bytes to a string (UTF-8 encoding)
534
+ # base64_string = base64_encoded.decode('utf-8')
391
535
 
392
- return base64_string
536
+ # # return base64_string
537
+ return base64.b64encode(image_bytes).decode('utf-8')
393
538
 
394
539
  def __formatar_tamanho(tamanho_bytes):
395
540
  # Converte o valor de string para inteiro
@@ -408,122 +553,164 @@ class ServiceNow:
408
553
  return f"{tamanho_bytes:.2f} PB" # Petabyte
409
554
 
410
555
  anexo_dict = {"var_servicenow": campo, "anexos":[]}
556
+
557
+ try:
558
+ if campo == 'default':
559
+ url = f"{self.api_url}/now/attachment?sysparm_query=table_name={tabela}^table_sys_id={sys_id}"
560
+ response = self.request(url, timeout=timeout)
561
+
562
+ if response.status_code == 200:
563
+ for attachment in response.json().get("result", []):
564
+ arquivo = {
565
+ "file_name": attachment["file_name"],
566
+ "size": __formatar_tamanho(attachment["size_bytes"]),
567
+ "content_type": attachment["content_type"],
568
+ "base64": None
569
+ }
570
+ byte_response = self.request(attachment["download_link"], timeout=timeout)
571
+ if byte_response.status_code == 200:
572
+ arquivo["base64"] = __bytes_to_base64(byte_response.content)
573
+ if download_dir:
574
+ with open(os.path.join(download_dir, arquivo["file_name"]), 'wb') as f:
575
+ f.write(byte_response.content)
576
+ anexo_dict["anexos"].append(arquivo)
577
+ return {"success": True, "result": anexo_dict}
578
+ return {"success": False, "error": response.json(), "status_code": response.status_code}
411
579
 
412
- # Fazendo download do anexo do campo padrao do ticket
413
- if campo == 'default':
414
-
415
- print(f"verificando o campo '{campo}' de anexo")
580
+ else:
581
+ url = f"{self.api_url}/now/table/{tabela}?sysparm_query=sys_id={sys_id}&sysparm_fields={campo}&sysparm_display_value=all"
582
+ response = self.request(url, timeout=timeout)
583
+ if response.status_code == 200 and response.json().get("result"):
584
+ campo_data = response.json()["result"][0].get(campo)
585
+ if campo_data:
586
+ attachment_id = campo_data["value"]
587
+ attachment_url = f"{self.api_url}/now/attachment/{attachment_id}/file"
588
+ byte_response = self.request(attachment_url, timeout=timeout)
589
+ if byte_response.status_code == 200:
590
+ arquivo = {
591
+ "file_name": campo_data["display_value"],
592
+ "size": __formatar_tamanho(byte_response.headers.get("Content-Length", 0)),
593
+ "content_type": byte_response.headers.get("Content-Type"),
594
+ "base64": __bytes_to_base64(byte_response.content)
595
+ }
596
+ if download_dir:
597
+ with open(os.path.join(download_dir, arquivo["file_name"]), 'wb') as f:
598
+ f.write(byte_response.content)
599
+ anexo_dict["anexos"].append(arquivo)
600
+ return {"success": True, "result": anexo_dict, "error" : None}
601
+ return {"success": False, "error": response.json(), "status_code": response.status_code}
416
602
 
417
- try:
418
- #url = f"{SERVICENOW_API_URL}/now/attachment?sysparm_query=table_name=sc_req_item^table_sys_id={sys_id}"
419
- url = f"{self.api_url}/now/attachment?sysparm_query=table_name={tabela}^table_sys_id={sys_id}"
420
- response = self.request(url)
421
- # response = requests.get(url, auth=(self.username, self.password), verify=True, timeout=timeout)
422
- if response.status_code == 200 and len(response.json()["result"]) >= 1:
423
-
424
- for attachment in response.json()["result"]:
425
-
426
- print("attachment")
427
- print(attachment)
428
- arquivo = {}
429
- arquivo["file_name"] = attachment["file_name"]
430
- arquivo["size"] = __formatar_tamanho(attachment["size_bytes"]["display_value"])
431
- arquivo["content_type"] = attachment["content_type"]
432
- #arquivo["table_sys_id"] = attachment["table_sys_id"]
433
-
434
- try:
435
- byte_response = self.request(attachment["download_link"])
436
- # byte_response = requests.get(attachment["download_link"], auth=(self.username, self.password), verify=True, timeout=timeout)
603
+ except Exception as e:
604
+ logging.debug("Erro ao obter anexos.")
605
+ return {"success": False, "error": str(e)}
606
+
607
+ # # Fazendo download do anexo do campo padrao do ticket
608
+ # if campo == 'default':
609
+
610
+ # logging.debug(f"verificando o campo '{campo}' de anexo")
611
+
612
+ # try:
613
+ # #url = f"{SERVICENOW_API_URL}/now/attachment?sysparm_query=table_name=sc_req_item^table_sys_id={sys_id}"
614
+ # url = f"{self.api_url}/now/attachment?sysparm_query=table_name={tabela}^table_sys_id={sys_id}"
615
+ # response = self.request(url)
616
+ # # response = requests.get(url, auth=(self.username, self.password), verify=True, timeout=timeout)
617
+ # if response.status_code == 200 and len(response.json()["result"]) >= 1:
618
+
619
+ # for attachment in response.json()["result"]:
620
+
621
+ # # print("attachment")
622
+ # # print(attachment)
623
+ # arquivo = {}
624
+ # arquivo["file_name"] = attachment["file_name"]
625
+ # arquivo["size"] = __formatar_tamanho(attachment["size_bytes"]["display_value"])
626
+ # arquivo["content_type"] = attachment["content_type"]
627
+ # #arquivo["table_sys_id"] = attachment["table_sys_id"]
628
+
629
+ # try:
630
+ # byte_response = self.request(attachment["download_link"])
631
+ # # byte_response = requests.get(attachment["download_link"], auth=(self.username, self.password), verify=True, timeout=timeout)
437
632
 
438
- if byte_response.status_code == 200:
633
+ # if byte_response.status_code == 200:
439
634
 
440
- arquivo["base64"] = __bytes_to_base64(byte_response.content)
635
+ # arquivo["base64"] = __bytes_to_base64(byte_response.content)
441
636
 
442
- if download_dir is not None:
443
- with open(arquivo["file_name"],'wb') as download_file:
444
- download_file.write(byte_response.content)
445
-
446
- #del arquivo["sys_id"]
447
- #del arquivo["download_link"]
448
- #del arquivo["table_sys_id"]
449
- anexo_dict["anexos"].append(arquivo)
450
- #return JSONResponse(content=arquivo, status_code=byte_response.status_code, media_type="application/json")
451
- # Requisicao não OK (!= 200)
637
+ # if download_dir is not None:
638
+ # with open(arquivo["file_name"],'wb') as download_file:
639
+ # download_file.write(byte_response.content)
640
+
641
+ # #del arquivo["sys_id"]
642
+ # #del arquivo["download_link"]
643
+ # #del arquivo["table_sys_id"]
644
+ # anexo_dict["anexos"].append(arquivo)
645
+ # #return JSONResponse(content=arquivo, status_code=byte_response.status_code, media_type="application/json")
646
+ # # Requisicao não OK (!= 200)
647
+ # return {"success" : True, "result" : anexo_dict, "error": None, "status_code" : byte_response.status_code }
452
648
 
453
- except Exception as e:
649
+ # except Exception as e:
650
+ # return {"success" : False, "result" : None, "error" : e, "status_code" : byte_response.status_code}
454
651
 
455
- raise e
456
-
457
- elif response.status_code != 200:
458
-
459
- return response.json()
652
+ # elif response.status_code != 200:
653
+ # return {"success" : False , "result" : response.json(), "status_code" : response.status_code, "error" : None}
460
654
 
461
- except Exception as e:
655
+ # except Exception as e:
656
+ # return {"success" : False, "result" : None, "error" : e}
462
657
 
463
- raise e
658
+ # # Fazendo download do anexo do campo especificado do ticket
659
+ # else:
464
660
 
465
- # Fazendo download do anexo do campo especificado do ticket
466
- else:
467
-
468
- print(f"verificando o campo '{campo}' de anexo")
661
+ # logging.debug(f"verificando o campo '{campo}' de anexo")
469
662
 
470
- url = f"{self.api_url}/now/table/{tabela}?sysparm_query=sys_id={sys_id}&sysparm_fields={campo}&sysparm_display_value=all&sysparam_limit=1"
471
- try:
472
- response = requests.get(url, auth=(self.username, self.password), verify=True)
663
+ # url = f"{self.api_url}/now/table/{tabela}?sysparm_query=sys_id={sys_id}&sysparm_fields={campo}&sysparm_display_value=all&sysparam_limit=1"
664
+ # try:
665
+ # response = self.request(url)
666
+ # # response = requests.get(url, auth=(self.username, self.password), verify=True)
473
667
 
474
- # Requisicao OK e com anexo
475
- if response.status_code == 200 and len(response.json()["result"]) >= 1:
668
+ # # Requisicao OK e com anexo
669
+ # if response.status_code == 200 and len(response.json()["result"]) >= 1:
476
670
 
477
- for ticket_data in response.json()["result"]:
671
+ # for ticket_data in response.json()["result"]:
478
672
 
479
- if len(ticket_data) >= 1:
673
+ # if len(ticket_data) >= 1:
480
674
 
481
- try:
482
-
483
- #temp_file_name = ticket_data[campo]["display_value"]
484
- temp_sys_id = ticket_data[campo]["value"]
485
-
486
- except Exception as e:
487
-
488
- raise e
675
+ # try:
489
676
 
490
- url = f"{self.api_url}/now/table/sys_attachment?sysparm_query=sys_id={temp_sys_id}&sysparm_display_value=all&sysparam_limit=1"
677
+ # #temp_file_name = ticket_data[campo]["display_value"]
678
+ # temp_sys_id = ticket_data[campo]["value"]
679
+ # url = f"{self.api_url}/now/table/sys_attachment?sysparm_query=sys_id={temp_sys_id}&sysparm_display_value=all&sysparam_limit=1"
680
+ # response = self.request(url=url)
491
681
 
492
- response = requests.get(url, auth=(self.username, self.password), verify=True)
682
+ # except Exception as e:
683
+ # return {"success" : False, "error" : e, "result" : None}
493
684
 
494
- if response.status_code == 200:
495
-
496
- attachment = response.json()["result"][0]
497
- arquivo = {}
498
- arquivo["file_name"] = attachment["file_name"]["display_value"]
499
- arquivo["size"] = __formatar_tamanho(attachment["size_bytes"]["display_value"])
500
- #arquivo["sys_id"] = attachment["sys_id"]
501
- arquivo["content_type"] = attachment["content_type"]["value"]
502
- #attach_sys_id = attachment["sys_id"]["display_value"]
503
- #attach_sys_id_table = attachment["table_sys_id"]["value"]
504
- #url = f"{self.api_url}/now/attachment/{sys_id}/file"
505
- try:
506
-
507
- url = f"{self.api_url}/now/attachment/{temp_sys_id}/file"
508
- byte_response = requests.get(url, auth=(self.username, self.password), verify=True)
509
- arquivo["base64"] = __bytes_to_base64(byte_response.content)
510
- anexo_dict["anexos"].append(arquivo)
511
- if download_dir is not None:
512
-
513
- with open(arquivo["file_name"],'wb') as download_file:
514
- download_file.write(byte_response.content)
515
-
516
- except Exception as e:
517
-
518
- raise e
519
-
520
- # Requisicao não OK (!= 200)
521
- elif response.status_code != 200:
522
-
523
- return response.json()
685
+ # if response.status_code == 200:
686
+
687
+ # attachment = response.json()["result"][0]
688
+ # arquivo = {}
689
+ # arquivo["file_name"] = attachment["file_name"]["display_value"]
690
+ # arquivo["size"] = __formatar_tamanho(attachment["size_bytes"]["display_value"])
691
+ # #arquivo["sys_id"] = attachment["sys_id"]
692
+ # arquivo["content_type"] = attachment["content_type"]["value"]
693
+ # #attach_sys_id = attachment["sys_id"]["display_value"]
694
+ # #attach_sys_id_table = attachment["table_sys_id"]["value"]
695
+ # #url = f"{self.api_url}/now/attachment/{sys_id}/file"
696
+ # try:
697
+
698
+ # url = f"{self.api_url}/now/attachment/{temp_sys_id}/file"
699
+ # byte_response = self.request(url)
700
+ # # byte_response = requests.get(url, auth=(self.username, self.password), verify=True)
701
+ # arquivo["base64"] = __bytes_to_base64(byte_response.content)
702
+ # anexo_dict["anexos"].append(arquivo)
703
+ # if download_dir is not None:
704
+
705
+ # with open(arquivo["file_name"],'wb') as download_file:
706
+ # download_file.write(byte_response.content)
707
+ # return {"success" : True, "status_code" : response.status_code, "error" : None, "result" : anexo_dict}
708
+ # except Exception as e:
709
+ # return {"success" : False, "status_code" : response.status_code, "error" : e, "result" : response.json()}
524
710
 
525
- except Exception as e:
711
+ # # Requisicao não OK (!= 200)
712
+ # elif response.status_code != 200:
713
+ # return {"success" : False, "status_code" : response.status_code, "error" : e, "result" : response.json()}
526
714
 
527
- raise e
528
-
529
- return anexo_dict
715
+ # except Exception as e:
716
+ # return {"success" : False, "error" : e, "result" : None}