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 +1 -1
- csc_cia_stne/servicenow.py +197 -230
- csc_cia_stne/utilitarios/validations/ServiceNowValidator.py +47 -11
- csc_cia_stne/utilitarios/validations/__init__.py +2 -1
- {csc_cia_stne-0.0.37.dist-info → csc_cia_stne-0.0.38.dist-info}/METADATA +1 -1
- {csc_cia_stne-0.0.37.dist-info → csc_cia_stne-0.0.38.dist-info}/RECORD +9 -9
- {csc_cia_stne-0.0.37.dist-info → csc_cia_stne-0.0.38.dist-info}/LICENCE +0 -0
- {csc_cia_stne-0.0.37.dist-info → csc_cia_stne-0.0.38.dist-info}/WHEEL +0 -0
- {csc_cia_stne-0.0.37.dist-info → csc_cia_stne-0.0.38.dist-info}/top_level.txt +0 -0
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
|
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
|
|
csc_cia_stne/servicenow.py
CHANGED
@@ -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
|
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.
|
55
|
-
self.
|
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.
|
61
|
+
return (self.__username, self.__password)
|
67
62
|
|
68
63
|
|
69
|
-
def request (self, url : str , params : str = "",
|
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=
|
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=
|
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" :
|
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,
|
328
|
-
|
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
|
-
|
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
|
-
|
373
|
-
return {"success" : True, "result" : response.json() , "error" : None}
|
392
|
+
return response
|
374
393
|
|
375
394
|
except Exception as e:
|
376
|
-
return
|
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
|
-
|
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
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
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
|
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
|
-
#
|
481
|
-
|
482
|
-
|
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.
|
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.
|
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.
|
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.
|
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
|
32
|
-
|
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'
|
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
|
-
|
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', '
|
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 :
|
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,
|
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',
|
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")
|
@@ -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=
|
11
|
-
csc_cia_stne/servicenow.py,sha256=
|
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=
|
23
|
-
csc_cia_stne/utilitarios/validations/__init__.py,sha256=
|
24
|
-
csc_cia_stne-0.0.
|
25
|
-
csc_cia_stne-0.0.
|
26
|
-
csc_cia_stne-0.0.
|
27
|
-
csc_cia_stne-0.0.
|
28
|
-
csc_cia_stne-0.0.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|