Skip to content

Commit

Permalink
Login method fix, tests
Browse files Browse the repository at this point in the history
  • Loading branch information
davidesner committed May 20, 2024
1 parent 7dda285 commit 52bb982
Show file tree
Hide file tree
Showing 20 changed files with 152 additions and 73 deletions.
74 changes: 37 additions & 37 deletions python-sync-actions/src/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def run(self):
Main component method
"""
# self.make_call()
pass
self.test_request()

def init_component(self):

Expand Down Expand Up @@ -179,42 +179,42 @@ def _fill_placeholders(self, placeholders, path):
result_path = result_path.replace(f"{{{dict.get('placeholder')}}}", str(dict.get('value')))
return result_path

def _process_nested_job(self, parent_result, config, parent_results_list, client,
method, **request_parameters) -> list:
"""
Process nested job
Args:
parent_result: result of the parent job
config: configuration of the nested job
parent_results_list: list of parent results
client: http client
method: method to use
request_parameters: request parameters
"""
results = []
for row in parent_result or [{}]:

parent_results_ext = parent_results_list + [row]

placeholders = PlaceholdersUtils.get_params_for_child_jobs(config.get('placeholders', {}),
parent_results_ext, self._parent_params)

self._parent_params = placeholders[0]
row_path = self._fill_placeholders(placeholders, config['endpoint'])
response = client.send_request(method=method, endpoint_path=row_path, **request_parameters)
child_response = self._parse_data(response.json(), DataPath(config.get('dataType'),
config.get('dataField', '.')))
children = config.get('children', [])
results = []
if children[0] if children else None:
nested_data = self._process_nested_job(child_response, children[0], parent_results_ext,
client, method, **request_parameters)
results.append(nested_data)
else:
self._final_results.append(child_response)
self._final_response = response

return results
# def _process_nested_job(self, parent_result, config, parent_results_list, client,
# method, **request_parameters) -> list:
# """
# Process nested job
# Args:
# parent_result: result of the parent job
# config: configuration of the nested job
# parent_results_list: list of parent results
# client: http client
# method: method to use
# request_parameters: request parameters
# """
# results = []
# for row in parent_result or [{}]:
#
# parent_results_ext = parent_results_list + [row]
#
# placeholders = PlaceholdersUtils.get_params_for_child_jobs(config.get('placeholders', {}),
# parent_results_ext, self._parent_params)
#
# self._parent_params = placeholders[0]
# row_path = self._fill_placeholders(placeholders, config['endpoint'])
# response = client.send_request(method=method, endpoint_path=row_path, **request_parameters)
# child_response = self._parse_data(response.json(), DataPath(config.get('dataType'),
# config.get('dataField', '.')))
# children = config.get('children', [])
# results = []
# if children[0] if children else None:
# nested_data = self._process_nested_job(child_response, children[0], parent_results_ext,
# client, method, **request_parameters)
# results.append(nested_data)
# else:
# self._final_results.append(child_response)
# self._final_response = response
#
# return results

def _parse_data(self, data, path) -> list:
"""
Expand Down
35 changes: 27 additions & 8 deletions python-sync-actions/src/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import re
import time
import urllib.parse as urlparse
from dataclasses import dataclass, field
from enum import Enum
from typing import List, Tuple, Optional, Literal
Expand Down Expand Up @@ -289,7 +290,7 @@ def build_api_request(configuration: dict) -> List[Tuple[ApiRequest, RequestCont
path = data_field.get('path')
delimiter = data_field.get("delimiter", ".")
else:
path = data_field
path = data_field or '.'
delimiter = "."

result_requests.append(
Expand Down Expand Up @@ -347,14 +348,17 @@ def convert(cls, config_parameters: dict) -> Authentication | None:
config_parameters (dict):
"""
auth_method = config_parameters.get('config', {}).get('__AUTH_METHOD', None)
# or take it form the authentication section
auth_method = auth_method or config_parameters.get('api', {}).get('authentication', {}).get('type')
if not auth_method:
return None

methods = {
'basic': cls._convert_basic,
'bearer': cls._convert_bearer,
'api-key': cls._convert_api_key,
'query': cls._convert_query
'query': cls._convert_query,
'login': cls._convert_login
}

func = methods.get(auth_method)
Expand Down Expand Up @@ -402,14 +406,29 @@ def _convert_login(cls, config_parameters: dict) -> Authentication:
# evaluate functions and user parameters
login_request_eval = ConfigHelpers().fill_in_user_parameters(login_request, config_parameters.get('config'))
api_request_eval = ConfigHelpers().fill_in_user_parameters(api_request, config_parameters.get('config'))




if not login_request:
raise ValueError('loginRequest configuration not found in the Login Authentication configuration')

return Authentication(type='Login', parameters={'#token': token})
raise ValueError('loginRequest configuration not found in the Login 88Authentication configuration')

login_endpoint: str = login_request_eval.get('endpoint')
login_url = urlparse.urljoin(config_parameters.get('api', {}).get('baseUrl', ''), login_endpoint)
method: str = login_request_eval.get('method', 'GET')
login_request_content: RequestContent = build_request_content(method, login_request_eval.get('params', {}))
login_query_parameters: dict = login_request_content.query_parameters
login_headers: dict = login_request_eval.get('headers', {})
api_request_headers: dict = api_request_eval.get('headers', {})
api_request_query_parameters: dict = api_request_eval.get('params', {})

parameters = {'login_endpoint': login_url,
'method': method,
'login_query_parameters': login_query_parameters,
'login_headers': login_headers,
'login_query_body': login_request_content.body,
'login_content_type': login_request_content.content_type.value,
'api_request_headers': api_request_headers,
'api_request_query_parameters': api_request_query_parameters}

return Authentication(type='Login', parameters=parameters)


class ConfigHelpers:
Expand Down
38 changes: 21 additions & 17 deletions python-sync-actions/src/http_generic/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import requests
from requests.auth import AuthBase, HTTPBasicAuth

from configuration import RequestContent, ContentType
from configuration import ContentType
from placeholders_utils import get_data_from_path


Expand Down Expand Up @@ -54,21 +54,22 @@ def build(cls, method_name: str, **parameters):
return supported_actions[method_name](**parameters)

@staticmethod
def _validate_method_arguments(method: object, **args):
class_prefix = f"_{method.__name__}__"
arguments = [p for p in inspect.signature(method.__init__).parameters if p != 'self']
def _validate_method_arguments(c_converted_method: object, **args):
class_prefix = f"_{c_converted_method.__name__}__"
arguments = [p for p in inspect.signature(c_converted_method.__init__).parameters if p != 'self']
missing_arguments = []
for p in arguments:
if p not in args:
missing_arguments.append(p.replace(class_prefix, '#'))
if missing_arguments:
raise AuthBuilderError(f'Some arguments of method {method.__name__} are missing: {missing_arguments}')
raise AuthBuilderError(f'Some arguments of method {c_converted_method.__name__} '
f'are missing: {missing_arguments}')

@staticmethod
def _convert_secret_parameters(method: object, **parameters):
def _convert_secret_parameters(c_converted_method: object, **parameters):
new_parameters = {}
for p in parameters:
new_parameters[p.replace('#', f'_{method.__name__}__')] = parameters[p]
new_parameters[p.replace('#', f'_{c_converted_method.__name__}__')] = parameters[p]
return new_parameters

@staticmethod
Expand Down Expand Up @@ -166,8 +167,9 @@ def __call__(self, r):

class Login(AuthMethodBase, AuthBase):
def __init__(self, login_endpoint: str, method: str = 'GET',
login_request_content: RequestContent = None,
login_query_parameters: dict = None,
login_query_body=None,
login_content_type: str = ContentType.json.value,
login_headers: dict = None,
api_request_headers: dict = None, api_request_query_parameters: dict = None):
"""
Expand All @@ -183,7 +185,8 @@ def __init__(self, login_endpoint: str, method: str = 'GET',
self.login_endpoint = login_endpoint
self.method = method
self.login_query_parameters = login_query_parameters or {}
self.login_request_content = login_request_content
self.login_query_body = login_query_body
self.login_content_type = ContentType(login_content_type)
self.login_headers = login_headers or {}
self.api_request_headers = api_request_headers or {}
self.api_request_query_parameters = api_request_query_parameters or {}
Expand Down Expand Up @@ -231,20 +234,19 @@ def _replace_placeholders_with_response(self, response_data: dict, source_object
source_object_params_str = json.dumps(source_object_params, separators=(',', ':'))
for path, placeholder in response_placeholders.items():
lookup_str = '{"response":"' + placeholder + '"}'
value_to_replace = get_data_from_path(path, response_data, separator='.')
value_to_replace = get_data_from_path(placeholder, response_data, separator='.', strict=False)
source_object_params_str = source_object_params_str.replace(lookup_str, '"' + value_to_replace + '"')
return json.loads(source_object_params_str)

def login(self) -> Union[AuthBase, Callable]:
request_parameters = {}

self._retrieve_response_placeholders(self.login_request_content.body)
if self.login_request_content.content_type == ContentType.json:
request_parameters['json'] = self.login_request_content.body
elif self.login_request_content.content_type == ContentType.form:
request_parameters['data'] = self.login_request_content.body
if self.login_content_type == ContentType.json:
request_parameters['json'] = self.login_query_body
elif self.login_content_type == ContentType.form:
request_parameters['data'] = self.login_query_body

response = requests.request(self.method, self.login_endpoint, params=self.login_headers,
response = requests.request(self.method, self.login_endpoint, params=self.login_query_parameters,
headers=self.login_headers,
**request_parameters)

Expand All @@ -257,6 +259,8 @@ def login(self) -> Union[AuthBase, Callable]:

def __call__(self, r):

r.url = f"{r.url}?{urlencode(self.api_request_query_parameters)}"
r.url = f"{r.url}"
if self.api_request_query_parameters:
r.url = f"{r.url}?{urlencode(self.api_request_query_parameters)}"
r.headers.update(self.api_request_headers)
return r
2 changes: 1 addition & 1 deletion python-sync-actions/src/placeholders_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def get_data_from_path(json_path: str, data: Dict[str, Any], separator: str = '.
for key in keys:
if key not in data:
if strict:
raise NoDataFoundException(f"Key '{key}' not found in data.")
raise NoDataFoundException(f"Key '{key}' not found in login data.")
return None
data = data[key]
return data
2 changes: 1 addition & 1 deletion python-sync-actions/tests/calls/000-bearer/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"#__BEARER_TOKEN": "token"
},
"api": {
"baseUrl": "http://localhost:8888/",
"baseUrl": "http://mock-server:80/",
"http": {
"headers": {
"Authorization": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"#__AUTH_TOKEN": "token"
},
"api": {
"baseUrl": "http://localhost:8888/",
"baseUrl": "http://mock-server:80/",
"http": {
"headers": {
"key": {
Expand Down
2 changes: 1 addition & 1 deletion python-sync-actions/tests/calls/002-token-body/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"#__AUTH_TOKEN": "token"
},
"api": {
"baseUrl": "http://localhost:8888/",
"baseUrl": "http://mock-server:80/",
"http": {
"defaultOptions": {
"params": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
accept: */*
content-length: 1
accept: */*
2 changes: 1 addition & 1 deletion python-sync-actions/tests/calls/003-basic/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"#password": "pass"
},
"api": {
"baseUrl": "http://localhost:8888/",
"baseUrl": "http://mock-server:80/",
"authentication": {
"type": "basic"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"#password": "pass"
},
"api": {
"baseUrl": "http://localhost:8888/",
"baseUrl": "http://mock-server:80/",
"authentication": {
"type": "basic"
}
Expand Down
2 changes: 1 addition & 1 deletion python-sync-actions/tests/calls/004-nested/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"#password": "pass"
},
"api": {
"baseUrl": "http://localhost:8888/",
"baseUrl": "http://mock-server:80/",
"authentication": {
"type": "basic"
}
Expand Down
2 changes: 1 addition & 1 deletion python-sync-actions/tests/calls/005-query/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"__AUTH_METHOD": "query"
},
"api": {
"baseUrl": "http://localhost:8888",
"baseUrl": "http://mock-server:80",
"http": {
"defaultOptions": {
"params": {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
accept: */*
accept: */*
36 changes: 36 additions & 0 deletions python-sync-actions/tests/calls/006-login-auth-headers/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"parameters": {
"__SELECTED_JOB": "0",
"api": {
"baseUrl": "http://mock-server:80/006-login-auth-headers/",
"authentication": {
"type": "login",
"loginRequest": {
"endpoint": "login",
"method": "GET",
"headers": {
"X-Login": "JohnDoe",
"X-Password": "TopSecret"
}
},
"apiRequest": {
"headers": {
"X-ApiToken": {
"response": "authorization.token"
}
}
}
}
},
"config": {
"debug": true,
"outputBucket": "mock-server",
"jobs": [
{
"endpoint": "users"
}
]
}
},
"action": "test_request"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GET /006-login-auth-headers/login
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
X-Login: JohnDoe
X-Password: TopSecret
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"authorization": {
"token": "a1b2c3d435f6"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GET /006-login-auth-headers/users
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
accept: */*
x-apitoken: a1b2c3d435f6
Loading

0 comments on commit 52bb982

Please sign in to comment.