Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eacb9635c0 | |||
| 0371ae9175 | |||
| 86d1d612a0 | |||
| 4c130636d3 | |||
| f67bef6d43 | |||
| 6a3720190f | |||
| 7e0959d9b7 | |||
| 08161fd276 | |||
| 4b548df2f7 | |||
| 65b39e4bb8 | |||
| 21f5227844 |
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
**/__pycache__/
|
**/__pycache__/
|
||||||
**/main.py
|
**/main.py
|
||||||
|
*.http
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
class API_Proxy:
|
||||||
|
"""
|
||||||
|
## API Proxy
|
||||||
|
---
|
||||||
|
O Serviço API Proxy permite realizar chamadas a api externas proxiadas pelo Replay. Dessa forma, as chamadas podem ficar pré- configuradas dentro do Replay.
|
||||||
|
|
||||||
|
Caso o robô ou aplicação desejem alterar algum parâmetro antes da chamada ainda é possível, para isso, deve-se modificar a estrutura da requisição.
|
||||||
|
|
||||||
|
Para entender melhor a como funciona a estrutura de requisição, você pode visitar a documentaçao oficial ou dar uma olhada no método "do ()" deste client.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ep: str = ""
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
self.ep = "https://localhost:8443"
|
||||||
|
|
||||||
|
def __request_json_post__(self, path: str, object: dict):
|
||||||
|
|
||||||
|
"""
|
||||||
|
## HTTP JSON POST
|
||||||
|
---
|
||||||
|
Este método é responsável por realizar requisições HTTP do tipo POST para objetos JSON.
|
||||||
|
|
||||||
|
Ele retorna o corpo de resposta da requisição, ou uma mensagem de erro, que indica qual foi a irregularidade ocorrida ao chamar a API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url = self.ep + path
|
||||||
|
print("Calling: " + url)
|
||||||
|
|
||||||
|
apikey = os.environ.get('REPLAY_APIKEY')
|
||||||
|
headers = {"X-API-KEY": apikey}
|
||||||
|
res = requests.post(url, json = object, headers = headers, verify = False)
|
||||||
|
|
||||||
|
response = json.loads(res.text)
|
||||||
|
|
||||||
|
byte_encoded_body = base64.b64decode(response['body'])
|
||||||
|
decoded_body = str(byte_encoded_body, "utf-8")
|
||||||
|
|
||||||
|
if response['status_code'] >= 400:
|
||||||
|
raise Exception(f"HTTP ERROR: {str(response['status_code'])} - {response['body']}")
|
||||||
|
|
||||||
|
if response['header']['Content-Type'] != None and response['header']['Content-Type'].find('json') != -1:
|
||||||
|
return json.loads(decoded_body)
|
||||||
|
else:
|
||||||
|
return decoded_body
|
||||||
|
|
||||||
|
def do (self, request: dict):
|
||||||
|
"""
|
||||||
|
## API Proxy Do
|
||||||
|
Faz o proxy da chamada remota e retorna qualquer que seja o resultado desta requisição.
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Parâmetros:
|
||||||
|
- request: Estrutura específica da requisição. Veja abaixo como construí-la:
|
||||||
|
|
||||||
|
{
|
||||||
|
"Name": "Nome de template da chamada",
|
||||||
|
|
||||||
|
"Method": "Método de requisição (GET, POST, PUT, ...)",
|
||||||
|
|
||||||
|
"Url": "Url da API que se deseja chamar",
|
||||||
|
|
||||||
|
"Header": {
|
||||||
|
"Chave da configuração de cabeçalho": "Valor associado",
|
||||||
|
|
||||||
|
...
|
||||||
|
},
|
||||||
|
|
||||||
|
"Body": "Elementos pertencentes ao corpo da requisição",
|
||||||
|
|
||||||
|
"Readonly":
|
||||||
|
|
||||||
|
- True: Mesmo que seja encontrado um template correspondente ao valor de "Name" passado, as configurações desta estrutura prevalecerão na requisição.
|
||||||
|
|
||||||
|
- False: Caso um template seja encontrado em correspondência ao valor passado em "Name", as configurações do mesmo sobrescreverão as informações passadas nesta estrutura.
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Retorna:
|
||||||
|
-> Resultado da requisição
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.__request_json_post__ ("/ipc/apiproxy/do", request)
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Feature: The API Proxy client
|
||||||
|
Scenario: A HTTP request with json body response
|
||||||
|
Given a API Proxy client
|
||||||
|
When a request is made with GET for the API: https://pokeapi.co/api/v2/pokemon/ditto
|
||||||
|
Then there must be a json response
|
||||||
|
And the pokemon name must be ditto
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: A HTTP request with text body response
|
||||||
|
Given a API Proxy client
|
||||||
|
When a request is made with GET for the API: https://www.slashdot.org
|
||||||
|
Then the response content type must be string
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
from re import A
|
||||||
|
from behave import *
|
||||||
|
from cli import API_Proxy
|
||||||
|
|
||||||
|
# =========================== JSON RESPONSE BODY ===========================
|
||||||
|
@given(u'a API Proxy client')
|
||||||
|
def step_impl(context):
|
||||||
|
context.client = API_Proxy ()
|
||||||
|
|
||||||
|
@when(u'a request is made with GET for the API: https://pokeapi.co/api/v2/pokemon/ditto')
|
||||||
|
def step_impl(context):
|
||||||
|
context.response = context.client.do (
|
||||||
|
{
|
||||||
|
"Method": "GET",
|
||||||
|
"Url": "https://pokeapi.co/api/v2/pokemon/ditto",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@then(u'there must be a json response')
|
||||||
|
def step_impl(context):
|
||||||
|
assert type (context.response) == dict, "Something went wrong calling the pokemon/ditto API."
|
||||||
|
|
||||||
|
@then(u'the pokemon name must be ditto')
|
||||||
|
def step_impl(context):
|
||||||
|
assert context.response["forms"][0]["name"] == "ditto", "This pokemon is not Ditto."
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# =========================== TEXT RESPONSE BODY ===========================
|
||||||
|
@when(u'a request is made with GET for the API: https://www.slashdot.org')
|
||||||
|
def step_impl(context):
|
||||||
|
context.response = context.client.do (
|
||||||
|
{
|
||||||
|
"Method": "GET",
|
||||||
|
"Url": "https://www.slashdot.org",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@then(u'the response content type must be string')
|
||||||
|
def step_impl(context):
|
||||||
|
assert type (context.response) == str, "Something went wrong calling the slashdot API."
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
class DataAPI:
|
||||||
|
"""
|
||||||
|
## Data API
|
||||||
|
---
|
||||||
|
Esta classe permite a execução de comandos básicos SQL para o banco de dados remoto do Replay.
|
||||||
|
|
||||||
|
Para que a classe funcione corretamente, é necessário que o usuário tenha acesso à chave da API do BD remoto. Devendo passá-la como parâmetro ao instanciar a classe DataAPI. Por exemplo:
|
||||||
|
d_api = DataAPI ("ahsgcn21390-dsaf_dain2eq81288sad9")
|
||||||
|
"""
|
||||||
|
|
||||||
|
ep: str = ""
|
||||||
|
|
||||||
|
def __init__ (self, api_key: str):
|
||||||
|
self.ep = "https://dataapi.digitalcircle.com.br"
|
||||||
|
self.api_key = api_key
|
||||||
|
|
||||||
|
def __request_json_post__ (self, object: dict):
|
||||||
|
|
||||||
|
"""
|
||||||
|
## HTTP JSON POST
|
||||||
|
---
|
||||||
|
Este método é responsável por realizar requisições HTTP do tipo POST para objetos JSON.
|
||||||
|
|
||||||
|
Ele retorna o corpo de resposta da requisição, ou uma mensagem de erro, que indica qual foi a irregularidade ocorrida ao chamar a API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url = self.ep + "/"
|
||||||
|
print("Calling: " + url)
|
||||||
|
|
||||||
|
headers = {"X-API-KEY": self.api_key}
|
||||||
|
res = requests.post(url, json = object, headers = headers, verify = False)
|
||||||
|
|
||||||
|
if res.status_code >= 400:
|
||||||
|
raise Exception(f"HTTP ERROR: {str(res.status_code)} - {res.text}")
|
||||||
|
if res.headers.get("Content-Type") != None and res.headers.get("Content-Type").find("json") != -1:
|
||||||
|
return json.loads(res.text)
|
||||||
|
else:
|
||||||
|
return res.text
|
||||||
|
|
||||||
|
def do (self, dataAPI_request: dict):
|
||||||
|
"""
|
||||||
|
## DataAPI Do
|
||||||
|
Este método é responsável pela execução dos comandos SQL no Banco de Dados remoto.
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Parâmetros de dataAPI_request:
|
||||||
|
- Col: Nome da tabela SQL em que se quer realizar a operação (OBRIGATÓRIO)
|
||||||
|
- Op: Operação que se quer desenvolver. São elas: "R", "C", "D", "U" (OBRIGATÓRIO)
|
||||||
|
- R: Retrieve (Busca informações da coluna da tabela)
|
||||||
|
- C: Create (Cria uma nova tabela)
|
||||||
|
- D: Delete (Deleta uma tabela ou as colunas de uma tabela)
|
||||||
|
- U: Update (Atualiza as informações de uma tabela)
|
||||||
|
|
||||||
|
- Q: Nome do atributo (coluna) que se quer consultar ou alterar na tabela SQL
|
||||||
|
- Id: Identificador da instância (linha) da tabela que se quer realizar a operação
|
||||||
|
- Data: Dados que se quer inserir na tabela
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Retorna:
|
||||||
|
-> O retorno do comando feito, qualquer que seja ele.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.__request_json_post__(dataAPI_request)
|
||||||
|
|
||||||
|
def registrar_exec (self, register_table: str, check: bool):
|
||||||
|
"""
|
||||||
|
## Registrar Execução
|
||||||
|
Esta função é utilizada para realizar um registro de execuções falhas e bem sucedidas de um robô. Tudo é feito diretamente na Base de Dados remota, eliminando a necessidade de planilhas Excel locais.
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Parâmetros:
|
||||||
|
- register_table: Tabela utilizada para os registros de execução. Para começos de projeto, procure criar uma nova tabela, cheque se ela já existe utilizando a operação de Retrieve pelo método Do.
|
||||||
|
- check: Assume dois valores dependendo do sucesso da execução do robô: True ou False.
|
||||||
|
- True: Execução obteve sucesso.
|
||||||
|
- False: Execução falhou em algum ponto.
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Retorna:
|
||||||
|
---
|
||||||
|
"""
|
||||||
|
|
||||||
|
today_date = datetime.now().isoformat()[:10]
|
||||||
|
|
||||||
|
table_check = self.do({
|
||||||
|
"Col": register_table,
|
||||||
|
"Op": "R",
|
||||||
|
"Q": f"@[?date=='{today_date}']"
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(table_check["data"]) > 0:
|
||||||
|
data = table_check["data"][0]
|
||||||
|
id = str(int(data["ID"]))
|
||||||
|
|
||||||
|
if check:
|
||||||
|
success = int(data["exec"]) + 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.do({
|
||||||
|
"Col": register_table,
|
||||||
|
"Op": "U",
|
||||||
|
"Id": id,
|
||||||
|
"Data": {
|
||||||
|
"exec": success
|
||||||
|
}
|
||||||
|
})
|
||||||
|
except:
|
||||||
|
return "Algo falhou ao registrar a execução"
|
||||||
|
|
||||||
|
else:
|
||||||
|
fail = int(data["err"]) + 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.do({
|
||||||
|
"Col": register_table,
|
||||||
|
"Op": "U",
|
||||||
|
"Id": id,
|
||||||
|
"Data": {
|
||||||
|
"err": fail
|
||||||
|
}
|
||||||
|
})
|
||||||
|
except:
|
||||||
|
return "Algo falhou ao registrar a execução"
|
||||||
|
|
||||||
|
else:
|
||||||
|
if check:
|
||||||
|
success = 1
|
||||||
|
fail = 0
|
||||||
|
else:
|
||||||
|
success = 0
|
||||||
|
fail = 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.do({
|
||||||
|
"Col": register_table,
|
||||||
|
"Op": "C",
|
||||||
|
"Data": {
|
||||||
|
"date": today_date,
|
||||||
|
"exec": success,
|
||||||
|
"err": fail,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
except:
|
||||||
|
return "Algo falhou ao registrar a execução"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
Feature: The Data API Client
|
||||||
|
Scenario: Testing the data_API.do() method
|
||||||
|
Given a data_API client
|
||||||
|
When a new table is created with the value "Bond" for the query "the_name_is"
|
||||||
|
Then the value retrieved from the new table associated with the query "the_name_is" must be "Bond"
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Testing the data_API.registrar_exec method
|
||||||
|
Given a data_API client
|
||||||
|
When the registrar_exec method is called with True for check
|
||||||
|
And the registrar_exec method is called with False for check
|
||||||
|
And data is retrieved from the table
|
||||||
|
Then there must be at least 1 register for "exec" and "err" today
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
from behave import *
|
||||||
|
from cli import *
|
||||||
|
|
||||||
|
# ============================= DATA_API DO =============================
|
||||||
|
@given(u'a data_API client')
|
||||||
|
def step_impl(context):
|
||||||
|
context.client = DataAPI ("Insira aqui a chave de API. Você pode obtê-la pedindo ao fornecedor deste client.")
|
||||||
|
|
||||||
|
@when(u'a new table is created with the value "Bond" for the query "the_name_is"')
|
||||||
|
def step_impl(context):
|
||||||
|
context.client.do ({
|
||||||
|
"Col": "newTestTable",
|
||||||
|
"Op": "C",
|
||||||
|
"Data": {
|
||||||
|
"the_name_is": "Bond",
|
||||||
|
"James": "Bond"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
@then(u'the value retrieved from the new table associated with the query "the_name_is" must be "Bond"')
|
||||||
|
def step_impl(context):
|
||||||
|
context.retrieved_data = context.client.do({
|
||||||
|
"Col": "newTestTable",
|
||||||
|
"Op": "R"
|
||||||
|
})["data"][0]
|
||||||
|
|
||||||
|
context.query_value = context.retrieved_data["the_name_is"]
|
||||||
|
|
||||||
|
assert context.query_value == "Bond", "Something went wrong with the method Do"
|
||||||
|
|
||||||
|
|
||||||
|
# ============================= DATA_API REGISTRAR_EXEC =============================
|
||||||
|
@when(u'the registrar_exec method is called with True for check')
|
||||||
|
def step_impl(context):
|
||||||
|
context.client.registrar_exec("anotherNewTestTable", True)
|
||||||
|
|
||||||
|
@when(u'the registrar_exec method is called with False for check')
|
||||||
|
def step_impl(context):
|
||||||
|
context.client.registrar_exec("anotherNewTestTable", False)
|
||||||
|
|
||||||
|
@when(u'data is retrieved from the table')
|
||||||
|
def step_impl(context):
|
||||||
|
today_date = datetime.now().isoformat()[:10]
|
||||||
|
|
||||||
|
context.retrieved_data = context.client.do({
|
||||||
|
"Col": "anotherNewTestTable",
|
||||||
|
"Op": "R",
|
||||||
|
"Q": f"@[?date=='{today_date}']"
|
||||||
|
})["data"][0]
|
||||||
|
|
||||||
|
@then(u'there must be at least 1 register for "exec" and "err" today')
|
||||||
|
def step_impl(context):
|
||||||
|
assert context.retrieved_data["exec"] > 0 and context.retrieved_data["err"] > 0, "Something went wrong with the method registrar_exec"
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
class Data_sync:
|
||||||
|
"""
|
||||||
|
## Data Sync
|
||||||
|
---
|
||||||
|
?
|
||||||
|
"""
|
||||||
|
|
||||||
|
ep: str = ""
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
self.ep = "https://localhost:8443"
|
||||||
|
|
||||||
|
def __request_json_post__ (self, path: str, object: dict):
|
||||||
|
|
||||||
|
"""
|
||||||
|
## HTTP JSON POST
|
||||||
|
---
|
||||||
|
Este método é responsável por realizar requisições HTTP do tipo POST para objetos JSON.
|
||||||
|
|
||||||
|
Ele retorna o corpo de resposta da requisição, ou uma mensagem de erro, que indica qual foi a irregularidade ocorrida ao chamar a API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url = self.ep + path
|
||||||
|
print("Calling: " + url)
|
||||||
|
|
||||||
|
apikey = os.environ.get('REPLAY_APIKEY')
|
||||||
|
headers = {"X-API-KEY": apikey}
|
||||||
|
res = requests.post(url, json = object, headers = headers, verify = False)
|
||||||
|
|
||||||
|
if res.status_code >= 400:
|
||||||
|
raise Exception(f"HTTP ERROR: {str(res.status_code)} - {res.text}")
|
||||||
|
if res.headers.get("Content-Type") != None and res.headers.get("Content-Type").find("json") != -1:
|
||||||
|
return json.loads(res.text)
|
||||||
|
else:
|
||||||
|
return res.text
|
||||||
|
|
||||||
|
|
||||||
|
def new (self, data_sync_report: dict):
|
||||||
|
"""
|
||||||
|
???
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.__request_json_post__("/ipc/datasyncreportmgr/new", data_sync_report)
|
||||||
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from time import time
|
|
||||||
import requests
|
import requests
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
@@ -546,31 +545,3 @@ class Replay:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
return self.__request_get__(f"/api/v1/robots/op/enqueue/{job_id}")
|
return self.__request_get__(f"/api/v1/robots/op/enqueue/{job_id}")
|
||||||
|
|
||||||
def error (self, err_msg: str, desc: str):
|
|
||||||
"""
|
|
||||||
## Error
|
|
||||||
Este método é utilizado para postar erros relacionados ao programa, não às APIs, na base de dados do Replay.
|
|
||||||
|
|
||||||
---
|
|
||||||
#### Parâmetros:
|
|
||||||
- err_msg: Mensagem de erro que será registrada. Pense nela como um título, deve ser curto e direto ao ponto.
|
|
||||||
- desc: Descrição do erro ocorrido. Aqui devem ser dados mais detalhes acerca do que ocorreu de errado.
|
|
||||||
|
|
||||||
---
|
|
||||||
#### Retorna:
|
|
||||||
- ?
|
|
||||||
"""
|
|
||||||
|
|
||||||
robot_data = self.queue_get_my_data ()
|
|
||||||
|
|
||||||
error = {
|
|
||||||
"Feature": self.replay_env_alias (),
|
|
||||||
"Err": err_msg,
|
|
||||||
"When": datetime.now().isoformat() + "Z",
|
|
||||||
"Stack": "",
|
|
||||||
"InputData": robot_data,
|
|
||||||
"Details": desc
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.__request_json_post__("/api/v1/err", error)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user