csc-cia-stne 0.0.37__py3-none-any.whl → 0.0.38__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.
csc_cia_stne/provio.py CHANGED
@@ -90,7 +90,7 @@ class Provio:
90
90
  skip += 500
91
91
  requisicao += 1
92
92
  if verbose:
93
- logging.info(f"Exportando relatório: Requisição #{str(requisicao).zfill(3)} - {len(todos_os_dados)} registros exportados")
93
+ logging.info(f"Exportando relatório: Requisição #{str(requisicao).zfill(3)} - {len(todos_os_dados)} registros no total")
94
94
 
95
95
  else:
96
96
 
@@ -3,7 +3,7 @@ import base64, json
3
3
  import os
4
4
  import logging
5
5
  from pydantic import ValidationError
6
- from .utilitarios.validations.ServiceNowValidator import InitParamsValidator, RequestValidator, PutValidator, PostValidator, ListTicketValidator, UpdateTicketValidator, AttachFileTicketValidator, GetAttachValidator
6
+ from .utilitarios.validations.ServiceNowValidator import *
7
7
 
8
8
  class ServiceNow:
9
9
 
@@ -27,14 +27,10 @@ class ServiceNow:
27
27
  api_header (dict): Cabeçalhos padrão para requisições API.
28
28
  """
29
29
  try:
30
- InitParamsValidator(username=username, password=password, env=env, )
30
+ InitParamsValidator(username=username, password=password, env=env )
31
31
  except ValidationError as e:
32
32
  raise ValueError("Erro na validação dos dados de input da inicialização da instância 'ServiceNow':", e.errors())
33
-
34
- # Verifica se todos os parâmetros foram fornecidos e não estão vazios
35
- if not all([username and username.strip(), password and password.strip(), env and env.strip()]):
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)")
37
-
33
+
38
34
  # Normaliza o valor de 'env' para maiúsculas
39
35
  env = env.strip().upper()
40
36
 
@@ -51,22 +47,21 @@ class ServiceNow:
51
47
  raise ValueError("O valor de 'env' precisa ser 'dev', 'qa', 'qas' ou 'prod'.")
52
48
 
53
49
  # Atribui as variáveis de instância
54
- self.username = username.strip()
55
- self.password = password.strip()
50
+ self.__username = username.strip()
51
+ self.__password = password.strip()
56
52
  self.env = env
57
53
  self.api_url = valid_envs[env]
58
54
  self.api_header = {"Content-Type":"application/json","Accept":"application/json"}
59
55
 
60
- # Criação de funções básicas para reutilizar nas funções especificas
61
56
 
62
57
  def __auth(self):
63
58
  """
64
59
  Retorna o e-mail e senha para realizar a autenticação.
65
60
  """
66
- return (self.username, self.password)
61
+ return (self.__username, self.__password)
67
62
 
68
63
 
69
- def request (self, url : str , params : str = "", timout : int = 15):
64
+ def request (self, url : str , params : str = "", timeout : int = 15):
70
65
  """
71
66
  Realiza uma requisição GET para a URL especificada.
72
67
 
@@ -96,7 +91,7 @@ class ServiceNow:
96
91
  - Verifica o certificado SSL (`verify=True`).
97
92
  """
98
93
  try:
99
- RequestValidator(url=url, params=params, timeout=timout)
94
+ RequestValidator(url=url, params=params, timeout=timeout)
100
95
  except ValidationError as e:
101
96
  raise ValueError("Erro na validação dos dados de input do método:", e.errors())
102
97
 
@@ -106,7 +101,7 @@ class ServiceNow:
106
101
  params=params,
107
102
  auth=self.__auth(),
108
103
  headers=self.api_header,
109
- timeout=timout,
104
+ timeout=timeout,
110
105
  verify=True
111
106
  )
112
107
 
@@ -116,7 +111,7 @@ class ServiceNow:
116
111
  # Analisa o conteúdo da resposta JSON
117
112
  result = response.json()
118
113
  if "result" in result:
119
- return {"success" : True, "result" : result}
114
+ return {"success" : True, "result" : result.get("result")}
120
115
  else:
121
116
  logging.debug("A resposta não contém o campo 'result'.")
122
117
  return {"success" : False, "result" : result}
@@ -134,6 +129,67 @@ class ServiceNow:
134
129
  return {"success" : False, "result" : None, "error" : str(e)}
135
130
 
136
131
 
132
+ def __request_download(self, url, params = "", timeout = 15):
133
+ """
134
+ Realiza uma requisição GET para a URL especificada.
135
+
136
+ Parâmetros:
137
+ url (str): URL para a qual a requisição será enviada.
138
+ params (dict, opcional): Parâmetros de consulta (query parameters) a serem incluídos na requisição.
139
+ Padrão é uma string vazia.
140
+
141
+ Retorno:
142
+ dict: Um dicionário contendo:
143
+ - success (bool): Indica se a requisição foi bem-sucedida.
144
+ - result (dict ou None): Resultado da resposta, se disponível. Contém o conteúdo JSON do campo "result".
145
+ - error (Exception ou None): Objeto de exceção em caso de erro, ou None se não houver erros.
146
+
147
+ Tratamento de exceções:
148
+ - requests.exceptions.HTTPError: Lança um erro HTTP caso o código de status indique falha.
149
+ - requests.exceptions.RequestException: Captura outros erros relacionados à requisição, como timeout ou conexão.
150
+ - Exception: Captura erros inesperados.
151
+
152
+ Logs:
153
+ - Logs de depuração são gerados para erros HTTP, erros de requisição e exceções inesperadas.
154
+ - Um aviso é registrado caso a resposta não contenha o campo "result".
155
+
156
+ Observações:
157
+ - Utiliza autenticação fornecida pelo método privado `__auth()`.
158
+ - Define um tempo limite (`timeout`) de 15 segundos.
159
+ - Verifica o certificado SSL (`verify=True`).
160
+ """
161
+ try:
162
+ response = requests.get(
163
+ url,
164
+ params=params,
165
+ auth=self.__auth(),
166
+ headers=self.api_header,
167
+ timeout=timeout,
168
+ verify=True
169
+ )
170
+
171
+ # VALIDA SE HOUVE SUCESSO NA REQUISIÇÃO
172
+ response.raise_for_status()
173
+ # Analisa o conteúdo da resposta JSON
174
+ if response.status_code == 200:
175
+ return {"success" : True, "result" : response}
176
+ else:
177
+ logging.debug("Erro ao realizar a consulta")
178
+ return {"success" : False, "result" : response.status_code}
179
+
180
+ except requests.exceptions.HTTPError as http_err:
181
+ logging.debug(f"Erro HTTP ao buscar os detalhes do ticket: {http_err}")
182
+ return {"success" : False, "error" : str(http_err), "result" : None}
183
+
184
+ except requests.exceptions.RequestException as req_err:
185
+ logging.debug(f"Erro ao buscar os detalhes do ticket: {req_err}")
186
+ return {"success" : False, "error" : str(req_err), "result" : None}
187
+
188
+ except Exception as e:
189
+ logging.debug(f"Erro inesperado: {e}")
190
+ return {"success" : False, "error" : str(e), "result" : None}
191
+
192
+
137
193
  def put(self, url, payload, timeout = 15):
138
194
  """
139
195
  Realiza uma requisição PUT para a URL especificada.
@@ -272,7 +328,7 @@ class ServiceNow:
272
328
  return {"success" : False, "error" : str(e) , "result" : None}
273
329
 
274
330
 
275
- def listar_tickets(self, tabela: str = None, campos: list = None, query: str = None, limite: int = 50, timeout:int=15)->dict:
331
+ def listar_tickets(self, tabela: str = None, campos: list = None, query: str = None, limite: int = 50, timeout:int=15, sysparm_display_value: str = "")->dict:
276
332
  """lista tickets do ServiceNow
277
333
 
278
334
  Args:
@@ -290,46 +346,23 @@ class ServiceNow:
290
346
  except ValidationError as e:
291
347
  raise ValueError("Erro na validação dos dados de input do método:", e.errors())
292
348
 
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
349
 
313
350
  params = {
314
351
  "sysparm_query" : query,
315
352
  "sysparm_fields" : ','.join(campos),
316
- "sysparm_display_value" : "all",
353
+ "sysparm_display_value" : sysparm_display_value,
317
354
  "sysparm_limit" : limite
318
355
  }
319
- # params["sysparm_query"] = query
320
- # params["sysparm_fields"] = ','.join(campos)
321
- # params["sysparm_display_value"] = "all"
322
- # params["sysparm_limit"] = limite
323
356
 
324
357
  url = f"{self.api_url}/now/table/{tabela}"
325
358
 
326
359
  try:
327
- response = self.request(url=url, params=params, timout=timeout)
328
- # response = requests.get(url, params=params, auth=(self.username, self.password), headers=self.api_header, verify=True, timeout=timeout)
329
- return {"success":True, "result": response.json()}
360
+ response = self.request(url=url, params=params, timeout=timeout)
361
+ return response
330
362
 
331
363
  except Exception as e:
332
- return {"success" : False, "result" : None, "error" : str(e)}
364
+ logging.debug(f"erro: {e}")
365
+ return str(e)
333
366
 
334
367
 
335
368
  def update_ticket(self, tabela: str = None, sys_id: str = None, payload: dict = None, timeout:int=15)->dict:
@@ -348,32 +381,18 @@ class ServiceNow:
348
381
  UpdateTicketValidator(tabela=tabela, sys_id=sys_id, payload=payload, timeout=timeout)
349
382
  except ValidationError as e:
350
383
  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.")
355
-
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.")
359
-
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.")
363
-
384
+
364
385
  if "assigned_to" not in payload:
365
-
366
- payload["assigned_to"] = self.username
386
+ payload["assigned_to"] = self.__username
367
387
 
368
388
  try:
369
389
 
370
390
  url = f"{self.api_url}/now/table/{tabela}/{sys_id}"
371
391
  response = self.put(url, payload=payload, timeout=timeout)
372
- # response = requests.patch(url, auth=(self.username, self.password), headers=self.api_header, data=str(campos).encode('utf-8'), verify=True, timeout=timeout)
373
- return {"success" : True, "result" : response.json() , "error" : None}
392
+ return response
374
393
 
375
394
  except Exception as e:
376
- return {"success" : False, "result" : None, "error" : str(e) }
395
+ return str(e)
377
396
 
378
397
 
379
398
  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):
@@ -389,55 +408,40 @@ class ServiceNow:
389
408
  Returns:
390
409
  dict: resposta do ServiceNow
391
410
  """
411
+ if header_content_type is None:
412
+ header_content_type = self.__valida_header_content_type(anexo_path, header_content_type)
413
+
392
414
  try:
393
415
  AttachFileTicketValidator(header_content_type=header_content_type, anexo_path=anexo_path, tabela=tabela, sys_id=sys_id, timeout=timeout)
394
416
  except ValidationError as e:
395
417
  raise ValueError("Erro na validação dos dados de input do método:", e.errors())
396
418
 
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.")
400
-
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.")
404
-
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.")
408
-
409
419
  if not os.path.exists(anexo_path):
410
420
  raise FileExistsError(f"O arquivo não foi encontrado ({anexo_path})")
411
421
 
412
- header_content_type = self.valida_header_content_type(anexo_path)
422
+
413
423
 
414
424
  # Converte as chaves do dicionário para minúsculas
415
425
  header_content_type_lower = {k.lower(): v for k, v in header_content_type.items()}
416
426
 
417
427
 
418
-
419
428
  if "content-type" not in header_content_type_lower:
420
-
421
429
  raise ValueError("O parâmetro 'header_content_type' não possui a chave 'Content-Type' com o tipo do anexo")
422
430
 
423
431
  nome_arquivo = os.path.basename(anexo_path)
424
-
425
- with open(anexo_path, 'rb') as f:
426
-
427
- file_data = f
428
-
429
432
  try:
430
- url = f"{self.api_url}/now/attachment/file?table_name={tabela}&table_sys_id={sys_id}&file_name={nome_arquivo}"
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)
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)
434
- return {"success" : True, "result" : response.json(), "error" : None}
433
+ with open(anexo_path, 'rb') as f:
434
+ url = f"{self.api_url}/now/attachment/file?table_name={tabela}&table_sys_id={sys_id}&file_name={nome_arquivo}"
435
+ response = requests.post(url, headers=header_content_type, auth=(self.__username, self.__password), data=f, timeout=timeout)
436
+
437
+ logging.debug("Arquivo adicionado no ticket")
438
+ return {"success": True, "result" : response}
435
439
 
436
440
  except Exception as e:
437
441
  return {"success" : False, "result" : None, "error" : str(e)}
438
442
 
439
443
 
440
- def valida_header_content_type(self, anexo_path ):
444
+ def __valida_header_content_type(self, anexo_path, header_content_type):
441
445
  """
442
446
  Valida e define o cabeçalho `Content-Type` baseado na extensão do arquivo anexado.
443
447
 
@@ -477,17 +481,106 @@ class ServiceNow:
477
481
 
478
482
  header_content_type = {"Content-Type": "text/plain"}
479
483
 
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\"})")
483
-
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\"})")
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\"})")
487
487
 
488
+
488
489
  return header_content_type
489
490
 
490
491
 
492
+ def download_anexo(self, sys_id_file : str, file_path : str, timeout : int = 15):
493
+ """
494
+ Faz o download de um anexo do ServiceNow utilizando o `sys_id` do arquivo e salva no caminho especificado.
495
+
496
+ Parâmetros:
497
+ sys_id_file (str): O identificador único (sys_id) do arquivo no ServiceNow.
498
+ file_path (str): O caminho completo onde o arquivo será salvo localmente.
499
+ timeout (int, opcional): O tempo limite, em segundos, para a requisição HTTP. Padrão é 15 segundos.
500
+
501
+ Retorna:
502
+ bool: Retorna `True` se o arquivo foi salvo com sucesso no caminho especificado.
503
+ dict: Em caso de erro, retorna um dicionário com os seguintes campos:
504
+ - "success" (bool): Indica se a operação foi bem-sucedida (`False` em caso de falha).
505
+ - "error" (str): Mensagem de erro detalhada em caso de falha.
506
+ - "path" (str, opcional): O caminho do arquivo salvo, caso tenha sido criado após a criação de diretórios.
507
+
508
+ Comportamento:
509
+ - Em caso de sucesso na requisição HTTP (status code 200), o arquivo é salvo no `file_path`.
510
+ - Caso o caminho do arquivo não exista, tenta criá-lo automaticamente.
511
+ - Registra logs de erros ou informações em caso de falha.
512
+
513
+ Exceções Tratadas:
514
+ - FileNotFoundError: Se o caminho especificado não existir, tenta criar os diretórios necessários.
515
+ - Exception: Registra quaisquer outros erros inesperados durante o salvamento.
516
+
517
+ Logs:
518
+ - `logging.debug`: Para erros de salvamento no arquivo.
519
+ - `logging.error`: Para erros genéricos durante o processo.
520
+ - `logging.info`: Para informações sobre falhas na requisição HTTP.
521
+
522
+ Exemplo:
523
+ obj.download_anexo("abc123sysid", "/caminho/para/salvar/arquivo.txt")
524
+ {'success': False, 'error': 'Status code: 404'}
525
+ """
526
+ try:
527
+ DownloadFileValidator(sys_id_file=sys_id_file, file_path=file_path)
528
+ except ValidationError as e:
529
+ raise ValueError("Erro na validação dos dados de input do método:", e.errors())
530
+
531
+ url = f"{self.api_url}/now/attachment/{sys_id_file}/file"
532
+ response = self.__request_download(url=url, timeout=timeout)
533
+ response = response.get("result")
534
+ if response.status_code == 200:
535
+ try:
536
+ if not os.path.exists(file_path):
537
+ diretorio = os.path.dirname(file_path)
538
+ print(diretorio)
539
+ os.makedirs(diretorio, exist_ok=True)
540
+ logging.debug(f"Diretorio criado: {diretorio}")
541
+ with open(file_path, 'wb') as f:
542
+ f.write(response.content)
543
+ return {"success" : True, "path" : file_path }
544
+
545
+ except FileNotFoundError:
546
+ try:
547
+ path = os.path.dirname(file_path)
548
+ os.makedirs(path)
549
+ with open(file_path, 'wb') as f:
550
+ f.write(response.content)
551
+ return {"success" : True, "path" : file_path }
552
+ except Exception as e:
553
+ logging.debug(f"Erro ao salvar o arquivo. Erro: {e}")
554
+ return {"success" : False, "error" : str(e) }
555
+ except Exception as e:
556
+ logging.error(f"Erro ao salvar o arquivo. Erro: {e}")
557
+ return {"success" : False, "error" : str(e) }
558
+ else:
559
+ logging.debug(f"{response.status_code}")
560
+ return {"success" : False, "error" : f"Status code: {response.status_code }"}
561
+
562
+ def get_variables_from_ticket(self, sys_id : str, campos : str = ''):
563
+ """
564
+ Obtém as variáveis associadas ao ticket.
565
+ args:
566
+ sys_id : STR - Id do ticket
567
+ """
568
+ if isinstance(campos, list):
569
+ campos = ','.join(campos)
570
+ if campos == "":
571
+ campos = 'name,question_text,type,default_value, sys_id'
572
+
573
+ logging.debug(f"Obtendo variáveis do ticket {sys_id}")
574
+ url = f"{self.api_url}/now/table/item_option_new"
575
+ params = {
576
+ 'sysparm_query': f'cat_item={sys_id}',
577
+ 'sysparm_display_value': 'true',
578
+ 'sysparm_fields': campos,
579
+ }
580
+ response = self.request(url, params=params)
581
+ return response
582
+
583
+
491
584
  def get_anexo(self, sys_id: str = None, tabela: str = None, campo: str = 'default', download_dir:str=None, timeout:int=15)->dict:
492
585
  """Traz os anexos de um campo do ticket especificado
493
586
 
@@ -508,19 +601,6 @@ class ServiceNow:
508
601
  except ValidationError as e:
509
602
  raise ValueError("Erro na validação dos dados de input do método:", e.errors())
510
603
 
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.")
514
-
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.")
518
-
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.")
522
-
523
- # Validação de 'download_dir'
524
604
  if download_dir is not None:
525
605
  if not isinstance(download_dir, str) or not download_dir.strip():
526
606
  raise ValueError("O parâmetro 'download_dir' precisa ser a pasta pra onde o anexo será feito o download.")
@@ -535,12 +615,7 @@ class ServiceNow:
535
615
 
536
616
  # Convert bytes to base64
537
617
  def __bytes_to_base64(image_bytes):
538
- # base64_encoded = base64.b64encode(image_bytes)
539
-
540
- # # Decode the bytes to a string (UTF-8 encoding)
541
- # base64_string = base64_encoded.decode('utf-8')
542
618
 
543
- # # return base64_string
544
619
  return base64.b64encode(image_bytes).decode('utf-8')
545
620
 
546
621
  def __formatar_tamanho(tamanho_bytes):
@@ -564,8 +639,8 @@ class ServiceNow:
564
639
  try:
565
640
  if campo == 'default':
566
641
  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
-
642
+ response = self.__request_download(url, timeout=timeout)
643
+ response = response.get("result")
569
644
  if response.status_code == 200:
570
645
  for attachment in response.json().get("result", []):
571
646
  arquivo = {
@@ -574,7 +649,8 @@ class ServiceNow:
574
649
  "content_type": attachment["content_type"],
575
650
  "base64": None
576
651
  }
577
- byte_response = self.request(attachment["download_link"], timeout=timeout)
652
+ byte_response = self.__request_download(attachment["download_link"], timeout=timeout)
653
+ byte_response = byte_response.get("result")
578
654
  if byte_response.status_code == 200:
579
655
  arquivo["base64"] = __bytes_to_base64(byte_response.content)
580
656
  if download_dir:
@@ -586,13 +662,15 @@ class ServiceNow:
586
662
 
587
663
  else:
588
664
  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)
665
+ response = self.__request_download(url, timeout=timeout)
666
+ response = response.get("result")
590
667
  if response.status_code == 200 and response.json().get("result"):
591
668
  campo_data = response.json()["result"][0].get(campo)
592
669
  if campo_data:
593
670
  attachment_id = campo_data["value"]
594
671
  attachment_url = f"{self.api_url}/now/attachment/{attachment_id}/file"
595
- byte_response = self.request(attachment_url, timeout=timeout)
672
+ byte_response = self.__request_download(attachment["download_link"], timeout=timeout)
673
+ byte_response = byte_response.get("result")
596
674
  if byte_response.status_code == 200:
597
675
  arquivo = {
598
676
  "file_name": campo_data["display_value"],
@@ -609,115 +687,4 @@ class ServiceNow:
609
687
 
610
688
  except Exception as e:
611
689
  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)
639
-
640
- # if byte_response.status_code == 200:
641
-
642
- # arquivo["base64"] = __bytes_to_base64(byte_response.content)
643
-
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 }
655
-
656
- # except Exception as e:
657
- # return {"success" : False, "result" : None, "error" : e, "status_code" : byte_response.status_code}
658
-
659
- # elif response.status_code != 200:
660
- # return {"success" : False , "result" : response.json(), "status_code" : response.status_code, "error" : None}
661
-
662
- # except Exception as e:
663
- # return {"success" : False, "result" : None, "error" : e}
664
-
665
- # # Fazendo download do anexo do campo especificado do ticket
666
- # else:
667
-
668
- # logging.debug(f"verificando o campo '{campo}' de anexo")
669
-
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)
674
-
675
- # # Requisicao OK e com anexo
676
- # if response.status_code == 200 and len(response.json()["result"]) >= 1:
677
-
678
- # for ticket_data in response.json()["result"]:
679
-
680
- # if len(ticket_data) >= 1:
681
-
682
- # try:
683
-
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)
688
-
689
- # except Exception as e:
690
- # return {"success" : False, "error" : e, "result" : None}
691
-
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()}
717
-
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()}
721
-
722
- # except Exception as e:
723
- # return {"success" : False, "error" : e, "result" : None}
690
+ return {"success": False, "error": str(e)}
@@ -28,23 +28,24 @@ class InitParamsValidator(BaseModel):
28
28
  """
29
29
  @field_validator('username', 'password', 'env')
30
30
  def check_str_input(cls, value, info):
31
- if not isinstance(value, str) or not value.strip():
32
- raise ValueError(f"O campo '{info.field_name}' deve ser strings e não {type(value)}")
31
+ if not value.strip():
32
+ raise ValueError(f"O campo '{info.field_name}' não pode ser vazio")
33
+ if not isinstance(value, str):
34
+ raise ValueError(f"O campo '{info.field_name}' deve ser string e não ser do tipo: {type(value)}")
33
35
  return value
34
36
 
37
+
35
38
  class RequestValidator(BaseModel):
36
39
  """
37
40
  Classe para validar os campos de uma requisição.
38
41
  Atributos:
39
42
  - url (str): A URL da requisição.
40
- - params (str): Os parâmetros da requisição.
41
43
  - timeout (int): O tempo limite da requisição em segundos. O valor padrão é 15.
42
44
  Métodos:
43
45
  - check_str_input(value, info): Valida se o valor é uma string não vazia.
44
46
  - check_input_basic(value, info): Valida se o valor é um inteiro.
45
47
  """
46
48
  url: str
47
- params: str
48
49
  timeout: int = 15
49
50
 
50
51
 
@@ -58,7 +59,7 @@ class RequestValidator(BaseModel):
58
59
  Lança:
59
60
  - ValueError: Se o valor não for uma string ou estiver vazio.
60
61
  """
61
- @field_validator('url', 'params')
62
+ @field_validator('url')
62
63
  def check_str_input(cls, value, info):
63
64
  if not isinstance(value, str) or not value.strip():
64
65
  raise ValueError(f"O campo '{info.field_name}' deve ser uma string e não um {type(value)} e não vazio")
@@ -81,6 +82,7 @@ class RequestValidator(BaseModel):
81
82
 
82
83
  return value
83
84
 
85
+
84
86
  class PutValidator(BaseModel):
85
87
  """
86
88
  Classe de validação para requisições PUT.
@@ -146,6 +148,7 @@ class PutValidator(BaseModel):
146
148
  raise ValueError(f"O campo '{info.field_name}' deve ser um dicionário e não um {type(value)}")
147
149
  return value
148
150
 
151
+
149
152
  class PostValidator(BaseModel):
150
153
  """
151
154
  Classe responsável por validar os dados de um post.
@@ -171,6 +174,7 @@ class PostValidator(BaseModel):
171
174
  raise ValueError(f"O campo '{info.field_name}' deve ser um dicionário e não um {type(value)}")
172
175
  return value
173
176
 
177
+
174
178
  class ListTicketValidator(BaseModel):
175
179
  """
176
180
  Classe para validar os campos de entrada da lista de tickets.
@@ -189,7 +193,7 @@ class ListTicketValidator(BaseModel):
189
193
  query : str
190
194
  campos : List[str]
191
195
  timeout : int
192
- limit : int
196
+ limite : int
193
197
 
194
198
  @field_validator('tabela', 'query')
195
199
  def check_str_input(cls, value, info):
@@ -197,7 +201,7 @@ class ListTicketValidator(BaseModel):
197
201
  raise ValueError(f"O campo '{info.field_name}' deve ser uma string e não um {type(value)} e não vazio")
198
202
  return value
199
203
 
200
- @field_validator('timeout', 'limit')
204
+ @field_validator('timeout', 'limite')
201
205
  def check_input_basic(cls, value, info):
202
206
  if not isinstance(value, int):
203
207
  raise ValueError(f"O campo '{info.field_name}' deve ser um inteiro e não um {type(value)}")
@@ -209,6 +213,7 @@ class ListTicketValidator(BaseModel):
209
213
  raise ValueError(f"O campo '{info.field_name}' deve ser uma lista e não um {type(value)}")
210
214
  return value
211
215
 
216
+
212
217
  class UpdateTicketValidator(BaseModel):
213
218
  """
214
219
  Classe responsável por validar os campos do ticket de atualização.
@@ -225,7 +230,7 @@ class UpdateTicketValidator(BaseModel):
225
230
 
226
231
  sys_id : str
227
232
  tabela : str
228
- payload : List[str]
233
+ payload : dict
229
234
  timeout : int
230
235
 
231
236
  """
@@ -272,10 +277,41 @@ class UpdateTicketValidator(BaseModel):
272
277
  """
273
278
  @field_validator('payload')
274
279
  def check_list_input(cls, value, info):
275
- if not isinstance(value, list):
280
+ if not isinstance(value, dict):
276
281
  raise ValueError(f"O campo '{info.field_name}' deve ser uma lista e não um {type(value)}")
277
282
  return value
278
283
 
284
+
285
+ class DownloadFileValidator(BaseModel):
286
+ """
287
+ Classe responsável por validar os campos do ticket de download.
288
+ Atributos:
289
+ - sys_id_file (str): O ID do anexo dentro do formulário aberto.
290
+ - file_path (str): URL onde será salvo o arquivo.
291
+ Métodos:
292
+ - check_str_input(value, info): Valida se o valor fornecido é uma string não vazia.
293
+ """
294
+ sys_id_file : str
295
+ file_path : str
296
+
297
+
298
+ """
299
+ Valida se o valor fornecido é uma string não vazia.
300
+ Parâmetros:
301
+ - value (Any): O valor a ser validado.
302
+ - info (FieldInfo): Informações sobre o campo.
303
+ Retorna:
304
+ - value (Any): O valor validado.
305
+ Lança:
306
+ - ValueError: Se o valor não for uma string ou estiver vazio.
307
+ """
308
+ @field_validator('sys_id_file', 'file_path')
309
+ def check_str_input(cls, value, info):
310
+ if not isinstance(value, str) or not value.strip():
311
+ raise ValueError(f"O campo '{info.field_name}' deve ser uma string e não um {type(value)} e não vazio")
312
+ return value
313
+
314
+
279
315
  class AttachFileTicketValidator(BaseModel):
280
316
  """
281
317
  Classe responsável por validar os campos de entrada do anexo de um ticket no ServiceNow.
@@ -314,6 +350,7 @@ class AttachFileTicketValidator(BaseModel):
314
350
  raise ValueError(f"O campo '{info.field_name}' deve ser um dicionário e não um {type(value)}")
315
351
  return value
316
352
 
353
+
317
354
  class GetAttachValidator(BaseModel):
318
355
  """
319
356
  Classe responsável por validar os campos de entrada para a obtenção de anexos no ServiceNow.
@@ -330,7 +367,6 @@ class GetAttachValidator(BaseModel):
330
367
 
331
368
  sys_id : str
332
369
  tabela : str
333
- campo :str
334
370
  download_dir : str
335
371
  timeout : int
336
372
 
@@ -344,7 +380,7 @@ class GetAttachValidator(BaseModel):
344
380
  Lança:
345
381
  - ValueError: Se o campo não for uma string ou estiver vazio.
346
382
  """
347
- @field_validator('sys_id', 'tabela', 'campo', 'download_dir')
383
+ @field_validator('sys_id', 'tabela','download_dir')
348
384
  def check_str_input(cls, value, info):
349
385
  if not isinstance(value, str) or not value.strip():
350
386
  raise ValueError(f"O campo '{info.field_name}' deve ser uma string e não um {type(value)} e não vazio")
@@ -1,2 +1,3 @@
1
1
  from .GoogleDriveValidator import *
2
- from .GcpBigQueryValidator import *
2
+ from .GcpBigQueryValidator import *
3
+ from .ServiceNowValidator import *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: csc_cia_stne
3
- Version: 0.0.37
3
+ Version: 0.0.38
4
4
  Summary: Biblioteca do time CSC-CIA utilizada no desenvolvimento de RPAs
5
5
  License: MIT
6
6
  Keywords: karavela,csc,cia,stone,rpa
@@ -7,8 +7,8 @@ csc_cia_stne/google_drive.py,sha256=yLfaAWcrOKk9YuI1qcOTkZEqyrSSEF68RmzYgC7dq-g,
7
7
  csc_cia_stne/karavela.py,sha256=LlpFiDu0ww7eh8F-oREWSQo2mVoQMibx0EGiHR6tznI,4231
8
8
  csc_cia_stne/logger_json.py,sha256=W6Fj0G1-TWXHdHoLLX5SbVV7BSpVvjHm1fkKI5Q69YQ,3129
9
9
  csc_cia_stne/logger_rich.py,sha256=FYO9tG-tEJJDP2EDYFTQYS8GayvbRMgRbKI9FcXvRCs,7812
10
- csc_cia_stne/provio.py,sha256=HU31l134U58wag3fx3A1X4pZxfubF5VoO4D-LQi6-Yg,3957
11
- csc_cia_stne/servicenow.py,sha256=QN7R49Bb8HcqfGT9iytbPaqwV2rtH4N7Nhvb_kfTsHU,36400
10
+ csc_cia_stne/provio.py,sha256=yTRX4YJ6RdNc2ZCVPgsIEgsxe3wQgwnXns4bPX4plAI,3955
11
+ csc_cia_stne/servicenow.py,sha256=05KWtUXUR8dGLwzPjxuEFN4skDvF4Z5RoarNArXW-6M,32181
12
12
  csc_cia_stne/stne_admin.py,sha256=vnGSEzcmqWE42vg71oEuoRg6ENaGsZsXFOjxduSH4KU,23561
13
13
  csc_cia_stne/utilitarios/__init__.py,sha256=oWxCOFL2wxjs8KBgxoeA6-gVe4ulicVGDgdaw8jzvsY,253
14
14
  csc_cia_stne/utilitarios/functions/__init__.py,sha256=iSLKxM8lgPM1lJ51WZ1mwy36IW5iitfMRc_AnEtzpxg,364
@@ -19,10 +19,10 @@ csc_cia_stne/utilitarios/functions/func_settings.py,sha256=rlu4Ec5M-7UzrreiR92L_
19
19
  csc_cia_stne/utilitarios/functions/func_titulo.py,sha256=bH4tYxovTARF-g2kWUK_GIzzXt8egbVdp6mWD6fc_3I,5345
20
20
  csc_cia_stne/utilitarios/validations/GcpBigQueryValidator.py,sha256=PfCeeQquheZkrm07HTIjobleh3QQOjljRFGdxbQ1amQ,4630
21
21
  csc_cia_stne/utilitarios/validations/GoogleDriveValidator.py,sha256=cNuqQyQifxx3zIqDCljfcUhWCty9SyT9oGTVUKp-uWw,3752
22
- csc_cia_stne/utilitarios/validations/ServiceNowValidator.py,sha256=Vg4i9P7PV_qQ-gUDtSbSK4wOybt3deKG0OcotpufT7Y,13391
23
- csc_cia_stne/utilitarios/validations/__init__.py,sha256=VE49g8ExrBUyyUzhOUjsP9880L1PWCKPHckd848MQeo,71
24
- csc_cia_stne-0.0.37.dist-info/LICENCE,sha256=LPGMtgKki2C3KEZP7hDhA1HBrlq5JCHkIeStUCLEMx4,1073
25
- csc_cia_stne-0.0.37.dist-info/METADATA,sha256=KG5dCQwIri5BpfPPvxVMiG4pF786BNU-0DMrd8IV7ps,1025
26
- csc_cia_stne-0.0.37.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
27
- csc_cia_stne-0.0.37.dist-info/top_level.txt,sha256=ldo7GVv3tQx5KJvwBzdZzzQmjPys2NDVVn1rv0BOF2Q,13
28
- csc_cia_stne-0.0.37.dist-info/RECORD,,
22
+ csc_cia_stne/utilitarios/validations/ServiceNowValidator.py,sha256=yleKUIo1ZfyloP9fDPNjv3JJXdLcocT81WIgRSYmqEA,14423
23
+ csc_cia_stne/utilitarios/validations/__init__.py,sha256=O_qyEU2ji3u6LHUXZCXvUFsMpoMWL625qqHTXyXivTA,106
24
+ csc_cia_stne-0.0.38.dist-info/LICENCE,sha256=LPGMtgKki2C3KEZP7hDhA1HBrlq5JCHkIeStUCLEMx4,1073
25
+ csc_cia_stne-0.0.38.dist-info/METADATA,sha256=FhHW3D7TwNxBHj4ovARDu05vqAF7wiDAMYAbko9GT2U,1025
26
+ csc_cia_stne-0.0.38.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
27
+ csc_cia_stne-0.0.38.dist-info/top_level.txt,sha256=ldo7GVv3tQx5KJvwBzdZzzQmjPys2NDVVn1rv0BOF2Q,13
28
+ csc_cia_stne-0.0.38.dist-info/RECORD,,