Skip to content

Commit

Permalink
Get tproject tplans (#14)
Browse files Browse the repository at this point in the history
* [qatl] moved exception to own package

+ added exceptions for auth API XMLRPC
+ removed old code

* [qatl] divided managers classes to own files

* [qatl] missing spaces FIX

* [qatl] added to models

+ missing properties for TProject object
+ added new model, TPlan

* [qatl] FIX for int params

* [qatl] added tl.getProjectTestPlans response

* [qatl] added test for tl.getProjectTestPlans
  • Loading branch information
netzulo committed Dec 25, 2017
1 parent 9b037f8 commit aeedb0e
Show file tree
Hide file tree
Showing 9 changed files with 393 additions and 194 deletions.
Empty file.
17 changes: 17 additions & 0 deletions qatestlink/core/exceptions/response_exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""TODO: doc module"""


class ResponseException(Exception):
"""Inherits exception, just for raises testlink XMLRPC errors"""

def __init__(self, code, log,
message='Response Exception message not defined at raise',
err=None):
"""Raise an exception from any part of qacode package"""
super(ResponseException, self).__init__(err, message)
self._code = code
self._message = message
log.error(
"Response exception detected: \n code={}\n message={}".format(
self._code, self._message))
95 changes: 94 additions & 1 deletion qatestlink/core/models/tl_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ class TProject(ModelBase):
id = None
name = None
is_public = None
notes = None
color = None
active = None
option_reqs = None
option_priority = None
option_automation = None
options = None
prefix = None
tc_counter = None
issue_tracker_enabled = None
reqmgr_integration_enabled = None
api_key = None
opt = None #noqa

def __init__(self, res_members):
"""TODO: doc method"""
Expand All @@ -41,10 +54,90 @@ def _load(self):
self.name = value
if name == 'is_public':
self.is_public = value
if name == 'notes':
self.notes = value
if name == 'color':
self.color = value
if name == 'active':
self.active = value
if name == 'option_reqs':
self.option_reqs = value
if name == 'option_priority':
self.option_priority = value
if name == 'option_automation':
self.option_automation = value
if name == 'options':
self.options = value
if name == 'prefix':
self.prefix = value
if name == 'tc_counter':
self.tc_counter = value
if name == 'issue_tracker_enabled':
self.issue_tracker_enabled = value
if name == 'reqmgr_integration_enabled':
self.reqmgr_integration_enabled = value
if name == 'api_key':
self.api_key = value
# not sure if is obtaining this member struct
if name == 'opt':
self.opt = value


def __repr__(self):
return "TProject: id={}, name={}, is_public={}".format(
self.id,
self.name,
self.is_public
)
)



class TPlan(ModelBase):
"""TODO: doc class"""

_res_members = None

# Testlink object properties
id = None
name = None
is_public = None
active = None
tproject_id = None
notes = None


def __init__(self, res_members):
"""TODO: doc method"""
super(TPlan, self).__init__()
if res_members is None:
raise Exception('Bad param, res_member can\'t be None')
if len(res_members) <= 0:
raise Exception(
'Bad param, res_member can\'t be empty list')
self._res_members = res_members
self._load()

def _load(self):
for res_member in self._res_members:
name = res_member.name
value = res_member.value
if name == 'id':
self.id = value
if name == 'name':
self.name = value
if name == 'is_public':
self.is_public = value
if name == 'active':
self.active = value
if name == 'testproject_id':
self.tproject_id = value
if name == 'notes':
self.notes = value

def __repr__(self):
return "TProject: id={}, name={}, is_public={}".format(
self.id,
self.name,
self.is_public
)

177 changes: 22 additions & 155 deletions qatestlink/core/testlink_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,11 @@
"""Testlink Managers"""


import json
import logging
import requests
from qatestlink.core.utils.Utils import read_file
from qatestlink.core.utils.logger_manager import LoggerManager
from qatestlink.core.connections.connection_base import ConnectionBase
from qatestlink.core.utils.Utils import read_file
from qatestlink.core.xmls.route_type import RouteType
from qatestlink.core.xmls.error_handler import ErrorHandler
from qatestlink.core.xmls.request_handler import RequestHandler
from qatestlink.core.xmls.response_handler import ResponseHandler
from qatestlink.core.xmls.base_handler import BaseHandler
from qatestlink.core.models.tl_models import TProject
from qatestlink.core.xmls.xmlrpc_manager import XMLRPCManager



PATH_CONFIG = 'qatestlink/configs/settings.json'
Expand Down Expand Up @@ -74,6 +67,7 @@ def api_login(self, dev_key=None):
dev_key = self._settings.get('dev_key')
req_data = self._xml_manager.req_check_dev_key(dev_key)
res = self._conn.post(self._xml_manager.headers, req_data)
self._xml_manager.parse_errors(res.text)
res_xml = self._xml_manager.res_check_dev_key(
res.status_code, res.text)
node_boolean = self._xml_manager.handler.find_node(
Expand All @@ -86,9 +80,10 @@ def api_tprojects(self, dev_key=None):
"""Call to method named 'tl.getProjects'"""
if dev_key is None:
dev_key = self._settings.get('dev_key')
req_data = self._xml_manager.req_get_tprojects(dev_key)
req_data = self._xml_manager.req_tprojects(dev_key)
res = self._conn.post(self._xml_manager.headers, req_data)
res_as_models = self._xml_manager.res_get_tprojects(
self._xml_manager.parse_errors(res.text)
res_as_models = self._xml_manager.res_tprojects(
res.status_code, res.text, as_models=True)
# TODO: filter by name and/or value
return res_as_models
Expand All @@ -97,151 +92,23 @@ def api_tproject(self, tproject_name, dev_key=None):
"""Call to method named 'tl.getTestProjectByName'"""
if dev_key is None:
dev_key = self._settings.get('dev_key')
req_data = self._xml_manager.req_get_tproject_by_name(
req_data = self._xml_manager.req_tproject_by_name(
dev_key, tproject_name)
res = self._conn.post(self._xml_manager.headers, req_data)
# TODO: parse errors on ALL requests
err = self._xml_manager.parse_errors(res.text)
res_as_model = self._xml_manager.res_get_tproject_by_name(
self._xml_manager.parse_errors(res.text)
res_as_model = self._xml_manager.res_tproject_by_name(
res.status_code, res.text, as_model=True)
return res_as_model


class XMLRPCManager(object):
"""
Manage all XMLRPCManager requests,
responses and handle errors. This class
store all official methods names used
on original XMLRPC php class
"""
_request_handler = None
_response_handler = None
_error_handler = None

log = None
headers = None
handler = None

def __init__(self, log):
self.log = log
self._request_handler = RequestHandler(self.log)
self._response_handler = ResponseHandler(self.log)
self._error_handler = ErrorHandler(self.log)
self.headers = {'Content-Type': 'application/xml'}
self.handler = BaseHandler(self.log)

def parse_errors(self, xml_str):
"""Raise an exception if response have error structure"""
#TODO: make enum and custom exception for each exception number
self._error_handler.get_response_error(xml_str)

def req_check_dev_key(self, dev_key):
"""
:return:
string xml object ready to use on API call
"""
req = self._request_handler.create(
RouteType.TLINK_CHECK_DEV_KEY)
return self._request_handler.create_param(
req, 'struct', 'devKey', dev_key)

def res_check_dev_key(self, status_code, res_str):
"""
:return:
string xml object ready to
parse/write/find/add Elements on it
"""
if status_code != 200:
raise Exception(
"status_code invalid: code={}".format(
status_code))
return self._response_handler.create(
RouteType.TLINK_CHECK_DEV_KEY, res_str)

def req_get_tprojects(self, dev_key):
"""
Obtains all test projects created on remote
testlink database,
TODO: can filter with any property+value combination
:return:
List of TProject objects containing all database
data loaded
"""
req = self._request_handler.create(
RouteType.TPROJECTS)
return self._request_handler.create_param(
req, 'struct', 'devKey', dev_key)

def res_get_tprojects(self, status_code, res_str, as_models=True):
"""
Parse and validate response for method
named 'tl.getProjects', by default response list
of TProject objects, can response xml string too
:return:
if as_models is True
list of objects instanced with
Model classes
if as_models is False
string xml object ready to
parse/write/find/add Elements on it
"""
if status_code != 200:
raise Exception(
"status_code invalid: code={}".format(
status_code))
res = self._response_handler.create(
RouteType.TPROJECTS, res_str)
if not as_models:
return res
res_members_list = self._response_handler.get_response_members(
xml_str=res)
tprojects = list()
for res_members in res_members_list:
tproject = TProject(res_members)
tprojects.append(tproject)
return tprojects


def req_get_tproject_by_name(self, dev_key, tproject_name):
"""
Obtains all test projects created on remote
testlink database, can filter by name
:return:
TProject object containing all database
data loaded
"""
if tproject_name is None:
raise Exception("Can't call XMLRPC without param, tproject_name")
req = self._request_handler.create(
RouteType.TPROJECT_BY_NAME)
req = self._request_handler.create_param(
req, 'struct', 'devKey', dev_key)
req = self._request_handler.add_param(
req, 'testprojectname', tproject_name)
return req

def res_get_tproject_by_name(self, status_code, res_str, as_model=True):
"""
Parse and validate response for method
named 'tl.getTestProjectByName', by default response
TProject object, can response xml string too
:return:
if as_models is True
object instanced with Model classes
if as_models is False
string xml object ready to
parse/write/find/add Elements on it
"""
if status_code != 200:
raise Exception(
"status_code invalid: code={}".format(
status_code))
res = self._response_handler.create(
RouteType.TPROJECT_BY_NAME, res_str)
if not as_model:
return res
res_members_list = self._response_handler.get_response_struct_members(
xml_str=res)
return TProject(res_members_list)
def api_tproject_tplans(self, tproject_id, dev_key=None):
"""Call to method named 'tl.getProjectTestPlans'"""
if dev_key is None:
dev_key = self._settings.get('dev_key')
req_data = self._xml_manager.req_tproject_tplans(
dev_key, tproject_id)
res = self._conn.post(self._xml_manager.headers, req_data)
self._xml_manager.parse_errors(res.text)
# TODO: make works
res_as_models = self._xml_manager.res_tproject_tplans(
res.status_code, res.text, as_models=True)
return res_as_models
14 changes: 1 addition & 13 deletions qatestlink/core/xmls/error_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from xml.etree.ElementTree import tostring as xml_to_str
from qatestlink.core.xmls.base_handler import BaseHandler
from qatestlink.core.exceptions.response_exception import ResponseException


class ErrorHandler(BaseHandler):
Expand Down Expand Up @@ -66,16 +67,3 @@ def get_response_error(self, xml_str):
raise ResponseException(code, self.log, message=message)


class ResponseException(Exception):
"""Inherits exception, just for raises testlink XMLRPC errors"""

def __init__(self, code, log,
message='Response Exception message not defined at raise',
err=None):
"""Raise an exception from any part of qacode package"""
super(ResponseException, self).__init__(err, message)
self._code = code
self._message = message
log.error(
"Response exception detected: \n code={}\n message={}".format(
self._code, self._message))
13 changes: 7 additions & 6 deletions qatestlink/core/xmls/request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,21 @@ def add_param(self, req_str, param_name, param_value):
obtained as string
"""
root = self.xml_parse(req_str)
# need to parse ints to string
# before to use on XMLs strings
param_value = str(param_value)
self.log.debug("Adding param:")
self.log.debug(" name={}".format(param_name))
self.log.debug(" value={}".format(param_value))
n_params = self.find_node('params', parent=root)
n_param = self.find_node('param', parent=n_params)
n_value = self.find_node('value', parent=n_param)
# TODO: type XML validation for params
# just struct handleded
# type XML validation for params
# just struct handled
n_struct = self.find_node('struct', parent=n_value)
n_member = self.create_node('member', parent=n_struct)
n_name = self.create_node(
'name', parent=n_member, text=param_name)
self.create_node('name', parent=n_member, text=param_name)
n_value = self.create_node('value', parent=n_member)
n_value_string = self.create_node(
'string', parent=n_value, text=param_value)
self.create_node('string', parent=n_value, text=param_value)
self.log.info(MSG_CREATED_XMLPARAM.format(xml_to_str(root)))
return xml_to_str(root)

0 comments on commit aeedb0e

Please sign in to comment.