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