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