RedHatQE / pylero

Python wrapper for the Polarion WSDL API
https://redhatqe.github.io/pylero/
MIT License
37 stars 25 forks source link

Extracting custom fields with the workItem constructor fails. #136

Closed AlthausKonstantin closed 1 year ago

AlthausKonstantin commented 1 year ago

Dear pylero team,

I have a problem extracting custom fields of a workitem using the constructor keyword fields. This script demonstrates that a specific custom field does exists. Still get a KeyError: 'customFields.Active' if I use the constructor.

from pylero.work_item import * 

# Check which workitem types are available
# One entry is "Task"
print(globals()['workitems'].values()) 

# Successfully import an existing workitem of type "Task"
from pylero.work_item import Task 
task = Task(project_id="PHP2020", work_item_id="PHP2020-42443")

# Check which custom fields are avaiable
# One entry is "Active"
print(task.get_custom_field_keys())

# Successfully extract value of custom field "Active"
print(task.get_custom_field("Active").value)

# Successfully extract a standard field with the constructor
same_task =  Task(project_id="PHP2020",
               work_item_id="PHP2020-42443",
               fields=["title"])
print(same_task.title)

# CAREFUL: Trying to extract custom field with constructor throws an error!
same_task =  Task(project_id="PHP2020",
               work_item_id="PHP2020-42443",
               fields=["customFields.Active"])
leelavg commented 1 year ago

Currently afk, I don't think we need to have prefix for accessing custom fields, could you pls try fields=["Active"] once?

AlthausKonstantin commented 1 year ago

Using fields=["Active"] throws KeyError: 'Active'.

AlthausKonstantin commented 1 year ago

Dear all,

I have investigated the problem further.

Details of my Problem It seems that the attribute _cls_suds_map does not contain the custom fields defined in Polarion. Therefore, as mentioned above in https://github.com/RedHatQE/pylero/issues/136#issue-1684938210, I obtain a KeyError in this line: https://github.com/RedHatQE/pylero/blob/3af53c70febba00c6268ec8c52beac20969c793c/src/pylero/base_polarion.py#L336

Workaround I have written a dirty and unsatisfying workaround by monkeypatching the method pylero.base_polarion.BasePolarion._convert_obj_fields_to_polarion(). I pipe the custom-field-entries in argument the field through the method unchanged, see the following script. I do not consider this a stable solution...

from pylero.work_item import Task # import an existing workitem type

# Overwrite internal method to get custom fields
import pylero.base_polarion
@classmethod
def new_convert_obj_fields_to_polarion(cls, fields=[]):
    """Monkeypatch `pylero.base_polarion.BasePolarion._convert_obj_fields_to_polarion`.

    Apply original method to all items in `fields` that do not contain the
    string 'customFields' and join result with all items in `fields` which
    do contain the string 'customFields'.
    """
    p_fields = []
    if fields:
        if not isinstance(fields, list):
            fields = [fields]
        # convert given fields to Polarion fields
        p_fields = [
            "%s%s"
            % (
                "customFields."
                if isinstance(cls._cls_suds_map[x], dict)
                and cls._cls_suds_map[x].get("is_custom", False)
                else "",
                cls._cls_suds_map[x]
                if not isinstance(cls._cls_suds_map[x], dict)
                else cls._cls_suds_map[x]["field_name"],
            )
            for x in fields
            if "customFields" not in x
        ]
       # Do not touch custom fields
        custom_p_fields = [x for x in fields if "customFields" in x]
        p_fields.extend(custom_p_fields)

        # Omit 'URIs' and 'URI' from URIFields
        p_fields = [(x.replace("URIs", "").replace("URI", "")) for x in p_fields]
    return p_fields

pylero.base_polarion.BasePolarion._convert_obj_fields_to_polarion = (
    new_convert_obj_fields_to_polarion
)

# Successfully extract custom field
task =  Task(project_id="PHP2020",
               work_item_id="PHP2020-42443",
               fields=['customFields.Active'])
print(task._suds_object.customFields) 

Help Needed Could you help me to figure out, why the _cls_suds_map is not set up as expected? Thank you :)

AlthausKonstantin commented 1 year ago

I have added https://github.com/RedHatQE/pylero/pull/137 with a more reputable patch :)

AlthausKonstantin commented 1 year ago

As discussed extensively in the PR https://github.com/RedHatQE/pylero/pull/137, here is the solution of this issue. Thanks again for the patients and help of @leelavg and @simzacks!!

Extracting custom fields with the WorkItem constructor

To get the normal field "Title" and the custom field "Active", do this:

from pylero.work_item import Task
task =  Task(project_id="PHP2020",
               work_item_id="PHP2020-42443",
               fields=["title", "active"])
print(same_task.active) # prints correct value of the custom field `active`.

Explanation of the issue:

What confused me about this is, that if you get a list of the defined custom fields, the name is not in lower case:

# continues last snippet
print(task.get_custom_field_keys()) # this list contains the entry "Active"

Same is true for the method get_custom_field():

# continues last snippet
task.get_custom_field("Active") # gets correct value
task.get_custom_field("active") # returns `None`

In case you wonder, how the strings are transformed from one into the other: This happens in pylero.work_items 1436-1443: In partuclar everything becomes lower case and whitespaces become underscores.

The linked PR https://github.com/RedHatQE/pylero/pull/137

This PR adresses a different issue arising in the use of get_work_items(), a method defined in pylero.document