Skip to content

Commit

Permalink
adt: generate class testincludes upon writing error
Browse files Browse the repository at this point in the history
Fixes #14

---v2 (palubaj)
Added tests and fixed lint errors.
  • Loading branch information
jfilak committed Sep 8, 2023
1 parent 2839463 commit 0eed14e
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 2 deletions.
10 changes: 10 additions & 0 deletions sap/adt/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ def __str__(self):
return f'{self.message}'


class ExceptionResourceSaveFailure(ADTError):
"""Thin wrapper for the class type of ADTErrors"""

def __init__(self, message):
super().__init__('com.sap.adt', self.__class__.__name__, message)

def __str__(self):
return f'{self.message}'


def new_adt_error_from_xml(xmldata):
"""Parses the xml data and create the correct instance.
Expand Down
49 changes: 48 additions & 1 deletion sap/adt/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,53 @@ def get_headers(self):
return headers


class ClassIncludeEditor(ADTObjectSourceEditorWithResponse):
"""Source Code Editor"""

def __init__(self, include_type, instance, lock_handle=None, corrnr=None):
super().__init__(instance, lock_handle=lock_handle, corrnr=corrnr)

self.include_type = include_type

def generate_includes(self):
"""Generates the corresponding Class Inlcude"""

self.connection.execute(
'POST',
self.uri + '/includes',
headers={'content-type': 'application/vnd.sap.adt.oo.classincludes+xml'},
params=modify_object_params(self.lock_handle, self.corrnr),
body='<?xml version="1.0" encoding="UTF-8"?>'
'<class:abapClassInclude xmlns:class="http://www.sap.com/adt/oo/classes"'
' xmlns:adtcore="http://www.sap.com/adt/core" adtcore:name="dummy"'
f' class:includeType="{self.include_type}"/>'
)

def write(self, content):
"""Tries to write twice. If the first attempt fails, the method blindly
tries to generate the includes (because that is the most commont
root cause of failed writes) and repeats the write attempt.
"""

try:
super().write(content)
# Hooray, the class include exists!!!
return
except sap.adt.errors.ExceptionResourceSaveFailure as ex:
# Oops, the class include most probably does not exist.
mod_log().debug("Writing of the class include has failed: %s", str(ex))
# But we do not give up as that is expected.
# When you create an ABAP class in SAPGUI (or pull by abapGit)
# and then you try to write its test classes over ADT API
# you get the caught error because the test classes include
# was not generated automatically.

# So lets try to generate the class include ...
self.generate_includes()
# and give it one more try - this time unprotected to die on errors
super().write(content)


class ADTObjectPropertyEditor(ADTObjectEditor):
"""Object property modification actions - for objects which have only the
XML and other resource associated.
Expand Down Expand Up @@ -1209,7 +1256,7 @@ def open_editor(self, lock_handle=None, corrnr=None):
if lock_handle is None:
lock_handle = self.lock()

return self._clas.objtype.open_editor(self, lock_handle=lock_handle, corrnr=corrnr)
return ClassIncludeEditor(self.objtype.include_type, self, lock_handle=lock_handle, corrnr=corrnr)

def fetch(self):
"""Retrieve data from ADT"""
Expand Down
4 changes: 4 additions & 0 deletions test/unit/fixtures_adt_clas.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@
<atom:link xmlns:atom="http://www.w3.org/2005/Atom" href="source/main" rel="http://www.sap.com/adt/relations/source" type="text/html" etag="20190202130106001000001"/>
</class:include>
</class:abapClass>'''

WRITE_INCLUDE_ERROR_XML = '''<?xml version="1.0" encoding="utf-8"?><exc:exception xmlns:exc="http://www.sap.com/abapxml/types/communicationframework"><namespace id="com.sap.adt"/><type id="ExceptionResourceSaveFailure"/><message lang="EN">TEST=====CCAU does not have any inactive version</message></exc:exception>'''

GENERATE_INCLUDE_REQUEST_XML = '''<?xml version="1.0" encoding="UTF-8"?><class:abapClassInclude xmlns:class="http://www.sap.com/adt/oo/classes" xmlns:adtcore="http://www.sap.com/adt/core" adtcore:name="dummy" class:includeType="testclasses"/>'''
21 changes: 20 additions & 1 deletion test/unit/test_sap_adt_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from fixtures_adt import (LOCK_RESPONSE_OK, EMPTY_RESPONSE_OK, TEST_CLASSES_READ_RESPONSE_OK,
DEFINITIONS_READ_RESPONSE_OK, IMPLEMENTATIONS_READ_RESPONSE_OK)

from fixtures_adt_clas import CREATE_CLASS_ADT_XML, GET_CLASS_ADT_XML
from fixtures_adt_clas import (CREATE_CLASS_ADT_XML, GET_CLASS_ADT_XML, WRITE_INCLUDE_ERROR_XML,
GENERATE_INCLUDE_REQUEST_XML)


FIXTURE_CLASS_MAIN_CODE='''class zcl_hello_world definition public.
Expand Down Expand Up @@ -135,6 +136,24 @@ def test_adt_class_write_implementations(self):
def test_adt_class_write_tests(self):
self.include_write_test(lambda clas: clas.test_classes, 'includes/testclasses')

def test_adt_class_write_include_with_generate(self):
GENERATE_INCLUDE_REQUEST_ID = 2
GENERATE_INCLUDE_ADT_URI = '/sap/bc/adt/oo/classes/zcl_hello_world/includes'
WRITE_GENERATED_INCLUDE_REQUEST_ID = 3

conn = Connection([LOCK_RESPONSE_OK,
Response(status_code=405, headers={'content-type': 'application/xml'}, text=WRITE_INCLUDE_ERROR_XML),
EMPTY_RESPONSE_OK, # generate test class
EMPTY_RESPONSE_OK, # write test class
None])
clas = sap.adt.Class(conn, 'ZCL_HELLO_WORLD')
with clas.test_classes.open_editor() as editor:
editor.write('* new test')

self.assertEqual(conn.execs[GENERATE_INCLUDE_REQUEST_ID].adt_uri, GENERATE_INCLUDE_ADT_URI)
self.assertEqual(conn.execs[GENERATE_INCLUDE_REQUEST_ID].body, GENERATE_INCLUDE_REQUEST_XML)
self.assertEqual(conn.execs[WRITE_GENERATED_INCLUDE_REQUEST_ID].body, b'* new test')

def include_activate_test(self, getter, includes_uri):
conn = Connection(
[
Expand Down

0 comments on commit 0eed14e

Please sign in to comment.