diff --git a/doc/commands.md b/doc/commands.md
index e7e4d5f2..13a6b742 100644
--- a/doc/commands.md
+++ b/doc/commands.md
@@ -20,6 +20,7 @@
18. [flp](commands/flp.md) - Fiori Launchpad
19. [rap](commands/businessservice.md) - RAP Business Services
20. [strust](commands/strust.md) - SSL Certificates
-21. [structure](commands/structure.md) - ABAP DDIC structures
-22. [table](commands/table.md) - ABAP DDIC transparent tables
-23. [badi](commands/badi.md) - New style (Enhancements) BAdI operations
+21. [dataelement](commands/dataelement.md) - ABAP DDIC Data Elements
+22. [structure](commands/structure.md) - ABAP DDIC structures
+23. [table](commands/table.md) - ABAP DDIC transparent tables
+24. [badi](commands/badi.md) - New style (Enhancements) BAdI operations
\ No newline at end of file
diff --git a/doc/commands/dataelement.md b/doc/commands/dataelement.md
new file mode 100644
index 00000000..e0228025
--- /dev/null
+++ b/doc/commands/dataelement.md
@@ -0,0 +1,26 @@
+# Data Element
+
+- [Data Element](#data-element)
+ - [define](#define)
+
+## define
+
+Define an ABAP DDIC Data Element.
+
+```bash
+sapcli dataelement define DATA_ELEMENT_NAME --type=domain|predefinedAbapType [--corrnr TRANSPORT] [--activate] [--no-error-existing] [--domain_name] [--data_type] [--data_type_length] [--data_type_decimals] [--label_short] [--label_medium] [--label_long] [--label_heading]
+```
+
+* _DATA\_ELEMENT\_NAME_ specifying the name of the data element
+* _--type [domain|predefinedAbapType]_ type kind
+* _--domain\_name_ domain name (e.g. BUKRS) [default = ''] - mandatory in case the _--type_=domain **(optional)**
+* _--data\_type_ data type (e.g. CHAR) [default = ''] - mandatory in case the _--type_=predefinedAbapType **(optional)**
+* _--data\_type\_length_ data type length (e.g. 5) [default = '0'] **(optional)**
+* _--data\_type\_decimals_ data type decimals (e.g. 3) [default = '0'] **(optional)**
+* _--label\_short_ short label [default = ''] **(optional)**
+* _--label\_medium_ medium label [default = ''] **(optional)**
+* _--label\_long_ long label [default = ''] **(optional)**
+* _--label\_heading_ heading label [default = ''] **(optional)**
+* _--corrnr TRANSPORT_ specifies CTS Transport Request Number **(optional)**
+* _--activate_ activate after finishing the data element modification **(optional)**
+* _--no-error-existing_ do not fail if data element already exists **(optional)**
diff --git a/sap/adt/__init__.py b/sap/adt/__init__.py
index c173f07a..785e63e9 100644
--- a/sap/adt/__init__.py
+++ b/sap/adt/__init__.py
@@ -14,3 +14,4 @@
from sap.adt.table import Table # noqa: F401
from sap.adt.enhancement_implementation import EnhancementImplementation # noqa: F401
from sap.adt.structure import Structure # noqa: F401
+from sap.adt.dataelement import DataElement # noqa: F401
diff --git a/sap/adt/dataelement.py b/sap/adt/dataelement.py
new file mode 100644
index 00000000..d9a0a437
--- /dev/null
+++ b/sap/adt/dataelement.py
@@ -0,0 +1,185 @@
+"""ABAP Data Element ADT functionality module"""
+
+from sap.adt.objects import ADTObject, ADTObjectType, ADTCoreData, ADTObjectSourceEditor
+# pylint: disable=unused-import
+from sap.adt.annotations import OrderedClassMembers, xml_element, xml_text_node_property
+from sap.adt.objects import XMLNamespace, XMLNS_ADTCORE
+from sap.errors import SAPCliError
+
+XMLNS_DTEL = XMLNamespace('dtel', 'http://www.sap.com/adt/dictionary/dataelements')
+
+LABELS_LENGTH = {
+ 'short': '10',
+ 'medium': '20',
+ 'long': '40',
+ 'heading': '55'
+}
+
+
+class ADTDataElementData(ADTCoreData):
+ """Data Element nodes data.
+ """
+
+ # pylint: disable=too-many-instance-attributes
+ # pylint: disable=too-few-public-methods
+ class DataElement(metaclass=OrderedClassMembers):
+ """ADT Data Element data collector"""
+
+ type = xml_text_node_property('dtel:typeKind')
+ type_name = xml_text_node_property('dtel:typeName')
+ data_type = xml_text_node_property('dtel:dataType')
+ data_type_length = xml_text_node_property('dtel:dataTypeLength')
+ data_type_decimals = xml_text_node_property('dtel:dataTypeDecimals')
+ label_short = xml_text_node_property('dtel:shortFieldLabel')
+ label_short_length = xml_text_node_property('dtel:shortFieldLength')
+ label_short_max_length = xml_text_node_property('dtel:shortFieldMaxLength')
+ label_medium = xml_text_node_property('dtel:mediumFieldLabel')
+ label_medium_length = xml_text_node_property('dtel:mediumFieldLength')
+ label_medium_max_length = xml_text_node_property('dtel:mediumFieldMaxLength')
+ label_long = xml_text_node_property('dtel:longFieldLabel')
+ label_long_length = xml_text_node_property('dtel:longFieldLength')
+ label_long_max_length = xml_text_node_property('dtel:longFieldMaxLength')
+ label_heading = xml_text_node_property('dtel:headingFieldLabel')
+ label_heading_length = xml_text_node_property('dtel:headingFieldLength')
+ label_heading_max_length = xml_text_node_property('dtel:headingFieldMaxLength')
+ search_help = xml_text_node_property('dtel:searchHelp')
+ search_help_parameter = xml_text_node_property('dtel:searchHelpParameter')
+ set_get_parameter = xml_text_node_property('dtel:setGetParameter')
+ default_component_name = xml_text_node_property('dtel:defaultComponentName')
+ deactivate_input_history = xml_text_node_property('dtel:deactivateInputHistory')
+ change_document = xml_text_node_property('dtel:changeDocument')
+ left_to_right_direction = xml_text_node_property('dtel:leftToRightDirection')
+ deactivate_bidi_filtering = xml_text_node_property('dtel:deactivateBIDIFiltering')
+
+ # pylint: disable=too-many-arguments
+ def __init__(self, package=None, description=None, language=None,
+ master_language=None, master_system=None, responsible=None,
+ package_reference=None, abap_language_version=None):
+ super().__init__(package, description, language,
+ master_language, master_system, responsible,
+ package_reference, abap_language_version)
+
+ self._data_element = ADTDataElementData.DataElement()
+
+ @property
+ def data_element(self):
+ """The Data Element's reference"""
+
+ return self._data_element
+
+
+class DataElement(ADTObject):
+ """ABAP Data Element"""
+
+ OBJTYPE = ADTObjectType(
+ 'DTEL/DE',
+ 'ddic/dataelements',
+ XMLNamespace('blue', 'http://www.sap.com/wbobj/dictionary/dtel', parents=[XMLNS_ADTCORE, XMLNS_DTEL]),
+ 'application/vnd.sap.adt.dataelements.v2+xml',
+ {
+ 'application/vnd.sap.adt.dataelements.v2+xml': '',
+ 'application/vnd.sap.adt.dataelements.v1+xml': ''
+ },
+ 'wbobj',
+ editor_factory=ADTObjectSourceEditor
+ )
+
+ def __init__(self, connection, name, package=None, metadata=None):
+ super().__init__(connection, name, metadata, active_status='inactive')
+
+ self._metadata = ADTDataElementData(
+ metadata.package, metadata.description,
+ metadata.language, metadata.master_language,
+ metadata.master_system, metadata.responsible,
+ metadata.package_reference.name if metadata.package_reference is not None else None,
+ metadata.abap_language_version
+ ) if metadata is not None else ADTDataElementData()
+
+ self._metadata.package_reference.name = package
+
+ @xml_element('dtel:dataElement')
+ def data_element(self):
+ """The Data Element's reference"""
+
+ return self._metadata.data_element
+
+ def set_type(self, value):
+ """Setter for Type Kind element"""
+
+ self._metadata.data_element.type = value
+
+ def set_type_name(self, value):
+ """Setter for Type Name element"""
+
+ self._metadata.data_element.type_name = value.upper() if value is not None else None
+
+ def set_data_type(self, value):
+ """Setter for Data Type element"""
+
+ self._metadata.data_element.data_type = value.upper() if value is not None else None
+
+ def set_data_type_length(self, value):
+ """Setter for Data Type Length element"""
+
+ self._metadata.data_element.data_type_length = value
+
+ def set_data_type_decimals(self, value):
+ """Setter for Data Type Decimals element"""
+
+ self._metadata.data_element.data_type_decimals = value
+
+ def set_label_short(self, value):
+ """Setter for Label Short element"""
+
+ self._metadata.data_element.label_short = value
+
+ def set_label_medium(self, value):
+ """Setter for Label Medium element"""
+
+ self._metadata.data_element.label_medium = value
+
+ def set_label_long(self, value):
+ """Setter for Label Long element"""
+
+ self._metadata.data_element.label_long = value
+
+ def set_label_heading(self, value):
+ """Setter for Label Heading element"""
+
+ self._metadata.data_element.label_heading = value
+
+ def normalize(self):
+ """Validate Data Element setup before save"""
+ if self._metadata.data_element.type == 'domain':
+ self._metadata.data_element.data_type = ''
+ self._metadata.data_element.data_type_length = '0'
+ self._metadata.data_element.data_type_decimals = '0'
+ if self._metadata.data_element.type == 'predefinedAbapType':
+ self._metadata.data_element.type_name = ''
+
+ # pylint: disable=line-too-long
+ self._metadata.data_element.label_short_length = self._metadata.data_element.label_short_length if not self._metadata.data_element.label_short_length else LABELS_LENGTH['short']
+ # pylint: disable=line-too-long
+ self._metadata.data_element.label_medium_length = self._metadata.data_element.label_medium_length if not self._metadata.data_element.label_medium_length else LABELS_LENGTH['medium']
+ # pylint: disable=line-too-long
+ self._metadata.data_element.label_long_length = self._metadata.data_element.label_long_length if not self._metadata.data_element.label_long_length else LABELS_LENGTH['long']
+ # pylint: disable=line-too-long
+ self._metadata.data_element.label_heading_length = self._metadata.data_element.label_heading_length if not self._metadata.data_element.label_heading_length else LABELS_LENGTH['heading']
+
+ # pylint: disable=line-too-long
+ self._metadata.data_element.deactivate_input_history = self._metadata.data_element.deactivate_input_history if self._metadata.data_element.deactivate_input_history is not None else False
+ # pylint: disable=line-too-long
+ self._metadata.data_element.change_document = self._metadata.data_element.change_document if self._metadata.data_element.change_document is not None else False
+ # pylint: disable=line-too-long
+ self._metadata.data_element.left_to_right_direction = self._metadata.data_element.left_to_right_direction if self._metadata.data_element.left_to_right_direction is not None else False
+ # pylint: disable=line-too-long
+ self._metadata.data_element.deactivate_bidi_filtering = self._metadata.data_element.deactivate_bidi_filtering if self._metadata.data_element.deactivate_bidi_filtering is not None else False
+
+ def validate(self):
+ """Validate Data Element setup"""
+
+ if self._metadata.data_element.type == 'domain' and not self._metadata.data_element.type_name:
+ raise SAPCliError('Domain name must be provided (--domain_name) if the type (--type) is "domain"')
+ if self._metadata.data_element.type == 'predefinedAbapType' and not self._metadata.data_element.data_type:
+ # pylint: disable=line-too-long
+ raise SAPCliError('Data type name must be provided (--data_type) if the type (--type) is "predefinedAbapType"')
diff --git a/sap/adt/objects.py b/sap/adt/objects.py
index a192a046..e7701530 100644
--- a/sap/adt/objects.py
+++ b/sap/adt/objects.py
@@ -233,7 +233,9 @@ def get_uri_for_type(self, mimetype):
"""
try:
- return '/' + self._typeuris[mimetype]
+ mimetype_uri = self._typeuris[mimetype]
+
+ return '/' + mimetype_uri if mimetype_uri != '' else ''
except KeyError:
# pylint: disable=raise-missing-from
raise SAPCliError('Object {type} does not support plain \'text\' format')
diff --git a/sap/cli/__init__.py b/sap/cli/__init__.py
index 09d71495..62c8a3ef 100644
--- a/sap/cli/__init__.py
+++ b/sap/cli/__init__.py
@@ -47,6 +47,7 @@ def commands():
import sap.cli.table
import sap.cli.badi
import sap.cli.structure
+ import sap.cli.dataelement
if CommandsCache.adt is None:
CommandsCache.adt = [
@@ -69,6 +70,7 @@ def commands():
(adt_connection_from_args, sap.cli.rap.CommandGroup()),
(adt_connection_from_args, sap.cli.table.CommandGroup()),
(adt_connection_from_args, sap.cli.structure.CommandGroup()),
+ (adt_connection_from_args, sap.cli.dataelement.CommandGroup()),
(adt_connection_from_args, sap.cli.checkin.CommandGroup()),
(adt_connection_from_args, sap.cli.badi.CommandGroup()),
]
diff --git a/sap/cli/dataelement.py b/sap/cli/dataelement.py
new file mode 100644
index 00000000..2026148e
--- /dev/null
+++ b/sap/cli/dataelement.py
@@ -0,0 +1,109 @@
+"""ADT proxy for ABAP DDIC Data Element"""
+
+import sap.adt
+from sap.adt.errors import ExceptionResourceAlreadyExists
+import sap.cli.object
+import sap.cli.wb
+import sap.cli.core
+
+
+class CommandGroup(sap.cli.object.CommandGroupObjectMaster):
+ """Adapter converting command line parameters to sap.adt.DataElement methods
+ calls.
+ """
+
+ def __init__(self):
+ super().__init__('dataelement')
+
+ self.define()
+
+ def instance(self, connection, name, args, metadata=None):
+ package = None
+ if hasattr(args, 'package'):
+ package = args.package
+
+ return sap.adt.DataElement(connection, name.upper(), package=package, metadata=metadata)
+
+
+@CommandGroup.argument_corrnr()
+@CommandGroup.argument('--no-error-existing', action='store_true', default=False,
+ help='Do not fail if data element already exists')
+@CommandGroup.argument('-a', '--activate', action='store_true', default=False, help='Activate after modification')
+@CommandGroup.argument('-t', '--type', required=True, choices=['domain', 'predefinedAbapType'],
+ type=str, help='Type kind')
+@CommandGroup.argument('-d', '--domain_name', default=None, type=str, help='Domain name')
+@CommandGroup.argument('-dt', '--data_type', default=None, type=str, help='Data type')
+@CommandGroup.argument('-dtl', '--data_type_length', default='0', type=str, help='Data type length')
+@CommandGroup.argument('-dtd', '--data_type_decimals', default='0', type=str, help='Data type decimals')
+@CommandGroup.argument('-ls', '--label_short', default='', type=str, help='Short label')
+@CommandGroup.argument('-lm', '--label_medium', default='', type=str, help='Medium label')
+@CommandGroup.argument('-ll', '--label_long', default='', type=str, help='Long label')
+@CommandGroup.argument('-lh', '--label_heading', default='', type=str, help='Heading label')
+@CommandGroup.argument('package', help='Package assignment')
+@CommandGroup.argument('description', help='Data element description')
+@CommandGroup.argument('name', help='Data element name')
+@CommandGroup.command()
+def define(connection, args):
+ """Changes attributes of the given Data Element"""
+
+ console = sap.cli.core.get_console()
+
+ metadata = sap.adt.ADTCoreData(language='EN', master_language='EN', responsible=connection.user,
+ description=args.description)
+
+ dataelement = sap.adt.DataElement(connection, args.name.upper(), args.package, metadata=metadata)
+
+ # Create Data Element
+ console.printout(f'Creating data element {args.name}')
+ try:
+ dataelement.create(args.corrnr)
+ except ExceptionResourceAlreadyExists as error:
+ # Date Element already exists
+ console.printout(f'Data element {args.name} already exists')
+ if not args.no_error_existing:
+ raise error
+
+ # Fetch data element's content
+ dataelement.fetch()
+
+ if hasattr(args, 'type'):
+ dataelement.set_type(args.type)
+
+ if hasattr(args, 'domain_name'):
+ dataelement.set_type_name(args.domain_name)
+
+ if hasattr(args, 'data_type'):
+ dataelement.set_data_type(args.data_type)
+
+ if hasattr(args, 'data_type_length'):
+ dataelement.set_data_type_length(args.data_type_length)
+
+ if hasattr(args, 'data_type_decimals'):
+ dataelement.set_data_type_decimals(args.data_type_decimals)
+
+ if hasattr(args, 'label_short'):
+ dataelement.set_label_short(args.label_short)
+
+ if hasattr(args, 'label_medium'):
+ dataelement.set_label_medium(args.label_medium)
+
+ if hasattr(args, 'label_long'):
+ dataelement.set_label_long(args.label_long)
+
+ if hasattr(args, 'label_heading'):
+ dataelement.set_label_heading(args.label_heading)
+
+ dataelement.normalize()
+
+ dataelement.validate()
+
+ # Push Data Element changes
+ console.printout(f'Data element {args.name} setup performed')
+ with dataelement.open_editor(corrnr=args.corrnr) as editor:
+ editor.push()
+
+ # Activate Data Element
+ if args.activate:
+ console.printout(f'Data element {args.name} activation performed')
+ activator = sap.cli.wb.ObjectActivationWorker()
+ sap.cli.object.activate_object_list(activator, ((args.name, dataelement),), count=1)
diff --git a/test/unit/fixtures_adt_dataelement.py b/test/unit/fixtures_adt_dataelement.py
new file mode 100644
index 00000000..ca92cf34
--- /dev/null
+++ b/test/unit/fixtures_adt_dataelement.py
@@ -0,0 +1,187 @@
+DATA_ELEMENT_NAME = 'TEST_DATA_ELEMENT'
+
+DATA_ELEMENT_DEFINITION_ADT_XML = f'''
+
+
+
+
+
+
+
+
+ predefinedAbapType
+
+ STRING
+ 000000
+ 000000
+
+ 10
+ 10
+
+ 20
+ 20
+
+ 40
+ 40
+
+ 55
+ 55
+
+
+
+
+ false
+ false
+ false
+ false
+
+'''
+
+CREATE_DATA_ELEMENT_ADT_XML = f'''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+'''
+
+READ_DATA_ELEMENT_BODY = DATA_ELEMENT_DEFINITION_ADT_XML
+
+WRITE_DATA_ELEMENT_BODY = f'''
+
+
+
+
+
+
+
+
+
+ predefinedAbapType
+
+ STRING
+ 6
+ 0
+ Req. Add.
+ 10
+ 10
+ Request Address
+ 15
+ 20
+ Request Address
+ 20
+ 40
+ Request Address
+ 55
+ 55
+
+
+
+
+ false
+ false
+ false
+ false
+
+'''
+
+DEFINE_DATA_ELEMENT_W_DOMAIN_BODY = f'''
+
+
+
+domain
+ABC
+
+0
+0
+Tst DTEL
+10
+10
+Test Label Medium
+20
+20
+Test Label Long
+40
+40
+Test Label Heading
+55
+55
+
+
+
+
+false
+false
+false
+false
+
+'''
+
+DEFINE_DATA_ELEMENT_W_PREDEFINED_ABAP_TYPE_BODY = f'''
+
+
+
+predefinedAbapType
+
+STRING
+200
+0
+Tst DTEL
+10
+10
+Test Label Medium
+20
+20
+Test Label Long
+40
+40
+Test Label Heading
+55
+55
+
+
+
+
+false
+false
+false
+false
+
+'''
+
+FAKE_LOCK_HANDLE = 'lock_handle'
+
+ACTIVATE_DATA_ELEMENT_BODY = f'''
+
+
+'''
+
+ERROR_XML_DATA_ELEMENT_ALREADY_EXISTS=f'''Resource Data Element {DATA_ELEMENT_NAME} does already exist.Resource Package $SAPCLI_TEST_ROOT does already exist.'''
diff --git a/test/unit/test_sap_adt_dataelement.py b/test/unit/test_sap_adt_dataelement.py
new file mode 100644
index 00000000..4f6879c8
--- /dev/null
+++ b/test/unit/test_sap_adt_dataelement.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+
+import unittest
+import sap.adt
+import sap.adt.dataelement
+
+from mock import Connection, Response, Request
+from fixtures_adt_dataelement import DATA_ELEMENT_DEFINITION_ADT_XML, DATA_ELEMENT_NAME, CREATE_DATA_ELEMENT_ADT_XML
+
+
+class TestADTDataElement(unittest.TestCase):
+
+ def test_data_element_fetch(self):
+ connection = Connection([Response(text=DATA_ELEMENT_DEFINITION_ADT_XML, status_code=200, headers={})])
+
+ data_element = sap.adt.DataElement(connection, DATA_ELEMENT_NAME)
+ data_element.fetch()
+
+ self.assertEqual(data_element.name, DATA_ELEMENT_NAME)
+ self.assertEqual(data_element.active, 'active')
+ self.assertEqual(data_element.master_language, 'EN')
+ self.assertEqual(data_element.description, 'Test data element')
+
+ def test_data_element_serialize(self):
+ connection = Connection()
+
+ metadata = sap.adt.dataelement.ADTDataElementData(description='Test data element', language='EN', master_language='EN',
+ responsible='ANZEIGER')
+ data_element = sap.adt.DataElement(connection, DATA_ELEMENT_NAME, package='PACKAGE', metadata=metadata)
+ data_element.create()
+
+ expected_request = Request(
+ adt_uri='/sap/bc/adt/ddic/dataelements',
+ method='POST',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(CREATE_DATA_ELEMENT_ADT_XML, 'utf-8'),
+ params=None
+ )
+
+ self.assertEqual(connection.execs[0], expected_request)
diff --git a/test/unit/test_sap_cli_dataelement.py b/test/unit/test_sap_cli_dataelement.py
new file mode 100644
index 00000000..e5c80baa
--- /dev/null
+++ b/test/unit/test_sap_cli_dataelement.py
@@ -0,0 +1,495 @@
+#!/usr/bin/env python3
+
+import unittest
+from unittest.mock import call
+from sap.adt.errors import ExceptionResourceAlreadyExists
+
+import sap.cli.dataelement
+from sap.errors import SAPCliError
+
+from mock import (
+ Connection,
+ Response,
+ Request,
+ patch,
+)
+from io import StringIO
+
+from infra import generate_parse_args
+from fixtures_adt_dataelement import (
+ DATA_ELEMENT_NAME,
+ CREATE_DATA_ELEMENT_ADT_XML,
+ READ_DATA_ELEMENT_BODY,
+ WRITE_DATA_ELEMENT_BODY,
+ FAKE_LOCK_HANDLE,
+ ACTIVATE_DATA_ELEMENT_BODY,
+ DATA_ELEMENT_DEFINITION_ADT_XML,
+ DEFINE_DATA_ELEMENT_W_DOMAIN_BODY,
+ DEFINE_DATA_ELEMENT_W_PREDEFINED_ABAP_TYPE_BODY,
+ ERROR_XML_DATA_ELEMENT_ALREADY_EXISTS
+)
+from fixtures_adt_wb import RESPONSE_ACTIVATION_OK
+
+parse_args = generate_parse_args(sap.cli.dataelement.CommandGroup())
+
+
+class TestDataElementCreate(unittest.TestCase):
+
+ def data_element_create_cmd(self, *args, **kwargs):
+ return parse_args('create', *args, **kwargs)
+
+ def test_create(self):
+ connection = Connection()
+
+ the_cmd = self.data_element_create_cmd(DATA_ELEMENT_NAME, 'Test data element', 'package')
+ the_cmd.execute(connection, the_cmd)
+
+ exptected_request = Request(
+ adt_uri='/sap/bc/adt/ddic/dataelements',
+ method='POST',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(CREATE_DATA_ELEMENT_ADT_XML, 'utf-8'),
+ params=None
+ )
+
+ self.assertEqual(connection.execs[0], exptected_request)
+
+
+class TestDataElementActivate(unittest.TestCase):
+
+ def data_element_activate_cmd(self, *args, **kwargs):
+ return parse_args('activate', *args, **kwargs)
+
+ def test_activate(self):
+ connection = Connection([
+ RESPONSE_ACTIVATION_OK,
+ Response(text=DATA_ELEMENT_DEFINITION_ADT_XML, status_code=200, headers={})
+ ])
+
+ the_cmd = self.data_element_activate_cmd(DATA_ELEMENT_NAME)
+ the_cmd.execute(connection, the_cmd)
+
+ expected_request = Request(
+ adt_uri='/sap/bc/adt/activation',
+ method='POST',
+ headers={'Accept': 'application/xml', 'Content-Type': 'application/xml'},
+ body=ACTIVATE_DATA_ELEMENT_BODY,
+ params={'method': 'activate', 'preauditRequested': 'true'}
+ )
+
+ self.assertEqual(connection.execs[0], expected_request)
+
+
+class TestDataElementDefine(unittest.TestCase):
+
+ def data_element_define_cmd(self, *args, **kwargs):
+ return parse_args('define', *args, **kwargs)
+
+ @patch('sap.adt.objects.ADTObject.lock', return_value=FAKE_LOCK_HANDLE)
+ @patch('sap.adt.objects.ADTObject.unlock')
+ def test_define_w_domain(self, fake_lock, fake_unlock):
+ connection = Connection([
+ Response(
+ text='',
+ status_code=200,
+ headers={}
+ ),
+ Response(
+ text=READ_DATA_ELEMENT_BODY,
+ status_code=200,
+ headers={}
+ ),
+ Response(
+ text='',
+ status_code=200,
+ headers={}
+ ),
+ RESPONSE_ACTIVATION_OK,
+ Response(text=DATA_ELEMENT_DEFINITION_ADT_XML, status_code=200, headers={})
+ ], asserter=self)
+
+ the_cmd = self.data_element_define_cmd(DATA_ELEMENT_NAME, 'Test data element', 'package', '--activate', '--type=domain', '--domain_name=ABC', '--label_short=Tst DTEL', '--label_medium=Test Label Medium', '--label_long=Test Label Long', '--label_heading=Test Label Heading')
+ with patch('sys.stdin', StringIO(DATA_ELEMENT_DEFINITION_ADT_XML)):
+ the_cmd.execute(connection, the_cmd)
+
+ fake_lock.assert_called_once()
+ fake_unlock.assert_called_once()
+
+ self.assertEqual(len(connection.execs), 5)
+
+ exptected_request_create = Request(
+ adt_uri='/sap/bc/adt/ddic/dataelements',
+ method='POST',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(CREATE_DATA_ELEMENT_ADT_XML, 'utf-8'),
+ params=None
+ )
+
+ self.assertEqual(connection.execs[0], exptected_request_create)
+
+ expected_request_fetch = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='GET',
+ headers=None,
+ body=None,
+ params=None
+ )
+
+ self.assertEqual(connection.execs[1], expected_request_fetch)
+
+ expected_request_push = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='PUT',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(DEFINE_DATA_ELEMENT_W_DOMAIN_BODY, 'utf-8'),
+ params={'lockHandle': FAKE_LOCK_HANDLE}
+ )
+
+ self.assertEqual(connection.execs[2], expected_request_push)
+
+ expected_request_activate = Request(
+ adt_uri='/sap/bc/adt/activation',
+ method='POST',
+ headers={'Accept': 'application/xml', 'Content-Type': 'application/xml'},
+ body=ACTIVATE_DATA_ELEMENT_BODY,
+ params={'method': 'activate', 'preauditRequested': 'true'}
+ )
+
+ self.assertEqual(connection.execs[3], expected_request_activate)
+
+ expected_request_fetch = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='GET',
+ headers=None,
+ body=None,
+ params=None
+ )
+
+ self.assertEqual(connection.execs[4], expected_request_fetch)
+
+ @patch('sap.adt.objects.ADTObject.lock', return_value=FAKE_LOCK_HANDLE)
+ @patch('sap.adt.objects.ADTObject.unlock')
+ def test_define_w_predefined_abap_type(self, fake_lock, fake_unlock):
+ connection = Connection([
+ Response(
+ text='',
+ status_code=200,
+ headers={}
+ ),
+ Response(
+ text=READ_DATA_ELEMENT_BODY,
+ status_code=200,
+ headers={}
+ ),
+ Response(
+ text='',
+ status_code=200,
+ headers={}
+ ),
+ RESPONSE_ACTIVATION_OK,
+ Response(text=DATA_ELEMENT_DEFINITION_ADT_XML, status_code=200, headers={})
+ ], asserter=self)
+
+ the_cmd = self.data_element_define_cmd(DATA_ELEMENT_NAME, 'Test data element', 'package', '--activate', '--type=predefinedAbapType', '--data_type=STRING', '--data_type_length=200', '--label_short=Tst DTEL', '--label_medium=Test Label Medium', '--label_long=Test Label Long', '--label_heading=Test Label Heading')
+ with patch('sys.stdin', StringIO(DATA_ELEMENT_DEFINITION_ADT_XML)):
+ the_cmd.execute(connection, the_cmd)
+
+ fake_lock.assert_called_once()
+ fake_unlock.assert_called_once()
+
+ self.assertEqual(len(connection.execs), 5)
+
+ exptected_request_create = Request(
+ adt_uri='/sap/bc/adt/ddic/dataelements',
+ method='POST',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(CREATE_DATA_ELEMENT_ADT_XML, 'utf-8'),
+ params=None
+ )
+
+ self.assertEqual(connection.execs[0], exptected_request_create)
+
+ expected_request_fetch = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='GET',
+ headers=None,
+ body=None,
+ params=None
+ )
+
+ self.assertEqual(connection.execs[1], expected_request_fetch)
+
+ expected_request_push = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='PUT',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(DEFINE_DATA_ELEMENT_W_PREDEFINED_ABAP_TYPE_BODY, 'utf-8'),
+ params={'lockHandle': FAKE_LOCK_HANDLE}
+ )
+
+ self.assertEqual(connection.execs[2], expected_request_push)
+
+ expected_request_activate = Request(
+ adt_uri='/sap/bc/adt/activation',
+ method='POST',
+ headers={'Accept': 'application/xml', 'Content-Type': 'application/xml'},
+ body=ACTIVATE_DATA_ELEMENT_BODY,
+ params={'method': 'activate', 'preauditRequested': 'true'}
+ )
+
+ self.assertEqual(connection.execs[3], expected_request_activate)
+
+ expected_request_fetch = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='GET',
+ headers=None,
+ body=None,
+ params=None
+ )
+
+ self.assertEqual(connection.execs[4], expected_request_fetch)
+
+ @patch('sap.adt.objects.ADTObject.lock', return_value=FAKE_LOCK_HANDLE)
+ @patch('sap.adt.objects.ADTObject.unlock')
+ def test_define_domain_not_provided(self, fake_lock, fake_unlock):
+ connection = Connection([
+ Response(
+ text='',
+ status_code=200,
+ headers={}
+ ),
+ Response(
+ text=READ_DATA_ELEMENT_BODY,
+ status_code=200,
+ headers={}
+ ),
+ Response(
+ text='',
+ status_code=200,
+ headers={}
+ ),
+ RESPONSE_ACTIVATION_OK,
+ Response(text=DATA_ELEMENT_DEFINITION_ADT_XML, status_code=200, headers={})
+ ], asserter=self)
+
+ # --domain_name argument is missing
+ the_cmd = self.data_element_define_cmd(DATA_ELEMENT_NAME, 'Test data element', 'package', '--activate', '--type=domain', '--data_type=STRING', '--label_short=Tst DTEL', '--label_medium=Test Label Medium', '--label_long=Test Label Long', '--label_heading=Test Label Heading')
+ with patch('sys.stdin', StringIO(DATA_ELEMENT_DEFINITION_ADT_XML)):
+ try:
+ the_cmd.execute(connection, the_cmd)
+
+ self.fail('Exception should be raised but it has not been')
+ except SAPCliError as e:
+ self.assertEqual(f'Domain name must be provided (--domain_name) if the type (--type) is "domain"', str(e))
+
+ fake_lock.assert_not_called()
+ fake_unlock.assert_not_called()
+
+ self.assertEqual(len(connection.execs), 2)
+
+ exptected_request_create = Request(
+ adt_uri='/sap/bc/adt/ddic/dataelements',
+ method='POST',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(CREATE_DATA_ELEMENT_ADT_XML, 'utf-8'),
+ params=None
+ )
+
+ self.assertEqual(connection.execs[0], exptected_request_create)
+
+ expected_request_fetch = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='GET',
+ headers=None,
+ body=None,
+ params=None
+ )
+
+ self.assertEqual(connection.execs[1], expected_request_fetch)
+
+ @patch('sap.adt.objects.ADTObject.lock', return_value=FAKE_LOCK_HANDLE)
+ @patch('sap.adt.objects.ADTObject.unlock')
+ def test_define_predefined_abap_type_not_provided(self, fake_lock, fake_unlock):
+ connection = Connection([
+ Response(
+ text='',
+ status_code=200,
+ headers={}
+ ),
+ Response(
+ text=READ_DATA_ELEMENT_BODY,
+ status_code=200,
+ headers={}
+ ),
+ Response(
+ text='',
+ status_code=200,
+ headers={}
+ ),
+ RESPONSE_ACTIVATION_OK,
+ Response(text=DATA_ELEMENT_DEFINITION_ADT_XML, status_code=200, headers={})
+ ], asserter=self)
+
+ # --data_type argument is missing
+ the_cmd = self.data_element_define_cmd(DATA_ELEMENT_NAME, 'Test data element', 'package', '--activate', '--type=predefinedAbapType', '--domain_name=ABC', '--label_short=Tst DTEL', '--label_medium=Test Label Medium', '--label_long=Test Label Long', '--label_heading=Test Label Heading')
+ with patch('sys.stdin', StringIO(DATA_ELEMENT_DEFINITION_ADT_XML)):
+ try:
+ the_cmd.execute(connection, the_cmd)
+
+ self.fail('Exception should be raised but it has not been')
+ except SAPCliError as e:
+ self.assertEqual(f'Data type name must be provided (--data_type) if the type (--type) is "predefinedAbapType"', str(e))
+
+ fake_lock.assert_not_called()
+ fake_unlock.assert_not_called()
+
+ self.assertEqual(len(connection.execs), 2)
+
+ exptected_request_create = Request(
+ adt_uri='/sap/bc/adt/ddic/dataelements',
+ method='POST',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(CREATE_DATA_ELEMENT_ADT_XML, 'utf-8'),
+ params=None
+ )
+
+ self.assertEqual(connection.execs[0], exptected_request_create)
+
+ expected_request_fetch = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='GET',
+ headers=None,
+ body=None,
+ params=None
+ )
+
+ self.assertEqual(connection.execs[1], expected_request_fetch)
+
+ @patch('sap.adt.objects.ADTObject.lock', return_value=FAKE_LOCK_HANDLE)
+ @patch('sap.adt.objects.ADTObject.unlock')
+ def test_define_data_element_already_exists(self, fake_lock, fake_unlock):
+ connection = Connection([
+ Response(
+ text=ERROR_XML_DATA_ELEMENT_ALREADY_EXISTS,
+ status_code=500,
+ headers={'content-type': 'application/xml'}
+ ),
+ Response(
+ text=READ_DATA_ELEMENT_BODY,
+ status_code=200,
+ headers={}
+ ),
+ Response(
+ text='',
+ status_code=200,
+ headers={}
+ ),
+ RESPONSE_ACTIVATION_OK,
+ Response(text=DATA_ELEMENT_DEFINITION_ADT_XML, status_code=200, headers={})
+ ], asserter=self)
+
+ the_cmd = self.data_element_define_cmd(DATA_ELEMENT_NAME, 'Test data element', 'package', '--activate', '--type=domain', '--domain_name=ABC', '--label_short=Tst DTEL', '--label_medium=Test Label Medium', '--label_long=Test Label Long', '--label_heading=Test Label Heading')
+ with patch('sys.stdin', StringIO(DATA_ELEMENT_DEFINITION_ADT_XML)):
+ try:
+ the_cmd.execute(connection, the_cmd)
+
+ self.fail('Exception should be raised but it has not been')
+ except ExceptionResourceAlreadyExists as e:
+ self.assertEqual('Resource Data Element TEST_DATA_ELEMENT does already exist.', str(e))
+
+ fake_lock.assert_not_called()
+ fake_unlock.assert_not_called()
+
+ self.assertEqual(len(connection.execs), 1)
+
+ exptected_request_create = Request(
+ adt_uri='/sap/bc/adt/ddic/dataelements',
+ method='POST',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(CREATE_DATA_ELEMENT_ADT_XML, 'utf-8'),
+ params=None
+ )
+
+ self.assertEqual(connection.execs[0], exptected_request_create)
+
+ @patch('sap.adt.objects.ADTObject.lock', return_value=FAKE_LOCK_HANDLE)
+ @patch('sap.adt.objects.ADTObject.unlock')
+ def test_define_data_element_already_exists_but_skipped(self, fake_lock, fake_unlock):
+ connection = Connection([
+ Response(
+ text=ERROR_XML_DATA_ELEMENT_ALREADY_EXISTS,
+ status_code=500,
+ headers={'content-type': 'application/xml'}
+ ),
+ Response(
+ text=READ_DATA_ELEMENT_BODY,
+ status_code=200,
+ headers={}
+ ),
+ Response(
+ text='',
+ status_code=200,
+ headers={}
+ ),
+ RESPONSE_ACTIVATION_OK,
+ Response(text=DATA_ELEMENT_DEFINITION_ADT_XML, status_code=200, headers={})
+ ], asserter=self)
+
+ the_cmd = self.data_element_define_cmd(DATA_ELEMENT_NAME, 'Test data element', 'package', '--activate', '--no-error-existing', '--type=domain', '--domain_name=ABC', '--label_short=Tst DTEL', '--label_medium=Test Label Medium', '--label_long=Test Label Long', '--label_heading=Test Label Heading')
+ with patch('sys.stdin', StringIO(DATA_ELEMENT_DEFINITION_ADT_XML)):
+ the_cmd.execute(connection, the_cmd)
+
+ fake_lock.assert_called_once()
+ fake_unlock.assert_called_once()
+
+ self.assertEqual(len(connection.execs), 5)
+
+ exptected_request_create = Request(
+ adt_uri='/sap/bc/adt/ddic/dataelements',
+ method='POST',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(CREATE_DATA_ELEMENT_ADT_XML, 'utf-8'),
+ params=None
+ )
+
+ self.assertEqual(connection.execs[0], exptected_request_create)
+
+ expected_request_fetch = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='GET',
+ headers=None,
+ body=None,
+ params=None
+ )
+
+ self.assertEqual(connection.execs[1], expected_request_fetch)
+
+ expected_request_push = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='PUT',
+ headers={'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8'},
+ body=bytes(DEFINE_DATA_ELEMENT_W_DOMAIN_BODY, 'utf-8'),
+ params={'lockHandle': FAKE_LOCK_HANDLE}
+ )
+
+ self.assertEqual(connection.execs[2], expected_request_push)
+
+ expected_request_activate = Request(
+ adt_uri='/sap/bc/adt/activation',
+ method='POST',
+ headers={'Accept': 'application/xml', 'Content-Type': 'application/xml'},
+ body=ACTIVATE_DATA_ELEMENT_BODY,
+ params={'method': 'activate', 'preauditRequested': 'true'}
+ )
+
+ self.assertEqual(connection.execs[3], expected_request_activate)
+
+ expected_request_fetch = Request(
+ adt_uri=f'/sap/bc/adt/ddic/dataelements/{DATA_ELEMENT_NAME.lower()}',
+ method='GET',
+ headers=None,
+ body=None,
+ params=None
+ )
+
+ self.assertEqual(connection.execs[4], expected_request_fetch)